「这是我参与11月更文挑战的第4天,活动详情查看:2021最后一次更文挑战」
工作中使用Vue3已经有大半年,对一些用法也是比较熟悉了,最近对Vue3的源码进行学习与记录。主要用法就不在这里赘述,详阅官方中文文档,目前Vue3的版本是3.2.x。
Vue3简单体验
<body>
<div id="app">
<h3>{{title}}</h3>
<h3>{{state.title}}</h3>
</div>
</body>
<script src="http://unpkg.com/vue@next"></script>
<script>
const {
createApp,
reactive
} = Vue
const app = createApp({
// 兼容vue2.0 Options API
data() {
return {
title: '兼容vue2.0'
}
},
// Composition API
setup() {
const state = reactive({
title: 'vue3.0'
})
const click = () => {
console.log('Hi, vue3')
}
return {
state,
click
}
}
})
//挂载在dom上
app.mount('#app')
</script>
关于为什么vue3将Options API改成 Composition API
【Vue3.0】尤雨溪 - 聊聊 Vue.js 3.0 Beta 官方直播完整版
composition api 文档
手动实现初始化
首先将代码修改一下,去掉引入的Vue@next,我们手动来实现
<body>
<div id="app"></div>
</body>
<script>
const {
createApp,
} = Vue
const app = createApp({
// 兼容vue2.0 Options API
data() {
return {
title: '我是data中的title'
}
},
setup() {
return {
title: '我是setup中的title'
}
}
})
app.mount('#app')
</script>
- 分析
- vue3.0不需要实例化Vue,直接使用Vue对象中
createApp来构造一个新的对象app来初始化,createApp接收一些配置项options并且返回一个对象,其中有一个mount函数最终将结果挂载到页面上。因此Vue对象看起来应该是这样的
const Vue = {
createApp(options) {
return {
mount(selector) {}
}
}
}
mount函数接收一个selector参数表示需要挂载在页面上的dom宿主对象,可以新增一个编译的方法compile,涉及编译的内容比较复杂,这里就简单的生成一个dom
createApp(options) {
return {
mount(selector) {
const parent = querySelector(selector) // 宿主元素
const el = this.compile().call() // 渲染出来的对象
parent.insertBefore(el, null)
},
compile() {
return function render() {
// 假数据
const h3 = document.createElement('h3')
h3.textContent = '我是手动写的'
return h3
}
}
}
}
这时候刷新页面已经可以看到内容挂载到宿主app
3. 在渲染的时候获取数据并插入到正文中,关于数据的响应式将在之后的篇章中单独介绍,vue2.0中是在
data中添加数据,可以在执行complie函数时call(data)将data中的数据指向complie,则可以在其中用this使用data中的数据
createApp(options) {
return {
mount(selector) {
const parent = querySelector(selector)
// 如果没有手动写的render方法
if (!options.render) {
options.render = this.compile(parent.innerHTML)
}
this.data = options.data()
const el = options.render.call(this.data) //关键步骤
parent.insertBefore(el, null)
},
compile(template) {
return function render() {
const h3 = document.createElement('h3')
h3.textContent = this.title
return h3
}
}
}
}
这时候已经将data中的数据渲染到了页面上
4. vue3.0引入了Composition API的概念,可以在
setup中直接返回数据,如果在data中存在相同的属性,默认setup的优先级高于data。
mount(selector) {
const parent = querySelector(selector)
// 如果没有手动写的render方法
if (!options.render) {
options.render = this.compile(parent.innerHTML)
}
if (options.setup)
this.setupState = options.setup()
}
this.data = options.data()
// 代理中优先使用setup中的属性
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
}
}
})
const el = options.render.call(this.proxy)
insert(el, parent)
},
到这里,已经比较粗糙的完成了Vue3的初始化过程,附上优化过的完整代码
<body>
<div id="app"></div>
</body>
<script>
const Vue = {
createApp(options) {
const render = Vue.createRenderer({
querySelector(selector) {
return document.querySelector(selector)
},
insert(child, parent, anchor = null) {
parent.insertBefore(child, anchor)
}
})
return render.createApp(options)
},
//不同平台的扩展
createRenderer({
querySelector,
insert
}) {
return {
createApp(options) {
return {
mount(selector) {
const parent = querySelector(selector)
// 如果没有手动写的render方法
if(!options.render) {
options.render = this.compile(parent.innerHTML)
}
if (options.setup) {
this.setupState = options.setup()
}
this.data = options.data()
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
}
}
})
const el = options.render.call(this.proxy)
insert(el, parent)
},
compile(template) {
return function render() {
const h3 = document.createElement('h3')
h3.textContent = this.title
return h3
}
}
}
}
}
}
}
const {
createApp
} = Vue
const app = createApp({
// 兼容vue2.0 Options API
data() {
return {
title: '我是data中的title'
}
},
setup() {
return {
title: '我是setup中的title'
}
}
})
app.mount('#app')
</script>
如果你喜欢这篇文中麻烦点个赞
如果文中有错误的地方,麻烦各位大佬指正
--- end---