前:Vue和React的区别
lq782655835.github.io/blogs/vue/d…
1. vue需要背指令,React更像原生JavaScript
2.响应式原理不同
3.事件机制不同
1.computed和watch的选择
一般首选computed。有异步操作的时候,不能用computed,因为计算属性必须有返回值。
2.指令相关
v-pre
不会被编译,比如{{msg}}就直接展现出先来了。
自定义指令
第一个参数element是绑定的dom,第二个参数是绑定关系(包含value)。
对象式定义,可以指定触发时机。若函数式,默认是在绑定时和update时触发。
vue2和vue3区别
响应式处理
definepropty只能拦截set和get,因此,当我们给对象新增属性或者删除属性,它无法拦截。
vue3为什么用Reflect
可以发现,不用Reflect也行。但是,如果target对象中有 访问器属性(getter),那么get内部的this不是代理对象,我们需要将他设置为代理对象的话,就必须用Reflect。
const tempObj1 = {
a: 1,
get value() {
console.log("this是不是proxyObj", this === proxyObj); // false
return this.a;
},
};
const handler = {
get: function (obj, prop, receiver) {
// return obj[prop];
return Reflect.get(obj, prop, receiver);
},
set: function (obj, prop, value, receiver) {
obj[prop] = value;
return true;
},
};
const proxyObj = new Proxy(tempObj1, handler);
proxyObj.value; // 1
Vue有哪些生命周期
执行顺序如下:
自定义事件
发送方:context.emit()
接收方:注意,如果是子组件A emit的事件,那么@event就得绑定在A组件身上(绑在其他组件身上无效)
事件总线
封装事件总线:
发送方:
接收方: 在onMounted()声命周期中开始监听
解除事件绑定:
计算属性
完整写法如下,如果简写,直接在computed里写回调即可。
侦听/监视属性
直接包裹在watch里面:
Vue 响应式
observe: 循环遍历 data 对象,对于 data 的每个属性,使用 defineReactive 处理。
defineReactive: 使用 Object.defineProperty() ,访问或修改属性会触发 get 或者 set。每个属性有一个 dep 数组,数组里的每个元素都是一个watcher对象。
get:首次渲染的时候触发 get ,会将当前的 watcher 添加到 dep 数组中。
set:每次修改 data 的属性时,触发 set 。在 set 中,遍历该属性对应的 dep 数组,每遍历到一个 watcher 对象,就调用 watcher.update() ,更新视图。
可能的追问:
1.defineReactive 的属性也是对象怎么办?递归调用 observe ,继续循环遍历它的每个属性。
2.update如何改变视图?
例如更改某个属性:
let dom = document.getElementById('1');
dom.setAttribute('attr', 'diy');
3.视图改变了(比如input框),data的值如何改变?
遍历所有 DOM,如果某个 DOM 有 v-model属性,则给他 addEventListener(),修改 Vue 实例上的属性。
虚拟DOM
1.本质是JavaScript对象,一般包含 tag ,props,children
优势:
无需手动操作 DOM: 我们不再需要手动去操作 DOM(比如先 getElement,然后修改 innerHTML ),只需要写好 View-Model 的代码逻辑,框架会根据虚拟 DOM 和 数据双向绑定,帮我们以可预期的方式更新视图,极大提高我们的开发效率。
跨平台能力:虚拟DOM是与平台无关的中间层,使得Vue可以在不同的平台上运行,例如浏览器、移动端、服务器等。通过将虚拟DOM渲染成不同平台的实际DOM,实现了跨平台的能力。
数据改变引起虚拟DOM更新,虚拟DOM更新引起真实 DOM 更新。通过 Diff 算法找出最佳更新方法。
缺点:
首次渲染比较耗时:由于虚拟DOM需要将数据映射到虚拟DOM树并进行比对才能更新实际的DOM,因此初始渲染可能会比直接操作实际DOM略慢一些。这是因为需要进行额外的计算和比对过程。
简而言之:先构建虚拟DOM(Javasctipt对象),再生成真实DOM。
Diff算法
特点:只会同层比较,不会跨层比较,比如图中只会比较相同颜色的部分:
同一层级 比较 标签名、key值,如果两个值都一样,则认为是同一个节点。以此类推比较下一层级。发现有不同,则进行更新属性。
创建新真实DOM
如何调用 createElement():patch函数内调用
更新子节点
上面说的是新旧虚拟DOM不同,因此删除旧的真实DOM,创建新的真实DOM。那么,如果新旧虚拟DOM相同,只需要更新 children 应该怎么做呢?
命中是什么意思?命中的意思是,key 相同,标签名 也相同。新前和旧前如果命中,都是后移;新后和旧后如果命中,都是前移。循环条件是(新前 <= 新后 && 旧前 <= 旧后)。
需要注意的是,命中之后,先执行 patch(),再移动指针。
针对第三种和第四种情况的特殊说明:如何移动真实 DOM :
如果四种策略都没命中,则 for 循环旧的节点列表(寻找新前)。找到后将该节点移动到新的位置(旧前之前)。
- 如果旧的节点先循环结束,那么新前和新后之间剩余的部分就是需要新增的节点,我们应该创建相应的
真实DOM。 创建完之后,插入在哪?见代码。
- 如果新的节点先循环结束,那么旧前和旧后之间剩余的部分就是需要删除的节点,我们应该删除相应的
真实DOM。
nextTick
在下例中,message已经改变了,为什么dom.innerHTML仍然是旧值?因为DOM更新是异步操作,我们希望在下一次 dom更新结束后执行其指定的回调,就必须这么写。
<template>
<div>
<button @click="updateData">更新数据</button>
<p>{{ message }}</p>
</div>
</template>
<script>
export default {
data() {
return {
message: "初始值",
};
},
methods: {
updateData() {
this.message = "更新后的值";
// this.$nextTick(() => {
// // DOM已经更新完成,可以获取到正确的结果
// const updatedValue = this.$el.querySelector("p").textContent;
// console.log(updatedValue); // 输出:更新后的值
// });
const updatedValue = this.$el.querySelector("p").textContent;
console.log(updatedValue); // 输出:更新后的值
},
},
};
</script>