警告
这份文档是对应 @vue/cli。老版本的 vue-cli 文档请移步这里。
Vue CLI 是一个基于 Vue.js 进行快速开发的完整系统,提供:
Vue CLI 致力于将 Vue 生态中的工具基础标准化。它确保了各种构建工具能够基于智能的默认配置即可平稳衔接,这样你可以专注在撰写应用上,而不必花好几天去纠结配置的问题。与此同时,它也为每个工具提供了调整配置的灵活性,无需 eject。
Vue CLI 有几个独立的部分——如果你看到了我们的源代码,你会发现这个仓库里同时管理了多个单独发布的包。
CLI (@vue/cli) 是一个全局安装的 npm 包,提供了终端里的 vue 命令。它可以通过 vue create 快速创建一个新项目的脚手架,或者直接通过 vue serve 构建新想法的原型。你也可以通过 vue ui 通过一套图形化界面管理你的所有项目。我们会在接下来的指南中逐章节深入介绍。
CLI 服务 (@vue/cli-service) 是一个开发环境依赖。它是一个 npm 包,局部安装在每个 @vue/cli 创建的项目中。
CLI 服务是构建于 webpack 和 webpack-dev-server 之上的。它包含了:
如果你熟悉 create-react-app 的话,@vue/cli-service 实际上大致等价于 react-scripts,尽管功能集合不一样。
CLI 服务章节涵盖了它的具体用法。
CLI 插件是向你的 Vue 项目提供可选功能的 npm 包,例如 Babel/TypeScript 转译、ESLint 集成、单元测试和 end-to-end 测试等。Vue CLI 插件的名字以 @vue/cli-plugin- (内建插件) 或 vue-cli-plugin- (社区插件) 开头,非常容易使用。
当你在项目内部运行 vue-cli-service 命令时,它会自动解析并加载 package.json 中列出的所有 CLI 插件。
插件可以作为项目创建过程的一部分,或在后期加入到项目中。它们也可以被归成一组可复用的 preset。我们会在插件和 preset 章节进行深入讨论。
关于旧版本
如果您已经安装了旧版本的vue-cli(1.x或2.x),您需要先通过npm卸载vue-cli -g或yarn global remove vue-cli卸载它。
节点版本要求
Vue CLI需要 Node.js 8.9或更高版本(推荐8.11.0+)。您可以使用 nvm 或 nvm-windows 在同一台计算机中管理多个Node版本。
可以使用以下任一命令安装这个新的包:
npm install -g @vue/cli# ORyarn global add @vue/cli
安装之后,您就可以在命令行中访问vue命令。您可以通过简单运行vue,看看是否展示出了一份所有可用命令的帮助信息,来验证它是否安装成功。
你还可以用这个命令来检查其版本是否正确:
vue --version
你可以使用 vue serve 和 vue build 命令对单个 *.vue 文件进行快速原型开发,不过这需要先额外安装一个全局的扩展:
npm install -g @vue/cli-service-global
vue serve 的缺点就是它需要安装全局依赖,这使得它在不同机器上的一致性不能得到保证。因此这只适用于快速原型开发。
Usage: serve [options] [entry]在开发环境模式下零配置为 .js 或 .vue 文件启动一个服务器Options: -o, --open 打开浏览器 -c, --copy 将本地 URL 复制到剪切板 -h, --help 输出用法信息
你所需要的仅仅是一个 App.vue 文件:
<template> <h1>Hello!</h1></template>
然后在这个 App.vue 文件所在的目录下运行:
vue serve
vue serve 使用了和 vue create 创建的项目相同的默认设置 (webpack、Babel、PostCSS 和 ESLint)。它会在当前目录自动推导入口文件——入口可以是 main.js、index.js、App.vue 或 app.vue 中的一个。你也可以显式地指定入口文件:
vue serve MyComponent.vue
如果需要,你还可以提供一个 index.html、package.json、安装并使用本地依赖、甚至通过相应的配置文件配置 Babel、PostCSS 和 ESLint。
Usage: build [options] [entry]在生产环境模式下零配置构建一个 .js 或 .vue 文件Options: -t, --target <target> 构建目标 (app | lib | wc | wc-async, 默认值:app) -n, --name <name> 库的名字或 Web Components 组件的名字 (默认值:入口文件名) -d, --dest <dir> 输出目录 (默认值:dist) -h, --help 输出用法信息
你也可以使用 vue build 将目标文件构建成一个生产环境的包并用来部署:
vue build MyComponent.vue
vue build 也提供了将组件构建成为一个库或一个 Web Components 组件的能力。查阅构建目标了解更多。
运行以下命令来创建一个新项目:
vue create hello-world
警告
如果你在 Windows 上通过 minTTY 使用 Git Bash,交互提示符并不工作。你必须通过 winpty vue.cmd create hello-world 启动这个命令。不过,如果你仍想使用 vue create hello-world,则可以通过在 ~/.bashrc 文件中添加以下行来为命令添加别名。 alias vue='winpty vue.cmd' 你需要重新启动 Git Bash 终端会话以使更新后的 bashrc 文件生效。
你会被提示选取一个 preset。你可以选默认的包含了基本的 Babel + ESLint 设置的 preset,也可以选“手动选择特性”来选取需要的特性。
这个默认的设置非常适合快速创建一个新项目的原型,而手动设置则提供了更多的选项,它们是面向生产的项目更加需要的。
如果你决定手动选择特性,在操作提示的最后你可以选择将已选项保存为一个将来可复用的 preset。我们会在下一个章节讨论 preset 和插件。
~/.vuerc
被保存的 preset 将会存在用户的 home 目录下一个名为 .vuerc 的 JSON 文件里。如果你想要修改被保存的 preset / 选项,可以编辑这个文件。
在项目创建的过程中,你也会被提示选择喜欢的包管理器或使用淘宝 npm 镜像源以更快地安装依赖。这些选择也将会存入 ~/.vuerc。
vue create 命令有一些可选项,你可以通过运行以下命令进行探索:
vue create --help
用法:create [options] <app-name>创建一个由 `vue-cli-service` 提供支持的新项目选项: -p, --preset <presetName> 忽略提示符并使用已保存的或远程的预设选项 -d, --default 忽略提示符并使用默认预设选项 -i, --inlinePreset <json> 忽略提示符并使用内联的 JSON 字符串预设选项 -m, --packageManager <command> 在安装依赖时使用指定的 npm 客户端 -r, --registry <url> 在安装依赖时使用指定的 npm registry -g, --git [message] 强制 / 跳过 git 初始化,并可选的指定初始化提交信息 -n, --no-git 跳过 git 初始化 -f, --force 覆写目标目录可能存在的配置 -c, --clone 使用 git clone 获取远程预设选项 -x, --proxy 使用指定的代理创建项目 -b, --bare 创建项目时省略默认组件中的新手指导信息 -h, --help 输出使用帮助信息
你也可以通过 vue ui 命令以图形化界面创建和管理项目:
vue ui
上述命令会打开一个浏览器窗口,并以图形化界面将你引导至项目创建的流程。
Vue CLI >= 3 和旧版使用了相同的 vue 命令,所以 Vue CLI 2 (vue-cli) 被覆盖了。如果你仍然需要使用旧版本的 vue init 功能,你可以全局安装一个桥接工具:
npm install -g @vue/cli-init# `vue init` 的运行效果将会跟 `vue-cli@2.x` 相同vue init webpack my-project
Vue CLI 使用了一套基于插件的架构。如果你查阅一个新创建项目的 package.json,就会发现依赖都是以 @vue/cli-plugin- 开头的。插件可以修改 webpack 的内部配置,也可以向 vue-cli-service 注入命令。在项目创建的过程中,绝大部分列出的特性都是通过插件来实现的。
基于插件的架构使得 Vue CLI 灵活且可扩展。如果你对开发一个插件感兴趣,请翻阅插件开发指南。
提示
你可以通过 vue ui 命令使用 GUI 安装和管理插件。
每个 CLI 插件都会包含一个 (用来创建文件的) 生成器和一个 (用来调整 webpack 核心配置和注入命令的) 运行时插件。当你使用 vue create 来创建一个新项目的时候,有些插件会根据你选择的特性被预安装好。如果你想在一个已经被创建好的项目中安装一个插件,可以使用 vue add 命令:
vue add eslint
提示
vue add 的设计意图是为了安装和调用 Vue CLI 插件。这不意味着替换掉普通的 npm 包。对于这些普通的 npm 包,你仍然需要选用包管理器。
警告
我们推荐在运行 vue add 之前将项目的最新状态提交,因为该命令可能调用插件的文件生成器并很有可能更改你现有的文件。
这个命令将 @vue/eslint 解析为完整的包名 @vue/cli-plugin-eslint,然后从 npm 安装它,调用它的生成器。
# 这个和之前的用法等价vue add cli-plugin-eslint
如果不带 @vue 前缀,该命令会换作解析一个 unscoped 的包。例如以下命令会安装第三方插件 vue-cli-plugin-apollo:
# 安装并调用 vue-cli-plugin-apollovue add apollo
你也可以基于一个指定的 scope 使用第三方插件。例如如果一个插件名为 @foo/vue-cli-plugin-bar,你可以这样添加它:
vue add @foo/bar
你可以向被安装的插件传递生成器选项 (这样做会跳过命令提示):
vue add eslint --config airbnb --lintOn save
如果一个插件已经被安装,你可以使用 vue invoke 命令跳过安装过程,只调用它的生成器。这个命令会接受和 vue add 相同的参数。
提示
如果出于一些原因你的插件列在了该项目之外的其它 package.json 文件里,你可以在自己项目的 package.json 里设置 vuePlugins.resolveFrom 选项指向包含其它 package.json 的文件夹。
例如,如果你有一个 .config/package.json 文件:
{ "vuePlugins": { "resolveFrom": ".config" }}
如果你需要在项目里直接访问插件 API 而不需要创建一个完整的插件,你可以在 package.json 文件中使用 vuePlugins.service 选项:
{ "vuePlugins": { "service": ["my-commands.js"] }}
每个文件都需要暴露一个函数,接受插件 API 作为第一个参数。关于插件 API 的更多信息可以查阅插件开发指南。
你也可以通过 vuePlugins.ui 选项添加像 UI 插件一样工作的文件:
{ "vuePlugins": { "ui": ["my-ui.js"] }}
更多信息请阅读 UI 插件 API。
一个 Vue CLI preset 是一个包含创建新项目所需预定义选项和插件的 JSON 对象,让用户无需在命令提示中选择它们。
在 vue create 过程中保存的 preset 会被放在你的 home 目录下的一个配置文件中 (~/.vuerc)。你可以通过直接编辑这个文件来调整、添加、删除保存好的 preset。
这里有一个 preset 的示例:
{ "useConfigFiles": true, "cssPreprocessor": "sass", "plugins": { "@vue/cli-plugin-babel": {}, "@vue/cli-plugin-eslint": { "config": "airbnb", "lintOn": ["save", "commit"] }, "@vue/cli-plugin-router": {}, "@vue/cli-plugin-vuex": {} }}
Preset 的数据会被插件生成器用来生成相应的项目文件。除了上述这些字段,你也可以为集成工具添加配置:
{ "useConfigFiles": true, "plugins": {...}, "configs": { "vue": {...}, "postcss": {...}, "eslintConfig": {...}, "jest": {...} }}
这些额外的配置将会根据 useConfigFiles 的值被合并到 package.json 或相应的配置文件中。例如,当 "useConfigFiles": true 的时候,configs 的值将会被合并到 vue.config.js 中。
你可以显式地指定用到的插件的版本:
{ "plugins": { "@vue/cli-plugin-eslint": { "version": "^3.0.0", // ... 该插件的其它选项 } }}
注意对于官方插件来说这不是必须的——当被忽略时,CLI 会自动使用 registry 中最新的版本。不过我们推荐为 preset 列出的所有第三方插件提供显式的版本范围。
每个插件在项目创建的过程中都可以注入它自己的命令提示,不过当你使用了一个 preset,这些命令提示就会被跳过,因为 Vue CLI 假设所有的插件选项都已经在 preset 中声明过了。
在有些情况下你可能希望 preset 只声明需要的插件,同时让用户通过插件注入的命令提示来保留一些灵活性。
对于这种场景你可以在插件选项中指定 "prompts": true 来允许注入命令提示:
{ "plugins": { "@vue/cli-plugin-eslint": { // 让用户选取他们自己的 ESLint config "prompts": true } }}
你可以通过发布 git repo 将一个 preset 分享给其他开发者。这个 repo 应该包含以下文件:
发布 repo 后,你就可以在创建项目的时候通过 --preset 选项使用这个远程的 preset 了:
# 从 GitHub repo 使用 presetvue create --preset username/repo my-project
GitLab 和 BitBucket 也是支持的。如果要从私有 repo 获取,请确保使用 --clone 选项:
vue create --preset gitlab:username/repo --clone my-projectvue create --preset bitbucket:username/repo --clone my-project
当开发一个远程 preset 的时候,你必须不厌其烦的向远程 repo 发出 push 进行反复测试。为了简化这个流程,你也可以直接在本地测试 preset。如果 --preset 选项的值是一个相对或绝对文件路径,或是以 .json 结尾,则 Vue CLI 会加载本地的 preset:
# ./my-preset 应当是一个包含 preset.json 的文件夹vue create --preset ./my-preset my-project# 或者,直接使用当前工作目录下的 json 文件:vue create --preset my-preset.json my-project
在一个 Vue CLI 项目中,@vue/cli-service 安装了一个名为 vue-cli-service 的命令。你可以在 npm scripts 中以 vue-cli-service、或者从终端中以 ./node_modules/.bin/vue-cli-service 访问这个命令。
这是你使用默认 preset 的项目的 package.json:
{ "scripts": { "serve": "vue-cli-service serve", "build": "vue-cli-service build" }}
你可以通过 npm 或 Yarn 调用这些 script:
npm run serve# ORyarn serve
如果你可以使用 npx (最新版的 npm 应该已经自带),也可以直接这样调用命令:
npx vue-cli-service serve
提示
你可以通过 vue ui 命令使用 GUI 运行更多的特性脚本。
这里有一个 GUI 的 webpack Analyzer:
用法:vue-cli-service serve [options] [entry]选项: --open 在服务器启动时打开浏览器 --copy 在服务器启动时将 URL 复制到剪切版 --mode 指定环境模式 (默认值:development) --host 指定 host (默认值:0.0.0.0) --port 指定 port (默认值:8080) --https 使用 https (默认值:false)
vue-cli-service serve 命令会启动一个开发服务器 (基于 webpack-dev-server) 并附带开箱即用的模块热重载 (Hot-Module-Replacement)。
除了通过命令行参数,你也可以使用 vue.config.js 里的 devServer 字段配置开发服务器。
命令行参数 [entry] 将被指定为唯一入口,而非额外的追加入口。尝试使用 [entry] 覆盖 config.pages 中的 entry 将可能引发错误。
用法:vue-cli-service build [options] [entry|pattern]选项: --mode 指定环境模式 (默认值:production) --dest 指定输出目录 (默认值:dist) --modern 面向现代浏览器带自动回退地构建应用 --target app | lib | wc | wc-async (默认值:app) --name 库或 Web Components 模式下的名字 (默认值:package.json 中的 "name" 字段或入口文件名) --no-clean 在构建项目之前不清除目标目录 --report 生成 report.html 以帮助分析包内容 --report-json 生成 report.json 以帮助分析包内容 --watch 监听文件变化
vue-cli-service build 会在 dist/ 目录产生一个可用于生产环境的包,带有 JS/CSS/HTML 的压缩,和为更好的缓存而做的自动的 vendor chunk splitting。它的 chunk manifest 会内联在 HTML 里。
这里还有一些有用的命令参数:
用法:vue-cli-service inspect [options] [...paths]选项: --mode 指定环境模式 (默认值:development)
你可以使用 vue-cli-service inspect 来审查一个 Vue CLI 项目的 webpack config。更多细节请查阅审查 webpack config。
有些 CLI 插件会向 vue-cli-service 注入额外的命令。例如 @vue/cli-plugin-eslint 会注入 vue-cli-service lint 命令。你可以运行以下命令查看所有注入的命令:
npx vue-cli-service help
你也可以这样学习每个命令可用的选项:
npx vue-cli-service help [command]
在安装之后,@vue/cli-service 也会安装 yorkie,它会让你在 package.json 的 gitHooks 字段中方便地指定 Git hook:
{ "gitHooks": { "pre-commit": "lint-staged" }, "lint-staged": { "*.{js,vue}": [ "vue-cli-service lint", "git add" ] }}
注意
yorkie fork 自 husky 且并与后者不兼容。
通过 vue create 创建的项目无需额外的配置就已经可以跑起来了。插件的设计也是可以相互共存的,所以绝大多数情况下,你只需要在交互式命令提示中选取需要的功能即可。
不过我们也知道满足每一个需求是不太可能的,而且一个项目的需求也会不断改变。通过 Vue CLI 创建的项目让你无需 eject 就能够配置工具的几乎每个角落。更多细节请查阅配置参考。
你会发现有 package.json 文件里的 browserslist 字段 (或一个单独的 .browserslistrc 文件),指定了项目的目标浏览器的范围。这个值会被 @babel/preset-env 和 Autoprefixer 用来确定需要转译的 JavaScript 特性和需要添加的 CSS 浏览器前缀。
现在查阅这里了解如何指定浏览器范围。
一个默认的 Vue CLI 项目会使用 @vue/babel-preset-app,它通过 @babel/preset-env 和 browserslist 配置来决定项目需要的 polyfill。
默认情况下,它会把 useBuiltIns: 'usage' 传递给 @babel/preset-env,这样它会根据源代码中出现的语言特性自动检测需要的 polyfill。这确保了最终包里 polyfill 数量的最小化。然而,这也意味着如果其中一个依赖需要特殊的 polyfill,默认情况下 Babel 无法将其检测出来。
如果有依赖需要 polyfill,你有几种选择:
更多细节可查阅 @babel-preset/env 文档。
当使用 Vue CLI 来构建一个库或是 Web Component 时,推荐给 @vue/babel-preset-app 传入 useBuiltIns: false 选项。这能够确保你的库或是组件不包含不必要的 polyfills。通常来说,打包 polyfills 应当是最终使用你的库的应用的责任。
有了 Babel 我们可以兼顾所有最新的 ES2015+ 语言特性,但也意味着我们需要交付转译和 polyfill 后的包以支持旧浏览器。这些转译后的包通常都比原生的 ES2015+ 代码会更冗长,运行更慢。现如今绝大多数现代浏览器都已经支持了原生的 ES2015,所以因为要支持更老的浏览器而为它们交付笨重的代码是一种浪费。
Vue CLI 提供了一个“现代模式”帮你解决这个问题。以如下命令为生产环境构建:
vue-cli-service build --modern
Vue CLI 会产生两个应用的版本:一个现代版的包,面向支持 ES modules 的现代浏览器,另一个旧版的包,面向不支持的旧浏览器。
最酷的是这里没有特殊的部署要求。其生成的 HTML 文件会自动使用 Phillip Walton 精彩的博文中讨论到的技术:
对于一个 Hello World 应用来说,现代版的包已经小了 16%。在生产环境下,现代版的包通常都会表现出显著的解析速度和运算速度,从而改善应用的加载性能。
提示
<script type="module"> 需要配合始终开启的 CORS 进行加载。这意味着你的服务器必须返回诸如 Access-Control-Allow-Origin: * 的有效的 CORS 头。如果你想要通过认证来获取脚本,可使将 crossorigin 选项设置为 use-credentials。
同时,现代浏览器使用一段内联脚本来避免 Safari 10 重复加载脚本包,所以如果你在使用一套严格的 CSP,你需要这样显性地允许内联脚本:
Content-Security-Policy: script-src 'self' 'sha256-4RS22DYeB7U14dra4KcQYxmwt5HkOInieXK1NUMB
public/index.html 文件是一个会被 html-webpack-plugin 处理的模板。在构建过程中,资源链接会被自动注入。另外,Vue CLI 也会自动注入 resource hint (preload/prefetch、manifest 和图标链接 (当用到 PWA 插件时) 以及构建过程中处理的 JavaScript 和 CSS 文件的资源链接。
因为 index 文件被用作模板,所以你可以使用 lodash template 语法插入内容:
除了被 html-webpack-plugin 暴露的默认值之外,所有客户端环境变量也可以直接使用。例如,BASE_URL 的用法:
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
更多内容可以查阅:
<link rel="preload"> 是一种 resource hint,用来指定页面加载后很快会被用到的资源,所以在页面加载的过程中,我们希望在浏览器开始主体渲染之前尽早 preload。
默认情况下,一个 Vue CLI 应用会为所有初始化渲染需要的文件自动生成 preload 提示。
这些提示会被 @vue/preload-webpack-plugin 注入,并且可以通过 chainWebpack 的 config.plugin('preload') 进行修改和删除。
<link rel="prefetch"> 是一种 resource hint,用来告诉浏览器在页面加载完成后,利用空闲时间提前获取用户未来可能会访问的内容。
默认情况下,一个 Vue CLI 应用会为所有作为 async chunk 生成的 JavaScript 文件 (通过动态 import() 按需 code splitting 的产物) 自动生成 prefetch 提示。
这些提示会被 @vue/preload-webpack-plugin 注入,并且可以通过 chainWebpack 的 config.plugin('prefetch') 进行修改和删除。
示例:
// vue.config.jsmodule.exports = { chainWebpack: config => { // 移除 prefetch 插件 config.plugins.delete('prefetch') // 或者 // 修改它的选项: config.plugin('prefetch').tap(options => { options[0].fileBlacklist = options[0].fileBlacklist || [] options[0].fileBlacklist.push(/myasyncRoute(.)+?.js$/) return options }) }}
当 prefetch 插件被禁用时,你可以通过 webpack 的内联注释手动选定要提前获取的代码区块:
import(/* webpackPrefetch: true */ './someAsyncComponent.vue')
webpack 的运行时会在父级区块被加载之后注入 prefetch 链接。
提示
Prefetch 链接将会消耗带宽。如果你的应用很大且有很多 async chunk,而用户主要使用的是对带宽较敏感的移动端,那么你可能需要关掉 prefetch 链接并手动选择要提前获取的代码区块。
当基于已有的后端使用 Vue CLI 时,你可能不需要生成 index.html,这样生成的资源可以用于一个服务端渲染的页面。这时可以向 vue.config.js 加入下列代码:
// vue.config.jsmodule.exports = { // 去掉文件名中的 hash filenameHashing: false, // 删除 HTML 相关的 webpack 插件 chainWebpack: config => { config.plugins.delete('html') config.plugins.delete('preload') config.plugins.delete('prefetch') }}
然而这样做并不是很推荐,因为:
你应该考虑换用 indexPath 选项将生成的 HTML 用作一个服务端框架的视图模板。
不是每个应用都需要是一个单页应用。Vue CLI 支持使用 vue.config.js 中的 pages 选项构建一个多页面的应用。构建好的应用将会在不同的入口之间高效共享通用的 chunk 以获得最佳的加载性能。
静态资源可以通过两种方式进行处理:
当你在 JavaScript、CSS 或 *.vue 文件中使用相对路径 (必须以 . 开头) 引用一个静态资源时,该资源将会被包含进入 webpack 的依赖图中。在其编译过程中,所有诸如 <img src="...">、background: url(...) 和 CSS @import 的资源 URL 都会被解析为一个模块依赖。
例如,url(./image.png) 会被翻译为 require('./image.png'),而:
<img src="./image.png">
将会被编译到:
h('img', { attrs: { src: require('./image.png') }})
在其内部,我们通过 file-loader 用版本哈希值和正确的公共基础路径来决定最终的文件路径,再用 url-loader 将小于 4kb 的资源内联,以减少 HTTP 请求的数量。
你可以通过 chainWebpack 调整内联文件的大小限制。例如,下列代码会将其限制设置为 10kb:
// vue.config.jsmodule.exports = { chainWebpack: config => { config.module .rule('images') .use('url-loader') .loader('url-loader') .tap(options => Object.assign(options, { limit: 10240 })) }}
任何放置在 public 文件夹的静态资源都会被简单的复制,而不经过 webpack。你需要通过绝对路径来引用它们。
注意我们推荐将资源作为你的模块依赖图的一部分导入,这样它们会通过 webpack 的处理并获得如下好处:
public 目录提供的是一个应急手段,当你通过绝对路径引用它时,留意应用将会部署到哪里。如果你的应用没有部署在域名的根部,那么你需要为你的 URL 配置 publicPath 前缀:
Vue CLI 项目天生支持 PostCSS、CSS Modules 和包含 Sass、Less、Stylus 在内的预处理器。
所有编译后的 CSS 都会通过 css-loader 来解析其中的 url() 引用,并将这些引用作为模块请求来处理。这意味着你可以根据本地的文件结构用相对路径来引用静态资源。另外要注意的是如果你想要引用一个 npm 依赖中的文件,或是想要用 webpack alias,则需要在路径前加上 ~ 的前缀来避免歧义。更多细节请参考处理静态资源。
你可以在创建项目的时候选择预处理器 (Sass/Less/Stylus)。如果当时没有选好,内置的 webpack 仍然会被预配置为可以完成所有的处理。你也可以手动安装相应的 webpack loader:
# Sassnpm install -D sass-loader node-sass# Lessnpm install -D less-loader less# Stylusnpm install -D stylus-loader stylus
然后你就可以导入相应的文件类型,或在 *.vue 文件中这样来使用:
<style lang="scss">$color: red;</style>
如果你想自动化导入文件 (用于颜色、变量、mixin……),你可以使用 style-resources-loader。这里有一个关于 Stylus 的在每个单文件组件和 Stylus 文件中导入 ./src/styles/imports.styl 的例子:
// vue.config.jsconst path = require('path')module.exports = { chainWebpack: config => { const types = ['vue-modules', 'vue', 'normal-modules', 'normal'] types.forEach(type => addStyleResource(config.module.rule('stylus').oneOf(type))) },}function addStyleResource (rule) { rule.use('style-resource') .loader('style-resources-loader') .options({ patterns: [ path.resolve(__dirname, './src/styles/imports.styl'), ], })}
你也可以选择使用 vue-cli-plugin-style-resources-loader。
Vue CLI 内部使用了 PostCSS。
你可以通过 .postcssrc 或任何 postcss-load-config 支持的配置源来配置 PostCSS。也可以通过 vue.config.js 中的 css.loaderOptions.postcss 配置 postcss-loader。
我们默认开启了 autoprefixer。如果要配置目标浏览器,可使用 package.json 的 browserslist 字段。
关于 CSS 中浏览器前缀规则的注意事项
在生产环境构建中,Vue CLI 会优化 CSS 并基于目标浏览器抛弃不必要的浏览器前缀规则。因为默认开启了 autoprefixer,你只使用无前缀的 CSS 规则即可。
你可以通过 <style module> 以开箱即用的方式在 *.vue 文件中使用 CSS Modules。
如果想在 JavaScript 中作为 CSS Modules 导入 CSS 或其它预处理文件,该文件应该以 .module.(css|less|sass|scss|styl) 结尾:
import styles from './foo.module.css'// 所有支持的预处理器都一样工作import sassStyles from './foo.module.scss'
如果你想去掉文件名中的 .module,可以设置 vue.config.js 中的 css.requireModuleExtension 为 false:
// vue.config.jsmodule.exports = { css: { requireModuleExtension: false }}
如果你希望自定义生成的 CSS Modules 模块的类名,可以通过 vue.config.js 中的 css.loaderOptions.css 选项来实现。所有的 css-loader 选项在这里都是支持的,例如 localIdentName 和 camelCase:
// vue.config.jsmodule.exports = { css: { loaderOptions: { css: { // 注意:以下配置在 Vue CLI v4 与 v3 之间存在差异。 // Vue CLI v3 用户可参考 css-loader v1 文档 // https://github.com/webpack-contrib/css-loader/tree/v1.0.1 modules: { localIdentName: '[name]-[hash]' }, localsConvention: 'camelCaseOnly' } } }}
有的时候你想要向 webpack 的预处理器 loader 传递选项。你可以使用 vue.config.js 中的 css.loaderOptions 选项。比如你可以这样向所有 Sass/Less 样式传入共享的全局变量:
// vue.config.jsmodule.exports = { css: { loaderOptions: { // 给 sass-loader 传递选项 sass: { // @/ 是 src/ 的别名 // 所以这里假设你有 `src/variables.sass` 这个文件 // 注意:在 sass-loader v7 中,这个选项名是 "data" prependData: `@import "~@/variables.sass"` }, // 默认情况下 `sass` 选项会同时对 `sass` 和 `scss` 语法同时生效 // 因为 `scss` 语法在内部也是由 sass-loader 处理的 // 但是在配置 `data` 选项的时候 // `scss` 语法会要求语句结尾必须有分号,`sass` 则要求必须没有分号 // 在这种情况下,我们可以使用 `scss` 选项,对 `scss` 语法进行单独配置 scss: { prependData: `@import "~@/variables.scss";` }, // 给 less-loader 传递 Less.js 相关选项 less:{ // http://lesscss.org/usage/#less-options-strict-units `Global Variables` // `primary` is global variables fields name globalVars: { primary: '#fff' } } } }}
Loader 可以通过 loaderOptions 配置,包括:
提示
这样做比使用 chainWebpack 手动指定 loader 更推荐,因为这些选项需要应用在使用了相应 loader 的多个地方。
调整 webpack 配置最简单的方式就是在 vue.config.js 中的 configureWebpack 选项提供一个对象:
// vue.config.jsmodule.exports = { configureWebpack: { plugins: [ new MyAwesomeWebpackPlugin() ] }}
该对象将会被 webpack-merge 合并入最终的 webpack 配置。
警告
有些 webpack 选项是基于 vue.config.js 中的值设置的,所以不能直接修改。例如你应该修改 vue.config.js 中的 outputDir 选项而不是修改 output.path;你应该修改 vue.config.js 中的 publicPath 选项而不是修改 output.publicPath。这样做是因为 vue.config.js 中的值会被用在配置里的多个地方,以确保所有的部分都能正常工作在一起。
如果你需要基于环境有条件地配置行为,或者想要直接修改配置,那就换成一个函数 (该函数会在环境变量被设置之后懒执行)。该方法的第一个参数会收到已经解析好的配置。在函数内,你可以直接修改配置,或者返回一个将会被合并的对象:
// vue.config.jsmodule.exports = { configureWebpack: config => { if (process.env.NODE_ENV === 'production') { // 为生产环境修改配置... } else { // 为开发环境修改配置... } }}
Vue CLI 内部的 webpack 配置是通过 webpack-chain 维护的。这个库提供了一个 webpack 原始配置的上层抽象,使其可以定义具名的 loader 规则和具名插件,并有机会在后期进入这些规则并对它们的选项进行修改。
它允许我们更细粒度的控制其内部配置。接下来有一些常见的在 vue.config.js 中的 chainWebpack 修改的例子。
提示
当你打算链式访问特定的 loader 时,vue inspect 会非常有帮助。
// vue.config.jsmodule.exports = { chainWebpack: config => { config.module .rule('vue') .use('vue-loader') .loader('vue-loader') .tap(options => { // 修改它的选项... return options }) }}
提示
对于 CSS 相关 loader 来说,我们推荐使用 css.loaderOptions 而不是直接链式指定 loader。这是因为每种 CSS 文件类型都有多个规则,而 css.loaderOptions 可以确保你通过一个地方影响所有的规则。
// vue.config.jsmodule.exports = { chainWebpack: config => { // GraphQL Loader config.module .rule('graphql') .test(/.graphql$/) .use('graphql-tag/loader') .loader('graphql-tag/loader') .end() // 你还可以再添加一个 loader .use('other-loader') .loader('other-loader') .end() }}
如果你想要替换一个已有的基础 loader,例如为内联的 SVG 文件使用 vue-svg-loader 而不是加载这个文件:
// vue.config.jsmodule.exports = { chainWebpack: config => { const svgRule = config.module.rule('svg') // 清除已有的所有 loader。 // 如果你不这样做,接下来的 loader 会附加在该规则现有的 loader 之后。 svgRule.uses.clear() // 添加要替换的 loader svgRule .use('vue-svg-loader') .loader('vue-svg-loader') }}
// vue.config.jsmodule.exports = { chainWebpack: config => { config .plugin('html') .tap(args => { return [/* 传递给 html-webpack-plugin's 构造函数的新参数 */] }) }}
你需要熟悉 webpack-chain 的 API 并阅读一些源码以便了解如何最大程度利用好这个选项,但是比起直接修改 webpack 配置,它的表达能力更强,也更为安全。
比方说你想要将 index.html 默认的路径从 /Users/username/proj/public/index.html 改为 /Users/username/proj/app/templates/index.html。通过参考 html-webpack-plugin 你能看到一个可以传入的选项列表。我们可以在下列配置中传入一个新的模板路径来改变它:
// vue.config.jsmodule.exports = { chainWebpack: config => { config .plugin('html') .tap(args => { args[0].template = '/Users/username/proj/app/templates/index.html' return args }) }}
你可以通过接下来要讨论的工具 vue inspect 来确认变更。
因为 @vue/cli-service 对 webpack 配置进行了抽象,所以理解配置中包含的东西会比较困难,尤其是当你打算自行对其调整的时候。
vue-cli-service 暴露了 inspect 命令用于审查解析好的 webpack 配置。那个全局的 vue 可执行程序同样提供了 inspect 命令,这个命令只是简单的把 vue-cli-service inspect 代理到了你的项目中。
该命令会将解析出来的 webpack 配置、包括链式访问规则和插件的提示打印到 stdout。
你可以将其输出重定向到一个文件以便进行查阅:
vue inspect > output.js
注意它输出的并不是一个有效的 webpack 配置文件,而是一个用于审查的被序列化的格式。
你也可以通过指定一个路径来审查配置的一小部分:
# 只审查第一条规则vue inspect module.rules.0
或者指向一个规则或插件的名字:
vue inspect --rule vuevue inspect --plugin html
最后,你可以列出所有规则和插件的名字:
vue inspect --rulesvue inspect --plugins
有些外部工具可能需要通过一个文件访问解析好的 webpack 配置,比如那些需要提供 webpack 配置路径的 IDE 或 CLI。在这种情况下你可以使用如下路径:
<projectRoot>/node_modules/@vue/cli-service/webpack.config.js
该文件会动态解析并输出 vue-cli-service 命令中使用的相同的 webpack 配置,包括那些来自插件甚至是你自定义的配置。
你可以替换你的项目根目录中的下列文件来指定环境变量:
.env # 在所有的环境中被载入.env.local # 在所有的环境中被载入,但会被 git 忽略.env.[mode] # 只在指定的模式中被载入.env.[mode].local # 只在指定的模式中被载入,但会被 git 忽略
一个环境文件只包含环境变量的“键=值”对:
FOO=barVUE_APP_SECRET=secret
被载入的变量将会对 vue-cli-service 的所有命令、插件和依赖可用。
环境加载属性
为一个特定模式准备的环境文件的 (例如 .env.production) 将会比一般的环境文件 (例如 .env) 拥有更高的优先级。
此外,Vue CLI 启动时已经存在的环境变量拥有最高优先级,并不会被 .env 文件覆写。
NODE_ENV
如果在环境中有默认的 NODE_ENV,你应该移除它或在运行 vue-cli-service 命令的时候明确地设置 NODE_ENV。
模式是 Vue CLI 项目中一个重要的概念。默认情况下,一个 Vue CLI 项目有三个模式:
注意模式不同于 NODE_ENV,一个模式可以包含多个环境变量。也就是说,每个模式都会将 NODE_ENV 的值设置为模式的名称——比如在 development 模式下 NODE_ENV 的值会被设置为 "development"。
你可以通过为 .env 文件增加后缀来设置某个模式下特有的环境变量。比如,如果你在项目根目录创建一个名为 .env.development 的文件,那么在这个文件里声明过的变量就只会在 development 模式下被载入。
你可以通过传递 --mode 选项参数为命令行覆写默认的模式。例如,如果你想要在构建命令中使用开发环境变量,请在你的 package.json 脚本中加入:
"dev-build": "vue-cli-service build --mode development",
假设我们有一个应用包含以下 .env 文件:
VUE_APP_TITLE=My App
和 .env.staging 文件:
NODE_ENV=productionVUE_APP_TITLE=My App (staging)
这两种情况下,根据 NODE_ENV,构建出的应用都是生产环境应用,但是在 staging 版本中,process.env.VUE_APP_TITLE 被覆写成了另一个值。
只有以 VUE_APP_ 开头的变量会被 webpack.DefinePlugin 静态嵌入到客户端侧的包中。你可以在应用的代码中这样访问它们:
console.log(process.env.VUE_APP_SECRET)
在构建过程中,process.env.VUE_APP_SECRET 将会被相应的值所取代。在 VUE_APP_SECRET=secret 的情况下,它会被替换为 "secret"。
除了 VUE_APP_* 变量之外,在你的应用代码中始终可用的还有两个特殊的变量:
所有解析出来的环境变量都可以在 public/index.html 中以 HTML 插值中介绍的方式使用。
提示
你可以在 vue.config.js 文件中计算环境变量。它们仍然需要以 VUE_APP_ 前缀开头。这可以用于版本信息 process.env.VUE_APP_VERSION = require('./package.json').version。
有的时候你可能有一些不应该提交到代码仓库中的变量,尤其是当你的项目托管在公共仓库时。这种情况下你应该使用一个 .env.local 文件取而代之。本地环境文件默认会被忽略,且出现在 .gitignore 中。
.local 也可以加在指定模式的环境文件上,比如 .env.development.local 将会在 development 模式下被载入,且被 git 忽略。
当你运行 vue-cli-service build 时,你可以通过 --target 选项指定不同的构建目标。它允许你将相同的源代码根据不同的用例生成不同的构建。
应用模式是默认的模式。在这个模式中:
关于 IE 兼容性的提醒
在库模式中,项目的 publicPath 是根据主文件的加载路径动态设置的(用以支持动态的资源加载能力)。但是这个功能用到了 document.currentScript,而 IE 浏览器并不支持这一特性。所以如果网站需要支持 IE 的话,建议使用库之前先在页面上引入 current-script-polyfill。
注意对 Vue 的依赖
在库模式中,Vue 是外置的。这意味着包中不会有 Vue,即便你在代码中导入了 Vue。如果这个库会通过一个打包器使用,它将尝试通过打包器以依赖的方式加载 Vue;否则就会回退到一个全局的 Vue 变量。
要避免此行为,可以在build命令中添加--inline-vue标志。
vue-cli-service build --target lib --inline-vue
你可以通过下面的命令将一个单独的入口构建为一个库:
vue-cli-service build --target lib --name myLib [entry]
File Size Gzippeddist/myLib.umd.min.js 13.28 kb 8.42 kbdist/myLib.umd.js 20.95 kb 10.22 kbdist/myLib.common.js 20.57 kb 10.09 kbdist/myLib.css 0.33 kb 0.23 kb
这个入口可以是一个 .js 或一个 .vue 文件。如果没有指定入口,则会使用 src/App.vue。
构建一个库会输出:
警告
如果你在开发一个库或多项目仓库 (monorepo),请注意导入 CSS 是具有副作用的。请确保在 package.json 中移除 "sideEffects": false,否则 CSS 代码块会在生产环境构建时被 webpack 丢掉。
当使用一个 .vue 文件作为入口时,你的库会直接暴露这个 Vue 组件本身,因为组件始终是默认导出的内容。
然而,当你使用一个 .js 或 .ts 文件作为入口时,它可能会包含具名导出,所以库会暴露为一个模块。也就是说你的库必须在 UMD 构建中通过 window.yourLib.default 访问,或在 CommonJS 构建中通过 const myLib = require('mylib').default 访问。如果你没有任何具名导出并希望直接暴露默认导出,你可以在 vue.config.js 中使用以下 webpack 配置:
module.exports = { configureWebpack: { output: { libraryExport: 'default' } }}
兼容性提示
Web Components 模式不支持 IE11 及更低版本。更多细节
注意对 Vue 的依赖
在 Web Components 模式中,Vue 是外置的。这意味着包中不会有 Vue,即便你在代码中导入了 Vue。这里的包会假设在页面中已经有一个可用的全局变量 Vue。
你可以通过下面的命令将一个单独的入口构建为一个 Web Components 组件:
vue-cli-service build --target wc --name my-element [entry]
注意这里的入口应该是一个 *.vue 文件。Vue CLI 将会把这个组件自动包裹并注册为 Web Components 组件,无需在 main.js 里自行注册。也可以在开发时把 main.js 作为 demo app 单独使用。
该构建将会产生一个单独的 JavaScript 文件 (及其压缩后的版本) 将所有的东西都内联起来。当这个脚本被引入网页时,会注册自定义组件 <my-element>,其使用 @vue/web-component-wrapper 包裹了目标的 Vue 组件。这个包裹器会自动代理属性、特性、事件和插槽。请查阅 @vue/web-component-wrapper 的文档了解更多细节。
注意这个包依赖了在页面上全局可用的 Vue。
这个模式允许你的组件的使用者以一个普通 DOM 元素的方式使用这个 Vue 组件:
<script src="https://unpkg.com/vue" rel="external nofollow" rel="external nofollow" ></script><script src="path/to/my-element.js"></script><!-- 可在普通 HTML 中或者其它任何框架中使用 --><my-element></my-element>
当你构建一个 Web Components 组件包的时候,你也可以使用一个 glob 表达式作为入口指定多个组件目标:
vue-cli-service build --target wc --name foo 'src/components/*.vue'
当你构建多个 web component 时,--name 将会用于设置前缀,同时自定义元素的名称会由组件的文件名推导得出。比如一个名为 HelloWorld.vue 的组件携带 --name foo 将会生成的自定义元素名为 <foo-hello-world>。
当指定多个 Web Components 组件作为目标时,这个包可能会变得非常大,并且用户可能只想使用你的包中注册的一部分组件。这时异步 Web Components 模式会生成一个 code-split 的包,带一个只提供所有组件共享的运行时,并预先注册所有的自定义组件小入口文件。一个组件真正的实现只会在页面中用到自定义元素相应的一个实例时按需获取:
vue-cli-service build --target wc-async --name foo 'src/components/*.vue'
File Size Gzippeddist/foo.0.min.js 12.80 kb 8.09 kbdist/foo.min.js 7.45 kb 3.17 kbdist/foo.1.min.js 2.91 kb 1.02 kbdist/foo.js 22.51 kb 6.67 kbdist/foo.0.js 17.27 kb 8.83 kbdist/foo.1.js 5.24 kb 1.64 kb
现在用户在该页面上只需要引入 Vue 和这个入口文件即可:
<script src="https://unpkg.com/vue" rel="external nofollow" rel="external nofollow" ></script><script src="path/to/foo.min.js"></script><!-- foo-one 的实现的 chunk 会在用到的时候自动获取 --><foo-one></foo-one>
如果你用 Vue CLI 处理静态资源并和后端框架一起作为部署的一部分,那么你需要的仅仅是确保 Vue CLI 生成的构建文件在正确的位置,并遵循后端框架的发布方式即可。
如果你独立于后端部署前端应用——也就是说后端暴露一个前端可访问的 API,然后前端实际上是纯静态应用。那么你可以将 dist 目录里构建的内容部署到任何静态文件服务器中,但要确保正确的 publicPath。
dist 目录需要启动一个 HTTP 服务器来访问 (除非你已经将 publicPath 配置为了一个相对的值),所以以 file:// 协议直接打开 dist/index.html 是不会工作的。在本地预览生产环境构建最简单的方式就是使用一个 Node.js 静态文件服务器,例如 serve:
npm install -g serve# -s 参数的意思是将其架设在 Single-Page Application 模式下# 这个模式会处理即将提到的路由问题serve -s dist
如果你在 history 模式下使用 Vue Router,是无法搭配简单的静态文件服务器的。例如,如果你使用 Vue Router 为 /todos/42/ 定义了一个路由,开发服务器已经配置了相应的 localhost:3000/todos/42 响应,但是一个为生产环境构建架设的简单的静态服务器会却会返回 404。
为了解决这个问题,你需要配置生产环境服务器,将任何没有匹配到静态文件的请求回退到 index.html。Vue Router 的文档提供了常用服务器配置指引。
如果前端静态内容是部署在与后端 API 不同的域名上,你需要适当地配置 CORS。
如果你使用了 PWA 插件,那么应用必须架设在 HTTPS 上,这样 Service Worker 才能被正确注册。
根据 GitLab Pages 文档的描述,所有的配置都在根目录中的.gitlab-ci.yml 文件中。下面的范例是一个很好的入门:
# .gitlab-ci.yml 文件应放在你仓库的根目录下 pages: # 必须定义一个名为 pages 的 job image: node:latest stage: deploy script: - npm ci - npm run build - mv public public-vue # GitLab Pages 的钩子设置在 public 文件夹 - mv dist public # 重命名 dist 文件夹 (npm run build 之后的输出位置) artifacts: paths: - public # artifact path 一定要在 /public , 这样 GitLab Pages 才能获取 only: - master
通常, 你的静态页面将托管在 https://yourUserName.gitlab.io/yourProjectName 上, 所以你可以创建一个 initial vue.config.js 文件去 更新 BASE_URL 要匹配的值 :
// vue.config.js 位于仓库的根目录下// 确保用 GitLab 项目的名称替换了 `YourProjectName`module.exports = { publicPath: process.env.NODE_ENV === 'production' ? '/yourProjectName/' : '/'}
请阅读在 GitLab Pages domains 的文档来学习更多关于页面部署 URL 的信息。注意,你也可以使用自定义域名。
在推送到仓库之前提交 .gitlab-ci.yml 和 vue.config.js 文件。GitLab CI 的管道将会被触发: 当成功时候, 到 Settings > Pages 查看关于网站的链接。
也可以查看 vue-cli-plugin-netlify-lambda。
如果使用 Vue Router 的 history 模式,你需要在 /public 目录下创建一个 _redirects 文件:
# 单页应用的 Netlify 设置/* /index.html 200
详细信息请查看 Netlify 重定向文档。
Render 提供带有全托管 SSL,全球 CDN 和 GitHub 持续自动部署的免费静态站点托管服务。
大功告成!构建结束时你的应用便会在你的 Render URL 上线。
如果使用 Vue Router 的 history 模式,你需要在站点的 Redirects/Rewrites 设置中添加以下改写规则:
详细信息请查看 Render 的重定向和改写及自定义域名文档。
创建一个新的 Firebase 项目 Firebase console。 请参考文档。
确保已经全局安装了 firebase-tools :
npm install -g firebase-tools
在项目的根目录下, 用以下命令初始化 firebase :
firebase init
Firebase 将会询问有关初始化项目的一些问题。
// firebase.json{ "hosting": { "public": "dist" }}
// firebase.json{ "hosting": { "rewrites": [ { "source": "**", "destination": "/index.html" } ] }}
执行 npm run build 去构建项目。
在 Firebase Hosting 部署项目,执行以下命令 :
firebase deploy --only hosting
如果需要在部署的项目中使用的其他 Firebase CLI 功能, 执行 firebase deploy 去掉 --only 参数。
现在可以到 https://<YOUR-PROJECT-ID>.firebaseapp.com 访问你的项目了。
请参考 Firebase 文档 来获取更多细节。
ZEIT Now 是一个网站和无服务器 (Serverless) API 云平台,你可以使用你的个人域名 (或是免费的 .now.sh URL) 部署你的 Vue 项目。
要使用 npm 安装其命令行界面,运行以下命令:
npm install -g now
在项目根目录运行以下命令部署你的应用:
now
此外,你还可以使用他们的 GitHub 或 GitLab 集成服务。
大功告成!
你的站点会开始部署,你将获得一个形如 https://vue.now-examples.now.sh/ 的链接。
开箱即用地,请求会被自动改写到 index.html (除了自定义的静态文件) 并带有合适的缓存请求头。你可以改写这些规则。
未完成 | 欢迎参与贡献。
详细信息:https://gist.github.com/hone/24b06869b4c1eca701f9
要使用 Surge 进行部署,步骤非常简单。
首先,通过运行 npm run build 来构建项目。如果还没有安装 Surge 的命令行工具,可以通过运行命令来执行此操作:
npm install --global surge
然后 cd 进入项目的 dist/ 文件夹,然后运行 surge 并按照屏幕提示操作 。如果是第一次使用 Surge,它会要求设置电子邮件和密码。确认项目文件夹以及输入首选域来查看正在部署的项目,如下所示。
project: /Users/user/Documents/myawesomeproject/dist/ domain: myawesomeproject.surge.sh upload: [====================] 100% eta: 0.0s (31 files, 494256 bytes) CDN: [====================] 100% IP: **.**.***.*** Success! - Published to myawesomeproject.surge.sh
通过访问 myawesomeproject.surge.sh 来确保你的项目已经成功的用 Surge 发布,有关自定义域名等更多设置详细信息,可以到 Surge's help page 查看。
在 Docker 容器中使用 Nginx 部署你的应用。
有些针对 @vue/cli 的全局配置,例如你惯用的包管理器和你本地保存的 preset,都保存在 home 目录下一个名叫 .vuerc 的 JSON 文件。你可以用编辑器直接编辑这个文件来更改已保存的选项。
你也可以使用 vue config 命令来审查或修改全局的 CLI 配置。
请查阅指南中的浏览器兼容性章节。
vue.config.js 是一个可选的配置文件,如果项目的 (和 package.json 同级的) 根目录中存在这个文件,那么它会被 @vue/cli-service 自动加载。你也可以使用 package.json 中的 vue 字段,但是注意这种写法需要你严格遵照 JSON 的格式来写。
这个文件应该导出一个包含了选项的对象:
// vue.config.jsmodule.exports = { // 选项...}
从 Vue CLI 3.3 起已弃用,请使用publicPath。
module.exports = { pages: { index: { // page 的入口 entry: 'src/index/main.js', // 模板来源 template: 'public/index.html', // 在 dist/index.html 的输出 filename: 'index.html', // 当使用 title 选项时, // template 中的 title 标签需要是 <title><%= htmlWebpackPlugin.options.title %></title> title: 'Index Page', // 在这个页面中包含的块,默认情况下会包含 // 提取出来的通用 chunk 和 vendor chunk。 chunks: ['chunk-vendors', 'chunk-common', 'index'] }, // 当使用只有入口的字符串格式时, // 模板会被推导为 `public/subpage.html` // 并且如果找不到的话,就回退到 `public/index.html`。 // 输出文件名会被推导为 `subpage.html`。 subpage: 'src/subpage/main.js' }}
从 v4 起已弃用,请使用css.requireModuleExtension。 在 v3 中,这个选项含义与 css.requireModuleExtension 相反。
Babel 可以通过 babel.config.js 进行配置。
提示
Vue CLI 使用了 Babel 7 中的新配置格式 babel.config.js。和 .babelrc 或 package.json 中的 babel 字段不同,这个配置文件不会使用基于文件位置的方案,而是会一致地运用到项目根目录以下的所有文件,包括 node_modules 内部的依赖。我们推荐在 Vue CLI 项目中始终使用 babel.config.js 取代其它格式。
所有的 Vue CLI 应用都使用 @vue/babel-preset-app,它包含了 babel-preset-env、JSX 支持以及为最小化包体积优化过的配置。通过它的文档可以查阅到更多细节和 preset 选项。
同时查阅指南中的 Polyfill 章节。
ESLint 可以通过 .eslintrc 或 package.json 中的 eslintConfig 字段来配置。
更多细节可查阅 @vue/cli-plugin-eslint。
TypeScript 可以通过 tsconfig.json 来配置。
更多细节可查阅 @vue/cli-plugin-typescript。
更多细节可查阅 @vue/cli-plugin-unit-jest。
更多细节可查阅 @vue/cli-plugin-unit-mocha。
更多细节可查阅 @vue/cli-plugin-e2e-cypress。
更多细节可查阅 @vue/cli-plugin-e2e-nightwatch。
系统里有两个主要的部分:
两者皆应用了基于插件的架构。
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 字段。这会确保:
你的插件在其 UI 中使用时,可以展示不同的附加信息,使其更容易被发现和辨认。
你可以在将要发布到 npm 上的目录的根上放置一个 logo.png。它会展示在以下几个地方:
这个 logo 应该是一个不透明的正方形图 (最好 84x84).
为了提升你的插件在搜索时的可见度,请将描述插件的关键字放到插件的 package.json 文件的 description 字段。
示例:
{ "name": "vue-cli-plugin-apollo", "version": "0.7.7", "description": "vue cli plugin to add Apollo and GraphQL"}
你应该将插件网站的 URL 或仓库添加添加到 homepage 或 repository 字段,这样“More Info”按钮就会在你的插件描述中展示出来。
{ "repository": { "type": "git", "url": "git+https://github.com/Akryum/vue-cli-plugin-apollo.git" }, "homepage": "https://github.com/Akryum/vue-cli-plugin-apollo#readme"}
这个 cli-ui 暴露一个 API,允许增强项目的配置和任务,也可以分享数据和在进程间进行通信。
在每个安装好的 vue-cli 插件里,cli-ui 都会尝试从其插件的根目录加载一个可选的 ui.js 文件。注意你也可以使用文件夹 (例如 ui/index.js)。
该文件应该导出一个函数,函数会以 API 对象作为第一个参数:
module.exports = api => { // 在这里使用 API...}
警告
当试图在“项目插件 (Project plugins)”中获取插件列表时,这些文件将会被重新加载。点击 UI 左侧边栏导航“项目插件 (Project plugins)”按钮可以应用更改。
这里是一个使用 UI API 的 vue-cli 插件的文件夹结构示例:
- vue-cli-plugin-test - package.json - index.js - generator.js - prompts.js - ui.js - logo.png
如果你需要在项目里访问插件 API 而不需要创建一个完整的插件,你可以在 package.json 文件中使用 vuePlugins.ui 选项:
{ "vuePlugins": { "ui": ["my-ui.js"] }}
每个文件都需要暴露一个函数,将插件 API 作为第一个参数携带。
当构建你自己的插件时,你可能想在开发环境下运行 cli-ui,所以这样运行会输出较为实用的日志:
vue ui --dev
或:
vue ui -D
你可以通过 api.describeConfig 方法添加一个项目配置。
首先你需要传入一些信息:
api.describeConfig({ // 唯一的配置 ID id: 'org.vue.eslintrc', // 展示名称 name: 'ESLint configuration', // 展示在名称下方的描述 description: 'Error checking & Code quality', // “更多信息 (More info)”链接 link: 'https://eslint.org'})
危险
请确定为 id 设置正确的命名空间,因为它需要跨所有插件保持唯一。我们推荐使用反向域名记号 (reverse domain name notation)。
可以是一个 Material 图标代码或一个自定义的图片 (详见公共静态文件):
api.describeConfig({ /* ... */ // 配置图标 icon: 'application_settings'})
如果你没有定义图标,那就展示该插件可能存在的 logo (详见 Logo)。
默认情况下,配置 UI 可能会读写一个或多个配置文件,例如 .eslintrc 和 vue.config.js。
你可以提供可能需要在用户项目中检测的文件:
api.describeConfig({ /* ... */ // 该配置所有可能的文件 files: { // eslintrc.js eslint: { js: ['.eslintrc.js'], json: ['.eslintrc', '.eslintrc.json'], // 会从 `package.json` 读取 package: 'eslintConfig' }, // vue.config.js vue: { js: ['vue.config.js'] } },})
支持的类型有:json、yaml、js、package。这个顺序是很重要的:如果这项配置不存在,则会创建列表中的第一个文件。
使用 onRead 钩子来返回一个提示符列表,用以配置展示:
api.describeConfig({ /* ... */ onRead: ({ data, cwd }) => ({ prompts: [ // 提示符对象 ] })})
这些提示符会展示在配置的详情面板中。
查阅提示符了解更多信息。
这个 data 对象包含了每个配置文件内容的 JSON 结果。
例如,假设用户在其项目中的 vue.config.js 有以下内容:
module.exports = { lintOnSave: false}
我们在插件中像这样声明配置文件:
api.describeConfig({ /* ... */ // 该配置所有可能的文件 files: { // vue.config.js vue: { js: ['vue.config.js'] } },})
则这个 data 对象会是:
{ // 文件 vue: { // 文件数据 lintOnSave: false }}
多个文件的例子:如果我们在用户的项目中添加以下 eslintrc.js 文件:
module.exports = { root: true, extends: [ 'plugin:vue/essential', '@vue/standard' ]}
那么在我们的插件中将 files 选项改变成为:
api.describeConfig({ /* ... */ // 该配置所有可能的文件 files: { // eslintrc.js eslint: { js: ['.eslintrc.js'], json: ['.eslintrc', '.eslintrc.json'], // 会从 `package.json` 读取 package: 'eslintConfig' }, // vue.config.js vue: { js: ['vue.config.js'] } },})
则这个 data 对象会是:
{ eslint: { root: true, extends: [ 'plugin:vue/essential', '@vue/standard' ] }, vue: { lintOnSave: false }}
你可以将这些提示符组织成为几个选项卡:
api.describeConfig({ /* ... */ onRead: ({ data, cwd }) => ({ tabs: [ { id: 'tab1', label: 'My tab', // 可选的 icon: 'application_settings', prompts: [ // 提示符对象们 ] }, { id: 'tab2', label: 'My other tab', prompts: [ // 提示符对象们 ] } ] })})
使用 onWrite 钩子将数据写入配置文件 (或者执行任何 Node.js 代码):
api.describeConfig({ /* ... */ onWrite: ({ prompts, answers, data, files, cwd, api }) => { // ... }})
参数:
提示符的运行时对象:
{ id: data.name, type: data.type, name: data.short || null, message: data.message, group: data.group || null, description: data.description || null, link: data.link || null, choices: null, visible: true, enabled: true, // 当前值 (未被过滤的) value: null, // 如果用户修改过了则为 true valueChanged: false, error: null, tabId: null, // 原始的 inquirer 提示符对象 raw: data}
onWrite API:
示例 (来自 ESLint 插件):
api.describeConfig({ // ... onWrite: async ({ api, prompts }) => { // 更新 ESLint 规则 const result = {} for (const prompt of prompts) { result[`rules.${prompt.id}`] = await api.getAnswer(prompt.id, JSON.parse) } api.setData('eslint', result) }})
任务是从项目 package.json 文件的 scripts 字段生成的。
因为有 api.describeTask 方法,你可以为任务“增强”额外的信息和钩子:
api.describeTask({ // 用于匹配脚本命令的 RegExp 对象,来选择要被描述的任务 match: /vue-cli-service serve/, description: 'Compiles and hot-reloads for development', // “More info”链接 link: 'https://github.com/vuejs/vue-cli/blob/dev/docs/cli-service.md#serve'})
可以是一个 Material 图标代码或一个自定义的图片 (详见公共静态文件):
api.describeTask({ /* ... */ // 任务图标 icon: 'application_settings'})
如果你没有定义图标,那就展示该插件可能存在的 logo (详见 Logo)。
你可以添加提示符来修改命令参数。它们会展示在一个“参数”模态框中。
Example:
api.describeTask({ // ... // 选填参数 (inquirer 提示符) prompts: [ { name: 'open', type: 'confirm', default: false, description: 'Open browser on server start' }, { name: 'mode', type: 'list', default: 'development', choices: [ { name: 'development', value: 'development' }, { name: 'production', value: 'production' }, { name: 'test', value: 'test' } ], description: 'Specify env mode' } ]})
详见提示符。
有一些钩子是可用的:
例如,你可以将 (上述) 提示符的回答作为一个新参数添加到命令上:
api.describeTask({ // ... // 钩子 // 在这里修改参数 onBeforeRun: async ({ answers, args }) => { // 参数 if (answers.open) args.push('--open') if (answers.mode) args.push('--mode', answers.mode) args.push('--dashboard') }, // 任务运行之后立即执行 onRun: async ({ args, child, cwd }) => { // child: Node 子进程 // cwd: 进程所在目录 }, onExit: async ({ args, child, cwd, code, signal }) => { // code: 退出码 // signal: 可能会被使用的杀进程信号 }})
你可以在任务详情面板中使用 ClientAddon API 展示自定义视图:
api.describeTask({ // ... // 额外的视图 (例如 webpack 仪表盘) // 默认情况下,这里是展示终端输出的 `output` 视图 views: [ { // 唯一 ID id: 'vue-webpack-dashboard-client-addon', // 按钮文字 label: 'Dashboard', // 按钮图标 icon: 'dashboard', // 要加载的动态组件 (详见下述“客户端 addon”章节) component: 'vue-webpack-dashboard' } ], // 展示任务详情时默认选择的视图 (默认是 `output`) defaultView: 'vue-webpack-dashboard-client-addon'})
详见客户端 addon。
你也可以不使用 api.describeTask,而是通过 api.addTask 添加一个 package.json 脚本中没有的全新任务。这些任务只会出现在 cli UI 中。
你需要提供一个 command 选项替代掉 match 选项。
示例:
api.addTask({ // 必填 name: 'inspect', command: 'vue-cli-service inspect', // 选填 // 其余部分类似 `describeTask` 但是没有 `match` 选项 description: '...', link: 'https://github.com/vuejs/vue-cli/...', prompts: [ /* ... */ ], onBeforeRun: () => {}, onRun: () => {}, onExit: () => {}, views: [ /* ... */ ], defaultView: '...'})
警告
command 将会运行一个 Node 上下文。也就是说你可以像在 package.json 脚本中一样调用 Node 的 bin 命令。
提示符对象必须是合法的 inquirer 对象。
不过你也可以添加下列额外的字段 (只会被 UI 使用的可选项):
{ /* ... */ // 用来将提示符按章节分组 group: 'Strongly recommended', // 附加描述 description: 'Enforce attribute naming style in template (`my-prop` or `myProp`)', // “More info”链接 link: 'https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/attribute-hyphenation.md',}
支持的 inquirer 类型有:checkbox、confirm、input、password、list、rawlist。
此外,UI 还支持了仅在这里工作的特殊类型:
{ name: 'open', type: 'confirm', default: false, description: 'Open the app in the browser'}
{ name: 'mode', type: 'list', default: 'development', choices: [ { name: 'Development mode', value: 'development' }, { name: 'Production mode', value: 'production' }, { name: 'Test mode', value: 'test' } ], description: 'Build mode', link: 'https://link-to-docs'}
{ name: 'host', type: 'input', default: '0.0.0.0', description: 'Host for the development server'}
展示多个 switch。
{ name: 'lintOn', message: 'Pick additional lint features:', when: answers => answers.features.includes('linter'), type: 'checkbox', choices: [ { name: 'Lint on save', value: 'save', checked: true }, { name: 'Lint and fix on commit' + (hasGit() ? '' : chalk.red(' (requires Git)')), value: 'commit' } ]}
{ name: 'themeColor', type: 'color', message: 'Theme color', description: 'This is used to change the system UI color around the app', default: '#4DBA87'}
在 vue-cli 插件中,你可能已经有一个 prompts.js 文件,在 (用 CLI 或 UI) 安装该插件的时候询问用户一些问题。你可以向那些提示符对象额外添加只支持 UI 的上述字段,这样的话如果用户使用 UI 的话可以看到更多的信息。
警告
目前,那些不支持的 inquirer 类型不会在 UI 中正常工作。
客户端 addon 是一个动态加载到 cli-ui 中的 JS 包。用于加载自定义组件和路由。
推荐的创建一个客户端 addon 的方式是通过 vue cli 创建一个新项目。你也可以在插件的子目录或不同的 npm 包中这样做。
作为开发依赖安装 @vue/cli-ui。
然后添加一个 vue.config.js 文件并附带以下内容:
const { clientAddonConfig } = require('@vue/cli-ui')module.exports = { ...clientAddonConfig({ id: 'org.vue.webpack.client-addon', // 开发环境端口 (默认值 8042) port: 8042 })}
这个 clientAddonConfig 方法将会生成需要的 vue-cli 配置。除此之外,它会禁用 CSS extraction 并将代码输出到在客户端 addon 目录的 ./dist/index.js。
危险
请确定为 id 设置正确的命名空间,因为它需要跨所有插件保持唯一。我们推荐使用反向域名记号 (reverse domain name notation)。
然后修改 .eslintrc.json 文件以添加一些允许的全局对象:
{ // ... "globals": { "ClientAddonApi": false, "mapSharedData": false, "Vue": false }}
你现在可以在开发环境下运行 serve 脚本,也可以在准备发布时运行 build 脚本。
在客户端 addon 资源中打开 main.js 文件并删除所有代码。
警告
别在客户端 addon 源文件总导入 Vue ,请从浏览器 window 使用全局的 Vue 对象。
这里是一个 main.js 的示例代码:
import VueProgress from 'vue-progress-path'import WebpackDashboard from './components/WebpackDashboard.vue'import TestView from './components/TestView.vue'// 你可以安装额外的 Vue 插件// 使用全局的 'Vue' 变量Vue.use(VueProgress, { defaultShape: 'circle'})// 注册一个自定义组件// (工作原理类似 'Vue.component')ClientAddonApi.component('org.vue.webpack.components.dashboard', WebpackDashboard)// 在 vue-router 中为 /addon/<id> 添加子路由。// 例如,addRoutes('foo', [ { path: '' }, { path: 'bar' } ])// 将会向路由器添加 /addon/foo/ 和 /addon/foo/bar。// 我们在此用 'test-webpack-route' 名称创建一个新的 '/addon/vue-webpack/' 路由ClientAddonApi.addRoutes('org.vue.webpack', [ { path: '', name: 'org.vue.webpack.routes.test', component: TestView }])// 你可以翻译插件组件// (通过使用 vue-i18n) 加载语言文件const locales = require.context('./locales', true, /[a-z0-9]+.json$/i)locales.keys().forEach(key => { const locale = key.match(/([a-z0-9]+)./i)[1] ClientAddonApi.addLocalization(locale, locales(key))})
危险
请确定为 id 设置正确的命名空间,因为它需要跨所有插件保持唯一。我们推荐使用反向域名记号 (reverse domain name notation)。
cli-ui 在 window 作用域内注册了 Vue 和 ClientAddonApi 作为全局变量。
你可以在自己的组件里使用 @vue/ui 和 @vue/cli-ui 所有的组件和 CSS class 以保持样式和体验的一致性。你也可以用内置的 vue-i18n 翻译字符串。
回到 ui.js 文件,使用 api.addClientAddon 方法并带一个指向构建后的文件夹的 require 字符串:
api.addClientAddon({ id: 'org.vue.webpack.client-addon', // 包含构建出来的 JS 文件的文件夹 path: '@vue/cli-ui-addon-webpack/dist'})
这会使用 Node.js 的 require.resolve API 查找文件夹并为从客户端 addon 构建的文件 index.js 启动一个服务器。
或者当开发插件时指定一个 URL (理想中你需要在 Vue 的测试项目的 vue-cli-ui.js 中做这些)
// 用于开发环境// 如果已经在插件中定义过,则会覆写路径api.addClientAddon({ id: 'org.vue.webpack.client-addon', // 使用你之前配置过低同样的端口 url: 'http://localhost:8042/index.js'})
现在你可以在这些视图中使用客户端 addon 了。例如,你可以在一个被描述的任务中指定一个视图:
api.describeTask({ /* ... */ // 额外的视图 (例如 webpack dashboard) // 默认情况下,这是展示终端输出的 'output' 视图 views: [ { // 唯一的 ID id: 'org.vue.webpack.views.dashboard', // 按钮文字 label: 'Dashboard', // 按钮图标 (material-icons) icon: 'dashboard', // 加载的动态组件,会用 ClientAddonApi 进行注册 component: 'org.vue.webpack.components.dashboard' } ], // 展示任务详情时默认选择的视图 (默认情况下就是 output) defaultView: 'org.vue.webpack.views.dashboard'})
这是一个客户端 addon 代码,注册了 `'org.vue.webpack.components.dashboard' 组件 (像我们之前看到的一样):
/* 在 `main.js` 中 */// 导入组件import WebpackDashboard from './components/WebpackDashboard.vue'// 注册自定义组件// (工作原理类似 'Vue.component')ClientAddonApi.component('org.vue.webpack.components.dashboard', WebpackDashboard)
你可以使用 api.addView 方法在标准的“Project plugins”、“Project configuration”和“Project tasks”之下添加一个新的视图:
api.addView({ // 唯一的 id id: 'org.vue.webpack.views.test', // 路由名称 (来自 Vue Router) // 使用 'ClientAddonApi.addRoutes' 方法中相同的名字 (详见之前的客户端 addon 章节) name: 'org.vue.webpack.routes.test', // 按钮图标 (material-icons) icon: 'pets', // 你也可以指定一个自定义图片 (详见之前的公共静态文件章节): // icon: 'http://localhost:4000/_plugin/%40vue%2Fcli-service/webpack-icon.svg', // 按钮的提示文字 tooltip: 'Test view from webpack addon'})
这里是注册了 'org.vue.webpack.routes.test' 的客户端 addon 里的代码 (之前已经见过了):
/* 在 `main.js` 里 */// 导入组件import TestView from './components/TestView.vue'// 在 vue-router 中为 /addon/<id> 添加子路由// 例如,addRoutes('foo', [ { path: '' }, { path: 'bar' } ])// 将为 Vue Router 添加 /addon/foo/ 和 /addon/foo/bar 路由。// 我们这里创建一个新的 '/addon/vue-webpack/' 路由,并命名为 'test-webpack-route'。ClientAddonApi.addRoutes('org.vue.webpack', [ { path: '', name: 'org.vue.webpack.routes.test', component: TestView }])
一种简易的自定义组件之间通过共享的数据互通信息的方式。
例如,webpack 仪表盘在 UI 客户端和 UI 服务端之间通过这个 API 共享了构建的统计信息。
在插件 ui.js (Node.js) 中:
// 设置或更新api.setSharedData('com.my-name.my-variable', 'some-data')// 获取const sharedData = api.getSharedData('com.my-name.my-variable')if (sharedData) { console.log(sharedData.value)}// 移除api.removeSharedData('com.my-name.my-variable')// 侦听变化const watcher = (value, id) => { console.log(value, id)}api.watchSharedData('com.my-name.my-variable', watcher)// 取消侦听api.unwatchSharedData('com.my-name.my-variable', watcher)// 带命名空间的版本const { setSharedData, getSharedData, removeSharedData, watchSharedData, unwatchSharedData} = api.namespace('com.my-name.')
危险
请确定为 id 设置正确的命名空间,因为它需要跨所有插件保持唯一。我们推荐使用反向域名记号 (reverse domain name notation)。
在其自定义组件中:
// Vue 组件export default { // 同步共享的数据 sharedData () { return { // 你可以在模板中使用 `myVariable` myVariable: 'com.my-name.my-variable' // 也可以映射带命名空间的共享数据 ...mapSharedData('com.my-name.', { myVariable2: 'my-variable2' }) } }, // 手动方法 async created () { const value = await this.$getSharedData('com.my-name.my-variable') this.$watchSharedData(`com.my-name.my-variable`, value => { console.log(value) }) await this.$setSharedData('com.my-name.my-variable', 'new-value') }}
如果你使用了 sharedData 选项,共享的数据就可以一个相应的属性被赋值时进行更新。
<template> <VueInput v-model="message"/></template><script>export default { sharedData: { // 将会在服务端同步 'my-message' 共享的数据 message: 'com.my-name.my-message' }}</script>
例如在创建一个设置组件时,这个特性是非常有用的。
插件的 action 就是在 cli-ui (浏览器) 和插件 (Node.js) 直接的调用。
例如,你可能有一个自定义组件里的按钮 (详见客户端 addon),这个按钮会通过这个 API 向服务端调用一些 Node.js 代码。
在插件 (Node.js) 的 ui.js 文件里,你可以从 PluginApi 使用两个方法:
// 调用一个 actionapi.callAction('com.my-name.other-action', { foo: 'bar' }).then(results => { console.log(results)}).catch(errors => { console.error(errors)})
// 监听一个 actionapi.onAction('com.my-name.test-action', params => { console.log('test-action called', params)})
危险
请确定为 id 设置正确的命名空间,因为它需要跨所有插件保持唯一。我们推荐使用反向域名记号 (reverse domain name notation)。
你可以通过 api.namespace 使用带命名空间的版本 (类似共享的数据):
const { onAction, callAction } = api.namespace('com.my-name.')
在客户端 addon 组件 (浏览器) 中,你可以访问 $onPluginActionCalled、$onPluginActionResolved 和 $callPluginAction:
// Vue 组件export default { created () { this.$onPluginActionCalled(action => { // 当 action 被调用时 // 且在运行之前 console.log('called', action) }) this.$onPluginActionResolved(action => { // 当 action 运行完毕之后 console.log('resolved', action) }) }, methods: { testPluginAction () { // 调用一个插件的 action this.$callPluginAction('com.my-name.test-action', { meow: 'meow' }) } }}
IPC 就是进程间通信 (Inter-Process Communication) 的缩写。该系统允许你轻松的从子进程 (例如任务) 发送消息,并且轻量快速。
为了在 webpack 仪表盘 UI 上展示数据,@vue/cli-service 的 serve 和 build 命令会在 --dashboard 参数被传入时向 cli-ui Node.js 服务器发送 IPC 消息。
在进程代码中 (可以是一个 webpack 插件或一个 Node.js 的任务脚本),你可以使用 @vue/cli-shared-utils 中的 IpcMessenger 类:
const { IpcMessenger } = require('@vue/cli-shared-utils')// 创建一个新的 IpcMessenger 实例const ipc = new IpcMessenger()function sendMessage (data) { // 发送一条消息给 cli-ui 服务器 ipc.send({ 'com.my-name.some-data': { type: 'build', value: data } })}function messageHandler (data) { console.log(data)}// 监听消息ipc.on(messageHandler)// 不再监听ipc.off(messageHandler)function cleanup () { // 从 IPC 网络断开连接 ipc.disconnect()}
手动连接:
const ipc = new IpcMessenger({ autoConnect: false})// 这条消息会被放入队列ipc.send({ ... })ipc.connect()
闲时自动断开连接 (在没有任何消息一段时间之后):
const ipc = new IpcMessenger({ disconnectOnIdle: true, idleTimeout: 3000 // 默认值})ipc.send({ ... })setTimeout(() => { console.log(ipc.connected) // false}, 3000)
连接到另一个 IPC 网络:
const ipc = new IpcMessenger({ networkId: 'com.my-name.my-ipc-network'})
在一个 vue-cli 插件的 ui.js 文件中,你可以使用 ipcOn、ipcOff 和 ipcSend 方法:
function onWebpackMessage ({ data: message }) { if (message['com.my-name.some-data']) { console.log(message['com.my-name.some-data']) }}// 监听任何 IPC 消息api.ipcOn(onWebpackMessage)// 不监听任何消息api.ipcOff(onWebpackMessage)// 向所有已连接的 IpcMessenger 实例发送一条消息api.ipcSend({ webpackDashboardMessage: { foo: 'bar' }})
一个插件可以从 UI 服务器本地的 lowdb 数据库保存和加载数据。
// 向本地的数据库存入一个值api.storageSet('com.my-name.an-id', { some: 'value' })// 从本地的数据库取回一个值console.log(api.storageGet('com.my-name.an-id'))// 完整的 lowdb 实例api.db.get('posts') .find({ title: 'low!' }) .assign({ title: 'hi!'}) .write()// 带命名空间的辅助函数const { storageGet, storageSet } = api.namespace('my-plugin.')
危险
请确定为 id 设置正确的命名空间,因为它需要跨所有插件保持唯一。我们推荐使用反向域名记号 (reverse domain name notation)。
你可以基于用户操作系统的通知系统展示通知:
api.notify({ title: 'Some title', message: 'Some message', icon: 'path-to-icon.png'})
这里有一些内建的图标;
你可以用一些文字或进度条来展示进度界面:
api.setProgress({ status: 'Upgrading...', error: null, info: 'Step 2 of 4', progress: 0.4 // 从 0 到 1, -1 表示隐藏进度条})
移除进度界面:
api.removeProgress()
钩子可以用来响应某些 cli-ui 的事件。
当插件在当前项目中第一次被加载时触发。
api.onProjectOpen((project, previousProject) => { // 重置数据})
当插件被重新加载时触发。
api.onPluginReload((project) => { console.log('plugin reloaded')})
当一个配置界面被打开或刷新时触发。
api.onConfigRead(({ config, data, onReadData, tabs, cwd }) => { console.log(config.id)})
当用户在保存界面里保存时触发。
api.onConfigWrite(({ config, data, changedFields, cwd }) => { // ...})
当用户打开一项任务的详情面板时触发。
api.onTaskOpen(({ task, cwd }) => { console.log(task.id)})
当用户运行一项任务时触发。
api.onTaskRun(({ task, args, child, cwd }) => { // ...})
当一项任务退出时触发。不论任务成功或失败它都会触发。
api.onTaskExit(({ task, args, child, signal, code, cwd }) => { // ...})
当用户打开一个视图 (如 'Plugins'、'Configurations' 或 'Tasks') 时触发。
api.onViewOpen(({ view, cwd }) => { console.log(view.id)})
这里的建议是指为用户提议执行 action 的按钮。它们展示在界面的顶栏上。例如我们可以放一个按钮,在应用里没有检测到 Vue Router 包的时候建议将其安装。
api.addSuggestion({ id: 'com.my-name.my-suggestion', type: 'action', // 必填 (未来会加入更多类型) label: 'Add vue-router', // 该消息会展示在一个详情模态框里 message: 'A longer message for the modal', link: 'http://link-to-docs-in-the-modal', // 可选的图片 image: '/_plugin/my-package/screenshot.png', // 当该项建议被用户激活时调用的函数 async handler () { // ... return { // 默认移除这个按钮 keep: false } }})
危险
请确定为 id 设置正确的命名空间,因为它需要跨所有插件保持唯一。我们推荐使用反向域名记号 (reverse domain name notation)。
之后你可以移除这项建议:
api.removeSuggestion('com.my-name.my-suggestion')
你也可以给建议附带 actionLink,当用户激活它时,会换做打开一个页面:
api.addSuggestion({ id: 'com.my-name.my-suggestion', type: 'action', // Required label: 'Add vue-router', // 打开一个新标签 actionLink: 'https://vuejs.org/'})
通常情况下,你会选择适当的上下文用钩子来展示建议:
const ROUTER = 'vue-router-add'api.onViewOpen(({ view }) => { if (view.id === 'vue-project-plugins') { if (!api.hasPlugin('vue-router')) { api.addSuggestion({ id: ROUTER, type: 'action', label: 'org.vue.cli-service.suggestions.vue-router-add.label', message: 'org.vue.cli-service.suggestions.vue-router-add.message', link: 'https://router.vuejs.org/', async handler () { await install(api, 'vue-router') } }) } } else { api.removeSuggestion(ROUTER) }})
在这个例子中,如果 Vue Router 没有安装好,我们只会在插件视图中展示安装 Vue Router 的建议。
注意
addSuggestion 和 removeSuggestion 可以通过 api.namespace() 指定命名空间。
如果项目使用了该插件则返回 true。
api.hasPlugin('eslint')api.hasPlugin('apollo')api.hasPlugin('vue-cli-plugin-apollo')
获取当前工作目录。
api.getCwd()
在当前工程下解析一个文件:
api.resolve('src/main.js')
得出当前打开的工程。
api.getProject()
你可能需要在 cli-ui 内建的 HTTP 服务器上暴露一些静态文件 (通常是为自定义视图指定图标)。
在插件包根目录里可选的放置一个 ui-public 文件夹,这个文件夹里的任何文件都会暴露至 /_plugin/:id/* 的 HTTP 路由。
例如,如果你将 my-logo.png 文件放置到 vue-cli-plugin-hello/ui-public/ 文件夹,那么 cli-ui 加载插件的时候可以通过 /_plugin/vue-cli-plugin-hello/my-logo.png 这个 URL 来访问它。
api.describeConfig({ /* ... */ // 自定义图片 icon: '/_plugin/vue-cli-plugin-hello/my-logo.png'})
请遵循下列简单步骤来为 CLI UI 提交一种其它语言的翻译!
可以参考这里的英文地区文件。
作为示例,参考一份法语的包。
你也可以在插件的根目录的 locales 文件夹放置与 vue-i18n 兼容的地区文件。这样做会在项目打开的时候自动加载,然后你可以使用 $t 在你的组件和 vue-i18n 辅助函数里翻译字符串。同样的 UI API (像 describeTask) 用到的字符串将会进入 vue-i18n,这样你就可以对它们做本地化。
示例 locales 文件夹:
vue-cli-plugin/locales/en.jsonvue-cli-plugin/locales/fr.json
API 的用法示例:
api.describeConfig({ // vue-i18n 路径 description: 'com.my-name.my-plugin.config.foo'})
危险
请确定为 id 设置正确的命名空间,因为它需要跨所有插件保持唯一。我们推荐使用反向域名记号 (reverse domain name notation)。
在组件中使用的示例:
<VueButton>{{ $t('com.my-name.my-plugin.actions.bar') }}</VueButton>
如果你愿意的话,可以使用 ClientAddonApi 在一个客户端 addon 加载地区文件:
// 加载本地文件 (使用 vue-i18n)const locales = require.context('./locales', true, /[a-z0-9]+.json$/i)locales.keys().forEach(key => { const locale = key.match(/([a-z0-9]+)./i)[1] ClientAddonApi.addLocalization(locale, locales(key))})
警告
这份文档是对应 @vue/cli。老版本的 vue-cli 文档请移步这里。
Vue CLI 是一个基于 Vue.js 进行快速开发的完整系统,提供:
Vue CLI 致力于将 Vue 生态中的工具基础标准化。它确保了各种构建工具能够基于智能的默认配置即可平稳衔接,这样你可以专注在撰写应用上,而不必花好几天去纠结配置的问题。与此同时,它也为每个工具提供了调整配置的灵活性,无需 eject。
Vue CLI 有几个独立的部分——如果你看到了我们的源代码,你会发现这个仓库里同时管理了多个单独发布的包。
CLI (@vue/cli) 是一个全局安装的 npm 包,提供了终端里的 vue 命令。它可以通过 vue create 快速创建一个新项目的脚手架,或者直接通过 vue serve 构建新想法的原型。你也可以通过 vue ui 通过一套图形化界面管理你的所有项目。我们会在接下来的指南中逐章节深入介绍。
CLI 服务 (@vue/cli-service) 是一个开发环境依赖。它是一个 npm 包,局部安装在每个 @vue/cli 创建的项目中。
CLI 服务是构建于 webpack 和 webpack-dev-server 之上的。它包含了:
如果你熟悉 create-react-app 的话,@vue/cli-service 实际上大致等价于 react-scripts,尽管功能集合不一样。
CLI 服务章节涵盖了它的具体用法。
CLI 插件是向你的 Vue 项目提供可选功能的 npm 包,例如 Babel/TypeScript 转译、ESLint 集成、单元测试和 end-to-end 测试等。Vue CLI 插件的名字以 @vue/cli-plugin- (内建插件) 或 vue-cli-plugin- (社区插件) 开头,非常容易使用。
当你在项目内部运行 vue-cli-service 命令时,它会自动解析并加载 package.json 中列出的所有 CLI 插件。
插件可以作为项目创建过程的一部分,或在后期加入到项目中。它们也可以被归成一组可复用的 preset。我们会在插件和 preset 章节进行深入讨论。
关于旧版本
如果您已经安装了旧版本的vue-cli(1.x或2.x),您需要先通过npm卸载vue-cli -g或yarn global remove vue-cli卸载它。
节点版本要求
Vue CLI需要 Node.js 8.9或更高版本(推荐8.11.0+)。您可以使用 nvm 或 nvm-windows 在同一台计算机中管理多个Node版本。
可以使用以下任一命令安装这个新的包:
npm install -g @vue/cli# ORyarn global add @vue/cli
安装之后,您就可以在命令行中访问vue命令。您可以通过简单运行vue,看看是否展示出了一份所有可用命令的帮助信息,来验证它是否安装成功。
你还可以用这个命令来检查其版本是否正确:
vue --version
你可以使用 vue serve 和 vue build 命令对单个 *.vue 文件进行快速原型开发,不过这需要先额外安装一个全局的扩展:
npm install -g @vue/cli-service-global
vue serve 的缺点就是它需要安装全局依赖,这使得它在不同机器上的一致性不能得到保证。因此这只适用于快速原型开发。
Usage: serve [options] [entry]在开发环境模式下零配置为 .js 或 .vue 文件启动一个服务器Options: -o, --open 打开浏览器 -c, --copy 将本地 URL 复制到剪切板 -h, --help 输出用法信息
你所需要的仅仅是一个 App.vue 文件:
<template> <h1>Hello!</h1></template>
然后在这个 App.vue 文件所在的目录下运行:
vue serve
vue serve 使用了和 vue create 创建的项目相同的默认设置 (webpack、Babel、PostCSS 和 ESLint)。它会在当前目录自动推导入口文件——入口可以是 main.js、index.js、App.vue 或 app.vue 中的一个。你也可以显式地指定入口文件:
vue serve MyComponent.vue
如果需要,你还可以提供一个 index.html、package.json、安装并使用本地依赖、甚至通过相应的配置文件配置 Babel、PostCSS 和 ESLint。
Usage: build [options] [entry]在生产环境模式下零配置构建一个 .js 或 .vue 文件Options: -t, --target <target> 构建目标 (app | lib | wc | wc-async, 默认值:app) -n, --name <name> 库的名字或 Web Components 组件的名字 (默认值:入口文件名) -d, --dest <dir> 输出目录 (默认值:dist) -h, --help 输出用法信息
你也可以使用 vue build 将目标文件构建成一个生产环境的包并用来部署:
vue build MyComponent.vue
vue build 也提供了将组件构建成为一个库或一个 Web Components 组件的能力。查阅构建目标了解更多。
运行以下命令来创建一个新项目:
vue create hello-world
警告
如果你在 Windows 上通过 minTTY 使用 Git Bash,交互提示符并不工作。你必须通过 winpty vue.cmd create hello-world 启动这个命令。不过,如果你仍想使用 vue create hello-world,则可以通过在 ~/.bashrc 文件中添加以下行来为命令添加别名。 alias vue='winpty vue.cmd' 你需要重新启动 Git Bash 终端会话以使更新后的 bashrc 文件生效。
你会被提示选取一个 preset。你可以选默认的包含了基本的 Babel + ESLint 设置的 preset,也可以选“手动选择特性”来选取需要的特性。
这个默认的设置非常适合快速创建一个新项目的原型,而手动设置则提供了更多的选项,它们是面向生产的项目更加需要的。
如果你决定手动选择特性,在操作提示的最后你可以选择将已选项保存为一个将来可复用的 preset。我们会在下一个章节讨论 preset 和插件。
~/.vuerc
被保存的 preset 将会存在用户的 home 目录下一个名为 .vuerc 的 JSON 文件里。如果你想要修改被保存的 preset / 选项,可以编辑这个文件。
在项目创建的过程中,你也会被提示选择喜欢的包管理器或使用淘宝 npm 镜像源以更快地安装依赖。这些选择也将会存入 ~/.vuerc。
vue create 命令有一些可选项,你可以通过运行以下命令进行探索:
vue create --help
用法:create [options] <app-name>创建一个由 `vue-cli-service` 提供支持的新项目选项: -p, --preset <presetName> 忽略提示符并使用已保存的或远程的预设选项 -d, --default 忽略提示符并使用默认预设选项 -i, --inlinePreset <json> 忽略提示符并使用内联的 JSON 字符串预设选项 -m, --packageManager <command> 在安装依赖时使用指定的 npm 客户端 -r, --registry <url> 在安装依赖时使用指定的 npm registry -g, --git [message] 强制 / 跳过 git 初始化,并可选的指定初始化提交信息 -n, --no-git 跳过 git 初始化 -f, --force 覆写目标目录可能存在的配置 -c, --clone 使用 git clone 获取远程预设选项 -x, --proxy 使用指定的代理创建项目 -b, --bare 创建项目时省略默认组件中的新手指导信息 -h, --help 输出使用帮助信息
你也可以通过 vue ui 命令以图形化界面创建和管理项目:
vue ui
上述命令会打开一个浏览器窗口,并以图形化界面将你引导至项目创建的流程。
Vue CLI >= 3 和旧版使用了相同的 vue 命令,所以 Vue CLI 2 (vue-cli) 被覆盖了。如果你仍然需要使用旧版本的 vue init 功能,你可以全局安装一个桥接工具:
npm install -g @vue/cli-init# `vue init` 的运行效果将会跟 `vue-cli@2.x` 相同vue init webpack my-project
Vue CLI 使用了一套基于插件的架构。如果你查阅一个新创建项目的 package.json,就会发现依赖都是以 @vue/cli-plugin- 开头的。插件可以修改 webpack 的内部配置,也可以向 vue-cli-service 注入命令。在项目创建的过程中,绝大部分列出的特性都是通过插件来实现的。
基于插件的架构使得 Vue CLI 灵活且可扩展。如果你对开发一个插件感兴趣,请翻阅插件开发指南。
提示
你可以通过 vue ui 命令使用 GUI 安装和管理插件。
每个 CLI 插件都会包含一个 (用来创建文件的) 生成器和一个 (用来调整 webpack 核心配置和注入命令的) 运行时插件。当你使用 vue create 来创建一个新项目的时候,有些插件会根据你选择的特性被预安装好。如果你想在一个已经被创建好的项目中安装一个插件,可以使用 vue add 命令:
vue add eslint
提示
vue add 的设计意图是为了安装和调用 Vue CLI 插件。这不意味着替换掉普通的 npm 包。对于这些普通的 npm 包,你仍然需要选用包管理器。
警告
我们推荐在运行 vue add 之前将项目的最新状态提交,因为该命令可能调用插件的文件生成器并很有可能更改你现有的文件。
这个命令将 @vue/eslint 解析为完整的包名 @vue/cli-plugin-eslint,然后从 npm 安装它,调用它的生成器。
# 这个和之前的用法等价vue add cli-plugin-eslint
如果不带 @vue 前缀,该命令会换作解析一个 unscoped 的包。例如以下命令会安装第三方插件 vue-cli-plugin-apollo:
# 安装并调用 vue-cli-plugin-apollovue add apollo
你也可以基于一个指定的 scope 使用第三方插件。例如如果一个插件名为 @foo/vue-cli-plugin-bar,你可以这样添加它:
vue add @foo/bar
你可以向被安装的插件传递生成器选项 (这样做会跳过命令提示):
vue add eslint --config airbnb --lintOn save
如果一个插件已经被安装,你可以使用 vue invoke 命令跳过安装过程,只调用它的生成器。这个命令会接受和 vue add 相同的参数。
提示
如果出于一些原因你的插件列在了该项目之外的其它 package.json 文件里,你可以在自己项目的 package.json 里设置 vuePlugins.resolveFrom 选项指向包含其它 package.json 的文件夹。
例如,如果你有一个 .config/package.json 文件:
{ "vuePlugins": { "resolveFrom": ".config" }}
如果你需要在项目里直接访问插件 API 而不需要创建一个完整的插件,你可以在 package.json 文件中使用 vuePlugins.service 选项:
{ "vuePlugins": { "service": ["my-commands.js"] }}
每个文件都需要暴露一个函数,接受插件 API 作为第一个参数。关于插件 API 的更多信息可以查阅插件开发指南。
你也可以通过 vuePlugins.ui 选项添加像 UI 插件一样工作的文件:
{ "vuePlugins": { "ui": ["my-ui.js"] }}
更多信息请阅读 UI 插件 API。
一个 Vue CLI preset 是一个包含创建新项目所需预定义选项和插件的 JSON 对象,让用户无需在命令提示中选择它们。
在 vue create 过程中保存的 preset 会被放在你的 home 目录下的一个配置文件中 (~/.vuerc)。你可以通过直接编辑这个文件来调整、添加、删除保存好的 preset。
这里有一个 preset 的示例:
{ "useConfigFiles": true, "cssPreprocessor": "sass", "plugins": { "@vue/cli-plugin-babel": {}, "@vue/cli-plugin-eslint": { "config": "airbnb", "lintOn": ["save", "commit"] }, "@vue/cli-plugin-router": {}, "@vue/cli-plugin-vuex": {} }}
Preset 的数据会被插件生成器用来生成相应的项目文件。除了上述这些字段,你也可以为集成工具添加配置:
{ "useConfigFiles": true, "plugins": {...}, "configs": { "vue": {...}, "postcss": {...}, "eslintConfig": {...}, "jest": {...} }}
这些额外的配置将会根据 useConfigFiles 的值被合并到 package.json 或相应的配置文件中。例如,当 "useConfigFiles": true 的时候,configs 的值将会被合并到 vue.config.js 中。
你可以显式地指定用到的插件的版本:
{ "plugins": { "@vue/cli-plugin-eslint": { "version": "^3.0.0", // ... 该插件的其它选项 } }}
注意对于官方插件来说这不是必须的——当被忽略时,CLI 会自动使用 registry 中最新的版本。不过我们推荐为 preset 列出的所有第三方插件提供显式的版本范围。
每个插件在项目创建的过程中都可以注入它自己的命令提示,不过当你使用了一个 preset,这些命令提示就会被跳过,因为 Vue CLI 假设所有的插件选项都已经在 preset 中声明过了。
在有些情况下你可能希望 preset 只声明需要的插件,同时让用户通过插件注入的命令提示来保留一些灵活性。
对于这种场景你可以在插件选项中指定 "prompts": true 来允许注入命令提示:
{ "plugins": { "@vue/cli-plugin-eslint": { // 让用户选取他们自己的 ESLint config "prompts": true } }}
你可以通过发布 git repo 将一个 preset 分享给其他开发者。这个 repo 应该包含以下文件:
发布 repo 后,你就可以在创建项目的时候通过 --preset 选项使用这个远程的 preset 了:
# 从 GitHub repo 使用 presetvue create --preset username/repo my-project
GitLab 和 BitBucket 也是支持的。如果要从私有 repo 获取,请确保使用 --clone 选项:
vue create --preset gitlab:username/repo --clone my-projectvue create --preset bitbucket:username/repo --clone my-project
当开发一个远程 preset 的时候,你必须不厌其烦的向远程 repo 发出 push 进行反复测试。为了简化这个流程,你也可以直接在本地测试 preset。如果 --preset 选项的值是一个相对或绝对文件路径,或是以 .json 结尾,则 Vue CLI 会加载本地的 preset:
# ./my-preset 应当是一个包含 preset.json 的文件夹vue create --preset ./my-preset my-project# 或者,直接使用当前工作目录下的 json 文件:vue create --preset my-preset.json my-project
在一个 Vue CLI 项目中,@vue/cli-service 安装了一个名为 vue-cli-service 的命令。你可以在 npm scripts 中以 vue-cli-service、或者从终端中以 ./node_modules/.bin/vue-cli-service 访问这个命令。
这是你使用默认 preset 的项目的 package.json:
{ "scripts": { "serve": "vue-cli-service serve", "build": "vue-cli-service build" }}
你可以通过 npm 或 Yarn 调用这些 script:
npm run serve# ORyarn serve
如果你可以使用 npx (最新版的 npm 应该已经自带),也可以直接这样调用命令:
npx vue-cli-service serve
提示
你可以通过 vue ui 命令使用 GUI 运行更多的特性脚本。
这里有一个 GUI 的 webpack Analyzer:
用法:vue-cli-service serve [options] [entry]选项: --open 在服务器启动时打开浏览器 --copy 在服务器启动时将 URL 复制到剪切版 --mode 指定环境模式 (默认值:development) --host 指定 host (默认值:0.0.0.0) --port 指定 port (默认值:8080) --https 使用 https (默认值:false)
vue-cli-service serve 命令会启动一个开发服务器 (基于 webpack-dev-server) 并附带开箱即用的模块热重载 (Hot-Module-Replacement)。
除了通过命令行参数,你也可以使用 vue.config.js 里的 devServer 字段配置开发服务器。
命令行参数 [entry] 将被指定为唯一入口,而非额外的追加入口。尝试使用 [entry] 覆盖 config.pages 中的 entry 将可能引发错误。
用法:vue-cli-service build [options] [entry|pattern]选项: --mode 指定环境模式 (默认值:production) --dest 指定输出目录 (默认值:dist) --modern 面向现代浏览器带自动回退地构建应用 --target app | lib | wc | wc-async (默认值:app) --name 库或 Web Components 模式下的名字 (默认值:package.json 中的 "name" 字段或入口文件名) --no-clean 在构建项目之前不清除目标目录 --report 生成 report.html 以帮助分析包内容 --report-json 生成 report.json 以帮助分析包内容 --watch 监听文件变化
vue-cli-service build 会在 dist/ 目录产生一个可用于生产环境的包,带有 JS/CSS/HTML 的压缩,和为更好的缓存而做的自动的 vendor chunk splitting。它的 chunk manifest 会内联在 HTML 里。
这里还有一些有用的命令参数:
用法:vue-cli-service inspect [options] [...paths]选项: --mode 指定环境模式 (默认值:development)
你可以使用 vue-cli-service inspect 来审查一个 Vue CLI 项目的 webpack config。更多细节请查阅审查 webpack config。
有些 CLI 插件会向 vue-cli-service 注入额外的命令。例如 @vue/cli-plugin-eslint 会注入 vue-cli-service lint 命令。你可以运行以下命令查看所有注入的命令:
npx vue-cli-service help
你也可以这样学习每个命令可用的选项:
npx vue-cli-service help [command]
在安装之后,@vue/cli-service 也会安装 yorkie,它会让你在 package.json 的 gitHooks 字段中方便地指定 Git hook:
{ "gitHooks": { "pre-commit": "lint-staged" }, "lint-staged": { "*.{js,vue}": [ "vue-cli-service lint", "git add" ] }}
注意
yorkie fork 自 husky 且并与后者不兼容。
通过 vue create 创建的项目无需额外的配置就已经可以跑起来了。插件的设计也是可以相互共存的,所以绝大多数情况下,你只需要在交互式命令提示中选取需要的功能即可。
不过我们也知道满足每一个需求是不太可能的,而且一个项目的需求也会不断改变。通过 Vue CLI 创建的项目让你无需 eject 就能够配置工具的几乎每个角落。更多细节请查阅配置参考。
你会发现有 package.json 文件里的 browserslist 字段 (或一个单独的 .browserslistrc 文件),指定了项目的目标浏览器的范围。这个值会被 @babel/preset-env 和 Autoprefixer 用来确定需要转译的 JavaScript 特性和需要添加的 CSS 浏览器前缀。
现在查阅这里了解如何指定浏览器范围。
一个默认的 Vue CLI 项目会使用 @vue/babel-preset-app,它通过 @babel/preset-env 和 browserslist 配置来决定项目需要的 polyfill。
默认情况下,它会把 useBuiltIns: 'usage' 传递给 @babel/preset-env,这样它会根据源代码中出现的语言特性自动检测需要的 polyfill。这确保了最终包里 polyfill 数量的最小化。然而,这也意味着如果其中一个依赖需要特殊的 polyfill,默认情况下 Babel 无法将其检测出来。
如果有依赖需要 polyfill,你有几种选择:
更多细节可查阅 @babel-preset/env 文档。
当使用 Vue CLI 来构建一个库或是 Web Component 时,推荐给 @vue/babel-preset-app 传入 useBuiltIns: false 选项。这能够确保你的库或是组件不包含不必要的 polyfills。通常来说,打包 polyfills 应当是最终使用你的库的应用的责任。
有了 Babel 我们可以兼顾所有最新的 ES2015+ 语言特性,但也意味着我们需要交付转译和 polyfill 后的包以支持旧浏览器。这些转译后的包通常都比原生的 ES2015+ 代码会更冗长,运行更慢。现如今绝大多数现代浏览器都已经支持了原生的 ES2015,所以因为要支持更老的浏览器而为它们交付笨重的代码是一种浪费。
Vue CLI 提供了一个“现代模式”帮你解决这个问题。以如下命令为生产环境构建:
vue-cli-service build --modern
Vue CLI 会产生两个应用的版本:一个现代版的包,面向支持 ES modules 的现代浏览器,另一个旧版的包,面向不支持的旧浏览器。
最酷的是这里没有特殊的部署要求。其生成的 HTML 文件会自动使用 Phillip Walton 精彩的博文中讨论到的技术:
对于一个 Hello World 应用来说,现代版的包已经小了 16%。在生产环境下,现代版的包通常都会表现出显著的解析速度和运算速度,从而改善应用的加载性能。
提示
<script type="module"> 需要配合始终开启的 CORS 进行加载。这意味着你的服务器必须返回诸如 Access-Control-Allow-Origin: * 的有效的 CORS 头。如果你想要通过认证来获取脚本,可使将 crossorigin 选项设置为 use-credentials。
同时,现代浏览器使用一段内联脚本来避免 Safari 10 重复加载脚本包,所以如果你在使用一套严格的 CSP,你需要这样显性地允许内联脚本:
Content-Security-Policy: script-src 'self' 'sha256-4RS22DYeB7U14dra4KcQYxmwt5HkOInieXK1NUMB
public/index.html 文件是一个会被 html-webpack-plugin 处理的模板。在构建过程中,资源链接会被自动注入。另外,Vue CLI 也会自动注入 resource hint (preload/prefetch、manifest 和图标链接 (当用到 PWA 插件时) 以及构建过程中处理的 JavaScript 和 CSS 文件的资源链接。
因为 index 文件被用作模板,所以你可以使用 lodash template 语法插入内容:
除了被 html-webpack-plugin 暴露的默认值之外,所有客户端环境变量也可以直接使用。例如,BASE_URL 的用法:
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
更多内容可以查阅:
<link rel="preload"> 是一种 resource hint,用来指定页面加载后很快会被用到的资源,所以在页面加载的过程中,我们希望在浏览器开始主体渲染之前尽早 preload。
默认情况下,一个 Vue CLI 应用会为所有初始化渲染需要的文件自动生成 preload 提示。
这些提示会被 @vue/preload-webpack-plugin 注入,并且可以通过 chainWebpack 的 config.plugin('preload') 进行修改和删除。
<link rel="prefetch"> 是一种 resource hint,用来告诉浏览器在页面加载完成后,利用空闲时间提前获取用户未来可能会访问的内容。
默认情况下,一个 Vue CLI 应用会为所有作为 async chunk 生成的 JavaScript 文件 (通过动态 import() 按需 code splitting 的产物) 自动生成 prefetch 提示。
这些提示会被 @vue/preload-webpack-plugin 注入,并且可以通过 chainWebpack 的 config.plugin('prefetch') 进行修改和删除。
示例:
// vue.config.jsmodule.exports = { chainWebpack: config => { // 移除 prefetch 插件 config.plugins.delete('prefetch') // 或者 // 修改它的选项: config.plugin('prefetch').tap(options => { options[0].fileBlacklist = options[0].fileBlacklist || [] options[0].fileBlacklist.push(/myasyncRoute(.)+?.js$/) return options }) }}
当 prefetch 插件被禁用时,你可以通过 webpack 的内联注释手动选定要提前获取的代码区块:
import(/* webpackPrefetch: true */ './someAsyncComponent.vue')
webpack 的运行时会在父级区块被加载之后注入 prefetch 链接。
提示
Prefetch 链接将会消耗带宽。如果你的应用很大且有很多 async chunk,而用户主要使用的是对带宽较敏感的移动端,那么你可能需要关掉 prefetch 链接并手动选择要提前获取的代码区块。
当基于已有的后端使用 Vue CLI 时,你可能不需要生成 index.html,这样生成的资源可以用于一个服务端渲染的页面。这时可以向 vue.config.js 加入下列代码:
// vue.config.jsmodule.exports = { // 去掉文件名中的 hash filenameHashing: false, // 删除 HTML 相关的 webpack 插件 chainWebpack: config => { config.plugins.delete('html') config.plugins.delete('preload') config.plugins.delete('prefetch') }}
然而这样做并不是很推荐,因为:
你应该考虑换用 indexPath 选项将生成的 HTML 用作一个服务端框架的视图模板。
不是每个应用都需要是一个单页应用。Vue CLI 支持使用 vue.config.js 中的 pages 选项构建一个多页面的应用。构建好的应用将会在不同的入口之间高效共享通用的 chunk 以获得最佳的加载性能。
静态资源可以通过两种方式进行处理:
当你在 JavaScript、CSS 或 *.vue 文件中使用相对路径 (必须以 . 开头) 引用一个静态资源时,该资源将会被包含进入 webpack 的依赖图中。在其编译过程中,所有诸如 <img src="...">、background: url(...) 和 CSS @import 的资源 URL 都会被解析为一个模块依赖。
例如,url(./image.png) 会被翻译为 require('./image.png'),而:
<img src="./image.png">
将会被编译到:
h('img', { attrs: { src: require('./image.png') }})
在其内部,我们通过 file-loader 用版本哈希值和正确的公共基础路径来决定最终的文件路径,再用 url-loader 将小于 4kb 的资源内联,以减少 HTTP 请求的数量。
你可以通过 chainWebpack 调整内联文件的大小限制。例如,下列代码会将其限制设置为 10kb:
// vue.config.jsmodule.exports = { chainWebpack: config => { config.module .rule('images') .use('url-loader') .loader('url-loader') .tap(options => Object.assign(options, { limit: 10240 })) }}
任何放置在 public 文件夹的静态资源都会被简单的复制,而不经过 webpack。你需要通过绝对路径来引用它们。
注意我们推荐将资源作为你的模块依赖图的一部分导入,这样它们会通过 webpack 的处理并获得如下好处:
public 目录提供的是一个应急手段,当你通过绝对路径引用它时,留意应用将会部署到哪里。如果你的应用没有部署在域名的根部,那么你需要为你的 URL 配置 publicPath 前缀:
Vue CLI 项目天生支持 PostCSS、CSS Modules 和包含 Sass、Less、Stylus 在内的预处理器。
所有编译后的 CSS 都会通过 css-loader 来解析其中的 url() 引用,并将这些引用作为模块请求来处理。这意味着你可以根据本地的文件结构用相对路径来引用静态资源。另外要注意的是如果你想要引用一个 npm 依赖中的文件,或是想要用 webpack alias,则需要在路径前加上 ~ 的前缀来避免歧义。更多细节请参考处理静态资源。
你可以在创建项目的时候选择预处理器 (Sass/Less/Stylus)。如果当时没有选好,内置的 webpack 仍然会被预配置为可以完成所有的处理。你也可以手动安装相应的 webpack loader:
# Sassnpm install -D sass-loader node-sass# Lessnpm install -D less-loader less# Stylusnpm install -D stylus-loader stylus
然后你就可以导入相应的文件类型,或在 *.vue 文件中这样来使用:
<style lang="scss">$color: red;</style>
如果你想自动化导入文件 (用于颜色、变量、mixin……),你可以使用 style-resources-loader。这里有一个关于 Stylus 的在每个单文件组件和 Stylus 文件中导入 ./src/styles/imports.styl 的例子:
// vue.config.jsconst path = require('path')module.exports = { chainWebpack: config => { const types = ['vue-modules', 'vue', 'normal-modules', 'normal'] types.forEach(type => addStyleResource(config.module.rule('stylus').oneOf(type))) },}function addStyleResource (rule) { rule.use('style-resource') .loader('style-resources-loader') .options({ patterns: [ path.resolve(__dirname, './src/styles/imports.styl'), ], })}
你也可以选择使用 vue-cli-plugin-style-resources-loader。
Vue CLI 内部使用了 PostCSS。
你可以通过 .postcssrc 或任何 postcss-load-config 支持的配置源来配置 PostCSS。也可以通过 vue.config.js 中的 css.loaderOptions.postcss 配置 postcss-loader。
我们默认开启了 autoprefixer。如果要配置目标浏览器,可使用 package.json 的 browserslist 字段。
关于 CSS 中浏览器前缀规则的注意事项
在生产环境构建中,Vue CLI 会优化 CSS 并基于目标浏览器抛弃不必要的浏览器前缀规则。因为默认开启了 autoprefixer,你只使用无前缀的 CSS 规则即可。
你可以通过 <style module> 以开箱即用的方式在 *.vue 文件中使用 CSS Modules。
如果想在 JavaScript 中作为 CSS Modules 导入 CSS 或其它预处理文件,该文件应该以 .module.(css|less|sass|scss|styl) 结尾:
import styles from './foo.module.css'// 所有支持的预处理器都一样工作import sassStyles from './foo.module.scss'
如果你想去掉文件名中的 .module,可以设置 vue.config.js 中的 css.requireModuleExtension 为 false:
// vue.config.jsmodule.exports = { css: { requireModuleExtension: false }}
如果你希望自定义生成的 CSS Modules 模块的类名,可以通过 vue.config.js 中的 css.loaderOptions.css 选项来实现。所有的 css-loader 选项在这里都是支持的,例如 localIdentName 和 camelCase:
// vue.config.jsmodule.exports = { css: { loaderOptions: { css: { // 注意:以下配置在 Vue CLI v4 与 v3 之间存在差异。 // Vue CLI v3 用户可参考 css-loader v1 文档 // https://github.com/webpack-contrib/css-loader/tree/v1.0.1 modules: { localIdentName: '[name]-[hash]' }, localsConvention: 'camelCaseOnly' } } }}
有的时候你想要向 webpack 的预处理器 loader 传递选项。你可以使用 vue.config.js 中的 css.loaderOptions 选项。比如你可以这样向所有 Sass/Less 样式传入共享的全局变量:
// vue.config.jsmodule.exports = { css: { loaderOptions: { // 给 sass-loader 传递选项 sass: { // @/ 是 src/ 的别名 // 所以这里假设你有 `src/variables.sass` 这个文件 // 注意:在 sass-loader v7 中,这个选项名是 "data" prependData: `@import "~@/variables.sass"` }, // 默认情况下 `sass` 选项会同时对 `sass` 和 `scss` 语法同时生效 // 因为 `scss` 语法在内部也是由 sass-loader 处理的 // 但是在配置 `data` 选项的时候 // `scss` 语法会要求语句结尾必须有分号,`sass` 则要求必须没有分号 // 在这种情况下,我们可以使用 `scss` 选项,对 `scss` 语法进行单独配置 scss: { prependData: `@import "~@/variables.scss";` }, // 给 less-loader 传递 Less.js 相关选项 less:{ // http://lesscss.org/usage/#less-options-strict-units `Global Variables` // `primary` is global variables fields name globalVars: { primary: '#fff' } } } }}
Loader 可以通过 loaderOptions 配置,包括:
提示
这样做比使用 chainWebpack 手动指定 loader 更推荐,因为这些选项需要应用在使用了相应 loader 的多个地方。
调整 webpack 配置最简单的方式就是在 vue.config.js 中的 configureWebpack 选项提供一个对象:
// vue.config.jsmodule.exports = { configureWebpack: { plugins: [ new MyAwesomeWebpackPlugin() ] }}
该对象将会被 webpack-merge 合并入最终的 webpack 配置。
警告
有些 webpack 选项是基于 vue.config.js 中的值设置的,所以不能直接修改。例如你应该修改 vue.config.js 中的 outputDir 选项而不是修改 output.path;你应该修改 vue.config.js 中的 publicPath 选项而不是修改 output.publicPath。这样做是因为 vue.config.js 中的值会被用在配置里的多个地方,以确保所有的部分都能正常工作在一起。
如果你需要基于环境有条件地配置行为,或者想要直接修改配置,那就换成一个函数 (该函数会在环境变量被设置之后懒执行)。该方法的第一个参数会收到已经解析好的配置。在函数内,你可以直接修改配置,或者返回一个将会被合并的对象:
// vue.config.jsmodule.exports = { configureWebpack: config => { if (process.env.NODE_ENV === 'production') { // 为生产环境修改配置... } else { // 为开发环境修改配置... } }}
Vue CLI 内部的 webpack 配置是通过 webpack-chain 维护的。这个库提供了一个 webpack 原始配置的上层抽象,使其可以定义具名的 loader 规则和具名插件,并有机会在后期进入这些规则并对它们的选项进行修改。
它允许我们更细粒度的控制其内部配置。接下来有一些常见的在 vue.config.js 中的 chainWebpack 修改的例子。
提示
当你打算链式访问特定的 loader 时,vue inspect 会非常有帮助。
// vue.config.jsmodule.exports = { chainWebpack: config => { config.module .rule('vue') .use('vue-loader') .loader('vue-loader') .tap(options => { // 修改它的选项... return options }) }}
提示
对于 CSS 相关 loader 来说,我们推荐使用 css.loaderOptions 而不是直接链式指定 loader。这是因为每种 CSS 文件类型都有多个规则,而 css.loaderOptions 可以确保你通过一个地方影响所有的规则。
// vue.config.jsmodule.exports = { chainWebpack: config => { // GraphQL Loader config.module .rule('graphql') .test(/.graphql$/) .use('graphql-tag/loader') .loader('graphql-tag/loader') .end() // 你还可以再添加一个 loader .use('other-loader') .loader('other-loader') .end() }}
如果你想要替换一个已有的基础 loader,例如为内联的 SVG 文件使用 vue-svg-loader 而不是加载这个文件:
// vue.config.jsmodule.exports = { chainWebpack: config => { const svgRule = config.module.rule('svg') // 清除已有的所有 loader。 // 如果你不这样做,接下来的 loader 会附加在该规则现有的 loader 之后。 svgRule.uses.clear() // 添加要替换的 loader svgRule .use('vue-svg-loader') .loader('vue-svg-loader') }}
// vue.config.jsmodule.exports = { chainWebpack: config => { config .plugin('html') .tap(args => { return [/* 传递给 html-webpack-plugin's 构造函数的新参数 */] }) }}
你需要熟悉 webpack-chain 的 API 并阅读一些源码以便了解如何最大程度利用好这个选项,但是比起直接修改 webpack 配置,它的表达能力更强,也更为安全。
比方说你想要将 index.html 默认的路径从 /Users/username/proj/public/index.html 改为 /Users/username/proj/app/templates/index.html。通过参考 html-webpack-plugin 你能看到一个可以传入的选项列表。我们可以在下列配置中传入一个新的模板路径来改变它:
// vue.config.jsmodule.exports = { chainWebpack: config => { config .plugin('html') .tap(args => { args[0].template = '/Users/username/proj/app/templates/index.html' return args }) }}
你可以通过接下来要讨论的工具 vue inspect 来确认变更。
因为 @vue/cli-service 对 webpack 配置进行了抽象,所以理解配置中包含的东西会比较困难,尤其是当你打算自行对其调整的时候。
vue-cli-service 暴露了 inspect 命令用于审查解析好的 webpack 配置。那个全局的 vue 可执行程序同样提供了 inspect 命令,这个命令只是简单的把 vue-cli-service inspect 代理到了你的项目中。
该命令会将解析出来的 webpack 配置、包括链式访问规则和插件的提示打印到 stdout。
你可以将其输出重定向到一个文件以便进行查阅:
vue inspect > output.js
注意它输出的并不是一个有效的 webpack 配置文件,而是一个用于审查的被序列化的格式。
你也可以通过指定一个路径来审查配置的一小部分:
# 只审查第一条规则vue inspect module.rules.0
或者指向一个规则或插件的名字:
vue inspect --rule vuevue inspect --plugin html
最后,你可以列出所有规则和插件的名字:
vue inspect --rulesvue inspect --plugins
有些外部工具可能需要通过一个文件访问解析好的 webpack 配置,比如那些需要提供 webpack 配置路径的 IDE 或 CLI。在这种情况下你可以使用如下路径:
<projectRoot>/node_modules/@vue/cli-service/webpack.config.js
该文件会动态解析并输出 vue-cli-service 命令中使用的相同的 webpack 配置,包括那些来自插件甚至是你自定义的配置。
你可以替换你的项目根目录中的下列文件来指定环境变量:
.env # 在所有的环境中被载入.env.local # 在所有的环境中被载入,但会被 git 忽略.env.[mode] # 只在指定的模式中被载入.env.[mode].local # 只在指定的模式中被载入,但会被 git 忽略
一个环境文件只包含环境变量的“键=值”对:
FOO=barVUE_APP_SECRET=secret
被载入的变量将会对 vue-cli-service 的所有命令、插件和依赖可用。
环境加载属性
为一个特定模式准备的环境文件的 (例如 .env.production) 将会比一般的环境文件 (例如 .env) 拥有更高的优先级。
此外,Vue CLI 启动时已经存在的环境变量拥有最高优先级,并不会被 .env 文件覆写。
NODE_ENV
如果在环境中有默认的 NODE_ENV,你应该移除它或在运行 vue-cli-service 命令的时候明确地设置 NODE_ENV。
模式是 Vue CLI 项目中一个重要的概念。默认情况下,一个 Vue CLI 项目有三个模式:
注意模式不同于 NODE_ENV,一个模式可以包含多个环境变量。也就是说,每个模式都会将 NODE_ENV 的值设置为模式的名称——比如在 development 模式下 NODE_ENV 的值会被设置为 "development"。
你可以通过为 .env 文件增加后缀来设置某个模式下特有的环境变量。比如,如果你在项目根目录创建一个名为 .env.development 的文件,那么在这个文件里声明过的变量就只会在 development 模式下被载入。
你可以通过传递 --mode 选项参数为命令行覆写默认的模式。例如,如果你想要在构建命令中使用开发环境变量,请在你的 package.json 脚本中加入:
"dev-build": "vue-cli-service build --mode development",
假设我们有一个应用包含以下 .env 文件:
VUE_APP_TITLE=My App
和 .env.staging 文件:
NODE_ENV=productionVUE_APP_TITLE=My App (staging)
这两种情况下,根据 NODE_ENV,构建出的应用都是生产环境应用,但是在 staging 版本中,process.env.VUE_APP_TITLE 被覆写成了另一个值。
只有以 VUE_APP_ 开头的变量会被 webpack.DefinePlugin 静态嵌入到客户端侧的包中。你可以在应用的代码中这样访问它们:
console.log(process.env.VUE_APP_SECRET)
在构建过程中,process.env.VUE_APP_SECRET 将会被相应的值所取代。在 VUE_APP_SECRET=secret 的情况下,它会被替换为 "secret"。
除了 VUE_APP_* 变量之外,在你的应用代码中始终可用的还有两个特殊的变量:
所有解析出来的环境变量都可以在 public/index.html 中以 HTML 插值中介绍的方式使用。
提示
你可以在 vue.config.js 文件中计算环境变量。它们仍然需要以 VUE_APP_ 前缀开头。这可以用于版本信息 process.env.VUE_APP_VERSION = require('./package.json').version。
有的时候你可能有一些不应该提交到代码仓库中的变量,尤其是当你的项目托管在公共仓库时。这种情况下你应该使用一个 .env.local 文件取而代之。本地环境文件默认会被忽略,且出现在 .gitignore 中。
.local 也可以加在指定模式的环境文件上,比如 .env.development.local 将会在 development 模式下被载入,且被 git 忽略。
当你运行 vue-cli-service build 时,你可以通过 --target 选项指定不同的构建目标。它允许你将相同的源代码根据不同的用例生成不同的构建。
应用模式是默认的模式。在这个模式中:
关于 IE 兼容性的提醒
在库模式中,项目的 publicPath 是根据主文件的加载路径动态设置的(用以支持动态的资源加载能力)。但是这个功能用到了 document.currentScript,而 IE 浏览器并不支持这一特性。所以如果网站需要支持 IE 的话,建议使用库之前先在页面上引入 current-script-polyfill。
注意对 Vue 的依赖
在库模式中,Vue 是外置的。这意味着包中不会有 Vue,即便你在代码中导入了 Vue。如果这个库会通过一个打包器使用,它将尝试通过打包器以依赖的方式加载 Vue;否则就会回退到一个全局的 Vue 变量。
要避免此行为,可以在build命令中添加--inline-vue标志。
vue-cli-service build --target lib --inline-vue
你可以通过下面的命令将一个单独的入口构建为一个库:
vue-cli-service build --target lib --name myLib [entry]
File Size Gzippeddist/myLib.umd.min.js 13.28 kb 8.42 kbdist/myLib.umd.js 20.95 kb 10.22 kbdist/myLib.common.js 20.57 kb 10.09 kbdist/myLib.css 0.33 kb 0.23 kb
这个入口可以是一个 .js 或一个 .vue 文件。如果没有指定入口,则会使用 src/App.vue。
构建一个库会输出:
警告
如果你在开发一个库或多项目仓库 (monorepo),请注意导入 CSS 是具有副作用的。请确保在 package.json 中移除 "sideEffects": false,否则 CSS 代码块会在生产环境构建时被 webpack 丢掉。
当使用一个 .vue 文件作为入口时,你的库会直接暴露这个 Vue 组件本身,因为组件始终是默认导出的内容。
然而,当你使用一个 .js 或 .ts 文件作为入口时,它可能会包含具名导出,所以库会暴露为一个模块。也就是说你的库必须在 UMD 构建中通过 window.yourLib.default 访问,或在 CommonJS 构建中通过 const myLib = require('mylib').default 访问。如果你没有任何具名导出并希望直接暴露默认导出,你可以在 vue.config.js 中使用以下 webpack 配置:
module.exports = { configureWebpack: { output: { libraryExport: 'default' } }}
兼容性提示
Web Components 模式不支持 IE11 及更低版本。更多细节
注意对 Vue 的依赖
在 Web Components 模式中,Vue 是外置的。这意味着包中不会有 Vue,即便你在代码中导入了 Vue。这里的包会假设在页面中已经有一个可用的全局变量 Vue。
你可以通过下面的命令将一个单独的入口构建为一个 Web Components 组件:
vue-cli-service build --target wc --name my-element [entry]
注意这里的入口应该是一个 *.vue 文件。Vue CLI 将会把这个组件自动包裹并注册为 Web Components 组件,无需在 main.js 里自行注册。也可以在开发时把 main.js 作为 demo app 单独使用。
该构建将会产生一个单独的 JavaScript 文件 (及其压缩后的版本) 将所有的东西都内联起来。当这个脚本被引入网页时,会注册自定义组件 <my-element>,其使用 @vue/web-component-wrapper 包裹了目标的 Vue 组件。这个包裹器会自动代理属性、特性、事件和插槽。请查阅 @vue/web-component-wrapper 的文档了解更多细节。
注意这个包依赖了在页面上全局可用的 Vue。
这个模式允许你的组件的使用者以一个普通 DOM 元素的方式使用这个 Vue 组件:
<script src="https://unpkg.com/vue" rel="external nofollow" rel="external nofollow" ></script><script src="path/to/my-element.js"></script><!-- 可在普通 HTML 中或者其它任何框架中使用 --><my-element></my-element>
当你构建一个 Web Components 组件包的时候,你也可以使用一个 glob 表达式作为入口指定多个组件目标:
vue-cli-service build --target wc --name foo 'src/components/*.vue'
当你构建多个 web component 时,--name 将会用于设置前缀,同时自定义元素的名称会由组件的文件名推导得出。比如一个名为 HelloWorld.vue 的组件携带 --name foo 将会生成的自定义元素名为 <foo-hello-world>。
当指定多个 Web Components 组件作为目标时,这个包可能会变得非常大,并且用户可能只想使用你的包中注册的一部分组件。这时异步 Web Components 模式会生成一个 code-split 的包,带一个只提供所有组件共享的运行时,并预先注册所有的自定义组件小入口文件。一个组件真正的实现只会在页面中用到自定义元素相应的一个实例时按需获取:
vue-cli-service build --target wc-async --name foo 'src/components/*.vue'
File Size Gzippeddist/foo.0.min.js 12.80 kb 8.09 kbdist/foo.min.js 7.45 kb 3.17 kbdist/foo.1.min.js 2.91 kb 1.02 kbdist/foo.js 22.51 kb 6.67 kbdist/foo.0.js 17.27 kb 8.83 kbdist/foo.1.js 5.24 kb 1.64 kb
现在用户在该页面上只需要引入 Vue 和这个入口文件即可:
<script src="https://unpkg.com/vue" rel="external nofollow" rel="external nofollow" ></script><script src="path/to/foo.min.js"></script><!-- foo-one 的实现的 chunk 会在用到的时候自动获取 --><foo-one></foo-one>
如果你用 Vue CLI 处理静态资源并和后端框架一起作为部署的一部分,那么你需要的仅仅是确保 Vue CLI 生成的构建文件在正确的位置,并遵循后端框架的发布方式即可。
如果你独立于后端部署前端应用——也就是说后端暴露一个前端可访问的 API,然后前端实际上是纯静态应用。那么你可以将 dist 目录里构建的内容部署到任何静态文件服务器中,但要确保正确的 publicPath。
dist 目录需要启动一个 HTTP 服务器来访问 (除非你已经将 publicPath 配置为了一个相对的值),所以以 file:// 协议直接打开 dist/index.html 是不会工作的。在本地预览生产环境构建最简单的方式就是使用一个 Node.js 静态文件服务器,例如 serve:
npm install -g serve# -s 参数的意思是将其架设在 Single-Page Application 模式下# 这个模式会处理即将提到的路由问题serve -s dist
如果你在 history 模式下使用 Vue Router,是无法搭配简单的静态文件服务器的。例如,如果你使用 Vue Router 为 /todos/42/ 定义了一个路由,开发服务器已经配置了相应的 localhost:3000/todos/42 响应,但是一个为生产环境构建架设的简单的静态服务器会却会返回 404。
为了解决这个问题,你需要配置生产环境服务器,将任何没有匹配到静态文件的请求回退到 index.html。Vue Router 的文档提供了常用服务器配置指引。
如果前端静态内容是部署在与后端 API 不同的域名上,你需要适当地配置 CORS。
如果你使用了 PWA 插件,那么应用必须架设在 HTTPS 上,这样 Service Worker 才能被正确注册。
根据 GitLab Pages 文档的描述,所有的配置都在根目录中的.gitlab-ci.yml 文件中。下面的范例是一个很好的入门:
# .gitlab-ci.yml 文件应放在你仓库的根目录下 pages: # 必须定义一个名为 pages 的 job image: node:latest stage: deploy script: - npm ci - npm run build - mv public public-vue # GitLab Pages 的钩子设置在 public 文件夹 - mv dist public # 重命名 dist 文件夹 (npm run build 之后的输出位置) artifacts: paths: - public # artifact path 一定要在 /public , 这样 GitLab Pages 才能获取 only: - master
通常, 你的静态页面将托管在 https://yourUserName.gitlab.io/yourProjectName 上, 所以你可以创建一个 initial vue.config.js 文件去 更新 BASE_URL 要匹配的值 :
// vue.config.js 位于仓库的根目录下// 确保用 GitLab 项目的名称替换了 `YourProjectName`module.exports = { publicPath: process.env.NODE_ENV === 'production' ? '/yourProjectName/' : '/'}
请阅读在 GitLab Pages domains 的文档来学习更多关于页面部署 URL 的信息。注意,你也可以使用自定义域名。
在推送到仓库之前提交 .gitlab-ci.yml 和 vue.config.js 文件。GitLab CI 的管道将会被触发: 当成功时候, 到 Settings > Pages 查看关于网站的链接。
也可以查看 vue-cli-plugin-netlify-lambda。
如果使用 Vue Router 的 history 模式,你需要在 /public 目录下创建一个 _redirects 文件:
# 单页应用的 Netlify 设置/* /index.html 200
详细信息请查看 Netlify 重定向文档。
Render 提供带有全托管 SSL,全球 CDN 和 GitHub 持续自动部署的免费静态站点托管服务。
大功告成!构建结束时你的应用便会在你的 Render URL 上线。
如果使用 Vue Router 的 history 模式,你需要在站点的 Redirects/Rewrites 设置中添加以下改写规则:
详细信息请查看 Render 的重定向和改写及自定义域名文档。
创建一个新的 Firebase 项目 Firebase console。 请参考文档。
确保已经全局安装了 firebase-tools :
npm install -g firebase-tools
在项目的根目录下, 用以下命令初始化 firebase :
firebase init
Firebase 将会询问有关初始化项目的一些问题。
// firebase.json{ "hosting": { "public": "dist" }}
// firebase.json{ "hosting": { "rewrites": [ { "source": "**", "destination": "/index.html" } ] }}
执行 npm run build 去构建项目。
在 Firebase Hosting 部署项目,执行以下命令 :
firebase deploy --only hosting
如果需要在部署的项目中使用的其他 Firebase CLI 功能, 执行 firebase deploy 去掉 --only 参数。
现在可以到 https://<YOUR-PROJECT-ID>.firebaseapp.com 访问你的项目了。
请参考 Firebase 文档 来获取更多细节。
ZEIT Now 是一个网站和无服务器 (Serverless) API 云平台,你可以使用你的个人域名 (或是免费的 .now.sh URL) 部署你的 Vue 项目。
要使用 npm 安装其命令行界面,运行以下命令:
npm install -g now
在项目根目录运行以下命令部署你的应用:
now
此外,你还可以使用他们的 GitHub 或 GitLab 集成服务。
大功告成!
你的站点会开始部署,你将获得一个形如 https://vue.now-examples.now.sh/ 的链接。
开箱即用地,请求会被自动改写到 index.html (除了自定义的静态文件) 并带有合适的缓存请求头。你可以改写这些规则。
未完成 | 欢迎参与贡献。
详细信息:https://gist.github.com/hone/24b06869b4c1eca701f9
要使用 Surge 进行部署,步骤非常简单。
首先,通过运行 npm run build 来构建项目。如果还没有安装 Surge 的命令行工具,可以通过运行命令来执行此操作:
npm install --global surge
然后 cd 进入项目的 dist/ 文件夹,然后运行 surge 并按照屏幕提示操作 。如果是第一次使用 Surge,它会要求设置电子邮件和密码。确认项目文件夹以及输入首选域来查看正在部署的项目,如下所示。
project: /Users/user/Documents/myawesomeproject/dist/ domain: myawesomeproject.surge.sh upload: [====================] 100% eta: 0.0s (31 files, 494256 bytes) CDN: [====================] 100% IP: **.**.***.*** Success! - Published to myawesomeproject.surge.sh
通过访问 myawesomeproject.surge.sh 来确保你的项目已经成功的用 Surge 发布,有关自定义域名等更多设置详细信息,可以到 Surge's help page 查看。
在 Docker 容器中使用 Nginx 部署你的应用。
有些针对 @vue/cli 的全局配置,例如你惯用的包管理器和你本地保存的 preset,都保存在 home 目录下一个名叫 .vuerc 的 JSON 文件。你可以用编辑器直接编辑这个文件来更改已保存的选项。
你也可以使用 vue config 命令来审查或修改全局的 CLI 配置。
请查阅指南中的浏览器兼容性章节。
vue.config.js 是一个可选的配置文件,如果项目的 (和 package.json 同级的) 根目录中存在这个文件,那么它会被 @vue/cli-service 自动加载。你也可以使用 package.json 中的 vue 字段,但是注意这种写法需要你严格遵照 JSON 的格式来写。
这个文件应该导出一个包含了选项的对象:
// vue.config.jsmodule.exports = { // 选项...}
从 Vue CLI 3.3 起已弃用,请使用publicPath。
module.exports = { pages: { index: { // page 的入口 entry: 'src/index/main.js', // 模板来源 template: 'public/index.html', // 在 dist/index.html 的输出 filename: 'index.html', // 当使用 title 选项时, // template 中的 title 标签需要是 <title><%= htmlWebpackPlugin.options.title %></title> title: 'Index Page', // 在这个页面中包含的块,默认情况下会包含 // 提取出来的通用 chunk 和 vendor chunk。 chunks: ['chunk-vendors', 'chunk-common', 'index'] }, // 当使用只有入口的字符串格式时, // 模板会被推导为 `public/subpage.html` // 并且如果找不到的话,就回退到 `public/index.html`。 // 输出文件名会被推导为 `subpage.html`。 subpage: 'src/subpage/main.js' }}
从 v4 起已弃用,请使用css.requireModuleExtension。 在 v3 中,这个选项含义与 css.requireModuleExtension 相反。
Babel 可以通过 babel.config.js 进行配置。
提示
Vue CLI 使用了 Babel 7 中的新配置格式 babel.config.js。和 .babelrc 或 package.json 中的 babel 字段不同,这个配置文件不会使用基于文件位置的方案,而是会一致地运用到项目根目录以下的所有文件,包括 node_modules 内部的依赖。我们推荐在 Vue CLI 项目中始终使用 babel.config.js 取代其它格式。
所有的 Vue CLI 应用都使用 @vue/babel-preset-app,它包含了 babel-preset-env、JSX 支持以及为最小化包体积优化过的配置。通过它的文档可以查阅到更多细节和 preset 选项。
同时查阅指南中的 Polyfill 章节。
ESLint 可以通过 .eslintrc 或 package.json 中的 eslintConfig 字段来配置。
更多细节可查阅 @vue/cli-plugin-eslint。
TypeScript 可以通过 tsconfig.json 来配置。
更多细节可查阅 @vue/cli-plugin-typescript。
更多细节可查阅 @vue/cli-plugin-unit-jest。
更多细节可查阅 @vue/cli-plugin-unit-mocha。
更多细节可查阅 @vue/cli-plugin-e2e-cypress。
更多细节可查阅 @vue/cli-plugin-e2e-nightwatch。
系统里有两个主要的部分:
两者皆应用了基于插件的架构。
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 字段。这会确保:
你的插件在其 UI 中使用时,可以展示不同的附加信息,使其更容易被发现和辨认。
你可以在将要发布到 npm 上的目录的根上放置一个 logo.png。它会展示在以下几个地方:
这个 logo 应该是一个不透明的正方形图 (最好 84x84).
为了提升你的插件在搜索时的可见度,请将描述插件的关键字放到插件的 package.json 文件的 description 字段。
示例:
{ "name": "vue-cli-plugin-apollo", "version": "0.7.7", "description": "vue cli plugin to add Apollo and GraphQL"}
你应该将插件网站的 URL 或仓库添加添加到 homepage 或 repository 字段,这样“More Info”按钮就会在你的插件描述中展示出来。
{ "repository": { "type": "git", "url": "git+https://github.com/Akryum/vue-cli-plugin-apollo.git" }, "homepage": "https://github.com/Akryum/vue-cli-plugin-apollo#readme"}
这个 cli-ui 暴露一个 API,允许增强项目的配置和任务,也可以分享数据和在进程间进行通信。
在每个安装好的 vue-cli 插件里,cli-ui 都会尝试从其插件的根目录加载一个可选的 ui.js 文件。注意你也可以使用文件夹 (例如 ui/index.js)。
该文件应该导出一个函数,函数会以 API 对象作为第一个参数:
module.exports = api => { // 在这里使用 API...}
警告
当试图在“项目插件 (Project plugins)”中获取插件列表时,这些文件将会被重新加载。点击 UI 左侧边栏导航“项目插件 (Project plugins)”按钮可以应用更改。
这里是一个使用 UI API 的 vue-cli 插件的文件夹结构示例:
- vue-cli-plugin-test - package.json - index.js - generator.js - prompts.js - ui.js - logo.png
如果你需要在项目里访问插件 API 而不需要创建一个完整的插件,你可以在 package.json 文件中使用 vuePlugins.ui 选项:
{ "vuePlugins": { "ui": ["my-ui.js"] }}
每个文件都需要暴露一个函数,将插件 API 作为第一个参数携带。
当构建你自己的插件时,你可能想在开发环境下运行 cli-ui,所以这样运行会输出较为实用的日志:
vue ui --dev
或:
vue ui -D
你可以通过 api.describeConfig 方法添加一个项目配置。
首先你需要传入一些信息:
api.describeConfig({ // 唯一的配置 ID id: 'org.vue.eslintrc', // 展示名称 name: 'ESLint configuration', // 展示在名称下方的描述 description: 'Error checking & Code quality', // “更多信息 (More info)”链接 link: 'https://eslint.org'})
危险
请确定为 id 设置正确的命名空间,因为它需要跨所有插件保持唯一。我们推荐使用反向域名记号 (reverse domain name notation)。
可以是一个 Material 图标代码或一个自定义的图片 (详见公共静态文件):
api.describeConfig({ /* ... */ // 配置图标 icon: 'application_settings'})
如果你没有定义图标,那就展示该插件可能存在的 logo (详见 Logo)。
默认情况下,配置 UI 可能会读写一个或多个配置文件,例如 .eslintrc 和 vue.config.js。
你可以提供可能需要在用户项目中检测的文件:
api.describeConfig({ /* ... */ // 该配置所有可能的文件 files: { // eslintrc.js eslint: { js: ['.eslintrc.js'], json: ['.eslintrc', '.eslintrc.json'], // 会从 `package.json` 读取 package: 'eslintConfig' }, // vue.config.js vue: { js: ['vue.config.js'] } },})
支持的类型有:json、yaml、js、package。这个顺序是很重要的:如果这项配置不存在,则会创建列表中的第一个文件。
使用 onRead 钩子来返回一个提示符列表,用以配置展示:
api.describeConfig({ /* ... */ onRead: ({ data, cwd }) => ({ prompts: [ // 提示符对象 ] })})
这些提示符会展示在配置的详情面板中。
查阅提示符了解更多信息。
这个 data 对象包含了每个配置文件内容的 JSON 结果。
例如,假设用户在其项目中的 vue.config.js 有以下内容:
module.exports = { lintOnSave: false}
我们在插件中像这样声明配置文件:
api.describeConfig({ /* ... */ // 该配置所有可能的文件 files: { // vue.config.js vue: { js: ['vue.config.js'] } },})
则这个 data 对象会是:
{ // 文件 vue: { // 文件数据 lintOnSave: false }}
多个文件的例子:如果我们在用户的项目中添加以下 eslintrc.js 文件:
module.exports = { root: true, extends: [ 'plugin:vue/essential', '@vue/standard' ]}
那么在我们的插件中将 files 选项改变成为:
api.describeConfig({ /* ... */ // 该配置所有可能的文件 files: { // eslintrc.js eslint: { js: ['.eslintrc.js'], json: ['.eslintrc', '.eslintrc.json'], // 会从 `package.json` 读取 package: 'eslintConfig' }, // vue.config.js vue: { js: ['vue.config.js'] } },})
则这个 data 对象会是:
{ eslint: { root: true, extends: [ 'plugin:vue/essential', '@vue/standard' ] }, vue: { lintOnSave: false }}
你可以将这些提示符组织成为几个选项卡:
api.describeConfig({ /* ... */ onRead: ({ data, cwd }) => ({ tabs: [ { id: 'tab1', label: 'My tab', // 可选的 icon: 'application_settings', prompts: [ // 提示符对象们 ] }, { id: 'tab2', label: 'My other tab', prompts: [ // 提示符对象们 ] } ] })})
使用 onWrite 钩子将数据写入配置文件 (或者执行任何 Node.js 代码):
api.describeConfig({ /* ... */ onWrite: ({ prompts, answers, data, files, cwd, api }) => { // ... }})
参数:
提示符的运行时对象:
{ id: data.name, type: data.type, name: data.short || null, message: data.message, group: data.group || null, description: data.description || null, link: data.link || null, choices: null, visible: true, enabled: true, // 当前值 (未被过滤的) value: null, // 如果用户修改过了则为 true valueChanged: false, error: null, tabId: null, // 原始的 inquirer 提示符对象 raw: data}
onWrite API:
示例 (来自 ESLint 插件):
api.describeConfig({ // ... onWrite: async ({ api, prompts }) => { // 更新 ESLint 规则 const result = {} for (const prompt of prompts) { result[`rules.${prompt.id}`] = await api.getAnswer(prompt.id, JSON.parse) } api.setData('eslint', result) }})
任务是从项目 package.json 文件的 scripts 字段生成的。
因为有 api.describeTask 方法,你可以为任务“增强”额外的信息和钩子:
api.describeTask({ // 用于匹配脚本命令的 RegExp 对象,来选择要被描述的任务 match: /vue-cli-service serve/, description: 'Compiles and hot-reloads for development', // “More info”链接 link: 'https://github.com/vuejs/vue-cli/blob/dev/docs/cli-service.md#serve'})
可以是一个 Material 图标代码或一个自定义的图片 (详见公共静态文件):
api.describeTask({ /* ... */ // 任务图标 icon: 'application_settings'})
如果你没有定义图标,那就展示该插件可能存在的 logo (详见 Logo)。
你可以添加提示符来修改命令参数。它们会展示在一个“参数”模态框中。
Example:
api.describeTask({ // ... // 选填参数 (inquirer 提示符) prompts: [ { name: 'open', type: 'confirm', default: false, description: 'Open browser on server start' }, { name: 'mode', type: 'list', default: 'development', choices: [ { name: 'development', value: 'development' }, { name: 'production', value: 'production' }, { name: 'test', value: 'test' } ], description: 'Specify env mode' } ]})
详见提示符。
有一些钩子是可用的:
例如,你可以将 (上述) 提示符的回答作为一个新参数添加到命令上:
api.describeTask({ // ... // 钩子 // 在这里修改参数 onBeforeRun: async ({ answers, args }) => { // 参数 if (answers.open) args.push('--open') if (answers.mode) args.push('--mode', answers.mode) args.push('--dashboard') }, // 任务运行之后立即执行 onRun: async ({ args, child, cwd }) => { // child: Node 子进程 // cwd: 进程所在目录 }, onExit: async ({ args, child, cwd, code, signal }) => { // code: 退出码 // signal: 可能会被使用的杀进程信号 }})
你可以在任务详情面板中使用 ClientAddon API 展示自定义视图:
api.describeTask({ // ... // 额外的视图 (例如 webpack 仪表盘) // 默认情况下,这里是展示终端输出的 `output` 视图 views: [ { // 唯一 ID id: 'vue-webpack-dashboard-client-addon', // 按钮文字 label: 'Dashboard', // 按钮图标 icon: 'dashboard', // 要加载的动态组件 (详见下述“客户端 addon”章节) component: 'vue-webpack-dashboard' } ], // 展示任务详情时默认选择的视图 (默认是 `output`) defaultView: 'vue-webpack-dashboard-client-addon'})
详见客户端 addon。
你也可以不使用 api.describeTask,而是通过 api.addTask 添加一个 package.json 脚本中没有的全新任务。这些任务只会出现在 cli UI 中。
你需要提供一个 command 选项替代掉 match 选项。
示例:
api.addTask({ // 必填 name: 'inspect', command: 'vue-cli-service inspect', // 选填 // 其余部分类似 `describeTask` 但是没有 `match` 选项 description: '...', link: 'https://github.com/vuejs/vue-cli/...', prompts: [ /* ... */ ], onBeforeRun: () => {}, onRun: () => {}, onExit: () => {}, views: [ /* ... */ ], defaultView: '...'})
警告
command 将会运行一个 Node 上下文。也就是说你可以像在 package.json 脚本中一样调用 Node 的 bin 命令。
提示符对象必须是合法的 inquirer 对象。
不过你也可以添加下列额外的字段 (只会被 UI 使用的可选项):
{ /* ... */ // 用来将提示符按章节分组 group: 'Strongly recommended', // 附加描述 description: 'Enforce attribute naming style in template (`my-prop` or `myProp`)', // “More info”链接 link: 'https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/attribute-hyphenation.md',}
支持的 inquirer 类型有:checkbox、confirm、input、password、list、rawlist。
此外,UI 还支持了仅在这里工作的特殊类型:
{ name: 'open', type: 'confirm', default: false, description: 'Open the app in the browser'}
{ name: 'mode', type: 'list', default: 'development', choices: [ { name: 'Development mode', value: 'development' }, { name: 'Production mode', value: 'production' }, { name: 'Test mode', value: 'test' } ], description: 'Build mode', link: 'https://link-to-docs'}
{ name: 'host', type: 'input', default: '0.0.0.0', description: 'Host for the development server'}
展示多个 switch。
{ name: 'lintOn', message: 'Pick additional lint features:', when: answers => answers.features.includes('linter'), type: 'checkbox', choices: [ { name: 'Lint on save', value: 'save', checked: true }, { name: 'Lint and fix on commit' + (hasGit() ? '' : chalk.red(' (requires Git)')), value: 'commit' } ]}
{ name: 'themeColor', type: 'color', message: 'Theme color', description: 'This is used to change the system UI color around the app', default: '#4DBA87'}
在 vue-cli 插件中,你可能已经有一个 prompts.js 文件,在 (用 CLI 或 UI) 安装该插件的时候询问用户一些问题。你可以向那些提示符对象额外添加只支持 UI 的上述字段,这样的话如果用户使用 UI 的话可以看到更多的信息。
警告
目前,那些不支持的 inquirer 类型不会在 UI 中正常工作。
客户端 addon 是一个动态加载到 cli-ui 中的 JS 包。用于加载自定义组件和路由。
推荐的创建一个客户端 addon 的方式是通过 vue cli 创建一个新项目。你也可以在插件的子目录或不同的 npm 包中这样做。
作为开发依赖安装 @vue/cli-ui。
然后添加一个 vue.config.js 文件并附带以下内容:
const { clientAddonConfig } = require('@vue/cli-ui')module.exports = { ...clientAddonConfig({ id: 'org.vue.webpack.client-addon', // 开发环境端口 (默认值 8042) port: 8042 })}
这个 clientAddonConfig 方法将会生成需要的 vue-cli 配置。除此之外,它会禁用 CSS extraction 并将代码输出到在客户端 addon 目录的 ./dist/index.js。
危险
请确定为 id 设置正确的命名空间,因为它需要跨所有插件保持唯一。我们推荐使用反向域名记号 (reverse domain name notation)。
然后修改 .eslintrc.json 文件以添加一些允许的全局对象:
{ // ... "globals": { "ClientAddonApi": false, "mapSharedData": false, "Vue": false }}
你现在可以在开发环境下运行 serve 脚本,也可以在准备发布时运行 build 脚本。
在客户端 addon 资源中打开 main.js 文件并删除所有代码。
警告
别在客户端 addon 源文件总导入 Vue ,请从浏览器 window 使用全局的 Vue 对象。
这里是一个 main.js 的示例代码:
import VueProgress from 'vue-progress-path'import WebpackDashboard from './components/WebpackDashboard.vue'import TestView from './components/TestView.vue'// 你可以安装额外的 Vue 插件// 使用全局的 'Vue' 变量Vue.use(VueProgress, { defaultShape: 'circle'})// 注册一个自定义组件// (工作原理类似 'Vue.component')ClientAddonApi.component('org.vue.webpack.components.dashboard', WebpackDashboard)// 在 vue-router 中为 /addon/<id> 添加子路由。// 例如,addRoutes('foo', [ { path: '' }, { path: 'bar' } ])// 将会向路由器添加 /addon/foo/ 和 /addon/foo/bar。// 我们在此用 'test-webpack-route' 名称创建一个新的 '/addon/vue-webpack/' 路由ClientAddonApi.addRoutes('org.vue.webpack', [ { path: '', name: 'org.vue.webpack.routes.test', component: TestView }])// 你可以翻译插件组件// (通过使用 vue-i18n) 加载语言文件const locales = require.context('./locales', true, /[a-z0-9]+.json$/i)locales.keys().forEach(key => { const locale = key.match(/([a-z0-9]+)./i)[1] ClientAddonApi.addLocalization(locale, locales(key))})
危险
请确定为 id 设置正确的命名空间,因为它需要跨所有插件保持唯一。我们推荐使用反向域名记号 (reverse domain name notation)。
cli-ui 在 window 作用域内注册了 Vue 和 ClientAddonApi 作为全局变量。
你可以在自己的组件里使用 @vue/ui 和 @vue/cli-ui 所有的组件和 CSS class 以保持样式和体验的一致性。你也可以用内置的 vue-i18n 翻译字符串。
回到 ui.js 文件,使用 api.addClientAddon 方法并带一个指向构建后的文件夹的 require 字符串:
api.addClientAddon({ id: 'org.vue.webpack.client-addon', // 包含构建出来的 JS 文件的文件夹 path: '@vue/cli-ui-addon-webpack/dist'})
这会使用 Node.js 的 require.resolve API 查找文件夹并为从客户端 addon 构建的文件 index.js 启动一个服务器。
或者当开发插件时指定一个 URL (理想中你需要在 Vue 的测试项目的 vue-cli-ui.js 中做这些)
// 用于开发环境// 如果已经在插件中定义过,则会覆写路径api.addClientAddon({ id: 'org.vue.webpack.client-addon', // 使用你之前配置过低同样的端口 url: 'http://localhost:8042/index.js'})
现在你可以在这些视图中使用客户端 addon 了。例如,你可以在一个被描述的任务中指定一个视图:
api.describeTask({ /* ... */ // 额外的视图 (例如 webpack dashboard) // 默认情况下,这是展示终端输出的 'output' 视图 views: [ { // 唯一的 ID id: 'org.vue.webpack.views.dashboard', // 按钮文字 label: 'Dashboard', // 按钮图标 (material-icons) icon: 'dashboard', // 加载的动态组件,会用 ClientAddonApi 进行注册 component: 'org.vue.webpack.components.dashboard' } ], // 展示任务详情时默认选择的视图 (默认情况下就是 output) defaultView: 'org.vue.webpack.views.dashboard'})
这是一个客户端 addon 代码,注册了 `'org.vue.webpack.components.dashboard' 组件 (像我们之前看到的一样):
/* 在 `main.js` 中 */// 导入组件import WebpackDashboard from './components/WebpackDashboard.vue'// 注册自定义组件// (工作原理类似 'Vue.component')ClientAddonApi.component('org.vue.webpack.components.dashboard', WebpackDashboard)
你可以使用 api.addView 方法在标准的“Project plugins”、“Project configuration”和“Project tasks”之下添加一个新的视图:
api.addView({ // 唯一的 id id: 'org.vue.webpack.views.test', // 路由名称 (来自 Vue Router) // 使用 'ClientAddonApi.addRoutes' 方法中相同的名字 (详见之前的客户端 addon 章节) name: 'org.vue.webpack.routes.test', // 按钮图标 (material-icons) icon: 'pets', // 你也可以指定一个自定义图片 (详见之前的公共静态文件章节): // icon: 'http://localhost:4000/_plugin/%40vue%2Fcli-service/webpack-icon.svg', // 按钮的提示文字 tooltip: 'Test view from webpack addon'})
这里是注册了 'org.vue.webpack.routes.test' 的客户端 addon 里的代码 (之前已经见过了):
/* 在 `main.js` 里 */// 导入组件import TestView from './components/TestView.vue'// 在 vue-router 中为 /addon/<id> 添加子路由// 例如,addRoutes('foo', [ { path: '' }, { path: 'bar' } ])// 将为 Vue Router 添加 /addon/foo/ 和 /addon/foo/bar 路由。// 我们这里创建一个新的 '/addon/vue-webpack/' 路由,并命名为 'test-webpack-route'。ClientAddonApi.addRoutes('org.vue.webpack', [ { path: '', name: 'org.vue.webpack.routes.test', component: TestView }])
一种简易的自定义组件之间通过共享的数据互通信息的方式。
例如,webpack 仪表盘在 UI 客户端和 UI 服务端之间通过这个 API 共享了构建的统计信息。
在插件 ui.js (Node.js) 中:
// 设置或更新api.setSharedData('com.my-name.my-variable', 'some-data')// 获取const sharedData = api.getSharedData('com.my-name.my-variable')if (sharedData) { console.log(sharedData.value)}// 移除api.removeSharedData('com.my-name.my-variable')// 侦听变化const watcher = (value, id) => { console.log(value, id)}api.watchSharedData('com.my-name.my-variable', watcher)// 取消侦听api.unwatchSharedData('com.my-name.my-variable', watcher)// 带命名空间的版本const { setSharedData, getSharedData, removeSharedData, watchSharedData, unwatchSharedData} = api.namespace('com.my-name.')
危险
请确定为 id 设置正确的命名空间,因为它需要跨所有插件保持唯一。我们推荐使用反向域名记号 (reverse domain name notation)。
在其自定义组件中:
// Vue 组件export default { // 同步共享的数据 sharedData () { return { // 你可以在模板中使用 `myVariable` myVariable: 'com.my-name.my-variable' // 也可以映射带命名空间的共享数据 ...mapSharedData('com.my-name.', { myVariable2: 'my-variable2' }) } }, // 手动方法 async created () { const value = await this.$getSharedData('com.my-name.my-variable') this.$watchSharedData(`com.my-name.my-variable`, value => { console.log(value) }) await this.$setSharedData('com.my-name.my-variable', 'new-value') }}
如果你使用了 sharedData 选项,共享的数据就可以一个相应的属性被赋值时进行更新。
<template> <VueInput v-model="message"/></template><script>export default { sharedData: { // 将会在服务端同步 'my-message' 共享的数据 message: 'com.my-name.my-message' }}</script>
例如在创建一个设置组件时,这个特性是非常有用的。
插件的 action 就是在 cli-ui (浏览器) 和插件 (Node.js) 直接的调用。
例如,你可能有一个自定义组件里的按钮 (详见客户端 addon),这个按钮会通过这个 API 向服务端调用一些 Node.js 代码。
在插件 (Node.js) 的 ui.js 文件里,你可以从 PluginApi 使用两个方法:
// 调用一个 actionapi.callAction('com.my-name.other-action', { foo: 'bar' }).then(results => { console.log(results)}).catch(errors => { console.error(errors)})
// 监听一个 actionapi.onAction('com.my-name.test-action', params => { console.log('test-action called', params)})
危险
请确定为 id 设置正确的命名空间,因为它需要跨所有插件保持唯一。我们推荐使用反向域名记号 (reverse domain name notation)。
你可以通过 api.namespace 使用带命名空间的版本 (类似共享的数据):
const { onAction, callAction } = api.namespace('com.my-name.')
在客户端 addon 组件 (浏览器) 中,你可以访问 $onPluginActionCalled、$onPluginActionResolved 和 $callPluginAction:
// Vue 组件export default { created () { this.$onPluginActionCalled(action => { // 当 action 被调用时 // 且在运行之前 console.log('called', action) }) this.$onPluginActionResolved(action => { // 当 action 运行完毕之后 console.log('resolved', action) }) }, methods: { testPluginAction () { // 调用一个插件的 action this.$callPluginAction('com.my-name.test-action', { meow: 'meow' }) } }}
IPC 就是进程间通信 (Inter-Process Communication) 的缩写。该系统允许你轻松的从子进程 (例如任务) 发送消息,并且轻量快速。
为了在 webpack 仪表盘 UI 上展示数据,@vue/cli-service 的 serve 和 build 命令会在 --dashboard 参数被传入时向 cli-ui Node.js 服务器发送 IPC 消息。
在进程代码中 (可以是一个 webpack 插件或一个 Node.js 的任务脚本),你可以使用 @vue/cli-shared-utils 中的 IpcMessenger 类:
const { IpcMessenger } = require('@vue/cli-shared-utils')// 创建一个新的 IpcMessenger 实例const ipc = new IpcMessenger()function sendMessage (data) { // 发送一条消息给 cli-ui 服务器 ipc.send({ 'com.my-name.some-data': { type: 'build', value: data } })}function messageHandler (data) { console.log(data)}// 监听消息ipc.on(messageHandler)// 不再监听ipc.off(messageHandler)function cleanup () { // 从 IPC 网络断开连接 ipc.disconnect()}
手动连接:
const ipc = new IpcMessenger({ autoConnect: false})// 这条消息会被放入队列ipc.send({ ... })ipc.connect()
闲时自动断开连接 (在没有任何消息一段时间之后):
const ipc = new IpcMessenger({ disconnectOnIdle: true, idleTimeout: 3000 // 默认值})ipc.send({ ... })setTimeout(() => { console.log(ipc.connected) // false}, 3000)
连接到另一个 IPC 网络:
const ipc = new IpcMessenger({ networkId: 'com.my-name.my-ipc-network'})
在一个 vue-cli 插件的 ui.js 文件中,你可以使用 ipcOn、ipcOff 和 ipcSend 方法:
function onWebpackMessage ({ data: message }) { if (message['com.my-name.some-data']) { console.log(message['com.my-name.some-data']) }}// 监听任何 IPC 消息api.ipcOn(onWebpackMessage)// 不监听任何消息api.ipcOff(onWebpackMessage)// 向所有已连接的 IpcMessenger 实例发送一条消息api.ipcSend({ webpackDashboardMessage: { foo: 'bar' }})
一个插件可以从 UI 服务器本地的 lowdb 数据库保存和加载数据。
// 向本地的数据库存入一个值api.storageSet('com.my-name.an-id', { some: 'value' })// 从本地的数据库取回一个值console.log(api.storageGet('com.my-name.an-id'))// 完整的 lowdb 实例api.db.get('posts') .find({ title: 'low!' }) .assign({ title: 'hi!'}) .write()// 带命名空间的辅助函数const { storageGet, storageSet } = api.namespace('my-plugin.')
危险
请确定为 id 设置正确的命名空间,因为它需要跨所有插件保持唯一。我们推荐使用反向域名记号 (reverse domain name notation)。
你可以基于用户操作系统的通知系统展示通知:
api.notify({ title: 'Some title', message: 'Some message', icon: 'path-to-icon.png'})
这里有一些内建的图标;
你可以用一些文字或进度条来展示进度界面:
api.setProgress({ status: 'Upgrading...', error: null, info: 'Step 2 of 4', progress: 0.4 // 从 0 到 1, -1 表示隐藏进度条})
移除进度界面:
api.removeProgress()
钩子可以用来响应某些 cli-ui 的事件。
当插件在当前项目中第一次被加载时触发。
api.onProjectOpen((project, previousProject) => { // 重置数据})
当插件被重新加载时触发。
api.onPluginReload((project) => { console.log('plugin reloaded')})
当一个配置界面被打开或刷新时触发。
api.onConfigRead(({ config, data, onReadData, tabs, cwd }) => { console.log(config.id)})
当用户在保存界面里保存时触发。
api.onConfigWrite(({ config, data, changedFields, cwd }) => { // ...})
当用户打开一项任务的详情面板时触发。
api.onTaskOpen(({ task, cwd }) => { console.log(task.id)})
当用户运行一项任务时触发。
api.onTaskRun(({ task, args, child, cwd }) => { // ...})
当一项任务退出时触发。不论任务成功或失败它都会触发。
api.onTaskExit(({ task, args, child, signal, code, cwd }) => { // ...})
当用户打开一个视图 (如 'Plugins'、'Configurations' 或 'Tasks') 时触发。
api.onViewOpen(({ view, cwd }) => { console.log(view.id)})
这里的建议是指为用户提议执行 action 的按钮。它们展示在界面的顶栏上。例如我们可以放一个按钮,在应用里没有检测到 Vue Router 包的时候建议将其安装。
api.addSuggestion({ id: 'com.my-name.my-suggestion', type: 'action', // 必填 (未来会加入更多类型) label: 'Add vue-router', // 该消息会展示在一个详情模态框里 message: 'A longer message for the modal', link: 'http://link-to-docs-in-the-modal', // 可选的图片 image: '/_plugin/my-package/screenshot.png', // 当该项建议被用户激活时调用的函数 async handler () { // ... return { // 默认移除这个按钮 keep: false } }})
危险
请确定为 id 设置正确的命名空间,因为它需要跨所有插件保持唯一。我们推荐使用反向域名记号 (reverse domain name notation)。
之后你可以移除这项建议:
api.removeSuggestion('com.my-name.my-suggestion')
你也可以给建议附带 actionLink,当用户激活它时,会换做打开一个页面:
api.addSuggestion({ id: 'com.my-name.my-suggestion', type: 'action', // Required label: 'Add vue-router', // 打开一个新标签 actionLink: 'https://vuejs.org/'})
通常情况下,你会选择适当的上下文用钩子来展示建议:
const ROUTER = 'vue-router-add'api.onViewOpen(({ view }) => { if (view.id === 'vue-project-plugins') { if (!api.hasPlugin('vue-router')) { api.addSuggestion({ id: ROUTER, type: 'action', label: 'org.vue.cli-service.suggestions.vue-router-add.label', message: 'org.vue.cli-service.suggestions.vue-router-add.message', link: 'https://router.vuejs.org/', async handler () { await install(api, 'vue-router') } }) } } else { api.removeSuggestion(ROUTER) }})
在这个例子中,如果 Vue Router 没有安装好,我们只会在插件视图中展示安装 Vue Router 的建议。
注意
addSuggestion 和 removeSuggestion 可以通过 api.namespace() 指定命名空间。
如果项目使用了该插件则返回 true。
api.hasPlugin('eslint')api.hasPlugin('apollo')api.hasPlugin('vue-cli-plugin-apollo')
获取当前工作目录。
api.getCwd()
在当前工程下解析一个文件:
api.resolve('src/main.js')
得出当前打开的工程。
api.getProject()
你可能需要在 cli-ui 内建的 HTTP 服务器上暴露一些静态文件 (通常是为自定义视图指定图标)。
在插件包根目录里可选的放置一个 ui-public 文件夹,这个文件夹里的任何文件都会暴露至 /_plugin/:id/* 的 HTTP 路由。
例如,如果你将 my-logo.png 文件放置到 vue-cli-plugin-hello/ui-public/ 文件夹,那么 cli-ui 加载插件的时候可以通过 /_plugin/vue-cli-plugin-hello/my-logo.png 这个 URL 来访问它。
api.describeConfig({ /* ... */ // 自定义图片 icon: '/_plugin/vue-cli-plugin-hello/my-logo.png'})
请遵循下列简单步骤来为 CLI UI 提交一种其它语言的翻译!
可以参考这里的英文地区文件。
作为示例,参考一份法语的包。
你也可以在插件的根目录的 locales 文件夹放置与 vue-i18n 兼容的地区文件。这样做会在项目打开的时候自动加载,然后你可以使用 $t 在你的组件和 vue-i18n 辅助函数里翻译字符串。同样的 UI API (像 describeTask) 用到的字符串将会进入 vue-i18n,这样你就可以对它们做本地化。
示例 locales 文件夹:
vue-cli-plugin/locales/en.jsonvue-cli-plugin/locales/fr.json
API 的用法示例:
api.describeConfig({ // vue-i18n 路径 description: 'com.my-name.my-plugin.config.foo'})
危险
请确定为 id 设置正确的命名空间,因为它需要跨所有插件保持唯一。我们推荐使用反向域名记号 (reverse domain name notation)。
在组件中使用的示例:
<VueButton>{{ $t('com.my-name.my-plugin.actions.bar') }}</VueButton>
如果你愿意的话,可以使用 ClientAddonApi 在一个客户端 addon 加载地区文件:
// 加载本地文件 (使用 vue-i18n)const locales = require.context('./locales', true, /[a-z0-9]+.json$/i)locales.keys().forEach(key => { const locale = key.match(/([a-z0-9]+)./i)[1] ClientAddonApi.addLocalization(locale, locales(key))})