IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> 源码探秘之 vue-property-decorator -> 正文阅读

[JavaScript知识库]源码探秘之 vue-property-decorator

本文只浅析@Prop属性装饰器和@Watch方法装饰器的核心源码,其他原理相似,暂不赘述。

关于JS装饰器可查看本人另一篇:JS Decorator —— 装饰器(装饰模式)
关于@Component类装饰器及vue-class-component源码可查看本人另一篇:源码探秘之 vue-class-component

先回顾一下使用 vue+ts 开发的一个组件例子:

<template>
</template>

<script lang="ts">
// 此处是从vue-property-decorator 统一引入的,实际 Component 定义在 vue-class-component 包内
import { Component, Vue, Prop, Watch } from 'vue-property-decorator';

@Component({
  name: 'test' // 组件name
})
export default class Test extends Vue {
  // 父组件传递的参数
  @Prop({type: String, default: ''}) msg!:string

  // 定义的变量
  title = '标题'

  // 计算属性
  get computedMsg () {
    return 'computed ' + this.msg
  }

  // 侦听器
  @Watch('title', { immediate: true })
  watchTitleChange (val: string) {
    console.log('title change:', val);
  }

  // 方法
  initList () {
    // do something
  }

  // 生命周期
  mounted () {
    this.initList()
  }
}
</script>

经过@Component、@Prop、@Watch装饰器转换后,会变成标准的 Vue(2.x) 实例(当然很多属性是通过mixin加入进来的):

<script>
new Vue({
  name: 'test',
  props: {
    msg: { type: String, default: '' }
  },
  data() {
    return {
      title: '标题',
    };
  },
  computed: {
    computedMsg() {
      return 'computed ' + this.msg
    },
  },
  watch: {
    title: {
      handle(val) {
        console.log('title change:', val);
      },
      immediate: true
    },
  },
  methods: {
    initList() {
      // do something
    },
  },
  mounted() {
    this.initList();
  },
});
</script>

源码开始

@Prop属性装饰器

文件:src/decorators/Prop.ts

/**
 * decorator of a prop
 * @param  options the options for the prop
 * @return PropertyDecorator | void
 */
// 高阶函数接受参数,兼容三种写法
// @Prop(String) readonly name!: string | undefined;
// @Prop({ default: 30, type: Number }) private age!: number;
// @Prop([String, Boolean]) private sex!: string | boolean;
export function Prop(options: PropOptions | Constructor[] | Constructor = {}) {
  // 返回一个属性装饰器函数,传入当前Vue类 和 属性名
  return (target: Vue, key: string) => {
    // 设置类型
    applyMetadata(options, target, key)
    // 把props push到vue-class-component的__decorators__数组中
    createDecorator((componentOptions, k) => {
      // 将属性放在 Vue类.props 上, 生成如下对象,
      // 然后 createDecorator 会把他push到vue-class-component的__decorators__数组中
      // props: {
      //   name: String,
      //   age: { default: 30, type: Number },
      //   sex: [String, Boolean],
      // }
      ;(componentOptions.props || ((componentOptions.props = {}) as any))[
        k
      ] = options
    })(target, key)
  }
}

文件:/src/helpers/metadata.ts

// 设置类型
export function applyMetadata(
  options: PropOptions | Constructor[] | Constructor,
  target: Vue,
  key: string,
) {
  if (reflectMetadataIsSupported) {
    if (
      !Array.isArray(options) &&
      typeof options !== 'function' &&
      !options.hasOwnProperty('type') &&
      typeof options.type === 'undefined'
    ) {
      // 获取属性类型传至 Vue
      // 类型元数据使用元数据键"design:type"
      // 参考文章:https://jkchao.github.io/typescript-book-chinese/tips/metadata.html#%E5%9F%BA%E7%A1%80
      const type = Reflect.getMetadata('design:type', target, key)
      if (type !== Object) {
        options.type = type
      }
    }
  }
}

文件:vue-class-component/src/util.ts

// 接收一个工厂函数,返回一个装饰器函数
export function createDecorator (factory: (options: ComponentOptions<Vue>, key: string, index: number) => void): VueDecorator {
  return (target: Vue | typeof Vue, key?: any, index?: any) => {
    // 获取 vue-class-component 的 Component
    // 是函数类型,则为装饰的类;
    // 否则,为原型,通过constructor拿到构造函数
    const Ctor = typeof target === 'function'
      ? target as DecoratedClass
      : target.constructor as DecoratedClass
    if (!Ctor.__decorators__) {
      Ctor.__decorators__ = []
    }
    if (typeof index !== 'number') {
      index = undefined
    }
    // 将这个工厂函数(传入Vue类,属性名)push到vue-class-component的__decorators__数组中
    // 会在 vue-class-component 中调用
    Ctor.__decorators__.push(options => factory(options, key, index))
  }
}

@Watch方法装饰器

文件:/src/decorators/Watch.ts

/**
 * decorator of a watch function
 * @param  path the path or the expression to observe
 * @param  watchOptions
 */
// @Watch('title', { immediate: true })
// watchTitleChange (val: string) {
//   console.log('title change:', val);
// }
// path: 'title'    watchOptions: { immediate: true }
// componentOptions
export function Watch(path: string, watchOptions: WatchOptions = {}) {
  // 当前vue的option(在vue-class-component已经拼装好), handler: watchTitleChange 函数名
  return createDecorator((componentOptions, handler) => {
    componentOptions.watch ||= Object.create(null)
    // 拿到
    const watch: any = componentOptions.watch
    if (typeof watch[path] === 'object' && !Array.isArray(watch[path])) {
      watch[path] = [watch[path]]
    } else if (typeof watch[path] === 'undefined') {
      watch[path] = []
    }

    // watch: { title: [{ handler: watchTitleChange, immediate: true }] }
    watch[path].push({ handler, ...watchOptions })
  })
}

createDecorator方法在@Prop中有说

总结

核心就是:向vue-class-component 中Component构造函数的__decorators__属性中放进factory函数,然后在 @Component 中统一调用,目的是将这些属性/方法添加到option里组装。

这边调用有点绕,需要多看多理解。


码字不易,觉得有帮助的小伙伴点个赞支持下~


在这里插入图片描述

扫描上方二维码关注我的订阅号~

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-03-08 22:20:01  更:2022-03-08 22:21:39 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 9:49:19-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码