vue内部运行机制

AI权益加码!Claude Code、Cursor等20+工具免费用! 购周边限时加赠Coding Plan Lite,畅享主流AI工具!学习进阶更高效! 阅读详情

本文主要讲述 vue内部运行机制

 阅读本文可收获
 1.对vue内部运行机制有系统性的了解
 2.数据劫持 + 发布订阅者模式 实现响应式
 3.编译过程简单了解
 4.vue的异步批量更新策略
 5.为什么引入vNode 以及 diff 算法
 6.patch 是如何比较虚拟节点并得出差异的 

内部流程总览图

1.简述内部流程

1.1 new Vue()初始化 与 数据劫持

实例化vue对象new Vue(),会调用_init()函数进行初始化,这个过程会初始化 生命周期、事件、 props、 methods、 data、 computed 与 watch 等。并通过Object.defineProperty进行数据劫持,设置get,set,用于后期的依赖收集以及响应式!

1.2 $mount 挂载

初始化结束,会执行$mount进行挂载,此时会判断是否存在render funtion,如果存在则执行,否则会进入compile编译阶段;

在main.js,会存render funtion 执行render 渲染最终挂载到 #app 这个dom中

v2
new Vue({router,render: (h) => h(App),
}).$mount('#app');

v3
createApp(App).use(store).use(router).mount("#app") 

如果是页面组件或者业务组件会对 template 进行编译,进而得到 render function ,执行render 得到 vNode, 进行 patch 过程最终进行渲染并挂载到对应的文档dom

$mount()本质是想将render渲染模板挂载到文档dom中;

1.3 compile() 编译

编辑阶段会调用compile(),可以分成 parseoptimizegenerate 三个阶段,最终需要得到 render function;

parse 会用正则等方式解析 template 模板中的指令、class、style等数据,形成AST。

我们大概看下AST是什么样的

<div :class="c" class="demo" v-if="isShow"><span v-for="item in sz">{{item}}</span>
</div>

{/* 标签属性的map,记录了标签上属性 */'attrsMap': {':class': 'c','class': 'demo','v-if': 'isShow'},/* 解析得到的:class */'classBinding': 'c',/* 标签属性v-if */'if': 'isShow',/* v-if的条件 */'ifConditions': [{'exp': 'isShow'}],/* 标签属性class */'staticClass': 'demo',/* 标签的tag */'tag': 'div',/* 子标签数组 */'children': [{'attrsMap': {'v-for': "item in sz"},/* for循环的参数 */'alias': "item",/* for循环的对象 */'for': 'sz',/* for循环是否已经被处理的标记位 */'forProcessed': true,'tag': 'span','children': [{/* 表达式,_s是一个转字符串的函数 */'expression': '_s(item)','text': '{{item}}'}]}]
} 

optimize 的主要作用是标记 static 静态节点(还标记了注释节点,文本节点等) 这是 Vue 在编译过程中的一处优化,后面当 update 更新界面时,会有一个 patch 的过程, diff 算法会直接跳过静态节点,从而减少了比较的过程,优化了 patch 的性能。

generate 是将 AST 转化成 render function 字符串的过程,得到结果是 render 的字符串以及 staticRenderFns 字符串。

在经历过 parseoptimizegenerate 这三个阶段以后,组件中就会存 render function 了。render function 会被转化成 VNode 节点.

VNode 是就是一个json对象,用来描述改节点; 例如

Virtual DOM
{tag: 'div', /*说明这是一个div标签*/children: [ /*存放该标签的子节点*/{tag: 'a', /*说明这是一个a标签*/text: 'click me'/*标签的内容*/}]
} 

也因为虚拟dom,本质是一个js对象,所以只要支持js就支持VNode,故它具有了跨平台的能力,比如说浏览器平台、Weex、Node 等!

1.4 响应式原理 与 批量异步更新策略

当reder function被渲染的时候需要「读」取值,此时会触发getter方法进行依赖收集,执行订阅者dep中的addSub方法,把当前的 Watcher 对象(存放在 Dep.target 中)收集到 Dep 类中

如果当该对象被「写」的时候,则会触发 setter方法,通知 Dep 类调用 notify 来触发所有 Watcher 对象的 update 方法,会触发批量异步更新策略(一种优化手段)来,来更新试图

1.5 patch 与 diff算法

render function 执行会得到一个新的 VNode 节点,我们会将新老VNode节点传入patch方法进行比较,通过diff算法计算出差异,最后只需要对差异的节点进行渲染即可!

