问题:
1.Vue-cli3的webpack和vue-cli2webpack配置的差异吗
2.前端代码为何要进行构建和打包?
3.module chunk bundle分别是什么意思,有何区别?
4.loader和plugin的区别?
5.webpack如何实现懒加载?
6.webpack常见的性能优化
7.babel-runtime和babel-polyfill的区别
关于webpack5
webpack5主要是内部效率的优化
对比webpack4,没有太多使用上的改动
可以直接使用webpack5
升级 webpack5 以及周边插件后,代码需要做出的调整:
- package.json 的 dev-server 命令改了 `"dev": "webpack serve --config build/webpack.dev.js"`
- 升级新版本 `const { merge } = require('webpack-merge')`
- `module.rules`中 `loader: {'xxx-loader'}` 换成 `use: {'xxx-loader'}`
- `filename: 'bundle.[contenthash:8].js'` 其中 `h` 小写,不能大写
基本配置(基本配置只能做demo,不能做线上项目)
安全配置
拆分配置
webpack.dev.js 开发环境配置
webpack.prod.js 生产环境配置
webpack.common.js 公共配置
// webpack.common.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { srcPath, distPath } = require('./paths')
module.exports = {
entry: path.join(srcPath, 'index'),
module: {
rules: [
{
test: /\.js$/,
loader: ['babel-loader'],
include: srcPath,
exclude: /node_modules/
},
{
test: /\.css$/,
loader: ['style-loader', 'css-loader', 'postcss-loader']
},
{
test: /\.less$/,
loader: ['style-loader', 'css-laoder', 'less-loader']
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: path.join(srcPath, 'index.html'),
filename: 'index.html'
})
]
}
merge
安装webpack-merge工具 webpack-merge是一个用于合并webpack配置的工具,在使用webpack进行项目开发时,通常会有多个webpack配置文件,例如开发环境配置、生产环境配置等。webpack-merge提供了一个简单的方法来合并这些配置文件,以便在构建过程中根据需要使用不同的配置。通过使用webpack-merge,你可以将多个配置文件合并为一个配置对象,然后将其传递给webpack进行构建。这样可以避免在不同的配置文件中重复定义相同的配置项,提高配置的可维护性和复用性。
// cnpm install --save-dev webpack-merge
// webpack.dev.js
const webpackCommConf = require('./webpack.common.js')
const { smart } = require('webpack-merge')
module.exports = smart(webpackCommConf, {
mode: "development",
module: {
rules: [
{
test: /\.(png|jpg|jpeg|gif)$/,
use: 'file-loader' }
]
},
plugins: [
new webpack.DefinePlugin({
ENV: JSON.stringify('development')
})
]
})
dev-server 启动本地服务
// 安装webpack-dev-server
cnpm install webpack-dev-server ---save-dev
// webpack.dev.js
devServer: {
port: 8080,
progress: true, // 显示打包的进度条
contentBase: distPath, // 根目录
open: true, // 自动打开浏览器
compress: true, // 启动gzip压缩
}
// package.json
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"",
"devBuild": "webpack --config build-base-conf/webpack.dev.js",
"dev":"webpack-dev-server --config build-base-conf/webpack.dev.js" ,
"build": "webpack --config build-base-conf/webpack.prod.js",
// 设置代理
"proxy": {
// 将本地代理 /api/xxx 代理到 localhost:3000/api/xxx
'/api': 'http://localhost: 3000',
// 将本地 /api2/xxx 代理到 localhost:3000/xxx
'/api2': {
target: 'http://localhost: 3000',
pathRewrite: {
'/api2': ''
}
}
}
}
解析ES6
一般在webpack.common.js里用babel-loader处理,用到babel-loader需要配置一个.babelrc的文件
// .babelrc
{
"presets": ["@babel/preset-env"],
"plugins": []
}
// webpack.common.js
rules: [
{
test: /\.js$/,
loader: ['babel-loader'],
include: srcPath,
exclude: /node_modules/
}
]
"@babel/preset-env" 是一个特殊的预设,它根据目标环境的配置自动确定要应用的转换规则。它可以根据目标浏览器的版本或其他环境配置,自动选择并应用必要的转换,以确保代码在目标环境中能够运行。 通过将 "presets": ["@babel/preset-env"] 添加到babel配置文件中,你告诉Babel使用 "@babel/preset-env" 这个预设来进行代码转换。这样,Babel将根据目标环境的配置自动确定要应用的转换规则,以确保你的代码在不同环境中具有良好的兼容性。
解析样式
// loader的执行顺序是从后往前的
{
test: /\.css$/,
loader: ['style-loader', 'css-loader', 'postcss-loader']
}
// 加postcss-loader需要配置一个文件 和src文件夹同级 postcss.config.js
// postcss.config.js
module.exports = {
plugins: [require('autoprefixer')] Calculating...
}
// 主要是给css样式加一些前缀的东西
// 比如设置transform: rotate(-45deg)到Google浏览器解析的时会加上一行
// -webkit-transform: rotate(-45deg)
autoprefixer需要安装一下
{
test: /\.less$/,
// 增加 'less-loader'(安装一下), 注意顺序 从后往前解析 先解析less语法产出css语法到css文件到style
loader: ['style-loader', 'css-loader', 'less-loader']
}
解析 图片文件
// webpack.prod.js
module: {
rules: [
{
test: /\.(png|jpg|jpeg|gif)$/,
use: {
loader: 'url-loader',
options: {
// 小于5kb 的图片用那个base64格式产出
// 否则依然沿用 file-loader的形式,产出 url格式
limit: 5*1024
// 打包到 img 目录下(打包完到img目录下)
outputPath: '/img/',
// 设置图片的 cdn 地址(也可以统一在外面的 output 中
// publicPath: 'http://cdn.abc.com'
}
}
}
]
}
// webpack.dev.js
modules: {
rules: [
{
test: /\.(png|jpg|jpeg|gif)$/,
use: 'file-loader'
}
]
}
file-loader:用于将文件发送到输出目录,并返回文件的公共URL。将图像作为单独的文件保存在输出目录中时,它对于处理图像非常有用。它为每个导入的图像生成一个新文件,并提供访问该文件的URL。
url-loader: 与file-loader类似,但有一个额外的功能。它可以将小图像转换为baseURL,并将它们直接嵌入到捆绑包中,而不是创建单独的文件。这可能有利于提高性能,因为它减少了加载页面所需的HTTP请求数量。但是,较大的图像仍然作为单独的文件发射。
hash的作用
// webpack.prod.js
output: {
// 打包时加上hash值,如果hash值没变加载的时候走缓存,提高加载速度,8代表hash值有8位
filename: 'bundle.[contentHash:8].js',
path: distPath,
}
高级配置
多入口
在webpack.common.js里建入口需要建两个
// webpack.common.js
module.exports = {
entry: {
index: path.join(srcPath, 'index.js'),
pther: path.join(srcPAth, 'other.js')
},
plugins: [
// 多入口 - 生成 index.html
new HtmlWebpackPlugin({
template: path.join(srcPath, 'index.html'),
filename: 'index.html',
// chunks 表示该页面要引用哪些chunk(即上面的index和other对应的文件)
chunks: ['index'] // 只引用index.js
}),
// 多入口 --- 生成other.html
new HtmlWebpackPlugin({
template: path.join(srcPath, 'other.html'),
filename: 'other.html',
chunks: ['other'] // 只引用index.js })
]
}
// webpack.dev.js/webpack.prod.js
module.eports = smart(webpackCommonConf, {
mode: 'production',
output: {
filename: '[name].[cntentHash:8].js', // name 即多入口时 entry对应的变量名
path: distPath
}
})
// webpack.prod.js
plugin: [
new CleanWebpackPlugin(), // 会默认清空 output.path 文件夹
new webpack.DefinePlugin({
ENV: JSON.stringly('production')
})
]
多入口访问就是 localhost:8080/index.html / localhost:8080/other.html
抽离和压缩css
如初级配置那样,没有单独的把css抽离压缩,所以打包完的css是直接写到js文件里的,这样效率比较低,因为要执行js才能把css解析出来,并不科学。
抽离css 去掉在公共配置webpack.common.js中css的处理,在线上环境中webpack.prod.js配置,dev环境下还是沿用之前webpack.common.js中的配置
// webpack.prod.js
const path = require('path')
const webpack = require('webpack')
const { smart } = require('webpack-merge')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const MiniCssExtractPlugin = reuqire('mini-css-extract-plugin')
const TerserJSPlugin = require('terser-webpack-plugin')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack')
const webpackCommonConf = require('./webpack.common.js')
const {srcPath, distPath} = require('./paths')
module.exports = smart(webpackCommonConf, {
mode: 'production',
output: {
filename: '[name].[contentHash:8].js',
path: distPath,
},
module: {
rules: [
{
test: /\.(png|jpg|jpeg|gif)$/,
use: {
loader: 'url-loader',
......
}
},
// 抽离 css
{
test: /\.css$/,
loader: [
MiniCssExtractPlugin.loader, // 注意,这里不再用style-loader
'css-loader',
'postcss-loader'
]
},
// 抽离 less
{
test: /\.less$/,
loader:[
MiniCssExtractPlugin.loader, // 注意,这里不再用style-loader
'css-loader',
'less-loader',
'postcss-loader'
]
}
] },
plugins: [
new CleanWebpackPlugin(), // 会默认清空 output.path 文件夹
new webpack.DefinePlugin({
ENV: JSON.stringify('production')
})
// 抽离 css 文件
new MiniCssExtractPlugin({
filename: 'css/main.[contentHash:8].css'
})
],
optimization: {
// 压缩 css
minimizer: [new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({})]
}
})
mini-css-extract-plugin插件应该只在生产环境构建中使用,并且在loader链中不应该有style-loader,特别是我们在开发模式中使用HMR时。
[terser-webpack-plugin](https://www.jianshu.com/p/4ce8e2247033)
抽离公共代码(重要)
抽离公共代码的场景,多入口文件时打包,会把一个公共文件例如sum.js同时都打包进index.min.js 和 other.min.js,所以需要把sum.js单独抽出来 两边相互引用,或者是每个模块都引用了第三方模块,每次打包的时也会把loadash打包进去,基于我们之前配置输出文件hash值的方式,当修改我们自己的业务代码时,无论修改的多不多,hash都会改变,每次由于这样我们改了一点点的业务代码,而导致hash值变,但是我们引入的第三方模块并没有变,这时候也会被打包进去,这个第三模块也会被重新加载会加载很慢,如果单独把第三方模块拎出来,那么再加载的时候,由于第三方模块没有做出修改,那么他命中缓存,就会加载的很快
// webpack.common.js
plugins: [
// 多入口 -- 生成index.html
new HtmlWebpackPlugin({
template: path.join(srcPath, 'indx.html'),
filename: 'index.html',
chunks: ['index', 'vendor', 'common'] // 考虑代码分割
}),
// 多入口 -- 生成other.html
new HtmlWebpackPlugin({
template: path.join(srcPath, 'other.html'),
filename: 'other.html',
chunks: ['other', 'vendor', 'common'] // 考虑代码分割
})
]
webpack.dev.js开发环境不变,在开发环境下没有必要这么做,在开发环境下希望构建速度更快一点,所以把所有文件一起构建就可以了,不用拆分 也不用压缩 合并 分析,在本地主要就是测试功能。所以拆分抽离压缩合并主要针对线上环境的。
// webpack.prod.js
optimization: {
// 压缩 css
minimizer: [new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({})],
// 分割代码块
splitChunks: {
chunks: 'all',
/**
*initial 入口是chunk, 对于异步导入的文件不处理
async 异步chunk, 只对异步导入的文件处理
all 全部chunk
*/
cacheGroups: {
// 第三方模块
vendor: {
name: 'vendor', // chunk的名称
priority: 1, // 权限更高,优先抽离,重要!!!
test: /node_modules/, // 整个模块的路径,引入的第三方在node_modules里是否能命中
minSize: 0, // 大小限制,一般情况下可以按照需求写
minChunks: 1 // 最少复用过几次, 第三方模块引用一次就单独作为一个包里处理
},
common: {
name: 'common', // chunk名称
priority: 0, // 优先级
minSize: 0, // 公共模块的大小限制
minChunks: 2 // 公共模块最少复用过2次
}
}
}
}
拆分的时候第三方模块和公共模块可能会有一个冲突,第三方模块可能也作为公共模块去引用,所以用了priority权限 做了一个区分。
懒加载(重要)
在webpack中,懒加载(Lazy Loading)是一种代码分割(Code Splitting)的技术,用于延迟加载某些模块或资源,以优化应用程序的性能。 **懒加载可以将应用程序的代码拆分为多个较小的块(chunks)**,**在需要时动态加载这些块**。这样可以减小初始加载的文件大小,提高应用程序的加载速度,并且仅在需要时才加载额外的模块。 在webpack中,可以使用动态导入(Dynamic Import)语法来实现懒加载。通过在需要懒加载的模块上使用 `import()` 函数,webpack会将该模块单独打包为一个独立的chunk,并在需要时进行异步加载。 例如,假设有一个名为 `lazyModule.js` 的模块需要进行懒加载,可以这样使用:
import('./lazyModule.js')
.then(module => {
// 在模块加载完成后的回调函数中使用模块
})
.catch(error => {
// 处理模块加载失败的情况
});
这样, **`lazyModule.js` 将被打包为一个单独的chunk,**并在 `import()` 被调用时进行异步加载。当加载完成后,可以在 `then` 回调函数中使用该模块。 懒加载在大型应用程序中特别有用,可以根据需要按需加载模块,提高初始加载速度和用户体验。
vue react的异步组件也是如此加载的
回顾一下chunk
产出chunk的地方 entry里面定义chunk
plugins里使用chunk
分割代码的时候产出chunk
处理JSX
在[babel官网](https://www.babeljs.cn/docs/babel-preset-react)上可以搜一下react,安装preset-react
npm install --save-dev @babel/preset-react
安装完在.babelrc文件配置一下, 只要做react应用在.babelrc配置上就可以了
{
"presets": ["@babel/preset-react"]
}
因为整个的js解析都过babel-loader,过babel-loader时会使用preset-react把JSX语法解析了
处理VUE
用vue-loader,安装完成之后呢就可以在webpack.common.js中针对vue的文件配置下
npm i vue-loader
rules: [
{
test: /\.vue$/,
loader: ['vue-loader']
include: srcPath
}
]
module chunk bundle 的区别
module - 各个源码问价,webpack中一切皆模块
一个module是webpack中的基本构建块,它可以是一个JavaScript文件、一个CSS文件、一个图片文件或其他类型的文件。在webpack中,每个文件都被视为一个module。Webpack使用loader来处理不同类型的模块,并将它们转换为可被浏览器理解的代码。(除了index.html每一个都是模块,html是输出的类似于模板的一个东西)
chunk - 多模块合并成的,如entry import() splitChunk
chunk``主要是在内部用于管理捆绑过程。输出是由bundle由chunk组成,其中有几种类型entry child等。通常,chunk直接与bundle对应,但是有些配置不会产生一对一的关系,例如MiniCssExtractPlugin可从chunk中抽离出css文件,单独生成bundle。生成chunk有三种方式,entry、动态加载、splitChunks抽取共有代码
bundle - 最终的输出文件
bundle``由许多不同的模块生成,包含已经经过加载和编译过程的源文件的最终版本。通常情况下,一个应用程序会生成一个Bundle,它包含了所有的JavaScript、CSS、图片等资源,可以被直接加载到浏览器中运行。