如何构建一个简单的Vue CLI插件


如果您正在使用Vue框架,那么您可能已经知道Vue CLI是什么。它是快速Vue.js开发的完整系统,提供项目支架和原型设计。

CLI的一个重要部分是cli-plugins。他们可以修改内部webpack配置并将命令注入vue-cli-service。一个很好的例子是@vue/cli-plugin-typescript:当你调用它时,它会在你的项目中添加一个tsconfig.json并更改App.vue以获得类型,因此你不需要手动完成。

插件非常有用,今天有很多用于不同的情况 - GraphQL + Apollo支持,Electron构建器,添加UI库,如VuetifyElementUI ……但是,如果你想为某个特定的库提供一个插件并且它没有’存在吗?嗯,这是我的情况😅……我决定自己建造它。

在本文中,我们将构建一个vue-cli-plugin-rx。它允许我们将vue-rx库添加到我们的项目中,并在我们的Vue应用程序中获得RxJS支持。

🎛️Vue-cli插件结构

首先,什么是CLI插件?它只是一个具有特定结构的npm包。关于文档,它必须有一个服务插件作为其主要导出,并且可以具有其他功能,例如生成器和提示文件。

目前尚不清楚什么是服务插件或生成器,但不用担心 - 稍后会解释!

当然,像任何npm包一样,CLI插件必须在其根文件夹中有一个package.json,并且拥有带有一些描述的README.md会很不错。

那么,让我们从以下结构开始:

.
├── README.md
├── index.js      # service plugin
└── package.json

现在让我们来看看可选部分。生成器可以将其他依赖项或字段注入package.json并将文件添加到项目中。我们需要它吗?当然,我们想要添加rxjs和vue-rx作为我们的依赖项!更多的说,如果用户想在插件安装期间添加它,我们想要创建一些示例组件。因此,我们需要添加generator.js或generator/index.js。我更喜欢第二种方式。现在结构看起来像这样:

.
├── README.md
├── index.js      # service plugin
├── generator
│   └── index.js  # generator
└── package.json

还有一件事是添加一个提示文件:我希望我的插件询问用户是否想要一个示例组件。我们需要根文件夹中的prompts.js文件才能产生这种行为。所以,现在的结构看起来如下:

├── README.md
├── index.js      # service plugin
├── generator
│   └── index.js  # generator
├── prompts.js    # prompts file
└── package.json

⚙️服务插件

服务插件应该导出一个接收两个参数的函数:一个PluginAPI实例和一个包含项目本地选项的对象。它可以扩展/修改不同环境的内部webpack配置,并为vue-cli-service注入其他命令。让我们考虑一下:我们是想以某种方式更改webpack配置还是创建一个额外的npm任务?答案是否定的,我们只想在必要时添加一些依赖项和示例组件。所以我们需要在index.js中更改的是:

module.exports = (api, opts) => {}

如果您的插件需要更改webpack配置,请在官方Vue CLI文档中阅读本节。

🛠️通过生成器添加依赖项

如上所述,CLI插件生成器可帮助我们添加依赖项并更改项目文件。所以,我们需要的第一步是让我们的插件添加两个依赖项:rxjsvue-rx

module.exports = (api, options, rootOptions) => {
  api.extendPackage({
    dependencies: {
      'rxjs': '^6.3.3',
      'vue-rx': '^6.0.1',
    },
  });
}

生成器应该导出一个接收三个参数的函数:GeneratorAPI实例,生成器选项和 - 如果用户使用某个预设创建项目 - 整个预设将作为第三个参数传递。

api.extendPackage方法扩展了项目的package.json。嵌套字段是深度合并的,除非您将{merge:false}作为参数传递。在我们的例子中,我们将两个依赖项添加到dependencies部分。

现在我们需要更改main.js文件。为了使RxJS在Vue组件内工作,我们需要导入VueRx并调用Vue.use(VueRx)

首先,让我们创建一个我们想要添加到主文件的字符串:

let rxLines = `\nimport VueRx from 'vue-rx';\n\nVue.use(VueRx);`;

现在我们将使用api.onCreateComplete钩子。将文件写入磁盘时调用它。

api.onCreateComplete(() => {
    // inject to main.js
    const fs = require('fs');
    const ext = api.hasPlugin('typescript') ? 'ts' : 'js';
    const mainPath = api.resolve(`./src/main.${ext}`);
};

这里我们正在寻找主文件:如果它是一个TypeScript项目,它将是一个main.ts,否则它将是一个main.js文件。这里的fs是一个文件系统。

现在让我们改变文件内容

api.onCreateComplete(() => {
    // inject to main.js
    const fs = require('fs');
    const ext = api.hasPlugin('typescript') ? 'ts' : 'js';
    const mainPath = api.resolve(`./src/main.${ext}`);
    // get content
    let contentMain = fs.readFileSync(mainPath, { encoding: 'utf-8' });
    const lines = contentMain.split(/\r?\n/g).reverse();
    // inject import
    const lastImportIndex = lines.findIndex(line => line.match(/^import/));
    lines[lastImportIndex] += rxLines;
    // modify app
    contentMain = lines.reverse().join('\n');
    fs.writeFileSync(mainPath, contentMain, { encoding: 'utf-8' });
  });
};

