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