配置环境

  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中生效的。

解析为一个对象,args

源码结构

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

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

测试的demo文件,路径:vue-next/packages/vue/examples/classic文件中

比如打开一个todolist,或者自己创建测试文件,也是在这个文件夹下 (vue-next/packages/vue/examples):vue-next/packages/vue/examples/classic/todomvc.html

可以打断点,来看createApp如何创建

vue3.0的使用

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

Composition API的使用

<!DOCTYPE html><html lang="en"><head>  <meta charset="UTF-8">  <meta name="viewport" content="width=device-width, initial-scale=1.0">  <meta http-equiv="X-UA-Compatible" content="ie=edge">  <title>Document</title></head><body>  <div id="app">    <p  @click="onclick">      {{state.message}}    </p>  </div>  <script src="../dist/vue.global.js"></script>  <script>    const {createApp, reactive, onMounted} = Vue    const app = createApp({      setup(){        // 声明变量        const state=reactive({          message:'vue-3.0'        })        // 事件        function onclick(){          state.message="点击vue3.0"          console.log('点击.....')        }        // 生命周期         onMounted(()=>{          console.log('挂载完成,可以获取dome元素')        })//接收一个函数        // 导出        return {state, onclick}      }    })    app.mount('#app')  </script></body></html>
单值响应式
  // 单值响应式  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.namestate.name ='ohhhhhhh'delete state.name

  • 拦截两层的话,还需要修改:
 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  },

  • 对象里存对象-补全代码
 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.namestate.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.bconsole.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'


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