使用 pkg 打包 ThinkJS 项目

更新日期: 2019-12-01阅读: 2.1k标签: 打包

在 ThinkJS 的用户群里,经常有开发者提出需要对源码进行加密保护的需求。我们知道 JavaScript 是一门动态语言,不像其他静态语言可以编译成二进制包防止源码泄露。所以就出现了 pkgnexe 之类的工具,支持将 JS 代码连同 Node 一块打包成一个可执行文件,一来解决了环境依赖的问题,二来解决了大家关心的源码保护的问题。

在 pkg 模块的 README 中,罗列了它的几大用处,如果你有下面的几个需求的话建议不妨试试。

为应用提供商业发行版而不用暴露源码
为应用提供 demo 而不用暴露源码
一键打包所有平台可执行文件而不需要对应平台环境依赖
提供自解压或自安装的解决方案
运行应用不需要安装 Node.js 和 npm
部署仅需要一份单文件,不需要通过 npm 安装大量的依赖
资源打包后让应用迁移起来更加方便
在指定 Node.js 版本下对应用进行测试而不需要安装对应的版本


如何使用

关于 pkg 模块的基础使用,大家可以看 《把你的NodeJS程序给没有NodeJS的人运行》 这篇文章。通过 npm install -g pkg 在全局安装上模块后就可以在命令行中使用 pkg 命令了。pkg 除了支持在命令行中指定参数之外,还支持在 package.json 中进行配置。

{
  ...
  "bin": "production.js",
  "scripts": {
    "pkg": "pkg . --out-path=dist/"
  },
  "pkg": {
    "scripts": [...]
    "assets": [...],
    "targets": [...]
  },
  ...
}

以上就是一个简单的配置。bin 用来指定最终打包的入口文件,pkg.scripts 和 pkg.assets 用来指定除了入口文件之外需要打包进可执行文件中的内容,其中前者用来指定其他 .js 文件,后者用来指定非.js的资源。pkg.targets 则是用来指定需要打包的平台,平台名称结构如下,node${version}-${platform}-${arch}。version 用来指定具体 Node 的版本,platform 用来指定编译的平台,可以是 freebsd, linux, alpine, macos 或者 win,最后 arch 用来指定编译平台的架构,可以是 x64, x86, armv6 或者 armv7。例如 node10-macos-x64 表示的就是基于 Node 10 打包在 MacOS 平台上执行的可执行程序。scripts, assets 和 targets 都支持数组配置多个。

将入口文件、依赖的脚本和资源、需要编译的平台配置好之后,执行 npm run pkg 即可完成编译。


如何打包 ThinkJS

pkg 的原理大概是提供一个虚拟的文件系统,将 __filename, __dirname 等变量以及官方 api 中的 IO 操作方法指向本地文件系统的变量修改成指向虚拟系统。通过该虚拟文件系统读取压缩打包后的程序源码,提供脚本执行的环境。需要注意的是该虚拟文件系统是只读的,所以如果程序中有基于 __dirname 进行读写操作的方法,需要规避规避掉。

代码预处理

在 ThinkJS 项目中会有以下两个地方有文件写入操作:

项目启动后会在 runtime/config/${env}.json 下写入最终的配置文件

生产环境下默认会在 logs/ 目录中写入线上日志

这些目录默认都是基于当前项目文件夹的,所以基于之前的理论都需要规避。pkg 的 README 中告诉我们 process.cwd() 还是会指向到真实的环境中,所以我们可以修改以上目录的位置到 process.cwd() 来解决这个问题。

//pkg.js
const path = require('path');
const Application = require('thinkjs');

const instance = new Application({
  //在启动文件中可以自定义配置 runtime 目录
  RUNTIME_PATH: path.join(process.cwd(), 'runtime'), 
  ROOT_PATH: __dirname,
  proxy: true,
  env: 'pkg',
});

instance.run();

基于 production.js 我们新建一个 pkg.js 启动文件,定义项目启动后的 RUNTIME_PATH 路径,并将 env 赋值为 pkg,方便后续的配置中通过 think.env === 'pkg' 来切换配置。

