Skip to content

careteenL/vue

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

26 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

VUE3.0

学习并实现vue3.0

目录

TODO

  • 实现{{xxx}}
  • 实现v-model
  • 实现v-text
  • 实现v-html
  • 实现v-if
  • 实现v-show
  • 实现v-for
  • 实现computed
  • 实现watch
  • 实现life cycle
  • 实现VNode
  • 实现DOM Diff

VUE2 实现思路

  • 数据劫持
    • 使用Object.defineProperty递归实现。
    • 不足
      • 当数据层级较深时:递归次数会变多,可能存在内存泄露,进而影响性能。
      • 对象不存在的属性不能被拦截
  • 处理数组
    • 需要重写数组原型上的方法(原地修改的方法:push/pop/sort/reverse/splice
    • 使用AOP的思想重写,在执行原方法的同时,执行更新视图的操作函数
    • 不足
      • 数组改变 length 会无效,因为内部没做 watch
  • 依赖收集
    • 在编译阶段,模板生成的是一个字符串,
    • 当模板中有用到某个变量时,会使用正则去匹配这个双大括号并用this.$data去替换对应的变量
      • 与此同时,将这个变量使用watcher进行监听
        • 当页面呈现时会取到这个值,于是触发数据劫持部分的getter将其放入发布订阅机制的deps依赖中,页面可能多处调用,因此使用队列维护
        • 当在代码中设置这个值时,会触发数据劫持部分的setter将执行这个发布订阅实例notify发布方法去调用每个watcherupdate更新方法去更新视图
        • watcherupdate更新方法就是操作dom
  • 代理$data/computed/watch/methods
    • 使用Object.definePropertygetter实现取值代理

VUE3 实现思路

建议先看单元测试test tell everything,知道具体功能后,思考如果是自己会如何实现,然后再看源码。

  • 数据劫持
    • 使用ProxyReflect实现
      • 优势
        • 对象不存在的属性能被拦截
        • 不是一上来就就行递归数据劫持,而是在 get 中按需递归:当层级较深的对象赋值时再去递归取值再赋值
        • 使用Reflectset有返回值去告知是否设置成功,弥补直接赋值时writable=false的情况下不会报错也不成功的问题
        • Reflect主要作用是取代Object上的方法
      • 缺点
        • 兼容性比较差 ie11 都不支持
    • 避免多次调用Proxy
      • 使用WeakMap映射表做缓存
        • 如果代理过直接返回代理后的对象
        • 如果对象就是已经代理后的,不用再次代理,直接返回即可
  • 处理数组
    • 不需要重写原型上的方法,Proxy能监听数组的变化
    • 当调用原型上原地修改的方法(push/pop/sort/reverse/splice)时,会先操作数据,然后更改数组的length属性(这是无意义的修改,不应该再次渲染视图),所以屏蔽的方式是将新值和旧值对比,如果没修改且数组长度没变化就不去渲染视图
  • 依赖收集
    • track
    • 利用 js 是单线程的特性
      • 目标:在调用effect方法时,传参是一个函数,首先会立即执行一次,当函数内部的reactive数据变化时,会再次触发这个函数。
      • 实现:在effect内部会先执行一次传参的函数,由于 js 单线程特性,会执行其中代码,
        • 若含有reactive的数据在取值时会走Proxygetter,此时去对依赖进行收集,收集的方式是维护一个WeakMap,其键为原对象,值为一个Map去存放原对象上的每个key,这个Map的键为原对象的key,值为一个Set去存放所有依赖的effect的传参函数;
        • 当在改变已reactive的对象的某个键值时,会触发Proxysetter,此时直接对上面依赖收集时维护的WeakMap直接取值,去遍历执行对应effect函数。
    • 几个问题
      • 为什么使用WeakMap而不直接使用Map
        • 移步Why WeakMap
          • Node垃圾回收机制
            • 参考资料深入理解 Node.js 垃圾回收与内存管理
            • 引用计数 - 新生代
            • 老生代
              • 标记清除(Mark-Sweep):执行函数时,会有进入环境和离开环境,将变量进行标记,最后将没有标记的变量进行清除
                • 优点:快
                • 缺点:形成内存碎片,浪费空间
              • 标记整理(Mark-Compact):将引用计数>0 的变量移动到内存空间的一端,将引用计数=0 的变量移动内存空间的另一端,最后清除另一端的所有变量
                • 优点:不会形成内存碎片,充分利用内存
                • 缺点:比较慢
              • 增量标记(Mark-Increment):是对标记整理的优化,让整理的过程进行分割,比如每标记整理十个变量就不占用空间去执行一段代码,然后再标记整理;将标记整理需要很长一段时间的缺点进行分割到每个非常小的时间段。

DOM-DIFF

模拟虚拟dom并实现dom-diff,运行下面命令进行测试

npm i
npm run test:vdom

核心代码存放在vdom

总结

引用

Releases

No releases published

Packages

No packages published