大多数 Web 开发人员都喜欢编写具有所有最新语言特性的 JavaScript——async/await、类、箭头函数等。然而,尽管事实上所有现代浏览器都可以运行 ES2015+ 代码并原生支持我刚才提到的特性 , 大多数开发人员仍然将他们的代码转换为 ES5 并将其与 polyfills 捆绑在一起,以适应仍在使用旧版浏览器的一小部分用户。

这有点糟糕。 在理想情况下,我们不会写不必要的代码。

使用新的 JavaScript 和 DOM API,我们可以有条件地加载 polyfill,因为我们可以在运行时检测它们的支持。 但是对于新的 JavaScript 语法,这要复杂得多,因为任何未知的语法都会导致解析错误,然后所有代码都不会运行。

虽然我们目前没有一个好的解决方案来检测新语法的特性,但我们现在有办法检测基本的 ES2015 语法支持。

解决方案是 <script type="module">

大多数开发人员认为 <script type="module"> 是加载 ES 模块的方式(当然这是真的),但是 <script type="module"> 还有一个更直接和实用的用例——加载常规 JavaScript 具有 ES2015+ 特性并且知道浏览器可以处理的文件!

换句话说,每个支持 <script type="module"> 的浏览器也支持你所知道和喜爱的大部分 ES2015+ 特性。 例如:

  • 每个支持 <script type="module"> 的浏览器也支持 async/await
  • 每个支持 <script type="module"> 的浏览器也支持类。
  • 每个支持 <script type="module"> 的浏览器也支持箭头功能。
  • 每个支持 <script type="module"> 的浏览器也支持 fetch、Promises、Map、Set 等等!

剩下要做的唯一一件事就是为不支持 <script type="module"> 的浏览器提供回退。 幸运的是,如果我们当前正在生成代码的 ES5 版本,那么我们已经完成了这项工作。 我们现在只需要生成一个 ES2015+ 版本!

本文的其余部分解释了如何实现此技术,并讨论了发布 ES2015+ 代码的能力将如何改变我们编写模块的方式。


实现

如果你现在已经在使用像 webpackrollup 这样的模块打包器来生成你的 JavaScript,你应该继续这样做。

接下来,除了我们当前的捆绑包之外,我们将生成第二个捆绑包,就像第一个捆绑包一样; 唯一的区别是你不会一直转译到 ES5,也不需要包含遗留的 polyfill。

如果我们已经在使用 babel-preset-env(应该使用),则第二步非常简单。 你所要做的就是将你的浏览器列表更改为仅支持 <script type="module"> 的浏览器,Babel 将自动不应用它不需要的转换。

换句话说,它将输出 ES2015+ 代码而不是 ES5。

例如,如果我们正在使用 webpack 并且我们的主脚本入口点是 ./path/to/main.mjs,那么我们当前的 ES5 版本的配置可能看起来像这样(注意,我将这个包称为 main.mjs)。 es5.js 因为它是 ES5):

  1. module.exports = {
  2. entry: './path/to/main.mjs',
  3. output: {
  4. filename: 'main.es5.js',
  5. path: path.resolve(__dirname, 'public'),
  6. },
  7. module: {
  8. rules: [{
  9. test: /\.m?js$/,
  10. use: {
  11. loader: 'babel-loader',
  12. options: {
  13. presets: [
  14. ['env', {
  15. modules: false,
  16. useBuiltIns: true,
  17. targets: {
  18. browsers: [
  19. '> 1%',
  20. 'last 2 versions',
  21. 'Firefox ESR',
  22. ],
  23. },
  24. }],
  25. ],
  26. },
  27. },
  28. }],
  29. },
  30. };

要制作一个现代的 ES2015+ 版本,我们所要做的就是进行第二个配置并将您的目标环境设置为仅包括支持 <script type="module"> 的浏览器。 它可能看起来像这样(注意,我在这里使用 .mjs 扩展名,因为它是一个模块):

  1. module.exports = {
  2. entry: './path/to/main.mjs',
  3. output: {
  4. filename: 'main.mjs',
  5. path: path.resolve(__dirname, 'public'),
  6. },
  7. module: {
  8. rules: [{
  9. test: /\.m?js$/,
  10. use: {
  11. loader: 'babel-loader',
  12. options: {
  13. presets: [
  14. ['env', {
  15. modules: false,
  16. useBuiltIns: true,
  17. targets: {
  18. browsers: [
  19. 'Chrome >= 60',
  20. 'Safari >= 10.1',
  21. 'iOS >= 10.3',
  22. 'Firefox >= 54',
  23. 'Edge >= 15',
  24. ],
  25. },
  26. }],
  27. ],
  28. },
  29. },
  30. }],
  31. },
  32. };

运行时,这两个配置将输出两个可用于生产的 JavaScript 文件:

  • main.mjs(语法为 ES2015+)
  • main.es5.js(语法为 ES5)

