【手撕源码】vue3响应式原理解析(文末抽奖)

这篇具有很好参考价值的文章主要介绍了【手撕源码】vue3响应式原理解析(文末抽奖)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

🐱 个人主页:不叫猫先生
🙋‍♂️ 作者简介:2022年度博客之星前端领域TOP 2,前端领域优质作者、阿里云专家博主,专注于前端各领域技术,共同学习共同进步,一起加油呀!
💫优质专栏:vue3从入门到精通、TypeScript从入门到实践
📢 资料领取:前端进阶资料以及文中源码可以找我免费领取
🔥 前端学习交流:博主建立了一个前端交流群,汇集了各路大神,一起交流学习,期待你的加入!(文末有我wx或者私信)。

【手撕源码】vue3响应式原理解析(文末抽奖)

一、认识Proxy

Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
new Proxy(target,handler)表示生成一个Proxy实例,target参数表示所要拦截的目标对象,它可以是任意类型的对象,包括内置的数组,函数等,handler也是一个对象,用来定制拦截行为,当发生某些操作时触发该对象。

二、原理分析

1.reactive

声明副作用变量,如果该变量没有值就不进行追踪。在 Vue2 的时候,有一个“全局变量”,叫做 Dep.target – watcher,vue3中还要有这么一个全局变量,就是activeEffect。

//副作用变量
let activeEffect; 

targetMap用来存放依赖

let targetMap = new WeakMap();

判断传入的数据data是否为对象,需要除去data为null的情况。因为typeof null === ‘object’,null的机器码都是0,object机器码为000

function isObject(data) {
	return data && typeof data === 'object'
}

声明reactive函数,返回proxy实例。proxy支持get、set、deleteProperty、has、ownKeys等方法。

  • get
    • 通过Reflect.get(target, key, receiver)获取到属性为key的值
    • track收集依赖
    • ret为对象则递归为对象创建proxy代理
  • set
    • Reflect.set(target, key, value, receiver)设置属性为key的值
    • trigger 执行更新

receiver代表当前proxy对象或者继承proxy的对象,保证传递正确的this 给 getter、setter。

export function reactive(data) {
    //判断是否为对象
	if (!isObject(data)) return 
	// 返回proxy实例
	return new Proxy(data, { 
		get(target, key, receiver) {
			const ret = Reflect.get(target, key, receiver);
			// 收集依赖
			track(target, key)
			//如果获取的数据还是对象的话就递归,继续为此对象创建 Proxy 代理
			return isObject(ret) ? reactive(ret) : ret
		},
		//set修改数据,需要返回一个布尔值
		set(target, key, value,receiver) {
           // 首先获取旧值
            const oldValue = Reflect.get(target, key, receiver)
            // 判断新值和旧值是否一样来决定是否更新setter
            let result = true;
            // 当新值不等于旧值的时候执行更新草错
            if (oldValue !== value) {
                result = Reflect.set(target, key, value, receiver)
                // 更新操作
                trigger(target, key)
            }
            //返回布尔类型
            return result
		},
		deleteProperty(target, key) {
		    //首先需要判断是否有要删除的key
		    const hasK = hasKey(target, key)
		    const ret = Reflect.deleteProperty(target, key)
		    //存在key且有值则更新
		    if(hasK&&ret){
		    //更新
			 trigger(target, key)
		    }
			return ret
		},
		has(target, key) {
			track(target, key)
			const ret = Reflect.has(target, key)
		},
		ownKeys(target, key) {
			track(target)
			return Reflect.ownKeys(targety)
		},
	})
}
// 判断对象中key是否存在
const hasKey = (target, key) => Object.prototype.hasOwnProperty.call(target, key)

2.track

track里面会收集各种依赖,把依赖关系做成各种映射的关系,映射关系就叫 targetMap, 内部拿到这个key,就可以通过映射关系找到对应的value,就可以影响这个执行函数,

