【小玩意】--伪数组迭代器
对象是没有迭代器的,所以对于伪数组有迭代器需求的情况我可以手动在Object原型上定义一个迭代器,用于迭代伪数组
1 | let fakeArr = { |
对象是没有迭代器的,所以对于伪数组有迭代器需求的情况我可以手动在Object原型上定义一个迭代器,用于迭代伪数组
1 | let fakeArr = { |
模仿steam卡牌效果,鼠标悬浮会把卡牌按压进去的效果
1 | <!DOCTYPE html> |
delete a[1]
后 1 in a === false
说到WeakMap不得不说Map了,他们的相同之处在于都是用一个key 对应一个 value
1 | function deepClone(obj) { |
1 | function deepClone(obj) { |
1 | //测试 |
适用范围同 MessageChannel 方法的
1 | function deepClone(obj) { |
1 | import { cloneDeep } from 'loadsh' |
1 | let newObj = JSON.parse(JSON.stringify(obj)) |
1 | function deepClone(obj, hash = new WeakMap()) { |
1 | function deepClone(){ |
虚拟DOM是一个在内存中的数据结构,它是真实DOM的抽象表示。虚拟DOM是由一系列的虚拟节点(VNode)组成的,每一个虚拟节点都对应一个真实的DOM节点。
作用:
虚拟DOM的主要作用是提供一个在内存中操作DOM的平台,使得我们可以在不直接操作真实DOM的情况下进行复杂的操作。当状态变化时,Vue会生成一个新的虚拟节点树,然后和旧的虚拟节点树进行对比,找出差异,然后只更新差异部分到真实的DOM上,这个过程称为“打补丁”。
优势:
性能优化:由于直接操作DOM通常是前端性能瓶颈的主要原因,虚拟DOM通过在内存中进行计算,避免了直接操作DOM,从而提高了性能。
跨平台:虚拟DOM不仅可以表示浏览器中的DOM,还可以表示其他的平台,比如移动应用的UI。
缺陷:
首次渲染开销大:虚拟DOM需要在内存中构建一颗DOM树,所以在首次渲染时,虚拟DOM的开销会比直接操作DOM大。
内存消耗:虚拟DOM需要在内存中维护一颗DOM树,这会消耗一定的内存。
一个虚拟Dom节点至少包括三个属性:type、attr、children
effect是一个‘桶’, 他收集并触发指定方法
1 | /** |
计算属性的实现流程
1 | const fullName = computed(()=> firstName.value + lastName.value) |
reactive 只能监听复杂数据类型,因为proxy只能监听负责数据类型的getter和setter
1 | function reactive(obj){ |
为什么用Reflect
Reflect 就是反射,可以直接调用对象的基本方法。
1 | let obj = { |
1 | const proxy = new Proxy(obj, { |
1 | const proxy = new Proxy(obj, { |
那么ref怎么监听原始值类型?
利用了get 和 set 标记语法
1 | class RefImpl { |
1 | // 测试一下! |
上述effect函数和activeEffect都是简化后的,没有判断该副作用到底依赖了谁
把template模板编译为render函数
三大流程:
1 | <div v-if="isShow"> |
得到的AST如下
1 | { |
在原生元素上
transform之后不会生成modelValue属性,但会生成onUpdate:modelValue回调函数和vModelText
自定义指令
vModelText指令在create钩子里监听input或change事件,来调用onUpdate:modelValue实现view流向vm; 在mount和beforeUpdate钩子里修改原生元素的value实现 model->view
在组件上
const model = defineModel() // 返回一个ref
首先在vue编译阶段
此时v-model当作一个属性名
transform函数内部调用不同的函数来解析各种指令。经过transform处理AST,表示该节点的对象的属性codeGenNode
从 undefined 变成一个对象
两张图片看到经过处理后的codeGenNode对象内部。其中properties数组里每一个item都是有key和value。modelValue对应值,onUpdate:modelValue对应方法。
他们都用在后续步骤拼接成render函数。
例如: transformModel、transformIf
把上面两个放入组件实例里
此时v-model最终被编译成了 :modelValue
属性 和 @update:modelValue
事件
至此初次渲染完成,如果改变了响应式数据导致需要更新视图则会进入更新阶段
本质是一个对比算法,旧DOM更新为新DOM时怎么提高效率
但是不是DOM更新了就会触发diff,而是DOM挂载、卸载或变换顺序才会触发diff
原本两个树的完全的 diff 算法是一个时间复杂度为 O(n3) , Vue 进行了优化转换成 O(n) 复杂度的问题(只比较同级不考虑跨级问题)
节点比较:首先,Vue.js会比较新旧两个虚拟DOM树的根节点。如果根节点类型不同(例如,一个是div,另一个是p),那么Vue.js会直接替换旧的根节点及其所有子节点。如果根节点类型相同,那么Vue.js会保留该节点,并进一步比较其属性和子节点。
属性比较:Vue.js会比较同一节点的新旧属性,包括样式、类名、事件监听器等。如果新旧属性不同,那么Vue.js会更新这些属性。
子节点比较:如果节点类型相同并且属性也相同,那么Vue.js会进一步比较节点的子节点。这是一个递归过程,Vue.js会继续比较子节点的节点类型、属性和子节点,直到所有节点都被比较。
列表优化:在比较子节点时,Vue.js使用了一种称为keyed diffing的优化策略。通过给每个子节点分配一个唯一的key,Vue.js可以更快地找到新旧子节点列表中相同的节点,从而避免不必要的节点创建和删除操作。
1 | <!-- 原本 item = [{id=1,title='a'},{id=2,title='b'},{id=3,title='c'}] --> |
此时diff算法开始工作,
首先,怎么比较?isSameVNodeTyppe(n1: VNode, n2: VNode)
方法通过比较n1和n2的type
和key
,如果都相等就说明没有变化,无需挂载卸载DOM元素
上述情况可知,第三个li变了需要更新
sync from start:自前向后的对比
sync from end:自后向前的对比
common sequence + mount:新节点多于旧节点,需要挂载
common sequence + unmount:旧节点多于新节点,需要卸载
unknown sequence:乱序
1 | //前者是一组旧DOM的key数组 后者是更新后新DOM的key数组 |
阶段5是最复杂的,单独考虑一个进入阶段5的例子
1 | oldChildren= [a, b, c, d, e, f, g] |
1 | function _update(vnode) { |