在传统项目中使用 babel 编译 ES6

南山隐士 2022年07月10日 28 0

场景
曾经吾辈以为 ES6 早已推广开来,然而事实上远比想象中更加复杂。传统后台的项目就是要兼容性,兼容 2 年前的浏览器,没有 babel,全程 jQuery 一把梭做到底。

之前的项目基本上都是前后端分离的模式,最近新公司的项目却是使用的传统的模板视图的模式。
所以,一些东西发生了变化

thymeleaf 模板里面直接有 Java 的代码,在服务端直接编译 html 代码而非是纯粹的 API 交互
使用最多的库是 jquery,主要用于操作 dom
没有现代前端工具链 nodejs/npm/webpack/babel/vuejs
所以吾辈使用 ES6 的语法就被同事诟病语法太新(还有人连 ES5 的语法都没能完全掌握),浏览器无法正常显示,所以吾辈只能尝试用 babel 来做兼容。众所周知,自 babel6 以来,模块化大行其道,由原先的使用浏览器引入脚本的方式修改为由 npm 等包管理器引入,官方也不推荐使用浏览器引入的方式。

解决
幸好吾辈找到了一个项目 babel-standalone,它提供了从浏览器中引入 babel 的功能。

使用方式很简单,只要在你含有 ES6 代码的脚本之前引入,在含有 ES6 代码的 script 标签上加上 text/babel 即可。

以上,官方是这么说的,然而实际上,吾辈还是遇到了一些问题

使用 // common.js /** * 等待指定的时间/等待指定表达式成立 * @param {Number|Function} param 等待时间/等待条件 * @returns {Promise} Promise 对象 */ function wait(param) { return new Promise((resolve) => { if (typeof param === "number") { setTimeout(resolve, param); } else if (typeof param === "function") { var timer = setInterval(() => { if (param()) { clearInterval(timer); resolve(); } }, 100); } else { resolve(); } }); } // index.js wait(3000).then(function () { document.querySelector("#root").innerHTML = "等待 3s 结束"; }); 我们会得到一个错误

Uncaught ReferenceError: wait is not defined
为什么会这样呢?原因就是加载 common.js 之后实际上还需要被 babel 编译,然而这并非同步操作,所以我们之后的脚本就无法取得全局函数 wait()。那么,如何解决呢?

我们可以将所有的 script 标签都加上 type="text/babel",所有的 script 脚本都是需要编译的,那么就不会有异步的编译的问题了。

babel 没有完全支持
例如在 common.js 中添加一个函数 indexGenerator()

/**

  • 生成一个索引序列,从 0 开始,每次递增为 1
  • @returns {Generator} 一个生成器
    /
    function
    indexGenerator() {
    for (let i = 0; true; i++) {
    yield i;
    }
    }
    但我们只会得到一个错误

Uncaught ReferenceError: regeneratorRuntime is not defined
这是因为 babel 基础包并没有实现所有的 ES6 的特性,所以就会出现不支持的情况。我们需要拓展包 babel-polyfill,在 babel-standalone 下引入即可

不能使用未声明变量
如果我们在标记为需要编译的 script 脚本中使用了未定义的变量,就会出现错误。例如在 index.js 中

username = "rxliuli";
错误消息

Uncaught (in promise) ReferenceError: username is not defined
所以说编程规范很重要啦

默认不支持 ES7
是的,babel 默认是不支持 ES7 的,而 async/await 便属于 ES7 的内容。例如我们修改 index.js

(async () => {
await wait(3000);
document.querySelector("#root").innerHTML = "等待 3s 结束";
})();
错误消息

Uncaught SyntaxError: Unexpected token function
我们可以使用 data-presets="latest" 来修复这个问题,永远引入最新版的 presets。

thymeleaf 不能使用模板字符串 HTML
同时使用 type="text/babel" data-presets="latest" 和 th:inline="javascript" 的时候,thymeleaf 将无法解析 含有 HTML 的模板字符串。

使用环境

spring-boot 2.0.3.RELEASE
babel 6.26.0
babel-polyfill 2.6.1
例如下面这种代码

甚至于注释了也没用,只能删除掉才可以

目前的解决方案是分成两个 script 标签,分别使用 type="text/babel" data-presets="latest" 和 th:inline="javascript" 标签

不能使用浏览器较新的 API
使用一些浏览器较新的 API 时发现不能正常使用,babel-core 也没有实现。例如吾辈想要使用 NodeList.forEach 遍历 a 标签列表,然后打印出来他们的链接

document.querySelectorAll("a").forEach((el) => console.log(el.href));
会得到错误

Uncaught TypeError: document.querySelectorAll(...).forEach is not a function
在旧版浏览器中,NodeList 并没有 forEach 方法,后来,吾辈找到了另一个库 core-js,其最新版 3.x beta 实现了 NodeList.forEach API,唯一的缺点是我们要手动构建才行。

引入也很简单,只要在 babel-standalone 之后,babel-polyfill 之前使用 script 标签引入就好了

好了,下面我们可以愉快的使用新的浏览器 API 了

总结
那么,有关在传统项目中使用 babel 编译 ES6/ES7 的问题就到这里了,希望有更多的人使用这些新特性,让我们早日抛弃掉 babel 吧

Last Updated: 2022/07/10 14:15:44
3.2 栈的顺序存储结构及基本操作 2.3 顺序表的操作