这里发生了什么?我们正在阅读主文件的内容,将其分成几行并恢复其顺序。然后,我们使用import语句搜索第一行(它将是第二次还原后的最后一行)并在那里添加我们的rxLines。在此之后,我们反转行数组并保存文件。

💻在本地测试cli-plugin

让我们在package.json中添加一些关于插件的信息,并尝试在本地安装它来测试。最重要的信息通常是插件名称和版本(将插件发布到npm时需要这些字段),但随时添加更多信息!可以在此处找到package.json 文件的完整列表。以下是我的doc:

{
  "name": "vue-cli-plugin-rx",
  "version": "0.1.5",
  "description": "Vue-cli 3 plugin for adding RxJS support to project using vue-rx",
  "main": "index.js",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/NataliaTepluhina/vue-cli-plugin-rx.git"
  },
  "keywords": [
    "vue",
    "vue-cli",
    "rxjs",
    "vue-rx"
  ],
  "author": "Natalia Tepluhina <my@mail.com>",
  "license": "MIT",
  "homepage": "https://github.com/NataliaTepluhina/vue-cli-plugin-rx#readme"
}

现在是时候检查我们的插件是如何工作的!为此,让我们创建一个简单的vue-cli驱动项目:

vue create test-app

转到项目文件夹并安装我们新创建的插件:

cd test-app
npm install --save-dev file:/full/path/to/your/plugin

安装插件后,您需要调用它:

vue invoke vue-cli-plugin-rx

现在,如果您尝试检查main.js文件,您可以看到它已更改:

import Vue from 'vue'
import App from './App.vue'
import VueRx from 'vue-rx';
Vue.use(VueRx);

此外,您可以在测试应用程序包package.jsondevDependencies部分找到您的插件。

📂使用生成器创建新组件

太棒了,插件有效!现在是时候扩展它的功能并使它能够创建一个示例组件。Generator API为此目的使用render方法。

首先,让我们创建这个示例组件。它应该是位于项目src/components文件夹中的.vue文件。在生成器中创建一个模板文件夹,然后模仿它内部的整个结构:

图1

你的示例组件应该……好吧,只是一个Vue单文件组件!我不会深入研究本文中的RxJS解释,但我创建了一个简单的RxJS驱动的点击计数器,带有2个按钮:

图2

它的源代码可以在这里找到。

现在我们需要指示我们的插件在调用时呈现此组件。为此,我们将此代码添加到generator/index.js

api.render('./template', {
  ...options,
});

这将呈现整个模板文件夹。现在,当调用插件时,新的RxExample.vue将添加到src/components文件夹中。

我决定不覆盖App.vue,让用户自己添加一个示例组件。但是,您可以替换现有文件的一部分,请参阅文档中的示例

⌨️通过提示处理用户选择

如果用户不想拥有示例组件,该怎么办?在插件安装过程中让用户决定这一点是不是明智之举?这就是提示存在的原因!

以前我们在插件根文件夹中创建了prompts.js文件。该文件应包含一系列问题:每个问题都是一个具有某些字段集的对象,如名称,消息,选择,类型等。

名称很重要:我们稍后会在生成器中使用它来创建渲染示例组件的条件!

提示可以有不同的类型,但CLI中使用最广泛的是复选框确认。我们将使用后者创建一个带有是/否答案的问题。

那么,让我们将我们的提示代码添加到prompts.js

module.exports = [
  {
    name: `addExample`,
    type: 'confirm',
    message: 'Add example component to components folder?',
    default: false,
  },
];

我们有一个addExample提示符,询问用户是否要将组件添加到components文件夹。默认答案是否

让我们回到生成器文件并做一些修复替换api.render

if (options.addExample) {
    api.render('./template', {
      ...options,
    });
}

我们正在检查addExample是否有正答案,如果是,则将呈现该组件。

每次更改后都不要忘记重新安装并测试插件!

📦公开!

重要说明:在发布插件之前,请检查其名称是否与模式vue-cli-plugin-<YOUR-PLUGIN-NAME>相匹配。这允许您的插件可以通过@vue/cli-service发现并通过vue add安装。

我还希望我的插件在Vue CLI UI中有一个漂亮的外观,所以我在package.json中添加了描述,标签和存储库名称并创建了一个徽标。徽标图片应命名为logo.png并放在插件根文件夹中。因此,我的插件在UI插件列表中看起来像这样:

图3

现在我们准备发布了。你需要注册一个npmjs.com,显然你应该安装npm。

去发布插件,转到插件根文件夹并在终端中键入npm publish,你刚刚发布了一个npm包!

此时您应该能够使用vue addcommand安装插件。试试吧!

当然,本文中描述的插件非常基础,但我希望我的指示可以帮助某人开始使用cli-plugins开发。

你缺少什么样的CLI插件?请在评论中分享您的想法。


文章作者: 左智文
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 左智文 !
评论
  目录