靜態類型系統能幫助你有效防止許多潛在的運行時錯誤,並且隨着你的應用日漸豐滿會更加顯著。這就是爲何 Vue 不單單爲 Vue core 提供了針對 TypeScript 的官方類型聲明,還爲 Vue Router 和 Vuex 也提供了相應的聲明文件
{ "compilerOptions": { // ts 文件編譯成 js 文件的時候,同時生成對應的 map 文件 "sourceMap": true, "strict": true, "strictNullChecks": true, // 當表達式和申明 類型爲any時,是否須要發出警告,設置true,則不警告 "noImplicitAny": true, // 設置爲true時,若是不是函數中的全部路徑都有返回值,則提示Error "noImplicitReturns": true, // module 用於指定模塊的代碼生成規則,可使用 commonjs 、 amd 、 umd 、 system 、 es6 、 es2015 、 none 這些選項。 // 選擇commonJS,會生成符合commonjs規範的文件,使用amd,會生成知足amd規範的文件,使用system會生成使用ES6的 // system.import的代碼。使用es6或者是es2015會生產包含ES6特性的代碼。 "module": "es2015", "moduleResolution": "node", // 設置爲true時,則容許從沒有默認導出的模塊中默認導入(也就是不作檢查) "allowSyntheticDefaultImports": true, // 設置爲true,則支持ES7的裝飾器特性 "experimentalDecorators": true, // target 用於指定生成代碼的兼容版本,能夠從es3,es5,es2015,es6中選擇一個,若是不設置,默認生產的代碼兼容到es3 "target": "es5" }, "include": [ "./src/**/*" ] }
配置參考:css
每一個項目最重要的一部分我的感受是webpack的配置,只有配置好webpack部分後續才能順利進行開發html
這裏webpack使用了4.+的版本,因此算是體驗了較爲新的webpack,其中和舊版的有些區別,這裏不作介紹vue
先貼出webpack的配置代碼node
const path = require('path') const webpack = require('webpack') const VueLoaderPlugin = require('vue-loader/lib/plugin') module.exports = { mode: process.env.NODE_ENV === 'production' ? 'production' : 'development', entry: './src/index.ts', output: { path: path.resolve(__dirname, './dist'), publicPath: '/dist/', filename: 'build.js' }, module: { rules: [ { test: /\.vue$/, loader: 'vue-loader', options: { loaders: { 'scss': 'vue-style-loader!css-loader!sass-loader', 'sass': 'vue-style-loader!css-loader!sass-loader?indentedSyntax', } } }, { test: /\.tsx?$/, loader: 'ts-loader', exclude: /node_modules/, options: { transpileOnly: true, appendTsSuffixTo: [/.vue$/] } } ] }, resolve: { extensions: ['.ts', '.js', '.vue', '.josn'], alias: { 'vue$': 'vue/dist/vue.esm.js' } }, devServer: { contentBase: './public', host: 'localhost', port: '8080', open: true, hot: true, inline: true, historyApiFallback: true, noInfo: true }, performance: { hints: false }, devtool: '#eval-source-map', plugins: [ new VueLoaderPlugin() ] } if (process.env.NODE_ENV === 'production') { module.exports.devtool = '#source-map' module.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 }) ]) } else { module.exports.plugins = (module.exports.plugins || []).concat([ new webpack.HotModuleReplacementPlugin() ]) }
注意點:webpack
這裏只是簡單進行webpack配置,沒有太完整地根據完整的項目來進行配置,只是簡單配置了生產環境下的代碼混淆壓縮,以及對應的開發服務器和熱更新等,有須要其餘功能擴展的自行配置。ios
這個是比較重要的一個配置,該文件須要放到vue的入口文件中,具體的d.ts代碼以下:git
declare module '*.vue' { import Vue from 'vue' export default Vue }
目的是讓ts可以識別到vue的靜態類型es6
index.ts:github
import Vue from 'vue' import App from './App.vue' // vuex部分 import store from './store' new Vue({ el: '#app', store, render: h => h(App), })
入口文件跟普通的js寫法沒有太多的區別,只是文件類型爲ts。web
<template> ... </template> <script lang="ts"> ... </script> <style> ... </style>
主要是在script項中把lang寫爲ts類型
這裏咱們主要使用兩個裝飾器庫vue-property-decorator 和 vuex-class, vue-property-decorator其是基於vue-class-得component的基礎擴展修改的。
一共有七個裝飾器:
@Emit
@Inject
@Model
@Prop
@Provide
@Watch
@Component
(exported from vue-class-component
)這裏使用vue-property-decorator的例子來作解析
import { Component, Emit, Inject, Model, Prop, Provide, Vue, Watch } from 'vue-property-decorator' const s = Symbol('baz') @Component export class MyComponent extends Vue { @Emit() addToCount(n: number){ this.count += n } @Emit('reset') resetCount(){ this.count = 0 } @Inject() foo: string @Inject('bar') bar: string @Inject({from: 'optional', default: 'default'}) optional: string @Inject(s) baz: string @Model('change') checked: boolean @Prop() propA: number @Prop({ default: 'default value' }) propB: string @Prop([String, Boolean]) propC: string | boolean @Provide() foo = 'foo' @Provide('bar') baz = 'bar' @Watch('child') onChildChanged(val: string, oldVal: string) { } @Watch('person', { immediate: true, deep: true }) onPersonChanged(val: Person, oldVal: Person) { } }
至關於js的寫法:
const s = Symbol('baz') export const MyComponent = Vue.extend({ name: 'MyComponent', inject: { foo: 'foo', bar: 'bar', 'optional': { from: 'optional', default: 'default' }, [s]: s }, model: { prop: 'checked', event: 'change' }, props: { checked: Boolean, propA: Number, propB: { type: String, default: 'default value' }, propC: [String, Boolean], }, data () { return { foo: 'foo', baz: 'bar' } }, provide () { return { foo: this.foo, bar: this.baz } }, methods: { addToCount(n){ this.count += n this.$emit("add-to-count", n) }, resetCount(){ this.count = 0 this.$emit("reset") }, onChildChanged(val, oldVal) { }, onPersonChanged(val, oldVal) { } }, watch: { 'child': { handler: 'onChildChanged', immediate: false, deep: false }, 'person': { handler: 'onPersonChanged', immediate: true, deep: true } } })
相信經過以上的例子咱們很容易就看出各個裝飾器如何去使用,這裏就再也不作太多的解釋。
一樣舉例官方的使用列子
import Vue from 'vue' import Component from 'vue-class-component' import { State, Getter, Action, Mutation, namespace } from 'vuex-class' const someModule = namespace('path/to/module') @Component export class MyComp extends Vue { @State('foo') stateFoo @State(state => state.bar) stateBar @Getter('foo') getterFoo @Action('foo') actionFoo @Mutation('foo') mutationFoo @someModule.Getter('foo') moduleGetterFoo @State foo @Getter bar @Action baz @Mutation qux created () { this.stateFoo // -> store.state.foo this.stateBar // -> store.state.bar this.getterFoo // -> store.getters.foo this.actionFoo({ value: true }) // -> store.dispatch('foo', { value: true }) this.mutationFoo({ value: true }) // -> store.commit('foo', { value: true }) this.moduleGetterFoo // -> store.getters['path/to/module/foo'] } }
import Vue from 'vue' import Vuex, { StoreOptions } from 'vuex' import { RootState } from './modules/types' import { profile } from './modules/profile' Vue.use(Vuex) const store: StoreOptions<RootState> = { state: { version: 'v1.0.0' }, modules: { profile } } export default new Vuex.Store<RootState>(store);
這裏RootState只是用於留空,目的是爲了注入全局的store,區別於modules的狀態
export interface RootState { version: string; }
version字段就是咱們剛纔在RootState中定義的字段
profile模塊的類型聲明:
export interface ProfileState { firstName: string lastName: string }
profile的模塊實現:
import { RootState } from '../types' import { Module } from 'vuex' import { ProfileState } from './types' import { GetterTree, ActionTree, MutationTree } from 'vuex' import axios, { AxiosPromise } from 'axios' const state: ProfileState = { firstName: '', lastName: '' } const getters: GetterTree<ProfileState, RootState> = { firstName(state) : string { return state.firstName }, lastName(state) : string { return state.lastName } } const actions: ActionTree<ProfileState, RootState> = { fetchName({ commit }, id: number): AxiosPromise<ProfileState> { console.log('action:', id) return axios.request({ url: 'https://www.apiopen.top/satinCommentApi?id=27610708&page=1' }).then(res => { commit('setProfile', { firstName: 'lin', lastName: 'guangyu' }) return res }).catch(err => { return err }) } } const mutations: MutationTree<ProfileState> = { setProfile(state, payload: ProfileState) { state.firstName = payload.firstName state.lastName = payload.lastName } } const namespaced: boolean = true; export const profile: Module<ProfileState, RootState> = { namespaced, state, getters, actions, mutations };
這裏咱們就完成了Vuex的配置了,就能夠結合裝飾器對vuex進行調用,並且具備靜態類型提示,十分方便。
完成了這一系列的配置咱們的嘗試已經完成,本身寫了個簡單的demo,有興趣能夠觀看github怎麼配置。