Webpack项目构建入门:babel的核心角色

  • 作者简介:大家好,我是文艺理科生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-loaderpnpm 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代码

安装typescriptbabel-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代码

分别安装 reactbabel-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预设的作用进行了分析。
日拱一卒,功不唐捐。

相关推荐
学习前端的小z5 分钟前
【前端】深入理解 JavaScript 逻辑运算符的优先级与短路求值机制
开发语言·前端·javascript
彭世瑜29 分钟前
ts: TypeScript跳过检查/忽略类型检查
前端·javascript·typescript
FØund40430 分钟前
antd form.setFieldsValue问题总结
前端·react.js·typescript·html
Backstroke fish30 分钟前
Token刷新机制
前端·javascript·vue.js·typescript·vue
小五Five32 分钟前
TypeScript项目中Axios的封装
开发语言·前端·javascript
小曲程序32 分钟前
vue3 封装request请求
java·前端·typescript·vue
临枫54132 分钟前
Nuxt3封装网络请求 useFetch & $fetch
前端·javascript·vue.js·typescript
前端每日三省33 分钟前
面试题-TS(八):什么是装饰器(decorators)?如何在 TypeScript 中使用它们?
开发语言·前端·javascript
小刺猬_98534 分钟前
(超详细)数组方法 ——— splice( )
前端·javascript·typescript
契机再现35 分钟前
babel与AST
javascript·webpack·typescript