Vue-cli3+Typescript+Element搭建專案

Primise7發表於2020-05-19

由於vue3.0已經發布beta版本,vue3.0將會支援typescript,加上之前angular2.0已經支援typescript。縱觀幾個框架,TS已經是一個趨勢。能夠熟練掌握TS並可以應用到專案中去,就可以成為前端開發中的優勢。因為vue2.0對TS不太友好,所以就一直擱置沒有去重構。vue3.0在休息時間也準備重構一下自己得專案,在實踐中不斷摸坑。

1.使用vue-cli快速搭建專案

本人工作專案中使用得vue-cli3,所以打算用vue-cli3腳手架來搭建整個專案,與vue-cli2不同的是webpack整合到vue.config.js裡面了,所以還是比較方便得,當然蘿蔔白菜各有所愛,完全看自己的喜歡怎麼舒服怎麼來了。
1.初始化專案以及配置項
在這裡插入圖片描述
在這裡插入圖片描述
安裝相對應得配置項
在這裡插入圖片描述
然後等待自動安裝,等到搭建完之後,大致框架結構就是這樣。

2.目錄檔案說明

* public: 靜態資原始檔
* node_modules: 安裝的依賴
* src:專案原始碼
   - api: 存放所有的api請求,按照模組編寫,入口檔案為`index.js`
   - assets:靜態檔案目錄
   - components:公共元件目錄,目標命名為大駝峰,自動註冊
   - store:vuex目錄 
   - router:專案主路由
   - views:專案html檔案
   - App.vue: 頁面入口
   - main.ts: 指令碼入口
   - shims-tsx.d.ts: 相關tsx模組注入
   - shims-vue.d.ts: Vue 模組注入
* .env.development:開發環境變數檔案
* .env.production:生產環境變數檔案
* .env.test:測試環境變數檔案
* babel-config.js: babel配置
* package.json: 依賴
* tsconfig.json: ts依賴
* vue.config.js:專案webpack整合配置檔案 (自己建立 )

專案中vue.config.js檔案 借鑑大佬的vueconfig.js配置

const path = require('path')

function resolve(dir) {
   return path.join(__dirname, dir)
}

