1. 程式人生 > 實用技巧 >vue-cli3.0 搭建專案模版教程(ts+vuex+axios)

vue-cli3.0 搭建專案模版教程(ts+vuex+axios)

vue-cli3.0 搭建專案模版教程(ts+vuex+axios)

1.本文模版不適用於小型專案,兩三個頁面的也沒必要用vue
2.對typescript、vue全家桶能夠掌握和運用

此次專案模版主要涉及的技術框架:
vue2.5
vuex3.0
vue-router3.0
axios
typescript3.2
Tip: 由於vue-cli3.0幫我們簡化了webpack的配置,我們只需要在根目錄下的vue.config.js檔案的chainWebpack進行配置即可。

接下來進入正題(前提是你已經安裝並配置好了node環境)

一.初始化專案
安裝vue-cli3.0
如果你之前安裝的是vue-cli2.0版本的,那麼你需要先解除安裝2.0版本,再安裝3.0版本

// cnpm 為淘寶映象
cnpm uninstall vue-cli -g // 解除安裝
cnpm install -g @vue/cli // 安裝新版本
建立專案
vue create vue-cli3-tpl
選擇Manually select features回車,按照下圖所示選中(空格選中)回車安裝外掛

然後一路回車,放一下配置圖
配置圖

等安裝完之後,進入專案並啟動專案
cd vue-cli3-tpl
cnpm run serve
啟動後顯示如下,第一步完成。

啟動成功顯示介面
二.刪除不必要的檔案
1.刪除assets、components、views目錄下的所有檔案。
2.刪除./src/store.ts。
3.刪除./src/router.ts

三.新增並配置檔案
1.新增資料夾並建立檔案
1.在根目錄下建立scripts資料夾,並新增template.js、component.js
2.在./src目錄下建立api資料夾
3.在./src目錄下建立config資料夾,並新增index.ts、requestConfig.ts
4.在./src目錄下建立router資料夾,並新增index.ts、router.ts
5.在./src目錄下建立store資料夾,並新增index.ts和module資料夾
6.在./src目錄下建立types資料夾,並新增index.ts、components資料夾和views資料夾
7.在./src目錄下建立utils資料夾,並新增common.ts和request.ts
8.在./src/assets目錄下建立images和scss兩個資料夾,並在scss資料夾下新增common.scss和variables.scss

2.配置檔案
首先配置一下根目錄下tslint.json,編碼習慣還是根據團隊的要求來,我自己關閉了很多在我看來很雞肋的東西,下面是個人的配置,僅供參考

