传统开发问题 ? 「模块化」就是把单独的一个功能封装到一个模块(文件中),模块之间相互隔离,但是可以通过特定的接口公开内部成员,也可以依赖别的模块 ? 模块化开发的好处: ? 每个模块中,只允许使用唯一的一次 export default,否则会报错,如果文件没有导出任何变量,默认引入的是个空对象 {} ? ? 每个模块中可以多次按需导出 ? 为什么需要构建工具 核心 webpack 默认只能打包处理 js 文件,其他非 js 模块,需要「loader 加载器」才能正常打包,否则报错 常用的 less-loader sass-loader url-loader(打包css中与url路径相关的文件)babel-loader 解决版本和缓存 1个模块可能有多个方法,只要其中的某个方法使用到了,则整个文件都会被打到 bundle 里面去,tree shaking 就是只把用到的方法打入 bundle,没用到的方法会在 uglify 阶段被擦除掉 将所有模块的代码按照引用顺序放在一个函数作用域里,然后适当的重命名一些变量以防止变量名冲突 通过 scope hoisting 可以减少函数声明代码和内存开销 将代码库分割成 chunks (语块),当代码运行到需要它们的时候再进行加载 脚本懒加载 js 方式模块化
概述
模块化规范
ES6 模块化的基本语法
// m1.js
const a = 1
export default { a }
// index.js
import m1 from './m1.js'
console.log(m1) // { a: 1 }
// m1.js
export const b = 2
// index.js
import m1, { b } from './m1.js'
console.log(m1) // { a: 1 }
console.log(b) // 2
// m2.js
console.log('直接执行')
// index.js
import './m2.js'
webpack 基本使用
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({
tempalate: './src/index.html' // 指定要用的模板文件
filename: 'index.html' // 指定生成的文件名称
})
]
}
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
}
}
]
}
]
}
}
npm 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'
]
}
]
}
}
npm 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转换器
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 文件处理
}
]
}
}
npm 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()
]
}
文件指纹设置
// 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 [email protected] -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 (摇树优化)
// .babelrc
{
modules: false // 开启
}
// production mode 环境默认开启
scope hoisting
代码分割
npm i @babel/plugin-syntax-dynamic-import -S
// .babelrc
{
"plugins": ["@babel/plugin-syntax-dynamic-import"]
}
// index.js
import('./test.js').then(Test => {
// Test.default 组件
})