Webpack(四)在webpack中集成babel

221 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第2天,点击查看活动详情

Webpack 是一个前端资源加载/打包工具。它将根据模块的依赖关系进行静态分析,然后将这些模块按照指定的规则生成对应的静态资源。

Webpack 系列专栏地址 进入

本文demo地址查看

babel

js使用 es6+ 的一些语法时,遇到某些版本较低的浏览器时可能无法运行,此时就可以用 babel 对js的一些语法进行兼容处理。

Babel 是一个 JavaScript 编译器,可以做以下事情:

  • 通过语法转换器来支持新版本的 JavaScript 语法
  • 通过 Polyfill 方式在目标环境中添加缺失的特性 (通过引入第三方 polyfill 模块,例如 core-js

Babel 是一个工具链,主要用于将采用 ECMAScript 2015+ 语法编写的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。

安装

安装 loader 和 核心包:

# 当前版本 7.x
npm i babel-loader @babel/core -D

preset-env

安装预设 用来处理es6+语法:

npm i @babel/preset-env -D

webpack.config.js 配置:

{
    test: '/.js$/',
    use: {
        loader: 'babel-loader',
        options: {
            presets: ['@babel/preset-env'],
        },
    },
},

src/index.js中写一段测试代码:

const arr = [new Promise(() => {}), new Promise(() => {})]
arr.map((item) => {
	console.log(item)
})

执行打包命令成功后,查看bundle文件:

捕获.PNG

const 被转换成了 var,箭头函数被转换成了 普通的function。相关的语法都被转换成了兼容的形式,但是Promise并没有被转换,这是因为babel只是针对语法进行转换,一些目标环境缺失的特性(如浏览器不支持Promise),需要使用Polyfill 方式。

Polyfill

Polyfill 基于 core-js, core-js中包含了es6+的一些新特性。在代码执行之前加载core-js就可以让原本不支持新特性的浏览器通过调用core-js而支持Promise等新特性。

安装Polyfill:

npm i @babel/polyfill -S

注意 Polyfill 要安装在生产环境,在需要使用得地方引入 src/index.js

import '@babel/polyfill'

提示:从babel 7.4版本开始不再安装 Polyfily,直接使用core-js/stable

执行打包命令:

捕获.PNG

打包出来的文件大了很多,这是因为引入了 Polyfill,上面的引入方式是全量引入,没用到的特性也被打包了进来,可以利用按需引入来优化包体积,减少多余的代码。

通过给 preset-env添加配置项,来实现Polyfill的按需引入

useBuiltIns

useBuiltIns@babel/preset-env的一个配置项,可以为Polyfill提供按需引入特性的功能,可选参数有 entry | usage | fasle

  • entry 需要在webpack的入口模块中 import '@babel/polyfill',babel就会根据我们的代码情况导入相应的垫片(特性代码)
  • usage 不需要在入口模块 import ,它是一个全自动检测的过程
  • false 默认值,全量打包

配置:

{
    test: /\.js$/,
    use: {
        loader: 'babel-loader',
        options: {
            presets: [
                [
                    '@babel/preset-env',
                    {
                        corejs: 2, // 版本
                        useBuiltIns: 'usage',
                    },
                ],
            ],
                },
        },
},

使用usage模式,可以取消掉 index.js中的 import '@babel/polyfill,因为它是一个全自动检测的机制。

注意:配置项的结构,presets参数对应的值是数组,给presets添加配置项也是数组,下标1是表示要给@babel/preset-env添加配置,下标2是一个对象里面包含针对@babel/preset-env的配置项。

再次打包,bundle文件的大小就减少了很多,由原来的 400多kb 变成了现在的 190多kb

捕获.PNG

此时我们的目标浏览器集合是 package.json 中配置的:

	"browserslist": [
		"last 2 versions",
		">1%"
	],

@babel/preset-env 添加 targets 设置目标浏览器集合:

presets: [
	[
	'@babel/preset-env',
	{
	corejs: 2, // 版本
	useBuiltIns: 'usage',
	targets: {
		edge: '17',
		chrome: '67',
		firefox: '60',
		},
	},
	],
],

再次打包,目标浏览器集合就变成了 targets 中的现代浏览器,打包出来的bundle文件体积只有 4kb 左右,这是因为针对于现代新版本的浏览器,我们需要的特性垫片更少,所以体积就会更小。

捕获.PNG

babel.config.json

创建 babel.config.json文件,把上述针对于 babel的配置文件抽离出来。

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "corejs": 2, // 版本
        "useBuiltIns": "usage"
        // 设置目标浏览器集合
        // targets: {
        // 	edge: '17',
        // 	chrome: '67',
        // 	firefox: '60',
        // },
      }
    ]
  ]
}

此时,webpack.config.js 关于babel的配置看起来就很简洁

{
    test: /\.js$/,
    use: {
        loader: 'babel-loader',
        },
},

preset-react

进行对 react JSX 的相关处理

安装 react 相关依赖:

npm i react react-dom -S

写一段 react 的代码:

import React from 'react'
import ReactDOM from 'react-dom/client'

const root = ReactDOM.createRoot(document.getElementById('app'))
root.render(<h1>hello JSX</h1>)

如何让项目支持这种 JSX 的写法?需要用到 @babel/preset-react 处理JSX 代码。当前版本:7.18.6

npm i @babel/preset-react -D

babel.config.json 添加相关的配置:

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "corejs": 2, // 版本
        "useBuiltIns": "usage"
      }
    ],
    // +++
    "@babel/preset-react"
  ]
}

配置完成,执行npm run build打包命令,打包完成运行代码,发现会报错 # Cannot assign to read only property ‘exports‘ of object ‘#<Object>‘

webpack.config.js 添加配置让babel-loader忽略处理node_modules目录下的文件。

			{
				test: /\.js$/,
				use: {
					loader: 'babel-loader',
				},
                                // +++
				exclude: /node_modules/,
			},

再次打包,就没有报错信息了。会发现bundle文件变大了很多,这是因为引入了react react-dom 的原因。