//src/config/adapter.js
const {Console, DateFile} = require('think-logger3');
const isDev = think.env === 'development';
const isPkg = think.env === 'pkg';
exports.logger = {
  type: isDev ? 'console' : 'dateFile',
  console: {
    handle: Console
  },
  dateFile: {
    handle: DateFile,
    level: 'ALL',
    absolute: true,
    pattern: '-yyyy-MM-dd',
    alwaysIncludePattern: true,
    filename: path.join(isPkg ? process.cwd() : think.ROOT_PATH, 'logs/app.log')
  }
};

在 adapter 配置中我们将原来基于 think.ROOT_PATH 的路径修改成基于 process.cwd()。除了日志服务之外,如果业务中有使用到 cache 和 session 等服务,它们如果也是基于文件存储的话,也需要修改对应的文件存储配置。当然这些都是 ThinkJS 自带的一些服务,如果项目中有用到其它的一些服务,或者说本身的业务逻辑中有涉及到文件写入的也都需要修改配置。

打包配置

项目的写入操作规避掉之后我们就可以正常的配置 pkg 然后进行打包处理了。一份简单的 pkg 模块的配置大概是这样的:

//package.json
{
  "bin": "pkg.js",
  "pkg": {
    "assets": [
      "src/**/*",
      "view/**/*",
      "www/**/*"
    ],
    "targets": [
      "node10-linux-x64",
      "node10-macos-x64",
      "node10-win-x64"
    ]
  }
}

这里我们指定了 pkg.js 为打包的入口文件,指定了需要编译出 linux, macos, win 三个平台的可执行脚本,同时指定了需要将 src/, view/, www/ 三个目录作为资源一块打包进去。这是因为 ThinkJS 是动态 require 的项目,具体的业务逻辑都是在执行的时候通过遍历文件目录读取文件的形式载入的,对于 pkg模块打包来说无法在编译的时候知道这些依赖关系,所以需要作为启动依赖的“资源”一块打包进去。

配置好后直接在项目目录下执行 pkg .,如果一切 OK 的话应该能在当前目录中看到三个可执行文件,直接执行对应平台的二进制文件即可启动服务了。

➜  www.thinkjs.org git:(master) npm run pkg-build

> thinkjs-official@1.2.0 pkg-build /Users/lizheming/workspace/thinkjs/www.thinkjs.org
> pkg ./ --out-path=dist

> pkg@4.4.0
➜  www.thinkjs.org git:(master) ✗ ls -alh dist
total 577096
drwxr-xr-x   5 lizheming  staff   160B 12 28 17:35 .
drwxr-xr-x@ 30 lizheming  staff   960B 12 28 17:34 ..
-rwxr-xr-x   1 lizheming  staff    87M 12 28 17:34 thinkjs-official-linux
-rwxr-xr-x   1 lizheming  staff    87M 12 28 17:35 thinkjs-official-macos
-rw-r--r--   1 lizheming  staff    82M 12 28 17:35 thinkjs-official-win.exe
➜  www.thinkjs.org git:(master) ✗


后记

项目打包后有一个问题是配置没办法修改了,如果有动态配置的需求的话就不是很方便了。这里提供两个思路解决该问题:

将动态的配置配置到环境变量中,程序通过读取环境变量覆盖默认的配置。

利用 ThinkJS 提供的 beforeStartServer() 钩子在启动前读取真实目录下的配置文件进行配置覆盖。

//pkg.js
const path = require('path');
think.beforeStartServer(() => {
  const configFile = path.join(process.cwd(), 'config.js');
  const config = require(configFile);
  think.config(config);
});

另外随着项目的复杂度提高,业务内可能会引入大量的第三方模块。前文只是解决了 ThinkJS 项目本身的动态引入问题,如果引入的第三方模块也有动态引入的话也需要在 pkg.assets 配置中显示指定出来。还有就是针对 C++ 模块,pkg 目前还没有办法做到自动引入,同样需要在 pkg.assets 中指定依赖资源。

//package.json
{
  "pkg": {
    "assets": [
      //以 node-sqlite3 模块为例
      "node_modules/sqlite3/lib/binding/node-v64-darwin-x64/node_sqlite3.node"
    ]
  }
}

