插件开发

本文共--字 阅读约--分钟 | 浏览: -- Last Updated: 2021-02-26

插件目录结构

官方文档-插件开发指南

CLI 插件是一个可以为 @vue/cli 项目添加额外特性的 npm 包。它应该始终包含:

  • 一个Service插件作为其主要导出
  • 可选的包含一个 Generator 和一个 Prompt 文件

目录:

.
├── README.md
├── generator.js  # generator(可选)
├── generator # 同上 不同的形式而已
│     ├─── index.js 
│     └─── template 
├── index.js      # service 插件
├── package.json
├── prompts.js    # prompt 文件(可选)
└── ui.js         # Vue UI 集成(可选)

Generator

一个发布为 npm 包的 CLI 插件可以包含一个 generator.jsgenerator/index.js 文件。

插件内的 generator 将会在两种场景下被调用:

  • 在一个项目的初始化创建过程中,如果 CLI 插件作为项目创建 preset 的一部分被安装。
  • 插件在项目创建好之后通过 vue invoke 独立调用时被安装。

功能:GeneratorAPI允许一个 generator 向 package.json 注入额外的依赖或字段,并向项目中添加文件。

Service 插件

Service 插件接收两个参数的函数:

  • 一个PluginAPI实例
  • 一个包含 vue.config.js 内指定的项目本地选项的对象,或者在 package.json 内的 vue 字段。

功能:它可以扩展/修改不同环境的内部webpack配置,并为其注入其他命令vue-cli-serve。

该文件是必须的,如果插件不是为了实现这部分功能,可以这样写:

module.exports = () => {}

修改 webpack 配置:

// plugin/index.js
const VueAutoRoutingPlugin = require('vue-auto-routing/lib/webpack-plugin')

module.exports = (api, options) => {
  api.chainWebpack(webpackConfig => {
    webpackConfig
      .plugin('vue-auto-routing')
      .use(VueAutoRoutingPlugin, [
        {
          pages: 'src/pages',
          nested: true
        }
      ])
  })
}

注入其他命令,请参考项目本地的插件

需要注意的是,如果一个已注册的插件命令需要运行在特定的默认模式下,则该插件需要通过 module.exports.defaultModes{ [commandName]: mode }的形式来暴露:

// plugin/index.js
module.exports = api => {
  api.registerCommand('build', () => {
    // ...
  })
}

module.exports.defaultModes = {
  build: 'production'
}

插件的 package.json

// plugin/package.json

{
  "name": "vue-cli-plugin-rx",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "keywords": [
    "vue",
    "vue-cli",
    "rxjs",
    "vue-rx"
  ],
  "author": "",
  "license": "ISC"
}

keywords 指定了在库中搜索时能够被哪些关键字搜索到,所以一般这个会多写一些项目相关的词在这里,是一个字符串的数组。

name 必须以vue-cli-plugin开头。

通过generator添加依赖项

generator可帮助我们添加依赖项并更改项目文件。所以,我们需要的第一步是让我们的插件添加依赖项。

// /plugin/generator/index.js
module.exports = (api, options, rootOptions) => {
  // 注入依赖 api.extendPackage`方法将会修改项目的`package.json
  api.extendPackage({
    dependencies: {
      rxjs: '^6.3.3',
      'vue-rx': '^6.1.0'
    }
  })
}

