其它解决方法,参考 https://www.snowpack.dev/#node.js-polyfills

@antv/l7-core 库依赖了 merge-json-schemas 库,merge-json-schemas 中使用了 node 内置库 assert,导致编译失败。

代码位置: https://github.com/goodeggs/merge-json-schemas/blob/445e937377adb6a04f4e53bcc2059448af642089/src/index.js#L9

第二个错误,运行时错误

编译通过后,打开浏览器

在浏览器全局定义一个高阶方法 require(注意此时全局当中不应当存在名为 require 的方法,如果存在则需要自行兼容)

function require(libName) {
    if (libName == "load-styles") {
        // https://www.npmjs.com/package/load-styles/v/2.0.0
        return function loadStyles(css, doc) {
            // default to the global `document` object
            if (!doc) doc = document;
            var head = doc.head || doc.getElementsByTagName("head")[0];
            // no <head> node? create one...
            if (!head) {
                head = doc.createElement("head");
                var body = doc.body || doc.getElementsByTagName("body")[0];
                if (body) {
                    body.parentNode.insertBefore(head, body);
                } else {
                    doc.documentElement.appendChild(head);
            var style = doc.createElement("style");
            style.type = "text/css";
            if (style.styleSheet) {
                // IE
                style.styleSheet.cssText = css;
            } else {
                // the world
                style.appendChild(doc.createTextNode(css));
            head.appendChild(style);
            return style;
    throw Error(
        `requrie ${libName} failed, require syntax is not supported`

以下是问题的定位及解决方法的思考过程

通过出现问题的上下文,逐步回溯寻找问题原因。

  • 错误代码位置在哪?
    从浏览器的错误代码上下文推测,出错代码在L7的库中,似乎想要通过 commonjs 语法来引入一个 load-styles 库,特征字符串是 require('load-styles')。然后到项目的 node_modules/@antv/ 目录下搜索特征字符串,发现出现多处。且都是为了引入 require('load-styles') 。

  • require('load-styles') 代码为什么会报错?
    Snowpack在编译项目时,使用了 ES Module 方式管理模块,而L7的发布代码在ES Module模块中使用了 require 语法!?这是不正确的

  • require('load-styles') 是做什么用的?
    因为node_modules目录下并非源码,不容易查看,所以到 github 上搜索 L7 源码: https://github.com/antvis/L7(L7的文档尚不完善,没有直接跳转连接)。找到后,看到先浏览 package.json 配置,发现存在 load-styles@2.0.0 库。于是先去查看 load-styles 库的功能,github上未找到该库,但是在npm上找到,意外的是该库为6年前发布的。下载后查看源码,代码很少,功能就是把一个字符创建为CSS标签并插入网页中。L7中的require('load-styles') 就是为了把一段CSS文本内容作为style标签插入网页。

  • 到这里基本确定了问题的原因:L7库中使用一个6年前的方式来将一段CSS文本变成一个style标签,然后在生成ES Module模块时,load-styles代码被错误的转义成 commonjs 语法。Snowpack在编译时不会对 ES Module 做处理,因为它认为 ES Module 已经是目标格式,不需要编译,因此也忽略了 ES Module 中错误的 require 语法,所以代码被发送给浏览器,在浏览器执行时才报出运行时错误。

    原因到这里已经可以解释我遇到的问题,那么,更深层次的原因是什么?为什么会L7会产生 require('load-styles') 这样的语法,其它库则不会?

  • 目标代码是怎么包含require('load-styles') 的?
    以 @antv/l7-component 库为例,通过 import "xxx.css" 的方式引入样式文件 查看代码
    babel-plugin-transform-import-css/helpers.js

    babel-plugin-transform-import-css 采用 babel 模板替换的方法,将 import 的 CSS 文件内容附加在 require('load-styles')(/* CSS */) 模板中。因此最终的ES Module编译结果中出现了 require 语句,导致错误。

  • 思路一:修改打包、编译的配置,兼容ES Module中包含 require 的语法
  • 不可行 。当前项目打包、编译依赖3个配置文件:babel.config.js、tsconfig.js、snowpack.config.js,尝试修改后都不能解决问题。原因是因为,无论哪个项目配置型都是针对项目中自己编写的代码的,不会处理 node_modules 中的文件,node_modules 中的文件被认为编译好的 ES Module,Snowpack 直接复制过来使用,不做处理。所以修改配置项都是不起作用的。

  • 思路二:对 node_modules 中的文件进行预处理
  • 不可行。Snowpack的优势就是默认采用 ES Module。如果对 node_modules 中的模块逐个编译就无法使用 Snowpack。虽然Snowpack中可以配置 Webpack 插件,但那时在 production模式下做代码合并优化时使用的。

  • 思路三:采用 Webpack 编译
  • 可行,但不想,不想退回 Webpack是因为技术框架选型的惯性,一旦采用了哪种方案,就不会轻易变动。

  • 思路四:修改 @antv/l7 代码中的编译错误
  • 不可行。首先 @antv/l7 是有组织的官方库,修正其代码就意味着要提交pull request,然后是漫长的等待,正在开发中的项目不能承受这种等待,提交PR然后等待修复的时间,不如想其它方案。

  • 思路五:自己fork @antv/l7 代码库修改问题
  • 成本高。@antv/l7 的代码库下 有多个package,而且相互依赖,有多个地方使用了 load-styles ,因此fork并维护多个package成本过高。

  • 思路六:hotfix,使用某种补丁方法规避掉这个错误
  • 值得一试。常用的比如 try catch、monkey patch、proxy 等等 magic ways ,可以解决一些不好处理的问题,只不过这种解决方式本身就是一种问题。所以最终打算使用这种方式来解决运行时报错问题。

    运行时错误有多处,但都是同一种错误,require('load-styles') 未定义导致。L7 使用这个方法只有一个目的,就是把一段 CSS 文本变成一个 style 标签插入 html 中。因此,在L7代码运行前实现一个全局方法,能够将 require('load-styles')('/* 这是一段CSS 文本*/') 正确的运行并生成style标签即可。

    function require(){
         return function(css) {
            var style = document.createElement("style");
            style.appendChild(document.createTextNode(css));
            document.head.appendChild(style)
    

    重新编译运行,页面可以正常显示。

    AntV/L7地图

    通过上述方法即可解决 Snowpack 的打包项目中引用 @antv/l7 项目的问题。虽然可以正常使用,但代码还可以做些优化,首先是创建style标签的功能,既然原本使用的是 load-styles 项目,这里就直接把 load-styles 源码直接复制过来,这样与原本的代码就完全一致了;其次是错误提醒,全局 require 方法是为了解决 require('load-style') 问题的,因此出现其他引用库时要抛出异常提示。最终的代码版本,参看上开始的“解决办法”内容。

    (虽然最终解决,但问题根源是 @antv/l7 代码的编译问题。而且这个破事,花了我5个小时)

  • Snowpack: https://www.snowpack.dev/
  • AntV/L7: https://l7.antv.vision/zh
  • Babel: https://babeljs.io/docs/en/babel-preset-env#targetsesmodules
  •