其中 node-v64-darwin-x64 可能会根据平台不一样导致名字不太一样。无法引入 .node 模块的原因是因为 C++ 模块安装的时候会通过 node-gyp 进行动态编译,该操作是和平台相关的。也就是说该特性和 pkg 模块在一个平台上能打包所有平台的二进制包特性是冲突的,毕竟 pkg 模块也没办法在 Mac 平台上编译 Windows 平台的模块。所以在这种情况下除了需要手动引入编译后的 .node 模块之外,还需要注意引入的该 .node 模块和 pkg.targets 指定的编译平台的一致性。

获取 .node 模块除了在对应平台模块安装之外,也可以选择下载其它同学提供编译好的模块。淘宝源上提供了很多二进制模块的编译后结果,以 node-sqlite3 为例,它的所有编译模块可以在 https://npm.taobao.org/mirror... 这里下载,自行选择对应的版本和平台即可。

本文说的打包配置都已在 ThinkJS 官网 项目中实现,想要尝试的同学可以直接克隆官网项目,安装完依赖后执行 npm run pkg-build 即可在 dist/ 目录中获得二进制可执行文件。

来自:https://segmentfault.com/a/1190000021438245

链接: https://www.fly63.com/article/detial/7110

用Cordova打包Vue项目

现在的打包Vue项目目前流行的就是使用weex和cordova。weex是阿里提供并且Vue的作者也极力推荐的,有兴趣的可以去学习使用一下。下面说说怎么使用cordova打包Vue项目

Webpack 打包含动态加载的类库

在编写库的时候,我们有时候会希望按需加载某些依赖,例如如果代码的运行环境不支持某些功能的话,就加载相关的Polyfill。webpack作为当前最流行的打包工具,早已支持动态加载的功能了。本文将讨论一种用webpack打包含动态加载的类库的方法。

webpack如何编译ES6打包

随着ES的普及我们越来越多的开始使用ES6的语法了,当然也随着mvvm框架的流行少不了js模块化,那js模块化又有那些呢?在很早的时候大家都用的命名空间,现在也有人用(库名.类别名.方法名)

解决webpack打包速度慢的解决办法

webpack在打包的时候第一次总是会做很长的准备工作,包括加载插件之类的。在刚接触webpack的时候总是webpack一下-测一下-改一下-再webpack一下,这种方式最后让很多人崩溃了觉得webpack一点都不好用

vue实现分环境打包步骤

在新建好的项目中,一般执行npm run build就是打包了,但此时只能打包到一个环境,不同环境需要配置不同的地址,可以手动更改接口的地址,也可以自行配置命令而不需要每次打包进行地址切换

前端js打包工具

打包工具可以更好的管理html,css,javascript,使用可以锦上添花,不使用也没关系。html好比是房子的地基,css和 javascript是房子的建筑材料,这三个部分一起组成个漂亮的房子

webpack4 打包静态资源

准备一个空文件夹,然后执行下列命令:然后创建一个 dist 目录,并在里面创建一个 indedx.html:接着创建一个 src 目录,在里面创建一个 lib 文件夹,创建一个 until.js:再创建 components 文件夹,再写入几个 js:

vue-cli3.0中自定css、js和图片的打包路径

我们有时候因为一些特殊需求,可能需要将js/css/img等资源文件都打包到根路径下,但vue-cli3.0的路径配置中仅有assetsDir配置项能够配置所有的静态文件所在的文件夹,并不能针对css/js/img等资源文件分别来做设置,那么请看我如何尝试的吧!

Webpack安装配置及打包详细过程

前端经过漫长的发展,涌现出了很多实践方法来处理复杂的工作流程,让开发变得更加简便,其中,模块化可以使复杂的程序细化成为各个小的文件,而webpack并不强制你使用某种模块化方案,而是通过兼容所有模块化方案让你无痛接入项目

webpack动态加载打包chunk命名

最近,遇到复杂h5页面开发,为了优化H5首屏加载速度,想到使用按需加载的方式,减少首次加载的JavaScript文件体积,于是将处理过程在这里记录一下,涉及到的主要是以下三点:

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!