Vue-cli

Kesa...大约 46 分钟Frontendvuevue-cli

1. 简介

Vue CLI 是一个基于 Vue.js 进行快速开发的完整系统,提供:

  • 通过 @vue/cli 实现的交互式的项目脚手架。

  • 通过 @vue/cli + @vue/cli-service-global 实现的零配置原型开发。

  • 一个运行时依赖 (@vue/cli-service),该依赖:

    • 可升级;
    • 基于 webpack 构建,并带有合理的默认配置;
    • 可以通过项目内的配置文件进行配置;
    • 可以通过插件进行扩展。
  • 一个丰富的官方插件集合,集成了前端生态中最好的工具。

  • 一套完全图形化的创建和管理 Vue.js 项目的用户界面

2. 组件

Vue CLI 有几个独立的部分:

  • CLI
  • CLI 服务
  • CLI 插件

2.1 CLI

CLI (@vue/cli) 是一个全局安装的 npm 包,提供了终端里的 vue 命令。它可以通过 vue create 快速搭建一个新项目,或者直接通过 vue serve 构建新想法的原型。你也可以通过 vue ui 通过一套图形化界面管理你的所有项目。我们会在接下来的指南中逐章节深入介绍

2.2 CLI 服务

CLI 服务 (@vue/cli-service) 是一个开发环境依赖。它是一个 npm 包,局部安装在每个 @vue/cli 创建的项目中。

CLI 服务是构建于 webpackopen in new windowwebpack-dev-serveropen in new window 之上的。它包含了:

  • 加载其它 CLI 插件的核心服务;
  • 一个针对绝大部分应用优化过的内部的 webpack 配置;
  • 项目内部的 vue-cli-service 命令,提供 servebuildinspect 命令

2.3 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

3. 安装

可以使用下列任一命令安装这个新的包:

npm install -g @vue/cli
# OR
yarn global add @vue/cli

安装之后查看版本

vue --version

这里我使用 yarn 全局安装后,提示无法找到命令(OS:Manjaro,SHELL:zsh),需要将 $HOME/.yarn/bin添加加到PATH环境变量:

$HOME/.zshrc中添加

export PATH=$PATH:$HOME/.yarn/bin

3.1 升级

升级全局的 Vue CLI 包,运行:

npm update -g @vue/cli
# OR
yarn global upgrade --latest @vue/cli

3.2 项目依赖

上面列出来的命令是用于升级全局的 Vue CLI。如需升级项目中的 Vue CLI 相关模块(以 @vue/cli-plugin-vue-cli-plugin- 开头),请在项目目录下运行 vue upgrade

用法: upgrade [options] [plugin-name]

(试用)升级 Vue CLI 服务及插件

选项:
  -t, --to <version>    升级 <plugin-name> 到指定的版本
  -f, --from <version>  跳过本地版本检测,默认插件是从此处指定的版本升级上来
  -r, --registry <url>  使用指定的 registry 地址安装依赖
  --all                 升级所有的插件
  --next                检查插件新版本时,包括 alpha/beta/rc 版本在内
  -h, --help            输出帮助内容

4. 快速原型开发

可以使用 vue servevue build 命令对单个 *.vue 文件进行快速原型开发,不过这需要先额外安装一个全局的扩展:

npm install -g @vue/cli-service-global

vue serve 的缺点就是它需要安装全局依赖,这使得它在不同机器上的一致性不能得到保证。因此这只适用于快速原型开发

4.1 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.jsindex.jsApp.vueapp.vue 中的一个。你也可以显式地指定入口文件:

vue serve MyComponent.vue

如果需要,你还可以提供一个 index.htmlpackage.json、安装并使用本地依赖、甚至通过相应的配置文件配置 Babel、PostCSS 和 ESLint

4.2 vue build

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 组件的能力

5. 创建一个项目

5.1 vue create

使用vue create 来创建一个新项目:

vue create hello-world

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                      输出使用帮助信息

5.2 使用 GUI 界面

你也可以通过 vue ui 命令以图形化界面创建和管理项目:

vue ui

上述命令会打开一个浏览器窗口,并以图形化界面将你引导至项目创建的流程

6. 插件和 Preset

6.1 插件

Vue CLI 使用了一套基于插件的架构。如果你查阅一个新创建项目的 package.json,就会发现依赖都是以 @vue/cli-plugin- 开头的。插件可以修改 webpack 的内部配置,也可以向 vue-cli-service 注入命令。在项目创建的过程中,绝大部分列出的特性都是通过插件来实现的

6.1.1 现有项目中安装插件

每个 CLI 插件都会包含一个 (用来创建文件的) 生成器和一个 (用来调整 webpack 核心配置和注入命令的) 运行时插件。当你使用 vue create 来创建一个新项目的时候,有些插件会根据你选择的特性被预安装好。如果你想在一个已经被创建好的项目中安装一个插件,可以使用 vue add 命令:

vue add eslint

vue add 的设计意图是为了安装和调用 Vue CLI 插件。这不意味着替换掉普通的 npm 包。对于这些普通的 npm 包,你仍然需要选用包管理器。

这个命令将 @vue/eslint 解析为完整的包名 @vue/cli-plugin-eslint,然后从 npm 安装它,调用它的生成器。

# 这个和之前的用法等价
vue add cli-plugin-eslint

如果不带 @vue 前缀,该命令会换作解析一个 unscoped 的包。例如以下命令会安装第三方插件 vue-cli-plugin-apollo

# 安装并调用 vue-cli-plugin-apollo
vue 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"
  }
}

6.1.2 项目本地的插件

如果你需要在项目里直接访问插件 API 而不需要创建一个完整的插件,你可以在 package.json 文件中使用 vuePlugins.service 选项:

{
  "vuePlugins": {
    "service": ["my-commands.js"]
  }
}

每个文件都需要暴露一个函数,接受插件 API 作为第一个参数

