webpack 是前端项目常用的模块打包器,但不少开发者对 webpack 通常一年只接触两次,剩下的时间就 "只管用"了。接下来作者将带着大家重温下webpack以及内置webpack的vue-cli。
webpack基础配置
webpack简介及作用
本质上, webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具。当 webpack 处理应用程序时,它会在内部从一个或多个入口点构建一个 依赖图(dependency graph),然后将你项目中所需的每一个模块组合成一个或多个 bundles,它们均为静态资源,用于展示你的内容。
webpack的安装与基本配置
安装
npm install webpack webpack-cli --save-dev
不推荐 全局安装 webpack。这会将你项目中的 webpack 锁定到指定版本,并且在使用不同的 webpack 版本的项目中, 可能会导致构建失败。
基本webpack.config.js文件配置
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
},
};
通过配置文件执行构建
npx webpack --config webpack.config.js
常用的webpack配置项
入口(entry)
入口起点(entry point)用来告诉 webpack 使用哪个模块来作为内部依赖图的构建开始。进入入口起点后,webpack 将会找出入口起点(直接和间接)依赖的模块和库。 入口起点可以只有一个也可以有多个
module.exports = {
// (简写)语法 单个入口
entry: './path/to/my/entry/file.js', //entry 默认值是./src/index.js
// 也可以将一个文件路径数组传递给 entry 属性
// entry: ['./src/file_1.js', './src/file_2.js'],
// 也可以采用对象语法。 对象语法会比较繁琐。然而,这是应用程序中定义入口的最可扩展的方式。
entry: {
app: './src/app.js',
adminApp: './src/adminApp.js',
},
};
用于描述入口的对象。你可以使用如下属性:
-
dependOn: 当前入口所依赖的入口。它们必须在该入口被加载前被加载。(不能是循环引用的) -
filename: 指定要输出的文件名称。 -
import: 启动时需加载的模块。 -
library: 指定 library 选项,为当前 entry 构建一个 library。 -
runtime: 运行时 chunk 的名字。如果设置了,就会创建一个新的运行时 chunk。在 webpack 5.43.0 之后可将其设为 false 以避免一个新的运行时 chunk。(runtime 不能指向已存在的入口名称) -
publicPath: 当该入口的输出文件在浏览器中被引用时,为它们指定一个公共 URL 地址。
输出(output)
output 指定 webpack 构建后所创建的 bundle 位置,以及命名规则。主要输出文件的默认值是 ./dist/main.js ,其他生成文件默认放置在 ./dist 文件夹中。(只能指定一个 output 配置)
const path = require('path');
module.exports = {
entry: './path/to/my/entry/file.js',
output: {
path: path.resolve(__dirname, 'dist'), // 生成的文件位置
filename: 'my-first-webpack.bundle.js', // 生成的文件名
// 如果配置中创建出多于一个 "chunk"(例如,使用多个入口起点或使用像 CommonsChunkPlugin 这样的插件),则应该使用 占位符(substitutions) 来确保每个文 唯一 的名称。
// filename: '[name].js',
},
};
加载器(loader)
loader 用于对模块的源代码进行转换。 webpack 只能理解 JavaScript 和 JSON 文件,loader 使 webpack 可以处理其他类型的文件,并将它们转换为有效模块,以供应用程序使用,以及被添加到依赖图中。 例如,你可以使用 loader 告诉 webpack 加载 CSS 文件,或者将 TypeScript 转为 JavaScript。为此,首先安装相对应的 loader:
npm install --save-dev css-loader ts-loader
然后指示 webpack 对每个 .css 使用 css-loader,以及对所有 .ts 文件使用 ts-loader。module.rules 允许你在 webpack 配置中指定多个 loader。 这种方式是展示 loader 的一种简明方式,并且有助于使代码变得简洁和易于维护。
loader 总是从右到左(或从下到上)地取值(evaluate)/执行(execute)。在实际(从右到左)执行 loader 之前,会先 从左到右 调用 loader 上的 pitch 方法。
module.exports = {
module: {
rules: ['a-loader', 'b-loader', 'c-loader'],
},
};
实际执行顺序
|- a-loader `pitch`
|- b-loader `pitch`
|- c-loader `pitch`
|- requested module is picked up as a dependency
|- c-loader normal execution
|- b-loader normal execution
|- a-loader normal execution
如果某个 loader 在 pitch 方法中给出一个结果,那么这个过程会回过身来,并跳过剩下的 loader。在我们上面的例子中,如果 b-loader 的 pitch 方法返回了一些东西:
module.exports = function (content) {
return someSyncOperation(content);
};
module.exports.pitch = function (remainingRequest, precedingRequest, data) {
if (someCondition()) {
return (
'module.exports = require(' +
JSON.stringify('-!' + remainingRequest) +
');'
);
}
};
上面的步骤将被缩短为:
|- a-loader `pitch`
|- b-loader `pitch` returns a module
|- a-loader normal execution
插件(plugin)
loader 用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量。webpack 插件是一个具有 apply 方法的 JavaScript 对象。apply 方法会被 webpack compiler 调用,并且在 整个 编译生命周期都可以访问 compiler 对象。
想要使用一个插件,你只需要 require() 它,然后把它添加到 plugins 数组中。多数插件可以通过选项(option)自定义。你也可以在一个配置文件中因为不同目的而多次使用同一个插件,这时需要通过使用 new 操作符来创建一个插件实例。
-
配置方式
webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack'); // 用于访问内置插件
module.exports = {
module: {
rules: [{ test: /\.txt$/, use: 'raw-loader' }],
},
plugins: [new HtmlWebpackPlugin({ template: './src/index.html' })],
};
-
NODE API方式
在使用 Node API 时,还可以通过配置中的
plugins属性传入插件。
const webpack = require('webpack'); // 访问 webpack 运行时(runtime)
const configuration = require('./webpack.config.js');
let compiler = webpack(configuration);
new webpack.ProgressPlugin().apply(compiler);
compiler.run(function (err, stats) {
// ...
});
模式(mode)
通过选择 development, production 或 none 其中的一个,来设置 mode 参数,使之可以在开发和生产环境采用不同的配置。你可以启用 webpack 内置在相应环境下的优化。其默认值为 production。
module.exports = {
mode: 'production',
};
生产环境下将会默认打开一些性能优化配置,如:代码压缩与tree shaking。
解析(Resolve)
配置模块如何解析。例如,当在 ES2015 中调用 import 'lodash',resolve 选项能够对 webpack 查找 'lodash' 的方式去做修改。
module.exports = {
//...
resolve: {
// configuration options
},
};
-
resolve.alias创建
import或require的别名,来确保模块引入变得更简单。例如,一些位于src/文件夹下的常用模块:
const path = require('path');
module.exports = {
//...
resolve: {
alias: {
Utilities: path.resolve(__dirname, 'src/utilities/'),
Templates: path.resolve(__dirname, 'src/templates/'),
},
},
};
现在,替换“在导入时使用相对路径”这种方式,就像这样:
import Utility from '../../utilities/utility';
//也可以这样使用别名:
import Utility from 'Utilities/utility';
-
resolve.fallback当正常解析失败时,重定向模块请求。
module.exports = { //... resolve: { fallback: { crypto: require.resolve('crypto-browserify'), stream: require.resolve('stream-browserify'), }, }, };
devServer
webpack-dev-server 够实现代码修改后自动打包,自动刷新浏览器,从而提高我们的开发效率。
devServer.allowedHosts: 该选项允许将允许访问开发服务器的服务列入白名单。module.exports = { //... devServer: { allowedHosts: [ 'host.com', 'subdomain.host.com', 'subdomain2.host.com', 'host2.com', ], }, };用
.作为子域通配符。.host.com 会与 host.com,www.host.com 以及 host.com 等其他任何其他子域匹配。当设置为 'all' 时会跳过 host 检查。并不推荐这样做,因为不检查 host 的应用程序容易受到 DNS 重绑定攻击。当设置为 'auto' 时,此配置项总是允许 localhost、 host 和 client.webSocketURL.hostnamedevServer.clientlogging允许在浏览器中设置日志级别,例如在重载之前,在一个错误之前或者 热模块替换 启用时。
module.exports = { //... devServer: { client: { // 'log' | 'info' | 'warn' | 'error' | 'none' | 'verbose' logging: 'info', }, }, };overlayboolean = true object: { errors boolean = true, warnings boolean = true }当出现编译错误或警告时,在浏览器中显示全屏覆盖。progressboolean在浏览器中以百分比显示编译进度。
devServer.compressboolean = true启用gzip压缩devServer.hot'only'boolean = true启用 webpack 的 热模块替换 特性。启用热模块替换功能,在构建失败时不刷新页面作为回退,使用hot: 'only'devServer.openbooleanstringobject[string, object]告诉 dev-server 在服务器已经启动后打开浏览器。设置其为 true 以打开你的默认浏览器。devServer.port'auto'stringnumber指定监听请求的端口号。devServer.proxyobject[object, function]如果你有单独的后端开发服务器 API,并且希望在同域名下发送 API 请求 ,那么代理某些 URL 会很有用。可以解决开发环境的跨域问题。现在,对module.exports = { //... devServer: { proxy: { '/api': 'http://localhost:3000', }, }, };/api/users的请求会将请求代理到http://localhost:3000/api/users。- 如果不希望传递
/api,则需要重写路径:module.exports = { //... devServer: { proxy: { '/api': { target: 'http://localhost:3000', pathRewrite: { '^/api': '' }, }, }, }, }; - 默认情况下,将不接受在 HTTPS 上运行且证书无效的后端服务器。 如果需要,可以这样修改配置
module.exports = { //... devServer: { proxy: { '/api': { target: 'https://other-server.example.com', secure: false, }, }, }, }; - 有时不想代理所有内容。 可以基于函数的返回值绕过代理。
在该功能中,可以访问请求,响应和代理选项。
- 返回
null或undefined以继续使用代理处理请求。 - 返回
false会为请求产生 404 错误。 - 返回提供服务的路径,而不是继续代理请求。 例如。 对于浏览器请求,想要提供 HTML 页面,但是对于 API 请求,想要代理它。 可以执行以下操作:
module.exports = { //... devServer: { proxy: { '/api': { target: 'http://localhost:3000', bypass: function (req, res, proxyOptions) { if (req.headers.accept.indexOf('html') !== -1) { console.log('Skipping proxy for browser request.'); return '/index.html'; } }, }, }, }, }; - 返回
- 如果想将多个特定路径代理到同一目标,则可以使用一个或多个带有
context属性的对象的数组:module.exports = { //... devServer: { proxy: [ { context: ['/auth', '/api'], target: 'http://localhost:3000', }, ], }, }; - 默认情况下,代理时会保留主机头的来源,可以将
changeOrigin设置为true以覆盖此行为。module.exports = { //... devServer: { proxy: { '/api': { target: 'http://localhost:3000', changeOrigin: true, }, }, }, };
- 如果不希望传递
cache
缓存生成的 webpack 模块和 chunk,来改善构建速度。cache 会在开发 模式被设置成 type: 'memory' 而且在 生产 模式 中被禁用。 cache: true 与 cache: { type: 'memory' } 配置作用一致。 传入 false 会禁用缓存:
module.exports = {
//...
cache: false,
};
当将 cache.type 设置为 'filesystem' 是会开放更多的可配置项。
-
cache.typestring: 'memory' | 'filesystem'将
cache类型设置为内存或者文件系统。memory选项很简单,它告诉 webpack 在内存中存储缓存,不允许额外的配置。 -
cache.cacheDirectory缓存文件存放的路径,默认为
node_modules/.cache/webpack。(当cache.type被设置成'filesystem'可用)。const path = require('path'); module.exports = { //... cache: { type: 'filesystem', cacheDirectory: path.resolve(__dirname, '.temp_cache'), }, }; -
cache.buildDependencies额外的依赖文件,当这些文件内容发生变化时,缓存会完全失效而执行完整的编译构建,通常可设置为项目配置文件。 默认是
webpack/lib来获取 webpack 的所有依赖项。module.exports = { cache: { buildDependencies: { // This makes all dependencies of this file - build dependencies config: [__filename], // 默认情况下 webpack 与 loader 是构建依赖。 }, }, }; -
cache.managedPaths受控目录,Webpack 构建时会跳过新旧代码哈希值与时间戳的对比,直接使用缓存副本,默认值为
['./node_modules'] -
cache.profile跟踪并记录各个
'filesystem'缓存项的详细时间信息。默认值为false -
cache.maxAge缓存失效时间(以毫秒为单位),默认值为
一个月(5184000000) -
cache.allowCollectingMemory收集在反序列化期间分配的未使用的内存,仅当
cache.type设置为'filesystem'时生效。这需要将数据复制到更小的缓冲区中,并有性能成本。module.exports = { cache: { type: 'filesystem', allowCollectingMemory: true, }, };
devtool
此选项控制是否生成,以及如何生成 source map。source map 是用于在浏览器的开发者工具中跳转到源代码的映射文件,可以方便地进行代码调试和性能分析。选择一种 source map 风格来增强调试过程。不同的值会明显影响到构建(build)和重新构建(rebuild)的速度。
你可以直接使用
SourceMapDevToolPlugin/EvalSourceMapDevToolPlugin来替代使用devtool选项,因为它有更多的选项。切勿同时使用devtool选项和SourceMapDevToolPlugin/EvalSourceMapDevToolPlugin插件。devtool选项在内部添加过这些插件,所以你最终将应用两次插件。
一些常见配置
| devtool | performance | production | quality |
|---|---|---|---|
| (none) | build: fastest rebuild: fastest | yes | bundle |
eval | build: fast rebuild: fastest | no | generated |
eval-cheap-source-map | build: ok rebuild: fast | no | transformed |
eval-source-map | build: slowest rebuild: ok | no | original |
eval-cheap-module-source-map | build: slow rebuild: fast | no | original lines |
source-map | build: slowest rebuild: slowest | yes | original |
hidden-source-map | build: slowest rebuild: slowest | yes | original |
nosources-source-map | build: slowest rebuild: slowest | yes | original |
验证 devtool 名称时, 我们期望使用某种模式, 注意不要混淆 devtool 字符串的顺序, 模式是:
[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
上述模式中有三类关键词:
inline、hidden、evalinline:Source Map内容通过base64放在js文件中引入。hidden:代码中没有sourceMappingURL,浏览器不自动引入Source Map。eval:生成代码和Source Map内容混淆在一起,通过eval输出。
nosources使用这个关键字的Source Map不包含sourcesContent,调试时只能看到文件信息和行信息,无法看到源码。
品质说明(quality)
quality 决定我们调试时能看到的源码内容。
bundled:将所有生成的代码视为一大块代码。你看不到相互分离的模块。generated:每个模块相互分离,并用模块名称进行注释。可以看到 webpack 生成的代码。示例:你会看到类似var module__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(42); module__WEBPACK_IMPORTED_MODULE_1__.a();,而不是import {test} from "module"; test();。transformed:每个模块相互分离,并用模块名称进行注释。可以看到 webpack 转换前、loader 转译后的代码。示例:你会看到类似import {test} from "module"; var A = function(_test) { ... }(test);,而不是import {test} from "module"; class A extends test {};。original:每个模块相互分离,并用模块名称进行注释。你会看到转译之前的代码,正如编写它时。这取决于 loader 支持。(lines only):source map 被简化为每行一个映射。这通常意味着每个语句只有一个映射(假设你使用这种方式)。这会妨碍你在语句级别上调试执行,也会妨碍你在每行的一些列上设置断点。与压缩后的代码组合后,映射关系是不可能实现的,因为压缩工具通常只会输出一行。
外部扩展(Externals)
externals 配置选项提供了「从输出的 bundle 中排除依赖」的方法。相反,所创建的 bundle 依赖于那些存在于用户环境(consumer's environment)中的依赖。此功能通常对 library 开发人员来说是最有用的,然而也会有各种各样的应用程序用到它。
externals 防止将某些 import 的包(package)打包到 bundle 中,而是在运行时(runtime)再去从外部获取这些扩展依赖(external dependencies)。
例如,从 CDN 引入 jQuery,而不是把它打包:
<!-- index.html -->
<script
src="https://code.jquery.com/jquery-3.1.0.js"
integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk="
crossorigin="anonymous"
></script>
// webpack.config.js
module.exports = {
//...
externals: {
jquery: 'jQuery',
},
};
这样就剥离了那些不需要改动的依赖模块,换句话,下面展示的代码还可以正常运行:
import $ from 'jquery';
$('.my-element').animate(/* ... */);
上面展示了一个使用外部全局变量的示例,但实际上可以以以下任何形式使用外部变量:全局变量、CommonJS、AMD、ES2015 模块。
- externals的几种写法
module.exports = {
// 1.字符串
externals: 'jquery',
// 2.[string]
externals: {
subtract: ['./math', 'subtract'],
},
// 3.对象
externals: {
react: 'react',
},
// 或者
externals: {
lodash: {
commonjs: 'lodash',
amd: 'lodash',
root: '_', // 指向全局变量
},
},
// 或者
externals: {
subtract: {
root: ['math', 'subtract'],
},
},
// 4.函数
/**
* 1) function ({ context, request, contextInfo, getResolve }, callback)
* 2) function ({ context, request, contextInfo, getResolve }) => promise
*
* 函数接收两个入参:
* - ctx (object):包含文件详情的对象。
ctx.context (string): 包含引用的文件目录。
ctc.request (string): 被请求引入的路径。
ctx.contextInfo (object): 包含 issuer 的信息(如,layer 和 compiler)
ctx.getResolve 5.15.0+: 获取当前解析器选项的解析函数。
- callback (function (err, result, type)): 用于指明模块如何被外部化的回调函数
回调函数接收三个入参:
- err (Error): 被用于表明在外部外引用的时候是否会产生错误。如果有错误,这将会是唯一被用到的参数。
- result (string [string] object): 描述外部化的模块。可以接受其它标准化外部化模块格式,(string, [string],或 object)。
- type (string): 可选的参数,用于指明模块的 external type(如果它没在 result 参数中被指明)。
**/
externals: [
function ({ context, request }, callback) {
if (/^yourregex$/.test(request)) {
// 使用 request 路径,将一个 commonjs 模块外部化
return callback(null, 'commonjs ' + request);
}
// 继续下一步且不外部化引用
callback();
},
],
// RegExp 匹配给定正则表达式的每个依赖,都将从输出 bundle 中排除。
externals: /^(jquery|\$)$/i,
// 5.混用语法
externals: [
{
// 字符串
react: 'react',
// 对象
lodash: {
commonjs: 'lodash',
amd: 'lodash',
root: '_', // indicates global variable
},
// 字符串数组
subtract: ['./math', 'subtract'],
},
// 函数
function ({ context, request }, callback) {
if (/^yourregex$/.test(request)) {
return callback(null, 'commonjs ' + request);
}
callback();
},
// 正则表达式
/^(jquery|\$)$/i,
],
};
补充
import()中的表达式
不能使用完全动态的 import 语句,例如 import(foo)。是因为 foo 可能是系统或项目中任何文件的任何路径。
import() 必须至少包含一些关于模块的路径信息。打包可以限定于一个特定的目录或文件集,以便于在使用动态表达式时 - 包括可能在 import() 调用中请求的每个模块。例如, import(./locale/${language}.json) 会把 .locale 目录中的每个 .json 文件打包到新的 chunk 中。在运行时,计算完变量 language 后,就可以使用像 english.json 或 german.json 的任何文件。
Magic Comments(魔法注释)
内联注释使 'import() 必须至少包含一些关于模块的路径信息' 这一特性得以实现。通过在 import 中添加注释,我们可以进行诸如给 chunk 命名或选择不同模式的操作。
// 单个目标
import(
/* webpackChunkName: "my-chunk-name" */
/* webpackMode: "lazy" */
/* webpackExports: ["default", "named"] */
'module'
);
// 多个可能的目标
import(
/* webpackInclude: /\.json$/ */
/* webpackExclude: /\.noimport\.json$/ */
/* webpackChunkName: "my-chunk-name" */
/* webpackMode: "lazy" */
/* webpackPrefetch: true */
/* webpackPreload: true */
`./locale/${language}`
);
// webpackIgnore:设置为 true 时,禁用动态导入解析。
// 将 webpackIgnore 设置为 true 则不进行代码分割。
import(/* webpackIgnore: true */ 'ignored-module.js');
webpackChunkName: 新 chunk 的名称。 从 webpack 2.6.0 开始,占位符[index]和[request]分别支持递增的数字或实际的解析文件名。 添加此注释后,将单独的给我们的 chunk 命名为 [my-chunk-name].js 而不是 [id].js。webpackMode:从 webpack 2.6.0 开始,可以指定以不同的模式解析动态导入。支持以下选项:'lazy'(默认值):为每个import()导入的模块生成一个可延迟加载(lazy-loadable)的 chunk。'lazy-once':生成一个可以满足所有import()调用的单个可延迟加载(lazy-loadable)的 chunk。此 chunk 将在第一次import()时调用时获取,随后的import()则使用相同的网络响应。注意,这种模式仅在部分动态语句中有意义,例如 import(./locales/${language}.json),其中可能含有多个被请求的模块路径。'eager':不会生成额外的 chunk。所有的模块都被当前的 chunk 引入,并且没有额外的网络请求。但是仍会返回一个 resolved 状态的Promise。与静态导入相比,在调用import()完成之前,该模块不会被执行。'weak':尝试加载模块,如果该模块函数已经以其他方式加载,(即另一个 chunk 导入过此模块,或包含模块的脚本被加载)。仍会返回Promise,但是只有在客户端上已经有该 chunk 时才会成功解析。如果该模块不可用,则返回 rejected 状态的Promise,且网络请求永远都不会执行。当需要的 chunks 始终在(嵌入在页面中的)初始请求中手动提供,而不是在应用程序导航在最初没有提供的模块导入的情况下触发,这对于通用渲染(SSR)是非常有用的。
webpackPrefetch:告诉浏览器将来可能需要该资源来进行某些导航跳转.webpackPreload:预加载,告诉浏览器在当前导航期间可能需要该资源。4.6+才支持,如果是老版本 webpack,可以使用preload-webpack-plugin这种插件来实现预加载。webpackInclude:在导入解析(import resolution)过程中,用于匹配的正则表达式。只有匹配到的模块才会被打包。webpackExclude:在导入解析(import resolution)过程中,用于匹配的正则表达式。所有匹配到的模块都不会被打包。
Vue-cli的构建优化
Vue-cli简介及使用方法
简介
Vue-cli(Vue Command Line Interface)是一个官方提供的基于命令行的工具,可以快速搭建 Vue.js 开发环境和进行 Vue.js 项目的构建、调试和部署。它集成了大量的常用工具和插件,包括 webpack、babel、eslint 等,使得 Vue.js 开发更加高效、便捷、规范
安装
可以使用下列任一命令安装这个新的包:
npm install -g @vue/cli
// OR
yarn global add @vue/cli
webpack相关的配置
-
简单的配置方式
调整 webpack 配置最简单的方式就是在 vue.config.js 中的 configureWebpack 选项提供一个对象:
// vue.config.js module.exports = { configureWebpack: { plugins: [ new MyAwesomeWebpackPlugin() ] } } -
链式操作(高级)
Vue CLI 内部的 webpack 配置是通过 webpack-chain 维护的。这个库提供了一个 webpack 原始配置的上层抽象,使其可以定义具名的 loader 规则和具名插件,并有机会在后期进入这些规则并对它们的选项进行修改。 它允许我们更细粒度的控制其内部配置。
// vue.config.js module.exports = { chainWebpack: config => { config.module .rule('vue') .use('vue-loader') .tap(options => { // 修改它的选项... return options }) } }
构建优化
进行 vue-cli 的构建优化有以下一些方法:
-
使用路由懒加载:如果你的项目使用了 Vue Router,可以使用路由懒加载来按需加载路由组件。这样可以减少初始加载量,并提升页面加载速度。
-
通过配置 webpack,可以启用 Tree Shaking 来删除未使用的代码。这可以显著减少打包后的文件大小,提高加载速度。
-
压缩 CSS 和 JavaScript 文件:通过使用类似于 MiniCssExtractPlugin、TerserWebpackPlugin 等插件,你可以将 CSS 和 JavaScript 文件进行压缩,减小文件大小,加快加载速度。
-
使用 CDN 加速资源加载:将常用的外部资源(例如 Vue.js、jQuery 等)从自己的服务器上移到 CDN 上,可以提高资源的访问速度。
-
使用缓存控制策略:通过在 HTTP 响应头中设置合适的缓存控制策略,可以利用客户端缓存来减少服务器的请求和加载时间。
-
优化图片加载:使用适当的图片压缩和格式选择来减小图片文件大小,例如使用 WebP 格式,使用图片压缩工具等。
-
代码分割与预加载:根据项目需求,可以将页面中的某些部分(例如按需加载的组件、路由等)进行合理的代码分割,并根据用户行为预加载相关资源,以提高用户体验和加载速度。
Vue-cli4升级到5
升级
如需升级全局的 Vue CLI 包,请运行:
npm update -g @vue/cli
// 或者
yarn global upgrade --latest @vue/cli
上面列出来的命令是用于升级全局的 Vue CLI。如需升级项目中的 Vue CLI 相关模块(以 @vue/cli-plugin- 或 vue-cli-plugin- 开头),请在项目目录下运行 vue upgrade
注意事项
vue-cli 采用的是 webpack4,而 vue-cli5 采用的是 webpack5,因此在升级cli时还需要了解 webpack4 升级到5的一些注意事项
vue-cli
-
css.requireModuleExtension 选项被删除。如果确实需要剥离CSS模块文件名中的
.module部分,可以采用如下配置// vue.config.js module.exports = { css: { loaderOptions: { css: { modules: { auto: () => true } } } } } -
不再支持使用 node-sass 生成项目。它已被弃用一段时间了。请改用
sass包。 -
从v5.0.0-beta.0 开始,默认打开现代模式,运行 vue-cli-service 构建将根据您的浏览器列表配置自动生成不同的捆绑包。
-
放弃了 TSLint 支持。由于 TSLint 已被弃用,已经删除了此版本中所有与 TSLint 相关的代码。请考虑切换到
ESLint。Ts-loader从v6升级到v9。它现在只支持 TypeScript >= 3.6。
webpack
-
Webpack 5 对 Node.js 的版本要求至少是 10.13.0 (LTS),因此,如果你还在使用旧版本的 Node.js,请升级它们。
-
当 webpack 配置中使用了 [hash] 占位符时,考虑将它改为 [contenthash]。效果一致,但事实证明会更为有效。 当使用 [contenthash] 时,Webpack5 将使用真正的文件内容哈希值。之前它 "只" 使用内部结构的哈希值。 当只有注释被修改或变量被重命名时,这对长期缓存会有积极影响。这些变化在压缩后是不可见的。
-
webpack5 不再为 Node.js 模块 自动引用
Polyfills,在前端代码中应避免使用。如果需要可以手动为 Node.js 核心模块添加polyfill。在升级时可能会遇到如下相关问题:
只需按照错误提示,安装相关依赖并添加配置即可
-
如果你定义了 rules,以使用
raw-loader,url-loader或file-loader来加载资源,请使用 资源模块 替代,因为它们可能在不久的将来被淘汰。在 webpack 5 之前,通常使用:
-
raw-loader 将文件导入为字符串
-
url-loader 将文件作为 data URI 内联到 bundle 中
-
file-loader 将文件发送到输出目录
资源模块类型(asset module type),通过添加 4 种新的模块类型,来替换所有这些 loader:
-
asset/resource发送一个单独的文件并导出 URL。之前通过使用 file-loader 实现。 -
asset/inline导出一个资源的 data URI。之前通过使用 url-loader 实现。 -
asset/source导出资源的源代码。之前通过使用 raw-loader 实现。 -
asset在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用 url-loader,并且配置资源体积限制实现。
当在 webpack 5 中使用旧的 assets loader(如 file-loader/url-loader/raw-loader 等)和 asset 模块时,你可能想停止当前 asset 模块的处理,并再次启动处理,这可能会导致 asset 重复,你可以通过将 asset 模块的类型设置为
'javascript/auto'来解决。 -
-
为 JSON 模块使用具名导出 webpack5新规范中将不再支持下面这种方式,如此做会发出警告:
import { version } from './package.json'; console.log(version);请使用如下方式代替:
import pkg from './package.json'; console.log(pkg.version); -
自动添加唯一命名 webpack5 会从 package.json name 中自动推断出一个唯一的构建名称,并将其作为 output.uniqueName 的默认值。由于 package.json 中有唯一的名称,可将 output.jsonpFunction 删除。
-
自动添加公共路径 Webpack5 会在可能的情况下自动确定 output.publicPath。
-
结构的变化
-
entry: {} 现在可以赋值一个空对象(允许使用插件来修改入口)。
-
target 支持数组,版本及 browserslist
-
移除了 cache: Object:不能再设置内存缓存对象
-
devtool 更加严格
- 格式化:false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
- disableHostCheck -> allowedHosts,该选项允许将允许访问开发服务器的服务列入白名单。
-
optimization.splitChunks test 不再匹配 chunk 名 迁移:使用 test 函数
(module, { chunkGraph }) => chunkGraph.getModuleChunks(module).some(chunk => chunk.name === "name")
-
...
-
-
默认值变化
-
当 browserslist 配置可用时,target 默认为 "browserslist"。
-
module.unsafeCache 现默认只对 node_modules 启用。
-
optimization.moduleIds 在生产环境下默认为 deterministic,而不再是 size
-
optimization.chunkIds 在生产环境下默认为 deterministic,而不再是 total-size
-
...
-