Vue Q&A
MVC和MVVM的区别
- V即View即DOM;M即Model即数据模型;C即Controller即逻辑控制器
- MVC所有的数据通信都是单向的
- 数据流动的方向为V->C->M->V,即
- 我们通过DOM绑定的事件触发逻辑控制器Controller
- 通过Controller变更数据模型Model,同时将Model通过DOM API映射变化到View
- MVC一般有两种形式:
- View绑定DOM事件触发Controller;Controller完成业务逻辑更新触发Model变更状态;Model将变更状态反映到View
- 用户通过自定义事件或者hashchange等直接触发Controller;Controller完成业务逻辑触发Model变更状态;Model将变更同步到View
- MVVM即在View和Model中间存在ViewModel转换层,可以将View的变化通过中间层转化到Model,也可以将Model的变化通过中间层转化到View
- MVVM通过中间层VM实现了双向绑定
- VM层是可以将View和Model做自动映射的一层,即用户可以专注Model修改操作View变化
Vue的双向绑定原理
所谓的双向绑定是建立在MVVM的模型基础上的:
- 数据层Model:应用的数据以及业务逻辑
- 视图层View:应用的展示效果,各类的UI组件等
- 业务逻辑层ViewModel:负责将视图和数据关联起来
MVVM或者双向绑定的的本质:
- 数据变化后更新视图
- 视图变化后更新数据
这个双向绑定主要依赖两部分的功能:
- 监听器Observer:响应式属性监听
- 解析器Compiler:对每个元素节点的指令进行扫描和解析,根据指令替换数据,绑定对应的更新函数
具体实现原理?参考Vue2的双向绑定和Vue3的顶层代理原理
虚拟DOM的优缺点
优点:
- DOM解耦。首先,最重要的一点是,使用VDom使得框架从繁重的真实DOM操作中解耦出来,因为我们知道频繁操作真实DOM是很耗费性能的。
- 同构、平台无关。其次,这使得框架同构有了可能,我们可以在任何js可运行的环境中,重用框架的运行时。
- 在此基础上,我们完全可以通过创建自定义渲染解决方案的方式,跳出浏览器运行时约束,这里尤指SSR
- 算法优化。数据结构量化,可借助算法优化,减少真实DOM操作开销
缺点:
- 首次渲染大量DOM时,计算开销较大,慢于直接innerHTML
关于@click.native
修饰符
- Vue3已经废弃,因为已经改进了事件绑定机制,所以不再需要native修饰符
Vue2中存在的作用:
- 当自定义组件足够细腻,且自定义组件template root元素需要绑定某个事件时:
- 传统的思路是我们通过props绑定一个function,然后在子组件进行接收,然后手动绑定这个事件到子组件的根元素。
- 但是这样会侵入子组件逻辑,假如存在一种场景,我们需要更改element-ui中某一个组件的根节点绑定的事件,或者说这个根节点本身没有暴露出我们实际需要的事件接口时,我们可以通过native修饰符,在不侵入第三方组件代码时进行事件注入(其实我们也可以通过外层捕获事件冒泡的形式进行事件拦截)
- 需要注意这个事件注入的是浏览器原生DOM事件
- 传统的思路是我们通过props绑定一个function,然后在子组件进行接收,然后手动绑定这个事件到子组件的根元素。
Vue3使用指南:
- Vue3中已经没有native修饰符了,取而代之的是,我们在子组件内部接收一个
emits
选项,所有通过emits选项定义的事件,才会被子组件认定为组件内触发事件监听器- 没有在
emits
声明的事件,会被当作浏览器原生DOM事件绑定到子组件根DOM节点
- 没有在
v-model原理
在Vue2中使用v-model="modelProps"
进行input等非受控组件双向绑定时:
- 相当于在非受控组件自身同时绑定了
:value="modelProps"
@input="modelProps = $event.target.value"
- 相当于通过一个
v-model
简单语法糖使非受控组件瞬间受控
在Vue2中使用v-model="inputValue"
进行自定义组件双向绑定时:
- 相当于在组件自身同时绑定了
:value="inputValue"
和@input="inputValue = $event"
事件
在Vue2中使用:title.sync="pageTitle"
进行双向绑定时:
- 相当于同时绑定了
:title="pageTitle"
和@update:title="pageTitle = $event"
事件
在Vue3中,相当于将上述混乱的用法进行了整合:
- 在组件身上使用
v-model:title="pageTitle"
,相当于同时在组件身上绑定了:title="pageTitle"
@update:title="pageTitle = $event"
Vue生命周期
基本生命周期:
beforeCreate
: 初始化实例created
:创建实例,此时可以访问Vue实例- 包含以下配置完成:reactivity、computed、methods、events
- 检查是否有template,调用compiler生成render function
beforeMount
:调用render function生成VNodemounted
:挂载VNode,此时可以访问DOMbeforeUpdate
:当reactivity data change时调用updated
:当完成VNode re-render和patch以后调用beforeUnmount
:卸载组件实例之前调用,此时尚可访问实例unmounted
:组件实例卸载完成后调用- 此时所有指令、事件均解除/移除
有两个特殊的钩子,配合keep-alive
使用:
activated
:被keep-alive
缓存的组件激活时调用deactivated
:被keep-alive
缓存的组件失活时调用
Vue keep-alive原理
Vue watch支持的额外参数
immediate
:是否立即触发一次watch callbackdeep
:是否深度递归,发现对象内部的变化flush
:控制回调函数执行的时机。命名类似nodejs钩子pre
:默认值。在重新渲染之前调用,性能优化post
:在重新渲染之后调用,适用于需要通过watch的callback访问更新以后的DOM的场景sync
:watch监听值一旦发生变化即调用callback
key的作用
主要的区别在于diff计算VDom的算法逻辑应对上:
- 当使用key时,Vue会基于key的变化重新排列元素顺序,移除key不存在的元素
- 当不使用key时,因为完全对比两份VDom的异同复杂度是O(n3),其中n为元素节点数量,开销是比较大的
- 未了降低算法复杂度,在diff时会进行抉择,如:发现元素节点类型不一致且不存在key时,会直接销毁重建
所以有以下结论:
- 也就是说,在使用key的时候,能够更加高效精准的更新VDom
- Vue Transition过渡时,必须使用key,否则一旦发生元素的重建,就可能会不触发过渡效果
双向绑定和Vuex是否冲突
在严格模式下会有冲突,但是一般情况下,即使发布生产,也不建议使用严格模式
开启严格模式:
const store = new Vuex.Store({
// ...
strict: true
})
严格模式:
- 在严格模式下,任何不由mutation引发的状态变更,都会导致错误。这将保证所有的状态变更都能被调试工具跟踪到
- 严格模式会深度监测状态树,进而监测不合规的状态变更,耗费性能
Vue-router三种模式
- hash模式
- vue默认模式
- history模式
- abstract模式
- 抽象模式,在检测不到处于浏览器环境(判断window是否存在)时,默认进入抽象模式
let mode = options.mode || 'hash'
this.fallback =
mode === 'history' && !supportsPushState && options.fallback !== false
if (this.fallback) {
mode = 'hash'
}
if (!inBrowser) {
mode = 'abstract'
}
this.mode = mode
switch (mode) {
case 'history':
this.history = new HTML5History(this, options.base)
break
case 'hash':
this.history = new HashHistory(this, options.base, this.fallback)
break
case 'abstract':
this.history = new AbstractHistory(this, options.base)
break
default:
if (process.env.NODE_ENV !== 'production') {
assert(false, `invalid mode: ${mode}`)
}
}