vue3.0-数据响应式原理

配置环境

  1. git clone https://github.com/vuejs/vue-next.git
  2. 安装依赖: yarn
  3. 生成sourcemap文件,修改package.json
  4. "dev": "node scripts/dev.js --sourcemap"
  5. 编译: yarn dev
sourcemap
dev 命令执行的文件是 node scripts/dev.js,打开后可以看到
在后面传递的选项是在 minimist中生效的。
vue3.0-数据响应式原理_第1张图片
解析为一个对象, args

源码结构

  • 所有内容以模块化的方式进行展示,源码在packages里面;

vue3.0-数据响应式原理_第2张图片

  • 检验是否打包成功:vue-next/packages/vue/dist/可能这个路径是否存在,vue.global.js.map文件

vue3.0-数据响应式原理_第3张图片

测试的demo文件,路径:vue-next/packages/vue/examples/classic文件中
vue3.0-数据响应式原理_第4张图片

比如打开一个todolist,或者自己创建测试文件,也是在这个文件夹下 (vue-next/packages/vue/examples):vue-next/packages/vue/examples/classic/todomvc.html
vue3.0-数据响应式原理_第5张图片

可以打断点,来看createApp如何创建
vue3.0-数据响应式原理_第6张图片

vue3.0的使用

  1. option的API还是可以继续使用的,比如 “@click”
  2. 新增Composition API,基于函数的逻辑服用组合,相关的逻辑放在一个setup中

Composition API的使用




  
  
  
  Document


  

{{state.message}}

