我们在面试的时候经常会被问到vue框架的原理类问题,我今天整理了一些常见问题和答案,希望有不正确之处还请指正。
1.new Vue时发生了什么
首先实例化一个对象,该对象执行init方法初始化生命周期等等,随后执行$mount方法开始生成vnode和dom。其中el会作为根dom,然后会根据render生成vnode。
执行init方法
开始挂载
判断有无render函数,没有则会生成
h函数就是$createElement,调用该方法生成vnode
总结:new Vue时会触发vue的_init方法去初始化数据,将el作为根dom,然后调用$mount方法去挂载组件,挂载的过程中会执行_render方法生成vnode。
2.双向数据绑定的原理
所谓的双向数据绑定就是当数据改变时视图也跟着变化,接下来我们看看关键代码。首先在get方法中会判断Dep.target是否存在,target是watcher实例,在视图中或者函数中获取该变量就会触发变量的get方法。然后该变量的dep实例就会放入当前的watcher(建立变量和watcher的联系)。
在set的时候,即数据发生修改的时候会执行当前变量的dep实例的notify方法,该方法会遍历执行当前观察的视图(所有的watcher)的update方法去异步更新视图。
总结:双向绑定的原理就是利用Object.defineProperty去设置变量的get和set方法,在该函数中会实例化dep数组。在获取数据的时候会触发变量的get方法去收集watcher,当变量发生改变时会触发变量的set方法去遍历dep数组,并执行watcher的update方法去刷新视图。
3.Computed 和 Watch 的区别
computed
首先在init过程中会执行initProxy,在渲染的时候获取getA会从vm实例中获取getA:
我们再来看看computed的实现过程:
我们进入watcher看看,其中getter函数就是我们定义的computed函数getA。
在执行渲染函数的时候触发我们开头设置的proxy
createComputedGetter函数在init过程中定义
执行watcher的evaluate方法,此时调用get方法,该方法就是getA函数。
此时会触发变量的get方法去收集当前的Dep.target(computed的watcher实例)。所以当变量发生变化时会执行watcher的update方法。
缓存
computed还有缓存功能,当依赖不发生变化时是不会重新执行的,看如下代码dirty为false不会重新执行computed函数而是直接返回值。
不支持异步
从上面的分析可知会执行computed函数然后通知变量收集依赖,如果是异步函数那么返回的就是一个Promise,此时变量无法收集依赖,所以不支持异步也很好解释。
watch
先看看实现过程,首先watch也会实例化一个watcher,handler就是watcher函数。
支持异步监听
之所以支持异步是因为是根据获取函数名去监听的,函数名是从变量中获取到的,不涉及到函数内部实现,所以函数内部是否异步并无关联。
通过执行watcher的get方法获取初始值
由于expOrFn是字符串,所以会执行parsePath函数,obj是vm对象,从vm对象中获取变量的。在获取对象的值时,变量会收集当前的watcher。
当变量改变时,会触发watcher的update方法,从而执行queneWatcher函数,异步执行flushSchedulerQueue方法
这里又会执行watcher的run方法
不支持缓存
通过get方法获取变量的最新值,通过判断当前值是否改变或者值是个对象,或者有deep属性从而记录oldValue。然后调用用户定义的watch函数并传入新值和旧值,在实现方法中并没有缓存这么一说,因为没有返回值。
总结:
相同点:
- 初始化过程都会生成watcher实例。
- 监听的过程都是触发变量的get方法收集watcher实例
不同点:
- computed会对结果缓存(是因为有返回值), watch没有缓存(是因为没有返回值)。
- computed不支持异步(因为要函数执行结果只能拿到promise,无法触发变量的get方法),watch支持异步(因为不依赖函数内部实现)
4. 说说once事件修饰符原理
带once和不带的区别是渲染的时候click函数不一样,带once的是~click还有arguments参数和$event事件。
我们来看看怎么处理~click的,在createElm的时候执行invokeCreateHooks:
cbs.create对象中包含了updateDOMListeners函数:
这里先执行normalizeEvent解析name,
执行createOnceHandler返回onceHandler函数,如果当前执行函数返回不是null则会移除该事件。
所以函数返回值为null时是不会移除监听事件的
总结
在render函数createEle的时候会通过addEventListener给当前dom添加事件,once就是通过判断返回值是否为null来决定是否移除监听事件
5.说说v-show的原理
v-show时vue的内部指令,会被解析为如下格式
createEle时调用如下函数
这里会执行updateDirectives函数的_update
这里执行了
在执行bind方法的时候会先获取当前的指令的value值,然后根据该值设置是否设置当前el的display是否为none。
总结
v-show只是在创建el的时候通过获取判断条件的值去设置当前dom的display属性,所以v-show不会影响到vnode的形成。
未完待续,持续更新。。。