《微信小程序七日谈》- 第六天:小程序devtool隐藏的秘密
《微信小程序七日谈》系列文章:
本系列的文章并非初学教程,而是笔者在具体开发过程中遇到的问题以及部分解决方案。
笔者参与的小程序项目开发也进入尾声了,坑也踩得七七八八,对于哪些没有涵盖和深入使用的功能笔者就不班门弄斧了。
前几篇文章讲了那么多细节也好,策略也好,都是应用层面的东西。自微信小程序公布以来就有先行者不断的探索小程序背后的运行机制。小程序的开发语法和API与前端工程师熟悉的html/js/css非常相似,所以会令很多人疑惑小程序与普通的HTML5应用到底有什么区别。这篇文章其实将小程序的基本运行机制剖析的差不多了,简单概括就是:
- 大部分内容会转化为常规的html/css/js,并使用webview渲染;
- 部分组件调用native实现功能。
既然已经有先行者得出了结论,为啥还要写这篇文章呢?当然是为了凑齐七篇啦,哈哈哈...
开玩笑!这篇文章的目的不是重复别人的结论,而是将笔者研究小程序开发工具源码的一些心得和结论记录下来,以方便大家后续更深入的探索。
看看devtool的源码有什么
第一步是找到小程序devtool的源码,以mac系统为例,源码的打开方式如下图:
其他的文件不用理会,我们要研究的主要代码在Content/Resources/app.nw/dist
目录下,这个目录包括devtool的功能代码以及对小程序进行执行、编译、打包、上传等功能的代码。当然,这些代码都是经过混淆的,读起来还挺费劲(摊手~
需要着重注意的是Content/Resources/app.nw/dist/weapp
目录,以及commit和trans两个子目录:
从文件的命名上大致可以猜到每个文件对应的功能:
- trans目录下的文件负责将小程序源码进行一系列的转换,最终被转换为浏览器可识别的html、css以及js;
- commit目录下的文件负责执行小程序的构建、打包、上传等功能。
既然我们知道小程序会进行构建打包流程,想得知小程序运行机制最好的办法就是研究构建完毕之后的代码。有了这个目标之后,下一步就是查看devtool的日志获取小程序构建之后的代码存放位置。
找到日志文件
Content/Resources/app.nw/dist/common/log/log.js
是负责管理devtool日志功能的文件,其中有这样一段代码:
const a = require('fs'),
b = require('log'),
c = require('path'),
d = require('../../config/dirConfig.js'),
e = d.WeappLog;
其中e
便是日志文件的存放目录,然后我们追溯到config/dirConfig.js
中发现目录路径是由nw.App.getDataPath()
生成的,这个函数是node-webkit提供的API,生成结果的规则在不同的操作系统下有差异,可惜笔者并没有找到相关的说明(沮丧)。
但是此次代码的探索并非没有收获,起码我们知道了日志文件存放的目录名为“WeappLog”,我们可以使用强大的命令行从硬盘中搜索此目录:
mdfind WeappLog
大家可以参考这篇文章了解
mdfind
命令的用法
从输出结果可以得知日志文件在Mac系统的存放目录为/Users/<用户名>/Library/Application Support/微信web开发者工具/WeappLog
。进入目录后就会发现很多以.log
为后缀的日志文件:
上传小程序的流程
打包后的小程序在哪里
找到日志文件后便可以从devtool的执行日志中获取小程序被构建后的代码存放位置。当然,第一步是要讲小程序进行构建,操作方法是在小程序开发工具的“项目”菜单”中点击”预览“:
成功后再日志文件中会出现这么一行记录:
[Wed Jan 18 2017 15:20:24 GMT+0800 (CST)] INFO pack.js create /Users/<用户名>/Library/Application Support/微信web开发者工具/Weappdest/1484724024071.wx success!
/Users/<用户名>/Library/Application Support/微信web开发者工具/Weappdest/1484724024071.wx
就是构建完成的小程序代码!赶紧去看看!
兴致勃勃的找到/Users/<用户名>/Library/Application Support/微信web开发者工具/Weappdest/
目录,然后发现:空空如也!
看来微信团队还是很谨慎的,在将小程序源码上传之后便会删除构建产出的文件。但是这点小伎俩难不倒程序员!任何行为都是程序执行的,我们直接修改相关的程序代码就可以了嘛!
做点小手脚,看看打包后的代码
在Content/Resources/app.nw/dist/weapp/commit/upload.js
中有一段这样的代码:
const a = require('fs'),
j = require('rmdir');
//省略无关代码
_exports.uploadForTest = (l, m, n) => {
//省略无关代码
c(l, {
noCompile: !0
}, (s, t) => {
if (s) return void n(s.toString());
let u = d.join(k, `${+new Date}.wx`);
b(t, u, (v, w) => {
j(t, (A, B, C) => {});
//省略无关代码
if (y > q) return a.unlink(u, () => {}), void n(`代码包大小为 ${y} kb,超出限制 ${y-q} kb,请删除文件后重试`);
//省略无关代码
})
上述代码省略了一些与我们当前讨论内容无关的代码,感兴趣的读者可以自行研究。
上述代码有两个删除文件的行为:
-
rmdir
:删除构建完成但是并未打包的代码目录; -
fs.unlink
:删除打包完成的文件。
将执行删除的代码注释以后,再通过小程序开发者工具进行预览上传操作后,在上文中我们得到的目录中便会留下构建以及打包后的文件了。如下:
其中以.wx
为后缀的文件是经过打包后的文件,也就是上传到微信服务器的文件。其同名的目录文件夹是构建完成且打包之前的源文件。
以config.js
为例,构建后的代码如下:
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
exports.default = {
basePath: 'https://djtest.cn',
fileBasePath: 'https://djtest.cn'
};
其实仅仅将ES6的语法转译成了ES5语法。其余的wxml、wxss以及js文件基本也是这样的状态,所以可以推断源码上传至微信服务器后会执行真正的构建动作,开发工具只执行了一些简单地构建行为。
虽然笔者并未从这份代码中得到全部的真相,但希望这篇文章能够给后续的探索者提供一些微薄的帮助。
- ASP.NET Core应用的错误处理[2]:DeveloperExceptionPageMiddleware中间件如何呈现“开发者异常页面”
- RSA简介(二)——模幂算法
- 为虚拟机vCPU绑定物理CPU
- RSA简介(三)——寻找质数
- RSA简介(四)——求逆算法
- 平方根的C语言实现(三) ——最终程序实现
- ASP.NET Core应用的错误处理[3]:ExceptionHandlerMiddleware中间件如何呈现“定制化错误页面”
- 【视频】Es6新特性-Symbol
- shell编程/字库裁剪(3)——验证
- 特征选择(Feature Selection)引言
- shell编程/字库裁剪(2)——编程过程
- 使用mock来编译和管理RPM软件包
- C语言/原子/编译,你真的明白了吗?
- ASP.NET Core应用的错误处理[4]:StatusCodePagesMiddleware中间件如何针对响应码呈现错误页面
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Python 基础(十三):os 模块
- Python 基础(十四):错误和异常
- Python 基础(十五):枚举
- 一个老程序员的30年生涯回顾
- 经典 90 坦克大战 Python 版实现(支持单双人模式)
- Python 基础(十六):迭代器与生成器
- MySQL information_schema详解 COLUMNS
- MySQL information_schema详解 COLUMN_PRIVILEGES
- 一分钟学Python|Python的字典
- MySQL information_schema详解 ENGINES
- 一日一技:不用轮询,基于事件监控文件变动
- Python 基础(十七):装饰器
- XtraBackup工具详解 Part 10 使用innobackupex对数据库进行部分备份(指定表或数据库)
- Python 基础(十九):数学相关模块
- XtraBackup工具详解 Part 11 使用innobackupex对部分备份进行恢复