作者:京东物流 吴云阔

1 前言

Vue3曾经正式公布了一段时间了,各种生态曾经成熟。最近应用taro+vue3重构冷链的小程序,通过了一段时间的开发和应用,有了一些本人的思考。

总的来说,Vue3无论是在底层原理还是在理论开发过程中,都有了很大的提高。

从源码层面来说,应用Proxy代替Object.defineProperty的API,一个是代理的对象,一个是递归监控的属性,从而在性能上有了很大的提高,并且,也解决了对象动静属性减少、数组扭转监听上的缺点;对diff算法进行优化,减少了动态标记,大大提高了Vue的执行效率;还有动态晋升、事件监听缓存等一系列晋升效率的伎俩。

从利用层面来说,次要的扭转是将option API改成了composition API(组合式API),在业务中摈弃data、methods、生命周期函数隔离开的开发方式,使代码绝对于业务有更强的聚合性,在代码开发、代码浏览、代码保护方面对于开发者都是更加敌对。

对于typescript有了更好的反对,咱们晓得,对于大型的前端我的项目来说,应用typescript的类型校验,能使前端我的项目有更强的健壮性,这也使得Vue3对于大型项目的开发提供了更强的质量保证。

2 组合式API

所谓的组合式API,将Vue2中的data、methods、生命周期、数据监听等option,都封装成钩子函数,而后组合到setup函数中,其外围就在于setup函数。setup函数存在的意义,就是为了应用这些新增的组合式API,并且这些API只能在setup函数中应用。

setup函数执行的机会是,props初始化之后,beforeCreate函数执行之前,所以在执行setup时,还没有初始化Vue实例,因而在setup中不能应用this对象。setup函数的返回值会被注入到Vue实例中,供Vue组件应用,所以任何数据想在Vue组件的模板中应用,必须在setup函数中return进来。

组合式API的组合,体现在两个层面。第一层的意思是,将某一业务相干数据和解决逻辑放到一起,这是一种关注点的聚合,更不便咱们编写代码、解决业务逻辑,并且能更聚焦业务逻辑,更不便咱们看代码。第二层面的意思,当某个组件的业务逻辑足够简单,setup中的代码足够大的状况下,咱们能够在setup外部,进一步提取相干的一块业务,使代码逻辑更加清晰,做到了进一步的聚合作用。

如上面代码所示,将业务代码块A抽出来,则代码块A中return进去的数据就能够在组件中应用:

  1. // 组件
  2. import functionA from 'A'
  3. export default defineComponent({
  4. name: 'componentName',
  5. setup() {
  6. ...functionA()
  7. }
  8. })
  9. // 代码块A
  10. export default () => {
  11. return {
  12. a: 1
  13. }
  14. }

3 响应式API

在Vue3中响应式API,次要体现在ref和reactive两个函数。对于响应式API,想说两个问题,第一个是为什么要减少响应式API,第二个是响应式API函数ref和reactive的异同点。

3.1 为什么减少响应式API

在Vue2中所有数据都写在data的option中,data中的数据都是响应式的,这样产生的一个问题是,有些常量数据自身不须要监听,从而造成了资源的节约。所以在Vue3中减少了响应式API,只须要对须要动静更新dom的数据进行响应式,不须要动静更新dom的数据不进行响应式解决,从很大水平上节俭了资源。这里我感觉须要留神的是,写代码的时候肯定要认真思考一下,哪些数据须要进行响应式绑定,哪些数据不须要进行响应式绑定,而不是一股脑的全给绑定上,这样即便代码逻辑不能很清晰易懂,并且也会影响执行效率(写惯了Vue2的同学须要留神)。

3.2 ref和reactive的异同点

在理解了为什么要减少响应式API后,咱们发现Vue3提供了两个响应式API函数,ref和reactive。为什么会提供两个API呢? 一个不就能够了吗?那么这两个API之间的区别是什么呢?

