TypeScript+Vue

翟宏放
2023-12-01

Vue官方TypeScript支持文档

初始化项目

使用@vue/cli创建模版项目(啥是@vue/cli)
具体操作自行百度

提示:
@vue/cli脚手架默认生成的项目是零webpack配置,但是可以支持自定义webpack配置。只需在根目录下新建vue.config.js配置文件,这个文件会被@vue/cli-service自动加载。

vue.config.js基础配置模版:

const path = require("path");
const sourceMap = process.env.NODE_ENV === "development";

module.exports = {
  // 基本路径
  publicPath: "./",
  // 输出文件目录
  outputDir: "dist",
  // eslint-loader(作用:对编译前的ES6语法进行检查) 是否在保存的时候检查
  lintOnSave: false,
  // webpack配置
  chainWebpack: () => {},
  configureWebpack: config => {
    if (process.env.NODE_ENV === "production") {
      // 为生产环境修改配置
      config.mode = "production";
    } else {
      // 为开发环境修改配置
      config.mode = "development";
    }

    Object.assign(config, {
      // 开发生产共同配置
      resolve: {
        extensions: [".js", ".vue", ".json", ".ts", ".tsx"],
        alias: {
          vue$: "vue/dist/vue.js",
          "@": path.resolve(__dirname, "./src"),
          "@c": path.resolve(__dirname, "./src/components")
        }
      }
    });
  },
  // 生产环境是否生成 sourceMap 文件
  productionSourceMap: sourceMap,
  // css相关配置
  css: {
    // 是否使用css分离插件 ExtractTextPlugin
    extract: true,
    // 开启 CSS source maps?
    sourceMap: false,
    // css预设器配置项
    loaderOptions: {},
    // 设置为 false 后你就可以去掉文件名中的 .module 并将所有的 *.(css|scss|sass|less|styl(us)?)
    requireModuleExtension: false
  },
  // use thread-loader for babel & TS in production build
  // enabled by default if the machine has more than 1 cores
  parallel: require("os").cpus().length > 1,
  // PWA 插件相关配置
  // see https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-pwa
  pwa: {},
  // webpack-dev-server 相关配置
  devServer: {
    open: true,// 启动后自动打开浏览器
    host: "localhost",
    port: 8080,
    https: false,
    hotOnly: false,
    proxy: {
      // 设置代理
      // proxy all requests starting with /api to jsonplaceholder
      "/api": {
        target: "http://localhost:3000/",
        changeOrigin: true,
        ws: true,
        pathRewrite: {
          "^/api": ""
        }
      }
    },
    before: app => {}
  },
  // 第三方插件配置
  pluginOptions: {
    // ...
  }
};

注意

  • TypeScript开发环境默认只能识别.ts,.tsx。因此,在导入vue文件的时候,需要加上后缀名.vue

src目录下:

  • shims-tsx.d.ts
    作用:允许以.tsx的文件,在vue项目中写jsx代码
  • shims-vue.d.ts
    作用: 告诉TS以.vue为后缀的文件交给vue模块来处理
    declare module '*.vue' {
      import Vue from 'vue'
      export default Vue
    }
    

用法

一:基本用法

使用 Vue.componentVue.extend定义组件;
Vue.extend使用基础的Vue构造器,创建一个“子类”,这种方式最接近Vue的单文件组件写法。

import Vue from 'vue'
const Component = Vue.extend({
  // 类型推断已启用
})

const Component = {
  // 这里不会有类型推断,
  // 因为 TypeScript 不能确认这是 Vue 组件的选项
}

二:基于类的Vue组件用法

vue-class-componentvue-property-decorator

安装:

 yarn add vue-class-component vue-property-decorator --save-dev

区别:

  • vue-class-component官方出品(官方文档)
    是 vue 的官方库,作用是用类的方式编写组件,提供了Vue、Component等;

  • vue-property-decorator社区出品(文档)
    深度依赖了vue-class-component,拓展出了更多操作符:@Prop@Emit@Inject@Model@Provide@Watch

vue-property-decorator可以说是vue-class-component的一个超集,正常开发时只需使用vue-property-decorator即可。

具备以下几个装饰器和功能:

  1. @Prop
  2. @PropSync
  3. @Model
  4. @Watch
  5. @Provide
  6. @Inject
  7. @ProvideReactive
  8. @InjectReactive
  9. @Emit
  10. @Ref
  11. @Component (由 vue-class-component提供)
  12. Mixins (由 vue-class-component提供)

官方文档:https://www.npmjs.com/package/vue-property-decorator

@Prop

@Prop(options: (PropOptions | Constructor[] | Constructor) = {})

@Prop装饰器接收一个参数,这个参数可以有三种写法:

  • PropOptions,可以使用以下选项:type,default,required,validator;
  • Constructor[],指定 prop 的可选类型;
  • Constructor,例如String,Number,Boolean等,指定 prop 的类型;

示例代码:

import { Vue, Component, Prop } from 'vue-property-decorator'
 
@Component
export default class YourComponent extends Vue {
  @Prop(Number) readonly propA: number | undefined
  @Prop({ default: 'default value' }) readonly propB!: string
  @Prop([String, Boolean]) readonly propC: string | boolean | undefined
}

等同于:

export default {
  props: {
    propA: {
      type: Number,
    },
    propB: {
      default: 'default value',
    },
    propC: {
      type: [String, Boolean],
    },
  },
}

@PropSync

@PropSync(propName: string, options: (PropOptions | Constructor[] | Constructor) = {})
@PropSync装饰器与@prop用法类似,二者的区别在于:

  • @PropSync 装饰器接收两个参数:
  • propName: string 表示父组件传递过来的属性名;
  • options: Constructor | Constructor[] | PropOptions 与@Prop的第一个参数一致;
    @PropSync 会生成一个新的计算属性。

示例代码:

import { Vue, Component, PropSync } from 'vue-property-decorator'
 
@Component
export default class YourComponent extends Vue {
  @PropSync('name', { type: String }) syncedName!: string
}

等同于:

export default {
  props: {
    name: {
      type: String,
    },
  },
  computed: {
    syncedName: {
      get() {
        return this.name
      },
      set(value) {
        this.$emit('update:name', value)
      },
    },
  },
}

@Model

@Model(event?: string, options: (PropOptions | Constructor[] | Constructor) = {})
@Model装饰器允许我们在一个组件上自定义v-model,接收两个参数:

  • event: string 事件名。
  • options: Constructor | Constructor[] | PropOptions 与@Prop的第一个参数一致。

示例代码:

import { Vue, Component, Model } from 'vue-property-decorator'
 
@Component
export default class YourComponent extends Vue {
  @Model('change', { type: Boolean }) readonly checked!: boolean
}

相当于:

export default {
  model: {
    prop: 'checked',
    event: 'change',
  },
  props: {
    checked: {
      type: Boolean,
    },
  },
}
<template>
  <input
    type="text"
    :value="value"
    @change="$emit('change', $event.target.value)"
  />
</template>

@Watch

@Watch(path: string, options: WatchOptions = {})
@Watch 装饰器接收两个参数:

  • path: string 被侦听的属性名;
  • options?: WatchOptions={} options可以包含两个属性 :
    immediate?:boolean 侦听开始之后是否立即调用该回调函数;
    deep?:boolean 被侦听的对象的属性被改变时,是否调用该回调函数;

示例代码:

import { Vue, Component, Watch } from 'vue-property-decorator'
 
@Component
export default class YourComponent extends Vue {
  @Watch('child')
  onChildChanged(val: string, oldVal: string) {}
 
  @Watch('person', { immediate: true, deep: true })
  onPersonChanged1(val: Person, oldVal: Person) {}
 
  @Watch('person')
  onPersonChanged2(val: Person, oldVal: Person) {}
}

等同于:

export default {
  watch: {
    child: [
      {
        handler: 'onChildChanged',
        immediate: false,
        deep: false,
      },
    ],
    person: [
      {
        handler: 'onPersonChanged1',
        immediate: true,
        deep: true,
      },
      {
        handler: 'onPersonChanged2',
        immediate: false,
        deep: false,
      },
    ],
  },
  methods: {
    onChildChanged(val, oldVal) {},
    onPersonChanged1(val, oldVal) {},
    onPersonChanged2(val, oldVal) {},
  },
}

注意: 监听发生在beforeCreate勾子之后,created勾子之前

@Provide

@Provide(key?: string | symbol)

@Inject

@Inject(options?: { from?: InjectKey, default?: any } | InjectKey)

@ProvideReactive

@Provide(key?: string | symbol)

@InjectReactive

@InjectReactive(options?: { from?: InjectKey, default?: any } | InjectKey)

@Emit

@Emit(event?: string)

  • @Emit 装饰器接收一个可选参数,该参数是 E m i t 的 第 一 个 参 数 , 充 当 事 件 名 。 如 果 没 有 提 供 这 个 参 数 , Emit的第一个参数,充当事件名。如果没有提供 这个参数, EmitEmit会将回调函数名的camelCase转为kebab-case,并将其作为事件名;
  • @Emit会将回调函数的返回值作为第二个参数,如果返回值是一个Promise对象,$emit会在Promise对象被标记为resolved之后触发;
  • @Emit的回调函数的参数,会放在其返回值之后,一起被$emit当做参数使用。

@Ref

@Ref(refKey?: string)
@Ref 装饰器接收一个可选参数,用来指向元素或子组件的引用信息。如果没有提供这个参数,会使用装饰器后面的属性名充当参数

@Component

@Component(options:ComponentOptions = {})
@Component 装饰器可以接收一个对象作为参数,可以在对象中声明 components ,filters,directives等未提供装饰器的选项,也可以声明computed,watch等

Mixins

三: 两种用法的区别

参考地址

Vue.extend()

  1. 实现props,componentsdata与原JavaScript没任何区别。
  2. computed,methods,watch也没啥大改变,但是有this参与运算的必须标明返回值类型,不然会报错;
  3. 它不能完全实现mixins多混入的效果,只能混入一个(不推荐使用混入,无法实现多继承,Issue
  4. 函数式组件

vue-class-component/vue-property-decorator

  1. 使用props,componentswatch等需使用装饰器;
  2. data直接在类里面写变量;
  3. computed写法类似gettersetter;
  4. methods就直接在里面写方法;
  5. 能够实现多混入,写法类似类继承;
 类似资料: