欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

vue cli 3

程序员文章站 2022-03-28 15:56:40
...

介绍

  • Vue CLI 是一个基于 Vue.js 进行快速开发的完整系统
    • 通过 @vue/cli 搭建交互式的项目脚手架。
    • 通过 @vue/cli + @vue/cli-service-global 快速开始零配置原型开发。
    • 一个运行时依赖 (@vue/cli-service) 一个开发环境依赖,局部安装在每个 @vue/cli 创建的项目中。
      • 可升级 ?
      • 基于 webpack 构建,并带有合理的默认配置;
      • 可以通过项目内的配置文件进行配置;
      • 可以通过插件进行扩展。—— cli 插件是用来给 webpack 安装 loader包或插件包的工具,同时它能够自动的完成这些包的配置。
    • 一个丰富的官方插件集合,集成了前端生态中最好的工具。
    • 一套完全图形化的创建和管理 Vue.js 项目的用户界面。
  • Vue CLI 致力于将 Vue 生态中的工具基础标准化。

该系统的组件

CLI

  • @vue/cli 提供了终端里的 vue 命令
  • vue create 快速创建一个新项目的脚手架
  • vue serve 构建新想法的原型
  • vue ui 通过一套图形化界面管理你的所有项目

CLI 服务

  • @vue/cli-service 是一个开发环境依赖,局部安装在每个 @vue/cli 创建的项目中。
  • CLI 服务是构建于 webpack 和 webpack-dev-server 之上
  • 加载其它 CLI 插件的核心服务
  • 一个针对绝大部分应用优化过的内部的 webpack 配置
  • 项目内部的 vue-cli-service 命令,提供 serve、build 和 inspect 命令 (inspect 来审查一个 Vue CLI 项目的 webpack config)

CLI 插件

  • CLI 插件是向你的 Vue 项目提供可选功能的 npm 包,例如 Babel/TypeScript 转译、ESLint 集成、单元测试和 end-to-end 测试等。
  • Vue CLI 插件的名字以 @vue/cli-plugin- (内建插件) 或 vue-cli-plugin- (社区插件) 开头
  • 当你在项目内部运行 vue-cli-service 命令时,它会自动解析并加载 package.json 中列出的所有 CLI 插件。
  • 插件可以作为项目创建过程的一部分,或在后期加入到项目中。

————————————————————————————————————————————————————————————

安装

  • Vue CLI 需要 Node.js 8.9 或更高版本 (推荐 8.11.0+)
  • 可以使用 nvm 或 nvm-windows 在同一台电脑中管理多个 Node 版本。
  • 安装之后,你就可以在命令行中访问 vue 命令。
  • 可以用这个命令来检查其版本是否正确vue --version—— 返回@vue/cli的版本号

————————————————————————————————————————————————————————————

基础

————————————————————————————————————————————————————————————

快速原型开发

  • 可以使用 vue serve 和 vue build 命令对单个 *.vue 文件进行快速原型开发
  • 需要先额外安装一个全局的扩展npm install -g @vue/cli-service-global (不一定需要全局安装)

vue serve

  • 在开发环境模式下零配置为 .js 或 .vue 文件启动一个服务器,用法:vue serve [options] [entry]
  • Options:
    • -o, --open 打开浏览器
    • -c, --copy 将本地 URL 复制到剪切板
    • -h, --help 输出用法信息
  • vue serve 使用了 vue create 的默认设置 (webpack、Babel、PostCSS 和 ESLint)。
  • 入口可以是 main.js、index.js、App.vue 或 app.vue 中的一个,在命令vue serve执行的当前目录下
  • 可以显式地指定入口文件vue serve MyComponent.vuevue serve ccc/first.vue,从命令行执行的位置起
  • 可以提供一个 index.html —— 放在命令运行目录下或其public内,如果没有将使用标准html作为模板
  • 可以提供一个 package.json
  • 可以安装并使用本地依赖
  • 可以通过相应的配置文件配置 Babel、PostCSS 和 ESLint

vue build

  • 在生产环境模式下零配置将目标文件构建成一个生产环境的包并用来部署(一个 .js 或 .vue 文件),用法:vue build [options] [entry],例如:vue build MyComponent.vue (更多路径书写方法见vue serve)
  • 也提供了将组件构建成为一个库或一个 Web Components 组件的能力。(对于4种构建目标的区别将在后文进行研究)
  • Options:
    • -t, --target <target> 构建目标 (app | lib | wc | wc-async, 默认值:app)
    • -n, --name <name> 库的名字或 Web Components 组件的名字 (wc 和 wc-async 默认值:入口文件名,并要求必须有连字符) (lib 时默认值是package.json 中的 "name" 字段)
    • -d, --dest <dir> 输出目录 (默认值:dist)
    • -h, --help 输出用法信息

————————————————————————————————————————————————————————————

创建一个项目

vue create

  • 创建一个由 vue-cli-service 提供支持的新项目,用法create [options] <app-name>,例如:vue create hello-world
  • 在 Windows 上通过 minTTY 使用 Git Bash,交互提示符并不工作 ——?
    • 通过 winpty vue.cmd create hello-world 启动vue create hello-world这个命令
    • 可以通过在 ~/.bashrc 文件中添加以下行来为命令添加别名alias vue='winpty vue.cmd'来支持vue create hello-world,需要重新启动 Git Bash 终端会话以使更新后的 bashrc 文件生效
  • 在操作提示的最后你可以选择将已选项保存为一个将来可复用的 preset
    • 被保存的 preset 将会存在用户的 home 目录下一个名为 .vuerc 的 JSON 文件里。保存多套,操作提示的最后会让你输入每套配置的名称,已被将来选择
  • 在项目创建的过程中,你也会被提示选择喜欢的包管理器或使用淘宝 npm 镜像源以更快地安装依赖。
    • 这些选择也将会存入 ~/.vuerc。
  • 选项:
    • -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 命令以图形化界面创建和管理项目