你也可以通过 vuePlugins.ui 选项添加像 UI 插件一样工作的文件:

{
  "vuePlugins": {
    "ui": ["my-ui.js"]
  }
}

6.2 Preset

一个 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 中。

6.2.1 Preset 插件的版本管理

你可以显式地指定用到的插件的版本:

{
  "plugins": {
    "@vue/cli-plugin-eslint": {
      "version": "^3.0.0"
      // ... 该插件的其它选项
    }
  }
}

注意对于官方插件来说这不是必须的——当被忽略时,CLI 会自动使用 registry 中最新的版本。不过我们推荐为 preset 列出的所有第三方插件提供显式的版本范围

6.2.2 允许插件的命令提示

每个插件在项目创建的过程中都可以注入它自己的命令提示,不过当你使用了一个 preset,这些命令提示就会被跳过,因为 Vue CLI 假设所有的插件选项都已经在 preset 中声明过了。

在有些情况下你可能希望 preset 只声明需要的插件,同时让用户通过插件注入的命令提示来保留一些灵活性。

对于这种场景你可以在插件选项中指定 "prompts": true 来允许注入命令提示:

{
  "plugins": {
    "@vue/cli-plugin-eslint": {
      // 让用户选取他们自己的 ESLint config
      "prompts": true
    }
  }
}

6.2.3 远程 Preset

你可以通过发布 git repo 将一个 preset 分享给其他开发者。这个 repo 应该包含以下文件:

发布 repo 后,你就可以在创建项目的时候通过 --preset 选项使用这个远程的 preset 了:

# 从 GitHub repo 使用 preset
vue create --preset username/repo my-project

GitLab 和 BitBucket 也是支持的。如果要从私有 repo 获取,请确保使用 --clone 选项:

vue create --preset gitlab:username/repo --clone my-project
vue create --preset bitbucket:username/repo --clone my-project

# 私有服务器
vue create --preset gitlab:my-gitlab-server.com:group/projectname --clone my-project
vue create --preset direct:ssh://[email protected]/group/projectname.git --clone my-project

6.2.4 加载文件系统中的 Preset

当开发一个远程 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

7. CLI 服务

7.1 使用命令

在一个 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
# OR
yarn serve

如果你可以使用 npxopen in new window (最新版的 npm 应该已经自带),也可以直接这样调用命令:

npx vue-cli-service serve

7.2 vue-cli-service serve

用法: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)并附带开相机用的模块热重载(Module-Replacement)

除了通过命令行参数,可以使用vue.config.js中的devServer配置开发服务器

命令行参数 [entry] 将被指定为唯一入口,而非额外的追加入口。尝试使用 [entry] 覆盖 config.pages 中的 entry 将可能引发错误

7.3 vue-cli-service build

用法: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 spliting, 它的 chunk manifest 会内联在 HTML 中

  • --modern: 使用现代模式open in new window构建应用,为现代浏览器交付原生支持的 ES2015 代码,并生成一个兼容老浏览器的包用来自动回退
  • --target: 允许你将项目中的任何组件以一个库或 Web Components 组件的方式进行构建
  • --report--report-json:会根据构建统计生成报告,它会帮助你分析包中包含的模块们的大小

7.4 vue-cli-service inspect

用法:vue-cli-service inspect [options] [...paths]

选项:

  --mode    指定环境模式 (默认值:development)

可以使用 vue-cli-service inspect 来审查一个 Vue CLI 项目的 webpack config

7.5 查看所有的可用命令

有些 CLI 插件会向 vue-cli-service 注入额外的命令。例如 @vue/cli-plugin-eslint 会注入 vue-cli-service lint 命令。你可以运行以下命令查看所有注入的命令:

npx vue-cli-service help

可以这样学习每个命令可用的选项:

npx vue-cli-service help [command]

7.6 缓存和并行处理

  • cache-loader 会默认为 Vue/Babel/TypeScript 编译开启。文件会缓存在 node_modules/.cache 中——如果遇到了编译方面的问题,记得先删掉缓存目录之后再试试看
  • thread-loader 会在多核 CPU 的机器上为 Babel/TypeScript 转译开启

7.7 Git Hook

在安装之后,@vue/cli-service 也会安装 yorkieopen in new window,它会让你在 package.jsongitHooks 字段中方便地指定 Git hook:

{
  "gitHooks": {
    "pre-commit": "lint-staged"
  },
  "lint-staged": {
    "*.{js,vue}": ["vue-cli-service lint", "git add"]
  }
}

7.8 配置时无需 Eject

通过 vue create 创建的项目无需额外的配置就已经可以跑起来了。插件的设计也是可以相互共存的,所以绝大多数情况下,你只需要在交互式命令提示中选取需要的功能即可

8. 浏览器兼容性

8.1 browserslist

你会发现有 package.json 文件里的 browserslist 字段 (或一个单独的 .browserslistrc 文件),指定了项目的目标浏览器的范围。这个值会被 @babel/preset-envopen in new windowAutoprefixeropen in new window 用来确定需要转译的 JavaScript 特性和需要添加的 CSS 浏览器前缀。

现在查阅这里open in new window了解如何指定浏览器范围。

8.3 Polyfill

useBuiltIns: 'usage'

一个默认的 Vue CLI 项目会使用 @vue/babel-preset-appopen in new window,它通过 @babel/preset-envbrowserslist 配置来决定项目需要的 polyfill。

默认情况下,它会把 useBuiltIns: 'usage'open in new window 传递给 @babel/preset-env,这样它会根据源代码中出现的语言特性自动检测需要的 polyfill。这确保了最终包里 polyfill 数量的最小化。然而,这也意味着如果其中一个依赖需要特殊的 polyfill,默认情况下 Babel 无法将其检测出来。

