Vue3原理篇
Vue的设计概述
Vue的设计思路
- 声明式
- 数据驱动
- 渐进式
Vue解决的问题
- 高效性
- 编译器,编译模板
- 数据驱动
- 响应式
- 自动更新视图
- 运行时runtime-core
- 复用性、可维护性
- composition api
- mixin
- 指令
- 可扩展性、跨平台
- 浏览器平台运行时runtime-dom
- 自定义渲染器API
Vue源码的整体架构
1. 源码学习方法
- 找到一条主线
- 思考如何运行起来
- 数据何时变
- 如何更新
- 画思维导图
- 提出问题,去源码找答案
- 造一个轮子
- 写出一篇文章
2. 初始化流程分析
- 创建应用实例:获取渲染器renderer,renderer是含createApp方法的对象,调用renderer创建实例
- 创建根节点的vnode:传入的有template模板时,会执行compile编译模板为render函数
- 执行render函数,将生成的vnode传递给path函数,转换为dom
- 追加到宿主元素
注:
- 挂载都做了什么?
答:将传入组件数据和状态转换为dom,并追加到宿主元素
- 初始化方法变化及原因?
答:变化:
- 函数方式创建实例
- 实例方法
- 简化API,一致性增强
原因:
- ts支持更好
- 避免全局污染
- 简化
- 增强一致性
- 利于tree shaking
3. 更新流程分析
- 更新机制建立
- 数据更新,set方法调用trigger函数
- 将副作用函数添加到更新队列queueJob里
- promise异步执行更新队列queueFlush
- 循环执行相关副作用函数
Mine-Vue设计与实现
1. 视图初始化
- 应用程序实例创建
- createApp()返回App实例对象,实现一个mount()
- 挂载
- 实例的mount方法传入的组件渲染结果追加到宿主元素
export function createApp(rootComponent) {
// 接受根组件,返回App实例
return {
mount(selector) {
// 1.获取宿主
const container = document.querySelector(selector)
// 2. 渲染视图
const el = rootComponent.render.call(rootComponent.data())
// 3. 追加到宿主
container.appendChild(el)
}
}
}
2. 渲染器实现
将传入的组件转换为Dom
- 渲染器工厂
- createRenderer
- 所属runtime-core
- 渲染器实例
- render
- createApp
// runtime-dom
let renderer
// dom平台特有的节点操作
const rendererOptions = {
querySelector(selector) {
document.querySelector(selector)
},
insert(child, parent, anchor) {
// anchor设为null, 则为appendChild
parent.insertBefore(child, anchor || null)
},
setElementText(el, text) {
el.textContent = text
}
}
// 确保renderer单例
function ensureRenderer() {
return renderer || (renderer = createRenderer(rendererOptions))
}
export function createApp(rootComponent) {
return ensureRenderer().createApp(rootComponent)
}
// runtime-core
// ------------- custom renderer api -------------
export function createRenderer(options) {
// 渲染组件内容
const render = () => {
// 1.获取宿主
const container = options.querySelector(selector)
// 2. 渲染视图
const el = rootComponent.render.call(rootComponent.data())
// 3. 追加到宿主
options.insert(el, container)
}
// 返回渲染器实例
return {
render,
createApp: createAppAPI(options)
}
}
export function createAppAPI(render) {
return function createApp(rootComponent) {
const app = {
mount(selector) {
render(rootComponent)
}
}
return app
}
}
3. 数据响应式实现
- reactive()
- new Proxy()
- ref()
- get/set
let activeEffect
export function effect(fn) {
activeEffect = fn
}
export function reactive(obj) {
return new Proxy(obj, {
get(target, key) {
const vlaue = Reflect.get(target, key)
// 依赖跟踪
track(target, key)
return value
},
set(target, key, value) {
const result = Reflect.set(target, key, value)
// 通知更新
trigger(target, key)
return result
},
deleteProperty(target, key) {
const result = Reflect.deleteProperty(target, key)
// 通知更新
trigger(target, key)
return result
}
})
}
const targetMap = new WeakMap()
function track(target, key) {
if(activeEffect) {
let depsMap = targetMap.get(target)
// 首次depsMap不存在
if(!depsMap) {
targetMap.set(target, (depsMap = new Map()))
}
let deps = depsMap.get(key)
// 首次depsMap不存在
if(!deps) {
depsMap.set(key, (deps = new Set()))
}
// 添加激活的副作用
deps.add(activeEffect)
}
}
function trigger(target, key) {
const depsMap = targetMap.get(target)
if(depsMap) {
let deps = depsMap.get(key)
if(deps) {
deps.forEach(dep => dep())
}
}
}
import { reactive } from './reactive'
...
export function createRenderer(options) {
...
const render = () => {
// 1. 获取宿主
const container = options.querySelector(selector)
// 2. 渲染视图
const observed = reactive(rootComponent.data())
// 3. 组件更新函数
const componentUpdateFn = () => {
const el = rootComponent.render.call(observed)
options.setElementText(el, '')
// 3. 追加到宿主
options.insert(el, container)
}
effect(componentUpdateFn)
// 初始化
componentUpdateFn()
if(rootComponent.mounted) {
rootComponent.mounted.call(observed)
}
}
...
}
...
4. 虚拟DOM
// vnode.js
export function createVNode(type, props, children) {
return {
type,
props,
children
}
}
...
export function createRenderer(options) {
// 渲染组件内容
const render = (vnode, container) => {
if(vnode) {
patch(container._vnode || null, vnode, container)
}
container._vnode = vnode
}
// 挂载三件事:
// 1. 组件实例化
// 2. 状态初始化
// 3. 副作用安装
const mountComponent = (initialVNode, container) => {
// 组件实例化
const instance = {
data: {},
vnode: initialVNode,
isMounted: false
}
// 初始化组件状态
const { data: dataOptions } = instance.vnode.type
instance.data = reactive(dataOptions())
// 安装渲染函数副作用
setupRenderEffect(instance, container)
}
const setupRenderEffect = (instance, container) => {
// 声明组件更新函数
const componentUpdateFn = () => {
if(!instance.isMounted) {
// 创建阶段
const { render, isMounted } = instance.vnode.type
const vnode = render.call(instance.data)
patch(null, vnode, container)
if(isMounted) {
instance.vnode.type.isMounted.call(instance.data)
}
} else {
// 更新阶段
}
}
// 建立更新机制
effect(componentUpdateFn)
// 首次执行组件更新函数
componentUpdateFn()
}
const patch = (n1, n2, container) => {
const { type } = n2
if(typeof type === 'string') {
// 原生元素
processElement(n1, n2, container)
} else {
// 组件
processComponent(n1, n2, container)
}
}
const processComponent = (n1, n2, container) => {
if(n1 == null) {
mountComponent(n2, container)
} else {
}
}
const processElement = (n1, n2, container) => {
if(n1 == null) {
mountElement(n2, container)
} else {
patchElement(n1, n2)
}
}
// 返回渲染器实例
return {
render,
createApp: createAppAPI(options)
}
}
...
export function createAppAPI(render) {
return function createApp(rootComponent) {
const app = {
mount(selector) {
const vnode = createVNode(rootComponent)
render(vnode, selector)
}
}
return app
}
}