拉取 2.x 模板 (旧版本)

  • Vue CLI 3 和旧版使用了相同的 vue 命令,所以 Vue CLI 2 (vue-cli) 被覆盖了。
  • 可以全局安装一个桥接工具,使用旧版本的 vue init 功能npm install -g @vue/cli-init ?

————————————————————————————————————————————————————————————

插件和Preset

插件

  • 插件可以修改 webpack 的内部配置,也可以向 vue-cli-service 注入命令
  • 在项目创建的过程中,绝大部分列出的特性都是通过插件来实现的
    • 特性:比如使用ESLint管理状态就是其中一种特性
  • 可以通过 vue ui 命令使用 GUI(图形界面) 安装和管理插件

在现有的项目中安装插件

  • 每个 CLI 插件都会包含一个生成器和一个运行时插件
    • 生成器用来创建文件的(应该是指从npm上安装对应的模块,例如Eslint)
    • 运行时插件用来调整 webpack 核心配置和注入命令的(运行时是指?注入命令是指? )
  • 使用 vue create 来创建一个新项目的时候,有些插件会根据你选择的特性被预安装
  • 在一个已经被创建好的项目中安装一个插件,可以使用 vue add 命令vue add @vue/eslint
    • vue add @vue/cli-plugin-eslint等价
    • 这个命令将 @vue/eslint 解析为完整的包名 @vue/cli-plugin-eslint,然后从 npm 安装它,调用它的生成器。(依然能够向项目中插入插件定义的命令,例如vue add @vue/eslint后会在package中新增"lint": "vue-cli-service lint"命令)
  • 如果不带 @vue 前缀,该命令会换作解析一个 unscoped 的包。例如以下命令会安装第三方插件 vue-cli-plugin-apollo:vue add apollo
    • 可以基于一个指定的 scope(范围) 使用第三方插件。例如如果一个插件名为 @foo/vue-cli-plugin-bar,你可以这样添加它:vue add @foo/bar
  • 可以向被安装的插件传递生成器选项 (这样做会跳过命令提示):vue add @vue/eslint --config airbnb --lintOn save
  • vue-router 和 vuex 的情况比较特殊——它们并没有自己的插件,但是你仍然可以这样添加它们:
vue add router
vue add vuex
  • 如果一个插件已经被安装,你可以使用 vue invoke 命令跳过安装过程,只调用它的生成器。?
    • 这个命令会接受和 vue add 相同的参数。
  • 如果出于一些原因你的插件列在了该项目之外的其它 package.json 文件里,你可以在自己项目的 package.json 里设置 vuePlugins.resolveFrom 选项指向包含其它 package.json 的文件夹。?
// 如果你有一个 .config/package.json 文件

{
  "vuePlugins": {
    "resolveFrom": ".config"
  }
}
  • 推荐在运行 vue add 之前将项目的最新状态提交,因为该命令可能调用插件的文件生成器并很有可能更改你现有的文件。

项目本地的插件 ?

  • 在项目里直接访问插件 API 而不需要创建一个完整的插件 ?
  • 可以通过 vuePlugins.ui 选项添加像 UI 插件一样工作的文件 ?
  • 该章节涉及插件开发和高级应用,暂时看不懂 ?

Preset(预设.vuerc文件)

  • Vue CLI preset 是一个包含创建新项目所需预定义选项和插件的 JSON 对象
  • 在 vue create 过程中保存的 preset 会被放在你的 home 目录下的一个配置文件中 (~/.vuerc)
  • Preset 的数据会被插件生成器用来生成相应的项目文件(Preset中保存这插件以及和这个插件相关的提示符选项所以能够被插件生成器生成相应的项目文件)
  • 可以为集成工具添加配置:这些额外的配置将会根据 useConfigFiles 的值被合并到 package.json 或相应的配置文件中。(当有多个项目通用自定义vue.config.js配置时,可以在这里编辑,方便创建项目时不用重复配置)
{
  "useConfigFiles": true,    // 当 "useConfigFiles": true 的时候,configs 的值将会被合并到 vue.config.js 中
  "plugins": {...},
  "configs": {
    "vue": {...},
    "postcss": {...},
    "eslintConfig": {...},
    "jest": {...}
  }
}

Preset 插件的版本管理

  • 可以显式地指定用到的插件的版本
    • 对于官方插件来说这不是必须的
    • 推荐为 preset 列出的所有第三方插件提供显式的版本范围
{
  "plugins": {
    "@vue/cli-plugin-eslint": {
      "version": "^3.0.0",
      // ... 该插件的其它选项
    }
  }
}

允许插件的命令提示

  • 每个插件在项目创建的过程中都可以注入它自己的命令提示

  • 当你使用了一个 preset,这些命令提示就会被跳过,因为 Vue CLI 假设所有的插件选项都已经在 preset 中声明过了
  • 有些情况下你可能希望 preset 只声明需要的插件,对于这种场景你可以在插件选项中指定 "prompts": true 来允许注入命令提示
{
  "plugins": {
    "@vue/cli-plugin-eslint": {
      // 让用户选取他们自己的 ESLint config
      "prompts": true
    }
  }
}

———————————————————————————

远程 Preset ?

  • 可以通过发布 git repo 将一个 preset 分享给其他开发者。这个 repo 应该包含以下文件:
    • preset.json: 包含 preset 数据的主要文件(必需)。
    • generator.js: 一个可以注入或是修改项目中文件的 Generator(生产者)。
    • prompts.js 一个可以通过命令行对话为 generator 收集选项的 prompts(提示) 文件。
  • 可以在创建项目的时候通过 --preset 选项使用这个远程的 preset
# 从 GitHub repo 使用 preset(GitLab 和 BitBucket 也是支持的)
    # GitHub、GitLab、BitBucket都是仓库管理系统
vue create --preset username/repo my-project
  • 如果要从私有 repo 获取,请确保使用 --clone 选项
vue create --preset gitlab:username/repo --clone my-project
vue create --preset bitbucket:username/repo --clone my-project

————————————————————————————

加载文件系统中的 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

————————————————————————————————————————————————————————————

CLI服务

使用命令

  • Vue CLI 项目中,@vue/cli-service 安装了一个名为 vue-cli-service 的命令
  • 可以在 npm scripts 中以 vue-cli-service访问这个命令(npm scripts 在默认 preset 的项目的 package.json 中已经配置好了)
  • 或者从终端中以 ./node_modules/.bin/vue-cli-service 访问这个命令 ?
  • 可以通过 vue ui 命令使用 GUI(视图界面) 运行更多的特性脚本

vue-cli-service serve

  • vue-cli-service serve 命令会启动一个开发服务器 (基于 webpack-dev-server) 并附带开箱即用的模块热重载 (Hot-Module-Replacement)。
  • 用法vue-cli-service serve [options] [entry]
  • options选项:
    • --open 在服务器启动时打开浏览器
    • --copy 在服务器启动时将 URL 复制到剪切版
    • --mode 指定环境模式 (默认值:development)
    • --host 指定 host (默认值:0.0.0.0)
    • --port 指定 port (默认值:8080)
    • --https 使用 https (默认值:false)
  • 除了通过命令行参数,你也可以使用 vue.config.js 里的 devServer 字段配置开发服务器。(待扩展)

vue-cli-service build

  • vue-cli-service build 会在 ./dist/ 目录产生一个可用于生产环境的包,带有 JS/CSS/HTML 的压缩,和为更好的缓存而做的自动的 vendor chunk splitting(供应商块拆分)。
    • 它的 chunk manifest(块清单) 会内联在 HTML 里。
  • 用法: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" 字段(lib库时)或入口文件名(Web Components 模式时))
    • --no-clean 在构建项目之前不清除目标目录
    • --report 生成 report.html 以帮助分析包内容的大小
    • --report-json 生成 report.json 以帮助分析包内容的大小
    • --watch 监听文件变化(会生成一个热打包的服务)
"build": "vue-cli-service build --target lib ./src/components/HelloWorld.vue"

vue-cli-service inspect

  • 使用 vue-cli-service inspect 来审查一个 Vue CLI 项目的 webpack config
  • 用法:vue-cli-service inspect [options] [...paths] 例如:npx vue-cli-service inspect configureWebpack用来检查webpack中configureWebpack选项的值
  • 选项:
    • --mode 指定环境模式 (默认值:development)

查看所有的可用命令

  • 有些 CLI 插件会向 vue-cli-service 注入额外的命令
  • 可以运行以下命令查看所有注入的命令:npx vue-cli-service help
  • 可以这样学习每个命令可用的选项:npx vue-cli-service help [command]

缓存和并行处理

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

——————————————————————————————————————

Git Hook ?

  • @vue/cli-service 会安装 yorkie,它会让你在 package.json 的 gitHooks 字段中方便地指定 Git hook:
    • Git hook(Git 钩子)能在特定的重要动作发生时触发自定义脚本。
    • yorkie 是husky的一个自定义分支和未来版本的husky不兼容
{
  "gitHooks": {
    "pre-commit": "lint-staged"
  }
}

——————————————————————————————————————

配置时无需 Eject

  • 通过 Vue CLI 创建的项目让你无需 eject(计算机命令) 就能够配置工具的几乎每个角落

————————————————————————————————————————————————————————————

开发

————————————————————————————————————————————————————————————

浏览器兼容性

browserslist

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

Polyfill(是一块代码,用来为旧浏览器提供它没有原生支持的较新的功能。)

useBuiltIns: 'usage'(@vue/babel-preset-env 的配置)

  • 一个默认的 Vue CLI 项目会使用 @vue/babel-preset-app,它通过 @babel/preset-env 和 browserslist 配置来决定项目需要的 polyfill
    • 默认情况下,它会把 useBuiltIns: 'usage' 传递给 @babel/preset-env,这样它会根据源代码中出现的语言特性自动检测需要的 polyfill。这确保了最终包里 polyfill 数量的最小化。
    • 如果其中一个依赖需要特殊的 polyfill,默认情况下 Babel 无法将其检测出来
  • 依赖需要 polyfill,你有几种选择
    • 如果该依赖交付 ES5 代码,但使用了 ES6+ 特性且没有显式地列出需要的 polyfill (例如 Vuetify):请使用 useBuiltIns: 'entry' 然后在入口文件添加 import '@babel/polyfill'。会根据 browserslist 目标导入所有 polyfill
      • Vuetify:一种 vue 移动端的组件库
      • Vuetify应该交付了遵循ES5语法的代码(例如:使用var而不是let,cont),但是却用了ES5不支持的对象或对象方法,所以不需要再对该依赖进行语法转换,而只需要导入 polyfill用来支持ES6+ 的新特性
      • 但第二种方法也能实现这个功能,而且更好,为什么要采用这种呢?
    • 如果该依赖基于一个目标环境不支持的 ES 版本撰写: 将其添加到 vue.config.js 中的 transpileDependencies 选项。这会为该依赖同时开启语法语法转换和根据使用情况检测 polyfill
    • 如果该依赖交付了 ES5 代码并显式地列出了需要的 polyfill: 你可以使用 @vue/babel-preset-app 的 polyfills 选项预包含所需要的 polyfill。(如下在 babel.config.js配置)
      • es6.promise 将被默认包含,因为现在的库依赖 Promise 是非常普遍的。
      • 推荐以这种方式添加 polyfill 而不是在源代码中直接导入它们,因为如果这里列出的 polyfill 在 browserslist 的目标中不需要,则它会被自动排除。
module.exports = {
  transpileDependencies: [
    'webpack-dev-server/client',
  ],
}
// babel.config.js
module.exports = {
  presets: [
    ['@vue/app', {
      polyfills: [
        'es6.promise',
        'es6.symbol' // 一种新的数据类型,表示独一无二的对象
      ]
    }]
  ]
}

构建库或是 Web Component 时的 Polyfills

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

