在本讲中,我们只会概述性的来介绍一下 Vue3 中新增的一些主要内容,而这些主要知识点的详细的使用和功能以及作用,我们会在后面的课程里面逐渐的给大家去解锁。那么明确好了我们的学习目标之后呢?
我们来看一下 Vue3 之中到底新增了哪些比较核心的功能呢?让我们拭目以待吧。
Composition API 组合语法
在看 Composition API 之前,我们聊聊 Vue2 中 Options API 存在的问题。
Options API 是 Vue2 里面的开发形式,Options API 它存在着方便、易学、清晰等等一些特点。但是它在存在着这些特点的同时,也存在着一些问题,而其中最主要的一个问题就是:当你的组件变得越来越复杂的时候,你组件的可读性在变得越来越差,并且不易维护。
对于 Vue2 中 Options API 来说,所有的 methods、computed、watch 都在一个对象里配置,这对小应用来说,还是可以接受。但是,如果是一个大型应用,代码超过几千行的时候,新增或者修改一个功能,就需要不停地在 data、methods 里翻来翻去地写代码,这样的操作称之为上下反复横跳。如果大家也遇到过这种情况,应该也深有感触,我们在写整个代码时好像总是在不停的翻滚。
那为什么会出现这样一种情况呢?这是因为在 Vue2 中 Options API 里面,整体的组件代码结构如下图。
其中,同样颜色的部分表示使用了同样的一块数据,比如,首先我们在前面定义了 Data,然后在 Method 去使用这个 Data。如果我们需要查看 Data 在哪些方法使用,此时就得上下来回的翻滚。
这样不停翻滚的操作,就大大的影响到了用户体验,并且随着你的组件的复杂度越来越高,这种情况会变得越来越严重,而这个就是 Options API 所存在的一个问题。
而 Vue3 中的 Composition API 要解决就是这个问题,下面这个示例图很好地说明了问题。其中左边是使用 OptionS API 去完成的代码,而右边是使用 Composition API 去完成的代码。其实这两块代码对比起来,最大的一个变化就是相同颜色的区块被放到了一起了,而这个也就是 Composition API 最主要的一块内容,就是把定义数据和使用数据放到一起去进行处理,以达到更加易读,更加方便扩展的一个最终目的。
其实还可以更进一步,如果每个颜色块代码,我们都拆分出一个函数,就会写出类似上面右侧风格的代码,每个数据来源都清晰可见,而且每个功能函数都可以在各个地方复用。
响应式系统
Vue 2 的响应式机制是通过 Object.defineProperty 来实现这个 API 的,其中包含两个方面,一是数据代理,二是数据劫持。此外,Vue 还使用了 Proxy,这两者看起来都像是对数据的读写进行拦截,但是 defineProperty 是拦截具体某个属性,Proxy 才是真正的代理。
如果大家有认真看过 Vue2 文档,在 Vue2 官方文档中有这么一句话“由于 JavaScript 的限制,Vue 不能检测数组和对象的变化。”
怎样去理解 Object.defineProperty(),你可以到 JavaScript | MDN 查看详细文档,并且有详细的使用。
Object.defineProperty() 最核心的作用就是通过为对象的某几个属性来去指定描述符的形式去监听对象中某几个属性的 get 和 set。
就因为这样的一个特性,因此,在下面的这几种场景之中,新的属性是没有办法通过 Object.defineProperty() 来去进行监测到的。
场景一:对于对象
下面的代码中,我们在一开始声明了一个 data 对象,在该对象中定义了一个属性 a,并赋值为 1。而如果通过 vm.b 的形式来为这个对象新增了一个属性 b,并且赋值为 2。那么这种 Object.defineProperty() 形式是没有办法监测到的,因此,b 属性并不是一个响应式。
let vm=new Vue({
data:{
a:1
}
})
// mv.b 非响应式
vm.b=2
场景二:对于数组
而对于数组也是一样,如果通过 Array.push 这样的方法去给它添加数据,是没有问题的,万事大吉。但是,如果通过这种下标的形式数组重新赋值,或者通过 length 属性的形式给数组重新赋值,那么,同样也没有办法去监测到这样的变化。
let vm = new Vue({
data:{
arr:['a','b','c']
}
})
//非响应式
vm.arr[1]='d'
//非响应式
vm.arr.length='e'
这也是为什么在 Vue2 里面会存在着 Vue.set 方法的原因,这个方法就是为了向响应式数据中新增一个属性,并确保新的属性同样是响应式。
但是,虽然 在 Vue2 里面通过 Vue.set 方法来解决了这个非响应式的问题。其实这种情况一直都是不合理的。
为什么呢?因为 Vue.set 方法只是无意义的增加了我们的项目的复杂度而已,但是一直以来因为 Object.defineProperty() 这个 API 本身的限制,则一直没有办法去进行处理。
这个问题一直持续到了 Vue3 的发布,在 Vue3 里面摈弃掉了 Object.defineProperty(),而是通过了一个新的 Proxy 代理 API 来去处理了响应式的问题。
如何理解 Proxy,大家可以到 JavaScript | MDN 查看详细文档,详细地介绍 Proxy 如何使用。
它核心作用就是为对象去创建一个代理,从而实现一些基本的拦截操作和自定义操作,这里所指的拦截操作就是前面所提到的 get 和 set 对应操作。
通过 Proxy 就从根本上去解决了 Object.defineProperty() 所面临的这样的两个问题,这也是为什么 Vue3 里面就没有了 Vue.set 方法的原因。
全新的全家桶
为什么 Vue 被称之为是一个渐进式框架呢?就是因为对于 Vue 而言,它不仅仅是只有 Vue.js 这样的一个核心 JavaScript 库,它还有很多其他的周边库,比如 Vue-Router、Vuex 等等一些周边库存在,而这些周边库和 Vue.js 这个核心库一起共同组成了 Vue,这是我们 Vue 中最基础的知识。因此,在 Vue3 发布之后,它的这些周边库也会迎来一波新的更新。
而关于这些全家桶的一些更新内容,我们会在后面的课程中该大家去进行详细的讲解,在这里就不再去进行一些赘述了。
全新的 TS 的支持
这里的 TS 指的就是 TypeScript。如果大家关注过一些 Vue3 中代码的变化,是可以知道的 Vue3 使用 TypeScript 去进行代码的重构。重构的目的是为了防止随着应用的增长而产生许多潜在的运行时静态类型的错误。
而这意味着以后在 Vue 里面去使用 TypeScript,不需要借助其他的任何工具了。但是对于 TypeScript,有一点需要去提醒大家,虽然 Vue 对 TypeScript 进行了一个全新的支持,但是这并不代表着我们应该在任何情况下都无条件的去使用 TypeScript。
因为 TypeScript 主要的优势其实在于静态类型检查和环境声明,但同时这样的两个特性也会为我们的项目去增加非常高的复杂度。如果我们的项目需要使用到这两点特性的话,那么非常推荐你去使用 TypeScript。否则,你只是无谓的增加了项目的复杂度而已。
因此,决定我们使用哪一种技术的唯一条件就是我们的目标,而我们在当前的这个项目之中。在我们完成目标的基础之上,应该要做的就是寻找到最简单的一个实践方案,而不是找到一个最复杂的实践方案。
新一代工程化工具 Vite
严格来说,Vite 并不能算是 Vue3 里面的一个内容,只不过,在Vite 是跟随 Vue3 一起去进行了发布,并且热度又很广。
对于 Vite 来说,其实它是和 Webpack 一样的,也是一个前端的打包工具。而它区别于 Webpack 地方是它使用了基于原生 ES 模块所提供的一个特性。它基于原生的 ES 模块达到如速度快到惊人的热模块更新的能力。
对于 Vite 而言,现在它依然存在着一些问题,什么问题呢?因为 Vite 完全依赖于 ES Module,所以就导致它无法直接对 CommonJS 模块进行模块化的支持。如果在 Vite 里面想要去处理 CommonJS 的模块化支持,那么就必须采用依赖预构建的一种形式。这个在Vite 的官方文档中也有详细的介绍。
虽然 Vite 是一个全新的东西,尤大神非常的看好。但是,就目前我们国内的实际企业开发场景中,Vite 还不够稳定的足够支撑商用。
如果大家想要创建一个大型的商业级的应用的话,个人还是推荐更加成熟的 Webpack 解决方案,毕竟在商业的项目里面稳定是超过一切。