1. webpack的基本使用
在webpack.config.js文件中
const path = require('path');
module.exports = {
// 打包入口文件
entry: './src/main.ts',
// 打包输入文件
output: {
// 文件名
filename: 'build.js',
// 放置路径
path: path.resolve(__dirname, './dist'),
},
// 配置后import导入时可以不用写对应文件扩展名
resolve:{
extensions:['.js','.ts','.jsx']
}
}
2. loader的作用
Loader和Plugin的区别
- Loader是用于特定的模块类型进行转换。
- Plugin可以用于执行更加广泛的任务,比如打包优化、资源管理、环境变量注入等
除了 JavaScript,如果需要用 webpack 打包其他类型的文件,都需要配置响应的 loader,所以 loader 是增强扩宽了webpack 的功能。
下面用的loader或其它插件都需要先使用npm安装到项目的开发环境中
, 如要打包 css 文件,就需要安装 css-loader。
在webpack.config.js文件中
配置解析css的loader
const path = require('path');
module.exports = {
// 省略上面内容
...,
// 配置loader
module: {
rules: [
{
// test用于对resource(资源)进行匹配,通常设置为正则表达式
test: /\.css$/,
// 配置用于打色的loader
// 完整写法,可以在对象中配置其它的东西
use: [
{loader: 'style-loader'},
{loader: 'css-loader'},
],
// 简写方式
// use: [
// // 1.css-loader 只是解决了css语法解析的问题,只用css-loader是不能将样式加载到页面上的,还需要 style-loader。
// // 2.loader的配置顺序和他的加载顺序是相反的,所以 style-loader 必须放在 css-loader 之前!!!
// 'style-loader',
// 'css-loader',
// ]
}
]
}
}
配置解析less的loader
const path = require('path');
module.exports = {
// 省略上面内容
...,
// 配置loader
module: {
rules: [
// 省略上面内容
...,
{
test: /\.less$/,
use: [
{loader: 'style-loader'},
{loader: 'css-loader'},
{loader: 'less-loader'},
]
}
]
}
}
配置postcss的loader
const path = require('path');
module.exports = {
// 省略上面内容
...,
// 配置loader
module: {
rules: [
// 省略上面内容
...,
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'less-loader',
'postcss-loader'
]
}
]
}
}
配置了postcss-loader,还需要配置额外的postcss-preset-env插件才可以生效
新建 postcss.config.js文件
module.exports = {
plugins: {
'postcss-preset-env'
}
}
配置处理图片的loader
type的取值及区别
- asset: 通用资源类型。推荐这个取值。但需要额外配置才更合理。
- 小图片用base64,大图片用单独导出url
- 增加parser配置,见下面代码
- asset/resource: 发送一个单独的文件并导出url,把url设置到src或background的url中。之前通过file-loader实现。缺点:每个图片加载都会网络请求两次
- asset/inline: 将图片base64编码,并且直接把编码后的源码放到打包后的js文件中。之前通过file-loader实现。缺点:造成js文件非常大,下载js本身比较耗时
const path = require('path');
module.exports = {
// 省略上面内容
...,
// 配置loader
module: {
rules: [
// 省略上面内容
...,
// 使用webpack内置模块打包图片资源
{
test: /\.(png|jpe?g|svg|gif)$/,
type: 'asset',
// asset类型时,设置图片小于xx时使用base64,大于xx时用导出url
parser: {
dataUrlCondition: {
maxSize: 60 * 1024 // 60kb
}
},
generator: {
// 表示:用图片名+webpack生成的hash值+文件扩展名+文件参数命名的图片打包到images文件中
filename: 'images/[name][hash][ext][query]'
}
}
]
}
}
3. webpack插件
安装对应插件
CleanWebpackPlugin: 重新打包时,自动删除dist文件夹
HtmlWebpackPlugin: 打包时,生成指定模板和网站名称的html文件
const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
// 省略上面内容
...,
// 插件
plugins: [
// 重新打包时,自动删除之前打包的文件夹
new CleanWebpackPlugin(),
// 自动生成html文件
new HtmlWebpackPlugin({
// 指定生成html的模板
template: path.resolve(__dirname, './src/index.html'),
// 指定网站的名称
title: '网站名称'
})
]
}
4. Mode配置
作用:可以告诉webpack使用相应模式的内置优化
- 默认值:production
- 可选值:none、development、production
区别:
development:开发模式。会将DefinePlugin中的process.env.NODE_ENV的值设置为development。为模块和分包(chunk)启用有效(看的懂)的名字。
production:生产模式。会将DefinePlugin中的process.env.NODE_ENV的值设置为production。为模块和分包(chunk)启用混淆的名字。
module.exports = {
// 配置是 开发环境development或生产环境production
mode: 'development'
}
5. webpack开启本地服务器
安装webpack-dev-server
在编译之后不会写入到任何输出文件,而是将bundle文件保留在内存中,事实上它使用了memfs的库
module.exports = {
// 省略上面内容
...,
// 安装webpack-dev-server后,开启devServer配置
devServer:{
// 开启HMR模块热替换,默认是开启的
hot:true,
// 此配置,其它人可以通过本地电脑ip来访问这个网站
host:'0.0.0.0',
// 自定义端口号
port: 3000,
// 开发阶段使用代理解决跨域问题。生产阶段一般使用Nginx解决跨域
proxy: {
// 遇到/api开头的请求路径时,代理到下面target配置的地址
'/api':{
// 服务器地址,如http://localhost:5000
tatget:'http://localhost:5000',
pathRewrite:{
// 把以/api开头的这个路径替换为空,相当于去掉/api
'^/api':''
},
// 虽然上面代理了,但服务器接收的host属性地址还是http://localhost:3000,如果服务器设置了检验,这个请求还是失败的,配置changeOrigin属性,服务器接收的host属性的地址就是http://localhost:5000
changeOrigin:true
}
},
// 出现没有配置路由时,自动重定向到http://localhost:3000,避免出现404页面
histroyApiFallback:true
}
}
6. 使用source-map查看打包后的文件
devtool配置项,不配置默认就是none
最佳实践
- 开发阶段:source-map/cheap-module-source-map
- 生成阶段:source-map/cheap-module-source-map
- 线上阶段:false/none
module.exports = {
// 省略上面内容
...,
// 配置打包后代码是否生成source-map来解析
devtool: 'source-map'
}
7. 浏览器兼容性 browserslist
- 打包时为了兼容不同浏览器,可以配置browserslist
- 可以设置在package.json中,也可以单独创建一个.browserslistrc配置在其中
配置在package.json文件
"browserslist": {
// 市场占有率大于0.5%
> 0.5%
// 最新的两个版本
last 2 version
// 24个月还在维护的浏览器
not dead
}
8. 配置babel-loader
const path = require('path');
module.exports = {
// 省略上面内容
...,
// 配置loader
module: {
rules: [
// 省略上面内容
...,
// 使用babel转化js
{
text: /\.js$/,
use: [
'babel-loader'
]
},
//使用babel转化react的jsx
{
text: /\.jsx?$/,
use: [
'babel-loader'
]
},
//使用babel转化ts
{
text: /\.ts$/,
use: [
'babel-loader'
]
},
]
}
}
配置了babel-loader,还需要配置额外的@babel/preset-env预设才可以生效
新建 babel.config.js文件
module.exports = {
presets: [
'@babel/preset-env'
]
}
polyfill
相当于打补丁,针对浏览器不支持的语法
需要安装core.js和regenerator-runtime
在babel.config.js文件中
module.exports = {
presets: [
['@babel/preset-env', {
// useBuiltIns有三个取值。
// false:打包后文件不使用polyfill进行适配,可不设置corejs版本
// usage:根据源代码出现的语法,自动检测所需要的polyfill,打包后文件也会比较小一些
// entry:适用于第三方库本身使用了某些polyfill特性,需要在入口文件添加 import 'core-js/stable',import 'regenerator-runtime/runtime'
useBuiltIns: 'usage',
corejs: 3.8
}]
]
}
ts的编译
在babel.config.js文件中
module.exports = {
presets: [
// 编译es6新特性的预设
'@babel/preset-env',
// 编译react的预设
'@babel/preset-react',
// 编译ts的预设
'@babel/preset-typescript',
]
}
9. webpack性能优化
9.1. 分包处理
- 多入口起点:配置多个打包入口文件,目前vue和react项目都是单入口文件,不推荐
- 动态导入:使用import()语法,使用最多
module.exports = {
// 省略上面内容
...,
// 打包输入文件
output: {
// 文件名
filename: 'build.js',
// 放置路径
path: path.resolve(__dirname, './dist'),
// 设置分包后的文件名,默认情况下name是该文件的完整路径,可在import()中自定义打包后的文件名
chunkFilename: '[name]_chunk.js,
}
}
import(/*webpackChunkName:'文件名'*/ './src/router/about')
- 自定义分包
module.exports = {
// 省略上面配置
...,
//打包输入文件
output: {
//文件名
filename: 'build.js',
//放置路径
path: path.resolve(__dirname, './dist'),
// 设置分包后的文件名,默认情况下name是该文件的完整路径,可在import()中自定义打包后的文件名
chunkFilename: '[name]_chunk.js'
},
// 优化配置
optimization: {
// 设置生成chunkId的算法
// develoment模式下默认是named,表示完整文件名
// production模式下默认是deterministic,确定性的,在不同的编译中变成短数字id
chunkIds: 'named',
splitChunks: {
chunks: 'all',
// 当包大于指定大小时,继续拆包,下面表示大于20kb的继续拆包
maxSize: '20000',
// 将包拆分成不小于10kb的包
minSize: '10000',
// 自己对需要进行拆包的内容分包
cacheGroups: {
// 自定义key
a: {
test: '/node_modules/',
filename: '[name]_node_modules.js',
},
b: {
test: '/utils/',
filename: '[name]_utils.js',
}
}
}
}
}
9.2. 分包处理之prefetch和preload
- prefetch:预获取,将来某些导航下可能需要的资源,在父包加载结束后,浏览器闲置时下载,推荐
- preload:预加载,当前导航下可能需要的资源,和父包以并行方式开始加载
import(
/*webpackChunkName:'文件名'*/
/*webpackprefetch:true*/
'./src/router/about')
9.3. 分包处理之CDN内容分发网络
它是指通过相互连接的网络系统,利用最靠近每个用户的服务器,更快,更可靠的将音乐,图片,视频,应用程序及其他文件发送给用户,提供高性能,可扩展性及低成本的网络内容传递给用户
在output中配置购买的CDN服务器地址
module.exports = {
// 省略上面配置
...,
//打包输入文件
output: {
//文件名
filename: 'build.js',
//放置路径
path: path.resolve(__dirname, './dist'),
// 设置分包后的文件名,默认情况下name是该文件的完整路径,可在import()中自定义打包后的文件名
chunkFilename: '[name]_chunk.js',
// 在output中配置购买的CDN服务器地址
publicPath: 'CDN服务器地址'
},
// 在开发中一般使用第三方库的CDN地址,有两步操作
// 第一步,配置排除不用打包的文件
externals: {
axios: 'axios',
react: 'React'
}
}
第二步,在html文件中手动添加排除打包的第三方库的CDN地址
<body>
<script src='axios的CDN地址'></script>
<script src='react的CDN地址'></script>
</body>
9.4. 优化之提取css文件
- 使用MiniCssExtractPlugin插件可以把css单独打包成一个文件
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
// 省略上面内容
...,
// 配置loader
module: {
rules: [{
// 简写方式
use: [
//内联方式添加到网页中
// 'style-loader',
// 单独打包到一个独立文件
MiniCssExtractPlugin.loader,
'css-loader',
]
}]
},
// 插件
plugins: [
// css分包插件
new MiniCssExtractPlugin({
filename: 'css/[name].css'
})
]
}
10. 前端项目中的优化
-
首屏加载速度
1.1 减少首屏资源体积(收益比较大)- 打包工具的压缩-打包工具已经做好了
- 异步加载,如体积比较大,但又不是马上要用的功能,就可以异步加载
- 更换新版本第三方库,使用支持按需导入的ESM的方式的版本,然后通过配置打包工具中的tree-shaking来实现删除无用代码的目的,减少打包体积
- 能不用第三方库,就不用第三方库,比如一个简单的日期格式化
- 编写代码简洁,规范,减少冗余代码,也能减少代码的体积
- 合理配置需要转为base64的图片,大图片尽量不要转
1.2 收益较小的优化
- 减少简单数据的请求,尽量把小请求合并到大请求接口中
- 可以把没有关联的多个请求接口,使用并行的方式进行请求
- 页面dom元素较多时,可以实现瀑布流式的渲染方式
- 使用骨架屏或loading,给用户提示,减少用户看着白屏的等待焦虑
-
操作速度和渲染速度
- 避免一次性操作大量dom,可以使用长列表渲染和异步渲染
- vue中,频繁切换显示隐藏使用v-show来渲染
- vue或react中,循环添加key值
- vue中,使用keep-alive来缓存组件