现代模式

  • vue-cli-service build --modern会产生两个应用的版本:一个现代版的包,面向支持 ES modules 的现代浏览器,另一个旧版的包,面向不支持的旧浏览器。
  • 现代版的包会通过 <script type="module"> 在被支持的浏览器中加载;
    • <script type="module"> 代码会被当作JavaScript模块
    • 需要配合始终开启的 CORS 进行加载,这意味着服务器必须返回诸如 Access-Control-Allow-Origin: * 的有效的 CORS 头。(模块加载都是通过CORS获得的所以始终需要服务器返回)
    • 如果需要通过认证来获取脚本,可以将script标签的 crossorigin 选项设置为 use-credentials(文档中为了获取该JS文件而发起的请求会带cookie,通过 vue.config.js的 crossorigin 配置)
<script type="module">
  import message from './message.js'

  console.log(message) // hello world
</script>

<script nomodule>
  alert('your browsers can not supports es modules! please upgrade it.')
</script>

<script src="" crossorigin="use-credentials"></script>
  • 它们还会使用 <link rel="modulepreload" href=''> 让浏览器优先加载模块
    • modulepreload的行为类似于preload
    • preload能够指明哪些资源是在页面加载完成后即刻需要的。对于这种即刻需要的资源在页面加载的生命周期的早期阶段就开始获取,在浏览器的主渲染机制介入前就进行预加载。
  • 旧版的包会通过 <script nomodule> 加载,并会被支持 ES modules 的浏览器忽略。(这是因为浏览器默认只解析 type="text/javascript"的脚本,而如果不填写type属性则默认为text/javascript。这样就实现了向前兼容)
  • 一个针对 Safari 10 中 <script nomodule> 的修复会被自动注入(使用一段内联脚本来避免 Safari 10 重复加载脚本包)。
    • 如果你在使用一套严格的 CSP(内容安全策略),你需要这样显性地允许内联脚本:Content-Security-Policy: script-src 'self' 'sha256-4RS22DYeB7U14dra4KcQYxmwt5HkOInieXK1NUMBmQI='
      • 内联脚本是指<script src=''>和嵌入式脚本进行区分
      • Content-Security-Policy:一种响应报头。原本页面上带src的元素资源是不受同源限制的,Content-Security-Policy是改变这种现状的新安全策(内容安全政策)
      • script-src代表是一个指令,指示浏览器你只能加载我屁股后面那些规则下的js代码,其他的都一律拒绝。
      • 'self' 表示与当前来源(而不是其子域)匹配。
      • 最后的base64字符串是一份白名单,和script的特定属性配合使用,标明哪些内联js允许通过(白名单)
      • Content-Security-Policy(内容安全策略还有其他应用方式,将在专题中深入)
<script nonce=EDNnf03nceIOfn39fn3e9h3sdfa src=''></script>

Content-Security-Policy: script-src 'nonce-EDNnf03nceIOfn39fn3e9h3sdfa' // 指明了属性名为nonce

————————————————————————————————————————————————————————————

HTML和静态资源

HTML

