предисловие
предыдущий постИнтерпретация исходного кода Vue (5) — глобальный APIПодробно описан принцип реализации каждого глобального API Vue В этой статье будет подробно представлен принцип реализации каждого метода экземпляра.
Цель
Глубокое понимание принципов реализации следующих примеров методов.
-
vm.$set
-
vm.$delete
-
vm.$watch
-
vm.$on
-
vm.$emit
-
vm.$off
-
vm.$once
-
vm._update
-
vm.$forceUpdate
-
vm.$destroy
-
vm.$nextTick
-
vm._render
Интерпретация исходного кода
Вход
/src/core/instance/index.js
Этот файл является входным файлом экземпляра Vue, включая определение конструктора Vue и инициализацию каждого метода экземпляра.
// Vue 的构造函数
function Vue (options) {
// 调用 Vue.prototype._init 方法,该方法是在 initMixin 中定义的
this._init(options)
}
// 定义 Vue.prototype._init 方法
initMixin(Vue)
/**
* 定义:
* Vue.prototype.$data
* Vue.prototype.$props
* Vue.prototype.$set
* Vue.prototype.$delete
* Vue.prototype.$watch
*/
stateMixin(Vue)
/**
* 定义 事件相关的 方法:
* Vue.prototype.$on
* Vue.prototype.$once
* Vue.prototype.$off
* Vue.prototype.$emit
*/
eventsMixin(Vue)
/**
* 定义:
* Vue.prototype._update
* Vue.prototype.$forceUpdate
* Vue.prototype.$destroy
*/
lifecycleMixin(Vue)
/**
* 执行 installRenderHelpers,在 Vue.prototype 对象上安装运行时便利程序
*
* 定义:
* Vue.prototype.$nextTick
* Vue.prototype._render
*/
renderMixin(Vue)
вм.$данные, вм.$пропс
src/core/instance/state.js
Это два свойства экземпляра, а не методы экземпляра.Вот краткое введение.Конечно, его собственная реализация тоже очень проста.
// data
const dataDef = {}
dataDef.get = function () { return this._data }
// props
const propsDef = {}
propsDef.get = function () { return this._props }
// 将 data 属性和 props 属性挂载到 Vue.prototype 对象上
// 这样在程序中就可以通过 this.$data 和 this.$props 来访问 data 和 props 对象了
Object.defineProperty(Vue.prototype, '$data', dataDef)
Object.defineProperty(Vue.prototype, '$props', propsDef)
vm.$set
/src/core/instance/state.js
Vue.prototype.$set = set
set
/src/core/observer/index.js
/**
* 通过 Vue.set 或者 this.$set 方法给 target 的指定 key 设置值 val
* 如果 target 是对象,并且 key 原本不存在,则为新 key 设置响应式,然后执行依赖通知
*/
export function set (target: Array<any> | Object, key: any, val: any): any {
if (process.env.NODE_ENV !== 'production' &&
(isUndef(target) || isPrimitive(target))
) {
warn(`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`)
}
// 更新数组指定下标的元素,Vue.set(array, idx, val),通过 splice 方法实现响应式更新
if (Array.isArray(target) && isValidArrayIndex(key)) {
target.length = Math.max(target.length, key)
target.splice(key, 1, val)
return val
}
// 更新对象已有属性,Vue.set(obj, key, val),执行更新即可
if (key in target && !(key in Object.prototype)) {
target[key] = val
return val
}
const ob = (target: any).__ob__
// 不能向 Vue 实例或者 $data 添加动态添加响应式属性,vmCount 的用处之一,
// this.$data 的 ob.vmCount = 1,表示根组件,其它子组件的 vm.vmCount 都是 0
if (target._isVue || (ob && ob.vmCount)) {
process.env.NODE_ENV !== 'production' && warn(
'Avoid adding reactive properties to a Vue instance or its root $data ' +
'at runtime - declare it upfront in the data option.'
)
return val
}
// target 不是响应式对象,新属性会被设置,但是不会做响应式处理
if (!ob) {
target[key] = val
return val
}
// 给对象定义新属性,通过 defineReactive 方法设置响应式,并触发依赖更新
defineReactive(ob.value, key, val)
ob.dep.notify()
return val
}
vm.$delete
/src/core/instance/state.js
Vue.prototype.$delete = del
del
/src/core/observer/index.js
/**
* 通过 Vue.delete 或者 vm.$delete 删除 target 对象的指定 key
* 数组通过 splice 方法实现,对象则通过 delete 运算符删除指定 key,并执行依赖通知
*/
export function del (target: Array<any> | Object, key: any) {
if (process.env.NODE_ENV !== 'production' &&
(isUndef(target) || isPrimitive(target))
) {
warn(`Cannot delete reactive property on undefined, null, or primitive value: ${(target: any)}`)
}
// target 为数组,则通过 splice 方法删除指定下标的元素
if (Array.isArray(target) && isValidArrayIndex(key)) {
target.splice(key, 1)
return
}
const ob = (target: any).__ob__
// 避免删除 Vue 实例的属性或者 $data 的数据
if (target._isVue || (ob && ob.vmCount)) {
process.env.NODE_ENV !== 'production' && warn(
'Avoid deleting properties on a Vue instance or its root $data ' +
'- just set it to null.'
)
return
}
// 如果属性不存在直接结束
if (!hasOwn(target, key)) {
return
}
// 通过 delete 运算符删除对象的属性
delete target[key]
if (!ob) {
return
}
// 执行依赖通知
ob.dep.notify()
}
vm.$watch
/src/core/instance/state.js
/**
* 创建 watcher,返回 unwatch,共完成如下 5 件事:
* 1、兼容性处理,保证最后 new Watcher 时的 cb 为函数
* 2、标示用户 watcher
* 3、创建 watcher 实例
* 4、如果设置了 immediate,则立即执行一次 cb
* 5、返回 unwatch
* @param {*} expOrFn key
* @param {*} cb 回调函数
* @param {*} options 配置项,用户直接调用 this.$watch 时可能会传递一个 配置项
* @returns 返回 unwatch 函数,用于取消 watch 监听
*/
Vue.prototype.$watch = function (
expOrFn: string | Function,
cb: any,
options?: Object
): Function {
const vm: Component = this
// 兼容性处理,因为用户调用 vm.$watch 时设置的 cb 可能是对象
if (isPlainObject(cb)) {
return createWatcher(vm, expOrFn, cb, options)
}
// options.user 表示用户 watcher,还有渲染 watcher,即 updateComponent 方法中实例化的 watcher
options = options || {}
options.user = true
// 创建 watcher
const watcher = new Watcher(vm, expOrFn, cb, options)
// 如果用户设置了 immediate 为 true,则立即执行一次回调函数
if (options.immediate) {
try {
cb.call(vm, watcher.value)
} catch (error) {
handleError(error, vm, `callback for immediate watcher "${watcher.expression}"`)
}
}
// 返回一个 unwatch 函数,用于解除监听
return function unwatchFn() {
watcher.teardown()
}
}
vm.$on
/src/core/instance/events.js
const hookRE = /^hook:/
/**
* 监听实例上的自定义事件,vm._event = { eventName: [fn1, ...], ... }
* @param {*} event 单个的事件名称或者有多个事件名组成的数组
* @param {*} fn 当 event 被触发时执行的回调函数
* @returns
*/
Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component {
const vm: Component = this
if (Array.isArray(event)) {
// event 是有多个事件名组成的数组,则遍历这些事件,依次递归调用 $on
for (let i = 0, l = event.length; i < l; i++) {
vm.$on(event[i], fn)
}
} else {
// 将注册的事件和回调以键值对的形式存储到 vm._event 对象中 vm._event = { eventName: [fn1, ...] }
(vm._events[event] || (vm._events[event] = [])).push(fn)
// hookEvent,提供从外部为组件实例注入声明周期方法的机会
// 比如从组件外部为组件的 mounted 方法注入额外的逻辑
// 该能力是结合 callhook 方法实现的
if (hookRE.test(event)) {
vm._hasHookEvent = true
}
}
return vm
}
О hookEvent в следующей статье будет подробно рассказано.
vm.$emit
/src/core/instance/events.js
/**
* 触发实例上的指定事件,vm._event[event] => cbs => loop cbs => cb(args)
* @param {*} event 事件名
* @returns
*/
Vue.prototype.$emit = function (event: string): Component {
const vm: Component = this
if (process.env.NODE_ENV !== 'production') {
// 将事件名转换为小些
const lowerCaseEvent = event.toLowerCase()
// 意思是说,HTML 属性不区分大小写,所以你不能使用 v-on 监听小驼峰形式的事件名(eventName),而应该使用连字符形式的事件名(event-name)
if (lowerCaseEvent !== event && vm._events[lowerCaseEvent]) {
tip(
`Event "${lowerCaseEvent}" is emitted in component ` +
`${formatComponentName(vm)} but the handler is registered for "${event}". ` +
`Note that HTML attributes are case-insensitive and you cannot use ` +
`v-on to listen to camelCase events when using in-DOM templates. ` +
`You should probably use "${hyphenate(event)}" instead of "${event}".`
)
}
}
// 从 vm._event 对象上拿到当前事件的回调函数数组,并一次调用数组中的回调函数,并且传递提供的参数
let cbs = vm._events[event]
if (cbs) {
cbs = cbs.length > 1 ? toArray(cbs) : cbs
const args = toArray(arguments, 1)
const info = `event handler for "${event}"`
for (let i = 0, l = cbs.length; i < l; i++) {
invokeWithErrorHandling(cbs[i], vm, args, vm, info)
}
}
return vm
}
vm.$off
/src/core/instance/events.js
/**
* 移除自定义事件监听器,即从 vm._event 对象中找到对应的事件,移除所有事件 或者 移除指定事件的回调函数
* @param {*} event
* @param {*} fn
* @returns
*/
Vue.prototype.$off = function (event?: string | Array<string>, fn?: Function): Component {
const vm: Component = this
// vm.$off() 移除实例上的所有监听器 => vm._events = {}
if (!arguments.length) {
vm._events = Object.create(null)
return vm
}
// 移除一些事件 event = [event1, ...],遍历 event 数组,递归调用 vm.$off
if (Array.isArray(event)) {
for (let i = 0, l = event.length; i < l; i++) {
vm.$off(event[i], fn)
}
return vm
}
// 除了 vm.$off() 之外,最终都会走到这里,移除指定事件
const cbs = vm._events[event]
if (!cbs) {
// 表示没有注册过该事件
return vm
}
if (!fn) {
// 没有提供 fn 回调函数,则移除该事件的所有回调函数,vm._event[event] = null
vm._events[event] = null
return vm
}
// 移除指定事件的指定回调函数,就是从事件的回调数组中找到该回调函数,然后删除
let cb
let i = cbs.length
while (i--) {
cb = cbs[i]
if (cb === fn || cb.fn === fn) {
cbs.splice(i, 1)
break
}
}
return vm
}
vm.$once
/src/core/instance/events.js
/**
* 监听一个自定义事件,但是只触发一次。一旦触发之后,监听器就会被移除
* vm.$on + vm.$off
* @param {*} event
* @param {*} fn
* @returns
*/
Vue.prototype.$once = function (event: string, fn: Function): Component {
const vm: Component = this
// 调用 $on,只是 $on 的回调函数被特殊处理了,触发时,执行回调函数,先移除事件监听,然后执行你设置的回调函数
function on() {
vm.$off(event, on)
fn.apply(vm, arguments)
}
on.fn = fn
vm.$on(event, on)
return vm
}
vm._update
/src/core/instance/lifecycle.js
/**
* 负责更新页面,页面首次渲染和后续更新的入口位置,也是 patch 的入口位置
*/
Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
const vm: Component = this
const prevEl = vm.$el
const prevVnode = vm._vnode
const restoreActiveInstance = setActiveInstance(vm)
vm._vnode = vnode
// Vue.prototype.__patch__ is injected in entry points
// based on the rendering backend used.
if (!prevVnode) {
// 首次渲染,即初始化页面时走这里
vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)
} else {
// 响应式数据更新时,即更新页面时走这里
vm.$el = vm.__patch__(prevVnode, vnode)
}
restoreActiveInstance()
// update __vue__ reference
if (prevEl) {
prevEl.__vue__ = null
}
if (vm.$el) {
vm.$el.__vue__ = vm
}
// if parent is an HOC, update its $el as well
if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {
vm.$parent.$el = vm.$el
}
// updated hook is called by the scheduler to ensure that children are
// updated in a parent's updated hook.
}
vm.$forceUpdate
/src/core/instance/lifecycle.js
/**
* 直接调用 watcher.update 方法,迫使组件重新渲染。
* 它仅仅影响实例本身和插入插槽内容的子组件,而不是所有子组件
*/
Vue.prototype.$forceUpdate = function () {
const vm: Component = this
if (vm._watcher) {
vm._watcher.update()
}
}
vm.$destroy
/src/core/instance/lifecycle.js
/**
* 完全销毁一个实例。清理它与其它实例的连接,解绑它的全部指令及事件监听器。
*/
Vue.prototype.$destroy = function () {
const vm: Component = this
if (vm._isBeingDestroyed) {
// 表示实例已经销毁
return
}
// 调用 beforeDestroy 钩子
callHook(vm, 'beforeDestroy')
// 标识实例已经销毁
vm._isBeingDestroyed = true
// 把自己从老爹($parent)的肚子里($children)移除
const parent = vm.$parent
if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) {
remove(parent.$children, vm)
}
// 移除依赖监听
if (vm._watcher) {
vm._watcher.teardown()
}
let i = vm._watchers.length
while (i--) {
vm._watchers[i].teardown()
}
// remove reference from data ob
// frozen object may not have observer.
if (vm._data.__ob__) {
vm._data.__ob__.vmCount--
}
// call the last hook...
vm._isDestroyed = true
// 调用 __patch__,销毁节点
vm.__patch__(vm._vnode, null)
// 调用 destroyed 钩子
callHook(vm, 'destroyed')
// 关闭实例的所有事件监听
vm.$off()
// remove __vue__ reference
if (vm.$el) {
vm.$el.__vue__ = null
}
// release circular reference (#6759)
if (vm.$vnode) {
vm.$vnode.parent = null
}
}
vm.$nextTick
/src/core/instance/render.js
Vue.prototype.$nextTick = function (fn: Function) {
return nextTick(fn, this)
}
nextTick
/src/core/util/next-tick.js
const callbacks = []
/**
* 完成两件事:
* 1、用 try catch 包装 flushSchedulerQueue 函数,然后将其放入 callbacks 数组
* 2、如果 pending 为 false,表示现在浏览器的任务队列中没有 flushCallbacks 函数
* 如果 pending 为 true,则表示浏览器的任务队列中已经被放入了 flushCallbacks 函数,
* 待执行 flushCallbacks 函数时,pending 会被再次置为 false,表示下一个 flushCallbacks 函数可以进入
* 浏览器的任务队列了
* pending 的作用:保证在同一时刻,浏览器的任务队列中只有一个 flushCallbacks 函数
* @param {*} cb 接收一个回调函数 => flushSchedulerQueue
* @param {*} ctx 上下文
* @returns
*/
export function nextTick (cb?: Function, ctx?: Object) {
let _resolve
// 用 callbacks 数组存储经过包装的 cb 函数
callbacks.push(() => {
if (cb) {
// 用 try catch 包装回调函数,便于错误捕获
try {
cb.call(ctx)
} catch (e) {
handleError(e, ctx, 'nextTick')
}
} else if (_resolve) {
_resolve(ctx)
}
})
if (!pending) {
pending = true
// 执行 timerFunc,在浏览器的任务队列中(首选微任务队列)放入 flushCallbacks 函数
timerFunc()
}
// $flow-disable-line
if (!cb && typeof Promise !== 'undefined') {
return new Promise(resolve => {
_resolve = resolve
})
}
}
vm._render
/src/core/instance/render.js
/**
* 通过执行 render 函数生成 VNode
* 不过里面加了大量的异常处理代码
*/
Vue.prototype._render = function (): VNode {
const vm: Component = this
const { render, _parentVnode } = vm.$options
if (_parentVnode) {
vm.$scopedSlots = normalizeScopedSlots(
_parentVnode.data.scopedSlots,
vm.$slots,
vm.$scopedSlots
)
}
// 设置父 vnode。这使得渲染函数可以访问占位符节点上的数据。
vm.$vnode = _parentVnode
// render self
let vnode
try {
currentRenderingInstance = vm
// 执行 render 函数,生成 vnode
vnode = render.call(vm._renderProxy, vm.$createElement)
} catch (e) {
handleError(e, vm, `render`)
// 到这儿,说明执行 render 函数时出错了
// 开发环境渲染错误信息,生产环境返回之前的 vnode,以防止渲染错误导致组件空白
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production' && vm.$options.renderError) {
try {
vnode = vm.$options.renderError.call(vm._renderProxy, vm.$createElement, e)
} catch (e) {
handleError(e, vm, `renderError`)
vnode = vm._vnode
}
} else {
vnode = vm._vnode
}
} finally {
currentRenderingInstance = null
}
// 如果返回的 vnode 是数组,并且只包含了一个元素,则直接打平
if (Array.isArray(vnode) && vnode.length === 1) {
vnode = vnode[0]
}
// render 函数出错时,返回一个空的 vnode
if (!(vnode instanceof VNode)) {
if (process.env.NODE_ENV !== 'production' && Array.isArray(vnode)) {
warn(
'Multiple root nodes returned from render function. Render function ' +
'should return a single root node.',
vm
)
}
vnode = createEmptyVNode()
}
// set parent
vnode.parent = _parentVnode
return vnode
}
installRenderHelpers
src/core/instance/render-helpers/index.js
Этот метод отвечает за установку в экземпляр большого количества сокращенных инструментальных функций, связанных с рендерингом. функция (vm._c), но она здесь не объявлена, она объявлена в функции initRender.
Метод installRenderHelpers вызывается в renderMixin.
/**
* 在实例上挂载简写的渲染工具函数
* @param {*} target Vue 实例
*/
export function installRenderHelpers (target: any) {
target._o = markOnce
target._n = toNumber
target._s = toString
target._l = renderList
target._t = renderSlot
target._q = looseEqual
target._i = looseIndexOf
target._m = renderStatic
target._f = resolveFilter
target._k = checkKeyCodes
target._b = bindObjectProps
target._v = createTextVNode
target._e = createEmptyVNode
target._u = resolveScopedSlots
target._g = bindObjectListeners
target._d = bindDynamicKeys
target._p = prependModifier
}
Если вам интересен какой-то метод, вы можете изучить его самостоятельно.
Суммировать
-
интервьюер спросил: что делает vm.$set(obj, key, val)?
отвечать:
vm.$set используется для добавления нового свойства к реактивному объекту, чтобы убедиться, что это новое свойство также является реактивным, и запустить обновление представления. Потому что Vue не может обнаружить новое свойство объекта или добавить элемент в массив по индексу, например:
this.obj.newProperty = 'val'
,this.arr[3] = 'val'
. Итак, вот vm.$set, псевдоним Vue.set.-
Добавьте новые реагирующие данные в объект: вызовите метод defineReactive, чтобы добавить реагирующие данные в объект, затем выполните dep.notify для уведомления о зависимости и обновите представление.
-
Добавьте новые реактивные данные в массив: через метод splice
-
-
интервьюер спросил: что делает vm.$delete(obj, key)?
отвечать:
vm.$delete используется для удаления свойств объекта. Если объект реактивен, он гарантированно вызовет обновление представления. Этот метод в основном используется, чтобы избежать ситуаций, когда Vue не может обнаружить удаляемое свойство. Это псевдоним для Vue.delete.
-
Удалить элемент указанного нижнего индекса массива, что выполняется внутри метода splice.
-
Чтобы удалить указанное свойство объекта, сначала удалите свойство с помощью оператора удаления, затем выполните dep.notify для уведомления о зависимости и обновите представление.
-
-
интервьюер спросил: что делает vm.$watch(expOrFn, callback, [options])?
отвечать:
vm.$watch отвечает за просмотр изменений выражения или результата вычисления функции в экземпляре Vue. При его изменении выполняется функция обратного вызова, и функции обратного вызова передаются два параметра, первый — это новое значение после обновления, а второй — старое значение.
нужно здесьУведомлениеДело в том, что если вы наблюдаете за объектом, таким как массив, когда вы добавляете элемент в массив с помощью метода массива, такого как push, новое значение, передаваемое при запуске функции обратного вызова, совпадает со старым значением. , потому что они указывают на одну и ту же ссылку, поэтому вам нужно обратить внимание, наблюдая за объектом и оценивая, равны ли старое и новое значения в функции обратного вызова.
В.М.. Первый параметр $ Watch просто принимает данные критического пути отклика, для более сложных выражений рекомендуется функция первого параметра.
Что касается внутренностей vm.$watch :
-
Установите options.user = true, флаг является наблюдателем за пользователями.
-
Создайте экземпляр Watcher, когда будет обнаружено обновление данных, инициируйте выполнение функции обратного вызова через наблюдателя и передайте старые и новые значения в качестве параметров функции обратного вызова.
-
Возвращает функцию unwatch для отмены просмотра
-
-
интервьюер спросил: что делает vm.$on(event, callback)?
отвечать:
Слушайте пользовательские события в текущем экземпляре. События могут быть вызваны vm.$emit. Функция обратного вызова получит все дополнительные параметры, переданные в функцию запуска события (vm.$emit).
Принцип vm.$on очень прост: обработка переданных параметров события и обратного вызова и сохранение зарегистрированных событий и функций обратного вызова в объекте vm._event в виде пар ключ-значение, vm._events = {имя_события: [cb1, cb2, ...], ...}.
-
интервьюер спросилЧто делает :vm.$emit(eventName, [...args])?
отвечать:
Запускает указанное событие в текущем экземпляре, и в функцию обратного вызова события передаются дополнительные параметры.
Его внутренний принцип заключается в выполнении
vm._events[eventName]
Все функции обратного вызова в .Примечание: отПринцип реализации emit также можно увидеть, пользовательское событие компонента — это на самом деле, кто запускает, кто слушает, поэтому я оглянусь назад на этот момент.Интерпретация исходного кода Vue (2) — процесс инициализации VueОбъяснение initEvent в статье позволит понять, о чем вы говорите, потому что обработка пользовательских событий компонента использует vm.испускают.
-
интервьюер спросил: что делает vm.$off([событие, обратный вызов])?
отвечать:
Чтобы удалить пользовательский прослушиватель событий, удалите связанные данные в объекте vm._events.
-
Если аргумент не указан, удаляются все прослушиватели событий для экземпляра.
-
Если указан только параметр события, удаляются все прослушиватели события в экземпляре.
-
Если указаны оба параметра, удалите прослушиватель, соответствующий событию в экземпляре.
-
-
интервьюер спросил: что делает vm.$once(event, callback)?
отвечать:
Прислушивайтесь к пользовательскому событию, но оно будет запущено только один раз. Слушатель будет удален после срабатывания.
Его внутренний принцип реализации:
-
Обертывает функцию обратного вызова, переданную пользователем. Когда функция-оболочка выполняется, она выполняется в дополнение к пользовательской функции обратного вызова.
vm.$off(event, 包装函数)
удалить событие -
использовать
vm.$on(event, 包装函数)
Проблема с регистрацией
-
-
интервьюер спросил: что делает vm._update(vnode, Hydrating)?
отвечать:
Официальная документация не объясняет этот API.Это экземплярный метод, используемый внутри исходного кода.Он отвечает за обновление страницы и является точкой входа для рендеринга страницы.Он определяет, является ли это первым рендерингом или страница обновляется в соответствии с к тому, есть ли prevVnode, чтобы вызывалась функция __patch__, передайте разные параметры. Этот метод не будет использоваться в развитии бизнеса.
-
интервьюер спросил: что делает vm.$forceUpdate()?
отвечать:
Принуждает экземпляр Vue к повторному рендерингу, это влияет только на сам экземпляр компонента и дочерние компоненты, которые вставляют содержимое слота, а не на все дочерние компоненты. Его внутренний принцип также прост, то есть напрямую вызывать
vm._watcher.update()
, этоwatcher.update()
метод, выполните этот метод, чтобы запустить обновление компонента.
-
интервьюер спросил: что делает vm.$destroy()?
отвечать:
Отвечает за полное уничтожение экземпляра. Очистите его соединения с другими экземплярами и отмените привязку всех его директив и прослушивателей событий. будет вызываться во время выполнения
beforeDestroy
а такжеdestroy
Две функции хука. Этот метод не используется в большинстве сценариев развития бизнеса и обычно управляется с помощью инструкции v-if. Его внутренний принцип:-
Вызов функции ловушки beforeDestroy
-
Удалите себя из чрева отца ($parent), тем самым разрушив отношения с отцом
-
Удалить наблюдателей зависимостей через watcher.teardown()
-
Уничтожить узел с помощью метода vm.__patch__(vnode, null)
-
Вызов функции уничтоженного хука
-
пройти через
vm.$off
метод удаления всех прослушивателей событий
-
-
интервьюер спросил: что делает vm.$nextTick(cb)?
отвечать:
vm.$nextTick — это псевдоним Vue.nextTick, его функция заключается в задержке выполнения функции обратного вызова cb, обычно используемой для
this.key = newVal
После изменения данных я хочу немедленно получить измененные данные DOM:this.key = 'new val' Vue.nextTick(function() { // DOM 更新了 })
Его внутренний процесс выполнения:
-
this.key = 'new val'
, запустить обновление уведомления о зависимости и поместить наблюдателя, ответственного за обновление, в очередь наблюдателя. -
Поместите функцию, которая очищает очередь наблюдателя, в массив обратных вызовов.
-
Поместите функцию, которая обновляет массив обратных вызовов, в очередь асинхронных задач браузера.
-
vm.$nextTick(cb)Чтобы сократить очередь, поместите функцию cb прямо в массив callbacks.
-
Выполнить функцию, которая обновляет массив обратных вызовов в какой-то момент в будущем.
-
Затем выполните множество функций в массиве обратных вызовов, инициируйте выполнение watcher.run и обновите DOM.
-
Поскольку функция cb размещается позже в массиве callbacks, это гарантирует, что сначала завершится обновление DOM, а затем будет выполнена функция cb.
-
-
интервьюер спросил: что делает vm._render?
отвечать:
В официальной документации этот метод не описан, это экземплярный метод, используемый внутри исходного кода и отвечающий за генерацию vnodes. Код ключа — всего одна строка, которая выполняет функцию рендеринга для генерации vnode. Но это добавляет много кода обработки исключений.
Сопутствующее видео
Интерпретация исходного кода Vue (6) — метод экземпляра
Поиск внимания
Приветствую всех, чтобы следовать за мнойСчет наггетса такжеСтанция Б, если контент полезен для вас, ставьте лайк, добавляйте в избранное + подписывайтесь
Связь
-
Интерпретация исходного кода Vue (2) — процесс инициализации Vue
-
Интерпретация исходного кода Vue (4) — асинхронное обновление
-
Интерпретация исходного кода Vue (9) — оптимизация компилятора
-
Интерпретация исходного кода Vue (10) — Функция рендеринга, сгенерированная компилятором