如果有依赖需要 polyfill,你有几种选择:

  1. 如果该依赖基于一个目标环境不支持的 ES 版本撰写: 将其添加到 vue.config.js 中的 transpileDependenciesopen in new window 选项。这会为该依赖同时开启语法转换和根据使用情况检测 polyfill。

  2. 如果该依赖交付了 ES5 代码并显式地列出了需要的 polyfill: 你可以使用 @vue/babel-preset-apppolyfillsopen in new window 选项预包含所需要的 polyfill。注意 es.promise 将被默认包含,因为现在的库依赖 Promise 是非常普遍的。

    // babel.config.js
    module.exports = {
      presets: [
        [
          '@vue/app',
          {
            polyfills: ['es.promise', 'es.symbol'],
          },
        ],
      ],
    };
    

    提示

    我们推荐以这种方式添加 polyfill 而不是在源代码中直接导入它们,因为如果这里列出的 polyfill 在 browserslist 的目标中不需要,则它会被自动排除。

  3. 如果该依赖交付 ES5 代码,但使用了 ES6+ 特性且没有显式地列出需要的 polyfill (例如 Vuetify):**请使用 useBuiltIns: 'entry' 然后在入口文件添加 import 'core-js/stable'; import 'regenerator-runtime/runtime';。这会根据 browserslist 目标导入**所有 polyfill,这样你就不用再担心依赖的 polyfill 问题了,但是因为包含了一些没有用到的 polyfill 所以最终的包大小可能会增加。

8.3 构建库或是 Web Component 时的 Polyfills

当使用 Vue CLI 来构建一个库或是 Web Componentopen in new window 时,推荐给 @vue/babel-preset-app 传入 useBuiltIns: false 选项。这能够确保你的库或是组件不包含不必要的 polyfills。通常来说,打包 polyfills 应当是最终使用你的库的应用的责任。

8.5 现代模式

有了 Babel 我们可以兼顾所有最新的 ES2015+ 语言特性,但也意味着我们需要交付转译和 polyfill 后的包以支持旧浏览器。这些转译后的包通常都比原生的 ES2015+ 代码会更冗长,运行更慢。现如今绝大多数现代浏览器都已经支持了原生的 ES2015,所以因为要支持更老的浏览器而为它们交付笨重的代码是一种浪费。

Vue CLI 提供了一个“现代模式”帮你解决这个问题。以如下命令为生产环境构建:

vue-cli-service build --modern

Vue CLI 会产生两个应用的版本:一个现代版的包,面向支持 ES modulesopen in new window 的现代浏览器,另一个旧版的包,面向不支持的旧浏览器。

最酷的是这里没有特殊的部署要求。其生成的 HTML 文件会自动使用 Phillip Walton 精彩的博文open in new window中讨论到的技术:

  • 现代版的包会通过 <script type="module"> 在被支持的浏览器中加载;它们还会使用 <link rel="modulepreload"> 进行预加载。
  • 旧版的包会通过 <script nomodule> 加载,并会被支持 ES modules 的浏览器忽略。
  • 一个针对 Safari 10 中 <script nomodule> 的修复会被自动注入。

对于一个 Hello World 应用来说,现代版的包已经小了 16%。在生产环境下,现代版的包通常都会表现出显著的解析速度和运算速度,从而改善应用的加载性能。

提示

需要配合始终开启的 CORS 进行加载。这意味着你的服务器必须返回诸如 Access-Control-Allow-Origin: * 的有效的 CORS 头。如果你想要通过认证来获取脚本,可使将 crossorigin 选项设置为 use-credentials。

同时,现代浏览器使用一段内联脚本来避免 Safari 10 重复加载脚本包,所以如果你在使用一套严格的 CSP,你需要这样显性地允许内联脚本:

Content-Security-Policy: script-src 'self' 'sha256-4RS22DYeB7U14dra4KcQYxmwt5HkOInieXK1NUMBmQI='

9. HTML 和静态资源

9.1 HTML

9.1.1 Index 文件

public/index.html 文件是一个会被html-webpack-plugin open in new window处理的模板,在构建过程中资源链接会被自动注入