Index 文

  • public/index.html 文件是一个会被 html-webpack-plugin 处理的模板。在构建过程中,资源链接会被自动注入。
  • 会自动注入 resource hint 资源提示(preload/prefetch、manifest 和图标链接 (当用到 PWA 插件时) 以及构建过程中处理的 JavaScript 和 CSS 文件的资源链接。
    • manifest:属性规定文档的缓存 manifest 的位置。是一个简单的文本文件,列举出了浏览器用于离线访问而缓存的资源。
      • 每个指定了 manifest 的页面在用户对其访问时都会被缓存。也可以在文件中声明要缓存的页面
      • manifest 文件的建议的文件扩展名是:".appcache"。(具体将在在缓存章节整理)
    • 图标链接 (当用到 PWA 插件时) ?
<!DOCTYPE HTML>
<html manifest="demo.appcache">
...
</html>

插值

  • 因为 index 文件被用作模板,所以你可以使用 lodash 的 template 语法插入内容: ?
    • lodash:一个一致性、模块化、高性能的 JavaScript 实用工具库。
    • <%= VALUE %> 用来做不转义插值;
    • <%- VALUE %> 用来做 HTML 转义插值;
    • <% expression %> 用来描述 JavaScript 流程控制。
// 不转义插值
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<p><%= 'abc' %></p>

// HTML 转义插值


// 用来描述 JavaScript 流程控制
<% for (var index in htmlWebpackPlugin.options.title) { %>
<p><%= htmlWebpackPlugin.options.title[index] %></p>
<% } %>
  • 可以使用被 html-webpack-plugin 暴露的默认值 ()
    • vue.config.js 内 pages 各个页面的参数?
  • 所有客户端环境变量也可以直接使用 (后文介绍到,在.env文件中设置,必须以 VUE_APP_ 开头)

Preload

  • <link rel="preload"> 是一种 resource hint(资源提示),用来指定页面加载后很快会被用到的资源
  • 默认情况下,一个 Vue CLI 应用会为所有初始化渲染需要的文件自动生成 preload 提示。
  • 这些提示会被 @vue/preload-webpack-plugin 注入,并且可以通过 chainWebpack 的 config.plugin('preload') 进行修改和删除(详情见下文)

Prefetch

  • <link rel="prefetch"> 是一种 resource hint,用来告诉浏览器在页面加载完成后,利用空闲时间提前获取用户未来可能会访问的内容。
  • 默认情况下,一个 Vue CLI 应用会为所有作为 async chunk 生成的 JavaScript 文件自动生成 prefetch 提示。(import()方法导入的异步模块,详见模块导入导出)
  • 这些提示会被 @vue/preload-webpack-plugin 注入,并且可以通过 chainWebpack 的 config.plugin('prefetch') 进行修改和删除。
    • 删除后,异步模块依然会被单独打包,但是 webpack 的运行时不会在父级区块被加载之后注入
// 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 的内联注释手动选定要提前获取的代码区块,webpack 的运行时会在父级区块被加载之后注入 prefetch 链接
    • 如果你的应用很大且有很多 async chunk,而用户主要使用的是对带宽较敏感的移动端,那么你可能需要关掉 prefetch 链接并手动选择要提前获取的代码区块。
    • 在webpack提供的注释中并没有这个选项;被禁用是指上文提到的移除这个插件吗?
import(/* webpackPrefetch: true */ './someAsyncComponent.vue')

————————————————————————————————————————

不生成 index

  • 当基于已有的后端使用 Vue CLI 时,你可能不需要生成 index.html,这样生成的资源可以用于一个服务端渲染的页面
    • 硬编码的文件名不利于实现高效率的缓存控制。(什么是服务端渲染?组件在服务端渲染后还需要缓存吗?)
    • 硬编码的文件名也无法很好的进行 code-splitting (代码分段),因为无法用变化的文件名生成额外的 JavaScript 文件。?
    • 硬编码的文件名无法在现代模式下工作。?
  • 应该考虑换用 indexPath 选项将生成的 HTML 用作一个服务端框架的视图模板。
// vue.config.js
module.exports = {
  // 去掉文件名中的 hash,服务端引用可以写死
  filenameHashing: false,
  // 删除 HTML 相关的 webpack 插件
  chainWebpack: config => {
    config.plugins.delete('html')    // 仅仅删除html会导致preload和prefetch注入时报错
    config.plugins.delete('preload')
    config.plugins.delete('prefetch')
  }
}

————————————————————————————————————————

构建一个多页应用

  • Vue CLI 支持使用 vue.config.js 中的 pages 选项构建一个多页面的应用。
  • 构建好的应用将会在不同的入口之间高效共享通用的 chunk 以获得最佳的加载性能。
// vue.config.js配置
module.exports = {
  pages: {
    index: {
      // page 的入口
      entry: 'src/index/main.js',
      // 模板来源
      template: 'public/index.html',
      // 在 dist/index.html 的输出
      // 也可以写为`'index/index.html'`这样,html会被打包到index目录内,但是引用的其他资源不会归纳到一起。感觉没有任何意义
      // 如果需要部署到根目录以下的次一级目录中,应当修改 process.env.BASE_URL 值,并修改所有绝对引用。所以使用绝对引用时,应当以 process.env.BASE_URL 为前缀。见下文 public 文件夹 的详细说明
      filename: 'index.html',
      // 当使用 title 选项时,
      // template 中的 title 标签需要是 <title><%= htmlWebpackPlugin.options.title %></title>
      title: 'Index Page',
      // 在这个页面中包含的块,默认情况下会包含
      // 提取出来的通用 chunk 和 vendor chunk。
      chunks: ['chunk-vendors', 'chunk-common', 'index']
    },
    one: {
      entry: 'src/one/main.js',
      template: 'public/index.html',
      filename: 'one.html',
      title: 'one Page',
      chunks: ['chunk-vendors', 'chunk-common', 'index']
    },
    // 当使用只有入口的字符串格式时,
    // 模板会被推导为 `public/subpage.html`
    // 并且如果找不到的话,就回退到 `public/index.html`。
    // 输出文件名会被推导为 `subpage.html`。
    subpage: 'src/subpage/main.js'
  }
}

// 每个单页面的路由
export default new Router({
  mode: 'history',
  // 会依据基路由的path去服务器上获取对应名称的html,然后才调用该页面的路由实行渲染。例如,这里会先获取one.html页面,然后加载之上的路由实现渲染。
  // 搜索范围可能包括`/one.html``/one/index.html``/one/one.html`
  base: `${process.env.BASE_URL}one`, 
  routes: [
    {
      path: '/',
      name: 'home',
      component: Home
    },
    {
      path: '/about',
      name: 'about',
      // route level code-splitting 路由级代码拆分
      // this generates a separate chunk (about.[hash].js) for this route 这将为此路由生成一个单独的块 about.[hash].js
      // which is lazy-loaded when the route is visited. 当路线被访问时,它被延迟加载
      component: () => import(/* webpackChunkName: "about" */ './views/About.vue')
    }
  ]
})

// 通过a标签在页面中实现跨页面跳转
<a href="/one/about">one About</a>

处理静态资源

  • 在 JavaScript 被导入或在 template/CSS 中通过相对路径被引用。这类引用会被 webpack 处理。
    • 在 JavaScript 被导入
      • babel 语法转换和引入polyfill
      • chunk:分为供应商块(通过npm安装的块)、通用块(项目内自定义公共组件到达一定的代码级别才独立打包,小型的公共组件会被独立打包到各个异步块中)、异步块、app块(bundle 块)
      • base64文件名,实现优化缓存
    • template 中通过相对路径被引用,例如<img src="...">
      • base64文件名,实现优化缓存
      • 图片转为data内嵌
    • CSS 中通过相对路径被引用
      • 图片转为data内嵌
      • 本地环境css都是被内嵌的
      • 依据目标浏览器添加前缀
      • base64文件名,实现优化缓存
      • css中的引用也会被处理,例如background-image: url(./assets/bg.gif);
  • 本地开发环境服务时css样式都是嵌入式的,并且都不做base64文件名处理
  • 放置在 public 目录下或通过绝对路径被引用。这类资源将会直接被拷贝,而不会经过 webpack 的处理。

从相对路径导入

  • 在 JavaScript、CSS 或 *.vue 文件中使用相对路径 (必须以 . 开头) 引用一个静态资源时,该资源将会被包含进入 webpack 的依赖图中。
    • 通过webpack设置的@路径,本质上也是相对路径引用
    • ../ 代表上一级目录,也是以.开头的
  • 在其编译过程中,所有诸如 <img src="...">、background: url(...) 和 CSS @import 的资源 URL 都会被解析为一个模块依赖。
    • @import url("fineprint.css");
<img src="./image.png">
// 将会被编译为:
h('img', { attrs: { src: require('./image.png') }})
  • 在其内部,我们通过 file-loader 用版本哈希值和正确的公共基础路径来决定最终的文件路径(公共基础路径是指,例如打包好的js会被统一放到dist\js文件夹中,那么html内部的引用也要随之改变)
  • 再用 url-loader 将小于 4kb 的资源内联,以减少 HTTP 请求的数量。
  • 可以通过 chainWebpack 调整内联文件的大小限制。例如,下列代码会将其限制设置为 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 }))
  }
}