2.核心概念讲解

2.1数据劫持-响应式原理

简述:new Vue()的时候会调用observer方法,对data中的数据进行遍历,对遍历每个数据执行defineReactive方法,首先会实例化一个dep类对象,用于依赖收集,接着使用Object.defineProperty方法进行劫持,重写其get以及set方法!

当数据被读取的时候,会执行其get方法,执行dep.addSub()进行依赖收集,将对应的watcher存入dep的subs中!

当数据被写入的时候,会在执行其set方法,首先判断数据是否变化,当数据变化的时候会触发dep.notify()遍历通知收集的依赖watcher变化,进而执行watcher的updata方法更新视图

数据劫持一个数据,会实例化其对应的dep类对象,每个数据都有其对应的订阅者dep!

class Vue {/* Vue构造类 */constructor(options) {this._data = options.data;observer(this._data);}
}
function observer(value) {if (!value || typeof value !== 'object') {return;}/* * 为方便理解仅考虑简单类型,暂时不考虑 数组类型以及多层对象* 发现是数组类型则通过重写其数组操作方法使其能被响应* 但是也导致 仅通过 数组操作方法才被会被响应* 例如: 通过下标直接改变数组内容则不能被响应* 内部数据为多层对象则是通过递归循环劫持* 总得来 数据劫持 仅劫持data上面存在的数据* vue3使用proxy 进行双向绑定,不存在以上问题* proxy可以直接拦截整个对象,不需要再进行递归* 可以理解为 vue2是通过遍历以及递归为data内的每个数据进行拦截* 当新增数据的时候不会重新遍历,所以仅对data存在的数据响应(可以使用$set手动使其响应)* proxy 拦截的为data整个对象,无需遍历,data内部任意数据改变,均会触发其set*/Object.keys(value).forEach(key => {defineReactive(value, key, value[key]);});
}
function defineReactive(obj, key, val) {Object.defineProperty(obj, key, {enumerable: true /* 属性可枚举 */,configurable: true /* 属性可被修改或删除 */,get: function reactiveGetter() {return val; /* 实际上会依赖收集 */},set: function reactiveSetter(newVal) {if (newVal === val) return;cb(newVal);},});
}

function cb(val) {/* 渲染视图 */console.log('视图更新啦~');
}

let o = new Vue({data: {test: 'I am test.',},
});
o._data.test = 'hello,world.'; 
思考延伸—为何需要收集依赖
new Vue({template: `<div><span>{{text1}}</span> <span>{{text2}}</span> <div>`,data: {text1: 'text1',text2: 'text2',text3: 'text3'}
});
this.text3 = 'modify text3'; 

我们修改了 data 中 text3 的数据,但是因为视图中并不需要用到 text3 ,所以我们并不需要触发上一章所讲的 cb 函数来更新视图,调用 cb 显然是不正确的!

所以我们需要收集依赖,当依赖变化的时候,我们才会去更新视图

2.2依赖收集与发布者-观察者模式

 /* 发布者
*作用 1.收集依赖 2.下发通知更新
*/
class Dep {constructor () {/* 用来存放Watcher对象的数组 */this.subs = [];}/* 在subs中添加一个Watcher对象 */addSub (sub) {this.subs.push(sub);}/* 通知所有Watcher对象更新视图 */notify () {this.subs.forEach((sub) => {sub.update();})}
}
/* 观察者
*作用 接收到更新通知,执行更新视图
*/
class Watcher {constructor () {/* 在new一个Watcher对象时将该对象赋值给Dep.target,在get中会用到 */Dep.target = this;}/* 更新视图的方法 */update () {console.log("视图更新啦~");}
}

Dep.target = null;
class Vue {constructor(options) {this._data = options.data;observer(this._data);/* 新建一个Watcher观察者对象,这时候Dep.target会指向这个Watcher对象 */new Watcher();/* 在这里模拟render的过程,为了触发test属性的get函数 */console.log('render~', this._data.test);}
}
function defineReactive (obj, key, val) {/* 一个Dep类对象 */const dep = new Dep();Object.defineProperty(obj, key, {enumerable: true,configurable: true,get: function reactiveGetter () {/* 将Dep.target(即当前的Watcher对象存入dep的subs中) */dep.addSub(Dep.target);return val; },set: function reactiveSetter (newVal) {if (newVal === val) return;/* 在set的时候触发dep的notify来通知所有的Watcher对象更新视图 */dep.notify();}});
} 
思考延伸----为何需要批量更新机制
for (let i = 0; i < 1000; i++) { o._data.test = i;
} 

