Vue 2.x 如何接入
- vue-property-decorator
- vue-class-component
- vuex-class
依赖
npm install —save-dev typescript webpack webpack-cli ts-loader css-loader vue vue-loader vue-template-compiler
webpack.config.js
var path = require('path')var webpack = require('webpack')const VueLoaderPlugin = require('vue-loader/lib/plugin')module.exports = {entry: './src/index.ts',output: {path: path.resolve(__dirname, './dist'),publicPath: '/dist/',filename: 'build.js'},module: {rules: [{test: /\.vue$/,loader: 'vue-loader',options: {loaders: {// Since sass-loader (weirdly) has SCSS as its default parse mode, we map// the "scss" and "sass" values for the lang attribute to the right configs here.// other preprocessors should work out of the box, no loader config like this necessary.'scss': 'vue-style-loader!css-loader!sass-loader','sass': 'vue-style-loader!css-loader!sass-loader?indentedSyntax',}// other vue-loader options go here}},{test: /\.tsx?$/,loader: 'ts-loader',exclude: /node_modules/,options: {appendTsSuffixTo: [/\.vue$/],}},{test: /\.(png|jpg|gif|svg)$/,loader: 'file-loader',options: {name: '[name].[ext]?[hash]'}},{test: /\.css$/,use: ['vue-style-loader','css-loader']}]},resolve: {extensions: ['.ts', '.js', '.vue', '.json'],alias: {'vue$': 'vue/dist/vue.esm.js'}},devServer: {historyApiFallback: true,noInfo: true},performance: {hints: false},devtool: '#eval-source-map',plugins: [// make sure to include the plugin for the magicnew VueLoaderPlugin()]}if (process.env.NODE_ENV === 'production') {module.exports.devtool = '#source-map'// http://vue-loader.vuejs.org/en/workflow/production.htmlmodule.exports.plugins = (module.exports.plugins || []).concat([new webpack.DefinePlugin({'process.env': {NODE_ENV: '"production"'}}),new webpack.optimize.UglifyJsPlugin({sourceMap: true,compress: {warnings: false}}),new webpack.LoaderOptionsPlugin({minimize: true})])}
tsconfig.json
使用 tsc —init 生成 tsconfig.json
{"compilerOptions": {"outDir": "./built/","sourceMap": true,"strict": true,"noImplicitReturns": true,"module": "es2015","moduleResolution": "node","target": "es5"},"include": ["./src/**/*"]}
helloComponent.ts
// src/components/Hello.tsimport Vue from "vue";export default Vue.extend({template: `<div><div>Hello {{name}}{{exclamationMarks}}</div><button @click="decrement">-</button><button @click="increment">+</button></div>`,props: ['name', 'initialEnthusiasm'],data() {return {enthusiasm: this.initialEnthusiasm,}},methods: {increment() { this.enthusiasm++; },decrement() {if (this.enthusiasm > 1) {this.enthusiasm--;}},},computed: {exclamationMarks(): string {return Array(this.enthusiasm + 1).join('!');}}});
使用单文件组件
告诉 TypeScript .vue文件在导入时是什么样子,新增 vue-shim.d.ts 文件。
我们不需要将该文件导入到任何地方。
它自动包含在 TypeScript 中,并告诉它导入的以 . Vue 结尾的任何内容与 Vue 构造函数本身具有相同的形状。
// src/vue-shims.d.tsdeclare module "*.vue" {import Vue from "vue";export default Vue;}
第一个单文件组件
<!-- src/components/Hello.vue --><template><div><div class="greeting">Hello {{name}}{{exclamationMarks}}</div><button @click="decrement">-</button><button @click="increment">+</button></div></template><script lang="ts">import Vue from "vue";export default Vue.extend({props: ['name', 'initialEnthusiasm'],data() {return {enthusiasm: this.initialEnthusiasm,}},methods: {increment() { this.enthusiasm++; },decrement() {if (this.enthusiasm > 1) {this.enthusiasm--;}},},computed: {exclamationMarks(): string {return Array(this.enthusiasm + 1).join('!');}}});</script><style>.greeting {font-size: 20px;}</style>
使用装饰器
组件也可以使用装饰器来定义。
在两个附加包的帮助下(vue-class-component和vue-property-decorator),我们的组件可以按照以下方式重写:
import { Vue, Component, Prop } from "vue-property-decorator";@Componentexport default class HelloDecorator extends Vue {@Prop() name!: string;@Prop() initialEnthusiasm!: number;enthusiasm = this.initialEnthusiasm;increment() {this.enthusiasm++;}decrement() {if (this.enthusiasm > 1) {this.enthusiasm--;}}get exclamationMarks(): string {return Array(this.enthusiasm + 1).join('!');}}
vue-class-component
vue-class-component 对 Vue 组件进行了一层封装,让 Vue 组件语法在结合了 TypeScript 语法之后更加扁平化:**
<template><div><input v-model="msg"><p>prop: {{propMessage}}</p><p>msg: {{msg}}</p><p>helloMsg: {{helloMsg}}</p><p>computed msg: {{computedMsg}}</p><button @click="greet">Greet</button></div></template><script>import Vue from 'vue'import Component from 'vue-class-component'@Component({props: {propMessage: String}})export default class App extends Vue {// initial datamsg = 123// use prop values for initial datahelloMsg = 'Hello, ' + this.propMessage// lifecycle hookmounted () {this.greet()}// computedget computedMsg () {return 'computed ' + this.msg}// methodgreet () {alert('greeting: ' + this.msg)}}</script>
vue-property-decorator
vue-property-decorator 是在 vue-class-component 基础上增强了更多的结合 Vue 特性的装饰器,新增了这 7 个装饰器
- @Emit 指定事件 emit,可以使用此修饰符,也可以直接使用 this.$emit()
- @Inject 指定依赖注入)
- @Mixins mixin 注入
- @Model 指定 model
- @Prop 指定 Prop
- @Provide 指定 Provide
- @Watch 指定 Watch
- @Component export from vue-class-component

import { Component, Emit, Inject, Model, Prop, Provide, Vue, Watch } from 'vue-property-decorator'@Componentexport class MyComponent extends Vue {@Prop()propA: number = 1@Prop({ default: 'default value' })propB: string@Prop([String, Boolean])propC: string | boolean@Prop({ type: null })propD: any@Watch('child')onChildChanged(val: string, oldVal: string) { }}// 相当于export default {props: {checked: Boolean,propA: Number,propB: {type: String,default: 'default value'},propC: [String, Boolean],propD: { type: null }}methods: {onChildChanged(val, oldVal) { }},watch: {'child': {handler: 'onChildChanged',immediate: false,deep: false}}}
文件样式指南
api 顺序
vue 文件中 TS 上下文顺序
- data
- @Prop
- @State
- @Getter
- @Action
- @Mutation
- @Watch
- 生命周期钩子
- beforeCreate(按照生命周期钩子从上到下)
- created
- beforeMount
- mounted
- beforeUpdate
- updated
- activated
- deactivated
- beforeDestroy
- destroyed
- errorCaptured(最后一个生命周期钩子)
vuex
store 下面一个文件夹对应一个模块,每一个模块都有一个 interface 进行接口管理
Vue 大型 TS 项目实践经验
1 业务层面千万做好类型检测或者枚举定义,这样不仅便利了开发,还能在出了问题的时候迅速定位 2 如果定义了 .d.ts 文件,请重新启动服务让你的服务能够识别你定义的模块,并重启 vscode 让编辑器也能够识别 3 设置好你的 tsconfig ,比如记得把 strictPropertyInitialization 设为 false,不然你定义一个变量就必须给它一个初始值 4 跨模块使用 vuex,请直接使用 rootGetters
拓展vue 的声明 增强类型 支持this.$http这种写法

什么时候建议用 TypeScript
- 大型项目
- 项目生命周期长
- 团队成员接受程度高
- 项目是框架、库