URL 转换规则

  • 如果 URL 是一个绝对路径 (例如 /images/foo.png),它将会被保留不变。例如:<img alt="Vue logo" src="/assets/logo.png">(该路径指向的资源及路径本身不会被处理,即html被加载后依然向 /assets/去请求logo.png这个图片)
  • 如果 URL 以 . 开头,它会作为一个相对模块请求被解释且基于你的文件系统中的目录结构进行解析。
  • 如果 URL 以 ~ 开头,其后的任何内容都会作为一个模块请求被解析。这意味着你甚至可以引用 Node 模块中的资源(只发现 css下的用法,不明白这句什么含义?)
  • 如果 URL 以 @ 开头,它也会作为一个模块请求被解析。它的用处在于 Vue CLI 默认会设置一个指向 <projectRoot>/src 的别名 @。仅作用于模版中?(并不只在模板中被使用,JS中也可以使用,CSS比较特殊请参考后文)

public 文件夹

  • 任何放置在 public 文件夹的静态资源都会被简单的复制,而不经过 webpack。你需要通过绝对路径来引用它们。(会被直接复制到 /dist 目录下)
  • 我们推荐将资源作为你的模块依赖图的一部分导入,这样它们会通过 webpack 的处理并获得如下好处:
    • 脚本和样式表会被压缩且打包在一起,从而避免额外的网络请求。
    • 文件丢失会直接在编译时报错,而不是到了用户端才产生 404 错误。
    • 最终生成的文件名包含了内容哈希,因此你不必担心浏览器会缓存它们的老版本。
    • 会自动生成合适的引用路径,不必担心部署问题
  • public 目录提供的是一个应急手段,当你通过绝对路径引用它时,留意应用将会部署到哪里。如果你的应用没有部署在域名的根部,那么你需要为你的 URL 配置 publicPath 前缀:
    • 绝对路径引用时必须以 process.env.BASE_URL 为前缀,这是为了方便项目的日后维护
    • 可以把process.env.BASE_URL视为项目部署时的前缀目录
// 在 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`">

何时使用 public 文件夹

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

————————————————————————————————————————————————————————————

CSS相关

  • Vue CLI 项目天生支持 PostCSS、CSS Modules 和包含 Sass、Less、Stylus 在内的预处理器

引用静态资源

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

// 用 webpack alias(别名)
background-image: url([email protected]/one/assets/logo.png);

预处理器

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

# Less
npm install -D less-loader less

# Stylus
npm install -D stylus-loader stylus
  • 然后你就可以导入相应的文件类型,或在 *.vue 文件中这样来使用:
<style lang="scss">
$color: red;
</style>

自动化导入

  • 自动化导入文件 (用于颜色、变量、mixin……),你可以使用 style-resources-loader。这里有一个关于 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'),
      ],
    })
}

PostCSS

  • 可以通过 .postcssrc 或任何 postcss-load-config 支持的配置源来配置 PostCSS
    • package.json (vue create初始化项目时可以选择)
    • .postcssrc.json 或 .postcssrc.yml 或 .postcssrc.js
    • postcss.config.js (脚手架默认)
  • 也可以通过 vue.config.js 中的 css.loaderOptions.postcss 配置 postcss-loader。
  • 我们默认开启了 autoprefixer。在生产环境构建中,Vue CLI 会优化 CSS 并基于目标浏览器抛弃不必要的浏览器前缀规则。
  • 如果要配置目标浏览器,可使用 package.json 的 browserslist 字段或 .browserslistrc 这个文件

CSS Modules

  • 可以通过 <style module> 以开箱即用的方式在 *.vue 文件中使用 CSS Modules。
    • 已经默认通过向 css-loader 传入 modules: true 开启
    • CSS Modules 把 CSS 类名的集合作为计算属性 $style 绑定到 VUE 实例上
    • 即使是嵌套中的类名都会进行处理,例如下例中的 bold
    • 这是一个计算属性,所以它支持 :class 的对象/数组语法绑定,也可以通过 JavaScript 访问到它
    • CSS Modules 默认基于文件名和类名生成标识符,例如:App_app_7GWKZ
    • CSS Modules 和 scoped CSS 的区别在于前者是动态生成 CSS类名,而后者是通过添加特性配合CSS属性选择器使用
<style module>
.red {
  color: red;
  .bold {
    font-weight: bold;
  }
}

</style>

<template>
  <p :class="$style.red">
    This should be red
  </p>
</template>
  • 在 .vue 中你可以定义不止一个 <style>,为了避免被覆盖,你可以通过设置 module 属性来为它们定义注入后计算属性的名称。
<style module="a">    // 通过this.a来访问
  /* 注入标识符 a */
</style>

<style module="b">    // 通过this.b来访问
  /* 注入标识符 b */
</style>
  • 在 JavaScript 中作为 CSS Modules 导入 CSS 或其它预处理文件,该文件应该以 .module.(css|less|sass|scss|styl) 结尾
import styles from './foo.module.css'    // 导入的文件必须带有module,返回一个对象,作为计算属性的返回使用
// 所有支持的预处理器都一样工作
import sassStyles from './foo.module.scss'
  • 如果你想去掉文件名中的 .module,可以设置 vue.config.js 中的 css.modules 为 true
// vue.config.js
module.exports = {
  css: {
    modules: true
  }
}
  • 如果你希望自定义生成的 CSS Modules 模块的类名,可以通过 vue.config.js 中的 css.loaderOptions.css 选项来实现。所有的 css-loader 选项在这里都是支持的,例如 localIdentName 和 camelCase
// vue.config.js
module.exports = {
  css: {
    loaderOptions: {
      css: {
        localIdentName: '[name]-[hash]',
        camelCase: 'only'    // 驼峰式?
      }
    }
  }
}

向预处理器 Loader 传递选项

  • 向 webpack 的预处理器 loader 传递选项。
    • 例如:向 Sass 样式传入共享的全局变量
