webpck是什么?
1.模块打包工具bundler 模块是什么? // ES Module // common.js // CMD // AMD
ES Module
1.import Header from './header.js';
2.export default Header
common.js
1.var Header = require(./header);
2.module.exports = Header;
2.js 模块打包工具 -> css\html\jpg 等 3.底层基于node
常用指令
webpack --config [入口文件]
核心概念
loader
定义:打包方案,对于特定文件怎么去打包
module: {
rules: [{
test: /\.(jpg|png|gif)$/,
use: {
loader: 'url-loader',
options: {
name: '[name]_[hash].[ext]', // name、hash 为placeholder占位符
outputPath: 'images/',
limit: 10240
}
}
}]
},
常用loader
- file-loader
- url-loader(默认把文件转化成base64, 直接加载到js里。使用于小文件,limit)
- vue-loader
- css-loader 分析css文件依赖关系,合并css文件
- style-loader 把css内容挂载到head
- sass-loader loader顺序:从下到上,从右到左
module: {
rules: [{
test: /\.(jpg|png|gif)$/,
use: {
loader: 'url-loader',
options: {
name: '[name]_[hash].[ext]',
outputPath: 'images/',
limit: 10240
}
}
}, {
test: /\.scss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2, // 重新执行前N个loader
modules: true // css-module 模块化
}
},
'sass-loader',
'postcss-loader'
]
}]
},
plugin
plugin 可以在打包的节点上让webpack做一些事情 插件是什么? 例子,HtmlWebpackPlugin:会在打包结束后自动生成html文件,并且把打包生成的文件自动引入到html文件中
plugins: [new HtmlWebpackPlugin({
template: 'src/index.html'
}), new CleanWebpackPlugin(['dist'])],
常用插件
- HtmlWebpackPlugin
- CleanWebpackPlugin 清除插件
多个文件打包
entry: {
main: './src/index.js',
sub: './src/index.js'
},
output: {
publicPath: 'http://cdn.com.cn', // public Path ,最终在引入时会自动添加到路径前
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
} // 多个文件时HtmlWebpackPlugin会自动同时引入
详细参数见文档output output management
sourceMap
是什么? 映射关系:打包后的文件和source里文件位置的映射,打包出错时的定位
// development devtool: 'cheap-module-eval-source-map',
// production devtool: 'cheap-module-source-map',
devtool: 'cheap-module-eval-source-map'/'source-map',
- cheap:只显示行不显示列
- module: 是否cover除业务代码外模块
- inline:case64
- eval: eval执行方式来执行
webpack devServer
功能:热更新&打开浏览器&开启一个代码服务器 打包后放在内存里 自己手写一个devServer
const express = require('express');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const config = require('./webpack.config.js');
// 在node中直接使用webpack
// 在命令行里使用webpack
const complier = webpack(config);
// 生成一个webpack编译器
const app = express();
// webpackDevMiddleware 中间件,监听文件变化
app.use(webpackDevMiddleware(complier, {}));
app.listen(3000, () => {
console.log('server is running');
});
HMR 热模块替换
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html'
}),
new CleanWebpackPlugin(['dist']),
new webpack.HotModuleReplacementPlugin()
],
devServer: {
contentBase: './dist',
open: true,
port: 8080,
hot: true,
hotOnly: true
},
高级概念
tree shaking
摇树🌲,把没用的东西剔除掉 只支持ES Module 开发环境下保留代码,上线会删除
optimization: {
usedExports: true
}
package.json
sideEffects: ['*.css'], // 不执行tree shaking的模块
lazy loading
对一些模块进行懒加载,通过import 异步加载
async function getComponent() {
const { default: _ } = await import(/* webpackChunkName:"lodash" */ 'lodash');
const element = document.createElement('div');
element.innerHTML = _.join(['Dell', 'Lee'], '-');
return element;
}
document.addEventListener('click', () =>{
getComponent().then(element => {
document.body.appendChild(element);
});
})
webpack 环境变量使用
"scripts": {
"dev-build": "webpack --config ./build/webpack.common.js",
"dev": "webpack-dev-server --config ./build/webpack.common.js",
"build": "webpack --env.production --config ./build/webpack.common.js" // env,往配置文件传入production,默认true
},
webpack 实践
打包性能优化
- 更新工具版本(webpack、node、yarn、npm)
- 在尽可能少的模块上应用loader
- 尽可能少使用plugin ,并使用可靠的plugin
{
test: /\.js$/,
include: path.resolve(__dirname, '../src'), // include 和exclude的使用
use: [{
loader: 'babel-loader'
}]
}
- resolve 参数合理配置,resolve 会调用底层查找逻辑,成本较大
resolve: {
extensions: ['.js', '.jsx'],
alias: {
child: path.resolve(__dirname, '../src/a/b/c/child')
}
},
- 使用一些提高打包速度的插件(引入第三方模块时,第一次引入分析,再次打包不分析第三方模块)
module.exports = {
mode: 'production',
entry: {
vendors: ['lodash'],
react: ['react', 'react-dom'],
jquery: ['jquery']
},
output: {
filename: '[name].dll.js',
path: path.resolve(__dirname, '../dll'),
library: '[name]' // 打包后的文件通过全局变量暴露出去
},
plugins: [
new webpack.DllPlugin({
name: '[name]',
path: path.resolve(__dirname, '../dll/[name].manifest.json'),
})
]
}
配合配置
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');
const files = fs.readdirSync(path.resolve(__dirname, '../dll'));
files.forEach(file => {
if(/.*\.dll.js/.test(file)) {
plugins.push(new AddAssetHtmlWebpackPlugin({
filepath: path.resolve(__dirname, '../dll', file) // 做一次文件映射
}))
}
if(/.*\.manifest.json/.test(file)) {
plugins.push(new webpack.DllReferencePlugin({
manifest: path.resolve(__dirname, '../dll', file)
}))
}
})
- 控制包文件大小
- 多进程打包thread-loader、parellel-webpack
- 合理使用sourceMap
- 开发环境内存编译(webpack-dev-server)
- 开发环境无用插件剔除
底层原理
如何手写一个loader
// replaceLoader.js
const loaderUtils = require('loader-utils'); // 官方推荐获取options 传入参数的方法
module.exports = function(source) {
const options = loaderUtils.getOptions(this);
const callback = this.async();
setTimeout(() => {
const result = source.replace('dell', options.name);
callback(null, result);
}, 1000);
}
// webpack.config.js
const path = require('path');
module.exports = {
mode: 'development',
entry: {
main: './src/index.js'
},
resolveLoader: {
modules: ['node_modules', './loaders']
},
module: {
rules: [{
test: /\.js/,
use: [
{
loader: 'replaceLoader',
},
{
loader: 'replaceLoaderAsync',
options: {
name: 'lee'
}
},
]
}]
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js'
}
}