当一个数据被写入了1000次,岂不是要更新1000次视图?显然是不必要的,更新视图消耗巨大,用户只需要看到最后一个视图变化即可,从而需要批量异步更新策略,收集下发的多次更新请求,最终合并为一次更新!

2.3批量异步更新策略

简单的来说,就是一个数据一次性被修改了多次,会触发多次 set 从而触发多次 dep.notify进而触发了多次updata,正常我们每次updata都会取更新视图,批量异步更新策略 会在updata的时候把watcher 收集起来,并通过 watcher的id进行过滤,然后使用 Promise、setTimeout、setImmediate 进行更新视图

因为 Promise.then 为异步,会进入任务队列.当主线程中的任务执行完毕,就从任务队列中取出任务放进主线程中来进行执行,也就是 Promise.then 执行的时候, o._data.test 已经执行完毕第1000次写入了,此时 在进行驶入渲染,自然渲染的就是最终的结果了

setTimeout、setImmediate为宏任务,也会等待主线程中的任务执行完毕 在执行,同上!(注意setTimeout 执行顺序在 Promise.then 之后)

简单的描述一下一个event loop流程

首先初始状态 调用栈为空,微任务队列为空,宏任务队列只有一个script脚本

script脚本 入栈开始执行,首先执行同步代码,在执行过程中 可能会产生其他的 微任务(如异步调用接口),宏任务(定时器),当同步代码执行结束,script 脚本会被移出 宏任务队列,然后会检查微任务队列,同时执行一队微任务,微任务会逐个执行结束,并出队,当所有微任务都被清空队列,则会执行一次渲染操作,更新页面!

更新结束浏览器检查是否存在 Web worker 任务,如果有,则对其进行处理,此时可能会发现宏任务队列还有任务,会继续处理,过程同上

let uid = 0;
let has = {};
let queue = [];
let waiting = false;
let callbacks = [];
let pending = false;

//观察者
class Watcher {constructor() {/* 在new一个Watcher对象时将该对象赋值给Dep.target,在get中会用到 */Dep.target = this;this.id = ++uid;}/* 更新视图的方法 */update() {console.log('watch' + this.id + ' update');/* 批量更新机制 传入watcher本身 */queueWatcher(this);}run() {console.log('watch' + this.id + '视图更新啦~');}
}

/* 进行过滤*/
function queueWatcher(watcher) {const id = watcher.id;/* 过滤相同id的数据,实际一个组件对应一个watcher-id */if (has[id] == null) {has[id] = true;<!--将对应watcher加入队列-->queue.push(watcher);if (!waiting) {waiting = true;<!--异步策略-->nextTick(flushSchedulerQueue);}}
}

function nextTick (cb) {<!--收集回调函数-->callbacks.push(cb);if (!pending) {<!--进入等待-->pending = true; <!--Vue.js 源码中分别用 Promise、setTimeout、setImmediate 等方式-->setTimeout(flushCallbacks, 0);}
}

function flushCallbacks () {pending = false;for (let i = 0; i < callbacks.length; i++) {callbacks[i]();}callbacks.length = 0;
}

function flushSchedulerQueue () {let watcher, id;for (index = 0; index < queue.length; index++) {watcher = queue[index];id = watcher.id;has[id] = null;watcher.run();}waiting= false;
} 

2.4patch机制与diff算法

set-dep.notify-watcher.updata-queueWatcher-patch-渲染视图

patch这个过程主要是对比新老VNode节点,通过diff算法计算得出「差异」,最终将这些「差异」更新到视图上!

diff 算法是通过同层的树节点进行比较而非对树进行逐层搜索遍历的方式

思考1: 使用 diff 算法 计算 vnode 差异,并将差异渲染到视图上,是否比我们直接操作做真实dom最终渲染性能更高?

数据修改 === vNode(diff算法) ==== 修改真实dom

数据修改 === 修改真实dom

首先确定,直接修改dom的性能肯定高于间接通过 vnode+diff 操作节点

既然直接操作dom吸能最佳,为何要引入 vnode

1.使用虚拟dom有着还不错的性能,虽然比不上通过原生 js 直接操作 DOM 的性能,但是也没差太多,能保证性能的下限。

