errorCaptured三个钩子函数

发布时间:2025-06-24 20:01:22  作者:北方职教升学中心  阅读量:475


小米、

  • 载入前/后:在 beforeMount 阶段,vue 实例的$el 和 data 都初始化了,但还是挂载之前为虚拟的 dom 节点,data.message 还未替换。

    Vue.use(Vuex); // 1. vue的插件机制,安装vuex

    let store = new Vuex.Store({ // 2.实例化store,调用install方法

    state,

    getters,

    modules,

    mutations,

    actions,

    plugins

    });

    new Vue({ // 3.注入store, 挂载vue实例

    store,

    render: h=>h(app)

    }).$mount(‘#app’);

    Vue.use是vue中的插件机制,内部会调用插件的install方法,vuex的install方法:

    export function install (_Vue) {

    if (Vue) {

    if (process.env.NODE_ENV !== ‘production’) {

    console.error(

    ‘[vuex] already installed. Vue.use(Vuex) should be called only once.’

    )

    }

    return

    }

    /保存Vue,同时用于检测是否重复安装/

    Vue = _Vue

    /将vuexInit混淆进Vue的beforeCreate(Vue2.0)或_init方法(Vue1.0)/

    applyMixin(Vue)

    }

    vuex是个全局的状态管理,全局有且只能有一个store实例,所以在install的时候会判断是否已经安装过了,这个就是单例模式,确保一个类只有一个实例。360、特别强调,项目经历不可忽视;几乎简历上提到的项目都会被刨根问底,所以项目应用的技术要熟练,底层原理必须清楚。

    6. vuex 状态管理实现通信

    vuex是专为vue设计的状态管理模式。

    12.虚拟dom和diff算法


    虚拟DOM是对DOM的描述,用对象属性来描述节点,本质上是JavaScript对象。

    13.Vuex原理


    vuex是什么,先看下官方的原话:

    Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。destroyed

    这两个钩子是vue实例销毁的钩子,定义在Vue.prototype.$destroy中:

    Vue.prototype.$destroy = function () {

    const vm: Component = this

    if (vm._isBeingDestroyed) {

    return

    }

    callHook(vm, ‘beforeDestroy’)

    vm._isBeingDestroyed = true

    // remove self from parent

    const parent = vm.$parent

    if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) {

    remove(parent.$children, vm)

    }

    // teardown watchers

    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

    // invoke destroy hooks on current rendered tree

    vm.patch(vm._vnode, null)

    // fire destroyed hook

    callHook(vm, ‘destroyed’)

    // turn off all instance listeners.

    vm.$off()

    // remove vuereference

    if (vm.$el) {

    vm.$el.vue= null

    }

    if (vm.$vnode) {

    vm.$vnode.parent = null

    }

    }

    }

    在销毁之前执行callHook(vm, ‘beforeDestroy’),然后销毁的时候做了几件事:

    • 如果有父元素,将父元素的$children中把该组件实例移除。并且通过remove(keys, key),将当前的key从keys中删除再重新keys.push(key),这样就改变了当前key在keys中的位置。mounted

      3. beforeUpdate、看下callHook函数:

      function callHook (vm: Component, hook: string) {

      pushTarget()

      const handlers = vm.$options[hook]

      const info = ${hook} hook

      if (handlers) {

      for (let i = 0, j = handlers.length; i < j; i++) {

      invokeWithErrorHandling(handlers[i], vm, null, vm, info)

      }

      }

      if (vm._hasHookEvent) {

      vm.$emit(‘hook:’ + hook)

      }

      popTarget()

      }

      接收一个vm组件实例的参数和hook,取组件实例的$options传入的hook属性值,有的话会循环调用这个钩子的回调函数。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化

    这段话可以得出几个结论:Vuex是为vue.js服务的,而像redux与react是解耦的,然后vuex是状态管理模式,所有的状态以一种可预测的方式发生变化。要注意的是官网有这一段话:

    提示:provide 和 inject 绑定并不是可响应的。特别强调,项目经历不可忽视;几乎简历上提到的项目都会被刨根问底,所以项目应用的技术要熟练,底层原理必须清楚。接下来会将这个name通过include与exclude属性进行匹配,匹配不成功(说明不需要进行缓存)则不进行任何操作直接返回vnode(vnode节点描述对象,vue通过vnode创建真实的DOM)

    包含了腾讯、搜狐等一线互联网公司面试被问到的题目,涵盖了初中级前端技术点。运行、实际上在很多高级组件中都可以看到组件会将this通过provide传递给子孙组件,包括element-ui、listeners

    • $attrs: 包含了父作用域没被props声明绑定的数据,组件可以通过v-bind="$attrs"继续传给子组件

    • $listernes: 包含了父作用域中的v-on(不含 .native 修饰器的) 监听事件,可以通过v-on="$listeners"传入内部组件

    5. provide、props等属性。涵盖HTML,CSS,JavaScript,HTTP,TCP协议,浏览器,Vue框架,算法等高频考点238道(含答案)

    开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

    资料截图 :

    高级前端工程师必备资料包

    const silent = Vue.config.silent

    Vue.config.silent = true

    store._vm = new Vue({

    data: {

    $$state: state

    },

    computed

    })

    Vue.config.silent = silent

    // enable strict mode for new vm

    if (store.strict) {

    enableStrictMode(store)

    }

    if (oldVm) {

    if (hot) {

    // dispatch changes in all subscribed watchers

    // to force getter re-evaluation for hot reloading.

    store._withCommit(() => {

    oldVm._data.$$state = null

    })

    }

    Vue.nextTick(() => oldVm.$destroy())

    }

    }

    resetStoreVM首先会遍历wrappedGetters,使用Object.defineProperty方法对store.getters的每一个getter定义get方法,这样访问this.$store.getter.test就等同于访问store._vm.test。

    最后

    =============================================================

    在面试前花了三个月时间刷了很多大厂面试题,最近做了一个整理并分类,主要内容包括html,css,JavaScript,ES6,计算机网络,浏览器,工程化,模块化,Node.js,框架,数据结构,性能优化,项目等等。在挂载完成后监听include和exclude,动态地销毁已经不满足include的组件和满足exclude的组件实例:

    created () {

    this.cache = Object.create(null) // 存储需要缓存的组件

    this.keys = [] // 存储每个需要缓存的组件的key,即对应this.cache对象中的键值

    },

    // 销毁keep-alive组件的时候,对缓存中的每个组件执行销毁

    destroyed () {

    for (const key in this.cache) {

    pruneCacheEntry(this.cache, key, this.keys)

    }

    },

    mounted () {

    this.$watch(‘include’, val => {

    pruneCache(this, name => matches(val, name))

    })

    this.$watch(‘exclude’, val => {

    pruneCache(this, name => !matches(val, name))

    })

    },

    接下来是render函数:

    render () {

    const slot = this.$slots.default

    const vnode: VNode = getFirstComponentChild(slot)

    // 如果vnode存在就取vnode的选项

    const componentOptions: VNodeComponentOptions = vnode && vnode.componentOptions

    if (componentOptions) {

    // check pattern

    //获取第一个有效组件的name

    const name: string = getComponentName(componentOptions)

    const { include, exclude } = this

    if (

    // not included

    (include && (!name || !matches(include, name))) ||

    // excluded

    (exclude && name && matches(exclude, name))

    ) {

    return vnode// 说明不用缓存,直接返回这个组件进行渲染

    }

    // 匹配到了,开始缓存操作

    const { cache, keys } = this // keep-alive组件的缓存组件和缓存组件对应的key

    // 获取第一个有效组件的key

    const key: string = vnode.key == null

    // same constructor may get registered as different local components

    // so cid alone is not enough (#3269)

    componentOptions.Ctor.cid + (componentOptions.tag ::${componentOptions.tag}: ‘’)

    vnode.key

    if (cache[key]) {

    // 这个组件的实例用缓存中的组件实例替换

    vnode.componentInstance = cache[key].componentInstance

    // make current key freshest

    // 更新当前key在keys中的位置

    remove(keys, key)

    keys.push(key)

    } else {

    cache[key] = vnode

    keys.push(key)

    // prune oldest entry

    // 如果缓存中的组件个数超过传入的max,销毁缓存中的LRU组件

    // LRU: least recently used 最近最少用,缓存淘汰策略

    if (this.max && keys.length > parseInt(this.max)) {

    pruneCacheEntry(cache, keys[0], keys, this._vnode)

    }

    }

    vnode.data.keepAlive = true

    }

    // 若第一个有效的组件存在,但其componentOptions不存在,就返回这个组件进行渲染

    // 或若也不存在有效的第一个组件,但keep-alive组件的默认插槽存在,就返回默认插槽的第一个组件进行渲染

    return vnode || (slot && slot[0])

    }

    代码做了详细的注释,这里再分析下render做了什么。拼多多、滴滴、原生应用、

    这里给大家提供一份汇集各大厂面试高频核心考点前端学习资料。编译模板、created:

    Vue.prototype._init = function (options: Object) {

    initLifecycle(vm)

    initEvents(vm)

    initRender(vm)

    callHook(vm, ‘beforeCreate’)

    initInjections(vm) // resolve injections before data/props

    initState(vm)

    initProvide(vm) // resolve provide after data/props

    callHook(vm, ‘created’)

    if (vm.$options.el) {

    vm. m o u n t ( v m . mount(vm. mount(vm.options.el)

    }

    }

    在执行beforeCreate之前调用了 initLifecycle、

    所以Vue不会对provide中的变量进行响应式处理。

  • 销毁前/后:在执行 destroy 方法后,对 data 的改变不会再触发周期函数,说明此时 vue 实例已经解除了事件监听以及和 dom 的绑定,但是 dom 结构依然存在

  • 结合源码再理解,在源码中生命周期钩子是用callHook函数调用的。Redux,将数据存放到全局的store,再将store挂载到每个vue实例组件中,利用Vue.js的细粒度数据响应机制来进行高效的状态更新。

    store实现的源码在src/store.js,其中最核心的是响应式的实现,通过resetStoreVM(this, state)调用,看下这个方法:

    function resetStoreVM (store, state, hot) {

    const oldVm = store._vm

    // bind store public getters

    store.getters = {}

    // reset local getters cache

    store._makeLocalGettersCache = Object.create(null)

    const wrappedGetters = store._wrappedGetters

    const computed = {}

    forEachValue(wrappedGetters, (fn, key) => {

    // use computed to leverage its lazy-caching mechanism

    // direct inline function use will lead to closure preserving oldVm.

    // using partial to return function with only arguments preserved in closure environment.

    computed[key] = partial(fn, store)

    Object.defineProperty(store.getters, key, {

    get: () => store._vm[key],

    enumerable: true // for local getters

    })

    })

    // use a Vue instance to store the state tree

    // suppress warnings just in case the user has added

    // some funky global mixins

    const silent = Vue.config.silent

    Vue.config.silent = true

    store._vm = new Vue({

    data: {

    $$state: state

    },

    computed

    })

    Vue.config.silent = silent

    // enable strict mode for new vm

    if (store.strict) {

    enableStrictMode(store)

    }

    if (oldVm) {

    if (hot) {

    // dispatch changes in all subscribed watchers

    // to force getter re-evaluation for hot reloading.

    store._withCommit(() => {

    oldVm._data.$$state = null

    })

    }

    Vue.nextTick(() => oldVm.$destroy())

    }

    }

    resetStoreVM首先会遍历wrappedGetters,使用Object.defineProperty方法对store.getters的每一个getter定义get方法,这样访问this.$store.getter.test就等同于访问store._vm.test。要想 inject 接受的变量是响应式的,provide 提供的变量本身就需要是响应式的。

    8.computed、字节跳动、

    state是通过new一个Vue对象来实现数据的“响应式化”,运用Vue的data属性来实现数据与视图的同步更新,computed实现getters的计算属性。事件、

    基本上可以说8 个阶段创建前/后,载入前/后,更新前/后,销毁前/后。

    生命周期就是每个Vue实例完成初始化、inject

    父组件通过provide注入一个依赖,其所有的子孙组件可以通过inject来接收。58、

    看下组件更新的_update方法:

    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.patchis injected in entry points

    // based on the rendering backend used.

    if (!prevVnode) {

    // initial render

    vm.KaTeX parse error: Expected group after ‘_’ at position 9: el = vm.__patch__(vm.el, vnode, hydrating, false /* removeOnly */)

    } else {

    // updates

    vm.$el = vm.patch(prevVnode, vnode)

    }

    }

    vm.$el = vm._patch(),这个就是最终渲染的DOM元素,patch就是vue中diff算法的函数,在key的作用章节有提过。

    diff算法发生在视图更新的时候,也就是数据更新的时候,diff算法会将新旧虚拟DOM作对比,将变化的地方转换为DOM。涵盖HTML,CSS,JavaScript,HTTP,TCP协议,浏览器,Vue框架,算法等高频考点238道(含答案)

    开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

    资料截图 :

    高级前端工程师必备资料包

    服务端渲染等等。将实例挂载到 DOM 并在数据变化时更新 DOM 等。

    无偿分享给大家,算是一个感恩回馈吧,详细内容可以在文末自行获取哈!

    总结

    秋招即将开始,校招的朋友普遍是缺少项目经历的,所以底层逻辑,基础知识要掌握好!

    而一般的社招,更是神仙打架。

    如果没匹配到,说明没缓存过,这时候需要进行缓存,并且判断当前缓存的个数是否超过max指定的个数,如果超过,则销毁keys里的最后一个组件,并从keys中移除,这个就是LRU(Least Recently Used :最近最少使用)缓存淘汰算法。58、阿里、这个是为了实现max的功能,并且遵循缓存淘汰策略。errorCaptured三个钩子函数。销毁的一系列动作的钩子。

    在vue中template最终会转成render函数,而render函数最终是执行的createElement,生成vnode,vnode正是 vue中用来表示虚拟DOM的类,看下vnode:

    class VNode {

    tag: string | void;

    data: VNodeData | void;

    children: Array;

    text: string | void;

    elm: Node | void;

    ns: string | void;

    context: Component | void; // rendered in this component’s scope

    key: string | number | void;

    componentOptions: VNodeComponentOptions | void;

    componentInstance: Component | void; // component instance

    parent: VNode | void; // component placeholder node

    // strictly internal

    raw: boolean; // contains raw HTML (server only)

    isStatic: boolean; // hoisted static node

    isRootInsert: boolean; // necessary for enter transition check

    isComment: boolean; // empty comment placeholder

    isCloned: boolean; // is a cloned node

    isOnce: boolean; // is a v-once node

    asyncFactory: Function | void; // async component factory function

    asyncMeta: Object | void;

    isAsyncPlaceholder: boolean;

    ssrContext: Object | void;

    fnContext: Component | void; // real context vm for functional nodes

    fnOptions: ComponentOptions; // for SSR caching

    devtoolsMeta: Object; // used to store functional render context for devtools

    fnScopeId: string; // functional scope id support

    constructor (

    tag: string,

    data: VNodeData,

    children: Array,

    text: string,

    elm: Node,

    context: Component,

    componentOptions: VNodeComponentOptions,

    asyncFactory: Function

    ) {

    this.tag = tag

    this.data = data

    this.children = children

    this.text = text

    this.elm = elm

    this.ns = undefined

    this.context = context

    this.fnContext = undefined

    this.fnOptions = undefined

    this.fnScopeId = undefined

    this.key = data && data.key

    this.componentOptions = componentOptions

    this.componentInstance = undefined

    this.parent = undefined

    this.raw = false

    this.isStatic = false

    this.isRootInsert = true

    this.isComment = false

    this.isCloned = false

    this.isOnce = false

    this.asyncFactory = asyncFactory

    this.asyncMeta = undefined

    this.isAsyncPlaceholder = false

    }

    // DEPRECATED: alias for componentInstance for backwards compat.

    /* istanbul ignore next */

    get child (): Component | void {

    return this.componentInstance

    }

    }

    看下其中关键的几个属性:

    • tag: 当前节点的标签名

    • data: 表示节点上的class,attribute,style以及绑定的事件

    • children: 当前节点的子节点,是一个数组

    • text: 当前节点的文本

    • elm: 当前虚拟节点对应的真实dom节点

    • key: 节点的key属性,被当作节点的标志,用以优化

    • componentOptions: 组件的option选项

    • componentInstance: 当前节点对应的组件的实例

    • parent: 当前节点的父节点

    • isStatic: 是否为静态节点

    children和parent是指当前的vnode的子节点和父节点,这样一个个vnode就形成了DOM树。

    */

    if (isTrue(vnode.isStatic) &&

    isTrue(oldVnode.isStatic) &&

    vnode.key === oldVnode.key &&

    (isTrue(vnode.isCloned) || isTrue(vnode.isOnce))

    ) {

    vnode.componentInstance = oldVnode.componentInstance

    return

    }

    let i

    const data = vnode.data

    if (isDef(data) && isDef(i = data.hook) && isDef(i = i.prepatch)) {

    i(oldVnode, vnode)

    }

    const oldCh = oldVnode.children

    const ch = vnode.children

    if (isDef(data) && isPatchable(vnode)) {

    for (i = 0; i < cbs.update.length; ++i) cbs.update[i](oldVnode, vnode)

    if (isDef(i = data.hook) && isDef(i = i.update)) i(oldVnode, vnode)

    }

    /如果这个VNode节点没有text文本时/

    if (isUndef(vnode.text)) {

    if (isDef(oldCh) && isDef(ch)) {

    // 两个vnode都定义了子节点,并且不相同,就对子节点进行diff

    if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly)

    } else if (isDef(ch)) {

    // 如果只有新的vnode定义了子节点,则进行添加子节点的操作

    if (process.env.NODE_ENV !== ‘production’) {

    checkDuplicateKeys(ch)

    }

    if (isDef(oldVnode.text)) nodeOps.setTextContent(elm, ‘’)

    addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue)

    } else if (isDef(oldCh)) {

    // 如果只有旧的vnode定义了子节点,则进行删除子节点的操作

    removeVnodes(oldCh, 0, oldCh.length - 1)

    } else if (isDef(oldVnode.text)) {

    nodeOps.setTextContent(elm, ‘’)

    }

    } else if (oldVnode.text !== vnode.text) {

    nodeOps.setTextContent(elm, vnode.text)

    }

    if (isDef(data)) {

    if (isDef(i = data.hook) && isDef(i = i.postpatch)) i(oldVnode, vnode)

    }

    }

    通过代码可知,patchVnode分为多种情况,分析下子节点的diff过程 (oldCh 为 oldVnode的子节点,ch 为 Vnode的子节点)

    1. oldCh、initRender函数,所以beforeCreate是在初始化生命周期、在第一次install的时候会applyMixin,applyMixin是/src/mixin导入的方法:

      function (Vue) {

      const version = Number(Vue.version.split(‘.’)[0])

      if (version >= 2) {

      Vue.mixin({ beforeCreate: vuexInit })

      } else {

      // override init and inject vuex init procedure

      // for 1.x backwards compatibility.

      const _init = Vue.prototype._init

      Vue.prototype._init = function (options = {}) {

      options.init = options.init

      [vuexInit].concat(options.init)

      vuexInit

      _init.call(this, options)

      }

      }

      /

      **
      • Vuex init hook, injected into each instances init hooks list.

      */

      function vuexInit () {

      const options = this.$options

      // store injection

      if (options.store) {

      this.$store = typeof options.store === ‘function’

      options.store()

      options.store

      } else if (options.parent && options.parent.$store) {

      this. s t o r e = o p t i o n s . p a r e n t . store = options.parent. store=options.parent.store

      }

      }

      }

      先是判断下vue的版本,这边分析vue2的逻辑。美团、美团、

      最后返回vnode或者默认插槽的第一个组件进行DOM渲染。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。在调用生命钩子的回调函数之前会临时pushTarget一个null值,也就是将Dep.target置为空来禁止在执行生命钩子的时候进行依赖收集。

      keep-alive是一个组件,跟其他组件一样有生命周期和render函数,keep-alive包裹的分析keep-alive就是分析一个组件。搜狐等一线互联网公司面试被问到的题目,涵盖了初中级前端技术点。如果需要在某个数据变化时做一些事情,使用watch。360、

      1. beforeCreate、

      1. 代码可维护性更高

      通过虚拟 DOM 的抽象能力,可以用声明式写 UI 的方式,大大提高了我们的工作效率。新浪、provide、

      当oldCh 或者 newCh 遍历完后(遍历完的条件就是 oldCh 或者 newCh 的 startIndex >= endIndex ),说明剩下的节点为新增或者删除,这时候停止oldCh 和 newCh 的 diff。

      2. beforeMount、deactivated、deactivated这两个钩子函数分别是在keep-alive 组件激活和停用之后的回调。新浪、滴滴、

      接下来看每个生命钩子具体调用的时机。渲染函数之后的生命周期。字节跳动、

      patch

      看下patch代码(部分):

      function patch (oldVnode, vnode, hydrating, removeOnly, parentElm, refElm) {

      /vnode不存在则直接调用销毁钩子/

      if (isUndef(vnode)) {

      if (isDef(oldVnode)) invokeDestroyHook(oldVnode)

      return

      }

      let isInitialPatch = false

      const insertedVnodeQueue = []

      if (isUndef(oldVnode)) {

      // empty mount (likely as component), create new root element

      isInitialPatch = true

      createElm(vnode, insertedVnodeQueue, parentElm, refElm)

      } else {

      /标记旧的VNode是否有nodeType/

      /Github:https://github.com/answershuto/

      const isRealElement = isDef(oldVnode.nodeType)

      if (!isRealElement && sameVnode(oldVnode, vnode)) {

      // patch existing root node

      /是同一个节点的时候直接修改现有的节点/

      patchVnode(oldVnode, vnode, insertedVnodeQueue, removeOnly)

      return vnode.elm

      首先是判断是否有新的vnode,没有代表是要销毁旧的vnode,调用销毁组件的钩子。只有相同的节点才会进行diff算法!!!

      patchVnode

      function patchVnode (

      oldVnode,

      vnode,

      insertedVnodeQueue,

      ownerArray,

      index,

      removeOnly

      ) {

      // 两个vnode相同,说明不需要diff,直接返回

      if (oldVnode === vnode) {

      return

      }

      // 如果传入了ownerArray和index,可以进行重用vnode,updateChildren里用来替换位置

      if (isDef(vnode.elm) && isDef(ownerArray)) {

      // clone reused vnode

      vnode = ownerArray[index] = cloneVNode(vnode)

      }

      const elm = vnode.elm = oldVnode.elm

      // 如果oldVnode的isAsyncPlaceholder属性为true时,跳过检查异步组件,return

      if (isTrue(oldVnode.isAsyncPlaceholder)) {

      if (isDef(vnode.asyncFactory.resolved)) {

      hydrate(oldVnode.elm, vnode, insertedVnodeQueue)

      } else {

      vnode.isAsyncPlaceholder = true

      }

      return

      }

      /

      *

      如果新旧VNode都是静态的,同时它们的key相同(代表同一节点),

      并且新的VNode是clone或者是标记了once(标记v-once属性,只渲染一次),

      那么只需要替换elm以及componentInstance即可。props、这是刻意为之的。method有什么区别


      computed:有缓存,有对应的watcher,watcher有个lazy为true的属性,表示只有在模板里去读取它的值后才会计算,并且这watcher在初始化的时候会赋值dirty为true,watcher只有dirty为true的时候才会重新求值,重新求值后会将dirty置为false,false会直接返回watcher的value,只有下次watcher的响应式依赖有更新的时候,会将watcher的dirty再置为false,这时候才会重新求值,这样就实现了computed的缓存。

      errorCaptured捕获到当子孙组件错误时会被调用,在源码中可以经常看到try catch中catch会调用handleError函数,handleError会向组件所有的父级组件抛出异常,

      function handleError (err: Error, vm: any, info: string) {

      pushTarget()

      try {

      if (vm) {

      let cur = vm

      while ((cur = cur.$parent)) {

      const hooks = cur.$options.errorCaptured

      if (hooks) {

      for (let i = 0; i < hooks.length; i++) {

      try {

      const capture = hooks[i].call(cur, err, vm, info) === false

      if (capture) return

      } catch (e) {

      globalHandleError(e, cur, ‘errorCaptured hook’)

      }

      }

      }

      }

      }

      globalHandleError(err, vm, info)

      } finally {

      popTarget()

      }

      }

      分析完源码再一下官网图示,会更清楚:

      10.keep-aliva原理


      keep-alive是Vue.js的一个内置组件。methods 和 computed 的区别
      10.vue 中怎么重置 data
      11.组件中写 name 选项有什么作用?
      12.vue-router 有哪些钩子函数
      13.route 和 router 的区别是什么?
      14.说一下 Vue 和 React 的认识,做一个简单的对比
      15.Vue 的 nextTick 的原理是什么?
      16.Vuex 有哪几种属性
      17.vue 首屏加载优化
      18.Vue 3.0 有没有过了解?
      19.vue-cli 替我们做了哪些工作?

      如果你觉得对你有帮助,可以戳这里获取:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

      return vm

      }

      Vue.prototype.$emit = function (event: string): Component {

      const vm: Component = this

      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

      }

      4. attrs、

      Vue 面试题

      1.Vue 双向绑定原理
      2.描述下 vue 从初始化页面–修改数据–刷新页面 UI 的过程?
      3.你是如何理解 Vue 的响应式系统的
      4.虚拟 DOM 实现原理
      5.既然 Vue 通过数据劫持可以精准探测数据变化,为什么还需要虚拟 DOM 进行 diff 检测差异
      6.Vue 中 key 值的作用?
      7.Vue 的生命周期
      8.Vue 组件间通信有哪些方式
      9.watch、

    2. 移除watchers,并在依赖订阅者中移除自己。

      设计思想:

      Vuex的设计思想,借鉴了Flux、

      包含了腾讯、watch、Node、

      在执行created之前调用了initInjections、否则将vnode存储在cache中。然后在/src/core/observer/scheduler.js下的flushSchedulerQueue()函数中渲染DOM,flushSchedulerQueue会刷新watcher队列并执行,执行完所有watcher的run方法之后(run方法就是watcher进行dom diff并更新DOM的方法),再调用callHook(vm, ‘updated’),代码如下:

      /

      **
      • Flush both queues and run the watchers.

      */

      function flushSchedulerQueue () {

      for (index = 0; index < queue.length; index++) {

      watcher = queue[index]

      if (watcher.before) {

      watcher.before()

      }

      watcher.run()

      }

      callUpdatedHooks(updatedQueue)

      }

      function callUpdatedHooks (queue) {

      let i = queue.length

      while (i–) {

      const watcher = queue[i]

      const vm = watcher.vm

      if (vm._watcher === watcher && vm._isMounted && !vm._isDestroyed) {

      callHook(vm, ‘updated’)

      }

      }

      }

      4. beforeDestroy、

      1. 提升渲染性能

      频繁变动DOM会造成浏览器的回流或者重回,而通过将大量的DOM操作搬运到Javascript中,运用patching算法来计算出真正需要更新的节点,可以减少真实DOM的操作次数,从而提高性能。deactivated

      剩下的还有activated、

      • 创建前/后: 在 beforeCreate 阶段,vue 实例的挂载元素 el 还没有。它有几个意义:

        1. 具备跨平台的优势

        由于 Virtual DOM 是以 JavaScript 对象为基础而不依赖真实平台环境,所以使它具有了跨平台的能力,比如说浏览器、

        无偿分享给大家,算是一个感恩回馈吧,详细内容可以在文末自行获取哈!

        总结

        秋招即将开始,校招的朋友普遍是缺少项目经历的,所以底层逻辑,基础知识要掌握好!

        而一般的社招,更是神仙打架。它能够将不活动的组件实例保存在内存中,而不是直接将其销毁,它是一个抽象组件,不会被渲染到真实DOM中,也不会出现在父组件链中。initEvents、

        最后

        =============================================================

        在面试前花了三个月时间刷了很多大厂面试题,最近做了一个整理并分类,主要内容包括html,css,JavaScript,ES6,计算机网络,浏览器,工程化,模块化,Node.js,框架,数据结构,性能优化,项目等等。initState、

        watch:watcher的对象每次更新都会执行函数。每个组件实例都有共同的store实例,并且store.state是响应式的,改变state唯一的办法就是通过在这个store实例上commit一个mutation,方便跟踪每一个状态的变化,实现原理在下面的vuex原理里有讲。initProvide,这时候created初始化了data、

        vm.$emit(‘hook:’ + hook)则是用来给父组件监听该组件的回调事件。watcher、patch将新旧虚拟DOM节点比较后,最终返回真实的DOM节点。

      • 更新前/后:当 data 变化时,会触发 beforeUpdate 和 updated 方法。拼多多、小程序、最终访问store.state也就是访问store._vm.state。

        接下来判断旧的vnode是否是真实的元素,而不是组件,如果是组件并且用someVnode判断新旧节点是否是相同的节点(sameVnode在key的作用章节有做解析),是进行patchVnode,这时候进行真正的新老节点的diff。

        源码再src/core/components/keep-alive,created声明了要缓存的组件对象,和存储的组件keys,keep-alive销毁的时候会用pruneCacheEntry将缓存的所有组件实例销毁,也就是调用组件实例的destroy方法。

        activated、

        然后判断是否有旧的vnode,没有代表是新增,也就是新建root节点。ch都定义了调用updateChildren再进行diff

      • 若 oldCh不存在,ch 存在,首先清空 oldVnode 的文本节点,同时调用 addVnodes 方法将 ch 添加到elm真实 dom 节点当中

      • 若 oldCh存在,ch不存在,则删除 elm 真实节点下的 oldCh 子节点

      • 若 oldVnode 有文本节点,而 vnode 没有,那么就清空这个文本节点

    updateChildren是子节点diff的函数,也是最重要的环节。在 mounted 阶段,vue 实例挂载完成,data.message 成功渲染。

    include与exclude两个属性,允许组件有条件地进行缓存,max属性确定最多缓存多少组件实例。利用Vue.mixin混入的机制,在组件实例的beforeCreate调用vuexInit方法,首先判断options是否有store,没有代表是root节点,这时候要进行store初始化,没有的话就取父组件的$store赋值,这样就实现了全局共用唯一的store实例。阿里、然而,如果你传入了一个可监听的对象,那么其对象的 property 还是可响应的。ant-design-vue等。

    原理可以从使用方式开始分析。watch 更适用于数据变化时的异步操作。

    updateChildren

    function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) {

    // 声明oldCh和newCh的头尾索引和头尾的vnode,

    let oldStartIdx = 0

    let newStartIdx = 0

    let oldEndIdx = oldCh.length - 1

    let oldStartVnode = oldCh[0]

    let oldEndVnode = oldCh[oldEndIdx]

    let newEndIdx = newCh.length - 1

    let newStartVnode = newCh[0]

    let newEndVnode = newCh[newEndIdx]

    let oldKeyToIdx, idxInOld, vnodeToMove, refElm

    const canMove = !removeOnly

    if (process.env.NODE_ENV !== ‘production’) {

    checkDuplicateKeys(newCh)

    }

    while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {

    if (isUndef(oldStartVnode)) {

    oldStartVnode = oldCh[++oldStartIdx] // Vnode has been moved left

    } else if (isUndef(oldEndVnode)) {

    oldEndVnode = oldCh[–oldEndIdx]

    // 判断两边的头是不是相同节点

    } else if (sameVnode(oldStartVnode, newStartVnode)) {

    patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)

    oldStartVnode = oldCh[++oldStartIdx]

    newStartVnode = newCh[++newStartIdx]

    // 判断尾部是不是相同节点

    } else if (sameVnode(oldEndVnode, newEndVnode)) {

    patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx)

    oldEndVnode = oldCh[–oldEndIdx]

    newEndVnode = newCh[–newEndIdx]

    // 判断旧节点头部是不是与新节点的尾部相同,相同则把头部往右移

    } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right

    patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx)

    canMove && nodeOps.insertBefore(parentElm, oldStartVnode.elm, nodeOps.nextSibling(oldEndVnode.elm))

    oldStartVnode = oldCh[++oldStartIdx]

    newEndVnode = newCh[–newEndIdx]

    // 判断旧节点尾部是不是与新节点的头部相同,相同则把头部往左移

    } else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left

    patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)

    canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm)

    oldEndVnode = oldCh[–oldEndIdx]

    newStartVnode = newCh[++newStartIdx]

    } else {

    /

    *

    生成一个key与旧VNode的key对应的哈希表

    */

    if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)

    idxInOld = isDef(newStartVnode.key)

    oldKeyToIdx[newStartVnode.key]

    findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)

    if (isUndef(idxInOld)) { // New element

    createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)

    } else {

    vnodeToMove = oldCh[idxInOld]

    if (sameVnode(vnodeToMove, newStartVnode)) {

    patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)

    oldCh[idxInOld] = undefined

    canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm)

    } else {

    // same key but different element. treat as new element

    createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)

    }

    }

    newStartVnode = newCh[++newStartIdx]

    }

    }

    // oldCh或者newCh遍历完,说明剩下的节点不是新增就是删除

    if (oldStartIdx > oldEndIdx) {

    refElm = isUndef(newCh[newEndIdx + 1]) null : newCh[newEndIdx + 1].elm

    addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue)

    } else if (newStartIdx > newEndIdx) {

    removeVnodes(oldCh, oldStartIdx, oldEndIdx)

    }

    }

    首先给startIndex和endIndex来作为遍历的索引,在遍历的时候会先判断头尾节点是否相同,没有找到相同节点后再按照通用方式遍历查找;查找结束再按情况处理剩下的节点;借助key通常可以非常精确找到相同节点。

    匹配到了就开始缓存,根据key在this.cache中查找,如果存在则说明之前已经缓存过了,直接将缓存的vnode的componentInstance(组件实例)覆盖到目前的vnode上面。最终访问store.state也就是访问store._vm.state。inject等,所以这时候就可以访问到data、

    state是通过new一个Vue对象来实现数据的“响应式化”,运用Vue的data属性来实现数据与视图的同步更新,computed实现getters的计算属性。在src/core/instance/lifecycle.js找到beforeUpdate调用的代码:

    new Watcher(vm, updateComponent, noop, {

    before () {

    if (vm._isMounted && !vm._isDestroyed) {

    callHook(vm, ‘beforeUpdate’)

    }

    }

    }, true /* isRenderWatcher */)

    _isMounted为ture的话(DOM已经被挂载)会调用callHook(vm, ‘beforeUpdate’)方法,然后会对虚拟DOM进行重新渲染。updated

    这两个钩子函数是在数据更新的时候进行回调的函数。

    method: 将方法在模板里使用,每次视图有更新都会重新执行函数,性能消耗较大。

    这里给大家提供一份汇集各大厂面试高频核心考点前端学习资料。

    通过this.$slots.default拿到插槽组件,也就是keep-alive包裹的组件,getFirstComponentChild获取第一个子组件,获取该组件的name(存在组件名则直接使用组件名,否则会使用tag)。小米、

    9.生命周期


    官网对生命周期的说明:

    每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、

    当某个数据被修改的时候,依赖对应的watcher会通知更新,执行渲染函数会生成新的vnode,vnode再去与旧的vnode进行对比更新,这就是vue中的虚拟dom diff算法触发的流程。

  • 删除数据引用

  • 5. activated、