一、写作背景
接触小程序一年多,真实体验就是小程序开发门槛相对而言确实比较低。不过小程序的开发方式,一直是开发者吐槽的,如习惯了 Vue,React 开发的开发者经常会吐槽小程序一个 Page 必须由多个文件组成,组件化支持不完善或者说不能非常愉快的开发组件。在以前小项目中没太大感觉,从加入有赞,参与有赞微商城小程序的开发,是真切的体会到对于大型小程序项目开发的复杂性。
有赞从微信小程序内测就开始开发小程序,在不支持自定义组件的时代,只能通过 import 的形式拆分模块或实现组件。在业务复杂的页面,可能会 import 非常多的模块,而相应的 wxss 也需要 import 样式,除了操作繁琐,有时候也难免遗漏。
作为开发者,我们当然希望可以让工作更简单,更愉快,也希望改善我们的开发方式。所以希望能够更了解微信小程序框架,减少不必要的试错,于是有了一次对小程序框架的 debug 之旅。(基础库 1.9.93)
通过三周空余时间的 debug,也算对小程序框架有了一些浅显的认识,达到了最初的目的;对小程序启动,实例,运行等有了真切的体会。这篇文章记录了小程序框架的基本代码结构,启动流程,以及程序实例化过程。
本文的目的是希望把我看到的分享给对小程序感兴趣或者正在开发小程序的读者,主要解答“框架对传入的对象等到底做了什么”。
二、从启动流程一窥小程序框架细节
在开发者工具中使用 help() 方法,可以查看一些指令和方法。使用其中的 openVendor 方法可以打开微信开发者工具在小程序框架所在目录。其中以包括以基础库命名的目录和其他帮助文件,如其中有两个工具 wcc,wcsc。wcc 可把 wxml 转换为对应的 JS 函数 —— $gwx(path, global),wcsc 可将 wxss 转换为 css。而基础库目录包括 WAService.js 和 WAWebview.js 文件。小程序框架在开发者工具中以 WAService.js 命名(WAWebview.js 不知其作用,听说在真机环境使用该文件)。
在开发中工具命令行使用 document.head 可以查看到小程序的启动流程大致如下:
以小节的方式分别介绍这些流程,小程序是如何处理的(小节编号与图中编号相同)。
1、初始化全局变量
下图是小程序启动是初始化的一些全局的变量:
那些使用“__”开头,未在文档中提及可使用变量是不建议使用的,__wxAppCode__ 在开发者工具中分为两类值,json 类型和 wxml 类型。以 .json 结尾的,其 key 值为开发者代码中对应的 json 文件的内容,.wxml 结尾的,其 key 值为通过调用 $gwx('./pages/example/index.wxml') 将得到一个可执行函数,通过调用这个函数可得到一个标识节点关系的 JSON 树。
2、加载框架(WAService.js)
使用工具对 WAService.js 进行格式化后进行 debug。可以发现小程序框架大致由: WeixinJSBridge
、 NativeBuffer
、 wxConsole
、 WeixinWorker
、 JavaScript兼容
(这部分为猜测)、 Reporter
、 wx
、 exparser
、 __virtualDOM__
、 __appServiceEngine__
几部分组成。
其中除了 wx
和 WeixinJSBridge
这两个基础 API 集合, exparser
, __virtualDOM__
, __appServiceEngine__
这三部分作为框架的核心, __appServiceEngine__
提供了框架最基本的接口如 App,Page,Component; exparser
提供了框架底层的能力,如实例化组件,数据变化监听,view 层与逻辑层的交互等;而 __virtualDOM__
则起着链接 __appServiceEngine__
和 exparser
的作用,如对开发者传入 Page 方法的对象进行格式化再传入 exparser 的对应方法处理。
框架对外暴露了以下API:Behavior,App,Page,Component,getApp,getCurrentPages,definePlugin,requirePlugin,wx。
3、业务代码的加载
在小程序中,开发者的 JavaScript 代码会被打包为
define('xxx.js', function(require, module, exports, window, document, frames, self, location, navigator, localStorage, history, Caches, screen, alert, confirm, prompt, fetch, XMLHttpRequest, WebSocket, webkit, WeixinJSCore, Reporter, print, WeixinJSBridge) {
'use strict';
// your code
})
这里的 define 是在框架中定义的方法,在框架中提供了两个方法:require 和 define 用来定义和使用业务代码。其方式有些像 AMD 规范接口,通过 define 定义一个模块,使用 require 来应用一个模块。但是也有很大区别,首先 define 限制了模块可使用的其他模块,如 window,document;其次 require 在使用模块时只会传入 require 和 module,也就是说参数中的其他模块在定义的模块中都是 undefined,这也是不能在开发者工具中获取一些浏览器环境对象的原因。
在小程序中,JavaScript 代码的加载方式和在浏览器中也有些不同,其加载顺序是首先加载项目中其他 js 文件(非注册程序和注册页面的 js 文件),其次是注册程序的 app.js,然后是自定义组件 js 文件,最后才是注册页面的 js 代码。而且小程序对于在 app.js 以及注册页面的 js 代码都会加载完成后立即使用 require 方法执行模块中的程序。其他的代码则需要在程序中使用 require 方法才会被执行。
下面详细介绍了 app.js,自定义组件,页面 js 代码的处理流程。
由于篇幅字数有限,后续内容可查看《从源码看微信小程序启动过程(下)》
https://developers.weixin.qq.com/community/develop/doc/000486bf4dc7901ed578a50db5bc00