const webpack = require('webpack')
const CompressionWebpackPlugin = require('compression-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const LodashModuleReplacementPlugin = require("lodash-webpack-plugin");
const isPro = process.env.NODE_ENV === 'production';
module.export = {
   publicPath: "./",//公共路徑
   outputDir: 'dist',//打包輸出檔案目錄
   productionSourceMap: false,
   lintOnSave: false,//
   devServer: {
       overlay: {
           warning: false,
           errors: false
       },
       // port: PORT, //埠號
       https: false,
       hotOnly: false,
       proxy: { // 配置跨域
           '/api': {
               //要訪問的跨域的api的域名
               target: `${DEV_URL}/`,
               ws: true,
               secure: false,
               changOrigin: true,
               pathRewrite: {
                   '^/api': ''
               }
           }
       },
   },
   chainWebpack: () => { },
   configureWebpack:config=>{
       config.resolve = {
           extensions: ['.js', '.vue', '.json', ".css"],
           alias: {
               'vue$': 'vue/dist/vue.esm.js',
               '@': resolve('src'),
               "assets": resolve('src/assets')
           }
       }
       if (isPro) {
           return {
               plugins: [
                   //使用包分析工具
                   new BundleAnalyzerPlugin()
               ]
           }
       }
       if (process.env.NODE_ENV === 'production') {
           config.plugins.push(
               new CompressionWebpackPlugin({
                   filename: '[path].gz[query]',
                   algorithm: 'gzip',
                   test: new RegExp('\\.(' + productionGzipExtensions.join('|') + ')$'), //匹配檔名
                   threshold: 10240, //對10K以上的資料進行壓縮
                   minRatio: 0.8,
                   deleteOriginalAssets: false, //是否刪除原始檔
               }),
           )
       }
   },
   pluginOptions: {
       // ...第三方外掛
   }


}

接下來就cd到當前目錄,執行yarn serve 或者npm run serve
在這裡插入圖片描述
然後安裝UI框架

cnpm i element-ui -S

由於本專案使用的元件不多,為了優化,採用按需載入

cnpm install babel-plugin-component -D

安裝完後修改babel.config.js

module.exports = {
  // presets: [
  //   '@vue/cli-plugin-babel/preset'
  // ]
  "presets": [["es2015", { "modules": false }]],
  "plugins": [
    [
      "component",
      {
        "libraryName": "element-ui",
        "styleLibraryName": "theme-chalk"
      }
    ]
  ]
}

可以新建plugin 在main.ts引用外掛
接下里就完善一下專案的結構
Route目錄( 安裝進度條)

 cnpm install --save nprogress 
 cnpm install --save-dev @types/nprogress

3.構建路由模組

路由主要採用懶載入,本專案只是簡單設計 沒有做許可權Rbac許可權設計如圖所示:
路由

4.狀態管理Vuex

官方文件 https://championswimmer.in/vuex-module-decorators/
因為vue2.x版本vuex對ts的相容性不是很好,為了達到狀態管理模組,這裡要額外引用一個類庫vuex-module-decorators,它是基於vue-class-component 所做的擴充,它提供了一系列的裝飾器,讓vue+ts結合的專案達到狀態管理的作用。

cnpm install -D vuex-module-decorators

import { VuexModule, Module, Mutation, Action, getModule, } from 'vuex-module-decorators';
import { login } from '@/api/user'
import store from '@/store'

export interface UserInfo {
    userName:string,
    userPwd:string,
    roles:string[]

}

@Module({ name: 'user', dynamic: true, store })
//引數對比
// 引數一:module名稱,開啟名稱空間後會以name為名稱空間
// 引數二:是否使用動態載入,簡而言之只有在用到當前的module才會載入,詳細可以看vuex官網。本篇部落格必須選擇true,這也是為什麼index.ts一直不用修改的原因,如果設定為false會有很大的變動,如果您真的需要這麼做,可以自己研究一下。
// 引數三:是否開啟名稱空間,如果你的模組很多,強烈建議開啟
// 引數四:掛載的store目標
class User extends VuexModule implements UserInfo {
    public userName: string = ''
    public userPwd: string = ''
    public roles: string[] = []

    @Mutation
    private SET_ROLES(roles:string[]) {
        this.roles = roles
    }

    @Action
    public async Login(params:any){
     console.log(params)
      let {userName,userPwd,validCode} =params
      const {data} = await login({userName,userPwd,validCode})
      console.log(data)
    }
}
export const UserModule = getModule(User)

上面的程式碼相當於普通vuex以下程式碼

import { login } from '@/api/index'
import { setStore, removeStore } from '@/assets/js/storage'
import Vue from 'vue'
const user = {
state: {
  token: '',
  name: '',
  roles: [],
},
mutations: {
  SET_TOKEN: (state, token) => {
    state.token = token
  },
  SET_ROLES: (state, roles) => {
    state.roles = roles
  },
  SET_LOGOUT(state) {
    state.token = null;
    state.userInfo = null;
  },
  SET_NAME:(state,name)=>{
    state.name = name;
  }
},
actions: {
  SET_LOGOUT({ commit }) {
    removeStore('token');
    removeStore('userInfo');
    commit('SET_LOGOUT');
  },
  Login({ commit }, userInfo) {
    console.log(userInfo)
    return new Promise((resolve, reject) => {
      login(userInfo).then(response => {
        const result = response.object;
        commit('SET_TOKEN', result.userInfo.userToken),
        commit('SET_NAME', result.userInfo.userName)
        setStore('token', result.userInfo.userToken);
        setStore('userInfo', result.userInfo);
        resolve()
      })
      //  commit('SET_TOKEN','2113165465456465465');
      //  commit('SET_ROLES', ['1','2'])
      //  
    }).catch(error => {
      reject(error)
    })
  }
}
}

export default user

同時寫在vuex的方法在vue頁面呼叫的時候

import { Component, Vue } from "vue-property-decorator";
import { Form as ElForm, Input } from 'element-ui'
import {UserModule} from '@/store/modules/user'
import {validCode} from '@/api/user'
@Component({
 name: "login",
})
export default class extends Vue {
 private loginForm = {
   userName: "admin",
   userPwd: "1234567",
   validCode:''
 }
 private loginRules = {
   userName: [{ required: true, trigger: "blur" }],
   userPwd: [{ required: true, trigger: "blur" }]
 }
 private loading = false
 private capsTooltip = false
 private passwordType = "password"
 mounted() {
   // if (this.loginForm.userName === "") {
   //   this.$refs.userName.focus();
   // } else if (this.loginForm.userPwd === "") {
   //   this.$refs.userPwd.focus();
   // }
 }
 created(){
   this.refreshCode();
 }
 private async refreshCode() {
      const { object } = await validCode()
     this.loginForm.validCode = object
      console.log(this.loginForm)
 }
 private handleLogin() {
   (this.$refs.loginForm as ElForm).validate(async (valid: boolean) => {
     if (valid) {
       await UserModule.Login({
         ...this.loginForm,
       });
     } else {
       return false;
     }
   });
 }

在頁面使用ts的時候基本上template模板編譯基本上不會改變 ,script 會加上lang=ts 有點類似於java語言的感覺寫到這裡基本上一個專案的架構搭建起來了。後續會繼續更新內容未展示的 Watch Prop等方法。

程式碼借鑑:花褲衩的程式碼結構https://gitee.com/panjiachen/vue-element-admin

相關文章