VueCli構建項目

使用vue-cli3構建Typescript項目

import 和 require

require: 以同步的方式檢索其他模塊的導出 (開發)

import: 動態地加載模塊 (生產)

相關文檔:module methods

vue-cli3

vue create project-name

通過vue-cli3來構建typescript項目,生成目錄結構:

│  .browserslistrc
│  .gitignore
│  .postcssrc.js // postcss 配置
│  babel.config.js
│  cypress.json
│  package.json // 依賴
│  README.md
│  tsconfig.json // ts 配置
│  eslint.json // eslint 配置
│  yarn.lock
│  
├─public // 靜態頁面
│  │  favicon.ico
│  │  index.html
│  │  manifest.json
│  │  robots.txt
│  │  
│  └─img
│      └─icons
│              
├─src // 主目錄
│  │  App.vue // 頁面主入口
│  │  main.ts // 腳本主入口
│  │  registerServiceWorker.ts // PWA 配置
│  │  router.ts // 路由
│  │  shims-tsx.d.ts // 相關 tsx 模塊注入
│  │  shims-vue.d.ts // Vue 模塊注入
│  │  store.ts // vuex 配置
│  │  
│  ├─assets // 靜態資源
│  │      logo.png
│  │      
│  ├─components // 組件
│  │      HelloWorld.vue
│  │      
│  └─views // 頁面
│          About.vue
│          Home.vue
│          
└─tests // 測試用例
    ├─e2e
    │  ├─plugins
    │  │      index.js   
    │  ├─specs
    │  │      test.js   
    │  └─support
    │          commands.js
    │          index.js       
    └─unit
            HelloWorld.spec.ts

改造後的目錄結構:

│  .browserslistrc
│  .gitignore
│  .postcssrc.js // postcss 配置
│  babel.config.js
│  cypress.json
│  package.json // 依賴
│  README.md // 項目 readme
│  tsconfig.json // ts 配置
│  eslint.json // eslint 配置
│  vue.config.js // webpack 配置
│  yarn.lock
│  
├─public // 靜態頁面
│  │  favicon.ico
│  │  index.html
│  │  manifest.json
│  │  robots.txt
│  │  
│  └─img
│      └─icons
├─scripts // 相關腳本配置
├─src // 主目錄
│  │  App.vue // 頁面主入口
│  │  main.ts // 腳本主入口
│  │  registerServiceWorker.ts // PWA 配置
│  │  shims-tsx.d.ts
│  │  shims-vue.d.ts
│  │  
│  ├─assets // 靜態資源
│  │      logo.png
│  │      
│  ├─components
│  │      HelloWorld.vue
│  │      
│  ├─filters // 過濾
│  ├─lib // 全局插件
│  ├─router // 路由配置
│  │      index.ts
│  │      
│  ├─scss // 樣式
│  ├─store // vuex 配置
│  │      index.ts
│  │      
│  ├─typings // 全局注入
│  ├─utils // 工具方法(axios封裝,全局方法等)
│  └─views // 頁面
│          About.vue
│          Home.vue
│          
└─tests // 測試用例
    ├─e2e
    │  ├─plugins
    │  │      index.js
    │  │      
    │  ├─specs
    │  │      test.js
    │  │      
    │  └─support
    │          commands.js
    │          index.js
    │          
    └─unit
            HelloWorld.spec.ts         

vue-cli3配置

eslint 和 tslint

tslint配置

關閉不能cosole:

"no-console": false

函數前後有空格:

"space-before-function-paren": ["error", {
  "anonymous": "always",
  "named": "always",
  "asyncArrow": "always"
}]

tslint的函數前後空格

分號的配置:

"semicolon": [true, "never"]

tslint分號

eslint配置

在項目中是使用eslint

規範空格:'indent': 0

路由改造

引入組件方式

dev使用require:

/**
 * 開發環境載入文件
 * @param fileName 文件路徑,不包括文件名
 * @param viewPath 視圖目錄
 */

module.exports = (file: string, viewPath: string = 'views') => {
  return require(`@/${viewPath}/${file}.vue`).default
}

prod使用import:

