阿里、蚂蚁、晟腾、中科加禾精彩分享 AI 基础设施洞见,现购票可享受 9 折优惠 |AICon 了解详情
写点什么

实现微信小程序编译和运行环境系列 (核心篇三)

  • 2020-06-22
  • 本文字数:3690 字

    阅读完需:约 12 分钟

实现微信小程序编译和运行环境系列(核心篇三)

本章节就带大家通过微信官方的创建项目部分代码来讲解一下这些对外 api 如何通过我们自己方式来实现和微信相同的功能操作,我们通过微信开发者工具来自动创建一个默认的小程序项目 一个首页和日志页。



这个项目大家应该都比较熟悉吧,应该第一次接触小程序开始时引入眼前的场景,具体的其他内容我们就不在这里啰嗦了,直接看下它的 app.js 文件,编辑器打开后可以看到里面写了这些.


 //app.jsApp({  onLaunch: function () {    // 展示本地存储能力    var logs = wx.getStorageSync('logs') || []    logs.unshift(Date.now())    wx.setStorageSync('logs', logs)
// 登录 wx.login({ success: res => { // 发送 res.code 到后台换取 openId, sessionKey, unionId } }) // 获取用户信息 wx.getSetting({ success: res => { if (res.authSetting['scope.userInfo']) { // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框 wx.getUserInfo({ success: res => { } }) } } }) }, globalData: { userInfo: null }})
复制代码


先给大家把接下来部分要说明的消息事件整理出来让大家可以看的清楚一些,后面的内容主要是围绕这个消息事件和代码来说明



上面这个图就是从微信开发者工具里面打开页面时候出现的事件和消息类型以及有关数据,分析一下可以看出:


1、首先调用了同步 api getSystemInfo,我们可以看到在我们的项目代码里面没有存在这个 api,但是开发者工具第一步就调用了,所以在项目编译初始化的时候就触发了,至于 Receive 的数据返回的就是工具界面左上角的一些配置数据,大家可以打开自己的开发者工具看看,一些参数是你可以直接从上面观察到的,如手机型号 屏幕占比 网络类型等,这个同步 api 是系统自动调用的不用我们做写什么操作,而且它会比较频繁的调用。(这点我也没理解,当我打开控制台的时候他会调用很多次)


  • 在前面的文章中讲到同步的 api 都是通过走 http 协议’/apihelper/assdk’链接过来的,里面携带了 api 名和一些参数,所以我们可以直接拦截’/apihelper/assdk’请求进行处理


实例代码展示


     router.post('/apihelper/assdk', async (ctx, next) => {    const { api, args } = JSON.parse(ctx.request.body);    if (api === 'getSystemInfo') {      ctx.body = {        'errMsg': 'getSystemInfo:ok',        'model': 'iPhone 7 Plus',        'pixelRatio': 3,        'windowWidth': 414,        'windowHeight': 624,        'system': 'iOS 10.0.1',        'language': 'en',        'version': '7.0.4',        'screenWidth': 414,        'screenHeight': 736,        'SDKVersion': '2.7.1',        'brand': 'devtools',        'fontSizeSetting': 16,        'benchmarkLevel': 1,        'batteryLevel': 100,        'statusBarHeight': 20,        'safeArea': { 'right': 414, 'bottom': 736, 'left': 0, 'top': 20, 'width': 414, 'height': 716 }      };    }    })
复制代码


ctx.body 的结果集可以直接复制开发者工具中的数据就可以,如果要实际使用的话需要把部分参数改成动态获取的。


2、当代码执行 wx.getStorageSync(‘logs’) wx.setStorageSync(‘logs’, logs)的时候(标签二)通过前面的日志描述可以知道它们都是 appservice sdk 调用的同步的 api,就是我们普通使用的 Storage 它们包装做了一层转换,因为这个页面是初始化的时候调用的所以这里面的数据都是空的。当点击 log 页面的时候,就可以看到调用了 APPSERVICE_PUBLISH 事件类型,此类型是 view 层发给 service 层的我们看到它的 webviewID 存在值。



看出 data 里面的数据就是 Storage 存储的 Date.now()时间, wx.setStorageSync wx.getStorageSync 在源码对应的地方。




var r = re.IS_IOS ? "setStorage" : "setStorageSync"var r = re.IS_IOS ? "getStorage" : "getStorageSync"
复制代码


看出如果是 ios 的话就只能调用 setStorage getStorage 方式。


模拟代码展示:


const storage = new Map();
const getStorage = function (key) { return storage.get(key);};const setStorage = function (key, value) { storage.set(key, value);};const removeStorage = function (key) { storage.delete(key);};
module.exports = { getStorage, setStorage, removeStorage};
复制代码


如果想模拟全局的存储简单的可以直接使用 map 进行操作,然后在调用的时候获取对应的 api 进行获取和设置操作,完善的话存储可以采用浏览器的 LocalStorage,SessionStorage 或者一些 npm 包进行处理。


3、我们比较常用的一个 api 通过 wx.login 换取 code 可以看出(标签三)中,是通过逻辑层 service 自发自收的,发起一个请求通过 service 逻辑层处理后在回调回去,通过微信 api 源码可以看到调用的地方



这一块要想要弄的很好不是很容易,涉及到用户体系就要和服务端彻底打通,好在登录授权这块微信也是采用 OAuth2 协议实现的


如果有朋友对 OAuth2 还不是很了解明白的话,建议看下这个文档比市面上 90%的相关内容讲的更彻底通透。


4、当在小程序中打开页面时触发了 onAppRoute 事件,通过日志看出发送了 APPSERVICE_ON_EVENT 事件,path 表示当前页面,openType 表示操作类型,openType 如果不是很明显的话我们试着点击一下,从日志页面返回首页的后退操作。