单值响应式
  // 单值响应式
  const counter =ref(0)

  // 修改单值响应式,因为用了ref 是一个对象,修改需要访问它的value
  setInterval(()=>{
     counter.value++
  },
转化单值响应式

toRefs:可以把一个响应式的对象,里面的每个属性转化成单值的ref对象;在进行展开(...)之后,就都是单值的响应式。
在页面中,想直接 使用message,而不是{{state.message}};
不能在导出的时候,结构state;...state是错误的行为;会破坏数据响应式。再去触发点击事件的时候,会发现无效;无法实现数据响应式。

...toRefs(state)
// toRefs 会将每个属性转化成单值的ref对象
// 在进行展开(...)之后,就都是单值的响应式。

vue3.0数据响应式原理

vue2.0的数据响应式
  1. 对象响应化:遍历key,设置getter,setter
  2. 数据响应化:覆盖数组方法
vue2.0数据响应式弊端
  1. 响应化过程需要递归遍历,消耗较大
  2. 新加或删除属性无法监听
  3. 数组响应化需要额外实现
  4. Map、Set、Class等无法响应式
  5. 修改语法有限制
vue3.0数据响应式采用ES6的proxy特性,对象和数组都可以拦截
const observer = new Proxy(obj,{
    get(){},
    set(){},
    deleteProperty(){}
})
数据拦截

function reactive(obj){
  // 将传进来的对象,做一次代理
  if(typeof obj!=='object'){ // 不是对象就直接return 
    return obj
  }
  //做代理
  // https://es6.ruanyifeng.com/#docs/proxy
  const observed = new Proxy(obj,
    {
      // 获取
      get(target, key){
        console.log('get',key)
        // http://es6.ruanyifeng.com/#docs/reflect
        return Reflect.get(target,key)
      },
      // 修改
      set(target, key,value){
        console.log('set', key)
        return Reflect.set(target,key, value)
      },
      // 删除
      deleteProperty(target, key){
        console.log('del',key)
        return Reflect.deleteProperty(target, key)
      }
    })
  return observed
}

const state =reactive({
  name:'ohhh'
})

state.name
state.name ='ohhhhhhh'
delete state.name

vue3.0-数据响应式原理_第7张图片

  • 拦截两层的话,还需要修改:
 const state =reactive({
    a:{
        b:3333
    }
 })
const isObject = val =>val !==null && typeof val==='object'
  // 获取
  get(target, key){
    console.log('get',key)
    // http://es6.ruanyifeng.com/#docs/reflect
    res = Reflect.get(target,key)
    console.log('res',res)
    return isObject(res)?reactive(res):res
  },

vue3.0-数据响应式原理_第8张图片

  • 对象里存对象-补全代码
 const isObject = val =>val !==null && typeof val==='object'
function reactive(obj){
  // 将传进来的对象,做一次代理
  if(!isObject(obj)){ // 不是对象就直接return 
    return obj
  }
  //做代理
  // https://es6.ruanyifeng.com/#docs/proxy
  const observed = new Proxy(obj,
    {
      // 获取
      get(target, key){
        console.log('get',key)
        // http://es6.ruanyifeng.com/#docs/reflect
        res = Reflect.get(target,key)
        console.log('res',res)
        return isObject(res)?reactive(res):res
      },
      // 修改
      set(target, key,value){
        console.log('set', key)
        return Reflect.set(target,key, value)
      },
      // 删除
      deleteProperty(target, key){
        console.log('del',key)
        return Reflect.deleteProperty(target, key)
      }
    })
  return observed
}

const state =reactive({
  name:'ohhh',
  a:{
    b:333
  }
})

// state.name
// state.name ='ohhhhhhh'
// delete state.name
state.a.b

  • 避免重复代理
reactive(state)
// 缓存
// key 可以为对象,可以存储为:{obj(穿进来的纯对象,还没有被代理过):observerd(代理过的对象)}
const toProxy = new WeakMap()
const toRaw = new WeakMap() // 这个就是相反的{observed:obj}
  // 检测缓存
  if(toProxy.has(obj)){
    // 代理过了,直接返回缓存结果
    return toProxy.get(obj)
  }
  if(toRaw.has(obj)){
    // key已经是代理对象,直接返回
    return obj
  }
  //做缓存
  toProxy.set(obj,observed)
  toRaw.set(observed,obj)
console.log(reactive(state)===state) // true

完整代码:

const isObject = val =>val !==null && typeof val==='object'
// 缓存
// key 可以为对象,可以存储为:{obj(穿进来的纯对象,还没有被代理过):observerd(代理过的对象)}
 const toProxy = new WeakMap()
 const toRaw = new WeakMap() // 这个就是相反的{observed:obj}

function reactive(obj){
  // 将传进来的对象,做一次代理
  if(!isObject(obj)){ // 不是对象就直接return 
    return obj
  }
  // 检测缓存
  if(toProxy.has(obj)){
    // 代理过了,直接返回缓存结果
    return toProxy.get(obj)
  }
  if(toRaw.has(obj)){
    // key已经是代理对象,直接返回
    return obj
  }

  //做代理
  // https://es6.ruanyifeng.com/#docs/proxy
  const observed = new Proxy(obj,
    {
      // 获取
      get(target, key){
        console.log('get',key)
        // http://es6.ruanyifeng.com/#docs/reflect
        res = Reflect.get(target,key)
        console.log('res',res)
        return isObject(res)?reactive(res):res
      },
      // 修改
      set(target, key,value){
        console.log('set', key)
        return Reflect.set(target,key, value)
      },
      // 删除
      deleteProperty(target, key){
        console.log('del',key)
        return Reflect.deleteProperty(target, key)
      }
    })
  //做缓存
  toProxy.set(obj,observed)
  toRaw.set(observed,obj)
  return observed
}

const state =reactive({
  name:'ohhh',
  a:{
    b:333
  }
})

// state.name
// state.name ='ohhhhhhh'
// delete state.name
// state.a.b

console.log(reactive(state)===state)

依赖收集
const isObject = val =>val !==null && typeof val==='object'
// 缓存
// key 可以为对象,可以存储为:{obj(穿进来的纯对象,还没有被代理过):observerd(代理过的对象)}
 const toProxy = new WeakMap()
 const toRaw = new WeakMap() // 这个就是相反的{observed:obj}

function reactive(obj){
  // 将传进来的对象,做一次代理
  if(!isObject(obj)){ // 不是对象就直接return 
    return obj
  }
  // 检测缓存
  if(toProxy.has(obj)){
    // 代理过了,直接返回缓存结果
    return toProxy.get(obj)
  }
  if(toRaw.has(obj)){
    // key已经是代理对象,直接返回
    return obj
  }

  //做代理
  // https://es6.ruanyifeng.com/#docs/proxy
  const observed = new Proxy(obj,
    {
      // 获取
      get(target, key){
        console.log('get',key)
        // http://es6.ruanyifeng.com/#docs/reflect
        const res = Reflect.get(target,key)
        console.log('res',res)
        track(target, key)
        return isObject(res)?reactive(res):res
      },
      // 修改
      set(target, key,value){
        console.log('set', key)
        const ret = Reflect.set(target,key, value)
        trigger(target, key)
        return ret
      },
      // 删除
      deleteProperty(target, key){
        console.log('del',key)
        trigger(target, key)
        return Reflect.deleteProperty(target, key)
      }
    })
  //做缓存
  toProxy.set(obj,observed)
  toRaw.set(observed,obj)
  return observed
}
// 临时保存响应函数
const effectStack =[]

function effect(fn){
  const rxEffect =function(){
    try {
      // 入栈
      effectStack.push(rxEffect)
      return fn()
    } finally {
      // 出栈
      effectStack.pop()
    }
  }
  // 立即调用
  rxEffect()
  return rxEffect
}
//存储数据关系的结构 ,大管家
// 数据结构 {target:{key: [cb1,cb2, ......]}}
const targetMap= new WeakMap()

// 收集依赖
function track(target, key){
  //  console.log(target, key,'000000000')
  // 获取响应关系
  const effect = effectStack[effectStack.length-1]
  if(effect){
    //获取target的依赖关系
    let depsMap = targetMap.get(target)

    if(!depsMap){
      // 首次不存在 
      depsMap = new Map()
      // 创建
      targetMap.set(target, depsMap)
    }
    // 获取key对应的响应函数
    let deps = depsMap.get(key)
    if(!deps){
      deps =new Set()
      depsMap.set(key,deps)
    }
    // 存放一次回调函数
    if(!deps.has(effect)){
      deps.add(effect)
    }
  }
}
// 触发执行
function trigger(target, key){
  const depsMap = targetMap.get(target)
  if(depsMap){
    // 获取响应的集合
    const deps =depsMap.get(key)
    if(deps){
      deps.forEach(effect=>effect())
    }
  }
}

const state =reactive({
  name:'ohhh',
  a:{
    b:333
  }
})
// 激活 get
// 最开始的时候会执行一次
effect(()=>{
  console.log('effect1' , state.name)
})
effect(()=>{
  console.log('effect2' , state.name)
})
state.name ='ohhhhhhh'

vue3.0-数据响应式原理_第9张图片


先写到这里,目前还在看源码这块,我先把响应式原理这块分析写出来,这样也有助于梳理学习思路~~,后面会陆陆续续更新哈。

你可能感兴趣的:(vue.js,es6,响应式,代理,依赖管理工具)