Vue CLI 也会自动注入 resource hint (preload/prefetch、manifest 和图标链接 (当用到 PWA 插件时) 以及构建过程中处理的 JavaScript 和 CSS 文件的资源链接

9.1.2 差值

因为 index 文件被用作模板,可以使用 lodash template open in new window :

  • <%= VALUE %>: 用作不转义插值
  • <%- VALUE %>: 用作 HTML 转义插值
  • <% expression %>: 用于描述 js 流程控制

除了html-webpack-plugin 暴露的默认值open in new window之外,所有客户端环境变量open in new window也可以直接使用。例如,BASE_URL 的用法:

<link rel="icon" href="<%= BASE_URL %>favicon.ico" />

9.1.3 Preload

``open in new window 是一种 resource hint,用来指定页面加载后很快会被用到的资源,所以在页面加载的过程中,我们希望在浏览器开始主体渲染之前尽早 preload。

默认情况下,一个 Vue CLI 应用会为所有初始化渲染需要的文件自动生成 preload 提示。

这些提示会被 @vue/preload-webpack-pluginopen in new window 注入,并且可以通过 chainWebpackconfig.plugin('preload') 进行修改和删除

9.1.4 Prefetch

``open in new window 是一种 resource hint,用来告诉浏览器在页面加载完成后,利用空闲时间提前获取用户未来可能会访问的内容。

默认情况下,一个 Vue CLI 应用会为所有作为 async chunk 生成的 JavaScript 文件 (通过动态 import() 按需 code splittingopen in new window 的产物) 自动生成 prefetch 提示。

这些提示会被 @vue/preload-webpack-pluginopen in new window 注入,并且可以通过 chainWebpackconfig.plugin('prefetch') 进行修改和删除。

示例:

// vue.config.js
module.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 链接

tips

Prefetch 链接将会消耗带宽。如果你的应用很大且有很多 async chunk,而用户主要使用的是对带宽较敏感的移动端,那么你可能需要关掉 prefetch 链接并手动选择要提前获取的代码区块

9.1.5 不生成 index

当基于已有的后端使用 Vue CLI 时,可能不需要生成index.html, 这样生成的资源可以用于一个服务端渲染的页面,可以像 vue.config.js加入以下代码

// vue.config.js
module.exports = {
  // 去掉文件名中的 hash
  filenameHashing: false,
  // 删除 HTML 相关的 webpack 插件
  chainWebpack: (config) => {
    config.plugins.delete('html');
    config.plugins.delete('preload');
    config.plugins.delete('prefetch');
  },
};

然而这样做并不是很推荐,因为:

  • 硬编码的文件名不利于实现高效率的缓存控制。
  • 硬编码的文件名也无法很好的进行 code-splitting (代码分段),因为无法用变化的文件名生成额外的 JavaScript 文件。
  • 硬编码的文件名无法在现代模式open in new window下工作。

你应该考虑换用 indexPathopen in new window 选项将生成的 HTML 用作一个服务端框架的视图模板

9.1.6 构建一个多页应用

不是每个应用都需要是一个单页应用。Vue CLI 支持使用 vue.config.js 中的 pages 选项open in new window构建一个多页面的应用。构建好的应用将会在不同的入口之间高效共享通用的 chunk 以获得最佳的加载性能

9.2 处理静态资源

静态资源可以通过两种方式进行处理:

  • 在 JavaScript 被导入或在 template/CSS 中通过相对路径被引用。这类引用会被 webpack 处理。
  • 放置在 public 目录下或通过绝对路径被引用。这类资源将会直接被拷贝,而不会经过 webpack 的处理

9.6.1 从相对路径导入

当你在 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 请求的数量。

你可以通过 chainWebpackopen in new window 调整内联文件的大小限制。例如,下列代码会将其限制设置为 10kb:

// vue.config.js
module.exports = {
  chainWebpack: (config) => {
    config.module
      .rule('images')
      .use('url-loader')
      .loader('url-loader')
      .tap((options) => Object.assign(options, { limit: 10240 }));
  },
};

9.6.2 URL 转换规则

  • 如果 URL 是一个绝对路径 (例如 /images/foo.png),它将会被保留不变。

  • 如果 URL 以 . 开头,它会作为一个相对模块请求被解释且基于你的文件系统中的目录结构进行解析。

  • 如果 URL 以 ~ 开头,其后的任何内容都会作为一个模块请求被解析。这意味着你甚至可以引用 Node 模块中的资源:

    <img src="~some-npm-package/foo.png" />
    
  • 如果 URL 以 @ 开头,它也会作为一个模块请求被解析。它的用处在于 Vue CLI 默认会设置一个指向 <projectRoot>/src 的别名 @

9.6.3 public 文件夹

任何放置在 public 文件夹的静态资源将会被简单复制,而不经过 webpack, 需要通过绝对路径引用它们

推荐将资源作为模块依赖图的一部分导入,这样将会通过 webpack 处理:

  • 脚本和样式表会被压缩且打包在一起,从而避免额外的网络请求
  • 文件丢失会直接在编译时报错,而不是到了用户端才产生 404 错误
  • 最终生成的文件名包含了内容哈希,因此你不必担心浏览器会缓存它们的老版本

public 目录提供的是一个应急手段,当你通过绝对路径引用它时,留意应用将会部署到哪里。如果你的应用没有部署在域名的根部,那么你需要为你的 URL 配置 publicPathopen in new window 前缀:

  • public/index.html 或其它通过 html-webpack-plugin 用作模板的 HTML 文件中,你需要通过 <%= BASE_URL %> 设置链接前缀:

    <link rel="icon" href="<%= BASE_URL %>favicon.ico" />
    
  • 在模板中,你首先需要向你的组件传入基础 URL:

    data () {
      return {
        publicPath: process.env.BASE_URL
      }
    }
    

    然后:

    <img :src="`${publicPath}my-image.png`" />
    

何时该使用 pulic 文件夹

  • 你需要在构建输出中指定一个文件的名字。
  • 你有上千个图片,需要动态引用它们的路径。
  • 有些库可能和 webpack 不兼容,这时你除了将其用一个独立的 <script> 标签引入没有别的选择

10. CSS

Vue CLI 项目天生支持 PostCSSopen in new windowCSS Modulesopen in new window 和包含 Sassopen in new windowLessopen in new windowStylusopen in new window 在内的预处理器

10.1 引用静态资源

所有编译后的 CSS 都会通过 css-loaderopen in new window 来解析其中的 url() 引用,并将这些引用作为模块请求来处理。这意味着你可以根据本地的文件结构用相对路径来引用静态资源。另外要注意的是如果你想要引用一个 npm 依赖中的文件,或是想要用 webpack alias,则需要在路径前加上 ~ 的前缀来避免歧义

10.2 预处理器

你可以在创建项目的时候选择预处理器 (Sass/Less/Stylus)。如果当时没有选好,内置的 webpack 仍然会被预配置为可以完成所有的处理。你也可以手动安装相应的 webpack loader:

# Sass
npm install -D sass-loader sass

# Less
npm install -D less-loader less

# Stylus
npm install -D stylus-loader stylus

然后你就可以导入相应的文件类型,或在 *.vue 文件中这样来使用:

<style lang="scss">
$color: red;
</style>

10.2.1 自动化导入

如果你想自动化导入文件 (用于颜色、变量、mixin……),你可以使用 style-resources-loaderopen in new window。这里有一个关于 Stylus 的在每个单文件组件和 Stylus 文件中导入 ./src/styles/imports.styl 的例子:

// vue.config.js
const 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-loaderopen in new window

10.3 PostCSS

Vue CLI 内部使用了 PostCSS。

你可以通过 .postcssrc 或任何 postcss-load-configopen in new window 支持的配置源来配置 PostCSS。也可以通过 vue.config.js 中的 css.loaderOptions.postcss 配置 postcss-loaderopen in new window

我们默认开启了 autoprefixeropen in new window。如果要配置目标浏览器,可使用 package.jsonbrowserslistopen in new window 字段

** CSS 中浏览器前缀规则的注意事项**

在生产环境构建中,Vue CLI 会优化 CSS 并基于目标浏览器抛弃不必要的浏览器前缀规则。因为默认开启了 autoprefixer,你只使用无前缀的 CSS 规则即可

10.4 CSS Modules

你可以通过 <style module> 以开箱即用的方式*.vue 文件中使用 CSS Modulesopen in new window

如果想在 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.requireModuleExtensionfalse

// vue.config.js
module.exports = {
  css: {
    requireModuleExtension: false,
  },
};

如果你希望自定义生成的 CSS Modules 模块的类名,可以通过 vue.config.js 中的 css.loaderOptions.css 选项来实现。所有的 css-loader 选项在这里都是支持的,例如 localIdentNamecamelCase

// vue.config.js
module.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',
      },
    },
  },
};

