携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第29天,点击查看活动详情
如果想要在工程项目中引入 Cesium ,则需要手动添加几个资源,然后再添加一下资源路径的一些前置需求,比如:
而项目用的是基于 Umi 的项目,所以就做成 Umi 插件方便后续更多项目,快速启动 Cesium 环境,将这些操作内敛到配置中。
目前 Umi 已经是@4 的版本了,直接使用@4 的插件初始化即可。
想直接使用的同学,可以直接访问github。
使用 father 作为构建插件的工具
初始化使用 father,fatehr 也是出了@4 的版本,比之前更加的好用,感兴趣的同学可以看father@4 的发布介绍。
我们初始化项目后,装好依赖。
//package.json
"devDependencies": {
"father": "^4.0.0"
},
"dependencies": {
"cesium": "^1.96.0",
"umi": "^4.0.15"
}
因为是插件,所以只需要 cjs 即可,编写一下 father 的配置。
// .fatherrc.ts
import { defineConfig } from "father";
export default defineConfig({
cjs: {
output: "dist",
},
});
分析需求
我们先分析下我们的需求是什么:
- 通过配置开启 cesium 环境
- 自动注册 accessToken 以及 CESIUM_BASE_URL
- 通过 umi 导入 Cesium,保持类型声明
- 能够访问到 widgets.css
- 打包自动注入资源到 public,方便生成环境使用
那么我们就一个个实现这些需求。
通过配置开启 cesium 环境
通过配置开启,需要使用 describe API,我们设置 cesium 作为我们的插件配置名称。
// src/index.ts
import type { IApi } from "umi";
export default (api: IApi) => {
api.describe({
key: "cesium",
config: {
schema(joi) {
return joi.object({});
},
},
enableBy: api.EnableBy.config,
});
};
config 的配置是 schema,在开发的时候能够校验我们的配置是否正确,默认就用 joi.object({}) 就行了。
自动注册 accessToken 以及 CESIUM_BASE_URL
因为 accessToken 是用户的,所以我们通过配置让用户提供 token,我们先为之前的 config 配置加一个 accessToken 的配置。
// src/index.ts
import type { IApi } from "umi";
export default (api: IApi) => {
api.describe({
key: "cesium",
config: {
schema(joi) {
return joi.object({
accessToken: joi.string(),
});
},
},
enableBy: api.EnableBy.config,
});
};
然后我们再使用 onGenerateFiles API 生成静态文件,导出到 umi 中,这样我们就能在项目中使用插件依赖中的 Cesium,并且在导出之前我们先将要处理的逻辑写好
// src/index.ts
//...other code
export default (api: IApi) => {
//...other code
api.onGenerateFiles({
name: "cesium",
fn: () => {
// index.ts for export
api.writeTmpFile({
path: "index.ts",
content: `
import * as Cesium from 'cesium';
window.CESIUM_BASE_URL = '/Cesium';
${
api.config.cesium.accessToken
? `Cesium.Ion.defaultAccessToken = '${api.config.cesium.accessToken}';`
: ""
}
export { Cesium };
`,
});
},
});
我们先写入一个入口 index.js 文件,但是 import * as Cesium from 'cesium'
从开发来说,cesium 这个依赖是不对的,因为用户可能没有装 cesium 这个依赖,当然我们也可以要求用户装,但是我们也要提供自己的依赖,所以开始我是装了 cesium 的依赖的。
所以我们先找好 cesium 的依赖路径,优先从用户的依赖读,配置好 alias 即可
const pkgPath =
resolveProjectDep({
pkg: api.pkg,
cwd: api.cwd,
dep: "cesium",
}) || dirname(require.resolve("cesium/package.json"));
api.modifyConfig((memo) => {
memo.alias["cesium"] = pkgPath;
return memo;
});
但是这又会引发一个问题,那就是没有了类型提示,所以我们还需要提供 cesium 的类型,不过在做之前,我们还需要保证一步,就是开发环境下,提供 /Cesium 的服务访问,通过 addBeforeMiddlewares API 添加中间件来访问
api.addBeforeMiddlewares(() => {
return [
(req, res, next) => {
const { path } = req;
if (path.startsWith("/Cesium/")) {
return res.sendFile(join(cesiumPath, "Build", path));
}
return next();
},
];
});
很好,我们下面再解决类型的问题
通过 umi 导入 Cesium,保持类型声明
刚刚我们已经导出过 Cesium 了,但是还缺少一个类型提示,通过观察发现 Cesium 仓库是 js 写的,类型是自己维护的
declare module "cesium" {
//...
}
如果使用 import * as Cesium from '${cesiumPath}'
路径的话 是不对的,不符合 module 的定义
但我们可以通过写入 types.d.ts 导出 cesium 的声明文件来做到
api.onGenerateFiles({
name: "cesium",
fn: () => {
// ...other code
api.writeTmpFile({
path: "types.d.ts",
content: readFileSync(join(cesiumPath, "/Source/Cesium.d.ts"), "utf-8"),
});
},
});
这是上面用到的 cesiumPath 的声明,下面就不再说了
const cesiumPath = dirname(require.resolve("cesium"));
能够访问到 widgets.css
其实就是 import "cesium/Build/Cesium/Widgets/widgets.css";
这个操作,但如果我们使用 pnpm 开发,这个就是幽灵依赖,但也没办法,我们还是要解决它,也减少手动引入的麻烦。
这个还是比较简单的,我们直接使用 addHTMLStyles API 注入 css 样式即可
api.addHTMLStyles(() => [
{
content: readFileSync(
join(cesiumPath, "Build/Cesium/Widgets/widgets.css"),
"utf-8"
),
},
]);
打包自动注入资源到 public,方便生成环境使用
开发模式都做完了,但还有一个问题,就是生成模式的 /Cesium 是没有的,我们还需要将刚才访问的依赖写到输入出目录中,这次我们用 modifyConfig API 修改 config 中的 copy 配置就行了
api.modifyConfig({
fn: (config) => {
return api.env === "production"
? {
...config,
copy: [
...(config.copy || []),
...["Workers", "ThirdParty", "Assets", "Widgets"].map((dir) => ({
from: join(getAbsPath(api.cwd, cesiumPath), "Build/Cesium", dir),
to: join(config.outputPath || "dist", "Cesium", dir),
})),
],
}
: config;
},
stage: Infinity,
});
总结
至此,Cesium 就开发完了,使用也非常的简单,直接
export default {
plugins: ["umi-cesium-plugin"],
cesium: {
accessToken: "your accessToken",
},
};
再 import { Cesium } from "umi";
就可以用了,不用再自己配置
对全部代码感兴趣的同学可以看github。