1. 为什么使用webpack
js文件引入发展历程
- 单独在html中引入js文件:文件间存在先后顺序的依赖,管理js引入顺序劳心费力。
- 将所有js文件通过工具打包到一个js文件中,通过立即执行函数解决作用域问题,但文件过大,不好管理。
- nodeJS 使用的commonJS (export require) 包管理工具,前端不支持:通过工具(browserify、requirejs)转为前端支持的,但是生成的代码不够简洁。
- esmodule(export import) 直接前端支持的模块化,但是不是所有浏览器都适配。
- webpack 支持commonjs 及 esmodule,还支持其他静态资源的打包。
注意:webpack本身是处理js的依赖关系的,其他资源的打包,需要依赖插件。
2. webpack 安装
环境准备:安装node,会一起安装npm、npx
- Node Package Management:包管理工具
- Node Package Execute:包执行工具,先去node_modules中找命令
需要的包:webpack webpack-cli 最好不用global全局,给每个项目安装单独的webpack版本好控制
运行:npx webpack 打包
加自定义配置: npx webpack --entry ./src/index.js --mode production
保留及复用配置:新增webpack.config.js文件。
3. webpack.config.js 文件
位置:项目的根目录下
格式:js 文件
作用:设置打包时的配置信息 entry,output,mode,devtool,module,plugins,optimization,performance
const path = require('path')
module.exports = {
entry:'./src/index.js',
output:{
filename:'bundle.js',
path: path.resolve(__dirname,'./dist')
},
mode:'none',
}
4. webpack 打包流程
例如,js是依赖的起始,entry文件可以依赖多个文件,层层依赖。
webpack有很多插件,可以进行打包优化、资源管理(图片、文本等资源),利用插件还可以简化使用。
5. webpack 插件
注意:配置在webpack.config.js中的plugins:[{},{}] 中
5.1 html-webpack-plugin
作用:根据配置,将src index.html文件作为模板,生成dist下的新html文件,且自动添加js依赖(dist中打包后的)。
安装:
npm install html-webpack-plugin -D
使用:
//引入 返回的是构造函数
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
plugins:[
new HtmlWebpackPlugin({
//使用的模板,相对路径
template:'./index.html',
//生成的目标文件
filename:'app.html',
//js 注册的位置
inject:'body'
}),
]
}
6 webpack 开发环境搭建
6.1 mode:'development'
使用开发模式。
6.2 devtool:'inline-source-map'
作用:调试时,src下展示源文件。
配置前:src下是编译后的js文件,跟源码不一致。
配置后:src下是源文件格式。
6.3 npx webpack --watch
作用:监测js变化,自动重新打包,但还是需要刷新页面。
6.4 webpack-dev-server
第三方库,实现热模块更新替换,监测js变化,自动重新打包且刷新页面。
安装:npm install webpack-dev-server -D
配置:webpack.config.js中配置读取的目录。
//配置webpack-dev-server读取的目录
devServer:{
static:'./dist'
},
7. asset modules 内置资源模块
作用:用来引入其他类型资源,类似图片(png/jpg/svg)以及文本txt
7.1 资源模块类型
资源模块类型即webpack处理资源时,处理方式的类型。
- asset/resource 返回资源文件对应的url
- asset/inline 返回资源文件的dataUrl base64
- asset/source 返回资源的源码,类似txt中的内容,直接import是读不出来的,设置规则可读取txt中的文本内容
- asset 自动选择resource还是inline可配置临界值大小。
7.2 模块下配置规则
test&type
module.exports = {
//入口,相对路径,打包文件的入口
entry:'./src/index.js',
//资源模块配置
module:{
//配置规则
rules:[
{
//匹配资源的正则表达式
test:/\.png$/,
//资源类型 加载资源返回url
type:'asset/resource',
//配置打包生成文件的位置及名称
generator:{
// filename:'images/test.png'
filename:'images/[contenthash][ext]'
}
},
{
// asset/inline 行内资源 base64 常用来加载svg
test:/\.svg$/,
type:'asset/inline',
},
{
//获取资源的源数据
test:/\.txt$/,
type:'asset/source'
},
{
test:/\.jpg$/,
type:'asset',
parser:{
//什么时候选择dataUrl,设置其对应的最大值
dataUrlCondition:{
maxSize: 4*1024*4024,
}
}
}
]
}
}
7.3 引入资源并使用
使用时,直接import 引入
匹配到rules中的规则,webpack会处理加载及使用
如果没有匹配到,又非js/json文件,webpack会报错
import hello from "./hello";
//引入需要使用到的图片/文字资源,引入了资源,匹配到rules中的规则,webpack会处理加载及使用
//如果没有匹配到,非js,json文件,webpack会报错
import imgsrc from './assets/free.png'
import svgsrc from './assets/folder-svgrepo-com.svg'
import emText from './assets/hello.txt'
import jpgMap from './assets/16627.jpg'
//图片资源设置对应元素的src
const img = document.createElement('img');
img.src = imgsrc;
document.body.appendChild(img)
const img2 = document.createElement('img');
img2.src = svgsrc;
img2.style.cssText = "width:600px;hight:300px";
document.body.appendChild(img2);
//文本资源可用div textContent属性展示
const block = document.createElement('div')
block.textContent = emText;
block.style.cssText = "width:600px;height:400px;background:lightblue";
document.body.appendChild(block)
const img3 = document.createElement('img')
img3.src = jpgMap;
console.log(jpgMap)
img3.style.cssText = "width:1000px;height:800px;display: block"
document.body.appendChild(img3);
asset/inline返回样式,常用来加载svg小图片
8 loader 外部插件/库,加载其他资源
test&use
8.1 加载css、less文件
安装需要依赖的包
npm install style-loader css-loader less-loader less -D
- style-loader 负责将css样式加载到页面head中
- css-loader 加载css文件
- less-loader 加载less文件
- less 解析less文件
module中配置
注意use顺序:从右往左,链式调用,逆序执行。
module.exports = {
//入口,相对路径,打包文件的入口
entry:'./src/index.js',
module:{
rules:[
//加载css或less资源
{
test:/\.(css|less)$/,
use:['style-loader','css-loader','less-loader']
}
]
}
}
配置css/less样式
引入资源并使用
import './assets/style/hello.css'
import './assets/style/style.less'
document.body.classList.add('hello')
//less中是body元素上添加的样式,加载进来直接生效
加载效果
8.2 抽离css
安装插件:npm install mini-css-extract-plugin -D
config配置:
//抽离css到单独文件 默认main.css
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
//入口,相对路径,打包文件的入口
entry:'./src/index.js',
//插件配置
plugins: [
//使用抽离css插件
new MiniCssExtractPlugin({
//指定路径及文件名
filename: 'style/[contenthash].css'
}),
],
module:{
rules:[
//加载css或less资源
{
test:/\.(css|less)$/,
//使用抽离css的loader替换style-loader
use:['MiniCssExtractPlugin.loader','css-loader','less-loader']
}
]
}
}
重新打包后结果
app.html,css样式通过link标签引入
生成的css文件
8.3 压缩css
安装插件 npm install css-minimizer-webpack-plugin -D
config配置
//压缩dist中的css文件
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
module.exports = {
//入口,相对路径,打包文件的入口
entry:'./src/index.js',
//模式
mode: 'production',
/**
* 优化配置,需结合production 模式使用
*/
optimization: {
//压缩工具 mode未production时才执行
minimizer: [
new CssMinimizerPlugin()
]
}
}
打包后效果:
8.4 样式中使用images
css文件中直接引用图片相对路径即可
/* css中也可以直接使用图片资源,只要配置过图片资源加载方式,webpack就能加载 */
.block-bg {
background-image: url('../webpack-logo.svg') !important;
}
8.5 加载fonts字体
fonts字体也是资源的一种,用asset/resource加载
下载字体:这里是iconfont
配置config:
module.exports = {
//入口,相对路径,打包文件的入口
entry:'./src/index.js',
module:{
rules:[
{
// 加载字体 icon图标也是字体的一种
test:/\.(woff|woff2|eot|ttf|otf)$/,
type:'asset/resource',
},
]
}
}
CSS中配置字体:
使用字体:
页面效果:
8.6 加载csv/tsv/xml数据
下载loader: npm install csv-loader xml-loader -D
准备数据:
配置config
module.exports = {
//入口,相对路径,打包文件的入口
entry:'./src/index.js',
module:{
rules:[
// csv表格
{
test:/\.(csv|tsv)$/,
use:'csv-loader'
},
// xml文件
{
test:/.\xml$/,
use:'xml-loader',
},
]
}
}
使用数据:
//引入数据文件
import csvData from './assets/data.csv'
import xmlData from './assets/data.xml'
//直接输出
console.log(csvData);
console.log(xmlData);
加载结果:
csv数组,xml对象
8.7 自定义json模块parser
作用:加载类json文件
下载第三方库:npm install toml yaml json5 -D
作用解析toml yaml json5文件,且配有loader供webpack使用
准备数据:
配置config文件:
//引入 toml yaml json5
const toml = require('toml')
const yaml = require('yaml')
const json5 = require('json5')
module.exports = {
//入口,相对路径,打包文件的入口
entry:'./src/index.js',
module:{
rules:[
/**
* 自定义json模块,注意type为'json' 使用toml yaml json5的parse进行转换
*/
{
test:/\.toml$/,
//注意type为'json'
type:'json',
parser:{
parse:toml.parse
}
},
{
test:/\.yaml$/,
type:'json',
parser:{
parse:yaml.parse
}
},
{
test:/\.json5$/,
type:'json',
parser:{
parse:json5.parse
}
}
]
}
}
使用数据:
//引入数据文件
import yaml from './assets/data.yaml'
import toml from './assets/data.toml'
import json5 from './assets/data.json5'
//webpack直接将json转换为了对象
console.log(toml.title)
console.log(toml.owner.name);
console.log(yaml.title)
console.log(yaml.owner.name);
console.log(json5.title)
console.log(json5.owner.name);
输出结果:
9. babel-loader
webpabk本身能打包js资源,但是不会对ES6语法进行转换,某些浏览器不支持ES6的话,就会报错,所以使用babel进行ES6到ES5的转化。
将hello.js中加入ES6语法,重新打包,查看打包后的文件,发现未转换。
9.1 安装
npm install babel-loader @babel/core @babel/preset-env -D
- babel-loader webpack中解析es6
- @babel/core babel核心模块
- @babel/preset-env 针对不通的ES6语法(promise,=>,...)有定制对应的插件,preset-env是插件的集合
9.2 配置config
module.exports = {
//入口,相对路径,打包文件的入口
entry:'./src/index.js',
module:{
rules:[
{
test: /\.js$/,
//正则表达式,包中的js无需解析
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
]
}
}
9.3 效果
10 代码分离
项目大了后会有多个js文件,且如果都打包在一个文件中,体积过大,影响页面效率,且有些js文件特定条件下才需要加载,所以需要实现代码分离(多个js文件),按需加载。
10.1 入口文件
通过entry中配置多个文件,实现代码分离。
优点:配置简单
缺点:包不共享,依赖打包了多次,占用体积。
准备文件:
使用lodash包,且一旦被加载,直接输出拼接后的字符串
配置config
module.exports = {
//入口,相对路径,打包文件的入口
entry: {
index: './src/index.js',
another:'./src/another.js'
},
//输出配置
output: {
//输出的文件名,这里name会去entry中的key
filename: '[name].js',
//输出的路径,这里需要是绝对路径
path: path.resolve(__dirname, './dist'),
},
}
打包结果
此时文件大小:
在index中也使用lodash,重新编译打包,可以看到index也重新打包了lodash
10.2 防止重复
10.2.1 entry 配置dependOn和shared
缺点:依赖关系得手动设定,心智负担重。
配置config
module.exports = {
//入口配置dependOn&shared
entry: {
index: {
import:'./src/index.js',
dependOn:'shared',
},
another:{
import:'./src/another.js',
dependOn:'shared',
},
shared:'lodash'
},
}
打包效果:
10.2.2 SplitChunksPlugin
缺点:无法按需加载。
插件,webpack5自带的,自动分离公共依赖
module.exports = {
entry: {
index:'./src/index.js',
another:'./src/another.js',
},
optimization: {
//分离公共模块
splitChunks:{
chunks:'all'
}
}
}
打包结果:
10.2.3 动态导入
通过import('包、文件') 实现动态导入
当执行某些操作,可在对应的回调函数中import资源,实现按需加载。
10.3 懒加载
定义math.js
export const add= (x,y)=>{
return x+y;
}
import导入:index.js 中配置点击按钮时加载math.js
const button = document.createElement('button')
button.innerHTML = '加法计算'
button.addEventListener('click',()=>{
//动态导入 /*webpackChunkName:'math.js'*/ 指定打包后的文件名
import(/*webpackChunkName:'math.js'*/'./math.js').then(({add})=>{
console.log(add(3,5));
})
})
document.body.appendChild(button);
页面效果:
点击按钮,才加载math.js
10.3.1 懒加载的两种配置
webpack 魔法注释
prefecth:网络空闲时加载,不用非得等到事件触发 推荐
preload:直接跟随主chunk并行下载
button.addEventListener('click',()=>{
//动态导入 /*webpackChunkName:'math.js'*/ 指定打包后的文件名
import(/* webpackChunkName: 'math', webpackPrefetch: true */'./math.js').then(({add})=>{
console.log(add(3,5));
})
})
网络空闲时直接下载,非点击button才触发
11 缓存
浏览器会将文件缓存下来,减少网络开销,确保自身代码改动时,重新加载,而第三方库使用缓存
11.1 自定义文件
通过定义文件名通过内容生成hash值,当内容改变后,生成新文件名来去缓存
module.exports = {
entry: {
index:'./src/index.js',
another:'./src/another.js',
},
output: {
//输出的文件名,contenthash会根据内容生成hash值
filename: 'scripts/[name].[contenthash].js',
},
}
11.2 第三方库
第三方库,打包到一个文件中,配置需缓存。
多个包时,生成多个输出文件
config配置
module.exports = {
entry: {
index:'./src/index.js',
another:'./src/another.js',
},
output: {
//输出的文件名,contenthash会根据内容生成hash值
filename: '[name].[contenthash].js',
},
splitChunks:{
cacheGroups:{
vendor:{
test:/[\\/]node_modules[\\/]/,
name:'vendors',
chunks:'all',
}
}
}
}
重新打包后的效果:
12 生成、开发环境分离
12.1 分离配置
输出路径变更
12.2 合并逻辑
npm install webpack-merge -D
通过传入的env决定加载什么配置
const {merge} = require('webpack-merge')
const commonConfig = require('./webpack.config.common')
const devConfig = require('./webpack.config.dev')
const prodConfig = require('./webpack.config.prod')
module.exports = (env)=>{
switch(true){
case env.development:
let outObj = merge(commonConfig,devConfig);
console.log(JSON.stringify(outObj.output));
return outObj;
case env.production:
return merge(commonConfig,prodConfig);
default:
return new Error('No matching config was found')
}
}
12.3 配置脚本
npm run start/build 执行对应命令
{
"name": "webpack_base",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "webpack serve -c ./config/webpack.config.js --env development",
"build":"webpack serve -c ./config/webpack.config.js --env production"
}
//...
}