# fed-e-task-03-02 **Repository Path**: S_L_G/fed-e-task-03-02 ## Basic Information - **Project Name**: fed-e-task-03-02 - **Description**: No description available - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-12-14 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # fed-e-task-03-02 #### 介绍 Vue.js 源码剖析-响应式原理、虚拟 DOM、模板编译和组件化 #### 1:请简述 Vue 首次渲染的过程 - Vue 初始化实例成员和静态成员 - initGlobalAPI(Vue),初始化静态成员,在 Vue 属性上添加成员 - 代码位置 src/core/global-api/index.js - 初始化 Vue.config - 初始化 Vue.util - 初始化 Vue.set,Vue.delete,Vue.nextTick - 初始化 Vue.observable - 初始化 Vue.options,生成了 components,directives,filters 三个空对象,添加\_base(Vue) - 调用 extend 方法将 builtInComponents 组件,添加到 Vue.options.components,目前只有 keep-alive 组件 - initUse(Vue),初始化 Vue.use,注入所有的插件到 installedPlugins - initMixin(Vue),初始 Vue.mixin,合并 options 的属性 - initExtend(Vue),初始化 Vue.extend,在里面创建 Sub,原型继承 Vue 的成员.基于传入的 options,返回一个组件的构造函数 - initAssetRegisters(Vue), 注册 components,directives,filters,最后存储在 options 中 - 创建了 Vue 的构造函数,初始化实例成员,在 prototype 上添加成员 - 代码位置 core/instance/index.js - initMixin(Vue),添加实例成员\_\_init() - stateMixin(Vue) - 直接在 prototype 实例方法$set,$delete,\$watch - 通过 Object.defineProperty 给 prototype $data,$props - eventsMixin(Vue),添加了$on,$once,$off,$emit4 个方法 - lifecycleMixin(Vue),添加了\_update,$forceUpdate,$destroy - renderMixin(Vue),添加了\$nextTick,\_render - installRenderHelpers(Vue.prototype),添加了\_o, \_n, \_s, \_l, \_t, \_q, \_i, \_m, \_f, \_k, \_b, \_v, \_e, \_u, \_g, \_d, \_p - new Vue(options),实例化 Vue=>vm - Vue 构造函数里面执行了实例成员\_init() - 声明 vm 实例,给 vm 添加了\_uid - 合并 options,将构造函数的 options,传入的 options,实例 vm 做合并操作,赋值给了\$options - initLifecycle(vm),生命周期的初始化,给 vm 添加了$parent/$root/$children/$refs - updateComponentListeners(vm),初始化当前组件的事件 - initRender(vm),给 vm 添加了$slots/$scopedSlots/\_vnode/\_staticTrees/\_c/\$createElement(render 函数,创建 element),对 vm.$attrs和vm.$listener 添加了响应式 - callHook(vm, 'beforeCreate'),beforeCreate 生命钩子的回调 - initInjections(vm) 依赖注入,遍历所有的\$options.inject,如果在 vm.\_provided 中存在,都提取出来,然后变成响应式 - initState(vm),初始化 state - initProps(vm, vm.\$options.props),将 options.props 注入到 vm.\_props - initMethods(vm, vm.\$options.methods),将所有的 methods 注入到 vm 中 - 调用 initData(vm),当 vm.\$options.data 存在,将 data 都注入到\_data 中,如果 data 是 function,调用 getData(data,vm),最后将数据变成响应式 - initComputed(vm, vm.\$options.computed),将计算属性注入到 vm.\_computedWatchers 中,计算属性都是一个 watcher - initWatch(vm, vm.\$options.watch),创建 vm.\$watcher() - initProvide(vm),初始化 provide - callHook(vm, 'created'),created 生命钩子的回调 - 最后调用 vm.$mount(vm.$options.el) - vm.\$mount(el),执行 mountComponent(vm,el) - 如果 vm.$options.render不存在, vm.$options.render= createEmptyVNode,createEmptyVNode 该函数创建空的 vnode 节点 - callHook(vm, 'beforeMount'),beforeMount 的生命钩子回调 - new Watcher(),创建监听者 - 如果 vm.\$vnode 为空, vm.\_isMounted = true,并调用 callHook(vm, 'mounted')钩子回调 #### 2:vue响应式原理 vue响应式原理构建过程 1. 入口,initState() vm状态的初始化,整个响应式是从init方法中开始的,在init方法中,调用initState方法初始化状态,在initState方法中调用initData(),将data属性注入到vue实例上,并且调用observe()将其转化为响应式对象,observe是响应式的入口 2. observe(value)位于src/core/observer/index.js,首先判断value是否是对象,如果不是对象直接返回,判断value对象是否有__ob__,如果有证明value已经做过响应化处理,是响应式数据,则直接返回,如果没有,则在第三步创建observer对象,并将其返回。 Observe()位于src/core/observer/index.js,给value对象定义不可枚举的__ob__属性,记录当前的observer对象,进行数组的响应化处理,设置数组中的方法push、pop、sort等,这些方法会改变原数组,所以当这些方法被调用的时候,会发送通知,找到observe对象中的dep,调用dep.notify()方法,然后调用数组中的每一个成员,对其进行响应化处理,如果成员是对象,也会将转化为响应式对象,如果value是对象的话,会调用walk(),遍历对象中的每一个成员,调用defineReactive() 3. defineReactive src/core/observer/index.js,为每一个属性创建dep对象,如果当前属性是对象,递归调用observe(). 4. getter:为每一个属性收集依赖,如果当前属性是对象,也为对象的每一个属性收集依赖,最终返回属性值。 setter:保存新值,如果新值是对象,则调用observe,派发更新(发送通知),调用dep.notify() 5. 依赖收集 在watcher对象的get方法中调用pushTarget,会把当前的watcher记录Dep.target属性,访问的data成员的时候收集依赖,访问值的时候会调用defineReactive的getter中收集依赖,把属性对应的watcher对象添加到dep的subs数组中,如果属性是对象,则给childOb收集依赖,目的是子对象添加和删除成员时发送通知。 6. Watcher 当数据发生变化时,会调用dep.notify(),调用watcher对象的update()方法,在update方法中会调用queueWatcher(),方法中会判断watcher是否被处理,如果没有,则将其添加到queue队列中,并调用flushSchedulerQueue()刷新任务队列,在flushSchedulerQueue中,会触发beforeUpdate钩子函数,然后调用watcher.run(),然后清空上一次的依赖,触发actived钩子函数,触发update钩子函数 #### 3、请简述虚拟 DOM 中 Key 的作用和好处 * 作用: 如果设置了 key,对 key 所在的节点重用上一次的结果,而不是更新 dom key 的作用主要体现在虚拟 dom 的 diff 算法上 两个相同的组件产生类似的 dom 结构,不同的组件产生不同的 dom 结构 同一层的一组节点,他们可以通过唯一的 id 进行区分 基于上面两点,diff 算法的时间复杂度从 O(n^3)降到了 O(n) * 好处: 减少了 dom 渲染的次数S #### 4、请简述 Vue 中模板编译的过程 1. 通过compileToFunction函数,先从缓存中加载编译好的redner函数 2. 如果没有就调用compile函数,在compile函数中合并options,调用baseCompile函数 3. 在baseCompile函数中调用parse函数中将template装换成AST中的静态sub trees, 4. 调用generate 生成js的创建代码 5. 调用compileToFunction函数继续把上一步中生成的字符串js代码转换为函数 6. 调用createFuntion,render和staticRenderFns初始化完毕,挂载到Vue实例的options对应的属性中t