一、调试环境搭建
- 拉取Vue3源码
git clone https://github.com/vuejs/vue-next.git
- 安装依赖
yarn
使用
yarn安装,npm安装依赖后,运行会出问题
- 添加SourceMap文件
【rollup.config.js】
// 76行添加如下代码
output.sourcemap = true
【tsconfig.json】
"sourceMap": true
- 编译
yarn dev
生成:
packages/vue/dist/vue.global.js
- 测试代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>hello vue3</title>
<script src="../packages/vue/dist/vue.global.js"></script>
</head>
<body>
<div id='app'></div>
<script>
const App = {
template: `<h1>{{message}}</h1>`,
data: { message: 'Hello Vue 3!' }
}
Vue.createApp(App).mount('#app')
</script>
</body>
</html>
二、源码结构
源码的位置在package文件内,实际上源码主要分为两部分:编译器和运行时环境
编译器
- compiler-core 核心编译逻辑
- 模板解析
- AST
- 代码生成
- compiler-dom 针对浏览器的编译逻辑
- v-html
- v-text
- v-model
运行时环境
- runtime-core 运行时核心
- inject
- 生命周期
- watch
- directive
- component
- runtime-dom 运行时针对浏览器的逻辑
- class
- style
- runtime-test 测试环境仿真,主要为了解决单元测试问题的逻辑 在浏览器外完成测试比较方便
reactivity 响应式逻辑
template-explorer 模板解析器 可以这样运行
yarn dev template-explorer
vue 代码入口,整合编译器和运行时
server-renderer 服务器端渲染
share 公用方法
三、Composition API
Composition API字面意思是组合API,它是为了更方便的实现逻辑的组合而产生的。主要API
const {
createApp,
reactive, // 创建响应式数据对象
ref, // 将单个值包装为一个响应式对象
toRefs, // 将响应式数据对象转换为单一响应式对象 computed, // 创建计算属性
watch, // 创建watch监听
// 生命周期钩子
onMounted,
onUpdated,
onUnmounted,
} = Vue
基本使用
setup函数
setup函数会在 beforeCreate之后 created之前执行
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>hello vue3</title>
<script src="../packages/vue/dist/vue.global.js"></script>
</head>
<body>
<div id='app'></div>
<script>
const {createApp,reactive}=Vue
const App = {
template: `<h1>{{state.message}}</h1>`,
// data: { message: 'Hello Vue 3!' },
// setup函数会在 beforeCreate之后 created之前执行
setup(){
// 使用reactive做像樱花处理
const state=reactive({message:'Hello Vue 3!'})
return {state}
}
}
createApp(App).mount('#app')
</script>
</body>
</html>
事件处理
const {createApp,reactive}=Vue
const App = {
template: `<h1 @click="onClick">{{state.message}}</h1>`,
// data: { message: 'Hello Vue 3!' },
// setup函数会在 beforeCreate之后 created之前执行
setup(){
// 使用reactive做像樱花处理
const state=reactive({message:'Hello Vue 3!'})
function onClick(){
state.message='Vue3初体验!'
}
return {state,onClick}
}
}
createApp(App).mount('#app')
生命周期
const { onMounted } = Vue
const App = {
setup() {
onMounted(() => { console.log('App挂载!')})
}
}
单值响应化:ref
const { ref } = Vue
const App = {
template: `<h1 @click="onClick">{{ message }}</h1>`,
setup() {
// ref返回包装对象
const message = ref('Hello Vue 3!');
function onClick() {
// 包装对象要修改其value
message.value = 'Vue3初体验!';
}
// 指定包装对象到上下文,模板中可以直接用
return { message, onClick };
}
}
toRefs:将reactive创建出的对象展开为基础类型
const { toRefs } = Vue
const App = {
template: `<h1 @click="onClick">{{message}} | {{foo}} | {{bar}}</h1>`,
setup() {
// 使用reactive做像樱花处理
const state=reactive({
message:'hello vue3!'
bar:'bar',
foo:'foo'
})
function onClick(){
state.message='Vue3初体验!'
}
// 转换每个属性并展开
return { ...toRefs(state), onClick };
}
}
toRefs转过之后,原先页面中使用是state.message,转为基础类型后,使用变为message
computed:计算属性
const {createApp,reactive,ref,toRefs,onMounted,computed}=Vue
const App = {
template: `<h1 @click="onClick"> {{foo}} | {{bar}} | {{msg}}</h1>`,
// setup函数会在 beforeCreate之后 created之前执行
setup(){
// 使用reactive做像樱花处理
const state=reactive({
bar:'bar',
foo:'Hello Vue 3!',
msg:computed(()=>'computed'+state.bar)
})
return {...toRefs(state),onClick}
}
}
watch:创建监控表达式
const {createApp,reactive,ref,toRefs,onMounted,computed,watch}=Vue
const App = {
template: `<h1 @click="onClick">{{bar}}</h1>`,
// data: { message: 'Hello Vue 3!' },
// setup函数会在 beforeCreate之后 created之前执行
setup(){
// 使用reactive做像樱花处理
const state=reactive({
bar:'bar',
foo:'Hello Vue 3!',
msg:computed(()=>'computed'+state.bar)
})
function onClick(){
state.bar='更改bar'
}
watch(()=>state.bar,(val,oldVal)=>{
console.log(`bar变了,新值为:${val}`);
})
return {...toRefs(state),onClick}
}
}
四、数据响应式革新
vue2响应式问题
- 当
data/computed/props中数据规模庞大,遍历起来会很慢,要监听所有属性的变化,占用内存会很大 - 无法监听Set/Map的变化;Class类型的数据无法监听;属性新加或删除无法监听;数组元素 增加和删除无法监听;对于数组需要额外实现方法拦截,对应的修改语法也有限制。
vue3响应式原理:使用ES6的Proxy来解决这些问题。
function reactives(data) {
if (typeof data !== 'object' || data === null) {return data }
// Proxy相当于在对象外层加拦截
// http://es6.ruanyifeng.com/#docs/proxy
const observed = new Proxy(data, {
// 获取拦截
get(target, key, receiver) {
// Reflect用于执行对象默认操作,Proxy的方法它都有对应方法
// Reflect更规范、更友好
// http://es6.ruanyifeng.com/#docs/reflect
console.log(key+':获取');
const val = Reflect.get(target, key, receiver); // 若val为对象则定义代理
return typeof val === 'object' ? reactives(val) : val;
},
// 新增、更新拦截
set(target, key, value, receiver) {
console.log(key+':修改');
return Reflect.set(target, key, value, receiver)
},
// 删除属性拦截
deleteProperty(target, key){
console.log(key+':删除');
return Reflect.deleteProperty(target,key)
}
})
return observed
}
测试代码:
const data = {
foo: 'foo',
obj: {a:1},
arr: [1,2,3]
}
const react = reactives(data)
// get
react.foo// foo:获取
react.obj.a // obj:获取 a:获取
react.arr[0] // arr:获取 0:获取
// set
react.foo = 'fooooo' // foo:修改
react.obj.a = 10 // obj:获取 a:修改
react.arr[0] = 100 // arr:获取 0:修改
// add
react.bar = 'bar'// bar:修改
react.obj.b = 10 // obj:获取 b:修改
react.arr.push(4) // arr:获取 push:获取 length:获取 3:修改 length:修改
react.arr[4] = '5' // arr:获取 4:修改
// delete
delete react.bar // bar:删除
delete react.obj.b // obj:获取 b:删除
react.arr.splice(4, 1)
// arr:获取 splice:获取 length:获取 constructor:获取 4:获取 4:删除 length:修改
delete react.arr[3] // arr:获取 3:删除
Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。
var obj = new Proxy({}, {
get: function (target, propKey, receiver) {
console.log(`getting ${propKey}!`);
return Reflect.get(target, propKey, receiver);
},
set: function (target, propKey, value, receiver) {
console.log(`setting ${propKey}!`);
return Reflect.set(target, propKey, value, receiver);
}
});
五、vue3展望
- vue3会兼容之前写法,仅少量Portal、Suspense之类的组件需要看看,composition可选
- 目前为止,相关工具、生态、库都跟不上
- vue3重要特性如响应式会给大数据量应用带来福音。time-slicing对于用户交互敏感的 应用是重要特性,比如证券类应用;号称性能很好,移动端应用也更加适合使用了。
- 虽然不是正式版,但仍可提前学习vue3设计和实现思想