开课吧VUE体验课笔记
<div id="app">
<h3>{{title}}</h3>
<h3>{{state.title}}</h3>
</div>
<script>
// 接收obj,代理它,使他成为响应式的
// 替换Object.defineProperty()
function reactive(obj) {
// vue3中基于Proxy
return new Proxy(obj, {
get(target, key) {
console.log('get', key)
// 依赖收集
track(target, key)
return target[key]
},
set(target, key, val) {
console.log('set', key)
target[key] = val
// update()
// app.update()
// 触发依赖
trigger(target, key)
},
})
}
// 添加副作用函数
const effectStack = [] // 临时存储fn
function effect(fn) {
// 如果fn中用到了响应式数据,当他们发生变化,fn会再次执行,称为副作用函数
const eff = function() {
try {
effectStack.push(eff)
fn()
} finally {
effectStack.pop()
}
}
// 执行一次,出发依赖收集
eff()
return eff
}
// 依赖收集函数track
// {target: {key: [eff]}}
const targetMap = {}
function track(target, key) {
// 获取副作用函数
const effect = effectStack[effectStack.length - 1]
if (effect) {
let map = targetMap[target]
if (!map) {
map = targetMap[target] = {}
}
let deps = map[key]
if (!deps) {
deps = map[key] = []
}
// 将副作用函数放入deps
if (deps.indexOf(effect) === -1) {
deps.push(effect)
}
}
}
function trigger(target, key) {
const map = targetMap[target]
if (map) {
const deps = map[key]
if (deps) {
deps.forEach(dep => dep())
}
}
}
function h(tag, props, children) {
return {tag, props, children}
}
// createApp返回应用程序实例是什么样子
const Vue = {
createApp(options) {
// 暴露给浏览器平台
const renderer = Vue.createRenderer({
querySelector(selector) {
return document.querySelector(selector)
},
insert(child, parent, anchor) {
parent.insertBefore(child, anchor || null)
},
createElement(tag) {
return document.createElement(tag)
}
})
return renderer.createApp(options)
},
createRenderer({ querySelector, insert, createElement }) {
// 返回自定义的渲染器
return {
createApp(options) {
// 返回的就是app实例
return {
mount(selector) {
// mount的目标是什么?
const parent = querySelector(selector)
// 需要将组件配置解析为dom
//通过render函数实现
if (!options.render) {
options.render = this.compile(parent.innerHTML)
}
// 兼容options api
if (options.setup) {
// setupState已经是一个代理对象
this.setupState = options.setup()
} else {
this.data = options.data()
}
// Proxy
// 确定render中数据从哪获取
this.proxy = new Proxy(this, {
get(target, key) {
if (key in target.setupState) {
return target.setupState[key]
} else {
return target.data[key]
}
},
set(target, key, val) {
if (key in target.setupState) {
target.setupState[key] = val
} else {
target.data[key] = val
}
}
})
// app实例上加了一个更新函数
this.update = effect(() => {
// const el = options.render.call(this.proxy)
// // 追加到宿主元素上去
// parent.innerHTML = ''
// // parent.appendChild(el)
// insert(el, parent)
// 获取虚拟dom
const vnode = options.render.call(this.proxy)
// mounted
if (!this.isMounted) {
// mounted
// vnode => dom
const el = this.createElm(vnode)
parent.innerHTML = ''
insert(el, parent)
this.isMounted = true
} else {
// update
// diff
this.patch(this._vnode, vnode)
}
this._vnode = vnode
})
// 初始化
this.update()
},
createElm(vnode) {
// 创建根节点
const el = createElement(vnode.tag)
// todo props
// children
if (typeof vnode.children === 'string') {
el.textContent = vnode.children
} else {
// 递归
vnode.children.forEach(child => {
insert(this.createElm(child), el)
})
}
vnode.el = el
return el
},
// n1: 老节点
// n2: 新节点
patch(n1, n2) {
const el = n2.el = n1.el
// 判断双方是否是相同节点
if (n1.tag === n2.tag) {
// tots props
// children
const oldCh = n1.children
const newCh = n2.children
if (typeof oldCh === 'string') {
if (typeof newCh === 'string') {
// text
if (oldCh !== newCh) {
el.textContent = newCh
}
} else {
// replace text with elements
el.textContent = ''
newCh.forEach(child => insert(this.createElm(child), el))
}
} else {
if (typeof newCh === 'string') {
// replace elements with text
el.textContent = newCh
} else {
// children
}
}
}
},
compile(template) {
// 没有用到template
return function render() {
// const h3 = document.createElement('h3')
// h3.textContent = this.title
// return h3
// 返回vnode
return h('h3', null, this.title)
// return h('h3', null, [
// h('p', null, this.title),
// ])
}
}
}
}
}
}
}
</script>
<script>
// 1. 函数式
// 2. 标准化、简化、一致性:render函数,sync修饰符删除,指令定义,v-model调整
// 3. tree-shaking(摇树优化)
// 4. 复用性:composition api
// 5. 性能优化:响应式、编译期优化
// 6. 扩展性:自定义渲染器
const { createApp } = Vue
const app = createApp({
data() {
return {
title: 'hello vue3!'
}
},
setup() {
// 调函数等等
// 规避this
const state = reactive({
title: 'hello vue3!!!!!!'
})
setTimeout(() => {
state.title = 'vue3, hello!!!!!!'
}, 2000)
return state
},
})
// app.component()
// app.directive('focus', {})
app.mount('#app')
</script>