Предыдущий:Анализ принципа Vue (2): что делал beforeCreate во время инициализации?
давай продолжимthis._init()Путь инициализации , а затем будут выполнены следующие три метода инициализации:
initInjections(vm)
initState(vm)
initProvide(vm)
5. initInjections(vm): Основная функция — инициализироватьinject, вы можете получить доступ к соответствующим зависимостям.
injectа такжеprovideЗдесь необходимо кратко упомянуть, что этоvue@2.2Добавлена пара версий, которые нужно использовать вместеAPI, он позволяет родительскому компоненту предоставлять зависимости всем дочерним компонентам после него, так что к дочерним компонентам можно получить доступ независимо от того, насколько глубоко они вложены, оченьcoolТам есть дерево~
-
provide: Предоставляет объект или функцию, которая возвращает объект. -
inject: массив или объект строк.
параAPIсуществуетvueОфициальный сайт дает два совета по питанию:
provideа такжеinjectВ основном предоставляет варианты использования для библиотек плагинов/компонентов более высокого порядка. Его не рекомендуется использовать непосредственно в коде приложения.
- Вероятно, потому что это испортит иерархию данных компонентов, но это будет очень полезно при разработке библиотек компонентов.
provideа такжеinjectПривязки не реактивны. Это сделано намеренно. Однако, если вы передаете прослушиваемый объект, свойства объекта по-прежнему реагируют.
- Есть небольшая хитрость, сюда можно поставить корневой компонент
dataСвойства, определенные внутри, предоставляются компонентам-потомкам, так что, не прибегая кvuexВ случае простого управления глобальным состоянием это все еще оченьcoolда~
app.vue 根组件
export default {
provide() {
return {
app: this
}
},
data() {
return {
info: 'hello world!'
}
}
}
child.vue 子孙组件
export default {
inject: ['app'],
methods: {
handleClick() {
this.app.info = 'hello vue!'
}
}
}
после запускаhandleClickПосле события, независимо от того, насколько глубоки вложенные компоненты-потомки, до тех пор, пока они используются.injectинъекцияthis.app.infoНа переменные будут даны ответы, что завершает простуюvuex. Для получения дополнительных примеров вы можете перейти кvueЧитайте на официальном сайте.coolКак это работает?
несмотря на то чтоinjectа такжеprovideиспользуются парами, но внутри они инициализируются отдельно. Как видно из приведенных выше трех методов инициализации, сначала инициализируйтеinject, а затем инициализироватьprops/dataОтносится к состоянию, последняя инициализацияprovide. Цель этого состоит в том, чтобы иметь возможностьprops/dataиспользуется вinjectсодержимое, введенное внутрь.
Давайте сначала посмотрим на инициализациюinjectОпределение метода, когда:
export function initInjections(vm) {
const result = resolveInject(vm.$options.inject, vm) // 找结果
...
}
vm.$options.injectОпределяется пользователем для ранее объединенныхinject, затем используйтеresolveInjectметод, чтобы найти результат, который мы хотим, давайте посмотримresolveInjectОпределение метода:
export function resolveInject (inject, vm) {
if (inject) {
const result = Object.create(null)
const keys = Object.keys(inject) //省略Symbol情况
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
const provideKey = inject[key].from
let source = vm
while (source) {
if (source._provided && hasOwn(source._provided, provideKey)) { //hasOwn为是否有
result[key] = source._provided[provideKey]
break
}
source = source.$parent
}
... vue@2.5后新增设置inject默认参数相关逻辑
}
return result
}
}
Сначала определитеresultВерните найденные результаты. Затем используйте поиск с двойным циклом, внешний цикл for будет проходитьinjectкаждый элемент, а затем используйте внутренний слойwhileциклический поиск снизу вверхinjectПредоставляет ли родитель этого элемента соответствующие зависимости.
Ps:У некоторых людей могут быть сомнения здесь, прежде чемinjectОпределение, очевидно, является массивом, как я могу передатьObject.keysстоимость? Это потому, что в предыдущей главеoptionsПри слиянии параметры также форматируются, напримерpropsФормат, определенный как массив, также будет преобразован в формат объекта,injectОн определяется следующим образом:
定义时:
{
inject: ['app']
}
格式化后:
{
inject: {
app: {
from: 'app'
}
}
}
Книга сверху,sourceявляется текущим экземпляром, иsource._providedхранится текущийprovideпредоставленная стоимость. Сначала найдите текущий экземпляр, затем назначьте экземпляр его родительского компонента дляsource, посмотрите в его родительском компоненте. найти и использоватьbreakВырваться из цикла и присвоить результат поискаresult, затем найдите следующий.
Ps:У некоторых людей могут возникнуть сомнения, это время инициализируется первымinjectПовторно инициализированprovide, как получить доступ к родительскомуprovideуже? Он вообще не инициализируется, в этот раз надо подумать еще раз, т.к.vueЭто тип компонента, родительский компонент будет инициализирован первым, а затем будет инициализирован дочерний компонент, поэтому на этот разsource._providedатрибут.
После нахождения нужного результата завершаем предыдущееinitInjectionsОпределение:
export function initInjections(vm) {
const result = resolveInject(vm.$options.inject, vm)
if(result) { // 如果有结果
toggleObserving(false) // 刻意为之不被响应式
Object.keys(result).forEach(key => {
...
defineReactive(vm, key, result[key])
})
toggleObserving(true)
}
}
Если есть результаты поиска, первые звонкиtoggleObserving(false), не заботьтесь о конкретной реализации, просто знайте, что функция этого метода заключается в установке бита флага, который будет определятьdefineReactive()Устанавливает ли метод свой третий параметр в реактивные данные, т. е. решаетresult[key]Будет ли это значение установлено как реактивные данные, параметр здесьfalse, как раз вvmскачатьkeyСоответствует обычному значению, но таким образом его можно использовать в текущем экземпляре.thisдоступ кinjectУстанавливаются соответствующие зависимости, а затем вызываются после установки.toggleObserving(true), изменить бит флага, пустьdefineReactive()Третий параметр может быть установлен как реагирующие данные (defineReactiveЭто важный метод принципа отзывчивости, вы можете понять это здесь), каким он должен быть. ВышеупомянутоеinjectКороче говоря, соответствующий принцип реализации состоит в том, чтобы сначала пройти каждый элемент, а затем пройти каждый элемент один за другим, независимо от того, есть ли у родителя зависимости.
6. initState(vm): Инициализировать состояние, которое будет использоваться, состояние включаетprops,methods,data,computed,watchПять вариантов.
Первый взглядinitState(vm)Определение метода:
export function initState(vm) {
...
const opts = vm.$options
if(opts.props) initProps(vm, opts.props)
if(opts.methods) initMethods(vm, opts.methods)
if(opts.data) initData(vm)
...
if(opts.computed) initComputed(vm, opts.computed)
if(opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch)
}
}
Теперь слова здесь будут только знакомить с тем, что делает инициализация первых трех типов состояний, то естьprops,methods,data,потому чтоcomputedа такжеwatchбудет включать в себя отзывчивыйwatcher, который здесь пропущен. Далее у нас по очереди появятся эти три метода инициализации:
6.1initProps(vm, propsOptions):
- Основная функция состоит в том, чтобы определить, соответствует ли значение, принятое подкомпонентом, правилам, и сделать соответствующее значение доступным.
thisпрямое интервью.
function initProps(vm, propsOptions) { // 第二个参数为验证规则
const propsData = vm.$options.propsData || {} // props具体的值
const props = vm._props = {} // 存放props
const isRoot = !vm.$parent // 是否是根节点
if (!isRoot) {
toggleObserving(false)
}
for (const key in propsOptions) {
const value = validateProp(key, propsOptions, propsData, vm)
defineReactive(props, key, value)
if (!(key in vm)) {
proxy(vm, `_props`, key)
}
}
toggleObserving(true)
}
мы знаемpropsявляется важным способом взаимодействия родительского компонента с дочерним компонентом, в то время какinitPropsвторой параметр внутриpropsOptions, который является текущим экземпляром, который является подкомпонентом в коммуникационной роли, и правила, которые он определяет для приема параметров. подкомпонентpropsПравила могут быть определены в виде массива, но затем объединеныoptionsЗатем он будет отформатирован как объект:
定义时:
{
props: ['name', 'age']
}
格式化后:
{
name: {
type: null
},
age: {
type: null
}
}
Итак, в определенииpropsПри написании правил используйте формат объекта напрямую, что также является лучшей спецификацией написания.
Следующим шагом после знания правил является знание конкретного значения, передаваемого родительским компонентом дочернему компоненту, которое помещается в формат объекта.vm.$options.propsDataвнутри, который также слитoptionsполучено, когда. Далее под экземпляром определяется пустой объектvm._props, его роль заключается в монтировании значения, соответствующего спецификации под ним.isRootРоль состоит в том, чтобы определить, является ли текущий компонент корневым компонентом, если нет, то он не будетpropsк ответным данным.
Затем выполните итерацию по отформатированномуpropsправила проверки, пройтиvalidatePropМетод проверяет правило и получает соответствующее значение, а затем монтирует полученное значение вvm._propsВниз. В это время можно пройтиthis._propsдоступ кpropsВнутри определенного значения:
props: ['name'],
methods: {
handleClick() {
console.log(this._props.name)
}
}
Однако прямой доступ к внутренним закрытым переменным не является дружественным, поэтомуvueСлой прокси создается внутри, чтоthis.nameдоступ превращается вthis._props.nameДоступ. здесьproxyнеобходимо представить, потому что позжеdataОн также будет использоваться, см. его определение:
格式化了一下:
export function proxy(target, sourceKey, key) {
Object.defineProperty(target, key, {
enumerable: true,
configurable: true,
get: function () {
return this[sourceKey][key]
},
set: function () {
this[sourceKey][key] = val
}
})
}
На самом деле это очень просто, достаточно определить значение объектаgetметод, пусть при чтении возвращает другое значение, это делаетсяpropsинициализация.
6.2initMethods(vm, методы):
- Основная функция состоит в том, чтобы
methodsМетод внутри монтируется кthisВниз.
function initMethods(vm, methods) {
const props = vm.$options.props
for(const key in methods) {
if(methods[key] == null) { // methods[key] === null || methods[key] === undefined 的简写
warn(`只定义了key而没有相应的value`)
}
if(props && hasOwn(props, key)) {
warn(`方法名和props的key重名了`)
}
if((key in vm) && isReserved(key)) {
warn(`方法名已经存在而且以_或$开头`)
}
vm[key] = methods[key] == null
? noop // 空函数
: bind(methods[key], vm) // 相当于methods[key].bind(vm)
}
}
methodsИнициализация относительно проста. Однако у него также есть много пограничных случаев, таких как только определенныеkeyКонкретной реализации метода нет.keyа такжеpropsпереименован,keyуже существует и плохо назван, начиная с_или$В начале, почему это не работает, мы объяснили это в первой главе. наконецmethodsМетод внутри монтируется кthisвниз, это сделаноmethodsинициализация.
6.3данные инициализации (ВМ):
- Основная функция — инициализация.
data, или старая подпрограмма, монтировать вthisВниз. Есть важный момент, почемуdataДанные внутри реагируют и инициализируются здесь, у всех должно быть впечатление~.
function initData (vm: Component) {
let data = vm.$options.data
data = vm._data = typeof data === 'function'
? getData(data, vm) // 通过data.call(vm, vm)得到返回的对象
: data || {}
if (!isPlainObject(data)) { // 如果不是一个对象格式
data = {}
warn(`data得是一个对象`)
}
const keys = Object.keys(data)
const props = vm.$options.props // 得到props
const methods = vm.$options.methods // 得到methods
let i = keys.length
while (i--) {
const key = keys[i]
if (methods && hasOwn(methods, key)) {
warn(`和methods内的方法重名了`)
}
if (props && hasOwn(props, key)) {
warn(`和props内的key重名了`)
} else if (!isReserved(key)) { // key不能以_或$开头
proxy(vm, `_data`, key)
}
}
observe(data, true)
}
сначала черезvm.$options.dataполучить определение пользователяdata,еслиfunctionотформатировать, выполнить его и вернуть результат после выполнения, иначе вернутьdataили{}vm._dataprops
datamethodspropskeyproxyobserve(data, true)data
this
7. initProvide(vm)provide
providedata
export function initProvide (vm) {
const provide = vm.$options.provide
if (provide) {
vm._provided = typeof provide === 'function'
? provide.call(vm)
: provide
}
}
vm.$options.provideprovidefunctionvm._providedinjectfunctionprovide
8. callHook(vm, 'created')createdmixin
created
injectthisprovidecreated
vue
methods
-
thisvuemethodsvmmethods[key].bind(vm)undefinedundefined