// vue.config.js
module.exports = {
  css: {
    loaderOptions: {
      // 给 sass-loader 传递选项
      sass: {
        // @/ 是 src/ 的别名
        // 所以这里假设你有 `src/variables.scss` 这个文件
        data: `@import "@/variables.scss";`
      }
    }
  }
}
  • 这样做比使用 chainWebpack 手动指定 loader 更推荐,因为这些选项需要应用在使用了相应 loader 的多个地方。
  • Loader 可以通过 loaderOptions 配置,包括:
    • css-loader
    • postcss-loader
    • sass-loader
    • less-loader
    • stylus-loader

————————————————————————————————————————————————————————————

webpack相关

简单的配置方式

  • 调整 webpack 配置最简单的方式就是在 vue.config.js 中的 configureWebpack 选项提供一个对象:
// vue.config.js
module.exports = {
  configureWebpack: {
    plugins: [
      new MyAwesomeWebpackPlugin()
    ]
  }
}
  • 有些 webpack 选项是基于 vue.config.js 中的值设置的,所以不应该直接修改。因为 vue.config.js 中的值会被用在配置里的多个地方,以确保所有的部分都能正常工作在一起。
  • 如果你需要基于环境有条件地配置行为,或者想要直接修改配置,那就换成一个函数 (该函数会在环境变量被设置之后懒执行)。该方法的第一个参数会收到已经解析好的配置。在函数内,你可以直接修改配置,或者返回一个将会被合并的对象:
// vue.config.js
module.exports = {
  configureWebpack: config => {
    if (process.env.NODE_ENV === 'production') {
      // 为生产环境修改配置...
    } else {
      // 为开发环境修改配置...
    }
  }
}

链式操作 (高级)

  • Vue CLI 内部的 webpack 配置是通过 webpack-chain 维护的。它允许我们更细粒度的控制其内部配置。

修改 Loader 选项

// vue.config.js
module.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 可以确保你通过一个地方影响所有的规则。

添加一个新的 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

// 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')
  }
}

修改插件选项

// vue.config.js
module.exports = {
  chainWebpack: config => {
    config
      .plugin('html')
      .tap(args => {
        return [/* 传递给 html-webpack-plugin's 构造函数的新参数 */]
      })
  }
}
  • 将 index.html 默认的路径从 /Users/username/proj/public/index.html 改为 /Users/username/proj/app/templates/index.html。
// vue.config.js
module.exports = {
  chainWebpack: config => {
    config
      .plugin('html')
      .tap(args => {
        args[0].template = '/Users/username/proj/app/templates/index.html'
        return args
      })
  }
}

审查项目的 webpack 配置

  • 全局的 vue 可执行程序同样提供了 inspect 命令,这个命令只是简单的把 vue-cli-service inspect 代理到了你的项目中。
  • 将其输出重定向到一个文件以便进行查阅:(输出的并不是一个有效的 webpack 配置文件,而是一个用于审查的被序列化的格式。)
vue inspect > output.js
  • 可以通过指定一个路径来审查配置的一小部分:
# 只审查第一条规则
vue inspect module.rules.0
  • 指向一个规则或插件的名字:
vue inspect --rule vue
vue inspect --plugin html
  • 可以列出所有规则和插件的名字:
vue inspect --rules
vue inspect --plugins

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

  • 有些外部工具可能需要通过一个文件访问解析好的 webpack 配置,比如那些需要提供 webpack 配置路径的 IDE 或 CLI。
    • IDE:集成开发环境
    • CLI:命令行界面
// 该文件会动态解析并输出 vue-cli-service 命令中使用的相同的 webpack 配置,包括那些来自插件甚至是你自定义的配置。
<projectRoot>/node_modules/@vue/cli-service/webpack.config.js

————————————————————————————————————————————————————————————

环境变量和模式

  • 可以替换你的项目根目录中的下列文件来指定环境变量:
.env                # 在所有的环境中被载入
.env.local          # 在所有的环境中被载入,但会被 git 忽略
.env.[mode]         # 只在指定的模式中被载入,例如生产环境:.env.production
.env.[mode].local   # 只在指定的模式中被载入,但会被 git 忽略
  • 一个环境文件只包含环境变量的“键=值”对:
FOO=bar
VUE_APP_SECRET=secret
  • 被载入的变量将会对 vue-cli-service 的所有命令、插件和依赖可用。
  • 为一个特定模式准备的环境文件的将会比一般的环境文件拥有更高的优先级。
  • Vue CLI 启动时已经存在的环境变量拥有最高优先级,并不会被 .env 文件覆写。(例如:BASE_URL,Vue CLI 启动时是指vue-cli-service执行时传入的参数?已经存在的环境变量是指配置文件的部分参数,例如publicPath(用于配置BASE_URL)?)
  • 如果在环境中有默认的 NODE_ENV,你应该移除它或在运行 vue-cli-service 命令的时候明确地设置 NODE_ENV。?

模式

  • 一个 Vue CLI 项目有三个模式:
    • development 模式用于 vue-cli-service serve
    • production 模式用于 vue-cli-service build 和 vue-cli-service test:e2e
    • test 模式用于 vue-cli-service test:unit
  • 模式不同于 NODE_ENV,一个模式可以包含多个环境变量。每个模式仅仅只是会将 NODE_ENV 的值设置为模式的名称
  • 可以通过为 .env 文件增加后缀来设置某个模式下特有的环境变量
  • 可以通过传递 --mode 选项参数为命令行覆写默认的模式。
"dev-build": "vue-cli-service build --mode development"

示例:Staging 模式

  • 可以在系统环境变量中定义 NODE_ENV,这样可以实现都使用 production(生产)环境的配置打包,但是传入不同的环境变量
NODE_ENV=production
// 其他环境变量

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

  • 只有以 VUE_APP_ 开头的变量会被 webpack.DefinePlugin 静态嵌入到客户端侧的包中(只有嵌入到客户端侧的包中才能被模板使用)
