配置环境
git clone https://github.com/vuejs/vue-next.git
- 安装依赖: yarn
- 生成sourcemap文件,修改package.json
"dev": "node scripts/dev.js --sourcemap"
- 编译: 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的使用
- option的API还是可以继续使用的,比如 “@click”
- 新增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的数据响应式
- 对象响应化:遍历key,设置getter,setter
- 数据响应化:覆盖数组方法
vue2.0数据响应式弊端
- 响应化过程需要递归遍历,消耗较大
- 新加或删除属性无法监听
- 数组响应化需要额外实现
- Map、Set、Class等无法响应式
- 修改语法有限制
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'
先写到这里,目前还在看源码这块,我先把响应式原理这块分析写出来,这样也有助于梳理学习思路~~,后面会陆陆续续更新哈。