Интерпретация исходного кода Vue (6) — метод экземпляра

JavaScript Vue.js
Интерпретация исходного кода Vue (6) — метод экземпляра

предисловие

предыдущий постИнтерпретация исходного кода 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]Все функции обратного вызова в .

    Примечание: отonа такжена иПринцип реализации emit также можно увидеть, пользовательское событие компонента — это на самом деле, кто запускает, кто слушает, поэтому я оглянусь назад на этот момент.Интерпретация исходного кода Vue (2) — процесс инициализации VueОбъяснение initEvent в статье позволит понять, о чем вы говорите, потому что обработка пользовательских событий компонента использует vm.on,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) — метод экземпляра

Поиск внимания

Приветствую всех, чтобы следовать за мнойСчет наггетса такжеСтанция Б, если контент полезен для вас, ставьте лайк, добавляйте в избранное + подписывайтесь

Связь

группа по обмену знаниями

Связь