Vuex? 和 TypeScript 的 Webpack4.+ 嚐鮮

圖片描述

靜態類型系統能幫助你有效防止許多潛在的運行時錯誤,並且隨着你的應用日漸豐滿會更加顯著。這就是爲何 Vue 不單單爲 Vue core 提供了針對 TypeScript 的官方類型聲明,還爲 Vue Router 和 Vuex 也提供了相應的聲明文件

TsConfig配置

{
    "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的配置,只有配置好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

  • vue-loader v15須要在webpack插件中添加VueLoaderPlugin插件
  • webpack4.+須要指定mode,開發模式仍是生產模式
  • 注意ts-loader的配置

這裏只是簡單進行webpack配置,沒有太完整地根據完整的項目來進行配置,只是簡單配置了生產環境下的代碼混淆壓縮,以及對應的開發服務器和熱更新等,有須要其餘功能擴展的自行配置。ios

Vue環境搭建配置

vue-shims.d.ts的添加

這個是比較重要的一個配置,該文件須要放到vue的入口文件中,具體的d.ts代碼以下:git

declare module '*.vue' {
  import Vue from 'vue'
  export default Vue
}

目的是讓ts可以識別到vue的靜態類型es6

vue的入口文件

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

開始寫vue的單文件頁面和組件

單文件頁面模板

<template>
  ...
</template>

<script lang="ts">
  ...
</script>

<style>
  ...
</style>

主要是在script項中把lang寫爲ts類型

使用裝飾器來實現組件和頁面

這裏咱們主要使用兩個裝飾器庫vue-property-decorator 和 vuex-class, vue-property-decorator其是基於vue-class-得component的基礎擴展修改的。

  1. 大體瞭解一下vue-property-decorator的裝飾器的用法

一共有七個裝飾器:

  • @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
    }
  }
})

相信經過以上的例子咱們很容易就看出各個裝飾器如何去使用,這裏就再也不作太多的解釋。

  1. 再看一下vuex-class的使用方法

一樣舉例官方的使用列子

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']
  }
}

Vuex的配置

store的入口

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的狀態

vuex的modules的配置

  1. 寫一個全局類型聲明
export interface RootState {
  version: string;
}

version字段就是咱們剛纔在RootState中定義的字段

  1. 定義模板profile

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怎麼配置。

相關文章
相關標籤/搜索