webpack基础
模块化
概述
传统开发问题
- 命名冲突
- 文件依赖
模块化就是把单独的一个功能封装到一个模块(文件中),模块之间相互隔离,但是可以通过特定的接口公开内部成员,也可以依赖别的模块
模块化开发的好处:
- 方便代码的重用
- 提升开发效率
- 方便后期维护
模块化规范
AMD(过时)
CMD(过时)
CommonJS(node端)
- 模块分为单文件模块与包
- 导出:
module.exports
和exports
- 导入:
require('模块')
ES6模块化
- 每个
js
文件都是一个独立的模块 - 导入:
import
- 导出:
export
- 每个
ES6 模块化的基本语法
默认导出 与 默认导入
js// m1.js const a = 1 export default { a } // index.js import m1 from './m1.js' console.log(m1) // { a: 1 }
每个模块中,只允许使用唯一的一次
export default
,否则会报错,如果文件没有导出任何变量,默认引入的是个空对象 {}按需导出 与 按需导入
js// m1.js export const b = 2 // index.js import m1, { b } from './m1.js' console.log(m1) // { a: 1 } console.log(b) // 2
每个模块中可以多次按需导出
直接导入并执行模块代码
js// m2.js console.log('直接执行') // index.js import './m2.js'
webpack 基本使用
为什么需要构建工具
- ES6语法
- 转换 JSX
- CSS 前缀补全/预处理器
- 压缩混淆
- 图片压缩
核心
mode 指定构建环境
development
,production
,none
entry 单入口 或 多入口
output 单出口 或 多出口
loaders 加载器
plugins 插件
npm i webpack webpack-cli -D
// webpack.config.js
const path = require('path')
module.exports = {
// 编译模式
mode: 'development', // production
// 打包入口 str/object { app: './src/index.js', main: './src/main.js' }
entry: path.join(__dirname, './src/index.js'),
// 打包出口 str/object
output: {
path: path.join(__dirname, './dist') // 打包文件的存放路径
filename: '[name].js' // 打包文件的名称 [name] 对应多入口占位符
}
}
// 执行打包命令webpack
webpack
热更新
npm i webpack-dev-server -D
// webpack.config.js
module.exports = {
plugins: [
new webpack.HotModuleReplacementPlugin()
],
devServer: {
contentBase: './dist',
hot: true
}
}
html-webpack-plugin 生成预览页面
npm i html-webpack-plugin -D
// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
...
// plugins 数组是 webpack 打包期间会用到的一些插件列表
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html' // 指定要用的模板文件
filename: 'index.html' // 指定生成的文件名称
})
]
}
loader 加载器
webpack
默认只能打包处理 js
文件,其他非 js
模块,需要loader 加载器才能正常打包,否则报错
常用的 less-loader
sass-loader
url-loader
(打包css中与url路径相关的文件)babel-loader
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.css$/, // 表示匹配的文件类型
use: [
'style-loader',
'css-loader'
] // 表示要调用的loader,loader顺序是固定的,从后往前
},
{
test: /\.jpg|png|gif|ttf|eot|svg|woff$/,
use: 'url-loader?limit=16940' // 可以数组和字符串 ? 接参数
},
// 或者
{
test: /\.jpg|png|gif|ttf|eot|svg|woff$/,
use: [
{
loader: 'url-loader',
options: {
limit: 10240
}
}
]
}
]
}
}
postcss-loader 自动添加 css 的兼容前缀
jsnpm i postcss-loader autoprefixer -D // postcss.config.js module.exports = { plugins: [ require('autoprefixer') ] } // webpack.config.js module.exports = { ... module: { rules: [ { test: /\.css$/, use: [ 'style-loader', 'css-loader', 'postcss-loader' ] } ] } }
css 单位 px 自动转换 rem
- 使用 px2rem-loader
- 使用手淘
lib-flexible
库
jsnpm i px2rem-loader -D npm i lib-flexible -S // webpack.config.js module.exports = { module: { rules: [ ... { test: /\.less$/, use: [ 'style-loader', 'css-loader', 'less-loader', { loader: 'px2rem-loader', options: { remUnit: 75, // 相对单位 1rem = 75px remPrecision: 8 // 转换后小数点位数 } } ] } ] } }
babel 处理 js 高级语法
js// babel转换器 npm i babel-loader @babel/core @babel/runtime -D // babel语法插件 npm i @babel/preset-env @babel/plugin-transform-runtime @babel/plugin-proposal-class-properties -D // babel.config.js module.exports = { presets: ['@babel/preset-env'], plugins: [ '@babel/plugin-transform-runtime', '@babel/plugin-proposal-class-properties' ] } // webpack.config.js module.exports = { module: { rules: [ { test: /\.js$/, use: 'babel-loader', exclude: /node_modules/ // 排除 node_modules 中的 js 文件处理 } ] } }
vue 组件加载器
jsnpm i vue-loader vue-template-compiler -D // webpack.config.js const VueLoaderPlugin = require('vue-loader/lib/plugin') module.exports = { module: { rules: [ ... { test: /\.vue$/, use: 'vue-loader' } ] }, plugins: [ new VueLoaderPlugin() ] }
文件指纹设置
解决版本和缓存
- hash
- chunkhash
- contenthash
// webpack.config.js
const MinCssExtractPlugin = require('min-css-extract-plugin')
module.exports = {
...
output: {
...
filename: '[name]_[chunkhash:8].js'
},
module: {
rules: [
{
test: /\.jpg|png|gif|ttf|eot|svg|woff$/,
use: [
{
loader: 'url-loader',
options: {
name: '[name]_[hash:8][ext]'
limit: 10240
}
}
]
},
{
test: /\.css$/,
use: [
MinCssExtractPlugin.loader, // 提取css
'css-loader',
'postcss-loader'
]
}
]
},
plugins: [
new MinCssExtractPlugin({
filename: '[name]_[contenthash:8].css'
})
]
}
clean-webpack-plugin 删除目录
npm i clean-webpack-plugin -D
// webpack.config.js
const CleanWebpackPlugin = require('clean-webpack-plugin')
module.exports = {
...
plugins: [
new CleanWebpackPlugin() // 默认删除ouput指定的构建输出目录
]
}
raw-loader 静态文件内联
npm i raw-loader@0.5.1 -D
// 内联 html
${require('raw-loader!babel-loader!./meta.html')}
// 内联 js
<script>${require('raw-loader!babel-loader!../test.js')}</script>
html-webpack-externals--plugin 基础库分离
const HtmlWebpackExternalsPlugin = require('html-webpack-externals-plugin')
module.exports = {
plugins: [
...
new HtmlWebpackExternalsPlugin({
externals: [
{
module: 'react',
entry: 'https://11.url.cn/now/lib/16.2.0/react.min.js', // 能是本地文件
global: 'React',
},
{
module: 'react-dom',
entry: 'https://11.url.cn/now/lib/16.2.0/react-dom.min.js',
global: 'ReactDOM',
},
]
})
]
}
image-webpack-loader 图片压缩
npm i image-webpack-loader -D
// webpack.config.js
module.exports = {
modules: {
rules: [
...
{
test: /.(png|jpg|gif|jpeg)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name]_[hash:8].[ext]'
}
},
{
loader: 'image-webpack-loader',
options: {
mozjpeg: {
progressive: true,
quality: 65
},
// optipng.enabled: false will disable optipng
optipng: {
enabled: false,
},
pngquant: {
quality: '65-90',
speed: 4
},
gifsicle: {
interlaced: false,
},
// the webp option will enable WEBP
webp: {
quality: 75
}
}
}
]
}
]
}
}
purgecss-webpack-plugin 剔除无用 css
const PurgecssPlugin = require('purgecss-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const PATHS = {
src: path.join(__dirname, 'src')
};
module.exports = {
plugins: [
...
new MiniCssExtractPlugin({
filename: '[name]_[contenthash:8].css'
}),
new PurgecssPlugin({
paths: glob.sync(`${PATHS.src}/**/*`, { nodir: true })
})
]
}
一些概念
tree shaking (摇树优化)
1个模块可能有多个方法,只要其中的某个方法使用到了,则整个文件都会被打到 bundle
里面去,tree shaking
就是只把用到的方法打入 bundle
,没用到的方法会在 uglify
阶段被擦除掉
// .babelrc
{
modules: false // 开启
}
// production mode 环境默认开启
scope hoisting
将所有模块的代码按照引用顺序放在一个函数作用域里,然后适当的重命名一些变量以防止变量名冲突
通过 scope hoisting
可以减少函数声明代码和内存开销
代码分割
将代码库分割成 chunks
(语块),当代码运行到需要它们的时候再进行加载
抽离相同代码到一个共享块
脚本懒加载,使得初始下载的代码更小
脚本懒加载 js 方式
- CommonJS:require.ensure
- ES6:动态 import (目前还没有原生支持,需要 babel 转换)
jsnpm i @babel/plugin-syntax-dynamic-import -S // .babelrc { "plugins": ["@babel/plugin-syntax-dynamic-import"] } // index.js import('./test.js').then(Test => { // Test.default 组件 })