从零学脚手架(四)---babel

2021-03-16 15:26:46 浏览数 (1)

如果此篇对您有所帮助,在此求一个star。项目地址: OrcasTeam/my-cli

接下来介绍一个打包编译过程中一个极为重要的工具--babel。

ES6的枷锁

细心的朋友可以知道,在之前打包编译测试都是使用简单的ES5特性,

并没有使用过ES6(ES2015 )特性(import除外)

这是因为webpack本身不会处理代码中的ES6(ES2015 )特性,所以也就没有使用。

先来做一个测试

/src/index.js 文件使用部分ES6(ES2015 ),查看打包编译代码会发现webpack并没有处理ES6(ES2015 )特性。

自从ES6(ES2015 )时代来临后,前端才具有了飞速发展。ES6(ES2015 )各种特性也给开发人员带来了便利。

毫不客气的说,没有人再想写ES5代码了。

但是,前端代码的执行环境(浏览器)是由用户决定的,如果用户一直使用旧版本浏览器,那么新特性就无法运行在用户浏览器中。

这时候就需要一种工具:将代码使用的ES6(ES2015 )特性转换为ES5特性

这个工具就叫做:babel

?? ? webpack作为一个打包器。为babel提供了扩展支持。

?? ES6是ES2015 所有版本统称 有的文章会写成ES7、ES8。但其实都是ES6。

? 上面代码使用到了ES6的 Promise类型块级声明(const)箭头函数for-of语法数组APIawait属性,不了解ES6的朋友可以学习阮一峰老师的ES6入门教程

babel

babel介绍

ES6来临后,前端开启了百花绽放的时代。从而也导致了ES6转ES5的工作并不仅仅局限于JS语言的原始特性。

例如:Typescript、JSX语法等等。

这些都可以使用babel进行处理。

babel的设计思想也与webpack一致:提供核心引擎 插件化的模式管理

babel提供了一个核心引擎库:@babel/core 和 扩展插件库配置。

@babel/cli

babel 其实并不是webpack一个扩展插件,它是一个独立的工具。可以进行单独配置、运行。

babel提供了一个@babel/cli库,与webpack-cli库一样,允许命令行直接运行babel

代码语言:javascript复制
{
  "scripts": {
	"build": "babel src -d lib"
  }
}

在此就不介绍@babel/cli这一块的内容了,有兴趣的朋友可以去官网学习

??? babel作为一个独立工具,理论可以配置在所有打包器中。

babel-loader

babel作为一个独立的工具,那么肯定不能直接配置在webpack中。

那么想要babel执行在webpack,就必须提供一个适配器,来桥接两个库。

而这个适配器就是babel-loader。

babel-loader在webpack执行时拦截需要转换文件,将文件先交给babel进行转换,然后再传回webpack执行接下来的操作。

而babel-loader只是调用了@babel/core库中的API。最后执行的还是@babel/core引擎

下面先安装babel-loader和@babel/core

yarn add -D babel-loader@8.2.2 @babel/core@7.13.1

然后在webpack.config.js中配置所有的js文件都使用babel-loader进行转换。

代码语言:javascript复制
{
   module:{
    rules:[
      {
        //  所有的.js文件都走babel-loader
        test:/.js$/,
        include: path.join(config.root,'src'),
        loader: "babel-loader",
      }
    ]
  },
}

? babel@6.X版本时,核心引擎库名为babel-core。从babel@7.X版本之后,官方对库名称做了统一的修改,官方提供的包都以@babel/冠名,所以babel-core和@babel/core实际上是一个库 。有兴趣朋友可以在NPM中对比下两个包的版本 :@babel/core、babel-core

?后面会陆续加入其它文件执行babel-loader。例如:.ts、.jsx

但是目前依然无法转换ES6(ES2015 )代码。因为只添加了引擎(@babel/core),并没有添加具体转换库。

@babel/preset-env

先来介绍一下@babel/preset-env库,来完成部分转换功能。

@babel/preset-env是babel 预设的一个plugin

yarn add -D @babel/preset-env@7.13.5

在配置loader时,可以设置当前loader使用的属性和依赖库。babel-loader具有一个presets属性来依赖的预设插件(preset)

