深度定製團隊本身的 Vue template

衆所周知,使用 vue-cli 能夠快速初始化一個基於 Vue.js 的項目,官方提供了 webpackpwabrowserify-simple經常使用 templateshtml

當開發一個獨立項目的時候,使用官方提供的 template 確實很是方便,省去了繁瑣的依賴安裝配置、webpack 配置,甚至連項目結構也不用多加考慮。vue

可是,當咱們須要開發多個系統,每一個系統相對獨立但又有一些配置、依賴或邏輯相互通用的時候(例如集羣的多後臺系統),每次使用官方提供的 template 初始化項目以後,都須要進一步調整(添加依賴、修改配置、增長通用組件等等),這顯然是十分麻煩的。
本着懶惰是第一輩子產力的初衷,咱們須要定製一份本身的 template,以便咱們...額...偷懶哈~
hehenode

在開始定製咱們本身的 Vue template 前,咱們須要瞭解一些前置知識:webpack

前置知識

  1. 模板結構
      首先咱們先來了解模板的主要結構,模板結構很簡單,主要包括兩個部分:git

    • template 該目錄用於存放模板文件,初始化項目生成的文件來自於此。
    • meta.js / meta.json 用於描述初始化項目時命令行的交互動做。
  2. Metalsmith
      Metalsmith 在渲染項目文件流程中角色至關於 gulp.js,能夠經過添加一些插件對構建文件進行處理,如重命名、合併等。
  3. download-git-repo
      使用 vue-cli 初始化項目時會使用該工具來下載目標倉庫。默認的 webpack 等模板直接下載 vue-templates 中對應的模板倉庫。
      自定義的模板也能夠是一個 GitHub 倉庫,使用以下命令來初始化項目:github

    vue init username/repo my-project
    其中 username 爲自定義模板倉庫所在的 GitHub 用戶或組織名, repo 爲倉庫名。
  4. Inquirer.js
      vue-cli 在模板倉庫下載完成後,將經過 Inquirer.js 根據模板倉庫中的 meta.jsmeta.json 文件中的設置,與用戶進行一些簡單的交互以肯定項目的一些細節,以下圖:
    cli-perviewweb

    該交互配置是可選的,當項目中 沒有 meta.jsmeta.json 文件時,模板倉庫下載完成後將直接進入模板構建階段。
  5. Handlebars.js
      在經過命令行交互肯定了項目初始化的細節後,就該進入最後一道工序,按照模板初始化咱們的項目啦!\(≧▽≦)/
      這裏 vue-cli 選用的是 Handlebars.js —— 一個簡單高效的語義化模板構建引擎。

  畫了一張圖,更有助於理清這些依賴在 vue-cli 初始化項目時的相互關聯:
vue-cli-processvue-cli

定製模板主要圍繞着 命令行交互Inquirer.js)與 模板文件開發Handlebars.js)這兩部分。

meta.js 配置文件(Inquirer.js)

  因爲 meta.js 至關於模板項目的配置文件(雖然非必選),因此這裏先看看它主要能幹些啥。
  設置都在 meta.jsmeta.json 中配置,推薦使用 meta.js,更靈活一些。如下也將以 meta.js 進行展開說明。
  meta.js 一共可包含以下幾個字段,簡單列一下各字段功能:npm

  • helpers : 自定義 Handlebars.js 的輔助函數
  • prompts : 基於 Inquirer.js 的命令行交互配置
  • filters : 根據命令行交互的結果過濾將要渲染的項目文件
  • metalsmith : 配置 Metalsmith 插件,文件會像 gulp.js 中的 pipe 同樣依次通過各個插件處理
  • completeMessage : 將模板渲染爲項目後,輸出一些提示信息,取值爲字符串
  • complete : 與 completeMessage 功能相同,二選其一,取值爲函數,函數最後需返回輸出的字符串

命令行交互(Inquirer.js)

  命令行交互主要是 meta.jsprompts 字段的配置,詳細的配置能夠閱讀 Inquirer.jsREADME.md,這裏說一下經常使用的交互配置:json

// meta.js
module.export = {
  // ...
  "prompts": {
    "isCustomName": {
      "type"   : "confirm",
      "message": "是否自定義系統名稱?",
    },
    "sysName": {
      "type"    : "input",
      "when"    : "isCustomName",
      "default" : "默認系統名稱",
      "message" : "請輸入系統名稱:",
      "required": true,
      "validate": function (val) {
        if (!val) return '(✘) 請輸入系統名稱,該名稱將設爲 index.html 的 title';
        return true;
      },
    },
    // ...
  },
}

字段說明:

  • isCustomNamesysName : 交互字段名稱,可在後續條件交互或模板渲染時經過該字段讀取到交互結果
  • type : 交互類型,有 input, confirm, list, rawlist, expand, checkbox, password, editor 八種類型
  • message : 交互的提示信息
  • when : 進行該條件交互的先決條件,在該例子中,sysName 這個交互動做只在 isCustomName 交互結果爲真時纔會出現
  • default : 默認值,當用戶輸入爲空時,交互結果即爲此值
  • required : 默認爲 false,該值是否爲必填項
  • validate : 輸入驗證函數
注:示例中 default required validate 三個字段存在邏輯問題,僅爲舉例方便放到一塊兒。

模板基本語法(Handlebars.js)

  在模板編寫中,咱們能夠用 Mustache 語法在任何文本類型的文件中輸出在命令行交互中獲得的一些數據:

// dev.js
export default {
  //...
  token: '{{token}}',
  //...
};
<!-- index.html -->
<!DOCTYPE html>
<html>
  <head>
    <title>{{sysName}}</title>
  </head>
  <!-- ... -->
</html>

  以 {%raw%}{{xxx}}{%endraw%} 即爲一個 Mustache 句法標記。以上例子中 tokensysName 爲匹配命令行交互數據對應的鍵名。

vue 有過了解的都知道,在模板標籤中直接輸出實例上的數據也是用的 Mustache 語法。
若是定製 vue template 模板時不對這些數據作相應處理,在最終輸出由模板初始化的項目時,這些與命令行交互獲得的數據沒法匹配的 Mustache 句法標記會被移除。
此時咱們須要使用反斜槓 \{%raw%}{{xxx}}{%endraw%} 或者 {%raw%}{{{xxx}}}{%endraw%} 來跳過 Handlebars 的處理,直接輸出 {%raw%}{{xxx}}{%endraw%}

模板渲染時的輔助函數(Handlebars.js)

  vue-cli 中爲 Handlebars 預置了 if_equnless_eq 輔助函數,用於使用交互所得數據來處理模板中是否渲染的兩種邏輯關係,此外 Handlebars 中還內置了 ifunlesseach輔助函數

// sys.js
export default {
  {{#if_eq projType 'admin'}}
  id: {{#if_eq sysId ''}}undefined{{else}}{{sysId}}{{/if_eq}},
  {{/if_eq}}
  name: '{{sysName}}',
};

  如上,這裏用了 if_eq 輔助函數,projType 表明將要匹配的鍵,'admin' 表明將要匹配的值。這個鍵值來自於在命令行界面與用戶交互的操做結果。該栗子中,當命令行交互數據中 CLI[projType] == 'admin' 時,將在 sys.js 文件的導出數據中輸出 id 字段;id 的值來自一個嵌套的 if_eq 輔助函數,當 CLI[sysId] == '' 時,id 將被設置爲 undefined 不然 ({%raw%}{{else}}{%endraw%})輸出 CLI[sysId] 命令行交互所得數據中的 sysId

輔助函數使用語法: {%raw%}{{{%endraw%}# + 函數名 + ' '(空格)+ 以空格分隔的參數列表 + }}
以空格分隔的參數列表:未用引號包裹的參數名將被將爲自動取值爲命令行交互結果中對應的數據

自定義輔助函數(Handlebars.js)

  有時候現有的輔助函數可能不能知足咱們的需求,經過 mate.js 中的 helpers 字段咱們能夠自定義輔助函數:

// mate.js
module.exports = {
  "helpers": {
    "neither": function (k, v1, v2, options) {
      if (k !== v1 && k !== v2) {
        return options.fn(this);
      }
      return options.inverse(this);
    },
  },
  //...
}

  輔助函數能夠接受若干的參數,最後一個參數 options 爲輔助函數的鉤子,調用 options.fn(this) 即輸出該輔助函數運算結果爲真時的內容,反之調用 options.inverse(this) 則輸出 {%raw%}{{else}}{%endraw%} 的內容(若是有的話)。
  如今咱們能夠在模板中直接使用 neither 輔助函數了:

{{#neigher sysType 'admin' 'mobile'}}
isAdmin  = false
isMobile = false
{{else}}
isAdminOrMobile = true
{{/neigher}}

按條件過濾渲染文件

  輔助函數只能夠控制文件內一部份內容的輸出與否,有時候咱們須要根據交互結果控制某些文件自己是否輸出。
  在 mate.js 中的 filters 字段中進行相應的設置,就能夠達到控制文件輸出的效果:

module.exports = {
  //...
  "filters": {
    "project/config/test.env.js": "unit || e2e",
    "project/src/router/**/*": "router"
  },
  //...
}

  filters鍵名是要控制輸出的文件的路徑,可以使用字面量,也可以使用 簡化的 glob 表達式。鍵名對應的值爲命令行交互中獲得的數據。

渲染時文件的操做

  在模板項目比較複雜或是有特殊需求的時候,好比:

  • 按照條件不一樣須要渲染兩個文件名相同但內容徹底不一樣的文件
  • 模板模塊化,多個模板文件拼接渲染爲一個項目文件
  • 使用 GZip 壓縮一些非源碼資源

  能夠經過 mate.js 中的 metalsmith 字段配置相關插件來實現豐富的文件操做:

var renamer = require('metalsmith-renamer')
module.exports = {
  //...
  "metalsmith": function(metalsmith, opts, helpers) {
    metalsmith.use(renamer({
      index: {
        pattern: 'project/**/+(Mobile|Admin)Index.vue',
        rename: function(fileName) {
          return 'Index.vue';
        }
      },
      config: {
        pattern: 'project/src/+(mobile|admin)Config.js',
        rename: 'config.js'
      },
      //...
    }))
  },
  //...
}

  以上是 metalsmith-renamer 插件的簡單使用,更多插件能夠在這裏查找

使用 metalsmith 插件請注意:因爲 vue-cli 在下載完成模板倉庫後並無 npm install 安裝模板的項目依賴這一操做,因此在打包模板倉庫的時候也須要將依賴目錄 node_modules 一同打包, metalsmith 的插件都很精簡,通常不會有什麼嵌套依賴。不過仍是建議在使用前查看一下插件的相關 Github 倉庫。

  關於 vue 項目模板的開發涉及到的問題差很少就介紹完了,爲本身或團隊開發一份專屬的 Vue Template 吧!

原文: 深度定製團隊本身的 Vue template
相關文章
相關標籤/搜索