2.使用VNode + diff避免了我们直接操作dom,也不用去关注dom操作,vue会用一种相对更规范的方式操作dom,尽最大可能的符合浏览器的优化机制!

使用diff算法尽可能的减少了 创建dom节点的性能消耗,如果使用 innerHtml直接插入会导致旧节点被清空,所有dom节点都会被新建,消耗性能巨大,所以需要对比处差异

diff对比的过程操作的是VNode节点,过程不会引起回流以及重绘,会一次性找到所有差异,最终进行渲染,既然要渲染,最终肯定还是要转化为真实dom进而操作真实dom进行渲染!

思考2: 我们通过diff算法计算出差异,如何做到只渲染差异部分,如果差异部分为几何属性修改,或者dom新增或者确实,依然会引起回流,回流就会导致整个页面重新渲染,得到差异部分又有何用?

当页面dom被新增或者删除或者修改了几何属性,都会造成页面的回流,重新计算样式以及为位置,最终再次渲染到页面!

但是回流不会导致dom节点新建与删除!也就是回流的时候dom节点已经存在,浏览器会通通过引擎解析并计算出对应的css样式已经位置,然后重新渲染到页面中!

可以理解为将整个页面填白,然后对内容重新渲染!

只会重新渲染差异部分 主要是和 innerHtml 进行对比 这种方式对比,避免大面积新建未改变的dom节点!

例如: 我们要修改div的内容

首先,假设不适用diff对比,使用innerHtml直接插入

会导致我们需要创建对应的节点,替换当前节点,

const htmlTemplate = `<div>hello world</div>`
wrap.innerHTML = htmlTemplate 

通过diff算法,我们只需要 对比出差异,来最小化的修改变更的内容,首先发现整个标签可以复用,只是内容区域修改了,所以无需创建dom 直接替换节点即可!

当然,如果使用原生js直接操作dom,我们也可以精准的替换内容,无需创建节点,vue使用vnode + diff 多了计算对比的过程,但是js性能相对较高,所以性能也还是不错的!

而且使用原生js操作dom,用的好了性能极佳,但是就怕用的不好,一顿操作猛如虎,随便进行无用的dom操作,造成大量性能消耗!

const div = document.getElementById('div')
div.innerText = 'hello world' 

故:引入vNode + diff的主要优势

1.避免我们不熟练的直接操作dom产生不可预知的性能消耗,让我们更专注与操作data

2.本身性能也不错,等于 降低了小幅度的性能,方便了使用者的使用!

步入正题 diff算法开始
function patch (oldVnode, vnode, parentElm) {if (!oldVnode) {addVnodes(parentElm, null, vnode, 0, vnode.length - 1);} else if (!vnode) {removeVnodes(parentElm, oldVnode, 0, oldVnode.length - 1);} else {if (sameVnode(oldVNode, vnode)) {patchVnode(oldVNode, vnode);} else {removeVnodes(parentElm, oldVnode, 0, oldVnode.length - 1);addVnodes(parentElm, null, vnode, 0, vnode.length - 1);}}
} 

对比的时候会接收三个参数1.oldVnode,vnode,父级节点

1.首先在 oldVnode(老 VNode 节点)不存在的时候,相当于新的 VNode 替代原本没有的节点,所以直接用 addVnodes 将这些节点批量添加到 parentElm 上

2.在 vnode(新 VNode节点)不存在的时候,相当于要把老的节点删除,所以直接使用 removeVnodes 进行批量的节点删除即可

3.当新老VNode都存在的时候会通过sameVnode判断是否为同一节点

为相同节点时候,则对比差异

为不同节点时,删除旧节点,增加新节点

那么问题来了,如何判断为同一节点?

满足以下五条则判断为相同节点

1.key相同

2.tag类型相同

3.isComment(注释节点),同为注释节点或者同不为

4.都有data或者都没有

5.当tag类型为input 的时候判断其对应的type是否相同

function sameVnode () {return (a.key === b.key &&a.tag === b.tag &&a.isComment === b.isComment &&(!!a.data) === (!!b.data) &&sameInputType(a, b))
}

function sameInputType (a, b) {if (a.tag !== 'input') return truelet iconst typeA = (i = a.data) && (i = i.attrs) && i.typeconst typeB = (i = b.data) && (i = i.attrs) && i.typereturn typeA === typeB
} 

如果判断为同一节点相同,则进入最后的比较差异的过程!

1.对比 vNode === oldVnode

虚拟dom均为json字符串,可以直接对比