{
"defaultSeverity": "warning",
"extends": [
"tslint:recommended"
],
"linterOptions": {
"exclude": [
"node_modules/**"
]
},
"rules": {
"quotemark": false, // 字串文字需要單引號或雙引號。
"indent": false, // 使用製表符或空格強制縮排。
"member-access": false, // 需要類成員的顯式可見性宣告。
"interface-name": false, // 介面名要求大寫開頭
"ordered-imports": false, // 要求將import語句按字母順序排列並進行分組。
"object-literal-sort-keys": false, // 檢查物件文字中鍵的排序。
"no-consecutive-blank-lines": false, // 不允許連續出現一個或多個空行。
"no-shadowed-variable": false, // 不允許隱藏變數宣告。
"no-trailing-whitespace": false, // 不允許在行尾新增尾隨空格。
"semicolon": false, // 是否分號結尾
"trailing-comma": false, // 是否強象新增逗號
"eofline": false, // 是否末尾另起一行
"prefer-conditional-expression": false, // for (... in ...)語句必須用if語句過濾
"curly": true, //for if do while 要有括號
"forin": false, //用for in 必須用if進行過濾
"import-blacklist": true, //允許使用import require匯入具體的模組
"no-arg": true, //不允許使用 argument.callee
"no-bitwise": true, //不允許使用按位運算子
"no-console": false, //不能使用console
"no-construct": true, //不允許使用 String/Number/Boolean的建構函式
"no-debugger": true, //不允許使用debugger
"no-duplicate-super": true, //建構函式兩次用super會發出警告
"no-empty": true, //不允許空的塊
"no-eval": true, //不允許使用eval
"no-floating-promises": false, //必須正確處理promise的返回函式
"no-for-in-array": false, //不允許使用for in 遍歷陣列
"no-implicit-dependencies": false, //不允許在專案的package.json中匯入未列為依賴項的模組
"no-inferred-empty-object-type": false, //不允許在函式和建構函式中使用{}的型別推斷
"no-invalid-template-strings": true, //警告在非模板字元中使用${
"no-invalid-this": true, //不允許在非class中使用 this關鍵字
"no-misused-new": true, //禁止定義建構函式或new class
"no-null-keyword": false, //不允許使用null關鍵字
"no-object-literal-type-assertion": false, //禁止object出現在型別斷言表示式中
"no-return-await": true, //不允許return await
"arrow-parens": false, //箭頭函式定義的引數需要括號
"adjacent-overload-signatures": false, // Enforces function overloads to be consecutive.
"ban-comma-operator": true, //禁止逗號運算子。
"no-any": false, //不需使用any型別
"no-empty-interface": true, //禁止空介面 {}
"no-internal-module": true, //不允許內部模組
"no-magic-numbers": false, //不允許在變數賦值之外使用常量數值。當沒有指定允許值列表時,預設允許-1,0和1
"no-namespace": [true, "allpw-declarations"], //不允許使用內部modules和名稱空間
"no-non-null-assertion": true, //不允許使用!字尾操作符的非空斷言。
"no-parameter-reassignment": true, //不允許重新分配引數
"no-reference": true, // 禁止使用/// <reference path=> 匯入 ,使用import代替
"no-unnecessary-type-assertion": false, //如果型別斷言沒有改變表示式的型別就發出警告
"no-var-requires": false, //不允許使用var module = require("module"),用 import foo = require('foo')匯入
"prefer-for-of": true, //建議使用for(..of)
"promise-function-async": false, //要求非同步函式返回promise
"max-classes-per-file": [true, 2], // 一個指令碼最多幾個申明類
"variable-name": false,
"prefer-const": false // 提示可以用const的地方
}
}
1.開啟./scripts/template.js,並新增以下內容

/*

  • @Description: 頁面快速生成指令碼
  • @Date: 2018-12-06 10:28:08
  • @LastEditTime: 2018-12-10 09:43:50
    */
    const fs = require('fs')
    const path = require('path')
    const basePath = path.resolve(__dirname, '../src')

const dirName = process.argv[2]
const capPirName = dirName.substring(0, 1).toUpperCase() + dirName.substring(1)
if (!dirName) {
console.log('資料夾名稱不能為空!')
console.log('示例:npm run tep ${capPirName}')
process.exit(0)
}

