使用pkg打包ThinkJS项目的方法步骤

在 ThinkJS 的用户群里,经常有开发者提出需要对源码进行加密保护的需求。我们知道 JavaScript 是一门动态语言,不像其他静态语言可以编译成二进制包防止源码泄露。所以就出现了 pkg、nexe 之类的工具,支持将 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 项目中会有以下两个地方有文件写入操作:

  1. 项目启动后会在 runtime/config/${env}.json 下写入最终的配置文件
  2. 生产环境下默认会在 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) ✗

后记

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

  1. 将动态的配置配置到环境变量中,程序通过读取环境变量覆盖默认的配置。
  2. 利用 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/mirrors/sqlite3这里下载,自行选择对应的版本和平台即可。

本文说的打包配置都已在 ThinkJS官网 项目中实现,想要尝试的同学可以直接克隆官网项目,安装完依赖后执行 npm run pkg-build 即可在 dist/ 目录中获得二进制可执行文件。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Thinkjs3新手入门之如何使用静态资源目录

    静态资源访问 项目开发时,一般都需要在模版里引用静态资源. 使用 thinkjs 命令创建项目时,会自动创建 www/static 目录,该目录下专门用来存放 JS.CSS.图片等静态资源. 0x0 听说new的项目自动就有www/static!? 传说默认创建的项目结构如下: |--- development.js //开发环境下的入口文件 |--- nginx.conf //nginx 配置文件 |--- package.json |--- pm2.json //pm2 配置文件 |---

  • thinkjs之页面跳转同步异步操作

    对于刚入手thinkjs项目的新手来说,时常会犯的一个错误就是"混用"各种代码逻辑,比如:我们经常在做后台管理系统的时候用到的登录框, 其实它原本是有一个路由专门存放自己的代码逻辑,而在点击提交按钮的时候,要达到的效果便是账号密码正确的时候,正常跳转页面,而错误的时候给出提示:为了发现问题,就先把源代码贴出来吧: <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <h

  • thinkjs 文件上传功能实例代码

    介绍 ThinkJS 是一款面向未来开发的 Node.js 框架,整合了大量的项目最佳实践,让企业级开发变得如此简单.高效.从 3.0 开始,框架底层基于 Koa 2.x 实现,兼容 Koa 的所有功能. 特性 基于 Koa 2.x,兼容 middleware 内核小巧,支持 Extend.Adapter 等插件方式 性能优异,单元测试覆盖程度高 内置自动编译.自动更新机制,方便快速开发 使用更优雅的 async/await 处理异步问题,不再支持 */yield 从 3.2 开始支持 Type

  • 使用ThinkJs搭建微信中控服务的实现方法

    本人前端渣渣一枚,这篇文章是第一次写,如果有硬核bug,请大佬们轻喷.指出... 另外,本文不涉及任何接口安全.参数校验之类的东西,默认对调用方无脑级的信任:joy: 目前自用的接口包括但不限于以下这些 |--- 微信相关 | |--- 0. 处理微信推过来的一些消息 | |--- 1. 获取微信SDK配置参数 | |--- 2. 微信鉴权登陆 | |--- 3. 获取微信用户信息 | |--- 4. 获取AccessToken | |--- 5. 批量发送模版消息 | |--- 6. 获取模版

  • thinkjs微信中控之微信鉴权登陆的实现代码

    前言 上一篇文章大概写了一下如何搭一个微信中控服务: [thinkjs搭建微信中控服务] . 接下来这篇,专门写一下如何在此基础上扩展出来一个比较好用的微信鉴权登陆的方案. 由于这一段的逻辑着实有点绕,所以就单独拿出来写了. 有时候,调用方甚至可以通过这个方案,进行多公众号openid的之间的关联. 官方说明 开发文档 微信文档地址:传送门 鉴权逻辑 前端跳转到以下url,重定向或者代码跳转都可以:https://open.weixin.qq.com/connect/oauth2/authori

  • ThinkJS中如何使用MongoDB的CURD操作

    前言 众所周知目前使用Node.js + mongodb已经成为很多公司的技术栈.ThinkJS其实也提供了对mongo的支持,虽然官方文档较少,但是保证了ORM的API的一致性,所以用起来需要查看基本的>Model api 基本的模型文件放在common/model下 获取列表 getList(q, page) { return this.select(); } 分页加条件搜索 search(q, page) { if(q) { q = new RegExp(q,'i'); } return

  • Thinkjs3新手入门之添加一个新的页面

    前言 ThinkJS 是一款拥抱未来的 Node.js Web 框架,致力于集成项目最佳实践,规范项目让企业级团队开发变得更加简单,更加高效. 目前该版本已经在线上多个项目中使用,简单一个命令就可以让你下载使用它: npm install -g think-cli 本文将帮助大家往Thinkjs3中添加一个新的页面,没有原理描述,只提供给入门者一个感性认识. 0x0 首先需要一个Thinkjs3项目 我们创建一个叫做1uck的项目 $ thinkjs new 1uck $ cd ./1uck $

  • 使用pkg打包ThinkJS项目的方法步骤

    在 ThinkJS 的用户群里,经常有开发者提出需要对源码进行加密保护的需求.我们知道 JavaScript 是一门动态语言,不像其他静态语言可以编译成二进制包防止源码泄露.所以就出现了 pkg.nexe 之类的工具,支持将 JS 代码连同 Node 一块打包成一个可执行文件,一来解决了环境依赖的问题,二来解决了大家关心的源码保护的问题. 在pkg 模块的 README 中,罗列了它的几大用处,如果你有下面的几个需求的话建议不妨试试. 为应用提供商业发行版而不用暴露源码 为应用提供 demo 而

  • electron打包vue项目的方法 步骤

    目录 创建项目 添加electron-builder electron下载失败 窗体运行 打包exe 白屏 创建项目 点击这里 添加electron-builder 1.在项目目录下运行命令:vue add electron-builder2.electron-builder添加完成后会选择electron版本,直接选择最新版: electron下载失败 vue add electron-builder下载electron会下载失败,使用淘宝镜像下载:cnpm i electron 窗体运行 1

  • 用Cordova打包Vue项目的方法步骤

    现在国内越来越多的开发者使用Vue开发混合app,但是当大家开发完成过后才发现不知道该怎么将Vue项目打包成app. 现在的打包Vue项目目前流行的就是使用weex和cordova.weex是阿里提供并且Vue的作者也极力推荐的,有兴趣的可以去学习使用一下.下面说说怎么使用cordova打包Vue项目: 第一步:安装cordova,创建好cordova项目. 第二步:修改vue项目 首先修改vue项目的index.html,引入cordova.js.这个引入在浏览器打开会报错.要打包后运行在真机

  • vue项目打包发布上线的方法步骤

    目录 一.开发环境到生产环境的转变 二.设置统一的请求路径 三.运行打包命令 vue项目开发完成后,我们需要将项目打包上线,同时我们希望可以在本地预览生产环境项目 (以vue-cli脚手架生成的项目为例) 一.开发环境到生产环境的转变 项目开发结束之后,首先我们需要通知后端,获取一个线上的路径,之后将之前的开发路径切换为线上路径. 打开项目中config文件夹里面的 dev.env.js 文件,将后端给的线上路径填入. 'use strict' module.exports = { NODE_E

  • jenkins自动构建发布vue项目的方法步骤

    简介 Jenkins是一个开源的.提供友好操作界面的持续集成(CI)工具,起源于Hudson(Hudson是商用的),主要用于持续.自动的构建/测试软件项目.监控外部任务的运行(这个比较抽象,暂且写上,不做解释).Jenkins用Java语言编写,可在Tomcat等流行的servlet容器中运行,也可独立运行.通常与版本管理工具(SCM).构建工具结合使用.常用的版本控制工具有SVN.GIT,构建工具有Maven.Ant.Gradle. jenkins安装 1.安装JDK yum install

  • Linux下部署springboot项目的方法步骤

    最近在研究将springboot项目部署到Linux服务器上,由于springboot是内嵌了tomcat,所以可以直接将项目打包上传至服务器上. 1.在pom文件中添加springboot的maven插件 <build> <plugins> <plugin> <groupid>org.springframework.boot</groupid> spring-boot-maven-plugin</artifactid> </p

  • Maven搭建springboot项目的方法步骤

    Maven搭建springboot项目 本文是基于Windows 10系统环境,使用Maven搭建springboot项目 Windows 10 apache-maven-3.6.0 IntelliJ IDEA 2018.3.4 x64 一.springboot项目搭建 (1) 新建目录 在某个可用目录下,新建一个文件夹,本文新建目录为 D:\demo\zs200 (2) 创建maven父工程zs200a-parent 填写项目maven坐标 填写项目名称和路径 (2) maven父工程zs20

  • idea将maven项目改成Spring boot项目的方法步骤

    1.添加parent父级依赖 在pom.xml文件中,要首先添加parent父级依赖 <!-- 这个parent是springboot的父级依赖, 它提供相关的starter的maven管理以及版本号管理,还有相关maven插件的公共配置 --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artif

  • 使用docker部署dubbo项目的方法步骤

    1.首先用springboot构建一个简单的dubbo测试程序,并引入相关依赖 编写公共接口api 编写provider实现UserSvice的方法,并暴露服务 编写provider的配置文件 编写Consumer 通过调用provider的服务获取user信息并返回 consumer的配置文件 测试程序已完成 在本地启动,看看程序是否能正常调用服务 启动zookeeper 先启动provider端再启动consumer端 通过dubbo的控制台看到我们的服务已经注册成功 通过访问本地,看到我们

  • Gitlab CI-CD自动化部署SpringBoot项目的方法步骤

    目录 一.概述 二.前期准备 三.总体架构图 四.环境搭建 1.环境准备(可选) 2.Gitlab安装 3.安装 Runner 4.安装应用服务器环境 五.创建 SpringBoot 项目 1.使用Gitlab Spring 模板快速创建一个 SpringBoot 项目: 2.添加环境变量(登录应用服务器密码) 六.总结 一.概述 本文主要记录如何通过Gitlab CI/CD自动部署SpringBoot项目jar包. 二.前期准备 准备三台 CentOS7服务器,分别部署以下服务: 序号 系统

随机推荐