- 记:Vue 运行时的核心主要包括数据初始化、数据更新、异步队列、DOM渲染这几个部分。
- 记:Vue双向数据绑定的核心和基础api是Object.defineProperty。其内部真正参与数据双向绑定流程的主要有Obderver、Dep和Watcher,基于defineProperty和发布者订阅者模式,最终实现数据的双向绑定。
- 记:当数据发生变化时,会调用 dep.notity() 进而通知订阅的 watcher 进行更新。
-
记:MVVM中的ViewModel不受开发者操作
- 记:依赖收集:界面用到了就收集,没有用到就不收集。
-
记:el: 'body' 直接选中body标签,不通过类或id也可以实现挂载
- 记:dep对象就是一个闭包
- 记:computed计算属性是一个函数,就和react/JS函数一样,可以使用三目运算符等js方法
- 记:<p v-if="isShow"></p> 有哪些来源:
- 1⃣️data(){ return { isShow: false} } 2⃣️computed:{ isShow: function(){return false}; }
- 记:模板v指令的代码都会生成在render函数中,通过app.$options.render就能得到渲染函数
- 记:v-for: item in items形式遍历数组或对象、建议设置key值
- 记:new Vue()实例中可以有:【el、data、templete, methods、computed、全声明周期、definePorperty(){set,get}、watcher,Dep】
- 记:能改变data初始数据的方式有哪些:【definePorperty(set), methods、computed ,声明周期】,操作改变时都需要加上this
- 记:想使用Object.defineProperty()时,需要先在外层套一个函数➕函数调用👇🌟
- 记:以后,不管是原生JS, React,Vue,凡是跟遍历沾边的方法:最好都设置上key:
<li v-for="item in items" :key="item.id">
{{ item.label }}
</li>
- 记:数据监听器 Observer:能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者
- 记:指令解析器 Compile:对每个元素节点的V指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数
---------new Vue实例的生命周期-----------
- 记:beforeCreate在执行的时候,data还没有被初始化,DOM也没有初始化,所以不能在这里发起异步请求并且不能给数据模型的属性赋值。
- 记:created:可发起异步请求进行数据模型的赋值操作,此时还拿不到DOM
- 记:beforeMount与created之间只有一个是否是浏览器的判断,所以这时候在钩子函数中的里数据模型里、页面的状态,与created是一样的。
- 记:mounted被执行到的时候,数据模型和页面的DOM都初始化完成,在这里我们可以给数据模型赋值也可以进行DOM操作了。
- 记:beforeUpdate: 由于beforeUpdate不会自动触发,是数据发生改变时触发,所以在mounted中更改了data数据,beforeUpdate再执行更新时,值已经是最新的了
- 记:Update:与beforeUpdate的数据与页面保持一致。
- 记:beforeDestroy:在mounted手动进行了destory销毁组件,触发了beforeDestroy钩子函数执行,在此生命周期中,依旧能看到数据模型与DOM是未被注销的。
- 记:destroyed:可以看到DOM已经被清除了
- Ajax请求应该放在created钩子函数是最好的 --- 待求证
- 了解:生命周期拓展:activated与deactivated,被keep-alive标签包裹的子组件才会得到触发。不常用,易被遗忘
- 同步渲染🆚异步渲染:当数据变化时,页面订阅的响应操作与数据变化完全对应;当数据变化都操作完成时,页面才会得到响应/渲染。
-
Vue 不能检测通过数组索引直接修改一个数组项---待查资料验证,如下代码👇➕
-
var vm = new Vue({ data: { items: ['a', 'b', 'c'] } }) vm.items[1] = 'x' // 不是响应性的
-
在vue的data中,一开始并不存在变量edit, 我后期通过方法进行的赋值。这样可能会导致vue页面渲染的响应慢一拍。最好一开始就在data中初始化变量(订阅值)
--------------------------------------------------start👇----------------------------------------------------------------
computed和 watch 的区别?
-
Watch
1⃣️是什么?
是监听数据更新变化后,每次都会将最新的值赋给dom订阅者。结构:{ 键名:键值} 键名:被观察的表达式; 键值:回调函数/方法名/对象 -
2⃣️干什么的?
用于观察和监听页面上的vue实例
3⃣️啥时候用?
适用于在数据变化的同时执行异步操作(并发)
适用于比较大的开销
4⃣️代码实现? -
computed
1⃣️是什么?
计算属性
2⃣️干什么的?
页面上的数据有依赖性
3⃣️啥时候用?
大部分情况选择用它
当页面中某些数据依赖其他数据时用它
4⃣️代码实现? -
#两者有啥区别?
1.看data中有没有数据:
watch是看看data有没有数据,没有我就没活儿就歇着了
computed 是如果没有data没有数据,我又需要,那我就自己造变量用于计算2.各自的图:
上图解析#:
Computed:一个数据受多个数据的影响
验证:total受abc三个数据的影响
Computed :.{
Total:this.a*this.b*this.c
}
------------------------------------------------------------end👆-------------------------------------------------------------
-----------记 🌟🌟题:------------------start👇---------------------------------------------------------
// 初始化
const model = {
name: 'vue',
};
// 创建监听器
function observe(obj) {
let val = obj.name;
Object.defineProperty(obj, 'name', {
get() {
return val;
},
set(newVal) {
// 当有新值设置时,执行setter
console.log(`name变化:从${val}到${newVal}`);
// 解析到页面
compile(newVal);
val = newVal;
}
})
};
function compile(val) {
document.querySelector('#name').value = val;
};
observe(model); // 调用监听器,对model开始监听
------------------------------------------------------------🌟🌟end👆------------------------------------------------------------
- 1. 如果在data中没有相应的属性的话,是不能watch的,这点和computed不一样。
-
就算在data中没有直接声明出要计算的变量,也可以直接在computed中写入。
计算属性默认只有getter,可以在需要的时候自己设定setter:
记:测试v-for和v-if如果应用在同一个标签内,v-for优先级高。如果不在一个标签内,就该咋滴咋滴,按顺序执行。https://www.jb51.net/article/203971.htm
-
本例子🌰: 测试证明v-for和v-if如果应用在同一个标签内,v-for优先级高。 看vue源码也证实了结论:同一个标签内,v-for优先级确实比v-if高 判断依据是:模版对应的渲染函数代码👇👇👇 // vue模版语法 <div id="app"> <p v-if="isShow" v-for="item in items"> {{ item.title }} </p> </div> vue模版语法👆------>经app.$options.render方法 转换成------>对应的渲染函数👇: // 对应渲染函数 ƒ anonymous() { with (this) { return _c('div', { attrs: { "id": "app" } }, _l((items), function (item) { return (isShow) ? _c('p', [_v("\n" + _s(item.title) + "\n")]) : _e() }), 0) } } 知识点🌹: render渲染函数源码中_l意思是列表渲染 注意事项⚠️*: 永远不要把 v-if 和 v-for 同时用在同一个元素上,带来性能方面的浪费(因为每次渲染都会先循环再进行条件判断) 解决办法💡: 在外层嵌套template(页面渲染不生成dom节点),在这一层进行v-if判断,然后在内部进行v-for循环 <templete v-if="isShow"> <p v-for="item in items"></p> </templete> 下边这句还不理解🤔️:➕ 如果条件出现在循环内部,可通过计算属性computed提前过滤掉那些不需要显示的项 computed: { items: function() { return this.list.filter(function (item) { return item.isShow }) } }
先收集依赖流程:
observe ->
walk ->
defineReactive ->
get ->
dep.depend() ->
watcher.addDep(new Dep()) ->
watcher.newDeps.push(dep) ->
dep.addSub(new Watcher()) ->
dep.subs.push(watcher)
依赖收集会经过以上流程,最终watcher.newDeps数组中存放dep列表,dep.subs数组中存放watcher列表。
为什么v-for和v-if不能连用?
通过用app.$options.render方法得到模版(含v指令)对应的渲染函数。依据函数结果:
初步结论:v-for优先级是比v-if高
为什么要进行依赖收集?
new Vue({
data(){
return {
name:'zane',
sex:'男'
}
}
})
上面的data中,实际上页面只使用到了name,并没有使用age,根据Object.defineProperty的转换,如果我们设置了this.sex='女',那么Vue也会去执行一遍虚拟DOM的比较,这样就无形的浪费了一些性能,因此才需要做依赖收集,界面用到了就收集,没有用到就不收集。------【Object.defineProperty的缺点: 不会变通,性能低】
Dep相关的用法的作用?
new Dep() //
dep.notity() //
dep.depend() //
dep.addSub(new Watcher()) //
dep.subs.push(watcher) //
Dep.target ===Watcher实例化对象: 通知订阅的watcher更新
Dep.prototype.notify = function notify () {}
更新触发的依赖:
set ->
dep.notify() ->
subs[i].update() ->
watcher.run() || queueWatcher(this) ->
watcher.get() || watcher.cb ->
watcher.getter() ->
vm._update() ->
vm.__patch__()
1.当数据发生变化时,Obderver、Dep、Watcher都会第一时间知道吗?
2.Vue的异步队列的思路以及实现原理?👇
$nextTick、$nextTick 接口 是干嘛的?通过 nextTick 放入 callbacks 如何理解?Vue 中就是 flushSchedulerQueue?
1⃣️flushSchedulerQueue:
将队列中所有的 watcher 按照 id 进行排序、原因是?---要保证 watcher 按照正确的顺序执行(参照初始/原本的依赖关系), 之后再遍历队列依次执行其中的 watcher。
2⃣️nextTick:
xx->改成异步执行
目的:在一个事件循环(clickHandler)中让 flushSchedulerQueue 只执行一次,避免多次执行遍历、渲染,耗费性能
5. new Vue实例初始化data的2种写法?
// 第1种
new Vue({
el: 'example1',
data(){
return(){
a:1,
b:2
}
}
});
// 第2种
new Vue({
el: 'example2',
data:{
a:1,
b:2
}
});
6. 通过<div>{{ words }}</div>这个模版,猜测这个{{ words }}都可能来自于哪里?
1⃣️new Vue实例中的data初始化变量
2⃣️new Vue实例中的computed计算属性
<div id="example">
<div>{{ words }}</div>
<input type="button" @click="clickHanler" value="click"/>
</div>
var vm = new Vue({
el:"#example",
data: {
name: "Devin",
greetings: "Hello"
},
computed: {
words: function(){
return this.greetings + ' ' + this.name + '!'
}
},
methods: {
clickHanler(){
this.name = 'Devinn';
this.name = 'Devinnzhang';
this.greetings = 'Morning';
}
}
});
#解析:
此时 Vue 中创建了两个 watcher:
一个是渲染 watcher,负责渲染模板,一个是 computed watcher 负责计算属性。
#场景➕问题:
操作场景:当点击 clickHandler 的时候数据发生变化会通知道两个 watcher要更新。
Q1、渲染watcher 和 computed watcher 同时订阅了变化的数据,哪一个先执行,执行顺序是怎么样的?
答:可见,在数据更新的阶段,需要一个管理多个 watcher 进行更新的角色。
此角色需要保证: watcher 能够按照正确的顺序执行&并尽可能的减少执行次数。
在Vue中,这个角色===>异步队列: 是在数据更新时开启的(肯定的哇)===> new Dep();
当数据发生变化时,会调用 dep.notity() 进而通知订阅的 watcher 进行 更新
Q2、在一个事件循环中 name 的变化触发了两次,greetings 触发了一次,对应两个 watcher 一共执行了几次?DOM渲染了几次?
关于Vue数据响应式,常见的场景有下面这几个:
- 数据变 → 使用数据的视图变
- 数据变 → 使用数据的计算属性变 → 使用计算属性的视图变
- 数据变 → 开发者主动注册的watch回调函数执行
三个场景,对应三种watcher:
- 负责敦促视图更新的render-watcher
- 执行敦促计算属性更新的computed-watcher
- 用户注册的普通watcher(watch-api或watch属性)
4.Vue的渲染机制?--(见:onsite文档)
templete模版- compiler编译-->ats🌲-【loader/vue-templete-compilerwebpack工具编译】->render渲染函数--【源码调用render函数with方法】-->生成虚拟DOM--【vm._update(){} 2个执行时机】->都能将虚拟DOM转成真实DOM