vue核心模块源码解析

响应式原理

  1. Object.defineProperty
  2. setter
  3. Proxy
var count = 1
var state = {}
Object.defineProperty(state , 'count',{
	get(){
		return count
	},
	set(val){
		count = val
	}
})
//弊端:不能主动监听到对象属性的新增或者删除,add/delete
ref和reactive 声明响应式数据
  • ref:用于定义string、number、boolean --> set/get
function ref(value){
	let _value = vlaue
	let count = {
		get value(){
			return _value
		},
		set value(val){
			_value = val
		}	
	}
}
  • reactive:用于复杂数据类型(array、object、Map、Set) --> proxy数据劫持、代理
let proxy = new Proxy(state,{
	get:function(target,prop){
		return target[prop]
	},
	set:function(target,prop,value){
		target[prop] = value
	},
	deletePropety(target,prop){
		delete target[prop]
	}
})
vue文件在浏览器是怎么运行起来的?
  1. react -> jsx渲染函数
  2. vue -> template -> render 函数

模板的本质就是对HTML的增强 :增加了一些指令 v-if 、 {{}}
渲染函数本质就是js,注入了一些context

模板vs渲染函数
1、本质不同
2、表现能力不同,vue语法糖比react多很多
3、vue代码一致性比较弱(实现同一个功能方法路径太多)
4、性能,compiler优化

compiler编译时->

template -> AST抽象语法树 -> render => VNode
script  -> js
style -> less/scss  , scoped  <style></style>

vue3源码结构

在这里插入图片描述

  • compiler-core 编译器核心代码,且与平台无关,这个包主要功能是将代码解析成ast、然后转换成codegenNode对象、再编译生成可执行代码(render渲染函数)在这里插入图片描述
  • compiler-dom 针对浏览器(或DOM环境)的编译器。她在compiler-core基础上添加了一些特定于DOM的特性
  • compiler-sfc 负责处理vue文件,将其分解为模板、脚本、样式等部分,并对这些部分进行相应的处理
  • compiler-ssr 针对服务器渲染(SSR)的编译器。她在compiler-core基础上添加了一些特定与SSR的特性,如生成服务器渲染的代码
  • reactivity 这个是vuejs的响应式系统,提供了一些基础API
  • runtime-core 这是vuejs运行时核心,包括vuejs的主要功能。如响应式系统、组件系统、生命周期钩子等。这个包是平台无关的
  • runtime-dom 这个是针对浏览器(或DOM环境)的运行时。它在@vue/runtime-core的基础上添加了一些特定与DOM的特性
  • server-renderer 服务端渲染相关
  • shared 共享常量、工具函数
  • vue 人口包,整合各个子包
核心结构

在这里插入图片描述

结构之一: reactivity 响应式

在这里插入图片描述

响应式核心

在这里插入图片描述

在这里插入图片描述

手写实现一个简单的响应式
let object = {
	msg : 'hello world',
	age: 18
}
const render = (el)=>{
	el.innerHTML = 'msg:' + object.msg
}
let activeEffect = null
const effect = (fn)=>{
	const effectFn = ()=>{
		fn()
	}
}
effect(render)

//副作用函数集合,用Set数据结构进行管理
var bucket = new Set()

var trackMap = new WeakMap() 
//为什么不使用new Map而是new WeakMap。 new WeakMap数据不能进行枚举,键只能是对象,值可以是任何类型
//在 JavaScript 里,map API 可以通过四个 API 方法共用两个数组(一个存放键,一个存放值)来实现。
//这样在给这种 map 设置值时会同时将键和值添加到这两个数组的末尾。从而使得键和值的索引在两个数组中相对应。当从该 map 取值的时候,需要遍历所有的键,然后使用索引从存储值的数组中检索出相应的值。
//但这样的实现会有两个很大的缺点,首先赋值和搜索操作都是 O(n) 的时间复杂度(n 是键值对的个数),因为这两个操作都需要遍历整个数组来进行匹配。
//另外一个缺点是可能会导致 内存泄漏,因为数组会一直引用着每个键和值。这种引用使得 垃圾回收算法不能回收处理他们,即使没有其他任何引用存在了。
//reactive api返回的响应式对象
function track(target,key){
	const depsMap = trackMap.get(target)
	if(!depsMap){
		// target.set(target,key)
		
		//获取target的key对应的Map
		const targetKeyMap = trackMap.set(target,key)
		//key -> 依赖对象数组
		let detSet = targetKeyMap.get(key)
		//detSet 新增副作用函数
		detSet.add(activeEffect) 
	}
}


const reactiveObject = new Proxy(object,{
	get(target,key){
		// bucket.add(render)
		track(target,key)
		return target[key]
	},
	set(target,key,newvalue){
		target[key] = newvalue
		bucket.forEach(fn=>fn())
	}
})
渲染器
  1. 渲染器初次渲染 createApp().mount()
    在这里插入图片描述

  2. 二次更新:patch函数—dom diff

      1、n1===n2,不更新
      2、n1不存在,n2存在,挂载(mount)n2
      3、n1和n2类型不一样,卸载(unmount)n1,挂载(mount)n2
      4、根据不同的节点类型,调用不同的process函数
      5、有点比较n1和n2的props变化,调用patchProps函数
      6、比较n1和n2的子节点的变化,调用patchChildren函数
         1、patchFlag和dynamicChildren优化
    

在这里插入图片描述

编译器

编译原理:从模板到渲染函数===> 源代码->词法分析->语义分析->AST->transform->目标代码
在这里插入图片描述

在这里插入图片描述

  • 23
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值