前言: Vue3出来一段时间了,借着《玩转Vue3全家桶》的课程,来好好了解一下Vue3吧。
从普通开发者的角度来说,Vue2响应式并不是真正意义上的代理,而是基于Object.defineProperty()实现的。对于Object.defineProperty()这个API的细节,其实这个API并不是代理,而是对某个属性进行拦截,所以有很多缺陷,比如:删除数据就无法监听,需要$delete等API辅助才能监听到。
并且,Option API在组织代码较多组件的时候不易维护。对于Option API来说,所有的methods、computed都在一个对象里配置,这对小应用来说还好。但代码超过300行的时候,新增或修改一个功能,就需要不停地在data、methods里跳转写代码,上下反复横跳。
在Vue3新特性中,其中,响应式系统、Composition API组合语法、新的组件和Vite是需要重视的;自定义渲染器这方面的知识,想用Vue开发跨端应用时会用到;如果想对Vue源码作出贡献,RFC机制需要好好研究一下,并且得对TypeScript重构有很好的经验。
1.RFC机制
Vue3的第一个新特性和代码无关,而是Vue团队开发的工作方式。
2.响应式系统
Vue2的响应式机制是基于Object.defineProperty()这个API实现的,此外,Vue还是用了Proxy,这两者看起来都像是对数据的读写进行拦截,但是defineProperty是拦截具体某个属性,Proxy才是真正的“代理”。
怎么理解这两者的区别呢?首先看defineProperty这个API,defineProperty的使用,要明确地写在代码里,下面是示例代码:
Object.defineProperty(obj,'title',{
get() {},
set() {},
})
当项目里“读取obj.title”和“修改obj.title”的时候被defineProperty拦截,但defineProperty对不存在的属性无法拦截,所以,Vue2中所有数据必须要在data里声明。
而且,如果title是一个数组的时候,对数组的操作,并不会改变obj.title的指向,虽然我们可以拦截.push等操作实现部分功能,但是对数组的长度的修改等操作还是无法实现拦截,所以还需要额外的$set等API。
而Proxy这个API就是真正的代理了,先看它的用法:
new Proxy(obj,{
get() { },
set() { },
})
需要注意的是,虽然Proxy拦截obj这个数据,但obj具体是什么属性,Proxy则不关心,统一都拦截了。而且Proxy还可以监听更多的数据格式,比如Set、Map,这是Vue2做不到的。
当然,Proxy存在一些兼容性问题,这也是为什么Vue3不兼容IE11以下的游览器的原因,还好现在IE用的人不多了。
更重要的是,Proxy代表一种方向,就是框架会越来越多的拥抱游览器的新特性。在Proxy普及之前,我们是没有办法完整的监听一个JavaScript对象的变化,只能使用Object.defineProperty()去实现一部分功能。
3.自定义渲染器
Vue2内部所有的模块都是揉在一起的,这样做会导致不好扩展的问题。Vue3如何解决这个问题的呢?通过拆包,使用最近流行的monorepo管理方式,响应式、编译和运行时全部独立了,变成下图所示的模样:
在Vue3的组织架构中,响应式独立了出来。而Vue2的响应式只服务于Vue,Vue3的响应式就和Vue解耦了,甚至可以在Node.js和React中使用响应式。
渲染的逻辑也拆成了平台无关渲染逻辑和游览器渲染API两部分。
在这个架构下,Node的一些库,甚至React都可以依赖响应式。在任何时候,如果你希望数据被修改了之后能通知你,你都可以单独依赖Vue3的响应式。
那么,在你想使用Vue3开发小程序、开发canvas小游戏以及开发客户端的时候,就不用全部fork Vue的代码,只需要实现平台的渲染逻辑就可以。
在Vue3中,响应式、编译和运行时几部分组合在一起就是运行在游览器端的Vue3,每个模块又都可以独立扩展出新的功能。
4.全部模块使用TypeScript重构
JavaScript是弱类型的语言。类型系统带来的好处,笼统地说,就是类型系统带来了更方便的提示,并且让我们的代码能够更健壮。
来看看例子吧,在下面这段代码中,我们首先定义了name这个变量,在定义的时候标记的是一个字符串,因而后面赋值时,赋值为数字就会报错。
之后,我们定义一个类型Person,里面的变量name是字符串类型,变量age是数字类型。违反这个设置的数据就报错,这在多人协同和长期维护的项目里带来的收益是巨大的,因为这样可以使错误的代码在编译阶段就被发现,从而避免程序上线运行后,可能会发生的更大的异常错误。
let name:string = '我是个靓女'
name = 1 //报错
interface Person {
name: string;
age: number;
}
let me:Person = {
name:'靓仔胜',
age:18
}
me.age = '整条街' //报错
所以,大部分开源的框架都会引入类型系统,来对JavaScript进行限制。这样做的原因就是:①类型系统带来了更方便的提示;②类型系统让代码更健壮。
5.Composition API组合语法
Composition API是Vue3中,最喜欢的一个特性,饿哦们也叫它组合API。
先举个vue2的栗子,一个累加器,并且还有一个计算属性显示累加器乘以2的结果。
<div id="app">
<h1 @click="add">{{count}}*2</h1>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
let App = {
data(){
return {
count:1
}
},
methods:{
add(){
this.count++
}
},
computed:{
double(){
return this.count*2
}
}
}
Vue.createApp(App).mount('#app')
</script>
在Vue3中,除了上面这种这个写法,还可以采用下面的写法,新增一个setup配置:
<div id="app">
<h1 @click="add">{{state.count}} * 2 = {{double}}</h1>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const {reactive,computed} = Vue
let App = {
setup(){
const state = reactive({
count:1
})
function add(){
state.count++
}
const double = computed(()=>state.count*2)
return {state,add,double}
}
}
Vue.createApp(App).mount('#app')
</script>
使用Composition API后,代码看起来很繁琐,没有Vue2中Options APId 写法简单好懂,但Options API的写法也有几个很严重的问题:
我们使用Composition API后,虽然看起来繁琐了一些,但是带来了很多好处:
Composition API对我们开发Vue项目起到了巨大的帮助。 下面的图示很好地说明了问题:每一个功能模块的代码颜色一样,左边是Options API,一个功能的代码零散的分布在data、methods等配置内,维护起来很麻烦,二右边的Composition API就不一样了,每个功能模块都在一起维护。
6.新的组件
Vue3还内置了Fragment、Teleport和Suspense三个新组件。
7.新一代工程化工具Vite
Vite不在Vue3的代码包内,和Vue也不是强绑定,Vite的竞品是Webpack,而且按照现在的趋势看,使用率超过Webpack也是早晚的事。
Vite主要提升的是开发的体验,Webpack等工程化工具的原理,就是根据你的import依赖逻辑,形成一个依赖图,然后调用对应的处理工具,把整个项目打包后,放在内存里再启动调试。
由于要预打包,所以复杂项目的开发,启动调试环境需要 3 分钟都很常见,Vite 就是为了解决这个时间资源的消耗问题出现的。
你可能不知道,现代浏览器已经默认支持了 ES6 的 import 语法,Vite 就是基于这个原理来实现的。具体来说,在调试环境下,我们不需要全部预打包,只是把你首页依赖的文件,依次通过网络请求去获取,整个开发体验得到巨大提升,做到了复杂项目的秒级调试和热更新。
下图展示了 Webpack 的工作原理,Webpack 要把所有路由的依赖打包后,才能开始调试。
而下图所示的是 Vite 的工作原理,一开始就可以准备联调,然后根据首页的依赖模块,再去按需加载,这样启动调试所需要的资源会大大减少。
总结:
这篇学习的重点就是vue3主要的新特性,再来对这些特征做一个回顾吧: