媽媽:我會寫Vue-CLI插件了😸

Vue Cli 插件開發詳解

原文地址html

1、什麼是 CLI Plugin

引用官方的一段話:vue

CLI 插件是一個能夠爲 @vue/cli 項目添加額外特性的 npm 包。它應該始終包含一個 Service 插件做爲其主要導出,且可選的包含一個 Generator 和一個 Prompt 文件webpack

一般咱們須要將本身的一些配置/公用組件抽出來,以供各個項目使用,在其餘使用cli建立的工程可使用vue add oview的方式自動引入。git

2、Vue-cli 插件目錄結構

一個典型的 CLI 插件的目錄結構看起來是這樣的:github

.
├── README.md
├── generator.js  # generator (可選)
├── prompts.js    # prompt 文件 (可選)
├── index.js      # service 插件
└── package.json
複製代碼

若是你須要在插件安裝的同時,經過命令行來選擇是否建立一些示例組件,那麼目錄能夠改成:web

.
├── README.md
├── generator
|——-——template
│   └── index.js  # generator
├── prompts.js    # 命令行提示安裝
├── index.js      # service 插件
└── package.json


複製代碼

3、 GeneratorAPI

一個發佈爲 npm 包的 CLI 插件能夠包含一個 generator.jsgenerator/index.js 文件。插件內的 generator 將會在兩種場景下被調用:vue-cli

  • 在一個項目的初始化建立過程當中,若是 CLI 插件做爲項目建立 preset 的一部分被安裝。npm

  • 插件在項目建立好以後經過 vue invoke 獨立調用時被安裝。json

(1) Generator 做用

  • package.json 注入額外的依賴或字段,並向項目中添加文件
  • 使用ejs渲染 generator/template 下的文件。

示例:api

module.exports = (api, options, rootOptions) => {
  // 修改 `package.json` 裏的字段
  api.extendPackage({
    scripts: {
      test: "vue-cli-service test"
    }
  });

  // 複製並用 ejs 渲染 `./template` 內全部的文件
  api.render("./template");

  if (options.foo) {
    // 有條件地生成文件
  }
};
複製代碼

(2) GeneratorAPI 參數說明

一個 generator/index 應該導出一個函數,這個函數接收三個參數:

  1. 一個 GeneratorAPI 實例:

  2. 這個插件的 generator 選項。使用prompts.js建立對話的參數,或從一個保存在 ~/.vuerc 中的 preset 中加載。例如,若是保存好的 ~/.vuerc 像以下的這樣:

    {
      "presets": {
        "foo": {
          "plugins": {
            "@vue/cli-plugin-foo": { "option": "bar" }
          }
        }
      }
    }
    複製代碼

    若是用戶使用 preset foo 建立了一個項目,那麼 @vue/cli-plugin-foo 的 generator 就會收到 { option: 'bar' } 做爲第二個參數。

    對於一個第三方插件來講,該選項將會解析自對話或用戶執行 vue invoke 時的命令行參數中 (詳見第三方插件的對話)。

  3. 整個 preset (presets.foo) 將會做爲第三個參數傳入。

(3) Generator 經常使用 API

全部 API 詳情請見這裏,此處列出經常使用的 API

onCreateComplete

  • 入參

    • {function}回調函數
  • Usage: 當插件文件寫入磁盤後的回調函數,經常用來在安裝依賴完成以後,作自定義插件使用操做。例如 在 src/main.js 中寫入import oView from "oview";Vue.use(oView)

api.onCreateComplete(() => {
  let oview = `\nimport oView from 'oview';\n\nVue.use(oView);`;

  const fs = require("fs");
  const mainPath = api.resolve("./src/main.js");
  // 獲取內容
  let contentMain = fs.readFileSync(mainPath, {
    encoding: "utf-8"
  });
  if (contentMain.indexOf("oview") === -1) {
    const lines = contentMain.split(/\r?\n/g).reverse();
    // 注入import
    const lastImportIndex = lines.findIndex(line => line.match(/^import/));
    lines[lastImportIndex] += oview;
    // 修改應用
    contentMain = lines.reverse().join("\n");
    fs.writeFileSync(mainPath, contentMain, {
      encoding: "utf-8"
    });
  }
});
複製代碼

extendPackage

  • 入參

    • {object | () => object}
  • 使用說明: package.json中新增依賴。 除非傳遞{merge:false},不然嵌套字段是深度合併的。 還解決了插件之間的依賴衝突。 在將文件寫入磁盤以前,能夠將工具配置字段提取到獨立文件中。

render

  • 入參

    • {string | object | FileMiddleware} - 能夠是一下三者中的某個
      • 某個文件夾的相對路徑:例如./template;
      • {sourceTemplate:targetFile}映射的對象哈希;
      • 自定義文件中間件函數
    • {object} [additionalData] - 模板可用的其餘數據
    • {object} [ejsOptions] - ejs 額外參數
  • Usage: 使用ejs渲染文件到項目結構中

resolve

  • 入參

    • {string} _path - 相對於工程根目錄目錄
  • 返回值

    • {string}- 文件絕對路徑
  • 使用說明: 獲取一個文件/文件夾的絕對路徑

hasPlugin

判斷是否已經存在某個插件了。

  • 入參

    • {string} id - 插件 id, 能夠省略(@vue/|vue-|@scope/vue)-cli-plugin- 前綴
    • {string} version - 版本範圍, 可選值
  • 返回值

    • {boolean}

cliVersion

使用此插件的@vue/cli版本

cliServiceVersion

使用此插件的@vue/cli-service版本。

4、Service 插件

registerCommand

  • 做用:在 cli 中註冊一個相似於vue-cli-service [name]的命令
  • 入參:
    • 命令名稱(name),
    • 可選參數(opts)
{
  description: string,
  usage: string,
  options: { [string]: string }
}
複製代碼
  • 回調函數(fn)

vue-cli-service serve 的例子

module.exports = (api, options) => {
api.registerCommand('serve', {
    description: 'start development server',
    usage: 'vue-cli-service serve [options] [entry]',
    options: {
      '--open': `open browser on server start`,
      '--copy': `copy url to clipboard on server start`,
      '--mode': `specify env mode (default: development)`,
      '--host': `specify host (default: ${defaults.host})`,
      '--port': `specify port (default: ${defaults.port})`,
      '--https': `use https (default: ${defaults.https})`,
      '--public': `specify the public network URL for the HMR client`,
      '--skip-plugins': `comma-separated list of plugin names to skip for this run`
    }
  }, async function serve (args) {

}
複製代碼

chainWebpack

什麼是 chainWebpack?請見這裏

webpack-chain 官網

  • 做用:經過鏈式的方式修改 webpack 配置
  • 入參:回調函數
//一個例子:修改默認的index.html文件
api.chainWebpack(function(config) {
  config.plugin("html").tap(args => {
    args[0].template = "/Users/username/proj/app/templates/index.html";
    return args;
  });
});
複製代碼

configureWebpack

configureWebpack 修改 webpack 配置有兩種方式。vue 的 configureWebpack 介紹

  • configureWebpack 爲Object類型時,是合併配置到 webpack 中
api.configureWebpack: {
    plugins: [
      new MyAwesomeWebpackPlugin()
    ]
}

複製代碼
  • configureWebpack 爲function類型時,是直接修改 webpack 配置
api.configureWebpack: config => {
    if (process.env.NODE_ENV === 'production') {
      // 爲生產環境修改配置...
    } else {
      // 爲開發環境修改配置...
    }
}
複製代碼

resolveWebpackConfig

獲得修改以後的 webpack 配置,一般咱們在使用api.configureWebpack或者api.chainWebpack以後須要獲得修改以後的 webpack 值,那麼可使用

api.chainWebpack(webpackConfig => {
  //.....
});
//獲得修改以後的webpack值
const webpackConfig = api.resolveWebpackConfig();
複製代碼

resolveChainableWebpackConfig

返回一個鏈式的 chainWebpack 配置,resolveWebpackConfig 是直接返回 webpack 的配置,可是 resolveChainableWebpackConfig 返回的一個 chainWebpack

version

當前使用的 cli @vue/cli-service的版本。

getCwd

當前工做目錄

resolve

  • 入參:相對與根目錄的目錄
  • 返回值: 絕對路徑

hasPlugin

  • 做用:檢查項目是否已經存在某個插件
  • 入參: 插件 id,能夠忽略@vue/|vue-|@scope/vue前綴;
  • 出參: boolean

5、 Prompts 對話

建立新項目或向現有項目添加新插件時,須要提示來處理用戶的選擇。全部提示邏輯都存儲在 prompts.js 文件內.

vue 中的 prompt 是使用的Inquirer;

當用戶經過調用初始化插件時 vue invoke,若是插件prompts.js的根目錄中包含,則會在調用期間使用它。該文件應導出將由 Inquirer.js 處理的一系列問題。

prompt.js 導出能夠有兩種方式:

  • 導出一個問題數組
  • 導出一個處理問題的函數

prompt.js導出的結果將會成爲generator/index.js的第二個參數options

在介紹以上兩種以前,須要簡單的介紹一下Inquirer.js的使用

Inquirer.js

一個問題其實就是一個對象:

{
  type: "input|number|confirm|list|rawlist|expand|checkbox|password|editor".//默認值爲輸入:input
  name: String,//此問題的id,在後面查找此問題的結果時能夠直接使用options.example取到此問題的值
  message:String | Function,//要打印的問題。若是定義爲函數,則第一個參數將是當前查詢者會話答案。缺省值爲name(後跟冒號)
  default:String | Number | Boolean | Array | Function,//若是未輸入任何內容,則使用默認值,或者返回默認值的函數。若是定義爲函數,則第一個參數將是當前查詢者會話答案,
  choices:Array|Function,//(Array | Function)Choices數組或返回choices數組的函數。若是定義爲函數,則第一個參數將是當前查詢者會話答案。數組值能夠是simple numbers,strings或objects包含name(顯示在列表中),value(保存在上面講的name中和short(選擇後顯示)屬性
  validate:Function,//校驗輸入的值是否符合要求
  filter:Function,//接收用戶輸入並返回要在程序內部使用的過濾值,過濾的值將會返回到`Answers `中,詢問用戶是否正確
  when:Boolean|Function,//接收當前用戶的答案哈希,並應返回true或false取決因而否應詢問此問題
  pageSize:Number,//當使用list,rawList,expand,checkbox的時候可能存在分頁
  prefix:String,//更改默認的前綴消息
  suffix:String//更改默認的後綴消息

}
複製代碼

導出一個問題數組

module.exports = [
  {
    type: "input",
    name: "locale",
    message: "The locale of project localization.",
    validate: input => !!input,
    default: "en"
  }
  // ...
];
複製代碼

導出處理問題函數

// 入參爲package.json
module.exports = pkg => {
  const prompts = [
    {
      type: "input",
      name: "locale",
      message: "The locale of project localization.",
      validate: input => !!input,
      default: "en"
    }
  ];

  // 動態添加問題
  if ("@vue/cli-plugin-eslint" in (pkg.devDependencies || {})) {
    prompts.push({
      type: "confirm",
      name: "useESLintPluginVueI18n",
      message: "Use ESLint plugin for Vue I18n ?"
    });
  }

  return prompts;
};
複製代碼

6、一個例子 vue-cli-plugin-oview

此實例作什麼?

使用vue-cli插件安裝移動端圖表庫oview,並新增折線圖樣例。 源碼地址

  1. 初始化 npm
npm init
# 而後輸入vue-cli-plugin-oview
複製代碼
  1. 新建 index.js 因爲不須要註冊命令那麼只須要編寫默認導出便可。

    module.exports = (api, opts) => {};
    複製代碼
  2. 編寫 generator 生成模板

  • 新建 generator 目錄
  • 新建 index.js 文件
  • 新建 template 目錄

文件目錄爲:

├─generator
│ ├─index.js
│ └─template
│   └─src
│     ├─components
│     │ └─Line.vue
│     └─mock
│       └─data.js
├─index.js
├─package.json
├─prompts.js
└─README.md
複製代碼

template 目錄中方的是示例模板,cli 會使用 ejs 進行渲染。

  1. 添加用戶確認 咱們可能須要用戶確認是否須要安裝實例,
// prompts.js
module.exports = [
  {
    name: "example",
    type: "confirm",
    message: "是否添加示例組件到項目components目錄?",
    default: false
  }
];
複製代碼
  1. 添加依賴,main.js 中聲明
// generator/index.js
module.exports = (api, options, rootOptions) => {
  //在package.json中新增依賴
  api.extendPackage({
    dependencies: {
      oview: "^1.1.2"
    }
  });
  // 當文件寫入磁盤時,讀取main.js並在main.js中寫入import oView from "oview"; Vue.use(oView)
  api.onCreateComplete(() => {
    let oview = `\nimport oView from 'oview';\n\nVue.use(oView);`;

    const fs = require("fs");
    const mainPath = api.resolve("./src/main.js");
    // 獲取內容
    let contentMain = fs.readFileSync(mainPath, {
      encoding: "utf-8"
    });
    if (contentMain.indexOf("oview") === -1) {
      const lines = contentMain.split(/\r?\n/g).reverse();
      // 注入import
      const lastImportIndex = lines.findIndex(line => line.match(/^import/));
      lines[lastImportIndex] += oview;
      // 修改應用
      contentMain = lines.reverse().join("\n");
      fs.writeFileSync(mainPath, contentMain, {
        encoding: "utf-8"
      });
    }
  });
  //prompt.js傳遞的參數
  if (options.example) {
    //渲染template下的模板
    api.render("./template", {
      ...options
    });
  }
};
複製代碼
  1. 新建實例項目測試插件
vue create demo
# 如下路徑換成本身的
npm install --save-dev C:\Users\mrgao\Desktop\demo\vue-cli-plugin-oview
# 上面步驟完成,你能夠在package.json中查看到此依賴
vue invoke vue-cli-plugin-oview
# 如今你能夠看到一個命令,提示你是否添加文件,若是一切OK,那麼就能夠在package.json中發現oview的依賴,而且main.js中有oview
複製代碼
  1. 發佈到 npm
1、設置倉庫地址爲npm官方倉庫地址(國內大部分都使用阿里淘寶鏡像,若是沒改publish會失敗)
npm config set registry https://registry.npmjs.org/

2、登錄npm,用戶名密碼郵箱須要所有匹配
npm login
Username: 你的npm用戶名
Password:
Email: (this IS public) 你的郵箱

3、登錄完能夠publish了,執行如下命令
npm publish
輸出如下信息說明發布成功
+ ngx-xxx@0.0.1
這時登陸https://www.npmjs.com/能夠看到本身發佈的項目


複製代碼

給個讚唄!原文地址

示例:oview 插件

  1. vue-cli-plugin-oview

若是以爲此文對您有用歡迎來個Star: mrgaogang.github.io

相關文章
相關標籤/搜索