Webpack
此文章仅为webpack学习过程中的完整笔记。精粹笔记请见webpack学习之代码分析
为什么需要webpack?
不使用webpack时:
其一,需要加载多个js文件。缺点:多个js之间的依赖关系需要按顺序进行引入,扩展性低,局限性大;
其二,只加载一个继承了所有js文件代码的js文件。缺点:作用域问题(不同js绑定不同的变量,如jquery的$和loadash),文件太大,可读性差,可维护性弱。
解决作用域问题(早期GRUNT,GULP):
IIFE(immediately invoke function expression)立即执行函数表达式
;(function(){var name=1;return name;})();
//即没让name污染环境,解决了作用域问题,又能访问到name;
如何代码拆分,只要加载需要用到的模块
//暴露模块
module.exports = {module1,module2};
//获取模块
const a = require('./modules');
//在获取的页面进行使用
a.module1;a.module2;
require只适用于NodeJs,浏览器无法使用;
浏览器如何支持模块(早期browserify,reqiure.js)
//引用require.js,提供define和require
//define:定义模块,且要return,能被其他模块引用;define([以来的地址 空],function(){return 当前js定义的函数模块add })
//require:定义模块,不需要return,但不能被其他js引用;require([依赖的地址],function(按依赖地址顺序导入的模块的形参add){ 当前js定义的函数模块 add(1,2)})
//多模块为一个js文件
add.js & minus.js:里面用define
现在的浏览器支持方式
//定义
const add=(x,y)=>{return x+y}
export default add;
//载入
<script type="module">
//不配置type="module",会报错:不能再模块外不调用import
import add from '地址'
</script>
npx命令:当一个模块不存在时,会从网上先下载再执行
浏览器支持ECMAScript的模块不太好,且迭代太慢,所以要用webpack
webpack能干嘛
打包js应用程序,支持es的模块化和commomjs。扩展支持其他静态资源的打包,图片,字体,样式文件;
webpack竞品 Vite,Rollup,Parcel
如何解决作用域冲突(IIFE),如何代码拆分(模块化),如何让浏览器支持模块(require.js和es的模块化)
webpack应用
全局安装webpack(不推荐全局,会锁定某个版本;在团队协作在容易有差异。)
npm install webpack webpack-cli --global
//查看webpack版本
webpack -v
全新项目内安装webpack
npm init -y 使用默认配置生成
-y 的含义:yes的意思,在init的时候省去了敲回车的步骤,生成的默认的package.json
version 版本号,一般从0.1.0开始
description描述
keywords 关键词用逗号隔开
author名字加邮箱
license UNLICENSE,一般不授权公用
npm install webpack webpack-cli --save-dev
项目内安装‘开发’依赖
pwd 打印当前工作目录
//在项目文件夹内打包,此时的webpack时全局的webpack
webpack
//查看打包详情
webpack --status detailed
//使用项目内的webpack进行打包
①先卸载全局的webpack
npm uninstall webpack webpack-cli --global
②使用项目内的webpack进行打包
npx webpack //先在当亲目录找,没有就往上继续找
webpack.config.js配置
为什么用:命令行配置麻烦,且无法保存配置;
在项目目录下新建webpack.config.js。
运行时,webpack自动读取。
在nodejs中运行,使用了nodejs的commonjs模块,因此需要使用
const path = require('path')
module.exports = {
entry: './src/index.js',
mode: 'none',
// "development" | "production" | "none"
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, './dist')
//path需要为绝对路径
}
}
麻烦:每次都要去index.html里面更新打包后的js地址。
插件来源:
社区(htmlWebpackPlugin),
webpack内置,
第三方;
使用HtmlWebpackPlugin
mode选项
取值:none,development,production
使用sourceMap进行调试
#安装
npm install html-webpack-plugin -D (--save-dev)
目前预览项目效果是通过访问当前index.html的地址
入口文件及其依赖变化时,webpack自动进行打包,但还是要手动刷新页面;
npx webpack --watch
webpack热更新:使用webpack -dev-server进行页面的更新(重新加载);webpack --watch只是为了自动打包。
不监听webpack.config.js
//安装,作为开发依赖进行安装到本地
npm install webpack-dev-server -D
#原理:不依赖dist内的文件。没有输出任何物理文件,将最新的打包后的文件放在内存中,且指定的内存文件夹的名字由devServer.static指定,文件发生变化后,立即在内存生成一份最新的,并提供使用;
删除dist后,无发访问dist文件夹,但还是能访问到内部的html文件;
资源模块asset module(webpack内置资源模块):允许使用webpack来打包其他资源文件。字体,图片,样式等。
资源模块类型asset module type:通过四种资源模块类型来替换所有loader
asset/resource:发送单独文件并导出url(生成文件)
asset/inline:导出资源的URI (不生成文件,生成dataURI并填入要用的地方)
asset/source :导出资源的源代码(不生成文件)
asset:选择发送单独文件(resource)还是导出资源的URI(inline)
#自动运行webpack-dev-server之后打开网页
npx webpack-dev-server --open
Loader
什么是Loader:webpack只能理解js和json这样的文件;loader可以让webpack解析其它类型的文件。
使用loader加载css代码
#安装css-loader
npm install css-loader -D
//导入并使用style.css
import './style.css'
document.body.classList.add('hello')
抽离和压缩css
#安装抽离CSS插件
npm install mini-css-extrace-plugin -D
#安装压缩CSS插件
npm install css-minimizer-webpack-plugin
#webpack5版本才可以用
加载字体
//加载字体:需要为字体格式后缀的文件,用module.rule处理,type为'asset/resource'
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
type: 'asset/resource'
}
//不需要额外的loader,是因为字体是被css文件引用的,因此会被编入到css文件内,而css文件又有css-loader进行处理。
加载数据
CSV,TSV
#安装依赖
npm install csv-loader
npm install xml-loader
babel -loader
为什么需要babel-loader:将ES6->ES5,将es6转换成低版本浏览器能够运行的es5
#安装三个包
babel-loader:webpack里应用babel解析ES6的桥梁
@babel/core:babel核心模块
@babel/preset-env:babel的预设,一组babel插件的集合
npm install babel-loader @babel/core @babel/preset-env -D
缺少regeneratorruntime插件
#下载插件
#regeneratorruntime运行时需要的内容
npm install @babel/runtime -D
#在需要regeneratorruntime的地方自动导包。
npm install @babel/plugin-transform-runtime -D
代码分离(抽离重复代码;加快首屏加载;)
入口起点:使用entry配置手动地分离代码;共用的文件会被重复打包
防止重复:使用Entry dependencies或者splitChunksPlugin(抽离静态代码import…from…关键字)去重和分离代码
动态导入:通过模块的内联函数(import)来分离代码;可以进行懒加载(用到再加载);
const btn = document.createElement('button')
btn.textContent = '点击运行'
btn.addEventListener('click', () => {
import('./math.js').then(({ add }) => {
console.log(add(1, 2))
})
})
document.body.appendChild(btn);
splitChunksPlugin和动态导入(import方法导入模块)一起使用实现动静态代码抽离
实践:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l3HwEo50-1643037187254)(C:\Users\a1808\AppData\Roaming\Typora\typora-user-images\image-20220115020617736.png)]
魔法注释(动态分离)
为用import(‘…’)方法引入的模块,进行分包时命名。
//正常,以lodash的地址进行命名
import('lodash').then(({add})->{})
//魔法注释,剥离出来的包就叫math11
import(/*webpackChunkName:'math11'*/'lodash').then(({add})->{})
预加载(使用魔法注释配置webpackPrefetch:true;)
//魔法注释,剥离出来的包就叫math11
import(/*webpackChunkName:'math11',webpackPreload:true */'lodash').then(({add})=>{})
预获取:如若配置webpackPreload:true,则效果类似懒加载;
通过link标签来引入资源到index.html(再所有资源加载完成后,网络空闲时,进行加载)
下图为预获取
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OOapQ8ZD-1643037187256)(C:\Users\a1808\AppData\Roaming\Typora\typora-user-images\image-20220116015350259.png)]
//魔法注释,剥离出来的包就叫math11
import(/*webpackChunkName:'math11',webpackPrefetch:true */'lodash').then(({add})=>{})
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const CSSMinimizerWenpackPlugin = require('css-minimizer-webpack-plugin')
const toml = require('toml')
const yaml = require('yaml')
const json5 = require('json5')
const path = require('path')
module.exports = {
// 要打包的文件,单入口
// entry: './src/index.js',
// 多入口,对象;使用splitChunksPlugin进行分离需要搭配optimization.splitChunks:{chunks:'all'}
entry: {
index: './src/index.js',
another: './src/another-module.js'
},
// 多入口,防止重复
// entry: {
// index: {
// import: './src/index.js',
// dependOn: "shared"
// },
// another: {
// import: './src/another-module.js',
// dependOn: "shared"
// },
// // 共同模块的入口,也会呗putput输出;如果上面的包含dependOn的入口如果有引入lodash这个模块。那么就把'lodash'取名为‘shared’,供多个入口使用;
// shared: 'lodash'
// },
//打包后的文件放哪里
output: {
// 单入口的entry配置的文件名
// filename: 'bundle.js',
// 多入口
filename: '[name].bundle.js',
path: path.resolve(__dirname, './dist'),
//清空dist内的文件再生成
clean: true,
//指定资源的文件名
// assetModuleFilename: 'images/test.png'
assetModuleFilename: 'images/[contenthash][ext]'
},
// mode: 'production',
mode: 'development',
//定位错误到开发文件。而不是打包后的代码;
devtool: 'inline-source-map',
// "development" | "production" | "none"
// 插件(社区,内置,)
plugins: [
new HtmlWebpackPlugin({
// 要使用当作模板的文件地址
template: './index.html',
// 生成后的文件名
filename: 'app.html',
// 将打包后的bundle.js插入到body元素内
inject: 'body'
}),
new MiniCssExtractPlugin({
// 生成后的文件名
filename: 'style/mystyle.css',
})
// 生成html,且自动引入打包生成的bundle.js;如果不配置构造对象那么就生成默认的index.html,无自定内容,且打包后的js默认放在head里面;
],
// 供webpack-dev-server进行配置
devServer: {
//文件夹就打开目录,为文件就打开文件;先打开文件夹再开目录可以看;直接打开文件就不行;
static: './dist',
open: true
},
module: {
rules: [{
test: /\.png$/,
type: 'asset/resource',
//具体定制生成后的文件名,优先级别比output内的assetModuleFilename高
generator: {
// filename: 'images/generator1.png'
}
},
{
test: /\.jpg$/,
type: 'asset/inline'
},
{
test: /\.txt$/,
// 生成打他URI,base64的格式
type: 'asset/source'
}, {
test: /\.jpeg$/,
// 选择导出文件并返回url,或者是生成uri
type: 'asset',
parser: {
//设置判断资源的大小,单位KB
dataUrlCondition:
{ maxSize: 4 * 1024 * 1024 }
}
},
// {
// test: /.css$/,
// use: 'css-loader'
// },
// {
// test: /\.css$/,
// use: ['style-loader', 'css-loader']
// //解析成功,但是未被页面引入
// },
// {
// test: /\.(css|scss)$/,
// use: ['style-loader', 'css-loader', 'sass-loader']
// },
{
test: /\.(css|scss)$/,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader']
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
type: 'asset/resource'
},
{
test: /\.(csv|tsv)$/,
use: 'csv-loader'
},
{
test: /\.xml$/,
use: 'xml-loader'
},
{
test: /\.toml$/,
type: 'json',
parser: {
parse: toml.parse
}
},
{
test: /\.yaml$/,
type: 'json',
parser: {
parse: yaml.parse
}
},
{
test: /\.json5$/,
type: 'json',
parser: {
parse: json5.parse
}
},
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
plugins: [
[
'@babel/plugin-transform-runtime'
]
]
}
}
}
]
},
optimization: {
minimizer: [
// 需要将mode改为production(只在生产环境进行压缩)
new CSSMinimizerWenpackPlugin()
],
// 多入口时,处理共同代码。共同的模块生成n个(vendors- 模块的文件地址) 开头的js文件,这些模块由import ...from..进行引入,都是静态资源;
// splitChunks: {
// chunks: 'all'
// }
}
}