下一步是更新我们的 HTML 以在支持模块的浏览器中有条件地加载 ES2015+ 包。 我们可以使用 <script type="module"><script nomodule> 的组合来做到这一点:

  1. <!-- Browsers with ES module support load this file. -->
  2. <script type="module" src="main.mjs"></script>
  3. <!-- Older browsers load this file (and module-supporting -->
  4. <!-- browsers know *not* to load this file). -->
  5. <script nomodule src="main.es5.js"></script>

注意 :我们已经更新了本文中的示例,以对我作为模块加载的任何文件使用 .mjs 文件扩展名。 由于这种做法相对较新,如果我不指出在使用它时可能遇到的一些问题,那我们就是失职了:

  • 我们的 Web 服务器需要配置为使用 Content-Type 标头 text/javascript 提供 .mjs 文件。 如果我们的浏览器无法加载 .mjs 文件,这可能就是原因。
  • 如果我们使用 Webpack 和 babel-loader 来捆绑 JavaScript,我们可能已经复制/粘贴了一些仅转译 .js 文件的配置代码。 将配置中的正则表达式从 /\.js$/ 更改为 /\.m?js$/ 应该可以解决我们的问题。
  • 较旧的 webpack 版本不会为 .mjs 文件创建 sourcemap,但自 webpack 4.19.1 以来,此问题已得到修复。

重要注意事项

在大多数情况下,这种技术“有效”,但在实施这种策略之前,有一些关于如何加载模块的细节很重要,需要注意:

  1. 模块像 <script defer> 一样加载,这意味着它们在文档被解析之前不会被执行。 如果我们的某些代码需要在此之前运行,最好将该代码拆分出来并单独加载。
  2. 模块总是在严格模式下运行代码,因此如果出于任何原因您的任何代码需要在严格模式之外运行,则必须单独加载它。
  3. 模块对待顶级 var 和函数声明的方式与脚本不同。 例如,在脚本中 var foo = 'bar'function foo() {…} 可以从 window.foo 访问,但在模块中情况并非如此。 确保我们不依赖代码中的这种行为。

警告 ! Safari 10 不支持 nomodule 属性,但我们可以通过在使用任何 <script nomodule> 标记之前在 HTML 中内联一段 JavaScript 片段来解决这个问题。 (注意:这已在 Safari 11 中修复)。


是时候开始将我们的模块发布为 ES2015 了

目前该技术的主要问题是大多数模块作者不发布其源代码的 ES2015+ 版本,他们发布转译后的 ES5 版本。

现在可以部署 ES2015+ 代码了,是时候改变它了。

我完全理解这对不久的将来提出了许多挑战。 今天大多数构建工具都会发布文档,推荐假定所有模块都是 ES5 的配置。 这意味着如果模块作者开始将 ES2015+ 源代码发布到 npm,他们可能会破坏一些用户的构建并且通常会引起混淆。

问题是大多数使用 Babel 的开发人员将其配置为不在 node_modules 中转换任何内容,但是如果模块是使用 ES2015+ 源代码发布的,这就是一个问题。 幸运的是,修复很容易。 我们只需从构建配置中删除 node_modules 排除项:

  1. rules: [
  2. {
  3. test: /\.m?js$/,
  4. exclude: /node_modules/, // Remove this line
  5. use: {
  6. loader: 'babel-loader',
  7. options: {
  8. presets: ['env']
  9. }
  10. }
  11. }
  12. ]

不利的一面是,如果像 Babel 这样的工具除了本地依赖项之外还必须开始转译 node_modules 中的依赖项,那么构建速度会变慢。 幸运的是,这个问题可以在一定程度上通过持久的本地缓存在工具级别得到解决。

不管我们在 ES2015+ 成为新的模块发布标准的道路上可能会遇到什么坎坷,我认为这是一场值得一战的斗争。 如果我们作为模块作者,只将我们代码的 ES5 版本发布到 npm,我们就会将臃肿和缓慢的代码强加给我们的用户。

通过发布 ES2015,我们给了开发者一个选择,最终让每个人都受益。

