飞道的博客

Vue3 响应式原理

399人阅读  评论(0)

 响应式原理

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

2.0的不足

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

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>

 

 


转载:https://blog.csdn.net/qq1195566313/article/details/127563892
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场