- 作者简介:大家好,我是文艺理科生Owen,某车企前端开发,负责AIGC+RAG项目
- 目前在卷的技术方向:工程化系列,主要偏向最佳实践
- 希望可以在评论区交流互动,感谢支持~~~
最近在梳理webpack打包构建的相关内容,本着一起(卷 )面对寒冬的心态,把整个思考和分析过程分享出来。各位看官有任何想法都可以在评论区留言,感谢支持~
下面的内容会按照项目从0到1搭建过程的顺序展开。
首先创建和运行一个最小的webpack项目。
创建一个最小项目
项目初始化
新建一个空文件夹,在该目录下运行 npm init -y
,完成后目录下会多出一个package.json
文件,用来管理npm
依赖的相关配置。
安装 webpack
根目录下运行 pnpm i webpack webpack-cli -D
根目录下新建 webpack.config.js
,用于管理 webpack 相关配置。
在根目录下新建 src 文件夹,在 src 目录下新建 index.js
,并添加一段ES6的 js 代码:
js
// src/index.js
const a = 1
console.log(a, 'a')
在package.json
文件中配置打包构建命令
json
{
// 其他配置...
"scripts": {
"dev": "webpack --mode development --config webpack.config.js"
},
// 其他配置...
}
根目录下运行 pnpm dev
,结果输出:
js
/dist/main.js
并在打包结果中,es6的代码未被转换:
✅已完成里程碑1:
1.
最小项目的创建工作2.
运用webpack打包构建
🔖下一个里程碑2:
将ES6-const
代码转为ES5-var
代码
Babel的使用
Babel插件使用
安装 babel
: pnpm i babel-loader @babel/core -D
增加 webpack
基础配置
js
// webpack.config.js
const path = require('path')
module.exports = {
mode: 'development', // 默认为开发环境模式
entry: './src/index.js', // 入口文件
output: {
path: path.resolve(__dirname, 'dist'), // 打包后的文件路径
filename: 'bundle.js', // 打包后的文件名
},
// 模块配置
module: {
// 以下配置含义:以.js结尾的文件均使用babel-loader
rules: [
{
test: /.js$/,
use: 'babel-loader'
}
]
}
}
在根目录下新建 babel.config.json
,用来配置 babel
。
对应特定的语法转换,我们可以通过Babel提供的插件处理。在 Babel官网 中可以找到 const语法
对应的插件为 @babel/plugin-transform-block-scoping
安装插件 pnpm add @babel/plugin-transform-block-scoping -D
在babel.config.json
中使用
diff
// babel.config.json
+{
+ "plugins": ["@babel/plugin-transform-block-scoping"]
+}
运行 pnpm dev
打包,发现 const语法
被成功转为了 ES5-var语法
。
✅已完成里程碑2:
将ES6-const代码转为ES5-var代码
🔖下一个里程碑3:
将ES6-箭头函数
代码转为ES5-function
代码
在index.js
中新增一段 ES6-箭头函数 的代码:
diff
// index.js
+ const fn = () => {}
+ fn()
同样方法,找到了箭头函数 对应的babel插件
安装插件 pnpm add @babel/plugin-transform-arrow-functions -D
在babel配置文件中添加这个插件:
diff
// babel.config.json
{
"plugins": [
"@babel/plugin-transform-block-scoping",
+ "@babel/plugin-transform-arrow-functions"
]
}
运行 pnpm dev
打包,发现 ES6-箭头函数
代码转为了ES5-function
代码
再对比下配置前的:
通过上面两个例子发现 :ES高版本的语法均可通过 babel插件
进行转换兼容。但如果在开发项目时,每使用一个语法,便手动安装和配置一个插件,效率太低了。
✅已完成里程碑3:
将 ES6-箭头函数 代码转为 ES5-function 代码
🔖下一个里程碑4:
将ES6常用代码
转为ES5代码
Babel预设使用
为了解决上面的问题,Babel
推出了 preset(预设集)
。这个集合里包含了常见的语法转换插件,我们来修改一下:
先移除上面两个插件:
pnpm uninstall @babel/plugin-transform-arrow-functions @babel/plugin-transform-block-scoping -D
再安装 babel预设
:pnpm i @babel/preset-env -D
修改babel配置,注意预设需要在 presets 下配置 ,并且对应一个二维数组
json
// babel.config.json
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "entry",
"corejs": "3.22"
}
]
]
}
运行 pnpm dev
打包,转换成功。
考虑到语法转换需要消耗构建性能,通过配置目标浏览器范围可以减少不必要的损耗。
json
{
"browserslist": "> 0.05%, not dead"
}
配置说明:
- >0.05%:全球使用率大于0.05%的浏览器
- not dead:排除已弃用的浏览器版本,比如IE(兼容ie需要移除这条)
在 browserslist官网 中可以直观看到兼容范围:
✅已完成里程碑4:
将ES6常用代码转为ES5代码
🔖下一个里程碑5:
编译css代码
css代码编译
在 src目录 下新建 index.css
文件,添加一段 css 代码:
css
body {
color: red;
}
并在 index.js
中导入
diff
// index.js
+ import './index.css'
运行 pnpm dev
打包,出现报错 :css代码无法被识别
对于特定的语法格式,我们需要借助于 loader
进行转换。css 语法对应的 loader 为 css-loader
安装css-loader
:pnpm i css-loader -D
配置 webpack:
js
// webpack.config.js
// 其他代码
module.exports = {
module: {
rules: [
{
test: /.css$/,
use: 'css-loader'
}
]
}
}
运行 pnpm dev
打包,报错消失 ,并且在输出文件中可以看到 css代码 。
思考:在实际项目中,如果所有js代码和css代码共存于一个文件中,项目构建后的文件体积很大,每次变更都需要重新下载,下载时间长
如果可以将css文件和js文件拆分 ,使其并行下载 ,并且css文件采用缓存策略,可以提高性能。
✅已完成里程碑5:
编译css代码
🔖下一个里程碑6:
拆分css构建文件
css构建文件拆分
我们可以通过一个webpack插件mini-css-extract-plugin
来将css代码单独提取到一个单独的文件中。
安装插件:pnpm i mini-css-extract-plugin -D
修改 webpack 配置
js
// webpack.config.json
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
module: {
rules: [
{
test: /.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader']
}
]
},
plugins: [
new MiniCssExtractPlugin()
]
}
运行 pnpm dev
打包,完成后 dist目录下多了一个 main.css
的文件,它就是被提取出的css代码文件。
并且在 bundle.js
文件中无法搜索到css代码,说明 css代码提取成功。
✅已完成里程碑6:
拆分css构建文件
🔖下一个里程碑7:
编译typescript代码
编译typescript代码
安装typescript
、babel-typescript预设
:pnpm i @babel/preset-typescript typescript -D
在 src目录 下新建 index.ts
,增加ts代码
ts
// index.ts
const a: number = 1
console.log(a);
修改webpack配置
js
// webpack.config.js
module.exports = {
// 其他代码
entry: './src/index.ts', // 入口文件更改为index.ts
module: {
rules: [
{
test: /.(j|t)s$/, // 增加ts文件规则,使用babel进行编译
use: 'babel-loader'
}
]
}
}
修改babel配置
json
// babel.config.json
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "entry",
"corejs": "3.22"
}
],
"@babel/preset-typescript" // 添加ts预设
]
}
运行 pnpm dev
打包,构建完成。
✅已完成里程碑7:
编译typescript代码
🔖下一个里程碑8:
编译tsx代码
编译tsx代码
分别安装 react
和 babel-react预设
:
pnpm i react
pnpm i @babel/preset-react -D
在 src目录下 新建 Title.tsx
,用来表示一个 react标题组件
tsx
// Title.tsx
import React from 'react'
const count: number = 1
export const Title = () => <div>标题{count}组件</div>
并在index.ts
文件中引入
ts
// index.ts
import {Title} from './Title.tsx'
console.log(Title);
修改webpack配置
js
// webpack.config.js
module.exports = {
// 其他代码
module: {
rules: [
{
test: /.(j|t)sx?$/, // 目标文件增加 jsx和tsx类型
use: 'babel-loader'
}
]
},
}
修改babel配置
json
// babel.config.json
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "entry",
"corejs": "3.22"
}
],
"@babel/preset-typescript",
"@babel/preset-react" // 增加react预设
]
}
运行 pnpm dev
打包,构建完成
tips
: 如果在index.ts中引入组件文件时,省略了后缀名,则构建时会报错
解决方式:
在webpack配置中,增加后缀类型
js
// webpack.config.js
module.exports = {
// 其他代码
resolve: {
extensions: ['.tsx'] // 如果有其他文件类型,可手动添加
}
}
✅已完成里程碑8:
编译typescript代码✅✅✅恭喜你完成了全部的里程碑✅✅✅
总结:本文通过从零开始搭建一个最小的Webpack项目,解决了诸如ES6代码转换
、css代码编译与拆分优化
、ts代码编译
、tsx代码编译
等问题,重新梳理在成熟的cli框架中如何采用babel进行语法编译转换,并对babel预设的作用进行了分析。
日拱一卒,功不唐捐。