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",
},