在应用层面,ref绑定的数据,须要应用[data].value进行数据更改。而reactive绑定的数据须要应用[data].[prpoerty]的形式进行数据更改。在应用场景方面,个别的,单个的一般数据,咱们应用ref来定义响应式。而简单数据,如:表单数据对象、某一模块的一组数据等,应用reactive来定义响应式。

那么,对象是不是必须用reactive来定义呢? 其实不是的,都能够。官网说法是:能够依据本身习惯应用不同的API。其实,我感觉,他们是有各自的应用场景的,ref更强调的是数据Value的扭转,reactive更强调的是数据中某一属性的扭转。

4 treeShaking思维

当 Javascript 我的项目达到肯定体积时,将代码分成模块会更易于治理。然而,当这样做时,咱们最终可能会导入实际上未应用的代码。Tree Shaking 是一种通过打消最终文件中未应用的代码来优化体积的办法。

Vue3应用了tree shaking的办法,将组件以及其所有的生命周期函数等办法进行离开,如果在组件中应用的代码将不会呈现在最终的打包文件中,如此,会缩小大大Vue3我的项目的打包体积。由此造成的一个后果就是,应用办法的不同。

4.1 生命周期函数的应用办法

  1. import { defineComponent, ref, onMounted } from 'vue';
  2. export default defineComponent({
  3. name: 'Gift',
  4. setup() {
  5. const counter = ref(0);
  6. onMounted(() => {
  7. // 解决业务,个别进行数据申请
  8. })
  9. return {
  10. counter
  11. }
  12. }
  13. })

4.2 Vuex的应用办法

  1. import { useStore } from "vuex";
  2. import { defineComponent, ref, computed } from 'vue';
  3. export default defineComponent({
  4. name: 'Gift',
  5. setup() {
  6. const counter = ref(0);
  7. const store = useStore();
  8. const storeData = computed(() => store); // 配合computed,获取store的值。
  9. return {
  10. counter,
  11. storeData
  12. }
  13. }
  14. })

4.3 Router的应用办法

  1. import { useStore } from "vuex";
  2. import { useRouter } from "vue-router";
  3. import { defineComponent, ref, computed } from 'vue';
  4. export default defineComponent({
  5. name: 'Gift',
  6. setup() {
  7. const counter = ref(0);
  8. const router = useRouter();
  9. const onClick = () => {
  10. router.push({ name: "AddGift" });
  11. }
  12. return {
  13. counter,
  14. onClick
  15. }
  16. }
  17. })

5 对于Typescript的应用

这一部分是对于Ts的内容,不过它与Vue3的开发非亲非故。Vue3整体是应用Ts写的,因而,开发Vue3的我的项目须要应用Ts,所以,咱们还是要理解TS的。

对于Ts的应用这里就不在细说了,在这里想说的的是,在理论业务场景中是如何组织Ts代码的。通过对TS的大量应用,我的一个领会是:Ts的外围思维是先关注数据结构,在依据数据结构进行页面开发。而以前的前端开发模式是,先写页面,而后再关注数据。

比如说,咱们要开发一个页面,咱们可能须要先定义一些interface。开发页面的时候咱们要关注:页面数据的interface、接口返回数据的类型、申请参数的类型等等。

上面是开发一个列表页面的例子:

  1. // 这是列表中每一项的数据类型
  2. interface IDataItem {
  3. id: string | number;
  4. name: string;
  5. desc: string;
  6. [key: string]: any;
  7. }
  8. // 接口返回值类型, 一般来说,咱们不确定接口返回的数据的类型,因而应用泛型
  9. interface IRes<T> {
  10. code: number;
  11. msg: string;
  12. data: T
  13. }
  14. // 口返回数据类型定义
  15. interface IDataInfo {
  16. list: Array<IDataItem>;
  17. pageNum: number;
  18. pageSize: number;
  19. total: number;
  20. }
  21. // 申请
  22. export const getDatalist = (
  23. params: Record<string, any>
  24. ): Promise<IRes<IDataInfo>> => {
  25. return Http.get("/api/data/list", params);
  26. };

如下面代码,当咱们的interface定义实现后,咱们的页面数据根本都已分明,间接写页面就会清晰很多,且出错概率会大大降低。