• Vue3 响应式原理


     响应式原理

    Vue2 使用的是 Object.defineProperty  Vue3 使用的是 Proxy

    2.0的不足

    对象只能劫持 设置好的数据,新增的数据需要Vue.Set(xxx)  数组只能操作七种方法,修改某一项值无法劫持。数组的length修改也无法劫持

    reactive和effect的实现

    1. export const reactive = <T extends object>(target:T) => {
    2. return new Proxy(target,{
    3. get (target,key,receiver) {
    4. const res = Reflect.get(target,key,receiver) as object
    5. return res
    6. },
    7. set (target,key,value,receiver) {
    8. const res = Reflect.set(target,key,value,receiver)
    9. return res
    10. }
    11. })
    12. }

     Vue3 的响应式原理依赖了 Proxy 这个核心 API,通过 Proxy 可以劫持对象的某些操作。

    effect track trigger

    实现effect 副作用函数

    1. let activeEffect;
    2. export const effect = (fn:Function) => {
    3. const _effect = function () {
    4. activeEffect = _effect;
    5. fn()
    6. }
    7. _effect()
    8. }

     使用一个全局变量 active 收集当前副作用函数,并且初始化的时候调用一下

    实现track

    1. const targetMap = new WeakMap()
    2. export const track = (target,key) =>{
    3. let depsMap = targetMap.get(target)
    4. if(!depsMap){
    5. depsMap = new Map()
    6. targetMap.set(target,depsMap)
    7. }
    8. let deps = depsMap.get(key)
    9. if(!deps){
    10. deps = new Set()
    11. depsMap.set(key,deps)
    12. }
    13. deps.add(activeEffect)
    14. }

    执行完成成后我们得到一个如下的数据结构 

    实现trigger

    1. export const trigger = (target,key) => {
    2. const depsMap = targetMap.get(target)
    3. const deps = depsMap.get(key)
    4. deps.forEach(effect=>effect())
    5. }

     当我们进行赋值的时候会调用 set 然后 触发收集的副作用函数

    1. import {track,trigger} from './effect'
    2. export const reactive = <T extends object>(target:T) => {
    3. return new Proxy(target,{
    4. get (target,key,receiver) {
    5. const res = Reflect.get(target,key,receiver) as object
    6. track(target,key)
    7. return res
    8. },
    9. set (target,key,value,receiver) {
    10. const res = Reflect.set(target,key,value,receiver)
    11. trigger(target,key)
    12. return res
    13. }
    14. })
    15. }

     给 reactive 添加这两个方法

    测试代码

    1. <!DOCTYPE html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
    6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
    7. <title>Document</title>
    8. </head>
    9. <body>
    10. <div id="app">
    11. </div>
    12. <script type="module">
    13. import { reactive } from './reactive.js'
    14. import { effect } from './effect.js'
    15. const user = reactive({
    16. name: "小满",
    17. age: 18
    18. })
    19. effect(() => {
    20. document.querySelector('#app').innerText = `${user.name} - ${user.age}`
    21. })
    22. setTimeout(()=>{
    23. user.name = '大满很吊'
    24. setTimeout(()=>{
    25. user.age = '23'
    26. },1000)
    27. },2000)
    28. </script>
    29. </body>
    30. </html>

    递归实现reactive

    1. import { track, trigger } from './effect'
    2. const isObject = (target) => target != null && typeof target == 'object'
    3. export const reactive = <T extends object>(target: T) => {
    4. return new Proxy(target, {
    5. get(target, key, receiver) {
    6. const res = Reflect.get(target, key, receiver) as object
    7. track(target, key)
    8. if (isObject(res)) {
    9. return reactive(res)
    10. }
    11. return res
    12. },
    13. set(target, key, value, receiver) {
    14. const res = Reflect.set(target, key, value, receiver)
    15. trigger(target, key)
    16. return res
    17. }
    18. })
    19. }
    1. <!DOCTYPE html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
    6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
    7. <title>Document</title>
    8. </head>
    9. <body>
    10. <div id="app">
    11. </div>
    12. <script type="module">
    13. import { reactive } from './reactive.js'
    14. import { effect } from './effect.js'
    15. const user = reactive({
    16. name: "小满",
    17. age: 18,
    18. foo:{
    19. bar:{
    20. sss:123
    21. }
    22. }
    23. })
    24. effect(() => {
    25. document.querySelector('#app').innerText = `${user.name} - ${user.age}-${user.foo.bar.sss}`
    26. })
    27. setTimeout(()=>{
    28. user.name = '大满很吊'
    29. setTimeout(()=>{
    30. user.age = '23'
    31. setTimeout(()=>{
    32. user.foo.bar.sss = 66666666
    33. },1000)
    34. },1000)
    35. },2000)
    36. </script>
    37. </body>
    38. </html>

     

  • 相关阅读:
    maven仓库-阿里镜像-下载问题
    企业公众号怎么做内容?这四个阶段要做好
    C++虚函数的vptr与vtable
    MySQL游标
    web自动化测试 —— cypress测试框架
    vue父组件如何向子组件传递数据?
    【C++】类和对象(中)
    OA项目之会议通知(查询&是否参会&反馈详情)
    Codeforces Round 905 (Div. 3)ABCDEF
    SEO方案尝试--Nuxtjs项目基础配置
  • 原文地址:https://blog.csdn.net/qq1195566313/article/details/127563892