[toc]
# 1. 背景
基础库跨平台的方式是,在编译阶段用webpack plugin
替换关键词,比如下面的代码:
// @ts-ignore
export { initXss } from './index-@TIP_PLATFORM_NAME';
在web
端是映射到index-web
,在小程序映射到index-mp
。
这种使用方式由来已久,是早期兼容hippy
项目时开始的。
这种方法的一个缺点是缺少类型提示,因为ts
找不到@TIP_PLATFORM_NAME
这个文件,类型就中断了,同时eslint
默认也会报错。
# 2. 思路
报错问题很好解决,不管是eslint
还是ts
,只需要设置ignore
或rule
以及@ts-ignore
即可。
比如eslint
额外配置下规则:
module.exports = {
rules: {
'import/no-unresolved': [2,
{
ignore: [
'@TIP_PLATFORM_NAME',
],
},
],
},
};
但是没有类型提示就很不友好了,容易出错,效率也低。
如何解决呢,尝试了下面几种方案。
- 在
tsconfig.json
中设置compilerOptions.paths
,类似webpack
配置中的alias
。
但是paths
只能指定绝对路径的别名,比如@
、@/utils
等,基础库基本都是相对路径,如果都改成一个特殊前缀的绝对路径,并不直观。
- 写个
vscode插
件,做相关的类型提示。
这种方式成本相对高,而且使用基础库的人比较多,还有一些外部团队,沟通、教育成本比较高。
- 为引用方生成一份类型文件
可以在同级目录下,生成一份引用者的.d.ts
文件,比如index.js
使用了index-web
和index-mp
,那么可以生成index.d.ts
文件。这样ts
就可以找到相应的提示了。
但是,这种方式有2个问题。
第一,引用方的错误依然无法消除,因为依然找不到index-@TIP_PLATFORM_NAME
第二,引用方需要用js
编写。如果引用方是用ts
写的,也就是index.ts
,而不是index.js
,那么 typescript
会优先找ts
文件,而不是.d.ts
文件,由于index.ts
中有找不到的依赖,也就是编译失败,那么类型提示依然会中断。
- 生成
@TIP_PLATFORM_NAME
的类型文件
在同级目录下,生成一份.d.ts
文件。即使没有index-@TIP_PLATFORM_NAME
真实的文件,但是可以有类型文件index-@TIP_PLATFORM_NAME.d.ts
,其类型与index-web
中导出类型一致,这样不就可以提示了吗?
这种方案相对上面几种方式,相对成本低、效果好,所以采用这种方案。
# 3. 实现
搜了一下,有40多个文件用了这种“关键词编译”方法,手工写效率太低、容易出错,所以考虑用脚本。
脚本实现如下:
- 获取哪些文件使用了关键词
@TIP_PLATFORM_NAME
- 查找对应的
xxx-web
的路径,注意并不一定是同级,有可能是子文件夹 - 生成
xxx-web
的类型文件 - 将上面类型文件名更新为
xxx-@TIP_PLATFORM_NAME.d.ts
# 4. 效果对比
使用前:
使用后:
# 5. 后记
其实解决跨平台编译有更好的解决方案,比如uni-app
中的条件编译 (opens new window),小程序的条件编译 (opens new window)。
相比于上面的“关键词编译”,条件编译更加灵活。"关键词编译"必须把差异部分提取到一个文件中,而条件编译可以解决任意粒度的差异,可以是文件级别,也可以是几行代码,比如:
// #ifdef H5
console.log('only H5')
// #endif
另外,使用关键词编译意味着必须引入一个额外的工具(loader
),而条件编译时是注释方式,可以引入loader
,也可以不用,更灵活。
但是uni-app
的条件编译只能用在基于uni-app
的项目中,对于普通的前端项目,如何实现同样的条件编译呢?
这里我写了一个webpack loader (opens new window),可以实现相同效果,使用方式也与uni-app
相同。
← base 递归 partial →