代码语言:javascript复制
{
    module:{
        rules:[
            {
                //  所有的.js文件都走babel-loader
                test:/.js$/,
                include: path.join(config.root,'src'),
                loader: "babel-loader",
                options: {
                    presets:[
                        "@babel/preset-env",
                    ]
                }
            }
        ]
    }  
}

?? presets的执行是从后往前执行的,官方说为了确保向后兼容

? presets配置可以设置短名称,

  1. preset库名称以 babel-preset- 前缀,可以省去前缀。 例如:babel-preset-my-custom,可以直接设置为:custom
  2. 短名称也适用于冠名,例如:@org/preset-env,可以设置为:@org/env

此时执行yarn build操作后生成的代码就会处理部分ES6(ES2015 )

生成代码中可以看到:awaitfor-ofconst 这些ES6代码被转换了。

? 代码中的那堆 case 语句,是await ES5的写法。await 本质只是一个 将异步同步化的状态机。不熟悉 await 机制的朋友可以忽略,只需知道代码为await语法ES5写法即可。

但细心的朋友可以发现,并不是所有的ES6特性被转换了。

还有部分ES6特性并没有被转换(promiseincludesfilter),并且代码被一个箭头函数包裹着。

代码被箭头函数包裹这个问题稍后在解决。

先来了解下为什么有的ES6特性没有被转换。

? @babel/preset-env取代了preset-es20系列的预设插件(preset)

目前生成代码还无法在浏览器运行,缺少regeneratorRuntime,这个稍后再说

Syntax和API

思考一个问题:刚才被转换的ES6特性与未被转换的ES6特性有何不同。

答案是被转换的ES6特性是Syntax(语法),而未被转换的则是:API(类型、函数)

babel处理ES6特性时将Syntax(语法)API(类型、函数)进行了分开处理。

为什么要这样做呢?

原因是两者本质的不同:Syntax(语法)一个语言本身客观存在的事实,而API(类型、函数),则只是对一系列操作的封装

当执行环境不支持Syntax(语法)时,那么就只能使用其它Syntax(语法)进行替换。

而执行环境中不存在API(类型、函数)时,可以编写自定义API(类型、函数)进行替换。

? JSSyntax(语法)错误提示是:Uncaught SyntaxError;API(类型、函数)错误提示是:Uncaught ReferenceError。

@babel/preset-env只是babel提供处理Syntax(语法)的预设插件(preset)

至于API(类型、函数)的处理,则是由其它插件处理,这个插件俗称:垫片、腻子

babel配置形式

在处理API(类型、函数)之前,先介绍下babel配置文件。

刚才在配置@babel/preset-env时,直接配置在了babel-loader中presets属性。

除了babel-loader,babel还支持其它方式配置

package.json

@babel/core支持在package.json文件设置

package.json文件babel属性设置babel 插件

@babel/core执行时会尝试读取此属性。

代码语言:javascript复制
 "babel": {
   "presets": [
     "@babel/preset-env"
   ],
   "plugins": [
   ]
 }
配置文件

babel支持使用配置文件设置。

这种方式与webpack.config.js文件一样,使用.约定文件名称设置。@babel/core执行时会尝试读取.约定文件

约定文件名称 可以为 babel.config.js.babelrc.json 。 较为常用的是 .babelrc.json 。不过一般都会省略后缀, 名称叫做 .babelrc

package.json形式和配置文件形式 只能选择一种形式设置。如果同时存在会直接报错。

babel-loader配置方式优先级高于其他两种方式

参数设置

在使用plugin/preset时,可以设置属性。

不过参数形式有些奇葩。

plugin/preset与参数存在于一个数组内,第一个为plugin/preset,第二个为属性对象

代码语言:javascript复制
{
  "presets": [
     ["@babel/preset-env", {
          "targets": "defaults"
     }]
  ],
  "plugins": [
  ]
}

??? 以下会使用配置文件方式,所以一定要把babel-loader中的设置删除掉。否则会因为优先级问题而失效。:我就因为这个疏漏曾经被耽误了一天

转换API(类型、函数)
设置低版本浏览器

在转换API(类型、函数)时要进行测试。

而开发人员基本上使用的都是新版浏览器,所以需要具有一个不支持ES6API(类型、函数)的浏览器。

一般ES6的新特性,都已经不再支持IE浏览器了。所以IE浏览器是一个天然的测试对象。

例如ES6Promise类型,就不再支持IE浏览器

