Webpack Dev Server
集成了 主动编译 和 主动刷新浏览器 等性能。webpack-dev-server 在编译之后不会写入到任何输入文件,而是将 bundle 文件保留在内存中,而后将它们 serve 到 server 中。
yarn add webpack-dev-server --dev
const path = require("path");const { CleanWebpackPlugin } = require("clean-webpack-plugin");const HtmlWebpackPlugin = require("html-webpack-plugin");const CopyWebpackPlugin = require("copy-webpack-plugin");module.exports = { mode: "development", entry: "./src/main.js", output: { filename: "bundle.js", path: path.join(__dirname, "dist"), }, module: { rules: [ { test: /.css$/, use: ["style-loader", "css-loader"], }, { test: /.png$/, use: { loader: "url-loader", options: { limit: 10 * 1024, }, }, }, ], }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ title: "Ideal Webpack Develop Env", meta: { viewport: "width=device-width", }, template: "./src/index.html", }), // 开发阶段最好不要应用这个插件,频繁复制动态资源文件 // new CopyWebpackPlugin({ // patterns: ["public"], // }), ], devServer: { // 指定额定动态资源文件门路 contentBase: ["./public"], proxy: { "/api": { // http://localhost:8080/api/users -> https://api.github.com/api/users target: "https://api.github.com", // http://localhost:8080/api/users -> https://api.github.com/users pathRewrite: { "^/api": "", }, }, }, },};
yarn webpack serve --open
Source Map
Source map 就是一个信息文件,外面贮存着地位信息。也就是说,转换后的代码的每一个地位,所对应的转换前的地位。
有了它,出错的时候,除错工具将间接显示原始代码,而不是转换后的代码。这无疑给开发者带来了很大不便。
启用 Source map 只须要在转换后的代码尾部加上 //# sourceMappingURL=/path/to/file.js.map
Webpack 中的 source map
const path = require("path");module.exports = { mode: "development", entry: "./src/main.js", output: { filename: "bundle.js", path: path.join(__dirname, "dist"), }, devtool: "source-map",};
罕用模式:
eval
:只能定位文件,没有生成 source map 文件,构建速度快,不能晓得行列信息eval-source-map
:能够定位文件和行列信息eval-cheap-source-map
:只能定位到行信息,代码通过 loader 转化eval-cheap-module-source-map
:开发环境的源代码
HMR(Hot Module Replacement)
HMR 容许在运行时更新所有类型的模块,而无需齐全刷新
const path = require("path");const { CleanWebpackPlugin } = require("clean-webpack-plugin");const HtmlWebpackPlugin = require("html-webpack-plugin");const CopyWebpackPlugin = require("copy-webpack-plugin");module.exports = { mode: "development", entry: "./src/main.js", output: { filename: "bundle.js", path: path.join(__dirname, "dist"), }, module: { rules: [ { test: /.css$/, use: ["style-loader", "css-loader"], }, { test: /.png$/, use: { loader: "url-loader", options: { limit: 10 * 1024, }, }, }, ], }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ title: "Ideal Webpack Develop Env", meta: { viewport: "width=device-width", }, template: "./src/index.html", }), ], devServer: { hot: true, // hotOnly: true // 只应用 HMR,不会 fallback 到 live reloading }, devtool: "source-map",};
因为 css 具备对立的规定,只须要笼罩替换款式即可,而且 style-loader 曾经实现过 HMR 的热替换。而 js 文件是没有简单多样的,没有对立的规定,所以须要咱们本人依据理论状况实现
import "./main.css";import createEditor from "./editor";const editor = createEditor();document.body.appendChild(editor);// ================================================================// HMR 手动解决模块热更新// 不必放心这些代码在生产环境冗余的问题,因为通过 webpack 打包后,// 这些代码全副会被移除,这些只是开发阶段用到if (module.hot) { let hotEditor = editor; module.hot.accept("./editor.js", () => { // 当 editor.js 更新,主动执行此函数 // 长期记录编辑器内容 const value = hotEditor.innerHTML; // 移除更新前的元素 document.body.removeChild(hotEditor); // 创立新的编辑器 // 此时 createEditor 曾经是更新过后的函数了 hotEditor = createEditor(); // 还原编辑器内容 hotEditor.innerHTML = value; // 追加到页面 document.body.appendChild(hotEditor); }); module.hot.accept("./better.png", () => { // 当 better.png 更新后执行 // 重写设置 src 会触发图片元素从新加载,从而部分更新图片 img.src = background; }); // style-loader 外部主动解决更新款式,所以不须要手动解决款式模块}
不同环境下的配置
为不同环境创立不同的配置,应用webpack-merge
合并雷同的配置
webpack.common.js
const HtmlWebpackPlugin = require("html-webpack-plugin");module.exports = { entry: "./src/main.js", output: { filename: "js/bundle.js", }, module: { rules: [ { test: /\.css$/, use: ["style-loader", "css-loader"], }, { test: /\.(png|jpe?g|gif)$/, use: { loader: "file-loader", options: { outputPath: "img", name: "[name].[ext]", }, }, }, ], }, plugins: [ new HtmlWebpackPlugin({ title: "Webpack Tutorial", template: "./src/index.html", }), ],};
webpack.dev.js
const webpack = require("webpack");const { merge } = require("webpack-merge");const common = require("./webpack.common");module.exports = merge(common, { mode: "development", devtool: "source-map", devServer: { hot: true, contentBase: "public", }, plugins: [new webpack.HotModuleReplacementPlugin()],});
webpack.prod.js
const { merge } = require("webpack-merge");const { CleanWebpackPlugin } = require("clean-webpack-plugin");const CopyWebpackPlugin = require("copy-webpack-plugin");const common = require("./webpack.common");module.exports = merge(common, { mode: "production", plugins: [new CleanWebpackPlugin(), new CopyWebpackPlugin(["public"])],});
执行打包命令
yarn webpack --config webpack.dev.js
DefinePlugin
DefinePlugin
容许在 编译时 将你代码中的变量替换为其余值或表达式。这在须要依据开发模式与生产模式进行不同的操作时,十分有用。例如替换域名
new webpack.DefinePlugin({ // 值要求的是一个代码片段 API_BASE_URL: JSON.stringify("https://api.example.com"), }),
留神,因为本插件会间接替换文本,因而提供的值必须在字符串自身中再蕴含一个理论的引号。通常,能够应用相似 '"production"'
这样的替换引号,或者间接用 JSON.stringify('production')
。
Tree Shaking
用于形容移除 JavaScript 上下文中的未援用代码。production
模式下曾经默认启用。
const path = require("path");const webpack = require("webpack");const { CleanWebpackPlugin } = require("clean-webpack-plugin");const HtmlWebpackPlugin = require("html-webpack-plugin");module.exports = { mode: "none", entry: "./src/main.js", output: { filename: "bundle.js", path: path.join(__dirname, "dist"), }, optimization: { // 模块只导出被应用的成员 usedExports: true, // 压缩输入后果 minimize: true, },};
sideEffects
通过 package.json
的 "sideEffects"
属性作为标记,向 compiler 提供提醒,表明我的项目中的哪些文件是 "pure(纯正 ES2015 模块)",由此能够平安地删除文件中未应用的局部。
{ "sideEffects": false}
如果你的代码的确有一些副作用,能够改为提供一个数组:
{ "sideEffects": ["./src/some-side-effectful-file.js"]}
"side effect(副作用)" 的定义是,在导入时会执行非凡行为的代码,而不是仅仅裸露一个 export 或多个 export。举例说明,例如 polyfill,它影响全局作用域,并且通常不提供 export。
代码分包
多入口打包
实用于多页面打包,一个页面对应一个打包入口,提取公共局部
const { CleanWebpackPlugin } = require("clean-webpack-plugin");const HtmlWebpackPlugin = require("html-webpack-plugin");module.exports = { mode: "none", entry: { index: "./src/index.js", album: "./src/album.js", }, output: { filename: "[name].bundle.js", }, optimization: { splitChunks: { // 主动提取所有公共模块到独自 bundle chunks: "all", }, }, module: { rules: [ { test: /\.css$/, use: ["style-loader", "css-loader"], }, ], }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ title: "Multi Entry", template: "./src/index.html", filename: "index.html", chunks: ["index"], }), new HtmlWebpackPlugin({ title: "Multi Entry", template: "./src/album.html", filename: "album.html", chunks: ["album"], }), ],};
动静导入
动静导入的模块会被主动分包,实现按需加载
魔法正文:给分包的 bundle 定义名称
// import posts from './posts/posts'// import album from './album/album'const render = () => { const hash = window.location.hash || "#posts"; const mainElement = document.querySelector(".main"); mainElement.innerHTML = ""; if (hash === "#posts") { // mainElement.appendChild(posts()) import(/* webpackChunkName: 'components' */ "./posts/posts").then( ({ default: posts }) => { mainElement.appendChild(posts()); } ); } else if (hash === "#album") { // mainElement.appendChild(album()) import(/* webpackChunkName: 'components' */ "./album/album").then( ({ default: album }) => { mainElement.appendChild(album()); } ); }};render();window.addEventListener("hashchange", render);
CSS 文件压缩分包
yarn add terser-webpack-plugin optimize-css-assets-webpack-plugin mini-css-extract-plugin --dev
terser-webpack-plugin
: 该插件应用 terser 来压缩 JavaScriptoptimize-css-assets-webpack-plugin
: 应用 cssnano 优化和压缩 CSSmini-css-extract-plugin
: 本插件会将 CSS 提取到独自的文件中,为每个蕴含 CSS 的 JS 文件创建一个 CSS 文件,并且反对 CSS 和 SourceMaps 的按需加载
const { CleanWebpackPlugin } = require("clean-webpack-plugin");const HtmlWebpackPlugin = require("html-webpack-plugin");const MiniCssExtractPlugin = require("mini-css-extract-plugin");const OptimizeCssAssetsWebpackPlugin = require("optimize-css-assets-webpack-plugin");const TerserWebpackPlugin = require("terser-webpack-plugin");module.exports = { mode: "none", entry: { main: "./src/index.js", }, output: { filename: "[name].bundle.js", }, optimization: { minimizer: [ // 申明数组会让webpack认为咱们须要自定义压缩,所以须要本人申明js文件的压缩 new TerserWebpackPlugin(), new OptimizeCssAssetsWebpackPlugin(), ], }, module: { rules: [ { test: /\.css$/, use: [ // 'style-loader', // 将款式通过 style 标签注入 MiniCssExtractPlugin.loader, "css-loader", ], }, ], }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ title: "Dynamic import", template: "./src/index.html", filename: "index.html", }), new MiniCssExtractPlugin(), ],};
Hash 文件名
依赖文件名 hash 缓存。
我的项目级,我的项目任何改变都会变
output: { filename: "[name]-[hash].js", },
chunk 级,同一 chunk 改变会援用同 chunk 的变动
output: { filename: "[name]-[chunkhash].js", },
文件级,不同的文件不同的 hash 值,且 hash 长度为 8 位
output: { filename: "[name]-[contenthash:8].js", },