原文:
https://philipwalton.com/articles/deploying-es2015-code-in-production-today/
大部分前端开发人员热衷于使用新的 JavaScript 语言特性来书写 JS 代码,例如 async 、 await 、 classes 、 arrow functions 等。然而,尽管目前所有的前沿浏览器都能运行 ES2015+ 代码(译注:ES2015及俗称的ES6),自然也能够支持我刚刚列举的新特性,但是为了兼容占有小比例的低版本浏览器用户,大部分的开发者仍然使用 polyfills 将代码编译成 ES5 语法。
这种情况无疑糟透了,在理想的世界里,我们将无需输送不必要的代码!
使用新的 JavaScript 和 DOM APIs ,我们可以
有条件的加载 ployfills
,因为我们能够在运行时使用特性检测判断浏览器是否支持这些语法。但是随着一些新的 JavaScript 语法的出现,由于任何未知的语法都会导致代码解析错误,并且不再执行之后的代码,导致单凭特性检测来检查新语法的支持程度很是棘手。
尽管对于新的语法特性检测还没有一个好的解决方案,但目前对于 ES2015 的基本语法特性检测我们还是有办法的。解决之道便是
<
script
type
=
"module"
>
。
大部分开发者认为
<
script
type
=
"module"
>
是用来加载 ES 模块的(事实的确如此),但是
<
script
type
=
"module"
>
也拥有更直接且实用的功能——加载浏览器可以处理的、使用 ES2015+ 语法的 JavaScript 文件。
换句话说,每个支持
<
script
type
=
"module"
>
的浏览器都支持你所熟知的大部分 ES2015+ 语法,例如:
-
支持
<
script
type
=
"module"
>
的浏览器也支持
async
和
await
函数。
-
支持
<
script
type
=
"module"
>
的浏览器也支持
Class 类
。
-
支持
<
script
type
=
"module"
>
的浏览器也支持
arrow functions
。
-
支持
<
script
type
=
"module"
>
的浏览器也支持
fetch
、
Promises
、
Map
、
Set
等更多 ES2015+ 语法。
因此,唯一需要做的就是为不支持
<
script
type
=
"module"
>
的浏览器提供一个降级方案。幸运的是,如果你正在开发一个 ES5 版本的代码,你其实已经完成了该工作。现在你所需要做的是考虑如何生成 ES2015+ 版本的代码!
本文接下来将介绍如何实现这个方法,并讨论对 ES2015+ 代码的处理过程对我们未来如何编写模块有何影响。
实现方式
如果你已经使用了 webpack 或者 rollup 这类模块打包工具来生成 JS 文件,那么你应该继续保持。
接下来,除了当前的代码包,你还需要生成类似于第一份的另外一份代码包。(该代码包使用了 ES2015+ 语法),唯一的不同是你不需要将其编译成 ES5 语法的代码,并且不需要引入 polyfills 插件。
如果你已经开始使用
babel-preset-env
(实际上你应该使用该插件),第二个步骤将非常简单,你所需要做的事情就是使用支持
<
script
type
=
"module"
>
的浏览器,这样 babel 就会忽略没必要转化的语法。
换言之,这样操作之后就会输出 ES2015+ 语法代码,而不是 ES5 代码。
例如,假设你使用了 webpack 并且 JS 的入口文件是 ./path/to/main.js ,你当前的 ES5 版本的配置应该如下所示(注意,由于使用 ES5 语法书写,我给该代码包命名为 main-legacy )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
module
.
exports
=
{
entry
:
{
'main-legacy'
:
'./path/to/main.js'
,
},
output
:
{
filename
:
'[name].js'
,
path
:
path
.
resolve
(
__dirname
,
'public'
),
},
module
:
{
rules
:
[{
test
: /
\.
js
$
/
,
use
:
{
loader
:
'babel-loader'
,
options
:
{
presets
:
[
[
'env'
,
{
modules
:
false
,
useBuiltIns
:
true
,
targets
:
{
browsers
:
[
'> 1%'
,
'last 2 versions'
,
'Firefox ESR'
,
],
},
}],
],
},
},
}],
},
};
|
为了支持 ES2015+ 版本,你需要做的是生成第二个配置文件,该配置文件的使用环境是支持
<
script
type
=
"module"
>
的浏览器, 如下面所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
module
.
exports
=
{
entry
:
{
'main'
:
'./path/to/main.js'
,
},
output
:
{
filename
:
'[name].js'
,
path
:
path
.
resolve
(
__dirname
,
'public'
),
},
module
:
{
rules
:
[{
test
: /
\.
js
$
/
,
use
:
{
loader
:
'babel-loader'
,
options
:
{
presets
:
[
[
'env'
,
{
modules
:
false
,
useBuiltIns
:
true
,
targets
:
{
browsers
:
[
'Chrome >= 60'
,
'Safari >= 10.1'
,
'iOS >= 10.3'
,
'Firefox >= 54'
,
'Edge >= 15'
,
],
},
}],
],
},
},
}],
},
};
|
一旦运行,这两个配置文件就会输出两个 JS 文件:
接下来的步骤就是修改 HTML 代码,有条件的加载浏览器中支持 ES2015+ 的模块。你可以使用下面两个标签
<
script
type
=
"module"
>
和
<
script
nomodule
>
:
注意:这里唯一的问题是 Safari 10 并不支持 nomodule 属性,但是为了解决这一问题,你可以在使用
<
script
nomodule
>
标签前,在 HTML 中使用
内联JavaScript代码片段
(注意:这个插件已经安装在 Safari11 版本中了)。
注意事项
在大多数情况下,这种方法“仅仅是能够实现”,在实现该方法之前需要注意一些关于如何加载模块的细节:
1,模块的加载方式类似于
<