配置环境
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.name
state.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.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'
先写到这里,目前还在看源码这块,我先把响应式原理这块分析写出来,这样也有助于梳理学习思路~~,后面会陆陆续续更新哈。