相等 则直接复用 旧节点即可,不需要做任何改变

不相等则继续对比

2.判断新老vNode 是否均为静态节点,(vnode.isStatic在编译阶段会判断是否为静态节点),若均为静态节点,则跳过对比,直接复用

一方为静态节点,一方不是,则删除旧节点,新增新节点

双方均不为静态节点,则继续判断

3.判断vNode是否为文本节点(因为此时新老vNode tag类型相同所以不许判断老的)

3.1 vNode 为文本节点,则替换oldVnode的内容

3.2 vNode 不为文本节点

对比过程麻烦的很,可以自行感悟,以下为简单对比

此时判断vNode 与 oldVnode 是否有子节点 ch 与 oldChildren

双方皆不存在,则不做改动

一方存在的情况

1.ch 存在 oldChildren 不存在,然后将 ch 批量插入到节点oldVnode.elm下2.ch 不存在,则 oldChildren 存在 则删除oldChildren双方存在的情况

1.ch 与 oldChildren 都存在,且相等 不做改变(虚拟节点可以直接对比是否相等)2.ch 与 oldChildren 都存在,且不相等 则使用 updateChildren 来更新updateChildren 会对子元素进行一层层的对比,对比完头部对比尾部

<div>
 <ul> <li key='1'>1</li> <li key='2'>2</li> <li key='3'>3</li> <li key='4'>4</li>
 <ul/>
</div>

<div>
 <ul> <li key='1'>1</li> <li key='5'>5</li> <li key='2'>6</li> <li key='3'>3</li> <li key='4'>4</li>
 <ul/>
</div> 

首先对比 新旧的 第一个li 通过 sameVnode 来判断是否为相同节点, 相同,对比 vNode === oldVnode相等,则完全复用,若不相等,会继续对比 是否为静态节点,文本节点,children

然后对比双方最后一个节点,过程一样,也相同则继续复用

然后对比双方第二个节点,key不同故为不同节点,不可复用,在旧节点全面匹配是否有相同的key值,发现有相同的key,则通过 sameVnode 来判断是否为相同节点,相同,对比 vNode === oldVnode不相等,则判断内容是否均为为文本节点,是则仅替换内容

然后对比倒数第二个,相同节点且vNode === oldVnode相等则复用

最后

最近找到一个VUE的文档,它将VUE的各个知识点进行了总结,整理成了《Vue 开发必须知道的36个技巧》。内容比较详实,对各个知识点的讲解也十分到位。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

剖析 Vue.js 内部运行机制 把原理抽象为小 Demo,以一种对新手友好的方式带领读者漫游 Vue.js 的世界 阅读详情

相关推荐

VUE原理及运行机制

在修改对象值时,会触发响应的setter,setter通知之前【依赖收集】到的Dep 中的所有watcher,告诉他们自己的值变了,需要重新渲染视图。3.设置一个解析器 Compile,可以扫描和解析每个节点的相关指令(v-model,v-on等指令),如果节点存在 v-model,v-on等指令,则解析器 Compile 初始化这类节点的模板数据,使之可以显示在视图上,然后初始化相应的订阅者(Watcher)如果修改了text3的数据,但视图中不需要text3,所以并不需要调用cb函数。

三包酸奶!的博客 1585

Vue.js其原理和运作机制

每个组件都是一个具有独立逻辑和样式的模块。组件可以通过props接收数据,通过events传递消息。});

m0_65084430的博客 2148

vue篇——vue原理以及实现

官网解释Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架,简单易学、双向数据绑定、组件化。数据和结构的分离、虚拟DOM、运行速度快。它有以下的特性:1.轻量级的框架;2.双向数据绑定;3.指令;4.插件化。Vue.js 的核心是一个允许采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统:声明式编程(英语zhidao:Declarative programming)是一种编程范型,与命令式编程相对立。它描述目标性质,让计算机明白目标,而非流程

不确定因素的博客 2396

Vue源码】Vue源码解析之DOM和diff算法(二)

Vue源码解析之DOM和diff算法(二)

阿卡内的博客 513

Vue|项目结构与执行过程介绍

当执行了npm run serve 时,随后来到src目录找到main.js,引入vue,引入了App,接下来找到App.vue,发现里面用到了components下面的HelloWorld组件,对其进行执行,然后就到了index.html,通过容器进行渲染

商务合作 / 项目定制 / 学习交流。个人vx:lovely_wml 5092

