Vue原理
未经允许 禁止转载
MVVM 数据驱动视图
传统组件只是静态渲染,更新还要依赖于操作DOM
vue MVVM,数据驱动视图
react setState,数据驱动视图
MVVM:Modev-View-ViewModel
1. Model:可以理解为vue里的data对象
2. View:页面,DOM
3. ViewModel:Vue层,处理一些DOM监听、指令操作等
MVVM就是通过vue监听、指令操作等方法修改data进而渲染页面
Vue响应式
vue3.0之前
核心API:Object.defineProperty
Object.defineProperty基本用法:
var data = {}
var name = 'zhangsan'
Object.defineProperty(data,'name',{
get:function(){
console.log('get')
return name
},
set:function(newVal){
console.log('set')
name = newVal
}
})
console.log(data.name) //get zhangsan
data.name = 'liu' //set
console.log(data.name) //get liu
功能稍完整的Object.defineProperty:
//定义data
const data = {
name: 'zhangsan',
age: 14,
city:{ //需要深度监听
id: 010,
name: '北京'
}
}
//监听函数
function observer(target) {
//判断传入对象参数是否为object
if (typeof target !== 'object' || target == null) {
return target
}
//对传入对象进行for in遍历
for (let key in target) {
//target--对象;key--对象属性;target[key]--属性值
defineProperty(target, key, target[key])
}
}
//obj.defineProperty
function defineProperty(target, key, value) {
//深度监听
observer(value)
Object.defineProperty(target, key, {
get() {
return value
},
set(newVal) {
if (newVal !== value) {
//深度监听
observer(value)
value = newVal
update()
}
}
})
}
//update
function update() {
console.log('update')
}
observer(data)
data.name = 'liu'
data.age = 15
Object.defineProperty的缺点:
1. 深度监听需要递归到底,一次性计算量大。
2. 无法监听新增/删除属性。(需要用Vue.set/Vue.delete)
虚拟DOM和diff算法
1. vdom是实现vue和react的重要基石
2. diff算法是vdom中最核心、最关键的部分
用JS模拟DOM结构
<!-- DOM -->
<div class='container' id='div1'>
<p>vdom</p>
<ul style='font-size: 14px;'>
<li>a</li>
</ul>
</div>
//用JS模拟DOM
{
tag: 'div',
props:{
id: 'div1',
className: 'container'
},
children: [
{
tag: 'p',
children: 'vdom'
},
{
tag: 'ul',
props: {
style: 'font-size: 14px'
},
children: [
{
tag: 'li',
children: 'a'
}
]
}
]
}
vue、react的vdom参考了snabbdom
//snabbdom:
var container = document.getElementById('container')
var vnode = h(...) snabbdom通过h方法模拟出DOM结构赋值给vnode
patch(container,vnode) //把vnode结构赋值给container
snabbdom重点:1、h函数;2、vnode数据结构;3、patch函数
vdom总结:
1. 用JS模拟DOM结构(vnode)
2. 新旧vnode进行对比,得出最小的更新范围,最后更新DOM
3. 数据驱动视图的模式下,有效控制DOM操作
diff算法
树diff的时间复杂度O(n的3次方)
- 遍历tree1
- 遍历tree2
- 排序
1000个节点要计算1亿次
优化时间复杂度到O(n)
- 只比较同一层级,不跨级比较
- tag不相同,则直接删除重建,不再深度比较
- tag和key,两者都相同,则认为是相同节点,不再深度比较
1000个节点只需计算1000次
snabbdom源码解读
1. h函数
h函数一般接受3个参数:sel(元素标签)、data(元素属性)、children(子内容)。也可单独接受其中一两个参数。
h函数返回执行一个vnode函数,参数包括sel,data,children,text(如果children为字符串,则用text显示该字符串),undefined
2. vnode函数
返回一个对象,包含sel、data、children、text、elm(该DOM节点)、key
3. patch函数
- 接受的第一个参数为element||vnode,第二个参数为vnode。
- 执行pre hook生命周期。
- 判断第一个参数是否为vnode,不是的话(传入的第一个参数为一个DOM)则创建一个空vnode关联到这个DOM元素。
4. sameVnode函数
执行sameVnode判断两个vnode的key和ele是否都相等,相等的话则执行patchVnode函数,不相同则删除销毁旧的vnode,然后用新的vnode重建。
5. patchVnode函数
- 执行prepatch hook生命周期钩子。
- 设置新vnode的ele,把旧vnode的ele赋给新的。
- 判断新旧children
6. addVnodes函数
有旧的children,没有新的,则添加vnode
7. removeVnodes函数
没有旧的children,有新的,则移除vnode
8. updateVnode函数
对比children,如开始和开始作对比,如果相同,则执行patchVnode函数,执行后index会进行累加或者累减,直到对比完成。
如果几种对比方式(start-start,end-end,start-end,end-start)都未命中,则用key和sel进行对比,如果都相等则执行patchVnode函数。
从这里可以看出v-for使用key的重要性,不使用key的话无法做对比,直接销毁旧的创建新的,另外key如果是随机数或者index则也无法对比。所以key是有必要写的且不能乱写
diff算法总结
1. patchVnode
2. addVnodes removeVnodes
3. updateChildren(key的重要性,可回答v-for为什么要有key:因为vdom的diff算法会对比select节点和key是否相同,相同则继续深入对比,不相同则重建,所以key是必要的)
vdom和diff算法总结
vdom核心概念很重要:h、vnode、patch、diff、key等。
vdom存在价值更重要:数据驱动视图,控制DOM操作。
模板编译
with语法
const obj = {
a: 1,
b: 2
}
console.log(obg.a)
console.log(obg.b)
console.log(obg.c) //undefined
//使用with语法,打破了作用域规则,能改变{}内自由变量的查找方式
//将自由变量当做obj的属性来查找
with(obj){
console.log(a) //1
console.log(b) //2
console.log(c) //报错
}
1.模板不是html,因为包含一些指令、插值,直接放在浏览器里是不能执行的
2.html是标记性语言,只有js才能实现判断循环
3.因此,模板一定是转换成js代码,即模板编译
模板编译流程
- 模板编译为rander函数,执行rander函数返回vnode
- 基于vnode再执行patch和diff
- 使用webpack vue-loader,会在开发环境下编译模板
使用rander代替template
Vue.component('component',{
rander: function(createElement){
return createElment( //vnode
'h'+this.level,
[
createElement('a',{
attrs:{
name: 'headerId',
href: '#'+'headerId'
}
},'this is a tag')
]
)
}
})
模板编译总结
- with语法
- 模板到rander函数,再到vnode,再到渲染和更新
- vue组件可以使用rander替代template
组件 渲染/更新过程
涉及vue原理三大知识点:
1.响应式:监听data属性 getter setter
2.模板编译: 模板到rander函数 再到vnode
3.vdom: patch(elm,vnode)和patch(vnode,newVnode)
1. 初次渲染过程
1.解析模板为rander函数(在webpack的vue-loader、vue-cli环境下已完成)
2.触发响应式,监听data属性getter setter
3.执行rander函数生成vnode,执行patch(elem,vnode)
2. 更新过程
1.修改data,触发setter(此前在getter已经被监听)
2.重新执行rander函数生成newVnode,执行patch(vnode,newVnode)
3. 异步渲染
vue是异步渲染,修改data一次性提交,能提高性能
$nextTick相关