webpack
默认配置文件(webpack.config.js)
const path = require('path')
module.exports = {
entry: './src/main.js', // 入口文件, 需要加上./
mode: 'development', // 三种取值,production、development 和 none
// production 生产模式下,Webpack 会自动优化打包结果;
// development Webpack 会自动优化打包速度,添加一些调试过程中的辅助
// none 就是运行最原始的打包,不做任何额外处理
output: {
filename: 'bundle.js', // 打包之后的文件名
path: path.join(__dirname, 'output') // 打包之后文件存放的绝对路径
},
module: {
rules: [ // 配置不同的loader, loader执行顺序,默认从下到上,从右到左
{
test: /.css$/,
use: [
'style-loader',
'css-loader'
]
},
{
test: /.png$/,
use: 'file-loader'
}
]
}
}
常用loader
-
css处理
import './main.css' // webpack.config.js { test: /.css$/, use: [ 'style-loader', //动态创建style标签,将css-loader处理返回的结果添加进去,并append到head中 'css-loader' // 处理css文件, 最终返回 module.exports = 'css code' ] }, -
图片处理
import icon from './icon.png' // webpack.config.js { test: /.png$/, use: 'file-loader' // 将图片拷贝纸output.path执行的目录中,并返回对应的路径 } { test: /.png$/, use: { loader: 'url-loader', // 依赖于file-loader,在file-loader的基础上进行了优化,如果图片体积小于指定的limit,则转换换成base64,减少http请求 options: { limit: 10 * 1024 // 10 KB } } } -
js处理
import createHeading from './heading.js' // webpack.config.js { test: /.js$/, use: { loader: 'babel-loader', // webpack只是一个模块打包的工具,默认只支持js的打包,并且不会做语法转换,如果js中使用了es6的语法,需要使用babel进行转换, babel的配置选项一般都是放在一个独立的.babelrc文件中 options: { presets: ['@babel/preset-env'] } } },
常用的plugin
- CleanWebpackPlugin
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
plugins: [
new CleanWebpackPlugin(), // 默认打包前清除dist目录
]
-
HtmlWebpackPlugin
const HtmlWebpackPlugin = require('html-webpack-plugin') new HtmlWebpackPlugin({ // 多页应用可以 new 多次,每次都会生成单独的文件 title: 'Webpack Plugin Sample', // index.html中可以使用ejs语法替换页面title meta: { viewport: 'width=device-width' // 动态创建meta标签 }, template: './src/index.html' // 基于这个模板文件生成html }), -
CopyWebpackPlugin
const CopyWebpackPlugin = require('copy-webpack-plugin') new CopyWebpackPlugin({ patterns: [ 'public' // 将制定目录下面的文件,copy到打包生成目录中 ] }) -
DefinePlugin, 定义变量
// webapck.config.js const webpack = require('webpack') ... plugins: [ new webpack.DefinePlugin({ // 值要求的是一个代码片段 API_BASE_URL: JSON.stringify('https://api.example.com') // API_BASE_URL: 'abc', 千万不能这么写,有坑 }) ] // main.js console.log(API_BASE_URL)
webpack-dev-server
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': ''
},
// 不能使用 localhost:8080 作为请求 GitHub 的主机名
changeOrigin: true
}
}
},
dev-tool
推荐生产使用
none,开发环境使用cheap-module-eval-source-map,关于不同模式的比较,可以参考github仓库看了仔细了解,传送门
devtool: 'xxx'
// webpack 提供很多很多种source-map,不用类型具有不同的特点,可以基于几种基本的类型进行组合
// 1. eval sourcemap跟打包后的代码被包裹在eval函数中
// 2. inline sourcemap被转换成base64包含在打包文件中, 体积会很大
// 3. cheap 错误只有行号,没有列
// 4. module 带缓存,不转换语法,最大程度还原原始文件
// 5. hidden 有声场source-map,但是没有引用
// 6. nosources 有具体的报错位置,源代码指向的是一个空白文件
Hot Module Replace
-
css的热替换
style-loader处理了,css文件内容一旦变化,会重新将变化后的内容去替换已经append到head中的style标签里的内容
-
js的热替换
因为js的场景很多,不同的模块使用的方法和功能都不一样,所以不好统一处理,想要js也具有HMR功能,需要我们自己单独实现。
import createEditor from './editor' import background from './better.png' import './global.css' const editor = createEditor() document.body.appendChild(editor) const img = new Image() img.src = background document.body.appendChild(img) // ============ 以下用于处理 HMR,与业务代码无关 ============ // console.log(createEditor) // 此处的代码打包之后,会变成 if(false) {} if (module.hot) { let lastEditor = editor module.hot.accept('./editor', () => { // console.log('editor 模块更新了,需要这里手动处理热替换逻辑') // console.log(createEditor) const value = lastEditor.innerHTML document.body.removeChild(lastEditor) const newEditor = createEditor() newEditor.innerHTML = value document.body.appendChild(newEditor) lastEditor = newEditor }) module.hot.accept('./better.png', () => { img.src = background console.log(background) }) }
tree-shaking(去掉 dead code)
tree-shaking是基于esmodule特性实现的,所以,要是tree-shaking能够很好的工作,尽量使用符合esmodule规范的代码,webpack只有在mode=production才开始treeshaking
// component.js
export const Button = () => {
return document.createElement('button')
console.log('dead-code')
}
export const Link = () => {
return document.createElement('a')
}
export const Heading = level => {
return document.createElement('h' + level)
}
// index.js
import { Button } from './components'
document.body.appendChild(Button())
// 打包时候的bundle.js是不会包含Link, Heading以及 console.log('dead-code')
splitChunks
optimization: {
splitChunks: {
// 自动提取所有公共模块到单独 bundle
chunks: 'all'
}
}