vue原理

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次方)

  1. 遍历tree1
  2. 遍历tree2
  3. 排序

1000个节点要计算1亿次

优化时间复杂度到O(n)

  1. 只比较同一层级,不跨级比较
  2. tag不相同,则直接删除重建,不再深度比较
  3. 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函数

  1. 接受的第一个参数为element||vnode,第二个参数为vnode。
  2. 执行pre hook生命周期。
  3. 判断第一个参数是否为vnode,不是的话(传入的第一个参数为一个DOM)则创建一个空vnode关联到这个DOM元素。

4. sameVnode函数

执行sameVnode判断两个vnode的key和ele是否都相等,相等的话则执行patchVnode函数,不相同则删除销毁旧的vnode,然后用新的vnode重建。

5. patchVnode函数

  1. 执行prepatch hook生命周期钩子。
  2. 设置新vnode的ele,把旧vnode的ele赋给新的。
  3. 判断新旧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代码,即模板编译

模板编译流程

  1. 模板编译为rander函数,执行rander函数返回vnode
  2. 基于vnode再执行patch和diff
  3. 使用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')
            ]
        )
    }
})

模板编译总结

  1. with语法
  2. 模板到rander函数,再到vnode,再到渲染和更新
  3. 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相关

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,348评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,122评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,936评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,427评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,467评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,785评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,931评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,696评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,141评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,483评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,625评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,291评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,892评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,741评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,977评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,324评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,492评论 2 348

推荐阅读更多精彩内容