win 10系统携带的IE浏览器版本一般都为IE11。IE浏览器支持对版本进行修改IE浏览器

F12-开发者模式--仿真--文档模式 可以修改IE浏览器版本,在这里使用的版本为IE9

处理箭头函数包裹

在刚才打包编译时,发现生成的代码使用了一个箭头函数包裹。

这个箭头函数函数怀疑是打包时webpack搞得鬼,具体原因没排查,在这里只介绍下处理方案。

package.json文件中添加browserslist属性,设置打包代码支持IE9浏览器。

代码语言:javascript复制
"browserslist": [
 "ie 9"
]

? browserslist属性是browserslist库提供的一个属性,browserslist是提供浏览器版本支持的库。多个库中都依赖了browserslist。 browserslist库详情在下一篇介绍。

此时使用yarn build执行打包编译,生成代码就不再由箭头函数包裹

regenerator-runtime和core-js
regenerator-runtime

介绍下关于之前打包代码缺少 regeneratorRuntime() 问题。

regeneratorRuntime() 是由regenerator-runtime库提供的,

regenerator-runtime库是一个转换ES6中 generator函数await函数 功能的库。babel直接使用此库处理两种函数。

core-js

很多文章介绍时regenerator-runtime都与core-js一起介绍。所以在此也将这两个库放在一起介绍。

处理ES6 API(类型、函数)的解决方案在上面介绍过。

当执行环境中不存在API(类型、函数)时,可以使用自定义API(类型、函数)进行替代。

而core-js库就是一个自定义的API(类型、函数)库。也就是俗称的腻子

core-js是 个人开源项目,并不属于任何公司。

babel直接使用了core-js进行处理API(类型、函数)

core-js截至到编写文章时的最新版本为@3.9.0

core-js的@3.X与@2.X两个大版本间具有巨大的差异性,以至于影响到了babel。不过目前基本都是使用core-js@3.X版本。

? core-js开发者目前在开发core-js@4.X版本。可能到时候配置又会具有大变化。

@babel/polyfill

关于babel的文章中,有很多都会介绍@babel/polyfill。

@babel/polyfill库其实就是babel对core-js和regenerator-runtime的封装库。

不过在babel官网,这个库已经被弃用了。babel@7.4.0版本之后就建议直接使用core-js和regenerator-runtime

上面那段话的大致意思为:@babel@7.4.0开始,@babel/polyfill会被弃用,直接使用core-js和regenerator-runtime。

下面那段话的大致意思为:babel具有一个polyfill包含了core-js和regenerator-runtime。

??? 关于@babel/polyfill库被弃用的原因好像是因为:core-js@3.X版本和core-js@2.X版本的巨大差异 导致@babel/polyfill无法过渡适配

core-js、regenerator-runtime使用

yarn add regenerator-runtime@0.13.7 core-js@3.9.0 // 安装在dependencies

直接使用core-js和regenerator-runtime需要在代码中手动引用。babel当然也支持配置,慢慢来

index.js文件引用。

??

  1. 导入core-js库时,导入方式为:"core-js/stable",是为了只导入稳定版本特性, 关于stage请参考:[ECMAScript] TC39 process
  2. 导入regenerator-runtime时,导入方式为:regenerator-runtime/runtime,为了节省文件大小

此时执行yarn build打包 编译生成代码中会看到好多引用代码。这些都是core-js处理ES6 API(类型、函数)的垫片

例如promise类型,就可以在编译生成后的代码中找到core-js自定义的实现方式。

这时候使用IE9运行代码可以运行成功,也就是说ES6 API(类型、函数)被成功替代了。

@babel/preset-env 属性设置
按需加载

刚才加入core-js和regenerator-runtime后打包运行,可以知道ES6 API(类型、函数)被成功替代了。

但其实这里还具有一个非常严重的问题,那就是文件大小。

可以看到打包生成的文件现在高达428K。虽然打包代码压缩,但也不应该这个大小

在代码中仅写了两个函数。那么原因大概是引入core-js和regenerator-runtime导致。

core-js是ES6 API(类型、函数)的垫片。

core-js本身并不知道你使用哪些ES6 API(类型、函数),而babel默认情况会将所有的垫片引入,

也就造成了这个恐怖的文件大小