在生产中部署 ES2015+ 代码的更多相关文章

  1. 在生产中部署ML前需要了解的事

    在生产中部署ML前需要了解的事 译自:What You Should Know before Deploying ML in Production MLOps的必要性 MLOps之所以重要,有几个原因 ...

  2. 往服务器部署thinkphp5代码时要注意 pathinfo的问题

    往服务器部署thinkphp5代码时要注意 pathinfo的问题 如果nginx没有做任何设置 要使用?s=/的方式访问地址 只需要修改3个地方就可以了,亲测成功,看代码有注解 location ~ ...

  3. 服务器用 git 进行部署出现代码冲突的处理

    服务器用 git 进行部署出现代码冲突的处理 起因: 由于项目是之前很久之前上传的,且并没上线.使用 git pull 进行代码更新时出现很多冲突. 因为服务器上的代码有移动过位置,不知道为什么就冲突 ...

  4. Nginx部署前端代码实现前后端分离

    实现前后端分离,可以让前后端独立开发.独立部署.独立单测,双方通过JSON进行数据交互. 对于前端开发人员来说,不用每次调试都需要启动或配置Java/Tomcat运行环境:对于后端开发人员来说 ,也不 ...

  5. Jenkins之自动部署、代码安全扫描、自动化接口测试

    搭建Jenkins wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins-ci.org/redhat/jenkins.reporpm --i ...

  6. 通过nginx部署前端代码实现前后端分离

    实现前后端分离,可以让前后端独立开发.独立部署.独立单测,双方通过JSON进行数据交互. 对于前端开发人员来说,不用每次调试都需要启动或配置Java/Tomcat运行环境:对于后端开发人员来说 ,也不 ...

  7. ActiveMQ部署和代码尝试(二)

    部署和代码尝试 1. 部署在linux 上的acvtiveMQ 要可以通过前台windows 的页面访问,必须把linux 的IP和 windows的 IP 地址配置到同一个网关下 .这种情况一般都是 ...

  8. 解读与部署(三):基于 Kubernetes 的微服务部署即代码

    在基于 Kubernetes 的基础设施即代码一文中,我概要地介绍了基于 Kubernetes 的 .NET Core 微服务和 CI/CD 动手实践工作坊使用的基础设施是如何使用代码描述的,以及它的 ...

  9. J2EE(java)后台调用ArcGIS Engine(AE)的部署和代码

    arcgis的BS开发解决方案一直是个坑,主推的地图服务查询速度慢,需要异步,功能少.相对来说主要用于CS的AE功能更强大全面,只是部署有点复杂 本文软件环境: win7 sp1 64位 MyEcli ...

  10. 在Windows Server 2008上部署SVN代码管理总结

    这段时间在公司开发Flex程序,所以使用TortoiseSVN作为团队代码管理器,今天在公司服务器上部署SVN服务器,并实验成功,总结如下: 服务器环境: 操作系统:Windows Server 20 ...

随机推荐

  1. 分布式存储系统之Ceph集群存储池操作

    前文我们了解了ceph的存储池.PG.CRUSH.客户端IO的简要工作过程.Ceph客户端计算PG_ID的步骤的相关话题,回顾请参考https://www.cnblogs.com/qiuhom-187 ...

  2. 我用canvas带你看一场流星雨

    前言 最近总是梦见一些小时候的故事,印象最深刻的就是夏天坐在屋顶上,看着满天的繁星,一颗,两颗,三颗...不由自主地开始了数星星的过程.不经意间,一颗流星划过夜间,虽然只是转瞬即逝,但它似乎比夜空中的 ...

  3. VS Code For Web 深入浅出 -- 进程间通信篇

    在上一篇中,我们一起分析了 VS Code 整体的代码架构,了解了 VS Code 是由前后端分离的方式开发的.且无论前端是基于 electron 还是 web,后端是本地还是云端,其调用方式并无不同 ...

  4. MasaFramework -- 缓存入门与设计

    概念 什么是缓存,在项目中,为了提高数据的读取速度,我们会对不经常变更但访问频繁的数据做缓存处理,我们常用的缓存有: 本地缓存 内存缓存:IMemoryCache 分布式缓存 Redis: Stack ...

  5. 三十一、kubernetes网络介绍

    Kubernetes 网络介绍 Service是Kubernetes的核心概念,通过创建Service,可以为一组具有相同功能的容器应用提供一个统一的入口地址,并且将请求负载分发到后端的各个容器应用上 ...

  6. ahk_more

    ;20:47 2022/5/8 #NoEnv #Warn #SingleInstance Force ;设工作目录为桌面 SetWorkingDir %A_Desktop% ;托盘提示必须放在热键前面 ...

  7. 京东云开发者|IoT运维 - 如何部署一套高可用K8S集群

    环境 准备工作 配置ansible(deploy 主机执行) # ssh-keygen # for i in 192.168.3.{21..28}; do ssh-copy-id -i ~/.ssh/ ...

  8. 抠网页标题栏logo(图标)

    1.打开自己需要抠的网页,例如百度页面 2.在这个网页链接后面+" /favicon.ico " 就可以提取ico图片 3.回车进去,右键鼠标,选择另存为图片就可以成功保存网页中的 ...

  9. zabbix-钉钉报警部署

    zabbix-钉钉报警部署 1. 流程说明 申请钉钉机器人 获取Webhook配置安全设置 获取钉钉号 使用脚本(shell/python)调用钉钉接口: python 输入收件人 信息 配置发件人 ...

  10. webapi+vue跨域session丢失解决方法

    前后端分离中在webapi设置可以跨域,在web.config文件中添加 <httpProtocol>       <customHeaders>         <ad ...