10.5 向预处理器 Loader 传递选项

有的时候你想要向 webpack 的预处理器 loader 传递选项。你可以使用 vue.config.js 中的 css.loaderOptions 选项。比如你可以这样向所有 Sass/Less 样式传入共享的全局变量:

// vue.config.js
module.exports = {
  css: {
    loaderOptions: {
      // 给 sass-loader 传递选项
      sass: {
        // @/ 是 src/ 的别名
        // 所以这里假设你有 `src/variables.sass` 这个文件
        // 注意:在 sass-loader v8 中,这个选项名是 "prependData"
        additionalData: `@import "~@/variables.sass"`,
      },
      // 默认情况下 `sass` 选项会同时对 `sass` 和 `scss` 语法同时生效
      // 因为 `scss` 语法在内部也是由 sass-loader 处理的
      // 但是在配置 `prependData` 选项的时候
      // `scss` 语法会要求语句结尾必须有分号,`sass` 则要求必须没有分号
      // 在这种情况下,我们可以使用 `scss` 选项,对 `scss` 语法进行单独配置
      scss: {
        additionalData: `@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 配置,包括:

tips

这样做比使用 chainWebpack 手动指定 loader 更推荐,因为这些选项需要应用在使用了相应 loader 的多个地方

11. Webpack

11.1 简单配置

调整 webpack 配置最简单的方式就是在 vue.config.js 中的 configureWebpack 选项提供一个对象:

// vue.config.js
module.exports = {
  configureWebpack: {
    plugins: [new MyAwesomeWebpackPlugin()],
  },
};

该对象将会被 webpack-mergeopen in new window 合并入最终的 webpack 配置。

警告

有些 webpack 选项是基于 vue.config.js 中的值设置的,所以不能直接修改。例如你应该修改 vue.config.js 中的 outputDir 选项而不是修改 output.path;你应该修改 vue.config.js 中的 publicPath 选项而不是修改 output.publicPath。这样做是因为 vue.config.js 中的值会被用在配置里的多个地方,以确保所有的部分都能正常工作在一起。

如果你需要基于环境有条件地配置行为,或者想要直接修改配置,那就换成一个函数 (该函数会在环境变量被设置之后懒执行)。该方法的第一个参数会收到已经解析好的配置。在函数内,你可以直接修改配置,或者返回一个将会被合并的对象:

// vue.config.js
module.exports = {
  configureWebpack: (config) => {
    if (process.env.NODE_ENV === 'production') {
      // 为生产环境修改配置...
    } else {
      // 为开发环境修改配置...
    }
  },
};

11.2 链式操作 (高级)

Vue CLI 内部的 webpack 配置是通过 webpack-chainopen in new window 维护的。这个库提供了一个 webpack 原始配置的上层抽象,使其可以定义具名的 loader 规则和具名插件,并有机会在后期进入这些规则并对它们的选项进行修改。

它允许我们更细粒度的控制其内部配置。接下来有一些常见的在 vue.config.js 中的 chainWebpack 修改的例子。

提示

当你打算链式访问特定的 loader 时,vue inspectopen in new window 会非常有帮助。

11.2.1 修改 Loader 选项

// vue.config.js
module.exports = {
  chainWebpack: (config) => {
    config.module
      .rule('vue')
      .use('vue-loader')
      .tap((options) => {
        // 修改它的选项...
        return options;
      });
  },
};

提示

对于 CSS 相关 loader 来说,我们推荐使用 css.loaderOptionsopen in new window 而不是直接链式指定 loader。这是因为每种 CSS 文件类型都有多个规则,而 css.loaderOptions 可以确保你通过一个地方影响所有的规则。

11.2.2 添加一个新的 Loader

// vue.config.js
module.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();
  },
};

11.2.3 替换一个规则里的 Loader

如果你想要替换一个已有的基础 loaderopen in new window,例如为内联的 SVG 文件使用 vue-svg-loader 而不是加载这个文件:

// vue.config.js
module.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');
  },
};

11.2.4 修改插件选项

// vue.config.js
module.exports = {
  chainWebpack: (config) => {
    config.plugin('html').tap((args) => {
      return [
        /* 传递给 html-webpack-plugin's 构造函数的新参数 */
      ];
    });
  },
};

你需要熟悉 webpack-chain 的 APIopen in new window阅读一些源码open in new window以便了解如何最大程度利用好这个选项,但是比起直接修改 webpack 配置,它的表达能力更强,也更为安全。

比方说你想要将 index.html 默认的路径从 /Users/username/proj/public/index.html 改为 /Users/username/proj/app/templates/index.html。通过参考 html-webpack-pluginopen in new window 你能看到一个可以传入的选项列表。我们可以在下列配置中传入一个新的模板路径来改变它:

// vue.config.js
module.exports = {
  chainWebpack: (config) => {
    config.plugin('html').tap((args) => {
      args[0].template = '/Users/username/proj/app/templates/index.html';
      return args;
    });
  },
};

你可以通过接下来要讨论的工具 vue inspect 来确认变更。

11.3 审查项目的 webpack 配置

因为 @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 vue
vue inspect --plugin html

最后,你可以列出所有规则和插件的名字:

vue inspect --rules
vue inspect --plugins

11.4 以一个文件的方式使用解析好的配置

有些外部工具可能需要通过一个文件访问解析好的 webpack 配置,比如那些需要提供 webpack 配置路径的 IDE 或 CLI。在这种情况下你可以使用如下路径:

<projectRoot>/node_modules/@vue/cli-service/webpack.config.js

该文件会动态解析并输出 vue-cli-service 命令中使用的相同的 webpack 配置,包括那些来自插件甚至是你自定义的配置。

12. 模式和环境变量

12.1 模式

默认情况下,Vue CLI 项目由三个模式:

  • development: 用于 vue-cli-service serve
  • test: 用于 vue-cli-service test:uint
  • prodution: 用于 vue-cli-service buildvue-cli-service test:e2e

可以通过传递 --mode 选项参数为命令行覆写默认的模式

vue-cli-service build --mode development

当运行 vue-cli-service 命令时,所有的环境变量都从对应的环境文件中载入,若文件中不包含 NODE_ENV 变量,其值将取决于模式,例如,在 production 模式下被设置为 "production",在 test 模式下被设置为 "test",默认则是 "development"

NODE_ENV 将决定您的应用运行的模式,是开发,生产还是测试,因此也决定了创建哪种 webpack 配置。

例如通过将 NODE_ENV 设置为 "test",Vue CLI 会创建一个优化过后的,并且旨在用于单元测试的 webpack 配置,它并不会处理图片以及一些对单元测试非必需的其他资源。

同理,NODE_ENV=development 创建一个 webpack 配置,该配置启用热更新,不会对资源进行 hash 也不会打出 vendor bundles,目的是为了在开发的时候能够快速重新构建。

当你运行 vue-cli-service build 命令时,无论你要部署到哪个环境,应该始终把 NODE_ENV 设置为 "production" 来获取可用于部署的应用程序

tips

如果在环境中有默认的 NODE_ENV,你应该移除它或在运行 vue-cli-service 命令的时候明确地设置 NODE_ENV

12.2 环境变量

可以在你的项目根目录中放置下列文件来指定环境变量:

.env                # 在所有的环境中被载入
.env.local          # 在所有的环境中被载入,但会被 git 忽略
.env.[mode]         # 只在指定的模式中被载入
.env.[mode].local   # 只在指定的模式中被载入,但会被 git 忽略

一个环境文件只包含环境变量的 key-value

FOO=bar
VUE_APP_NOT_SECRET_CODE=some_value

tips

不要在你的应用程序中存储任何机密信息(例如私有 API 密钥)!

环境变量会随着构建打包嵌入到输出代码,意味着任何人都有机会能够看到它

只有 NODE_ENVBASE_URL 和以 VUE_APP_ 开头的变量将通过 webpack.DefinePlugin 静态地嵌入到客户端侧的代码中。这是为了避免意外公开机器上可能具有相同名称的私钥

想要了解解析环境文件规则的细节,请参考 dotenvopen in new window。我们也使用 dotenv-expandopen in new window 来实现变量扩展 (Vue CLI 3.5+ 支持)。例如:

FOO=foo
BAR=bar

CONCAT=$FOO$BAR # CONCAT=foobar

被载入的变量将会对 vue-cli-service 的所有命令、插件和依赖可用

环境文件加载优先级

为一个特定模式准备的环境文件 (例如 .env.production) 将会比一般的环境文件 (例如 .env) 拥有更高的优先级

此外,Vue CLI 启动时已经存在的环境变量拥有最高优先级,并不会被 .env 文件覆写

.env 环境文件是通过运行 vue-cli-service 命令载入的,因此环境文件发生变化,你需要重启服务

示例:Staging 模式

假设我们有一个应用包含以下 .env 文件:

VUE_APP_TITLE=My App

.env.staging 文件:

NODE_ENV=production
VUE_APP_TITLE=My App (staging)
  • vue-cli-service build 会加载可能存在的 .env.env.production.env.production.local 文件然后构建出生产环境应用。
  • vue-cli-service build --mode staging 会在 staging 模式下加载可能存在的 .env.env.staging.env.staging.local 文件然后构建出生产环境应用。

这两种情况下,根据 NODE_ENV,构建出的应用都是生产环境应用,但是在 staging 版本中,process.env.VUE_APP_TITLE 被覆写成了另一个值

12.3 在客户端侧代码中使用环境变量

只有以 VUE_APP_ 开头的变量会被 webpack.DefinePlugin 静态嵌入到客户端侧的包中。你可以在应用的代码中这样访问它们:

console.log(process.env.VUE_APP_SECRET);

在构建过程中,process.env.VUE_APP_SECRET 将会被相应的值所取代。在 VUE_APP_SECRET=secret 的情况下,它会被替换为 "secret"

除了 VUE_APP_* 变量之外,在你的应用代码中始终可用的还有两个特殊的变量:

  • NODE_ENV - 会是 "development""production""test" 中的一个。具体的值取决于应用运行的模式open in new window
  • BASE_URL - 会和 vue.config.js 中的 publicPath 选项相符,即你的应用会部署到的基础路径。

所有解析出来的环境变量都可以在 public/index.html 中以 HTML 插值open in new window中介绍的方式使用

tips

你可以在 vue.config.js 文件中计算环境变量。它们仍然需要以 VUE_APP_ 前缀开头。这可以用于版本信息:

process.env.VUE_APP_VERSION = require('./package.json').version;

module.exports = {
  // config
};

12.4 本地环境

有的时候你可能有一些不应该提交到代码仓库中的变量,尤其是当你的项目托管在公共仓库时。这种情况下你应该使用一个 .env.local 文件取而代之。本地环境文件默认会被忽略,且出现在 .gitignore 中。

.local 也可以加在指定模式的环境文件上,比如 .env.development.local 将会在 development 模式下被载入,且被 git 忽略

13. 构建目标

当你运行 vue-cli-service build 时,你可以通过 --target 选项指定不同的构建目标。它允许你将相同的源代码根据不同的用例生成不同的构建

13.1 应用

应用模式是默认的模式。在这个模式中:

  • index.html 会带有注入的资源和 resource hint
  • 第三方库会被分到一个独立包以便更好的缓存
  • 小于 4kb 的静态资源会被内联在 JavaScript 中
  • public 中的静态资源会被复制到输出目录中

13.2 库

关于 IE 兼容性的提醒

在库模式中,项目的 publicPath 是根据主文件的加载路径动态设置open in new window的(用以支持动态的资源加载能力)。但是这个功能用到了 document.currentScript,而 IE 浏览器并不支持这一特性。所以如果网站需要支持 IE 的话,建议使用库之前先在页面上引入 current-script-polyfillopen in new window

注意对 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                     Gzipped

dist/myLib.umd.min.js    13.28 kb                 8.42 kb
dist/myLib.umd.js        20.95 kb                 10.22 kb
dist/myLib.common.js     20.57 kb                 10.09 kb
dist/myLib.css           0.33 kb                  0.23 kb

这个入口可以是一个 .js 或一个 .vue 文件。如果没有指定入口,则会使用 src/App.vue

构建一个库会输出:

  • dist/myLib.common.js:一个给打包器用的 CommonJS 包 (不幸的是,webpack 目前还并没有支持 ES modules 输出格式的包)
  • dist/myLib.umd.js:一个直接给浏览器或 AMD loader 使用的 UMD 包
  • dist/myLib.umd.min.js:压缩后的 UMD 构建版本
  • dist/myLib.css:提取出来的 CSS 文件 (可以通过在 vue.config.js 中设置 css: { extract: false } 强制内联)

警告

如果你在开发一个库或多项目仓库 (monorepo),请注意导入 CSS 是具有副作用的。请确保在 package.json移除 "sideEffects": false,否则 CSS 代码块会在生产环境构建时被 webpack 丢掉。

13.2.1 Vue vs. JS/TS 入口文件

当使用一个 .vue 文件作为入口时,你的库会直接暴露这个 Vue 组件本身,因为组件始终是默认导出的内容。

然而,当你使用一个 .js.ts 文件作为入口时,它可能会包含具名导出,所以库会暴露为一个模块。也就是说你的库必须在 UMD 构建中通过 window.yourLib.default 访问,或在 CommonJS 构建中通过 const myLib = require('mylib').default 访问。如果你没有任何具名导出并希望直接暴露默认导出,你可以在 vue.config.js 中使用以下 webpack 配置:

module.exports = {
  configureWebpack: {
    output: {
      libraryExport: 'default',
    },
  },
};

13.3 Web Components 组件

兼容性提示

Web Components 模式不支持 IE11 及更低版本。更多细节open in new window

注意对 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 的文档open in new window了解更多细节。

注意这个包依赖了在页面上全局可用的 Vue

这个模式允许你的组件的使用者以一个普通 DOM 元素的方式使用这个 Vue 组件:

<script src="https://unpkg.com/vue"></script>
<script src="path/to/my-element.js"></script>

<!-- 可在普通 HTML 中或者其它任何框架中使用 -->
<my-element></my-element>

13.3.1 注册多个 Web Components 组件的包

当你构建一个 Web Components 组件包的时候,你也可以使用一个 glob 表达式作为入口指定多个组件目标:

vue-cli-service build --target wc --name foo 'src/components/*.vue'

当你构建多个 web component 时,--name 将会用于设置前缀,同时自定义元素的名称会由组件的文件名推导得出。比如一个名为 HelloWorld.vue 的组件携带 --name foo 将会生成的自定义元素名为 <foo-hello-world>

13.3.2 异步 Web Components 组件

当指定多个 Web Components 组件作为目标时,这个包可能会变得非常大,并且用户可能只想使用你的包中注册的一部分组件。这时异步 Web Components 模式会生成一个 code-split 的包,带一个只提供所有组件共享的运行时,并预先注册所有的自定义组件小入口文件。一个组件真正的实现只会在页面中用到自定义元素相应的一个实例时按需获取:

vue-cli-service build --target wc-async --name foo 'src/components/*.vue'
File                Size                        Gzipped

dist/foo.0.min.js    12.80 kb                    8.09 kb
dist/foo.min.js      7.45 kb                     3.17 kb
dist/foo.1.min.js    2.91 kb                     1.02 kb
dist/foo.js          22.51 kb                    6.67 kb
dist/foo.0.js        17.27 kb                    8.83 kb
dist/foo.1.js        5.24 kb                     1.64 kb

现在用户在该页面上只需要引入 Vue 和这个入口文件即可:

<script src="https://unpkg.com/vue"></script>
<script src="path/to/foo.min.js"></script>

<!-- foo-one 的实现的 chunk 会在用到的时候自动获取 -->
<foo-one></foo-one>

13.4 在构建时使用 vuex

在构建 Web Components 组件open in new windowopen in new window时,入口点不是 main.js ,而是 entry-wc.js 文件,该文件由此生成: https://github.com/vuejs/vue-cli/blob/dev/packages/%40vue/cli-service/lib/commands/build/resolveWcEntry.jsopen in new window

因此,要在 Web Components 组件的目标中使用 vuex ,你需要在 App.vue 中初始化存储 (store):

import store from './store';

// ...

export default {
  store,
  name: 'App',
  // ...
};

14. 部署

如果你用 Vue CLI 处理静态资源并和后端框架一起作为部署的一部分,那么你需要的仅仅是确保 Vue CLI 生成的构建文件在正确的位置,并遵循后端框架的发布方式即可。

如果你独立于后端部署前端应用——也就是说后端暴露一个前端可访问的 API,然后前端实际上是纯静态应用。那么你可以将 dist 目录里构建的内容部署到任何静态文件服务器中,但要确保正确的 publicPathopen in new window

14.1 本地预览

dist 目录需要启动一个 HTTP 服务器来访问 (除非你已经将 publicPath 配置为了一个相对的值),所以以 file:// 协议直接打开 dist/index.html 是不会工作的。在本地预览生产环境构建最简单的方式就是使用一个 Node.js 静态文件服务器,例如 serveopen in new window

npm install -g serve
# -s 参数的意思是将其架设在 Single-Page Application 模式下
# 这个模式会处理即将提到的路由问题
serve -s dist

14.2 使用 history.pushState 的路由

如果你在 history 模式下使用 Vue Router,是无法搭配简单的静态文件服务器的。例如,如果你使用 Vue Router 为 /todos/42/ 定义了一个路由,开发服务器已经配置了相应的 localhost:3000/todos/42 响应,但是一个为生产环境构建架设的简单的静态服务器会却会返回 404。

为了解决这个问题,你需要配置生产环境服务器,将任何没有匹配到静态文件的请求回退到 index.html。Vue Router 的文档提供了常用服务器配置指引open in new window

14.3 CORS

如果前端静态内容是部署在与后端 API 不同的域名上,你需要适当地配置 CORSopen in new window

14.4 PWA

如果你使用了 PWA 插件,那么应用必须架设在 HTTPS 上,这样 Service Workeropen in new window 才能被正确注册

14.5 GitHub Pages

  1. vue.config.js 中设置正确的 publicPath

    如果打算将项目部署到 https://<USERNAME>.github.io/ 上, publicPath 将默认被设为 "/",你可以忽略这个参数。

    如果打算将项目部署到 https://<USERNAME>.github.io/<REPO>/ 上 (即仓库地址为 https://github.com/<USERNAME>/<REPO>),可将 publicPath 设为 "/<REPO>/"。举个例子,如果仓库名字为“my-project”,那么 vue.config.js 的内容应如下所示:

    module.exports = {
      publicPath: process.env.NODE_ENV === 'production' ? '/my-project/' : '/',
    };
    
  2. 在项目目录下,创建内容如下的 deploy.sh (可以适当地取消注释) 并运行它以进行部署

    #!/usr/bin/env sh
    
    # 当发生错误时中止脚本
    set -e
    
    # 构建
    npm run build
    
    # cd 到构建输出的目录下
    cd dist
    
    # 部署到自定义域域名
    # echo 'www.example.com' > CNAME
    
    git init
    git add -A
    git commit -m 'deploy'
    
    # 部署到 https://<USERNAME>.github.io
    # git push -f [email protected]:<USERNAME>/<USERNAME>.github.io.git master
    
    # 部署到 https://<USERNAME>.github.io/<REPO>
    # git push -f [email protected]:<USERNAME>/<REPO>.git master:gh-pages
    
    cd -
    

14.6 Doker(Nginx)

在 Docker 容器中使用 Nginx 部署你的应用。

  1. 安装 Dockeropen in new window

  2. 在项目根目录创建 Dockerfile 文件

    FROM node:10
    COPY ./ /app
    WORKDIR /app
    RUN npm install && npm run build
    
    FROM nginx
    RUN mkdir /app
    COPY --from=0 /app/dist /app
    COPY nginx.conf /etc/nginx/nginx.conf
    
  3. 在项目根目录创建 .dockerignore 文件

    设置 .dockerignore 文件能防止 node_modules 和其他中间构建产物被复制到镜像中导致构建问题。

    **/node_modules
    **/dist
    
  4. 在项目根目录创建 nginx.conf 文件

    Nginx 是一个能在 Docker 容器中运行的 HTTP(s) 服务器。它使用配置文件决定如何提供内容、要监听的端口等。参阅 Nginx 设置文档open in new window 以了解所有可能的设置选项。

    下面是一个简单的 Nginx 设置文件,它会在 80 端口上提供你的 Vue 项目。页面未找到 / 404 错误使用的是 index.html,这让我们可以使用基于 pushState() 的路由。

    user  nginx;
    worker_processes  1;
    error_log  /var/log/nginx/error.log warn;
    pid        /var/run/nginx.pid;
    events {
      worker_connections  1024;
    }
    http {
      include       /etc/nginx/mime.types;
      default_type  application/octet-stream;
      log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                        '$status $body_bytes_sent "$http_referer" '
                        '"$http_user_agent" "$http_x_forwarded_for"';
      access_log  /var/log/nginx/access.log  main;
      sendfile        on;
      keepalive_timeout  65;
      server {
        listen       80;
        server_name  localhost;
        location / {
          root   /app;
          index  index.html;
          try_files $uri $uri/ /index.html;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
          root   /usr/share/nginx/html;
        }
      }
    }
    
  5. 构建你的 Docker 镜像

    docker build . -t my-app
    # Sending build context to Docker daemon  884.7kB
    # ...
    # Successfully built 4b00e5ee82ae
    # Successfully tagged my-app:latest
    
  6. 运行你的 Docker 镜像

    这个例子基于官方 Nginx 镜像,因此已经设置了日志重定向并关闭了自我守护进程。它也提供了其他有利于 Nginx 在 Docker 容器中运行的默认设置。更多信息参阅 Nginx Docker 仓库open in new window

    docker run -d -p 8080:80 my-app
    curl localhost:8080
    # <!DOCTYPE html><html lang=en>...</html>
    

Reference

  1. vue-cliopen in new window vue docs
上次编辑于:
评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.15.2