Vue内部运行机制剖析--思维导图总结

总结的Vue机制内部运行概览思维导图: VUE机制内部运行概览思维导图文字版本: 初始化以及挂载 来自于:new Vue()之后 调用分方法:_init 初始化生命周期、事件、 props、 methods、 data、 computed 与 watch 之后: $mount 会挂载组件 编译template 编译 parse 会用正则等方式解析 tem...

Wion Wan 1001

Vue内部运行机制

Vue内部运行机制 序 最近做了一个关于Vue内部运行机制的分享会,记录一番笔记。 目录大纲 概述 初始化及挂载 响应式的实现 编译 Virtual DOM和数据更新时的patch() 概述 初始化 初始化生命周期、事件、render、state Object.definePrototy() 响应式的定义 挂载 (运行时 + 编译器) template到render Funct...

RedlOvE 1246

剖析vue内部运行机制

掘金小册-剖析Vue.js内部运行机制

起风了 656

vue 2.x内部运行机制系列文章

最近一直在用vue.js作为前端技术栈,闲暇之余,也简单研究了下vue.js的源码和各位前端大牛的文章,为了加深自己对vue.js的理解和完成公司的硬性要求(内部分享),于是就有了这篇系列文章。同时也趁3.0正式版发布前做2.0的最后一波总结。 vue是什么 套用官网的话,vue是一套用于构建用户界面的渐进式框架。Vue 的核心库只关注视图层,它拥有优雅的API,并且易于上手,还便于与第三方库或既...

挥别了青春的博客 384

Vue内部运行机制剖析

1、初始化 调用Vue原型上的_init()进行初始化,会初始化vue的生命周期,props,data,methods,computed,watch,最重要的是利用Object.definedPropty()对data对象里面的属性设置setter和getter函数,也就是来实现响应式和依赖收集 2、挂载组件 调用$mount挂载组件 3、编译 编译三部曲,parse(解析)、optimize...

qq_36711388的博客 304

Vue.js 内部运行机制

Vue.js 内部运行机制

weixin_64974855的博客 682

Vue内部运行机制解析(整理)

解析vue原理的好文: https://github.com/answershuto/learnVue https://www.jianshu.com/p/4c47c12d4b3f 1,vue运行机制 捕获.PNG 在 new Vue() 之后。 Vue 会调用 init 函数进行初始化,其中最重要的是通过 Object.defineProperty 设置 setter 与 ...

sunct的博客 1299

浅谈vue内部运行机制

var vm = new Vue({ el: '#app', data: {} }) 引入vue.js,new Vue()干了什么呢? 初始化 调用Vue原型上的_init()进行初始化,会初始化vue的生命周期,props,data,methods,computed,watch,最重要的是利用Object.definedPropty()对data对象里面...

前端金牌摸鱼达人 639

前端知识】Vue.js内部运行机制简述

Vue.js内部运行机制简述

Outman's blog 230

剖析 Vue.js 内部运行机制 (1)

1. new Vue() 之后。 Vue 会调用 _init 函数进行初始化,也就是这里的 init 过程,它会初始化生命周 期、事件、 props、 methods、 data、 computed 与 watch 等。其中最重要的是通过 Object.defineProperty 设置 setter 与 getter 函数,用来实现「 响应式」以及「 依赖收集」 2. mounte...

weixin_30679823的博客 286

Vue内部运行机制剖析-模拟Vue的响应式原理

Vue.js 是一款 MVVM 框架:核心实现就是响应式系统;理解这里的响应式指的是数据的响应式 模拟视图更新 使用Object.defineProperty 封装方法 function cb(val) { /* 渲染视图 */ console.log(&quot;视图更新啦~&quot;); } function defineReactive(obj, key, val) { Object.defin...

Wion Wan 474

data闭包 vue_前端基础:Vue源码,窥探Vue内部运行机制

内部流程图:分模块解释:初始化及挂载在 new Vue() 之后。 Vue 会调用 _init 函数进行初始化,也就是这里的 init 过程,它会初始化生命周期、事件、 props、 methods、 data、 computed 与 watch 等。其中最重要的是通过 Object.defineProperty 设置 setter 与 getter 函数,用来实现「响应式」以及「依赖收集。初始化...

weixin_39552304的博客 160
上一篇: vue-cli的组件化开发(上)
下一篇: Vue源码学习之实现模板转化成ast语法树(下)
梅花十三儿
博客等级 码龄5年 1035粉丝 444原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值