/**
 * 生產環境載入文件
 * @param fileName 文件路徑,不包括文件名
 * @param viewPath 視圖目錄
 */

module.exports = (file: string, viewPath: string = 'views') => {
  return import(`@/${viewPath}/${file}.vue`)
}
路由處理邏輯

改文件在app.vue中引入:

/**
 * 路由處理邏輯
 */

import router from '@/router/index'


router.beforeEach((to: any, from: any, next: any) => {
  if (to.name === 'login') {
    next({name: 'home/index'})
  } else {
    next()
  }
})
router-view

一個<router-view />對應一個路由層級,下一級<router-view /> 對應路由表中的children路由

router 中的meta

配置每個路由的單獨特性

title, keepalive, main, desc, icon, hidden, auth

keep-alive

vue中的<keep-alive></keep-alive>其它生命週期不執行,只執行:activateddeactivated

axios改造

npm i axios --save

typings

在根目錄創建typings文件,裏面定義, 全局注入。

需要哪些接口引入哪些接口文件。

創建ajax.d.ts文件,並聲明後臺返回的數據格式。

declare namespace Ajax {
  // axios return data
  export interface AxiosResponse {
    data: AjaxResponse
  }

  // reqposne interface
  export interface AjaxResponse {
    id: number
    error: null | object
    jsonrpc: string
    result: any
  }
}

使用,在src根目錄下都可以使用。

let res: Ajax.AxiosResponse =  {
  data: {"id": "1533610056745", "result": 1, "error": null, "jsonrpc": "2.0"}
}
cookies的處理

安裝cookies的包:npm i js-cookie --save

增加項目前綴,封裝cookie, localStorage, sessionStorage 增刪改等方法

/**
 * 操作 cookie, localStorage, sessionStorage 封裝
 */
import Cookies from 'js-cookie'
import { isNil } from 'lodash'

const prefix = process.env.VUE_APP_PREFIX

/**
 * ============ Cookie ============
 */

export function getCookie (name: string): string {
  return Cookies.get(prefix + name)
}

export function setCookie (name: string, value: any, params= {}): void {
  if (isEmpty(value)) return
  Cookies.set(prefix + name, value, params)
}

export function removeCookie (name: string, params= {}): void {
  Cookies.remove(prefix + name, params)
}

/**
 * ============ localStorage ============
 */

export function setLocalStorage (name: string, value: any): void {
  if (isEmpty(value)) return
  window.localStorage.setItem(prefix + name, value)
}

export function getLocalStorage (name: string) {
  return window.localStorage.getItem(prefix + name)
}

export function removeLocalStorage (name: string) {
  window.localStorage.removeItem(prefix + name)
}

export function clearLocal () {
  window.localStorage.clear()
}

/**
 * ============ sessionStorage ============
 */

export function setSessionStorage (name: string, value: any): void {
  if (isEmpty(value)) return
  window.sessionStorage.setItem(prefix + name, value)
}

export function getSessionStorage (name: string) {
  window.sessionStorage.getItem(prefix + name)
}

export function removeSessionStorage (name: string) {
  window.sessionStorage.removeItem(prefix + name)
}

/**
 * 判斷值是否爲null或者undefined或者''或者'undefined'
 * @param val value
 */
function isEmpty (val: any) {
  if (isNil(val) || val === 'undefined' || val === '') {
    return true
  }
  return false
}
fetch

axios進行二次封裝,增加請求前後的攔截


import axios from 'axios'

/**
 * 創建 axios 實例
 */
const service = axios.create({
  timeout: 3000
})

/**
 * req 攔截器
 */
service.interceptors.request.use((config: object): object => {
  return config
}, (error: any): object => {
  return Promise.reject(error)
})

/**
 * res 攔截器
 */
service.interceptors.response.use((response: any) => {
  const res = response.data
  if (res.error) {
    if (process.env.NODE_ENV !== 'production') {
      console.error(res)
    }
    return Promise.reject(res)
  }
  return Promise.resolve(res)
})

export default service
請求參數統一處理
/**
 * 統一參數處理
 * 請求url處理
 */

const qs = require('qs')
import { merge, isPlainObject } from 'lodash'
import { getCookie } from '@/utils/cookies'

/**
 * 接口參數拼接
 * @param opts 接口參數
 * @param opsIdParams 是否傳遞opsId
 * @param requestType post 還是 get 參數處理
 * @param otherParams 是否傳有其它參數
 * @example
 * commonParams({
 *      'method': cmdName.login,
 *    'params': params
 *  }, false, undefined, false)
 */
export function commonParams (opts: object, opsIdParams: boolean= true, requestType: string= 'post', otherParams: boolean= true): object {
  const params = {
    json: JSON.stringify(merge({
      id: new Date().getTime(),
      jsonrpc: '2.0',
      params: dealParams(opsIdParams, otherParams),
    }, opts || {})),
  }
  return requestType === 'post' ? qs.stringify(params) : params
}

/**
 * 請求接口的地址處理
 * @param urlData 請求接口
 * @param type 請求路徑
 * @example url(cmdName.login)
 */
export function url (urlData: string, type: any = process.env.VUE_APP_API_PATH) {
  // @example  https://example.com + agsgw/api/ + auth.agent.login
  return process.env.VUE_APP_API_URL + type + urlData
}

/**
 * params 參數的處理
 * @param opsIdParams 是否傳遞opsId
 * @param otherParams 是否傳有其它參數
 */
function dealParams (opsIdParams: boolean, otherParams: boolean | object): object {
  let obj: any = {}
  // opsIdParams 默認傳opsId
  if (opsIdParams) {
    obj.opsId = getCookie('token') || ''
  }
  // otherParams其他默認參數, 如sn
  if (otherParams === true) {
    // obj.sn = getCookie('switchSn') || ''
  } else {
    // 其他object
    if (isPlainObject(otherParams)) {
      obj = {...obj, ...otherParams}
    }
  }
  return obj
}
接口名稱單獨作爲一個文件
/**
 * 後臺接口名稱
 */

const cmdName = {
  login: 'auth.agent.login'
}

export default cmdName
組合文件
/**
 * 組合請求http的請求
 */

import fetch from '@/utils/fetch'
import cmdName from './cmdName'
import { commonParams, url } from './commonParams'

export {
  fetch,
  cmdName,
  commonParams,
  url
}
導出的請求文件
import { fetch, cmdName, url, commonParams } from '@/api/common'

export function login (params: object) {
  return fetch({
    url: url(cmdName.login),
    method: 'post',
    data: commonParams({
      method: cmdName.login,
      params
    })
  })
}
使用接口方式
import * as API from '@/api/index'

API.login(params).then(res => {
})

store改造

vuex的作用:分離遠程記錄加載到本地存儲(操作)檢索從store 中的getter

  1. 數據加載策略
  2. 細節構造請求
  3. 導航響應
  • 使用module
  • 全局的一些操作,方法,放入store中,業務邏輯儘量少放,項目全局方法可以放入。例如:cookie, global cache

action(異步): api的操作, 調用方式:this.$store.dispatch(functionName, data)

mutations(同步): dom相關操作,方法名一般使用常量,
調用方式: this.$store.commit(mutationsName, data)

this.$store.getters[XXX] => this.$store.getters[namespaced/XXX]
this.$store.dispatch(XXX, {}) => this.$store.dispatch(namespaced/XXX, {})
this.$store.commit(XXX, {}) => this.$store.commit(namespaced/XXX, {})

組件內的Vue

<template>
  <div>
    <div>用戶名:<input type="text" v-model="username" /></div>
    <div>密碼:<input type="password" v-model="passwd" /></div>

    <div>{{computedMsg}}</div>
  </div>
</template>

<script lang="ts">
import { Component, Prop, Vue, Provide } from 'vue-property-decorator'

// 引入組件
@Component({
  components: {
    // input
  }
})
export default class Login extends Vue {
  // data
  @Provide() private username: string = ''
  @Provide() private passwd: string = ''

  // methods
  login (u: string, p: string) {
  }

  // computed
  get computedMsg () {
    return 'username: ' + this.username
  }

  // life cycle
  mounted () {
  }
}
</script>

