简介
webpack 可以做的事
代码转换
文件优化
代码分割
模块合并
自动刷新
代码校验
自动发布
面试常见考点
webpack 常见配置
webpack 高级配置
ast 抽象语法树
webpack 中的 Tapable
掌握 webpack 流程,手写 webpack
手写 webpack 中常见的 loader
手写 webpack 中常见的 plugin
webpack 基础配置
起步
创建 src—>index.js
npx webpack
基础配置
//webpack 是 node 写出来的,所以需要 node 的写法
let path = require(‘path’) // 核心模块
module.exports = {
mode: ‘development’, // 默认两种:production development
entry: ‘./src/index.js’, // 入口
output: {// 出口
filename: ‘bundle.js’, // 打包后的文件名
path: path.resolve(__dirname, ‘dist’), //resolve 把相对路径解析成绝对路径,__dirname 意思是在当前目录建立一个,路径必须是一个绝对路径
}
}
script 脚本
手动配置:npx webpack –config webpack.config.js
脚本配置:
“scripts”: {
“build”: “webpack”
}
//npm run build
传参 npx webpack — –config webpack.config.js
Html 插件
npx webpack-dev-server
开发服务,内部通过 express 实现这种服务
并不真实打包文件,只是在内存中生成
htmlWebpackPlugin
将打包后的 js 文件插入 html 文件,并放到 build 目录下
let htmlWebpackPlugin = require(‘html-webpack-plugin’)
module.exports = {
devServer: {// 开发服务器的配置
port: 3000, // 设置端口号
progress: true, // 进度条
contentBase: ‘/.build’, // 指定静态目录
compress: true
},
output: {
filename: ‘bundle.[hash:8].js’, // 文件名
path: path.resolve(__dirname, ‘dist’),
},
plugins: [// 数组 放着所有的 webpack 插件
new htmlWebpackPlugin({
template: ‘./src/index.html’, // 模板
filename: ‘index.html’, // 打包后的文件名
minify: {// 打包后的 html 也压缩
removeAttributeQuotes: true, // 删除属性的双引号
collapseWhitespace: true, // 折叠空行
},
hash: true //html 文件加上哈希戳
})
]
}
css 配置
loader:Webpack 本身只能处理 JavaScript 模块,如果要处理其他类型的文件,就需要使用 loader 进行转换。Loader 可以理解为是模块和资源的转换器,它本身是一个函数,接受源文件作为参数,返回转换的结果
css 配置
css-loader 解析 @import 这种语法
style-loader 把 css 插入到 head 的标签中
loader 的特点: 希望单一
loader 的用法: 字符串只用一个 loader,多个 loader 需要 []
loader 的顺序: 默认是从右向左执行 从下往上
优先级
{
loader: ‘style-loader’,
options: {
insertAt: ‘top’ // 确保优先级
}
}
处理 less、sass、stylus
yarn add less less-loader
yarn add node-sass sass-loader
yarn add stylus stylus-loader
module.exports = {
module: {// 模块
rules: [// 规则
//loader 的特点: 希望单一
//{test: /\.css$/, use: [‘style-loader’, ‘css-loader’] } // 第一种: 写法
//loader 还可以写成对象方式
{
// 处理 less 文件
test: /\.css$/,
use: [{
loader: ‘style-loader’,
options: {
insertAt: ‘top’ // 确保优先级
}
},
‘css-loader’, //@import 解析路径
‘less-loader’ // 把 less —->css
]
}
]
}
}
抽离 CSS 的插件
默认打包后只能插入 <style> 标签内,我们希望抽离成 <link> 形式
通过 mini-css-extract-plugin 这个插件
yarn add mini-css-extract-plugin -D
插件都是类,插件的使用顺序没有先后
let MiniCssExtractPlugin = require(‘mini-css-extract-plugin’)
// 配置 plugin
plugins: [
new MiniCssExtractPlugin({
filename: ‘main.css’,
})
],
// 配置 module
module: {
rules: [{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, ‘css-loader’]
},
{
test: /\.less$/,
use: [MiniCssExtractPlugin.loader, ‘css-loader’, ‘less-loader’]
}
]
}
自动添加前缀
autoprefixer
前提要用 postcss-loader
yarn add postcss-loader autoprefixer
// 配置 module
// 先处理 post-css 再处理 css
module: {
rules: [{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, ‘css-loader’,’postcss-loader’] // 加上 post-css
},
{
test: /\.less$/,
use: [MiniCssExtractPlugin.loader, ‘css-loader’, ‘postcss-loader’, ‘less-loader’]
}
]
}
// 创建 postcss.config.js 文件并配置
module.exports = {
plugins: [require(‘autoprefixer’)]
}
压缩 css(同时保证 js 的压缩)
通过 optimize-css-assets-webpack-plugin
yarn add optmize – css – assets – webpack – plugin – D
uglifyjs-webpack-plugin
let path = require(‘path’)
let htmlWebpackPlugin = require(‘html-webpack-plugin’)
let MiniCssExtractPlugin = require(‘mini-css-extract-plugin’)
let OptimizeCss = require(‘optimize-css-assets-webpack-plugin’)
let UglifyJsPlugin = require(‘uglifyjs-webpakc-plugin’)
module.exports = {
Optimization: {//*** 优化项,用了这个插件之后就必须用一下 Uglifyjs 压缩 js
minimizer: [
new UglifyJsPlugin({
cache: true, // 是否用缓存
parallel: true, // 是否并行打包
sourceMap: true
}),
new OptimizeCss()
]
},
devServer: {
port: 3000,
progress: true,
contentBase: ‘/.build’
},
mode: ‘development’,
entry: ‘./src/index.js’,
output: {
filename: ‘bundle.[hash:8].js’,
path: path.resolve(__dirname, ‘dist’),
},
plugins: [
new htmlWebpackPlugin({
template: ‘./src/index.html’,
filename: ‘index.html’,
minify: {
removeAttributeQuotes: true,
collapseWhitespace: true,
},
hash: true
}),
new MiniCssExtractPlugin({
filename: ‘main.css’
})
],
module: {
rules: [
//{test: /\.css$/, use: [‘style-loader’, ‘css-loader’] }
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader, //*** 创建 link 标签, 引用
‘css-loader’,
‘less-loader’,
‘postcss-loader’
]
}
]
}
}
JS 配置
转化 es6 语法
babel
yarn add babel-loader @babel/core @babel/preset-env -D
配置 module
module: {
rules: [{
test: /\.js$/,
use: {
loader: ‘babel-loader’,
options: {// 用 babel-loader es6—->es5
presets: [
‘@babel/preset-env’
]
}
}
}
]
}
配置提案里支持的语法
class A{
a = 1;
}
yarn add @babel/plugin-proposal-class-properties -D
{
test: /\.js$/,
use: {
loader: ‘babel-loader’,
options: {
presets: [
‘@babel/preset-env’
],
plugins: [
‘@babel/plugin-proposal-class-properties’
]
}
}
}
支持装饰器语法
function log(target) {
console.log(target, ’23’)
}
log(A)
yarn add @babel/plugin-proposal-decorators -D
配置 module
module: {
rules: [{
test: /\.js$/,
use: {
loader: ‘babel-loader’,
options: {// 用 babel-loader es6—->es5
presets: [
‘@babel/preset-env’
],
plugins: [
[‘@babel/plugin-proposal-class-properties’],
[‘@babel/plugin-proposal-decorators’,{“legacy”:true}],
[‘@babel/plugin-transform-runtime’]
]
}
}
}
]
}
处理 JS 语法及校验
function *gen(params){
yield 1;
}
console.log(gen().next()); // 内置 API
// Uncaught ReferenceError: regeneratorRuntime is not defined
babel-runtime
yarn add @babel/plugin-transform-runtime -D
yarn add @babel/runtime
module: {
rules: [{
test: /\.js$/,
use: {
loader: ‘babel-loader’,
options: {// 用 babel-loader es6—->es5
presets: [
‘@babel/preset-env’
],
plugins: [
[‘@babel/plugin-proposal-class-properties’], [‘@babel/plugin-proposal-decorators’,{“legacy”:true}],
[‘@babel/plugin-transform-runtime’]
]
}
},
include:path.resolve(__dirname,’src’), // 包括查找范围
exclude:/node_module/ // 排除查找范围
}
]
}
@babel/polyfill
‘aaa’.include(‘a’) //ES7 语法
// 实例上的方法默认都不会解析
yarn add @babel/polyfill
//a.js
require(‘@babel/polyfill’)
‘aaa’.include(‘a’)
eslint
yarn add eslint eslint-loader
module: {
rules: [//loader 默认从右向左执行 从下到上,写的太多容易乱,写到一起删除不方便
{
test:\.js$/,
use:{
loader:’eslint-loader’,
options:{
enforce:’pre’ //previous -> normal-> post 顺序执行
}
}
}
/*…*/
]
}
选择好配置, 下载.eslintrc.json 到 configuration
全局变量引入问题
第三方模块引用
yarn add jquery
import $ form ‘jqurey’
console.log($)
console.log(window.$) //undefined, 并不会挂载到 window 上
如何将变量暴露给 window?
expose-loader (内联 loader)
yarn add expose-loader
import $ from ‘expose-loader?$!jquery’ // 将 jquery 作为 $ 暴露给全局
也可以在 webpack.config.js 中配
module: {
rules: [{
test: require.resolve(‘jquery’),
use: ‘expose-loader?$!jquery’
}
]
}
在每个模块中注入 $ 对象
//webpack.config.js
let Webpack = require(‘webpack’)
new Webpack.ProvidePlugin({
// 再每个模块中都注入 $ 符
jqurey: ‘$’
})
//index.js
console.log($) // 只是在每个模块中都注入一个 `$`
// 此时 window.$ //undefined
cdn 外部路径引入
<script src=”https://cdn.bootcss.com/jquery/3.3.1/core.js”></script>
// 如果此时 js 也引入 jquery 会导致重复
import $ form jquery
externals: {
jquery: ‘jQuery’ // 告诉 webpack 从外部引入,并不需要打包,忽略
},
总结
expose-loader: 暴露到 window 上
providePlugin: 给每个提供一个
引入不打包
loader 类型
pre 前面执行的 loader
normal 普通的 loader
liader 内联 loader
post 后置 loader
图片处理
图片引入方式
在 js 中创建图片来引入
let image = new Image()
image.src = ‘./logo.png’
document.body.appendChild(image) // 打包完,其实就是一个普通的字符串,并没有真正的引入图片
file-loader
yarn add file-loader
默认在内部生成图片,到 build 目录下,并且把生成的路径的名字返回回来
//webpack.config.js
module: {
rules: [{
test: /\.(png|jpg|gif)$/,
use: ‘file-loader’
}
]
}
//index.js
import logo from ‘./logo.png’ // 把图片引入, 生成一个哈希戳的 logo,返回的结果是一个新的图片
console.log(logo)
let image = new Image();
image.src = logo;
document.body.appendChild(image)
在 css 中引入 background(‘url’)
<img src=”” alt=””>
打包文件分类
打包多页应用
配置 source-map