vue3初探 day02

摘要:记录跟随开课吧老师学习vue3的全过程

第二天的学习目标是,认识什么是数据响应式,vue2和vue3实现数据响应式的区别,自己实现响应式

什么是响应式

数据变化可侦测,和数据相关的内容可以更新

vue2 vs vue3

来看段vue2实现响应式的源码

function observer(obj){
	if(typeof obj !=='object' || obj == null){
		return
	}
	const keys = Object.keys(obj)
	for(let i = 0;i < keys.length;i++){
		const key = keys[i]
		defineReactive(obj,key,obj[key])
	}
}

从这段代码可以看出在vue2中对于obj对象vue2需要遍历对象obj所有key,然后通过defineReactive方法,通过defineProperties给每个key设置get和set方法。这会影响初始化速度。
再来看段代码

const originalProto = Array.prototype
const arrayProto = Object.create(originalProto)
;['push','pop','shift','unshift','splice','reverse','sort'].forEach(method=>{
	arrayProto[method] = function() {
		originalProto[method] = function(){
			originalProto[method].apply(this,arguments)
			dep.notify()
		}
	}
})

从这段代码可以看出vue2中对于数组拦截其push,pop,shift,unshift,splice,reverse,sort操作先执行其对应方法,再执行dep.notify()手动触发更新通知

在vue2中动态添加或删除对象属性需要使用额外API:Vue.set()/delete()

Vue.set(obj,'bar','barfffff')
Vue.delete(obj,'bar')

手写响应式

第一步:响应式实现:reactive

function reactive(obj) {
  // 返回代理过的对象
  return new Proxy(obj, {
    get(target, key) {
      const res = Reflect.get(target, key)
      // 依赖收集
      track(target, key)
      return (typeof res === 'object') ? reactive(res) : res
    },
    set(target, key, val) {
      const res = Reflect.set(target, key, val)
      // 触发副作用函数
      trigger(target, key)
      return res
    },
    deleteProperty(target, key) {
      const res = Reflect.deleteProperty(target, key)
      trigger(target, key)
      return res
    }
  })
}
const obj = reactive({
  foo: 'foo',
  n: {
    a: 1
  }
})
effect(() => {
  console.log('effect1', obj.foo)
})
effect(() => {
  console.log('effect2', obj.foo, obj.n.a)
})
obj.n.a = 10

上面的代码我们已经通过Proxy给对象做了代理拦截,获取,更新,删除都会触发对应方法,就可以添加对应的更新页面方法。但是为了避免更新页面方法全局污染,我们采用发布订阅的模式,订阅更新页面的方法,在对应操作上发布更新页面方法。
第二步:依赖收集和触发:effect()/track()/trigger()
依赖收集和触发
effect添加副作用函数,相当于发布订阅模式,cb回调函数传入effect,cb传入effect后会有两个线路,1.effectStack暂存cb回调函数。2.cb回调函数会执行一次,执行过后触发getter函数的拦截=>依赖收集函数stack,收集依赖的核心目标是为了创建WeakMap这样的数据结构,WeakMap是一个target,key和cb的映射对象。将来如果数据发生变化,setter函数会被触发,setter函数触发我们可以根据传入的target,key,从WeakMap映射关系中取出cb回调数组,执行遍历把所有的响应函数执行。
代码实现

// 保存副作用函数
const effectStack = []
// 添加副作用函数
function effect(fn) {
  const e = createReactiveEffect(fn)
  // 立即执行
  e()
  return e;
}
function createReactiveEffect(fn) {
  // 封装fn,处理其错误,执行之,存放到stack
  const effect = () => {
    try {
      // 0.入栈
      effectStack.push(effect)
      // 1.执行fn
      return fn()
    } finally {
      // 2.出栈
      effectStack.pop()
    }
  }
  return effect
}
// 保存映射关系的数据结构
const target
// 当副作用函数触发响应式数据之后,执行track,进行依赖收集工作
// 目标是将target,key和effectStack中的副作用函数之间建立映射关系
function stack() {
  // 1.拿出响应函数
  const effect = effectStack[effectStack.length - 1]
  if (effect) {
    // 获取target对应map
    let depMap = targetMap.get(target)
    if (!depMap) {
      // 初始化的时候,depMap不存在,需要创建一次
      depMap = new Map()
      targetMap.set(target, depMap)
    }
    // 从depMap中获取对应的Set
    let deps = depMap.get(key)
    if (!deps) {
      // 初始化创建Set
      deps = new Set()
      depMap.set(key, deps)
    }
    // 将副作用函数放入集合
    deps.add(effect)
  }
}
// 触发响应式函数
function trigger(target, key) {
  // 从targetMap中获取对应的副作用函数
  // 1.获取target对应的map
  const depMap = targetMap.get(target)
  if (!depMap) {
    return
  }
  // 2.根据key获取对应的deps
  const deps = depMap.get(key)
  if (deps) {
    // 遍历执行他们
    deps.forEach(dep => dep())
  }
}

注:

  1. 副作用函数:你做一件事,他有一些其他额外的事要做,函数内的响应式数据发生变化,副作用函数要重新执行
  2. WeakMap:弱引用,就算key是对象也不会导致垃圾回收机制数据不被清理
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值