vue3 源码解析之Reactive实现的原理
reactive(响应): shallowReactive(浅响应): readonly(只读): shallowReadonly(浅的只读), 浅相当于第一层。******这里有一个知识点: 懒代理 const obj = {name:'zs', list:{live:'apple'}}当你不使用obj.list的时候不会对里面的数据进行代理,因为proxy默认只代理第一层,如果使用里面的数据
·
第一节:vue3 响应式的API;
reactive(响应): shallowReactive(浅响应): readonly(只读): shallowReadonly(浅的只读), 浅相当于第一层。
import {reactive,readonly,shallowReactive,shallowReadonly} from 'vue'
const obj = reactive({name:'张三',list:;[{like:{live:'篮球'}]})
reactive: // 响应式的数据无论深浅整体会被进行代理;
readonly:// 只读的数据,无法对数据进行修改
shallowReactive: // 只代理浅层的数据 深层数据不会被代理
shallowReadonly:// 第一层是只读的,可以修改深层次的数据
第二节 封装以上响应式API的方法 :
以上的方法均采用了高阶函数的方法: 何为高阶函数, 函数的参数或者返回值为一个函数的时候。
在这里需要注意的点, 该API 分为是否为只读的 是否为深代理的
const reactiveHandlers = {}
const shallowReactiveHandlers = {}
const readonlyHanlders = {}
const shallowReadonlyHandlers = {}
export const reactive(target){ // target 为一个对象
return createReactObj(target,false,reactiveHandlers) //目标对象 是否可读 上面的对象
}
export const shallowReactive(target){ // target 为一个对象
return createReactObj(target,false,shallowReactiveHandlers)
}
export const readonly(target){ // target 为一个对象
return createReactObj(target,true,readonlyHandlers)
}
export const shallowReadonly(target){ // target 为一个对象
return createReactObj(target,true,shallowReadonlyHandlers)
}
// 这里是实现proxy代理的核心代码
// 下面定义两个数据结构 用来进行优化 判断该对象是否已经被代理过了 防止重复代理的问题
const reactiveMap = new WorkMap() // 这里面的key 是一个对象 定义的是非可读的
const readonlyMap = new WorkMap() // 这个定义的是可读的
function createRreactObj (target,isReadonly,baseHandlers){
// 第一步判断 target是否为object对象 如果是的用proxy进行代理
if(typeof target === 'object' && target != null ){
// 第二步判断 该对象是否已经被进行代理过了
const proxymap = isReadonly?readonlyMap:reactiveMap //根据isReactive判断使用数据结构
if(proxymap.get(target)){ // 这里代表已经被进行代理了
return proxtymap.get(target) // 将代理过的再返回出去 无需再代理
}else {
// 这里再做代理的工作
const proxy = new Proxy(target,baseHandlers)
proxyMap.set(target,proxy) // 把代理对象和目标对象放到结构表中
return proxy // 返回的是代理后的对象
}
}else {
return target
}
}
第三节 封装reactiveHandlers shallowReactiveHandlers readonlyHandlers shallowReadonlyHandlers ;
******这里有一个知识点: 懒代理 const obj = {name:'zs', list:{live:'apple'}}
当你不使用obj.list的时候不会对里面的数据进行代理,因为proxy默认只代理第一层,如果使用里面的数据 就会被重新代理了。
const get = createGetter() // 不是只读的 深的
const shallowGet = createGetter(false,true) // 不是只读的 浅的
const readonlyGet = creareGetter(true) // 只读的
const shallowReadonlyGet = creareGetter(true,true) // 只读的 浅的
// 定义 createGetter这个方法
function createGetter(isReadonly=false,isShallow=false){ // 默认的参数都为false
return function get(target,key,receiver){
const res = Reflect.get(target,key,receiver) // target[key]
if(!isReadonly){ // 不是只读的
// 收集依赖
}
if(shallow){ // 是浅的
return res
}
// 这里使用了懒代理 const obj = {list:{name:'zs'}} 如果不obj.list里面数据不会被代理
if(typeof res === 'object' && res !== null){ // 这里代表key是对象 需要递归
return isReadonly?readonly(res):reactive(res) // 递归
}
return res
}
}
-------------------------------------------------------以上处理get方法的
const set = createSetter()
const shallowSet = createSetter(true)
function createSetter(shallow=false){
return function set(target,key,value,receiver){
const result = Reflect.set(targer,key,value,receiver) // 获取最新的值
return result
}
}
--------------------------------------------------------这里处理set方法的
export const reactiveHandlers = {
get:get,
set:set
}
export const shallowReactiveHandlers = {//浅的{list:{a:b}} 里面的a不能被代理也就不是响应式
get:shallowGet,
set:shallowSet
}
export const readonlyHandlers = {
get:readonlyGet,
set:()=>{ // 这里set 是不允许被修改对象的值
console.log('set on key is faild')
}
}
export const shallowReadonlyHandlers = {/
get:shallowReadonlyGet,
set:()=>{ // 这里set 是不允许被修改对象的值
console.log('set on key is faild')
}
}
第四节 effect用来收集依赖 相当于vue2中的watcher
<body>
<div id="app"></div>
<script>
let {reactive,effect} from 'vue'
let state = reactive({name:'zs',age:12})
// effect默认执行 获取代理的数据 收集effect
effect(()=>{app.innerHTML = state.name+state.age},{Lazy:true})
// 修改数据 触发set 执行effect
setTimeout(()=>{state.age = 18},1000)
// 总结 (1)视图中获取数据 触发get 收集effect
// (2) 视图中修改数据 触发 set 执行effect
</script>
</body>
手写effect这个方法:
// 定义effect 这个方法 返回值是里面的小函数
function effect(fn,options={}) {
const effect = createReactEffect(fn,options) {
if(!options.lazt){ // 如果是不是懒加载 就立即执行 如果是懒加载就返回这个函数
effect() // 执行这个方法就相当于执行下面的函数 fn()
}
return effect
}
}
// 创建 createReactEffect这个方法
let uid = 0
let activeEffect // 保存当前的effect
const effectStack = []
function createReactEffect (fn,optons){
const effect = function reactiveEffect(){
if(!effectStack.includes(effect)){ // effect没有入栈 再执行下面的代码
try{
// 入栈
effectStack.push(effect)
activeEffect = effect
fn()
}finaly{
// 出栈
effectStack.pop()
activeEffect = effectStack[effectStack.length-1]
}
}
}
effect.id = uid++ // 区别effect
effect.isEffect = true // 区别effect 是不是响应式的
effect.raw = fn // 保存用户的方法
effect.options = options // 保存用户的属性
return effect
}
// 在获取数据的时候触发get 收集依赖 effect
let targetMap = new WeakMap()
export function Track(target,type,key){
if(activeEffect===undefined) {return} // 没有effect的使用
let depMap = targetMap.get(target)
if(!depMap) { // 没有 添加
targetMap.set(targer,(depMap = new Map()))
}
let dep = depMap.get(key) // 有
if(!dep){ // 没有属性
depMap.set(key,(dep=new Set))
}
if(!dep.has(activeEffect)){ // 有没有收集effect 没有的话 收集
dep.add(activeEffect)
}
}
这个在上面的函数中需要执行的收集依赖的方法
if(!isReadonly) { // 不是只读的收集依赖
Track(target,type,key) // target目标对象 type操作添加修改删除 key 属性名
}
第五节: 对象的属性的修改 需要触发set方法。
function createSetter(shallow=false){
return function set(target,key,value,receiver){
const oldValue = target[key] // 获取老值
// 进行判断 目标对象是不是数组 索引是否为整数
let haskey = Array.isArray(target) && (key)=>{parseInt(key)+'' === key}?
Number(key)<targer.length:(targer,key)=>
{Object.prototype.hasOwnProperty.call(target,key)}
const result = Reflect.set(targer,key,value,receiver) // 获取最新的值
if(!haskey) { // 如果没有就是新增
trigger(target,ADD,key,value)
}else { // 修改 新值和原来的值一样
if(value!==oldValue){ // 新值和老值不相同 在执行这个方法
trigger(target,Set,key,value,oldValue)
}
}
return result
}
}
export function trigger(target,type,key?,newValue,oldValue){
const depsMap = targetMap.get(target)
if(! depsMap){ // 是否存在依赖收集
return
}
// 有的情况下
let effectSet = new Set() // 如果有多个修改同一个值 并且值 相同
const add = (effectAdd)=>{
if(effectAdd){
effectAdd.forEach(effect=>effectSet.add(effect))
}
}
// 处理数组 就是key === length
if(key==='length' && Array.isArray(target)){
depsMap.forEach(dep,key)=>{
if(key === 'length' || key > newValue) {
add(dep)
}
}
}else{ // 可能是对象
if(key != undefiend) {
add(depsMap.get(key))
}
}
// 执行
effectSet.froEach((effect:any)=>effect())
}
更多推荐
已为社区贡献1条内容
所有评论(0)