故事背景
最近我将客户端项目(electron开发)从JavaScript全部迁移到了Typescript,由此产生了以下几个问题:
electron无法直接使用ts文件作为开发环境的启动入口,也不能使用ts文件作为打包入口;所以必须将Ts编译成Js
将Ts文件编译成Js文件之后,需将其中的高级语法编译成兼容性强的低级语法,这个过程需使用Babel
需要指定electron启动开发环境或者打包的入口路径
改进前的解决方案
使用嵌套的scripts命令以及rimraf实现↓↓↓
以启动开发环境为例:
需先使用tsc --outDir runtime_tmp将Typescript编译成JavaScript; 由Ts编译得到的Js代码放到 runtime_tmp这个临时目录下面;
然后使用npx babel ./runtime_tmp --out-dir ./runtime 这一步将runtime_tmp目录下的高级Js语法转化成低级Js语法并放到runtime目录下
最后再删除中间文件./runtime_tmp
到这里完成源代码的构建,最后还要使用electron ./runtime启动开发环境。
scripts命令如下所示:
"prev": "tsc --outDir runtime_tmp && npx babel ./runtime_tmp --out-dir ./runtime && rimraf ./runtime_tmp",
"start": "yarn prev && cross-env NODE_ENV=development electron ./runtime",
这里用到了cross-env和rimraf 两个库文件。使用rimraf是为了保证在不同的os下都能够成功的删除临时目录,直接使用shell命令可能会报错。
问题在于:上述的scripts脚本非常难阅读,增加不少的心智负担。于是我使用gulp构建自动化的工作流程,如下所示:
改进后的解决方案:
使用gulp构建工具的流来设置打包路线,从而使构建过程更加清晰
安装依赖并创建gulpfile.js
touch gulpfile.js
yarn add -D gulp gulp-typescript gulp-babel gulp-exec @types/gulp @types/gulp-typescript @types/gulp-babel @types/gulp-exec
gulpfile.js中的内容为:
// gulpfile.js
const gulp = require('gulp');
const ts = require('gulp-typescript');
const babel = require('gulp-babel');
const {
series
} = require('gulp');
const exec = require('gulp-exec');
// 将ts编译成js
function compileTypeScript() {
const tsProject = ts.createProject('tsconfig.json');
return tsProject.src()
.pipe(tsProject()) // ts => js
.pipe(babel()) // js => js
.pipe(gulp.dest('runtime'));
}
// 开发
function dev() {
const tsProject = ts.createProject('tsconfig.json');
return tsProject.src()
.pipe(tsProject())
.pipe(babel())
.pipe(gulp.dest('runtime'))
.pipe(exec('cross-env NODE_ENV=development electron ./runtime')); // 执行shell脚本
}
// 打包
function build() {
const tsProject = ts.createProject('tsconfig.json');
return tsProject.src()
.pipe(tsProject())
.pipe(babel())
.pipe(gulp.dest('runtime'))
.pipe(exec('electron-builder'));
}
gulp.task('compile', series(compileTypeScript));
gulp.task('dev', series(dev));
gulp.task('build', series(build));
// 默认导出是编译,也就是ts=>js的结果
exports.default = series(
compileTypeScript,
);
package.json中的脚本相应改成:
"compile": "npx gulp",
"start": "npx gulp dev",
"dist": "npx gulp build",
要点
在使用.pipe(tsProject())
和.pipe(babel())
的时候没有传入命令行参数,所以必要的参数需要在tsconfig.json和babel.config.js中提前配置好(主要是配置每一步输出文件目录和下一步的入口目录)
electron入口问题
在dev函数中,通过.pipe(exec('cross-env NODE_ENV=development electron ./runtime'));
制定了yarn start也就是npx gulp dev运行时的入口目录为**./runtime**,这样就解决了启动开发环境时候electron找到入口的问题。
解决electron打包时的入口问题,需要在package.json中配置build字段:
"build": {
"appId": "***",
"productName": "***",
"directories": {
"output": "***"
},
"files": [
{
"from": "runtime/",
"to": "src/"
},
"*/**",
"**/*",
"!src/"
],
"extraResources": [
{
"from": "***",
"to": "***"
}
],
"asar": true,
"win": {
"icon": "***",
"target": "dir",
"requestedExecutionLevel": "requireAdministrator"
},
"linux": {
"target": "dir"
}
},
其中的关键点在于配置build.files.from为runtime, 也就是babel的输出目录。
结论
使用gulp之后的构建流程变得非常简洁,并且容易阅读,可维护性大大增强。