给 Apollo-Link 打补丁 II
继上回给 Apollo Link 打补丁过去了几个月,本想翻看一下之前的那个 Pull Request 是否已经被 Merge,结果没想到原来的 Apollo Link 库已经被弃用了。新版的 Apollo Link 被重构后集成到了 @apollo/client 包中。不过并没有修复前面 PR 提到的问题。更糟糕的是之前使用的打补丁的方式无法起作用了。新的 selectHttpOptionsAndBody()
函数被专门放置到一个独立的 module 文件中。并且在提供的 cjs bundle 里,它直接被 createHttpLink 函数引用。所以之前的替换方法没办法在函数被引用前进行「狸猫换太子」。
那么是否有其它成本比较低的方式进行补救?于是我简单的搜索了一下 Webpack 的官方文档,很快找到了一个叫 NormalModuleReplacementPlugin 的内置插件。使用它可以非常方便地替换指定的模块文件。
new webpack.NormalModuleReplacementPlugin(
resourceRegExp,
newResource
);
于是我们将 @apollo/client
包中的 selectHttpOptionsAndBody.js
复制一份,并作以下修改:
- 在头部导入 stripIgnoredCharacters ;
- 在生成 body.query 的地方使用 stripIgnoredCharacters 压缩 graphql 查询;
Diff 结果如下:
3d2
< import { stripIgnoredCharacters } from "graphql";
38c37
< body.query = stripIgnoredCharacters(print(query));
---
> body.query = print(query);
将修改后的文件放到 /path/to/project/patch/selectHttpOptionsAndBody.js
然后在 Webpack 配置中增加一个插件:
plugins: {[
...,
new webpack.NormalModuleReplacementPlugin(
/\/node_modules\/@apollo\/client\/link\/http\/selectHttpOptionsAndBody\.js/,
path.resolve(process.cwd(), './patch/selectHttpOptionsAndBody.js'),
),
]},
重新运行 Webpack 构建,但发现并未能如愿将补丁打上。检查 Sourcemap 发现 Webpack 引入的是 @apollo/client
打包好的 commonjs 模块(*.cjs.js
)而非实际的源码。
经过一番排查,发现 @apollo/client/link/http
的 package.json
描述如下:
{
"name": "@apollo/client/link/http",
"main": "http.cjs.js",
"module": "index.js",
"types": "index.d.ts"
}
可见当前 Webpack 使用的是 main 字段的入口,而非 module 字段的入口。根据 Webpack 的 resolve 配置说明,我们可以使用 #resolvemainfields 来修改入口顺序。于是在 Webpack 配置中找到 mainFields 并作如下调整:
resolve: {
mainFields: ['module', 'browser', 'main', 'jsnext:main'],
...,
}
调整的依据:
- 优先使用 es module,虽然会稍微增加一些构建时间,但可以利用 tree-shaking 减小包的体积;
- 对于不支持 es module 的库,尝试使用 browser 或 main 入口,即预编译的浏览器包或者 commonjs 的入口;
再次构建,检查 Sourcemap 后发现 selectHttpOptionsAndBody.js
已经变成我们打了补丁的版本了。
最后为了保证依赖的一致性,将 @apollo/client
的版本锁定。下次升级时需要检查补丁是否需要修订。