前言
目前在开发一个生成骨架屏的 webpack
插件,使用 Puppeteer
来将处理脚本通过 addScriptTag
方法来插入到页面中。
想法是想模仿 webpack
可以通过配置 plugins
来添加自定义的处理规则,问题就出在这里,通过 page.evaluate
来执行页面脚本的时候获取不到我传入的 plugins
。
问题
webpack plugin config
:
class TestPlugin {
apply(api, ele, options) {
console.log(api, ele, options);
}
}
{
...
port: 8899,
plugins: [
function test(api, ele, options) {
console.log(api, ele, options);
},
new TestPlugin()
],
...
}
使用 puppeteer
的代码:
await page.evaluate(options => {
Skeleton.genSkeleton(options);
}, this.options);
在调用 Skeleton.genSkeleton
方法的时候, options.plugins
变成了 [null, {}]
。导致获取不到传递的自定义处理函数。
排查问题
首先确认 webpack
是否接受到参数
由此可以发现 wepack
已经接受到了配置的参数
那就只能是 Puppeteer
的问题了
查看了 puppetter
的官方文档。
evaluate
方法接受的参数必须是可以被序列化的。
然后去 github
上查看它的源码发现它使用 JSON.stringify
来进行序列化。
JSON.stringify
的问题了。
进一步分析问题
既然确定是 JSON.stringify
的问题了, 赶紧去看一波 JSON.stringify
的文档。
送上 JSON.stringify() 的MDN地址。
plugins
第二个元素返回 {}
, 当函数出现在数组对象中时,在序列化过程中会被转换成 null
,所以 plugins
的第一个元素返回 null
。
总结
思考一下,为什么有些属性不能被 stringify
序列化。
因为 JSON
是一个通用的文本格式,和语言无关。设想如果将函数定义也 stringify
的话,如何判断是哪种语言,并且通过合适的方式将其呈现出来将会变得特别复杂。特别是和语言相关的一些特性,比如 JavaScript
中的 Symbol
。
ECMASCript官方也特意强调了这一点:
It does not attempt to impose ECMAScript's internal data representations on other programming languages. Instead, it shares a small subset of ECMAScript's textual representations with all other programming languages.
有兴趣的小伙伴可以了解一下如何使用 toJSON
来自定义序列化行为。