function track(target, key) {
	// 如果当前没有effect就不执行追踪
    if (!activeEffect) return
    //找target有么有被追踪
	let depsMap = targetMap.get(target);
	//判断target是否为空,如果target为空则没被追踪,则set设置一个值
	if (!depsMap) targetMap.set(target, (depsMap = new Map()));
	//判断depsMap中有没有key,没有key就set一个(判断target.key有没有被追踪)
	let dep = depsMap.get(key)
	// 如果key没有被追踪,那么添加一个
	if (!dep) depsMap.set(key, (dep = new Set()))
	//触发副作用函数
	trackEffect(dep)
}
//副作用函数
function trackEffect(dep) {
	//相当于 Dep.target && dep.add(Dep.target)
	//如果key没有添加activeEffect,则添加一个
	if (!dep.has(activeEffect)) dep.add(activeEffect);
}

3.trigger

修改数据时通过 trigger目标对象找到key,根据映射关系找到cb函数执行更新视图。

function trigger(target, key) {
    // 获取依赖数据,对依赖数据循环,
	const depsMap = targetMap.get(target)
	console.log(depsMap,'depsMap')
	if (!depsMap) return
	//如果effect存在则执行run方法,run方法就是执行的视图更新回调
	depsMap.get(key).forEach(effect =>
		effect && effect.run()
	);
}

4.ref

ref中声明了一个RefImpl类,初始化时传入参数init。

  • get

    • 追踪变量,收集依赖
    • 返回初始化变量的值
  • set

    • 修改旧值,赋新值
    • trigger 更新
export function ref(init) {
	class RefImpl {
		constructor(init) {
		// 接收传过来的参数
			this.__value = init;
		}
		//获取数据,直接返回传过来的数据
		get value() {
			track(this, 'value')
			return this.__value
		}
		//更新数据
		set value(newVal) {
			this.__value = newVal;
			trigger(this, 'value')
		}
	}
	return new RefImpl(init);
}

5.effect

effect第一个参数是函数,如果这个函数中有使用 ref/reactive 对象,当该对象的值改变的时候effect就会执行。

function effect(fn,option={}){
   //effect 数据类型为ReactiveEffect,一上来就会执行run方法,之后可以自定义执行run方法,即设置option的内容
  let __effect = new ReactiveEffect(fn);
  if(!option.lazy){
	  __effect.run();
  }
  return __effect
}

6.ReactiveEffect

声明ReactiveEffect类,间接定义effect的数据类型。

class ReactiveEffect{
	constructor(fn){
		this.fn = fn;
	}
	// 依赖收集之前触发
	run(){
		activeEffect = this;
		return this.fn()
	}
}

7.computed

计算属性

export function computed(fn){
//只考虑函数情况
let __computed;
const e = effect(fn,{lazy:true })
__computed = {
	get value(){
		return e.run();
	}
}
return __computed
}

8.mount

mount的参数instance相当于整个app,el相当于挂在的节点

export function mount(instance,el){
   // 执行effect
	effect(function(){
		instance.$data && update(instance,el)
	})
	instance.$data = instance.setup();
	//更新节点
	update(instance,el)
	function update(instance,el){
	    //挂载节点的render函数返回值内容复制给节点的innerHTML,进行更新
		el.innerHTML = instance.render();
	}
}

三、源码地址

附:源码地址

🌟粉丝福利(抽奖)

《低代码开发实战——基于低代码平台构建企业级应用》
抽奖规则:抽奖助手小程序随机抽奖
活动时间:即日起至2023年4月18日 12:00
温馨提示:参与活动者提前参加博主wx(zbsguilai),以避免错过中奖通知。
【手撕源码】vue3响应式原理解析(文末抽奖)文章来源地址https://www.toymoban.com/news/detail-435462.html

