在介绍Cesium源码编译打包之前,先简单的介绍下目前前端工程中一些常用的打包工具,方便后面内容的理解。
前端常用的打包库
(1)gulp打包
gulp是用代码方式来写打包脚本,并且代码采用流式的写法,简单说,gulp是基于 nodejs 的 steam 流打包。它只抽象出了gulp.src,gulp.pipe,gulp.dest,gulp.watch 接口,运用相当简单,更易于学习和使用。Cesium就是利用gulp进行打包的。
(2)webpack打包
webpack是模块化管理工具和打包工具。通过 loader 的转换,任何形式的资源都可以视作模块,比如 CommonJs 模块、AMD 模块、ES6 模块、CSS、图片等。它可以将许多松散的模块按照依赖和规则打包成符合生产环境部署的前端资源。还可以将按需加载的模块进行代码分隔,等到实际需要的时候再异步加载。它定位是模块打包器,而 Gulp属于构建工具。Webpack 可以代替 Gulp 的一些功能,但不是一个职能的工具,可以配合使用。
webpack是目前最强大的打包工具,非常适合单页面应用程序(SPA)的打包,比如Vue工程、React工程。
(3)rollup打包
rollup是下一代 ES6 模块化工具,最大的亮点是利用 ES6 模块设计,按需打包,利用 tree-shaking生成更简洁、更简单的代码,非常适合前端类库的打包。比如,目前主流的前端JS库Vue、React都是利用rollup打包的。
Cesium打包
在执行打包命令之前,我们需要安装配置Node.js环境,环境配置可参考Cesium开发入门篇 | Cesium开发环境搭建及第一个示例相关内容,这里就不再次详细说明了。环境配置好之后,在某个文件资源目录下打开git命令窗口,输入git clone https://github.com/CesiumGS/cesium.git,拉取Cesium源码。成功后,进入cesium文件目录,执行yarn命令,系统会自动安装各种依赖的包,等待安装完成即可。
安装过程中,我们看一下package.json文件,所有的基于nodeJS创建的前端工程都会有一个package.json文件(创建工程时自动生成的),该文件记录了前端工程的名称、版本、描述、作者、开发环境依赖包(devDependencies)、生产环境依赖包(dependencies)、执行命令说明(scripts)等。我们看下配置文件中有关gulp的命令,如下所示:
"scripts": {
"convertToModules": "gulp convertToModules",
"start": "node server.cjs",
"startPublic": "node server.cjs --public",
"build": "gulp build",
"build-watch": "gulp build-watch",
"build-ts": "gulp build-ts",
"buildApps": "gulp buildApps",
"clean": "gulp clean",
"cloc": "gulp cloc",
"combine": "gulp combine",
"combineRelease": "gulp combineRelease",
"coverage": "gulp coverage",
"generateDocumentation": "gulp generateDocumentation",
"generateDocumentation-watch": "gulp generateDocumentation-watch",
"eslint": "eslint \"./**/*.js\" \"./**/*.cjs\" \"./**/*.html\" --cache --quiet",
"makeZipFile": "gulp makeZipFile",
"minify": "gulp minify",
"minifyRelease": "gulp minifyRelease",
"release": "gulp release",
"build-specs": "gulp build-specs",
"test": "gulp test",
"test-all": "gulp test --all",
"test-webgl": "gulp test --include WebGL",
"test-non-webgl": "gulp test --exclude WebGL",
"test-webgl-validation": "gulp test --webglValidation",
"test-webgl-stub": "gulp test --webglStub",
"test-release": "gulp test --release",
"deploy-s3": "gulp deploy-s3",
"deploy-status": "gulp deploy-status",
"deploy-set-version": "gulp deploy-set-version",
"prettier": "prettier --write \"**/*\"",
"prettier-check": "prettier --check \"**/*\"",
"pretty-quick": "pretty-quick"
}
这些命令在gulpfile.cjs文件(gulp任务主文件)中都能找到对应的task,task分同步和异步两种,异步的task一般都是通过Promise实现的。废话不多说了,scripts给我们提供了这么多的命令,我们看一下Cesium最终的打包文件是通过哪个命令实现的。观察仔细的童鞋,可能早就发现了,就是release这个命令,在cesium目录下执行 yarn release,结果在Builid文件夹下面生成了如下文件:
主要包括压缩(Cesium)和未压缩(CesiumUnmimified)的库文件、API参考文档(通过jsdoc实现)三类文件,如果你想在调试的时候查看cesium源码,请引用未压缩的库文件。
我们看一下执行yarn release 命令,中间都经历了什么。如下图所示:
gulp.task(
"release",
gulp.series(
"build",
"build-ts",
combine,
minifyRelease,
generateDocumentation
)
);
基本上分了三个部分,
(1)打包前的准备工作:build、build-ts、combine
gulp.task("build", function () {
mkdirp.sync("Build");
fs.writeFileSync(
"Build/package.json",
JSON.stringify({
type: "commonjs",
}),
"utf8"
);
glslToJavaScript(minifyShaders, "Build/minifyShaders.state");
createCesiumJs();
createSpecList();
createJsHintOptions();
return Promise.join(createWorkers(), createGalleryList());
});
gulp.task("build-ts", function () {
createTypeScriptDefinitions();
return Promise.resolve();
});
创建Build文件夹及Cesium文件夹 => 将glsl文件转为js形式 => createCesiumJs方法,遍历Source中所有js脚本,将所有Object记录到Source/Cesium.js => 范例,单元测试模块等。
function combine() {
const outputDirectory = path.join("Build", "CesiumUnminified");
return combineJavaScript({
removePragmas: false,
optimizer: "none",
outputDirectory: outputDirectory,
});
}
gulp.task("combine", gulp.series("build", combine));
在Build文件夹下创建用于保存未压缩cesium库文件的文件夹CesiumUnminified。combineJavaScript 方法稍后再详细讲解。
(2)minifyRelease
function minifyRelease() {
return combineJavaScript({
removePragmas: true,
optimizer: "uglify2",
outputDirectory: path.join("Build", "Cesium"),
});
}
gulp.task("minifyRelease", gulp.series("build", minifyRelease));
function combineJavaScript(options) {
const optimizer = options.optimizer;
const outputDirectory = options.outputDirectory;
const removePragmas = options.removePragmas;
const combineOutput = path.join("Build", "combineOutput", optimizer);
const promise = Promise.join(
combineCesium(!removePragmas, optimizer, combineOutput),
combineWorkers(!removePragmas, optimizer, combineOutput),
minifyModules(outputDirectory)
);
return promise.then(function () {
const promises = [];
//copy to build folder with copyright header added at the top
let stream = gulp
.src([combineOutput + "/**"])
.pipe(gulp.dest(outputDirectory));
promises.push(streamToPromise(stream));
const everythingElse = ["Source/**", "!**/*.js", "!**/*.glsl"];
if (optimizer === "uglify2") {
promises.push(minifyCSS(outputDirectory));
everythingElse.push("!**/*.css");
}
stream = gulp
.src(everythingElse, { nodir: true })
.pipe(gulp.dest(outputDirectory));
promises.push(streamToPromise(stream));
return Promise.all(promises).then(function () {
rimraf.sync(combineOutput);
});
});
}
combineJavaScript做了两件事情,打包Cesium和Workers脚本。Gulp根据指令的不同,比如minify下采用uglify2优化,而combine对应的参数为none,生成路径为CesiumUnminified。
(3)generateDocumentation
function generateDocumentation() {
child_process.execSync("npx jsdoc --configure Tools/jsdoc/conf.json", {
stdio: "inherit",
env: Object.assign({}, process.env, { CESIUM_VERSION: version }),
});
const stream = gulp
.src("Documentation/Images/**")
.pipe(gulp.dest("Build/Documentation/Images"));
return streamToPromise(stream);
}
gulp.task("generateDocumentation", generateDocumentation);
API参考文档就是基于 generateDocumentation 命令自动生成的,引用了jsdoc文档生成库,通过解析jsdpoc配置文件Tools/jsdoc/conf.json而生成的。
本文只简单介绍了打包流程中的部分代码,感兴趣的同学可以查看cesium源码中的gulpfile.cjs文件内容。