module.exports.hooks = (api) => {
  // api.entryFile 返回项目的主文件(main.js 或 main.ts)
  const mainPath = api.entryFile
  const rxLine = 'import VueRx from \'vue-rx\';'

  // 如果只是引入依赖可使用 injectImports API
  // injectImports 用于添加新的导入到主文件中
  // api.injectImports(mainPath, rxLine)

  // 在这里 需要引入依赖并使用use 这个钩子将在插件被文件被写入硬盘之后被调用
  api.afterInvoke(() => {
    const { EOL } = require('os')
    const fs = require('fs')
    const contentMain = fs.readFileSync(api.resolve(mainPath), { encoding: 'utf-8' })
    const lines = contentMain.split(/\r?\n/g).reverse() // reverse之后 findIndex 就是找到最后一行的import了

    const lastImportIndex = lines.findIndex(line => line.match(/^import/)) // 找到最后一行import语句
    lines[lastImportIndex] += `${EOL}${rxLine}${EOL}${EOL}Vue.use(VueRx);` // 加入 use 语句

    fs.writeFileSync(mainPath, lines.reverse().join(EOL), { encoding: 'utf-8' })
  })
}

generator 导出一个接收三个参数的函数:

  • GeneratorAPI实例
  • 插件的 generator 选项。这些选项在项目创建,或者从 ~/.vuerc 载入预设时被解析。
  • 如果用户使用某个预设创建项目,整个 preset 将会作为第三个参数传入。

如果有创建项目时使用的 preset (参考 preset ) 如下:

{
  "presets" : {
    "foo": {
      "plugins": {
        "@vue/cli-plugin-foo": { "option": "bar" }
      }
    }
  }
}

那么 @vue/cli-plugin-foo 的 generator 就会收到 { option: 'bar' } 作为第二个参数。

本地测试插件

# 插件指向的绝对路径 例: /Users/xxx/project/hello-pro/plugin
npm install --save-dev file:/full/path/to/your/plugin
vue invoke <your-plugin-name>

通过generator创建模板

.
├── generator
│    ├─── index.js 
│    └─── template 
│      └─── src
│        └─── components
│         └─── RxExample.vue

模板创建完之后,你应该在 generator/index.js 文件中添加 api.render 调用:

// plugin/generator/index.js
module.exports = api => {
  api.render('./template')
}

当你调用api.render('./template')时,generator将会使用 EJS 渲染 ./template 中的文件 (相对于 generator中的文件路径进行解析)。

对话

在插件安装过程中,我们可以向 prompts.js 添加提示代码,以供用户在命令行选择:

// plugin/prompts.js
module.exports = [
  {
    name: 'addExample',
    type: 'confirm',
    message: '是否添加示例组件到项目components目录?',
    default: false,
  },
];

// 或者返回一个函数

// 将 `package.json` 作为参数传入函数
module.exports = pkg => {
  const prompts = [
    {
      name: 'addExample',
      type: 'confirm',
      message: '是否添加示例组件到项目components目录?',
      default: false,
    }
  ]

  // 添加动态对话
  if ('@vue/cli-plugin-eslint' in (pkg.devDependencies || {})) {
    prompts.push({
      type: 'confirm',
      name: 'useESLintPluginVueI18n',
      message: 'Use ESLint plugin for Vue I18n ?'
    })
  }

  return prompts
}

这时我们需要修改下 generator/index.js

module.exports = (api, options) => {
  if (options.addExample) {
    api.render('./template', {
      ...options,
    });
  }
}

发布插件

你必须遵循 vue-cli-plugin-<name> 或者 @scope/vue-cli-plugin-<name> 这样的命名惯例 的命名约定将其发布到 npm 上。插件遵循命名约定之后就可以,这样你的插件才能够:

  • 被 @vue/cli-service 发现;
  • 被其他开发者通过搜索发现;
  • 通过 vue add <name> 或者 vue invoke <name> 安装。

为了能够被用户在搜索时更好的发现,可以将插件的关键描述放到 package.json 文件的 description 字段中。

你应该在 homepage 或者 repository 字段添加创建插件的官网地址或者仓库的地址,这样你的插件详情里就会出现一个 查看详情 按钮:

{
  "repository": {
    "type": "git",
    "url": "git+https://github.com/Akryum/vue-cli-plugin-apollo.git"
  },
  "homepage": "https://github.com/Akryum/vue-cli-plugin-apollo#readme"
}

发布步骤参考 发布npm模块