/**

  • @msg: vue頁面模版
    */
    const VueTep = `

    `

    // ts 模版
    const tsTep = `import { Component, Vue } from "vue-property-decorator"
    import { Getter, Action } from "vuex-class"
    import { ${capPirName}Data } from '@/types/views/${dirName}.interface'
    // import { } from "@/components" // 元件

    @Component({})
    export default class About extends Vue {
    // Getter
    // @Getter ${dirName}.author

    // Action
    // @Action GET_DATA_ASYN

    // data
    data: ${capPirName}Data = {
    pageName: '${dirName}'
    }

    created() {
    //
    }

    activated() {
    //
    }

    mounted() {
    //
    }

    // 初始化函式
    init() {
    //
    }

    }
    `

    // scss 模版
    const scssTep = `@import "@/assets/scss/variables.scss";

    .${dirName}-wrap {
    width: 100%;
    }
    `

    // interface 模版
    const interfaceTep = `// ${dirName}.Data 引數型別
    export interface ${capPirName}Data {
    pageName: string
    }

    // VUEX ${dirName}.State 引數型別
    export interface ${capPirName}State {
    data?: any
    }

    // GET_DATA_ASYN 介面引數型別
    // export interface DataOptions {}

    `

    // vuex 模版
    const vuexTep = `import { ${capPirName}State } from '@/types/views/${dirName}.interface'
    import { GetterTree, MutationTree, ActionTree } from 'vuex'
    import * as ${capPirName}Api from '@/api/${dirName}'

    const state: ${capPirName}State = {
    ${dirName}: {
    author: undefined
    }
    }

    // 強制使用getter獲取state
    const getters: GetterTree<${capPirName}State, any> = {
    author: (state: ${capPirName}State) => state.${dirName}.author
    }

    // 更改state
    const mutations: MutationTree<${capPirName}State> = {
    // 更新state都用該方法
    UPDATE_STATE(state: ${capPirName}State, data: ${capPirName}State) {
    for (const key in data) {
    if (!data.hasOwnProperty(key)) { return }
    state[key] = data[key]
    }
    }
    }

    const actions: ActionTree<${capPirName}State, any> = {
    UPDATE_STATE_ASYN({ commit, state: ${capPirName}State }, data: ${capPirName}State) {
    commit('UPDATE_STATE', data)
    },
    // GET_DATA_ASYN({ commit, state: LoginState }) {
    // ${capPirName}.getData()
    // }
    }

    export default {
    state,
    getters,
    mutations,
    actions
    }

    `

    // api 介面模版
    const apiTep = `import Api from '@/utils/request'

    export const getData = () => {
    return Api.getData()
    }

    `

    fs.mkdirSync(${basePath}/views/${dirName}) // mkdir

    process.chdir(${basePath}/views/${dirName}) // cd views
    fs.writeFileSync(${dirName}.vue, VueTep) // vue
    fs.writeFileSync(${dirName}.ts, tsTep) // ts
    fs.writeFileSync(${dirName}.scss, scssTep) // scss

    process.chdir(${basePath}/types/views); // cd types
    fs.writeFileSync(${dirName}.interface.ts, interfaceTep) // interface

    process.chdir(${basePath}/store/module); // cd store
    fs.writeFileSync(${dirName}.ts, vuexTep) // vuex

    process.chdir(${basePath}/api); // cd api
    fs.writeFileSync(${dirName}.ts, apiTep) // api

    process.exit(0)
    2.開啟./scripts/component.js,並新增以下內容

    /*

    • @Description: 元件快速生成指令碼
    • @Date: 2018-12-06 10:26:23
    • @LastEditTime: 2018-12-10 09:44:19
      */

    const fs = require('fs')
    const path = require('path')
    const basePath = path.resolve(__dirname, '../src')

    const dirName = process.argv[2]
    const capPirName = dirName.substring(0, 1).toUpperCase() + dirName.substring(1)
    if (!dirName) {
    console.log('資料夾名稱不能為空!')
    console.log('示例:npm run tep ${capPirName}')
    process.exit(0)
    }

    /**

    • @msg: vue頁面模版
      */
      const VueTep = `

      `

      // interface 模版
      const interfaceTep = `// ${dirName}.Data 引數型別
      export interface ${capPirName}Data {
      componentName: string
      }

      `

      fs.mkdirSync(${basePath}/components/${dirName}) // mkdir

      process.chdir(${basePath}/components/${dirName}) // cd views
      fs.writeFileSync(${dirName}.vue, VueTep) // vue

      process.chdir(${basePath}/types/components) // cd components
      fs.writeFileSync(${dirName}.interface.ts, interfaceTep) // interface

      process.exit(0)
      3.開啟./src/config/index.ts,並新增以下內容

      /**

      /**

      /**

      /**

      • 是否mock
        */
        export const ISMOCK: boolean = true

      /**

      • 當前的host ONLINEHOST | QAHOST | MOCKHOST
        */
        export const MAINHOST: string = ONLINEHOST

      /**

      • 請求的公共引數
        */
        export const conmomPrams: any = {}

      /**

      • @description token在Cookie中儲存的天數,預設1天
        */
        export const cookieExpires: number = 1

      4.開啟./src/config/requestConfig.ts,並新增以下內容

      export default {
      getData: '/mock/5c09ca373601b6783189502a/example/mock', // 隨機資料 來自 easy mock
      }
      5.開啟./src/router/index.ts,並新增以下內容

      import Vue from 'vue'
      import Router from 'vue-router'
      import routes from './router'
      import { getToken } from '@/utils/common'

      Vue.use(Router)

      const router = new Router({
      routes,
      mode: 'history'
      })

      // 登陸頁面路由 name
      const LOGIN_PAGE_NAME = 'login'

      // 跳轉之前
      router.beforeEach((to, from, next) => {
      const token = getToken()
      if (!token && to.name !== LOGIN_PAGE_NAME) {
      // 未登入且要跳轉的頁面不是登入頁
      next({
      name: LOGIN_PAGE_NAME // 跳轉到登入頁
      })
      } else if (!token && to.name === LOGIN_PAGE_NAME) {
      // 未登陸且要跳轉的頁面是登入頁
      next() // 跳轉
      } else if (token && to.name === LOGIN_PAGE_NAME) {
      // 已登入且要跳轉的頁面是登入頁
      next({
      name: 'index' // 跳轉到 index 頁
      })
      } else {
      if (token) {
      next() // 跳轉
      } else {
      next({
      name: LOGIN_PAGE_NAME
      })
      }
      }
      })

      // 跳轉之後
      router.afterEach(to => {
      //
      })

      export default router
      6.開啟./src/router/router.ts,並新增以下內容

      /**

      • meta 可配置引數
      • @param {boolean} icon 頁面icon
      • @param {boolean} keepAlive 是否快取頁面
      • @param {string} title 頁面標題
        */
        export default [
        {
        path: '/',
        redirect: '/index'
        },
        {
        path: '/login',
        name: 'login',
        component: () => import('@/views/login/login.vue'),
        meta: {
        icon: '',
        keepAlive: true,
        title: 'login'
        }
        },
        {
        path: '/index',
        name: 'index',
        component: () => import('@/views/index/index.vue'),
        meta: {
        icon: '',
        keepAlive: true,
        title: 'index'
        }
        }
        ]

      7.開啟./src/store/index.ts,並新增以下內容

      import Vue from 'vue'
      import Vuex from 'vuex'

      Vue.use(Vuex)

      export default new Vuex.Store({
      state: {
      //
      },
      mutations: {
      //
      },
      actions: {
      //
      },
      modules: {
      //
      }
      })

      8.開啟./src/utils/common.ts,在此之前先下載js-cookie,並新增以下內容

      // 下載js-cookie
      cnpm i js-cookie --S
      cnpm install @types/js-cookie --D
      /*

      • @Description: 公共函式
      • @Author: asheng
      • @Date: 2018-12-07 11:36:27
      • @LastEditors: asheng
      • @LastEditTime: 2018-12-12 13:37:30
        */

      import Cookies from 'js-cookie'
      import { cookieExpires } from '@/config' // cookie儲存的天數

      /**

      • @Author: asheng
      • @msg: 存取token
      • @param {string} token
        */
        export const TOKEN_KEY: string = 'token'
        export const setToken = (token: string) => {
        Cookies.set(TOKEN_KEY, token, { expires: cookieExpires || 1 })
        }
        export const getToken = () => {
        const token = Cookies.get(TOKEN_KEY)
        if (token) {
        return token
        } else {
        return false
        }
        }

      /**

      • @param {String} url
      • @description 從URL中解析引數
        */
        export const getParams = (url: string) => {
        const keyValueArr = url.split('?')[1].split('&')
        let paramObj: any = {}
        keyValueArr.forEach(item => {
        const keyValue = item.split('=')
        paramObj[keyValue[0]] = keyValue[1]
        })
        return paramObj
        }

      /**

      • 判斷一個物件是否存在key,如果傳入第二個引數key,則是判斷這個obj物件是否存在key這個屬性
      • 如果沒有傳入key這個引數,則判斷obj物件是否有鍵值對
        */
        export const hasKey = (obj: any, key: string | number) => {
        if (key) {
        return key in obj
        } else {
        const keysArr = Object.keys(obj)
        return keysArr.length
        }
        }

      /**

      • @msg: 獲取系統當前時間
      • @param {string} fmt 時間格式 具體看程式碼
      • @return: string
        */
        export const getDate = (fmt: any) => {
        let time = ''
        const date = new Date()
        const o: any = {
        "M+": date.getMonth() + 1, // 月份
        "d+": date.getDate(), // 日
        "H+": date.getHours(), // 小時
        "m+": date.getMinutes(), // 分
        "s+": date.getSeconds(), // 秒
        "q+": Math.floor((date.getMonth() + 3) / 3), // 季度
        "S": date.getMilliseconds() // 毫秒
        }
        if (/(y+)/.test(fmt)) {
        time = fmt.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length))
        }
        for (const k in o) {
        if (new RegExp("(" + k + ")").test(fmt)) {
        time = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)))
        }
        }
        return time
        }

      /**

      • @msg: 獲取系統當前時間
      • @param {string} date 時間
      • @param {string} fmt 時間格式
      • @return: string
        */
        export const formatDate = (date: any, fmt: string) => {
        let time = ''
        const o: any = {
        "M+": date.getMonth() + 1, // 月份
        "d+": date.getDate(), // 日
        "H+": date.getHours(), // 小時
        "m+": date.getMinutes(), // 分
        "s+": date.getSeconds(), // 秒
        "q+": Math.floor((date.getMonth() + 3) / 3), // 季度
        "S": date.getMilliseconds() // 毫秒
        }
        if (/(y+)/.test(fmt)) {
        time = fmt.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length))
        }
        for (const k in o) {
        if (new RegExp("(" + k + ")").test(fmt)) {
        time = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)))
        }
        }
        return time
        }

      // copy in the 'fx-fuli' utils
      /**

      • 校驗手機號是否正確
      • @param phone 手機號
        */

      export const verifyPhone = (phone: string | number) => {
      const reg = /^1[34578][0-9]{9}$/
      const _phone = phone.toString().trim()
      let toastStr = _phone === '' ? '手機號不能為空~' : !reg.test(_phone) && '請輸入正確手機號~'
      return {
      errMsg: toastStr,
      done: !toastStr,
      value: _phone
      }
      }

      export const verifyStr = (str: string | number, text: string) => {
      const _str = str.toString().trim()
      const toastStr = _str.length ? false : 請填寫${text}~
      return {
      errMsg: toastStr,
      done: !toastStr,
      value: _str
      }
      }

      // 擷取字串
      export const sliceStr = (str: any, sliceLen: number) => {
      if (!str) { return '' }
      let realLength = 0
      const len = str.length
      let charCode = -1
      for (let i = 0; i < len; i++) {
      charCode = str.charCodeAt(i)
      if (charCode >= 0 && charCode <= 128) {
      realLength += 1
      } else {
      realLength += 2
      }
      if (realLength > sliceLen) {
      return ${str.slice(0, i)}...
      }
      }

      return str
      }

      /**

      • JSON 克隆
      • @param {Object | Json} jsonObj json物件
      • @return {Object | Json} 新的json物件
        */
        export function objClone(jsonObj: any) {
        let buf: any
        if (jsonObj instanceof Array) {
        buf = []
        let i = jsonObj.length
        while (i--) {
        buf[i] = objClone(jsonObj[i])
        }
        return buf
        } else if (jsonObj instanceof Object) {
        buf = {}
        for (let k in jsonObj) {
        buf[k] = objClone(jsonObj[k])
        }
        return buf
        } else {
        return jsonObj
        }
        }
        9.開啟./src/utils/request.ts,先下載axios,並新增以下內容

      // 下載axios
      cnpm i axios --S
      import axios, { AxiosResponse, AxiosRequestConfig } from 'axios'
      import { MAINHOST, ISMOCK, conmomPrams } from '@/config'
      import requestConfig from '@/config/requestConfig'
      import { getToken } from '@/utils/common'
      import router from '@/router'

      declare type Methods = "GET" | "OPTIONS" | "HEAD" | "POST" | "PUT" | "DELETE" | "TRACE" | "CONNECT"
      declare interface Datas {
      method?: Methods
      [key: string]: any
      }
      const baseURL = process.env.NODE_ENV === 'production' ? MAINHOST : location.origin
      const token = getToken()

      class HttpRequest {
      public queue: any // 請求的url集合
      public constructor() {
      this.queue = {}
      }
      destroy(url: string) {
      delete this.queue[url]
      if (!Object.keys(this.queue).length) {
      // hide loading
      }
      }
      interceptors(instance: any, url?: string) {
      // 請求攔截
      instance.interceptors.request.use((config: AxiosRequestConfig) => {
      // 新增全域性的loading...
      if (!Object.keys(this.queue).length) {
      // show loading
      }
      if (url) {
      this.queue[url] = true
      }
      return config
      }, (error: any) => {
      console.error(error)
      })
      // 響應攔截
      instance.interceptors.response.use((res: AxiosResponse) => {
      if (url) {
      this.destroy(url)
      }
      const { data, status } = res
      if (status === 200 && ISMOCK) { return data } // 如果是mock資料,直接返回
      if (status === 200 && data && data.code === 0) { return data } // 請求成功
      return requestFail(res) // 失敗回撥
      }, (error: any) => {
      if (url) {
      this.destroy(url)
      }
      console.error(error)
      })
      }
      async request(options: AxiosRequestConfig) {
      const instance = axios.create()
      await this.interceptors(instance, options.url)
      return instance(options)
      }
      }

      // 請求失敗
      const requestFail = (res: AxiosResponse) => {
      let errStr = '網路繁忙!'
      // token失效重新登陸
      if (res.data.code === 1000001) {
      return router.replace({ name: 'login' })
      }

      return {
      err: console.error({
      code: res.data.errcode || res.data.code,
      msg: res.data.errmsg || errStr
      })
      }
      }

      // 合併axios引數
      const conbineOptions = (_opts: any, data: Datas, method: Methods): AxiosRequestConfig => {
      let opts = _opts
      if (typeof opts === 'string') {
      opts = { url: opts }
      }
      const _data = { ...conmomPrams, ...opts.data, ...data }
      const options = {
      method: opts.method || data.method || method || 'GET',
      url: opts.url,
      header: { 'user-token': token },
      baseURL
      }
      return options.method !== 'GET' ? Object.assign(options, { data: _data }) : Object.assign(options, { params: _data })
      }

      const HTTP = new HttpRequest()

      /**

      • 丟擲整個專案的api方法
        */
        const Api = (() => {
        const apiObj: any = {}
        const requestList: any = requestConfig
        const fun = (opts: AxiosRequestConfig | string) => {
        return async (data = {}, method: Methods = "GET") => {
        if (!token) {
        console.error('No Token')
        return router.replace({ name: 'login' })
        }
        const newOpts = conbineOptions(opts, data, method)
        const res = await HTTP.request(newOpts)
        return res
        }
        }
        Object.keys(requestConfig).forEach((key) => {
        apiObj[key] = fun(requestList[key])
        })

      return apiObj
      })()

      export default Api as any

      10.開啟./src/main.ts,引用common.scss

      import "@/assets/scss/common.scss"
      11.開啟./src/App.vue,按照如下修改(未貼程式碼表示不變)

      12.還有非常重要的一步,開啟根目錄下的vue.config.js,如果沒有就自己建立一個,放在根目錄下,並新增以下內容

      const path = require('path')

      const resolve = dir => {
      return path.join(__dirname, dir)
      }

      // 線上打包路徑,請根據專案實際線上情況
      const BASE_URL = process.env.NODE_ENV === 'production' ? '/' : '/'

      module.exports = {
      baseUrl: BASE_URL,
      outputDir: 'dist', // 打包生成的生產環境構建檔案的目錄
      assetsDir: '', // 放置生成的靜態資源路徑,預設在outputDir
      indexPath: 'index.html', // 指定生成的 index.html 輸入路徑,預設outputDir
      pages: undefined, // 構建多頁
      productionSourceMap: false, // 開啟 生產環境的 source map?
      chainWebpack: config => {
      // 配置路徑別名
      config.resolve.alias
      .set('@', resolve('src'))
      .set('_c', resolve('src/components'))
      },
      css: {
      modules: false, // 啟用 CSS modules
      extract: true, // 是否使用css分離外掛
      sourceMap: false, // 開啟 CSS source maps?
      loaderOptions: {} // css預設器配置項
      },
      devServer: {
      port: 8080, // 埠
      proxy: 'https://www.easy-mock.com' // 設定代理
      }
      }
      13.開啟根目錄下的package.json,在scripts中新增如下程式碼

      "scripts": {
      ...
      "tep": "node scripts/template",
      "com": "node scripts/component"
      }
      四.編寫業務程式碼
      1.編寫page頁
      執行我們之前新增的指令碼命令,建立page,也就是執行之前寫的template.js這個指令碼,實現快速建立我們所需要的page模版,而不需要一個一個的再建立,大大節省了時間,如果不用用指令碼跑也是可以的,分別需要建立以下資料夾:

      在views資料夾下建立login資料夾,再向login資料夾下新增login.vue、login.ts、login.scss
      在./src/api下新增login.ts
      在./src/store/module下新增login.ts
      在./src/types/views下新增login.interface.ts
      是不是非常繁瑣,還可能搞錯(不推薦,浪費時間 0.0),而使用指令碼只需要在命令列敲一條命令搞定(推薦)如下(根據demo需求,我們建立兩個頁面index、login):

      cnpm run tep index
      cnpm run tep login
      開啟./src/views/login/login.ts,發現報錯,沒有安裝模組vuex-class,安裝一下就好了

      cnpm i vuex-class --S
      再執行建立元件指令碼,隨意建立一個test元件

      cnpm run com test
      ok,這時候發現./src/components目錄下建立了test元件,為了引用元件更方便看起來更優雅,我們在./src/components目錄下新增一個index.ts,把所有元件都引入到這裡,作為一箇中轉檔案,如下:

      import Test from './test/test.vue'

      export {
      Test
      }
      引用元件
      上面建立好了元件後,開啟./src/views/login/login.ts,如下引用:

      import { Test } from "@/components" // 元件

      @Component({
      components: {
      Test
      }
      })
      在./src/views/login/login.vue中新增元件

      這時候頁面顯示為如下:

      元件示意圖
      呼叫http請求示例
      最後再說一下怎麼呼叫http請求吧,在這之前,先重啟一遍服務

      cnpm run serve
      按照我的步驟來,啟動是不會報錯的,如果報錯,那麼可以留言看到會回覆,或者重新走一遍。
      沒問題的話,我們按照如下步驟:
      1.開啟./src/config/requestConfig.ts檔案新增介面,由於我們之前的新增過了,那麼我們進行下一步。
      2.開啟./src/api/login.ts檔案新增請求函式,我們之前也寫好了,跳過。
      3.進入./src/store/module/login.ts檔案,把GET_DATA_ASYN函式的註釋開啟。
      4.在./src/store/index.ts中的module新增login,如下:

      // modules
      import Login from './module/login'
      import Index from './module/index'

      export default new Vuex.Store({
      ...
      modules: {
      Login,
      Index
      }
      }
      完成上述動作後就可以在任意頁面呼叫了,我們開啟./src/views/index/index.ts,如下呼叫:

      export default class About extends Vue {
      @Action GET_DATA_ASYN

      created() {
      this.GET_DATA_ASYN()
      }
      }