// 可以在应用的代码中这样访问它们:
console.log(process.env.VUE_APP_SECRET)
  • 在构建过程中,process.env.VUE_APP_SECRET 将会被相应的值所取代。
  • 在你的应用代码中始终可用的还有两个特殊的变量:
    • NODE_ENV - 会是 "development"、"production" 或 "test" 中的一个。(可以在.env自定义)
    • BASE_URL - 会和 vue.config.js 中的 publicPath 选项相符,即你的应用会部署到的基础路径。
  • 可以在 vue.config.js 文件中计算环境变量。它们仍然需要以 VUE_APP_ 前缀开头。(通过 process.env 直接设置环境变量)
// 获取package.json 中的版本信息,设置为环境变量
process.env.VUE_APP_VERSION = require('./package.json').version

只在本地有效的变量

  • 含有.local的为本地环境文件,本地环境文件默认会被忽略,且出现在 .gitignore 中。
    • .gitignore Git忽略提交规则

————————————————————————————————————————————————————————————

构建目标

应用

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

  • 在库模式中,项目的 publicPath(基础路径) 是根据主文件的加载路径动态设置的(用以支持动态的资源加载能力)。
    • 基础路径似乎只在非模块引用时需要运用,模块化图片和 ‘应用’ 的处理方式一致,非模块化图片会保留路径,但是不会复制 public 文件夹内容到 dist 中
  • 这个功能用到了 document.currentScript,而 IE 浏览器并不支持这一特性。建议使用库之前先在页面上引入 current-script-polyfill。
    • document.currentScript 返回其所包含的脚本中正在被执行的 script 元素
  • 在库模式中,Vue 是外置的。这意味着包中不会有 Vue
    • 同理依赖 vuex 和 其他库编写的库都需要引入相应的依赖
    • 库返回的是组件的配置,需要通过 Vue 实例化
  • 如果这个库会通过一个打包器使用,它将尝试通过打包器以依赖的方式加载 Vue;否则就会回退到一个全局的 Vue 变量。
  • 入口可以是一个 .js 或一个 .vue 文件。如果没有指定入口,则会使用 src/App.vue
  • 构建一个库会输出:
    • dist/myLib.common.js:一个给打包器用的 CommonJS 包 (不幸的是,webpack 目前还并没有支持 ES modules 输出格式的包)
      • 在项目默认配置中使用 require 引用 CommonJS 包时会出现Cannot assign to read only property 'exports' of object '#<Object>'报错,是源于 babel 默认配置不支持在一个文件中同时使用 ES6 和 CommonJS 的导入导出
    • dist/myLib.umd.js:一个直接给浏览器或 AMD loader 使用的 UMD 包
      • AMD loader 浏览器异步模块解决方案
      • UMD 通用模块规范,兼容 AMD 和 CommonJS
      • 会暴露一个以库名为名称的全局变量,例如:‘myLib’
    • dist/myLib.umd.min.js:压缩后的 UMD 构建版本
    • dist/myLib.css:提取出来的 CSS 文件 (可以通过在 vue.config.js 中设置 css: { extract: false } 强制内联)
      • css 默认采用 scoped CSS 做为模块化方案

Vue vs. JS/TS 入口文件

  • 当使用一个 .vue 文件作为入口时,你的库会直接暴露这个 Vue 组件本身,因为组件始终是默认导出的内容。
  • 当你使用一个 .js 或 .ts 文件作为入口时,它可能会包含具名导出,所以库会暴露为一个模块。
    • 可以通过 window.yourLib.default 访问(yourLib为模块名)
    • 或者在 CommonJS 构建中通过 const myLib = require('mylib').default 访问
  • 如果你没有任何具名导出并希望直接暴露默认导出,你可以在 vue.config.js 中使用以下 webpack 配置:
module.exports = {
  configureWebpack: {
    output: {
      libraryExport: 'default'
    }
  }
}

Web Components 组件

  • 这个模式允许你的组件的使用者以一个普通 DOM 元素的方式使用这个 Vue 组件
  • 将一个vue组件注册为一个自定义元素,不支持 IE11 及更低版本。
  • 在 Web Components 模式中,Vue 是外置的。这里的包会假设在页面中已经有一个可用的全局变量 Vue。
  • 这里的入口应该是一个 *.vue 文件。
  • Vue CLI 将会把这个组件自动包裹并注册为 Web Components 组件,无需在 main.js 里自行注册。
  • 该构建将会产生一个单独的 JavaScript 文件 (及其压缩后的版本) 将所有的东西都内联起来。
  • 当这个脚本被引入网页时,会注册自定义组件 <my-element>,其使用 @vue/web-component-wrapper 包裹了目标的 Vue 组件。这个包裹器会自动代理属性、特性、事件和插槽。
<script src="https://unpkg.com/vue"></script>
<script src="path/to/my-element.js"></script>

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

注册多个 Web Components 组件的包

  • 当你构建一个 Web Components 组件包的时候,你也可以使用一个 glob 表达式作为入口指定多个组件目标:
  • 当你构建多个 web component 时,--name 将会用于设置前缀,同时自定义元素的名称会由组件的文件名推导得出。比如一个名为 HelloWorld.vue 的组件携带 --name foo 将会生成的自定义元素名为 <<oo-hello-world>
vue-cli-service build --target wc --name foo 'src/components/*.vue'

异步 Web Components 组件

  • 异步 Web Components 模式会生成一个 code-split(代码分割) 的包,带一个只提供所有组件共享的运行时,并预先注册所有的自定义组件小入口文件。一个组件真正的实现只会在页面中用到自定义元素相应的一个实例时按需获取:
vue-cli-service build --target wc-async --name foo 'src/components/*.vue'

dist/foo.0.min.js 
dist/foo.min.js 
dist/foo.1.min.js   
dist/foo.js   
dist/foo.0.js 
dist/foo.1.js    


<script src="https://unpkg.com/vue"></script>
<script src="path/to/foo.min.js"></script>
<!-- foo-one 的实现的 chunk 会在用到的时候自动获取 -->
<foo-one></foo-one>

————————————————————————————————————————————————————————————

部署

通用指南

本地预览

使用 history.pushState 的路由

CORS

PWA

Platform Guides

GitHub Pages

GitLab Pages

Netlify

Amazon S3

Firebase

Now

Stdlib

Heroku

Surge

Bitbucket Cloud