webpack快速上手
针对仅有js文件设置打包入口和出口
const path = require('path')
module.exports = {
entry: './src/main.js',
output: {
filename: 'bundle.js',
path: path.join(__dirname, 'output')
}
}
package.json的scripts增加如下
"build": "webpack"
安装依赖webpack webpack-cli
将output文件夹下的bundle.js通过
mode
module.exports = {
// 这个属性有三种取值,分别是 production、development 和 none。
// 1. 生产模式下,Webpack 会自动优化打包结果;
// 2. 开发模式下,Webpack 会自动优化打包速度,添加一些调试过程中的辅助;
// 3. None 模式下,Webpack 就是运行最原始的打包,不做任何额外处理;
mode: 'development',
}
含有其他文件打包构建 在 Webpack 中,loader 的执行顺序是从右到左、从下到上
module.exports = {
module: {
rules: [
{
test: /.css$/,
use: [
'style-loader',
'css-loader'
]
},
{
test: /.png$/,
use: 'file-loader'
},
{
test: /.png$/,
use: {
loader: 'url-loader',
options: {
limit: 10 * 1024 // 10 KB
}
}
}
]
}
}
babel转译高阶语法
首先,安装必要的依赖:
npm install babel-loader @babel/preset-env @babel/core --save-dev
在 Webpack 配置中配置 babel-loader:
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
]
}
在项目根目录下创建 .babelrc 文件,配置 @babel/preset-env(可省略):
{
"presets": ["@babel/preset-env"]
}
html-loader用于解析html,markdown文件的解析获取
module: {
rules: [
{
test: /.md$/,
use: [
'html-loader',
'./markdown-loader'
]
}
]
}
创建js文件,通过marked包来解析成html交给下一个loader处理
const marked = require('marked')
module.exports = source => {
const html = marked(source)
// return html
// return `module.exports = "${html}"`
// return `export default ${JSON.stringify(html)}`
// 返回 html 字符串交给下一个 loader 处理
return html
}
插件
module.exports = {
plugins: [
new webpack.ProgressPlugin(),
new CleanWebpackPlugin(),
new HtmlWebpackPlugin()
]
}
webpack.ProgressPlugin() 是 Webpack 的内置插件,用于在控制台中显示构建进度信息。当构建项目时,它会输出构建进度的百分比、模块数量等信息
CleanWebpackPlugin() 是一个第三方插件,用于在每次构建之前清理输出目录。
CopyWebpackPlugin() 在构建过程中将指定的文件或目录复制到输出目录
HtmlWebpackPlugin() 是第三方插件,用于简化创建 HTML 文件的过程。它会自动在构建过程中生成一个 HTML 文件,并将打包后的资源(如 JavaScript 文件)自动注入到 HTML 中,同时还可以设置模板、标题、meta 标签等内容,可多次调用生成多个HTML文件,设置模板填充内容实例如下
// 用于生成 index.html
new HtmlWebpackPlugin({
title: 'Webpack Plugin Sample',
meta: {
viewport: 'width=device-width'
},
template: './src/index.html'
}),
// 用于生成 about.html
new HtmlWebpackPlugin({
filename: 'about.html'
})
template中的html文件含有如下内容可自动填充
<h1><%= htmlWebpackPlugin.options.title %></h1>
自己插件的开发 TO DO:
16-my-webpack-plugin
webpack配置实现项目启动和热更新
安装devServer,添加如下配置
module.exports = {
devServer: {
contentBase: './dist',
hot: true,
open:true,
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
],
};
webpack.HotModuleReplacementPlugin() 被添加到了插件列表中,以启用模块热替换功能。
在 devServer 配置中,设置了 contentBase,指定了服务器从哪里提供内容。通常这里设置为输出目录,以便 webpack-dev-server 可以从输出目录中提供生成的文件。
hot: true 选项告诉 webpack-dev-server 在发生代码更改时启用模块热替换,open:true浏览器自执行
确保在入口文件(例如 src/index.js)中添加了模块热替换的代码,以便接受更新的模块。
if (module.hot) {
module.hot.accept();
}
source map
Source map 是一种文件,它将编译后的代码映射回原始源代码,这样在调试时可以更容易地追踪到问题所在。
webpack配置如下
devtool: 'inline-source-map'| 'eval' | | 'cheap-source-map' | 'cheap-module-source-map'
webpack的配置可以是一个数组
生成多个输出文件
module.exports = [
{
entry: './src/main.js',
output: {
filename: 'a.js'
}
},
{
entry: './src/main.js',
output: {
filename: 'b.js'
}
}
]
不同sourceMap模式编译
const allModes = [
'eval',
'cheap-eval-source-map',
'cheap-module-eval-source-map',
'eval-source-map',
'cheap-source-map',
'cheap-module-source-map',
'inline-cheap-source-map',
'inline-cheap-module-source-map',
'source-map',
'inline-source-map',
'hidden-source-map',
'nosources-source-map'
]
module.exports = allModes.map(item => {
return {
devtool: item,
mode: 'none',
entry: './src/main.js',
output: {
filename: `js/${item}.js`
},
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
]
},
plugins: [
new HtmlWebpackPlugin({
filename: `${item}.html`
})
]
}
})
webpack-dev-server自动刷新来实现页面更新含有问题
若编辑器中已编辑数据,再写一些样式刷新后刚才输入文字没有了,这种情况需要手动刷新,入口文件中HMRAPI
// ================================================================
// 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 内部自动处理更新样式,所以不需要手动处理样式模块
HMRAPI中出现错误,则又会执行热更新自动刷新,错误不出现解决
devServer: {
hot: true
hotOnly: true // 只使用 HMR,不会 fallback 到 live reloading
},
生产环境webpack处理
module.exports = (env, argv) => {
const config = {
//开发环境下配置
}
if (env === 'production') {
config.mode = 'production'
config.devtool = false
config.plugins = [
...config.plugins,
new CleanWebpackPlugin(),
new CopyWebpackPlugin(['public'])
]
}
return config
}
合并webpack配置
将生产环境,开发环境配置分开,先写公共配置,将公共配置和生产或者开发环境配置合并在一起
const webpack = require('webpack')
const merge = require('webpack-merge')
const common = require('./webpack.common')
module.exports = merge(common, {
mode: 'development',
devtool: 'cheap-eval-module-source-map',
devServer: {
hot: true,
contentBase: 'public'
},
plugins: [
new webpack.HotModuleReplacementPlugin()
]
})
package.json启动命令
"build": "webpack --config webpack.prod.js"
DefinePlugin
用于在编译时创建全局常量,这些常量可以在项目的源代码中使用
module.exports = {
// other webpack configuration options
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
})
]
};
在项目的源代码中,你可以通过 process.env.NODE_ENV 来访问这个值
Tree shaking是删除应用程序中未使用的代码
webpack配置和babel注意事项
module.exports = {
mode: 'none',
entry: './src/index.js',
output: {
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
presets: [
// 如果 Babel 加载模块时已经转换了 ESM,则会导致 Tree Shaking 失效
// ['@babel/preset-env', { modules: 'commonjs' }]
// ['@babel/preset-env', { modules: false }]
// 也可以使用默认配置,也就是 auto,这样 babel-loader 会自动关闭 ESM 转换
['@babel/preset-env', { modules: 'auto' }]
]
}
}
}
]
},
optimization: {
// 模块只导出被使用的成员
usedExports: true,
// 尽可能合并每一个模块到一个函数中
// concatenateModules: true,
// 压缩输出结果
// minimize: true
}
}
多入口打包
module.exports = {
mode: 'none',
entry: {
index: './src/index.js',
album: './src/album.js'
},
output: {
filename: '[name].bundle.js'
},
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']
})
]
}
打包后的文件夹
optimization: {
splitChunks: {
// 自动提取所有公共模块到单独 bundle
chunks: 'all'
}
},
加上这个配置打包后的文件夹
webpack动态导入 动态导入模块被自动分包
在入口文件中
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())
})
}
/* webpackChunkName: 'components' */
用于给分包的bundle起名字。
MiniCssExtractPlugin用于提取单独css文件
style-loader不再适用//将样式通过 style 标签注入
module: {
rules: [
{
test: /\.css$/,
use: [
// 'style-loader', // 将样式通过 style 标签注入
MiniCssExtractPlugin.loader,
'css-loader'
]
}
]
},
plugins: [
new MiniCssExtractPlugin()
]
压缩js和css插件
optimization: {
minimizer: [
new TerserWebpackPlugin(), // 使用 TerserWebpackPlugin 压缩 JavaScript
new OptimizeCssAssetsWebpackPlugin() // 使用 OptimizeCssAssetsWebpackPlugin 压缩 CSS
]
}
生产环境文件名称加哈希值防止缓存策略文件不更新
对客户端而言,全新的文件名就是全新的请求,没有缓存问题
output: {
filename: '[name]-[contenthash:8].bundle.js'
},
plugins: [
new MiniCssExtractPlugin({
filename: '[name]-[contenthash:8].bundle.css'
})
]
在服务器端,设置 Cache-Control: max-age=31536000 表示静态资源可以在浏览器中缓存一年。