other

公用組件: dateRange, pagination, icon-font, clock, proxyAutocomplete

全局注入

Vue.component(modal.name, modal) // dialog
Vue.component(pagination.name, pagination) // 分頁
Vue.component(dateRange.name, dateRange) // 日期
Vue.component(proxyAutocomplete.name, proxyAutocomplete) // 遠程模糊搜索
Vue.component(card.name, card) // el-tabs
Vue.component(tabLoad.name, tabLoad) // el-tabs

main.ts中引入公用組件文件夾下的useElement

import '@/components/useElement'
一些問題

不能直接new

// 'new' expression, whose target lacks a construct signature, implicitly has an 'any' type.
// 不能直接new一個函數,通過重新as一個變量,或者new其原型的constructor 都可以解決
// const encodePsw = new Encode.prototype.constructor().encodePsw(this.passwd)
const E = Encode as any
const encodePsw = new E().encodePsw(this.passwd)

不能直接導入文件後再追加屬性或方法

import * as filters from '@/filters/index'

// 全局filter
const F = filters as any
Object.keys(filters).forEach(item => {
  Vue.filter(item, F[item])
})

declare var Chart: any;

@Component({
  selector: 'my-component',
  templateUrl: './my-component.component.html',
  styleUrls: ['./my-component.component.scss']
})

export class MyComponent {
    //you can use Chart now and compiler wont complain
    private color = Chart.color;
}

vue.config.js

const path = require('path')
const debug = process.env.NODE_ENV !== 'production'
const VueConf = require('./src/assets/js/libs/vue_config_class')
const vueConf = new VueConf(process.argv)

module.exports = {
  baseUrl: vueConf.baseUrl, // 根域上下文目錄
  outputDir: 'dist', // 構建輸出目錄
  assetsDir: 'assets', // 靜態資源目錄 (js, css, img, fonts)
  pages: vueConf.pages,
  lintOnSave: true, // 是否開啓eslint保存檢測,有效值:ture | false | 'error'
  runtimeCompiler: true, // 運行時版本是否需要編譯
  transpileDependencies: [], // 默認babel-loader忽略mode_modules,這裏可增加例外的依賴包名
  productionSourceMap: true, // 是否在構建生產包時生成 sourceMap 文件,false將提高構建速度
  configureWebpack: config => { // webpack配置,值位對象時會合並配置,爲方法時會改寫配置
    if (debug) { // 開發環境配置
      config.devtool = 'cheap-module-eval-source-map'
    } else { // 生產環境配置
    }
    Object.assign(config, { // 開發生產共同配置
      resolve: {
        alias: {
          '@': path.resolve(__dirname, './src'),
          '@c': path.resolve(__dirname, './src/components'),
          'vue$': 'vue/dist/vue.esm.js'
        }
      }
    })
  },
  chainWebpack: config => { // webpack鏈接API,用於生成和修改webapck配置,https://github.com/vuejs/vue-cli/blob/dev/docs/webpack.md
    if (debug) {
      // 本地開發配置
    } else {
      // 生產開發配置
    }
  },
  css: { // 配置高於chainWebpack中關於css loader的配置
    modules: true, // 是否開啓支持‘foo.module.css’樣式
    extract: true, // 是否使用css分離插件 ExtractTextPlugin,採用獨立樣式文件載入,不採用<style>方式內聯至html文件中
    sourceMap: false, // 是否在構建樣式地圖,false將提高構建速度
    loaderOptions: { // css預設器配置項
      css: {
        localIdentName: '[name]-[hash]',
        camelCase: 'only'
      },
      stylus: {}
    }
  },
  parallel: require('os').cpus().length > 1, // 構建時開啓多進程處理babel編譯
  pluginOptions: { // 第三方插件配置
  },
  pwa: { // 單頁插件相關配置 https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-pwa
  },
  devServer: {
    open: true,
    host: '0.0.0.0',
    port: 8080,
    https: false,
    hotOnly: false,
    proxy: {
      '/api': {
        target: '<url>',
        ws: true,
        changOrigin: true
      }
    },
    before: app => {}
  }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章