vue2技术栈思考 & vue-class-component原理分析

实习入职第四天了,通过阅读项目代码发现业务开发中前辈们使用vue2都是基于class-styled-component 去开发的,不去采用options 风格去开发组件自然有它的出发点与考量,我认为核心在与class-styled-componet对于ts有更好的支持。并且基于这个问题的思考我翻看了一下vue-class-component和vue-property-decorator的源码,理清了他们的底层运行逻辑,并做了下文的记录。

vue与ts

说到vue3相较于vue2的优势,大概率会提到vue3有更好的typescript支持,社区里有不少人说因为vue3是用ts写的,确实用ts写的话自然vue项目打包出来就顺带得到了.d.ts类型声明文件,我觉着这个勉强算个原因,但不主要。

我认为vue2之所以对ts支持差,是因为它options api(配置式)的风格,它的组件就是一个对象,方法、计算属性、watch回调...啥都属于这个组件对象,但ts的类型检查能力不是针对对象的!而是针对函数和类class的,这也就是为啥vue2中如果想用ts,一般都会选择Vue2+TypeScript+vue-class-component+vue-property-decorator的技术栈体系,以OO(面向对象)的方式开发Vue程序。

本质上vue-class-component就是把class风格的组件最终转化成options配置组件,至于vue-property-decorator就是基于vue-class-component提供的自定义装饰器的能力,把原先的option配置通过装饰器的形式提供给我们,方便开发。

至于vue3,它组合式api的风格,把vue2中的各种配置都转化为了用函数来定义,这样自然而然就与ts的类型检查能力相契合了。

vue-class-component原理

我们写一个类组件时会用Component装饰器去修饰class,下面是Component源码:

vue-class-component/src/index.ts:

typescript 复制代码
function Component <V extends Vue>(options: ComponentOptions<V> & ThisType<V>): <VC extends VueClass<V>>(target: VC) => VC
function Component <VC extends VueClass<Vue>>(target: VC): VC
function Component (options: ComponentOptions<Vue> | VueClass<Vue>): any {
  if (typeof options === 'function') {
    return componentFactory(options)
  }
  return function (Component: VueClass<Vue>) {
    return componentFactory(Component, options)
  }
}

说白了Component就是一个重载函数,目的就是支持多种传参形式,本质是一个装饰器工厂(即return function xxx创建装饰器函数),而装饰器函数的逻辑就是对componentFactory函数的调用并返回。

vue-class-component/src/component.ts:

经过上面的源码分析,给人的最直观的感觉就是:@Component装饰器在装饰class类组件时做的事情类似于一种对所有class属性的"拦截",拦截过程中同步构造options对象组件。 所以我们也可以明确,vue-class-component将class组件处理成options组件也不是在编译时,而是运行时。

vue-property-decorator原理

我们实际开发用的不是vue-class-component,而是基于它二次开发的库vue-property-decorator,它提供了若干class属性的装饰器,如@Watch、@Prop等,其实本质就是让我们借助这些装饰器将class属性转化为vue组件的options对象的属性。

vue-class-component拓展机制

要想理解vue-property-decorator的原理,首先我们要明白vue-class-component是如何对外提供扩展能力的:

回顾@Component装饰器的实现,见上面github源码截图的第70-75行:

上面的代码我们尝试从Component,也就是class组件类身上拿到 __decorator__ 数组并遍历,从代码可知, __decorator__ 中存储的是函数回调,我们依次触发所有回调时会把 @Component装饰器正在构造的组件options对象 作为参数 传递给回调函数。

也就是说如果我们想基于vue-class-component拓展能力的话,我们可以通过给class添加__decorator__数组来完成(这样我们就有能力介入options组件对象的构造)。

这里有个需要注意的知识点------装饰器的执行顺序:首先执行属性装饰器,然后是方法装饰器,最后是类装饰器。

所以说@Component装饰器执行时可以保证class内部的所有逻辑已经执行完毕,即__decorator__已经收集完毕。

同时vue-class-component对外暴露了createDecorator这个api,让我们方便的基于vue-class-component进行拓展,vue-class-component/src/util.ts:

以@Ref为例分析

如果我们在class组件中写如下代码,通过上文的分析我们知道对于普通的class方法都是直接把函数体赋值到options对象身上的,所以经过@Component装饰器的处理,MyMethod方法的函数体仍然会是this.myDom,但是MyMethod方法中通过this.myDom我们预期的是访问this.$refs.myDom才对!

typescript 复制代码
@Component
export default class YourComponent extends Vue {
  // vue的ref的装饰器写法
  @Ref() myDom: HTMLDivElement
  
  myMethod() {
    this.myDom.style.color = '#f00';
  }
}

所以@Ref要做的事情就是让class组件中的this.myDom"映射"成options组件对象中的this.$refs.myDom,我们可以借助计算属性来完成这个转化,所以@Ref要做的就是给options对象添加一个名为myDom的计算属性,让它指向this.$refs.myDom即可!

下面我们看源码就不难理解了:

vue-property-decorator/src/decorators/Ref.ts:

typescript 复制代码
import Vue from 'vue'
import { createDecorator } from 'vue-class-component'
​
/**
 * decorator of a ref prop
 * @param refKey the ref key defined in template
 */
export function Ref(refKey?: string) { // refKey可以省略,我们忽略它往下看
  
  // createDecorator做的事情就是把函数参数封装一层(目的是把options传给函数参数)然后push的__decorator__数组中,等待@Component装饰器执行的最后调用
  return createDecorator((options, key) => {
    // 给options添加计算属性
    options.computed = options.computed || {} 
    options.computed[key] = {
      cache: false,
      get(this: Vue) {
        return this.$refs[refKey || key]
      },
    }
  })
}

上面的@Ref源码中,主体逻辑就是return createDecorator,createDecorator做的事情就是把函数参数封装一层(目的是把options传给函数参数)然后push的__decorator__数组中,等待@Component装饰器执行时调用。自然传递给createDecorator的函数逻辑就是给options对象添加computed计算属性了。

总结

  1. Vue2+TypeScript+vue-class-component+vue-property-decorator的技术栈体系目的是让vue2有更好的ts支持
  2. vue-class-component提供的核心装饰器@Component,是在class实例化的运行时,而非编译时,通过装饰器提供的类似于"拦截"的能力同步构建options对象组件,从而实现class组件到普通配置型对象组件的转化。
  3. vue-class-component提供了一定的拓展能力,vue-property-decorator就是基于vue-class-component自定义了一些常用的属性/方法装饰器,从而实现class组件中一些常用配置的便捷定义。
相关推荐
卸任7 小时前
Vibe coding工具使用心得
前端·ai编程
brien98 小时前
为什么模型标称 1M,Google Antigravity 聊到 256K 却突然失忆?我们给它做了个原生行内监控圆环
前端
修炼的dance8 小时前
录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%
前端
Lank_M8 小时前
关键帧从1秒拉到10秒,录屏小87.3%,摄像头小10.8%
前端
补码缩写8 小时前
用 ffprobe 拆录屏,八成字节在 29 个关键帧上
前端
百思不得小李8 小时前
一个复杂的动态表单是如何实现的
前端·设计模式
陈随易8 小时前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员
风骏时光牛马8 小时前
前沿视界
前端
kyriewen9 小时前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员
陈随易9 小时前
3000 万人在用的翻译插件,重写一遍就是一门生意
前端·后端·程序员