系统里有两个主要的部分:
两者皆应用了基于插件的架构。
Creator 是调用 vue create <app> 时创建的类。负责偏好对话、调用 generator 和安装依赖。
Service 是调用 vue-cli-service <command> [...args] 时创建的类。负责管理内部的 webpack 配置、暴露服务和构建项目的命令等。
CLI 插件是一个可以为 @vue/cli 项目添加额外特性的 npm 包。它应该始终包含一个 Service 插件作为其主要导出,且可选的包含一个 Generator 和一个 Prompt 文件。
一个典型的 CLI 插件的目录结构看起来是这样的:
.
├── README.md
├── generator.js # generator (可选)
├── prompts.js # prompt 文件 (可选)
├── index.js # service 插件
└── package.json
Service 插件会在一个 Service 实例被创建时自动加载——比如每次 vue-cli-service 命令在项目中被调用时。
注意我们这里讨论的“service 插件”的概念要比发布为一个 npm 包的“CLI 插件”的要更窄。前者涉及一个会被 @vue/cli-service 在初始化时加载的模块,也经常是后者的一部分。
此外,@vue/cli-service 的内建命令和配置模块也是全部以 service 插件实现的。
一个 service 插件应该导出一个函数,这个函数接受两个参数:
这个 API 允许 service 插件针对不同的环境扩展/修改内部的 webpack 配置,并向 vue-cli-service 注入额外的命令。例如:
module.exports = (api, projectOptions) => {
api.chainWebpack(webpackConfig => {
// 通过 webpack-chain 修改 webpack 配置
})
api.configureWebpack(webpackConfig => {
// 修改 webpack 配置
// 或返回通过 webpack-merge 合并的配置对象
})
api.registerCommand("test", args => {
// 注册 `vue-cli-service test`
})
}
注意:插件设置模式的方式从 beta.10 开始已经改变了。
如果一个已注册的插件命令需要运行在特定的默认模式下,则该插件需要通过 module.exports.defaultModes 以 { [commandName]: mode } 的形式来暴露:
module.exports = api => {
api.registerCommand("build", () => {
// ...
})
}
module.exports.defaultModes = {
build: "production"
}
这是因为我们需要在加载环境变量之前知道该命令的预期模式,所以需要提前加载用户选项/应用插件。
一个插件可以通过调用 api.resolveWebpackConfig() 取回解析好的 webpack 配置。每次调用都会新生成一个 webpack 配置用来在需要时进一步修改。
module.exports = api => {
api.registerCommand("my-build", args => {
const configA = api.resolveWebpackConfig()
const configB = api.resolveWebpackConfig()
// 针对不同的目的修改 `configA` 和 `configB`...
})
}
// 请确保为正确的环境变量指定默认模式
module.exports.defaultModes = {
"my-build": "production"
}
或者,一个插件也可以通过调用 api.resolveChainableWebpackConfig() 获得一个新生成的链式配置:
api.registerCommand("my-build", args => {
const configA = api.resolveChainableWebpackConfig()
const configB = api.resolveChainableWebpackConfig()
// 针对不同的目的链式修改 `configA` 和 `configB`...
const finalConfigA = configA.toConfig()
const finalConfigB = configB.toConfig()
})
vue.config.js 的导出将会通过一个 schema 的验证以避免笔误和错误的配置值。然而,一个第三方插件仍然允许用户通过 pluginOptions 字段配置其行为。例如,对于下面的 vue.config.js:
module.exports = {
pluginOptions: {
foo: { }
}
}
该第三方插件可以读取 projectOptions.pluginOptions.foo 来做条件式的决定配置。
一个发布为 npm 包的 CLI 插件可以包含一个 generator.js 或 generator/index.js 文件。插件内的 generator 将会在两种场景下被调用:
这里的 GeneratorAPI 允许一个 generator 向 package.json 注入额外的依赖或字段,并向项目中添加文件。
一个 generator 应该导出一个函数,这个函数接收三个参数:
示例:
module.exports = (api, options, rootOptions) => {
// 修改 `package.json` 里的字段
api.extendPackage({
scripts: {
test: "vue-cli-service test"
}
})
// 复制并用 ejs 渲染 `./template` 内所有的文件
api.render("./template")
if (options.foo) {
// 有条件地生成文件
}
}
当你调用 api.render("./template") 时,该 generator 将会使用 EJS 渲染 ./template 中的文件 (相对于 generator 中的文件路径进行解析)
此外,你可以使用 YAML 前置元信息继承并替换已有的模板文件的一部分:
---
extend: "@vue/cli-service/generator/template/src/App.vue"
replace: !!js/regexp /<script>[^]*?</script>/
---
<script>
export default {
// 替换默认脚本
}
</script>
你也可以完成多处替换,当然你需要将要替换的字符串用 <%# REPLACE %> 和 <%# END_REPLACE %> 块包裹起来:
---
extend: "@vue/cli-service/generator/template/src/App.vue"
replace:
- !!js/regexp /欢迎来到你的 Vue.js 应用/
- !!js/regexp /<script>[^]*?</script>/
---
<%# REPLACE %>
替换欢迎信息
<%# END_REPLACE %>
<%# REPLACE %>
<script>
export default {
// 替换默认脚本
}
</script>
<%# END_REPLACE %>
如果你想要渲染一个以点开头的模板文件 (例如 .env),则需要遵循一个特殊的命名约定,因为以点开头的文件会在插件发布到 npm 的时候被忽略:
# 以点开头的模板需要使用下划线取代那个点:
/generator/template/_env
# 调用 api.render("./template") 会在项目目录中渲染成为:
.env
同时这也意味着当你想渲染以下划线开头的文件时,同样需要遵循一个特殊的命名约定:
# 这种模板需要使用两个下划线来取代单个下划线:
/generator/template/__variables.scss
# 调用 api.render("./template") 会在项目目录中渲染成为:
_variables.scss
只有内建插件可以定制创建新项目时的初始化对话,且这些对话模块放置在 @vue/cli 包的内部。
一个对话模块应该导出一个函数,这个函数接收一个 PromptModuleAPI 实例。这些对话的底层使用 inquirer 进行展示:
module.exports = api => {
// 一个特性对象应该是一个有效的 inquirer 选择对象
api.injectFeature({
name: "Some great feature",
value: "my-feature"
})
// injectPrompt 期望接收一个有效的 inquirer 对话对象
api.injectPrompt({
name: "someFlag",
// 确认对话只在用户已经选取了特性的时候展示
when: answers => answers.features.include("my-feature"),
message: "Do you want to turn on flag foo?",
type: "confirm"
})
// 当所有的对话都完成之后,将你的插件注入到
// 即将传递给 Generator 的 options 中
api.onPromptComplete((answers, options) => {
if (answers.features.includes("my-feature")) {
options.plugins["vue-cli-plugin-my-feature"] = {
someFlag: answers.someFlag
}
}
})
}
第三方插件通常会在一个项目创建完毕后被手动安装,且用户将会通过调用 vue invoke 来初始化这个插件。如果这个插件在其根目录包含一个 prompts.js,那么它将会用在该插件被初始化调用的时候。这个文件应该导出一个用于 Inquirer.js 的问题的数组。这些被解析的答案对象会作为选项被传递给插件的 generator。
或者,用户可以通过在命令行传递选项来跳过对话直接初始化插件,比如:
vue invoke my-plugin --mode awesome
为了让一个 CLI 插件能够被其它开发者使用,你必须遵循 vue-cli-plugin-<name> 的命名约定将其发布到 npm 上。插件遵循命名约定之后就可以:
注意
这个章节只用于 vuejs/vue-cli 仓库内部的内建插件工作。
一个带有为本仓库注入额外依赖的 generator 的插件 (比如 chai 会通过 @vue/cli-plugin-unit-mocha/generator/index.js 被注入) 应该将这些依赖列入其自身的 devDependencies 字段。这会确保:
自定义指令简介除了默认设置的核心指令(v-model和v-show),Vue 也允许注册自定义指令。注意,在 Vue2.0 里面,代码复用的主要形式...
需要服务端渲染(SSR)吗?在开始服务端渲染前,我们先看看它能给我们带来什么,以及什么时候需要用它。SEO(搜索引擎优化)谷歌...
监听事件可以用v-on指令监听 DOM 事件来触发一些 JavaScript 代码。示例:div id="example-1"button v-on:click="counter += 1"...
稳定性: 2 - 不稳定流用于处理Node.js中的流数据的抽象接口,在Node里被不同的对象实现。例如,对HTTP服务器的请求是流,process...
稳定性: 2 - 不稳定Node.js域包含了能把不同的IO操作看成单独组的方法。如果任何一个注册到域的事件或者回调触发error事件,或者...
MongoDB是一种文档导向数据库管理系统,由C++撰写而成。本章节我们将为大家介绍如何使用 Node.js 来连接 MongoDB,并对数据库进...
关于术语的一点说明:请务必注意一点,TypeScript 1.5里术语名已经发生了变化。 “内部模块”现在称做“命名空间”。 “外部模块...
简介这篇指南的目的是教你如何书写高质量的TypeScript声明文件。 我们在这里会展示一些API的文档,还有它们的使用示例, 并且阐...