前端对于文件大小非常敏感,文件大小直接影响到网站的加载速度。所以必须要做到按需加载垫片 (仅加载需要使用的垫片)

不同项目对浏览器支持版本需求不一样。

babel处理ES6 API(类型、函数)垫片时的按需加载垫片具有三种含义

  1. 按照浏览器版本加载垫片
  2. 按照代码中使用加载垫片
  3. 按照浏览器版本 代码中使用加载垫片

?浏览器支持版本需求 取决于项目的使用用户,例如有的项目只是公司管理项目,无须兼容老版本浏览器

babel中@babel/preset-env提供了两种按需加载配置方案:按照浏览器版本加载(1)按照浏览器版本 代码中使用加载(3)

@babel/preset-env 属性配置

设置浏览器版本(browserslist、targets)

按需加载垫片中有一个浏览器版本加载的含义,想要实现浏览器版本加载那就必须设置浏览器版本,

babel提供了两种设置浏览器版本的方案:

browserslist

browserslist方案在刚才处理函数包裹代码时使用到了,设置在package.json中的browserslist属性

代码语言:javascript复制
"browserslist": [
    "ie 9"
]

browserslist是一个提供浏览器版本的一个库,提供了多种配置规则,好多库都使用到了browserslist,例如:babel。

browserslist属性是Array,允许设置多个浏览器版本。例如ie 9,便是支持IE9浏览器。

还可以设置范围版本,例如大于Chrome75版本。

代码语言:javascript复制
"browserslist": [
    "Chrome > 75"
]

在这里只使用这两种规则测试,browserslist会在下一篇介绍

targets

targets属性是babel自身提供浏览器版本设置,配置在@babel/preset-env属性中

targets属性类型为 StringObject;支持browserslist格式规则。

targets属性的优先级高于browserslist

