yarn add webpack webpack-cli --dev
yarn webpack
,默认将 src/index.js 作为入口文件,打包到 dist/main.js 中yarn webpack --mode development
开发模式,优化打包速度,不压缩,方便调试yarn webpack --mode production
生产模式,优化打包结果,压缩代码yarn webpack --mode none
原始模式,不对代码做任何处理/webpack.config.js,导出一个配置对象
const path = require('path')
module.exports = {
// 打包模式
mode: 'development',
// 入口文件,./ 不能省略
entry: './src/main.js',
// 输出目录
output: {
//输出文件的默认值是 ./dist/main.js
filename: 'bundle.js',
path: path.join(__dirname, 'dist'),
// bundle.js中的 __webpack_require__.p
// 使用html-webpack-plugin插件自动生成html的话无需指定
publicPath: 'dist/'
},
// 源代码地图
devtool: 'source-map',
// webpack-dev-server配置
devServer: {
// hot: true,
hotOnly: true
proxy: {}
},
module: {
//loader
rules: []
},
// 插件
plugins: []
}
bundle.js
(function(modules){
function require(moduleId){
return require(0)
}
})([
(function(module, exports, require){
}),
(function(module, exports, require){})
])
module.exports = {
module: {
rules: [
{
test: /.js$/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
},
{
test: /.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /.png$/,
use: {
loader: 'url-loader',
options: {
limit: 10*1024 //10KB
}
}
},
{
test: /.html$/,
use: {
loader: 'html-loader',
options: {
attrs: ['img:src', 'a:href']
}
}
}
]
}
}
建议项目中统一使用同一种标准加载模块。
将加载的资源文件转换成标准JavaScript代码
增强webpack自动化能力
yarn add xxx --dev
const {CleanWebpackPlugin} = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
module.exports = {
plugins: [
new CleanWebpackPlugin(),
// 可以传入一个配置对象,对生成的html进行配置
new HtmlWebpackPlugin({
title: 'Html Webpack Plugin',
meta: {
viewport: 'width=device-width'
},
// 指定一个模版文件,按照模版生成html
template: './src/index.html'
}),
// 同时输出多个页面文件,需要创建多个实例
new HtmlWebpackPlugin({
filename: 'about.html', //默认是index.html
}),
new CopyWebpackPlugin([
//需要复制的文件路径
'public'
])
]
}
通过在webpack生命周期的钩子(Compiler Hooks)中挂载任务函数来实现。
一个插件就是一个函数或者一个包含apply函数的对象,一般定义为一个类型,使用时在plugins中构建一个实例来使用。
Demo: 开发一个插件,用来删除bundle.js中的注释
class MyPlugin {
// webpack启动时会自动执行apply方法
apply(compiler){
console.log('MyPlugin 启动')
//emit钩子:webpack往输出目录输出文件之前执行
compiler.hooks.emit.tap('Myplugin', compilation => {
// compilation 可以理解为此次打包的上下文
for (const name in compilation.assets) {
//资源文件名称
console.log(name)
//获取资源文件内容
console.log(compilation.assets[name].source())
if(name.endsWith('.js')){
//获取带有注释的js文件
const content = compilation.assets[name].source()
//去掉文件中的注释
const withoutComments = content.replace('/\/\*\*+\*\//g', '')
//覆盖当前资源文件
compilation.assets[name] = {
source: () => withoutComments,
size: () => withoutComments.length
}
}
}
})
}
}
yarn add webpack-dev-server --dev
module.exports = {
devServer: {
//指定静态资源目录,值可以是一个字符串或一个数组
conentBase: './public',
//配置代理
proxy: {
'/api': {
//目标服务器
target: 'https://api.github.com',
//重写代理路径
pathRewrite: {
'^/api': ''
},
//不能使用localhost:8080 作为请求的主机名
changeOrigin: true
}
}
}
}
yarn webpack-dev-server
或将启动命令写入Npm Scripts中
解决源代码和打包后的运行代码不一致所带来的调试问题
//# sourceMappingURL = xxx.map
选择建议:
开发过程用 cheap-module-eval-source-map 模式;
生产环境用 none,或者用 nosources-source-map 模式。
页面不刷新的情况下,及时更新模块代码,解决浏览器自动刷新带来的页面状态和用户输入丢失的问题。
--hot
参数 或 配置文件 开启。const webpack = require('webpack')
module.exports = {
devServer: {
//hot: true
hotOnly: true
},
plugins: [
new webpack.HotModuleReplacementPlugin()
]
}
在入口函数(如main.js)中通过 HMR APIs 处理引入的模块的HMR
// 注册模块更新后的处理函数 两个参数:1.模块路径 2.处理函数
module.hot.accept('./editor', () => {
console.log('模块已更新')
...
})
if(module.hot){
module.hot.accept()
}
开发环境更注重开发效率;
生产环境更注重运行效率;
不同的工作环境需要创建不同的webpack配置。
在配置文件中添加判断条件,根据环境名参数(env)判断不同的工作环境,从而导出不同配置。适合中小型项目
const webpack = require('webpack')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
// 导出一个函数
module.exports = (env, argv) => {
//开发环境
const config = {
mode: 'development',
entry: './src/main.js',
output: {
filename: 'js/bundle.js'
},
devtool: 'cheap-eval-module-source-map',
devServer: {
hot: true,
contentBase: 'public'
},
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'
}),
new webpack.HotModuleReplacementPlugin()
]
}
//生产环境
if(env === 'production'){
//改变工作模式
config.mode = 'production'
//禁用source map
config.devtool = false
//增加生产模式需要使用的插件
config.plugins = [
...config.plugins,
new CleanWebpackPlugin(),
new CopyWebpackPlugin(['public'])
]
}
return config
}
yarn webpack
,以开发模式进行打包yarn webpack --env production
,以生产模式进行打包一个环境对应一个配置文件。适合大型项目
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 Test',
template: './src/index.html'
})
]
}
const webpack = require('webpack')
const merge = require('webpack-merge')
const common = require('./webpack.commom.js')
module.exports = merge(common, {
mode: 'development',
devtool: 'cheap-eval-module-source-map',
devServer: {
hot: true,
contentBase: 'public'
},
plugins: [
new webpack.hot.HotModuleReplacementPlugin()
]
})
const merge = require('webpack-merge')
const {CleanWebpackPlugin} = require('clean-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const common = require('./webpack.commom.js')
module.exports = merge(common, {
mode: 'production',
plugins: [
new CleanWebpackPlugin()
new CopyWebpackPlugin(['public'])
]
})
yarn add webpack-merge --dev
yarn webpack --config webpack.dev.js
yarn build
package.json
{
"scripts": {
"dev": "webpack --config webpack.dev.js",
"build": "webpack --config webpack.prod.js"
}
}
通过 process.env.NODE_ENV 为代码注入全局变量,
变量的值要求为JS代码片段
const webpack = require('webpack')
module.exports = {
entry: './src/main.js',
output: {
filename: 'bundle.js'
},
plugins: [
new webpack.DefinePlugin({
API_BASE_URL: JSON.stringfy('http://api.example.com')
})
]
}
在main.js中就使用变量API_BASE_URL
console.log(API_BASE_URL)
去掉代码中未引用的部分(dead-code)
module.exports = {
mode: 'none',
entry: './src/index.html',
output: {
filename: 'bundle.js'
},
optimization: {
//只导出被使用的成员
usedExports: true,
//压缩代码
minimize: true,
//合并模块
concatenateModules: true,
//开启副作用功能
sideEffects: true
}
}
用法:配置 optimization 的 concatenateModules 属性
作用:尽可能地将所有模块合并输出到一个函数中,提升运行效率,减小代码体积。
webpack4 新特性,一般用于npm包标记是否有副作用
副作用:模块执行时除了导出成员外所做的事情。
如果没有副作用,并且未被引用,打包时就可以被shaking掉。
{
"sideEffects": [
"*.css",
]
}
避免bundle.js体积过大,将打包结果按照一定的规则分离到多个bundle中,根据运行需要按需加载
适用于多页面应用,一个页面对应一个打包入口。
module.exports = {
//多入口打包时入口配置为一个对象
entry: {
index: '.src/index/html',
about: '.src/about.html'
},
//动态输出文件名
output: '[name].bundle.js',
optmization: {
splitChunks: {
//提取所有公共模块到单独的bundle
chunks: 'all'
}
},
module: {
},
plugins: [
//html-webpack-plugin默认使用所有打包结果
//使用chunks属性为每个Html指定所使用的打包结果
new HtmlWebpackPlugin({
title: 'Index',
template: './src/index.html',
filename: 'index.html',
chunks: ['index']
}),
new HtmlWebpackPlugin({
title: 'About',
template: './src/about.html',
filename: 'about.html',
chunks: ['about']
})
]
}
提取公共模块:配置优化属性 optimization 将页面公共的部分提取出来,如公共的样式和请求api的部分。
动态导入的模块会被自动分包,实现按需加载。
import(/* webpackChunkName: name */'模块路径').then(module => {
。。。
})
将css从打包结果中单独提取到css文件中,从而实现css的按需加载。
建议:超过150k的样式文件才需要单独提取
yarn mini-css-extract-plugin
webpack内部的压缩插件只针对js,其他资源文件需要额外的插件来压缩
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: './src/index.js',
output: {
filename: '[name].bundle.js'
},
optimization: {
minimizer: [
new TerserWebpackPlugin(),
new OptimizeCssAssetsWebpackPlugin()
]
},
module: {
rules: [
{
test: /\.css$/,
use: [
// 'style.loader',
MiniCssExtractPlugin.loader,
'css-loader'
]
}
]
},
plugins: [
new MiniCssExtractPlugin()
]
}
部署前端资源文件时一般会启用服务器的静态资源缓存,在设置filename时给文件名加上hash,就可以将缓存失效时间设置得很长
module.exports = {
entry: '.src/index.html',
output: {
filename: '[name]-[contenthash:8]'
}
}