到了这里,关于【手撕源码】vue3响应式原理解析(文末抽奖)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 前端开发攻略---从源码角度分析Vue3的Propy比Vue2的defineproperty到底好在哪里。一篇文章让你彻底弄懂响应式原理。

    Vue的响应式到底要干什么? 无非就是要知道当你 读取 对象的时候,要知道它读了。要做一些别的事情 无非就是要知道当你 修改 对象的时候,要知道它改了。要做一些别的事情 所以要想一个办法, 把读取和修改的动作变成一个函数 ,读取和修改的时候分别调用对应的函数

    2024年04月17日
    浏览(37)
  • vue3响应式原理

    Vue 3 中的响应式原理是通过使用 ES6 的 Proxy 对象来实现的。在 Vue 3 中,每个组件都有一个响应式代理对象,当组件中的数据发生变化时,代理对象会立即响应并更新视图。 具体来说,当一个组件被创建时,Vue 会为组件的 data 对象创建一个响应式代理对象。这个代理对象可以

    2024年02月15日
    浏览(61)
  • Vue3响应式原理 私

    响应式的本质:当数据变化后会自动执行某个函数映射到组件,自动触发组件的重新渲染。 响应式的实现方式就是劫持数据,Vue3的reactive就是通过Proxy劫持数据,由于劫持的是整个对象,所以可以检测到任何对象的修改,弥补了2.0的不足。 名词解释: **副作用函数:**函数的

    2024年02月10日
    浏览(31)
  • Vue3 数据响应式原理

    核心: 通过Proxy(代理): 拦截对data任意属性的任意(13种)操作, 包括属性值的读写, 属性的添加, 属性的删除等… 通过 Reflect(反射): 动态对被代理对象的相应属性进行特定的操作 Vue3的响应式比Vue2好在哪里? 效率更高了,Vue2中假设监听某个对象,该对象中有一万个属性,他要循

    2024年02月11日
    浏览(40)
  • 【手撕源码】vue2.x中keep-alive源码解析

    🐱 个人主页: 不叫猫先生 🙋‍♂️ 作者简介:前端领域新星创作者、阿里云专家博主,专注于前端各领域技术,共同学习共同进步,一起加油呀! 💫系列专栏:vue3从入门到精通、TypeScript从入门到实践 📢 资料领取:前端进阶资料以及文中源码可以找我免费领取 🔥 前端

    2024年02月20日
    浏览(25)
  • 手写Vue3响应式数据原理

    我们想要对一个对象数据进行处理,从而实现更改dom。但如何更改对一个对象数据进行更改呢? vue2 的双向数据绑定是利⽤ES5 的⼀个 API ,Object.defineProperty()对数据进⾏劫持 结合 发布订阅模式的⽅式来实现的。 vue3 中使⽤了 ES6 的 ProxyAPI 对数据代理,通过 reactive() 函数给每⼀

    2024年02月11日
    浏览(37)
  • Vue3响应式源码实现

    初始化项目结构 reactive.ts effect.ts 测试 执行 tsc 转成 js 代码,没有 tsc 的全局安装 typescript 新建 index.js ,分别引入 effect.js 和 reactive.js 新建 index.html 然后再根目录执行 安装依赖 然后新建 webpack.config.js 执行命令启动项目

    2024年02月09日
    浏览(25)
  • Vue3.0中的响应式原理

    实现原理: - 对象类型:通过 ``Object.defineProperty()``对属性的读取、修改进行拦截(数据劫持)。 - 数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。 存在问题: - 新增属性、删除属性, 界面不会更新。 - 直接通过下标修改数组, 界面

    2023年04月17日
    浏览(34)
  • 【Vue3响应式原理#01】Reactivity

    专栏分享:vue2源码专栏,vue3源码专栏,vue router源码专栏,玩具项目专栏,硬核💪推荐🙌 欢迎各位ITer关注点赞收藏🌸🌸🌸 以下是柏成根据Vue3官方课程整理的响应式书面文档 - 第一节,课程链接在此:Vue 3 Reactivity - Vue 3 Reactivity | Vue Mastery,本文档可作为课程的辅助材料,

    2024年02月08日
    浏览(30)
  • Web前端 ---- 【Vue3】Proxy响应式原理

    目录 前言 安装Vue3项目 安装 Proxy 语法格式 从本文开始进入vue3的学习。本文介绍vue3中的响应式原理,相较于vue2中通过object.defineProperty(vue2中的响应式)来实现响应式,vue3中换成了Proxy来进行实现。 相较于vue2通过vue-cli脚手架来创建项目,这里更推荐使用create-vue来创建vue3的

    2024年01月16日
    浏览(53)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包