代码语言:javascript复制
{
    "presets": [
        ["@babel/preset-env",{
            "targets": "chrome > 75",
        }]
    ],
    "plugins": [
    ]
}
代码语言:javascript复制
{
    "presets": [
        ["@babel/preset-env",{
            "targets": {
                "chrome": "58",
                "ie": "11"
            }]
            ],
        "plugins": [
    ]
}

推荐使用browserslist设置,也就是package.jsonbrowserslist属性。

因为browserslist库已经被社区高度认可。好多库都依赖了browserslist,使用browserslist库可以做到:配置统一管理,利于项目维护

?:?? 浏览器版本设置也会影响Syntax(语法)的转换。 指定的浏览器版本支持的Syntax(语法)不会被转换ES5

corejs

在介绍按需加载垫片之前再说一个@babel/preset-env属性:corejs

corejs属性是babel@7.4.0时加入的,用于设置加载core-js版本。

corejs设置的类型为: StringObject

代码语言:javascript复制
{
    "presets": [
        ["@babel/preset-env",{
            "corejs": {
                "version": "3.9",
                "proposals":true
            }
        }]
    ],
    "plugins": [
    ]
}
  • version:设置加载的core-js版本。 此属性可以设置任何受支持的core-js 参数类型为 String 默认值为:2.0
  • proposals:是否加载core-js支持的 提议API 参数类型为:Boolean 默认值为:false

?? corejs属性只有在启用按需加载垫片(useBuiltIns设置为entryusage才有效。

useBuiltIns

按需加载垫片是由@babel/preset-env库提供的useBuiltIns属性设置。

useBuiltIns属性可以设置三个属性值:

false

不启用按需加载垫片功能,全部加载core-js垫片。此值为默认值。

entry

按照浏览器版本加载垫片。

代码语言:javascript复制
{
  "presets": [
    ["@babel/preset-env",{
      "useBuiltIns": "entry",
      "corejs": {
        "version": "3.9",
        "proposals":true
      }

    }]
  ],
"plugins": [
  ]
}

browserslist属性为 Chrome > 75 时 打包出来的文件大小就会小很多

代码语言:javascript复制
"browserslist": [
    "Chrome > 75"
]

可以看到,此时文件大小与刚才是天壤之别。因为浏览器设置的为 Chrome > 75 ,几乎支持全部新特性

可以看到打包生成代码中没有提供filter垫片,并且 await 语法都没有转换。这些特性在新版Chrome都提供了。

如果将browserslist属性设置为 ie 9

那么文件大小依然会很大。因为ES6 新特性都不支持IE 9

代码语言:javascript复制
"browserslist": [
    "ie 9"
]

usage

刚才使用entry属性值实现了按照浏览器版本加载垫片的功能。

不过并不能算是我们需要的真正按需加载垫片。

useBuiltIns属性的usage值提供了理论上真正的按需加载浏览器版本 代码中使用

代码语言:javascript复制
{
  "presets": [
    	["@babel/preset-env",{
      "useBuiltIns": "usage",
      "corejs": {
        "version": "3.9",
        "proposals":true
      }
 	}]
  ],
  "plugins": [
  ]
}

在使用usage属性值时,就不需要手动引用core-js和regenerator-runtime库了

babel会自动加载。

此时哪怕设置ie 9。打包文件大小也不会像entry时那么大了。

代码语言:javascript复制
"browserslist": [
    "ie 9"
]

而在Chrome > 75的情况下,代码都不需要进行处理了

代码语言:javascript复制
"browserslist": [
    "Chrome > 75"
]

entry、usage有话说

  1. babel在处理entry属性值时,直接将按需加载处理逻辑做到了入口。而在处理usage时,则在用到时进行了单独引用,并且保证每一个API(类型、函数)只引用一次
  2. 在两者选择使用时,不要一味的追求usage,因为usage使用起来更为棘手

modules

@babel/preset-env配置项中有一个modules

modules属性表示是否将ES modules转换为指定模块类型处理。

modules属性值具有:amdsystemjsumdcommonjscjsautofalse

默认属性值为auto:默认情况下,使用ES modules来进行处理,但是会受到其它plugin的modules属性影响。

推荐使用ES modules,将属性值设置为false

因为ES6 modules 可以进行tree-shaking优化

代码语言:javascript复制
{
  "presets": [
    [
      "@babel/preset-env",
      {
        "modules":false
      }
    ]
  ]
}

@babel/preset-env还有一些别的属性,在此就不赘述。有兴趣的朋友可以查询官网。

@babel/plugin-transform-runtime

babel处理ES6特性时,还提供了一个解决全局污染的垫片库:@babel/plugin-transform-runtime

@babel/plugin-transform-runtime也是一个经常被使用到的库。

在日常开发中都应该遵守的一个原则:避免全局污染

全局污染是一件极为可怕的问题。在协同、代码运行时会出现不可预知的问题。

@babel/plugin-transform-runtime库就是将代码使用到的ES6 API(类型、函数)名称转换为自定义名称,从而避免污染运行环境自身API

? @babel/plugin-transform-runtime与usage属性值一样:按照浏览器版本 代码中使用加载垫片

开发第三方库,强烈建议使用@babel/plugin-transform-runtime

@babel/runtime-corejs3

@babel/plugin-transform-runtime库依赖了一个@babel/runtime-corejs3或@babel/runtime-corejs2库。

??? @babel/runtime-corejs3对应的core-js@3.X @babel/runtime-corejs2对应的core-js@2.X

@babel/runtime-corejs3是babel提供的core-js封装库,内部做了一些处理,具体可以参考这篇文章。不过此文章是基于@babel/runtime-corejs2版本,与@babel/runtime-corejs3具有一定差异。

yarn add -D @babel/plugin-transform-runtime@7.13.7 @babel/runtime-corejs3@7.13.7

?? 使用@babel/plugin-transform-runtime时,就不需要安装core-js和regenerator-runtime ,@babel/runtime-corejs3中会依赖这两个库

.babelrc文件中使用@babel/plugin-transform-runtime配置替代@babel/preset-env中配置。

不过注意的是@babel/plugin-transform-runtime属性中corejs.version不再是字符串,而是23。 因为加载的是@babel/runtime-corejs[3/2]

代码语言:javascript复制
{
  "presets": [
    [
      "@babel/preset-env",
      {
        //  移除useBuiltIns设置
        //      "targets": "chrome > 75",
        //      "useBuiltIns": "usage",
        //      "corejs": {
        //        "version": "3.9",
        //        "proposals":true
        //      }
      }
    ]
  ],
  "plugins": [
    [
      "@babel/plugin-transform-runtime",
      {
        "corejs": {
          "version": 3,
          "proposals": true
        }
      }
    ]
  ]
}

配置完毕后,不再需要任何引用就可以进行打包生成。

代码语言:javascript复制
"browserslist": [
    "ie 9"
]

在IE9环境yarn build

可以看到使用的ES6-API已经被转换为另外的API了,所以并不会再污染全局代码。至于打包的大小,并没有多大

至于在Chrome > 75的打包结果,有兴趣的朋友可以自行测试。

preset和plugin

在使用babel库时,发现有两种类型:

  1. preset:@babel/preset-env
  2. plugin:@babel/plugin-transform-runtime

配置时也是不同属性:

代码语言:javascript复制
{
  "presets": [
    
  ],
  "plugins": [
  ]
}

preset的中文翻译为:预置。其实也就是babel提供的预置插件库,其本质也都是plugin

总结

???

  • babel来用来处理ES6特性的库
  • babel也是核心引擎 插件化的设计模式
  • babel-loader是babel的适配器,将babel提供webpack使用
  • babel使用不同的插件分别处理Syntax(语法)API(类型、函数)
  • babel提供不少的预设插件,配置在presets属性中。
  • @babel/preset-env中useBuiltIns属性用来设置按需加载垫片
  • @babel/plugin-transform-runtime提供了一种不污染全局情况下使用垫片方式。

本文参考

  • @babel/preset-env 与@babel/plugin-transform-runtime 使用及场景区别
  • babel corejs@3 是如何按需polyfill原型对象方法的
  • @babel/plugin-transform-runtime 到底是什么?
  • Babel7 转码(四)- polyfill 还是 transform-runtime
  • Polyfill 方案的过去、现在和未来 #80
  • 2020 如何优雅的兼容 IE

本文依赖

  • babel-loader@8.2.2
  • @babel/core@7.13.1
  • @babel/preset-env@7.13.5
  • regenerator-runtime@0.13.7
  • core-js@3.9.0
  • @babel/plugin-transform-runtime@7.13.7
  • @babel/runtime-corejs3@7.13.7

package.json

代码语言:javascript复制
{
  "name": "my-cli",
  "version": "1.0.0",
  "main": "index.js",
  "author": "mowenjinzhao<yanzhangshuai@126.com>",
  "license": "MIT",
  "devDependencies": {
    "@babel/core": "7.13.1",
    "@babel/plugin-transform-runtime": "7.13.7",
    "@babel/preset-env": "7.13.5",
    "@babel/runtime-corejs3": "7.13.7",
    "babel-loader": "8.2.2",
    "clean-webpack-plugin": "3.0.0",
    "html-webpack-plugin": "5.2.0",
    "webpack": "5.24.0",
    "webpack-cli": "4.5.0"
  },
  "dependencies": {
    "jquery": "3.5.1",
  },
  "scripts": {
    "start": "webpack --mode=development  --config webpack.config.js",
    "build": "webpack --mode=production  --config webpack.config.js"
  },
  
  "browserslist": [
    "ie 9",
    "Chrome > 75"
    ]
}

webpack.config.js

代码语言:javascript复制
const path = require('path')
const webpack = require("webpack");
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const TerserPlugin = require('terser-webpack-plugin')


const config = {
  root: path.join(__dirname, './'),
}

const modules = {

  //  入口文件
  //  字符串形式
  entry: path.join(config.root, 'src/index.js'),
  //  对象形式
  // entry:{
  //   'index':  path.join(config.root, 'src/index.js'),
  // },

  //  输出文件
  //  字符串形式
  // output:path.join(config.root, './dist/[name].js')
  //对象形式
  output: {
    //  输出文件的目录地址
    path: path.join(config.root, 'dist'),
    //  输出文件名称,contenthash代表一种缓存,只有文件更改才会更新hash值,重新打包
    filename: '[name]_[contenthash].js'
  },

  //devtool:false, //'eval'

  module:{
    rules:[
      {
        //  所有的.js文件都走babel-loader
        test:/.js$/,
        include: path.join(config.root,'src'),
        loader: "babel-loader"
      }
    ]
  },


  optimization: {
    minimize: false,
    minimizer: [
    new TerserPlugin({
          //  指定压缩的文件
          include: /.js(?.*)?$/i,

          // 排除压缩的文件
          // exclude:/.js(?.*)?$/i,

          //  是否启用多线程运行,默认为true,开启,默认并发数量为os.cpus()-1
          //  可以设置为false(不使用多线程)或者数值(并发数量)
          parallel: true,

          //  可以设置一个function,使用其它压缩插件覆盖默认的压缩插件,默认为undefined,
          minify: undefined,

          //  是否将代码注释提取到一个单独的文件。
          //  属性值:Boolean | String | RegExp | Function<(node, comment) -> Boolean|Object> | Object
          //  默认为true, 只提取/^**!|@preserve|@license|@cc_on/i注释
          //  感觉没什么特殊情况直接设置为false即可
          extractComments: false,

          // 压缩时的选项设置
          terserOptions: {
            //  是否保留原始函数名称,true代表保留,false即保留
            //  此属性对使用Function.prototype.name
            //  默认为false
            keep_fnames: false,

            // 是否保留原始类名称
            keep_classnames: false,

            //  format和output是同一个属性值,,名称不一致,output不建议使用了,被放弃
            // 指定压缩格式。例如是否保留*注释*,是否始终为*if*、*for*等设置大括号。
            format: {
              comments: false,
            },
            output: undefined,

            //  是否支持IE8,默认不支持
            ie8: false,

            compress: {
              // 是否使用默认配置项,这个属性当只启用指定某些选项时可以设置为false
              defaults: false,

              // 是否移除无法访问的代码
              dead_code: false,

              // 是否优化只使用一次的变量
              collapse_vars: true,

              warnings: true,

              //  是否删除所有 console.*语句,默认为false,这个可以在线上设置为true
              drop_console: false,

              //  是否删除所有debugger语句,默认为true
              drop_debugger: true,

              //  移除指定func,这个属性假定函数没有任何副作用,可以使用此属性移除所有指定func
              // pure_funcs: ['console.log'], //移除console
            },
          },
    	})
    ]
  },

  plugins: [
    new HtmlWebpackPlugin({
       //  HTML的标题,
        //  template的title优先级大于当前数据
        title: 'my-cli',

        //  输出的html文件名称
        filename: 'index.html',

        //  本地HTML模板文件地址
        template: path.join(config.root, 'src/index.html'),

        // 引用JS文件的目录路径
        publicPath: './',

        //  引用JS文件的位置
        //  true或者body将打包后的js脚本放入body元素下,head则将脚本放到中
        //  默认为true
        inject: 'body',

        //  加载js方式,值为defer/blocking
        //  默认为blocking, 如果设置了defer,则在js引用标签上加上此属性,进行异步加载
        scriptLoading: 'blocking',

        //  是否进行缓存,默认为true,在开发环境可以设置成false
        cache: false,

        //  添加mate属性
        meta: {}
    }),

    new CleanWebpackPlugin({
 		// 是否假装删除文件
        //  如果为false则代表真实删除,如果为true,则代表不删除
        dry: false,

        //  是否将删除日志打印到控制台 默认为false
        verbose: true,

        //  允许保留本次打包的文件
        //  true为允许,false为不允许,保留本次打包结果,也就是会删除本次打包的文件
        //  默认为true
        protectWebpackAssets: true,

        //  每次打包之前删除匹配的文件
        cleanOnceBeforeBuildPatterns: ['**/*'],

        //  每次打包之后删除匹配的文件
        cleanAfterEveryBuildPatterns:["*.js"],
    }),


    new webpack.DefinePlugin({ "global_a": JSON.stringify("我是一个打包配置的全局变量") }),
  ],

  resolve: {
    alias:{
      //  设置路径别名
      '@': path.join(config.root, 'src') ,

      '~':  path.join(config.root, './src/assets') ,
    },
    //  可互忽略的后缀
    extensions:['.js', '.json'],
    //  默认读取的文件名
    mainFiles:['index', 'main'],
  }
}

//  使用node.js的导出,将配置进行导出
module.exports = modules

.babelrc

代码语言:javascript复制
{
  "presets": [
    [
      "@babel/preset-env",
      {
        "modules":false
        //  移除useBuiltIns设置
        //      "targets": "chrome > 75",
        //      "useBuiltIns": "usage",
        //      "corejs": {
        //        "version": 3,
        //        "proposals":true
        //      }
      }
    ]
  ],
  "plugins": [
    [
      "@babel/plugin-transform-runtime",
      {
        "corejs": {
          "version": 3,
          "proposals": true
        }
      }
    ]
  ]
}

0 人点赞