看到"openType":"navigateBack"这个应该比较好理解解释了。


到这里我们会有一些不一样的地方了,因为这里的操作的是事件处理不是简单的 api 处理就 ok 了,所有前端页面操作的控制器都是一个整体,首先我们要先搭建这个载体容器存放各个部分。


模拟代码实例:


构建一个总管理处理的信息


constructor(wxConfig = {}, socketPort) {    super();    this.wxConfig = wxConfig;    this.systemManager = new SystemManager(this, wxConfig);    this.navigatorManager = new NavigatorManager(this, wxConfig);    this.pageManager = new PageManager(wxConfig, socketPort);    this.tabbarManager = new TabbarManager(this, wxConfig);    this._render();    this._launch();    window.socketClient.setEmitter(this);  }
复制代码


然后 render 页面信息,结合 node 后台服务渲染前端展示


    this.domElement = document.createElement('div');    this.domElement.id = 'container';    this.domElement.style = ` height: ${global.simulator.height}px; width: ${global.simulator.width}px;`    // system    this.domElement.appendChild(this.systemManager.domElement);    // navigator    this.domElement.appendChild(this.navigatorManager.domElement);    // pages    this.domElement.appendChild(this.pageManager.domElement);    // tabbar    this.domElement.appendChild(this.tabbarManager.domElement);
复制代码


后面就是监听各个事件控制和各种业务处理,核心还是要按照开发者工具的消息顺序和内容来实现。


例如上面我们提到的 navigateBack 我们自己这边实现按照平常的业务写法就 ok。


  navigateBack (path, query) {    let currentWebview = this.domElement.children.item(0);    let currentIndex = currentWebview.style['z-index'];    this.domElement.removeChild(currentWebview);
let targetPage = null; for (let i = 0; i < this.domElement.children.length; i++) { let webview = this.domElement.children.item(i); if (webview.style['z-index'] === `${currentIndex - 1}`) {
let viewId = +webview.getAttribute('data-view-id'); let path = webview.getAttribute('data-view-path'); let query = JSON.parse(webview.getAttribute('data-view-query')); window.socketClient.send(WebsocketMessage.onAppRoute(viewId, path, query, 'navigateBack')); window.socketClient.send(WebsocketMessage.onAppRouteDone(viewId, path, query, 'navigateBack')); targetPage = path; break; } } return targetPage; }
复制代码


重要的还是 socketClient.send 消息的正确传递才可以和基础库正确的交互


其他的很多对外的 api 实现方式都是大同小异,主要是在接收到消息后怎么处理设计


我们知道了核心的流程,下面要做的就是模仿设计,模仿它的消息格式和返回结构,设计自己的各系统模块的关联


对于小游戏而言大致是一样的,主要有几个点不同:


  1. 小游戏是通过根目录下的 game.json 来对小游戏进行全局配置,决定相关界面渲染和属性设置等;

  2. 在小游戏的运行环境里面不存在 BOM 和 DOM API,只有 wx API 对它们进行了包装,所以无法直接使用;

  3. 小游戏的运行层只有一层在 view 里面跑;

  4. 小游戏的大部分 api 主要都是对文件系统和网络的处理。


上面的一些总结主要是根据一些 api 的实现来描述扩展了一些,看完后希望大家对此有所了解,后面我打算从全局来讲下怎么从代码设计方面来设计整个浏览器运行环境实现方案。


2020-06-22 11:081750

评论

发布
暂无评论
发现更多内容

第 8 周 系统架构总结

心在那片海

Architecture Phase1 Week12:HomeWork

phylony-lu

第3周学习总结

Binary

极客大学架构师训练营

第12周总结

睡觉表演者

极客时间架构师一期

第3周作业提交

Binary

极客大学架构师训练营

单词匹配算法,算法常见的模数1000000007 模数10 ^ 9 + 7,swift mock URLSession,John 易筋 ARTS 打卡 Week 30

John(易筋)

ARTS 打卡计划 单词匹配算法 swift mock数据 算法中的模数

第十二周 作业2

Yangjing

极客大学架构师训练营

第七周作业

Geek_9527

架构师训练营第十二周总结

吴传禹

极客大学架构师训练营

架构师训练营第十二周命题作业

一马行千里

第8周作业

hunk

极客大学架构师训练营

架构师训练营第 1 期 -- 第十二周学习总结

发酵的死神

极客大学架构师训练营

架构师训练营第十二周作业

吴传禹

极客大学架构师训练营

第 8 周 系统架构作业

心在那片海

数据应用 课后练习

ABS

架构设计:企业总体架构要如何做?小白也能快速领悟的设计思想

互联网应用架构

架构设计

Java内存模型

懒AI患者

常量池 Java内存模型 元数据区

Week 8 性能优化

evildracula

学习 架构

大数据概述

garlic

极客大学架构师训练营

第八周作业总结

hunk

极客大学架构师训练营

架构训练营第八周总结

xiaomao

生产环境全链路压测建设历程之八 生产全链路压测和传统压测的差异点

数列科技杨德华

架构师第三周作业

胡益

数据应用(一)

wing

极客大学架构师训练营

第八周作业

Geek_9527

架构师训练营第二期 第 8 周总结

月下独酌

极客大学架构师训练营

架构训练营第八周

xiaomao

Architecture Phase1 Week12:Summarize

phylony-lu

极客大学架构师训练营

架构师训练营第 1 期 -- 第十二周作业

发酵的死神

极客大学架构师训练营

架构师训练营第2期 第8周命题作业

月下独酌

极客大学架构师训练营

架构师训练营第 1 期 - week12 - 作业

lucian

极客大学架构师训练营

实现微信小程序编译和运行环境系列(核心篇三)_语言 & 开发_风逝_InfoQ精选文章