目录
- 课程目标
- 背景
- 双线程架构
- WebView 结构
- 快速渲染 PageFrame
- 编译原理
- Exparser
- 通讯系统
- 生命周期
- 基础库解包
- 跨端框架
- 预编译
- 半编译半运行
- 运行时框架
- 主流技术
- Taro
- uni-app
- 汇总
- 下周安排
课程目标
本次课程主要通过后台管理小程序回顾一下小程序的高阶语法,然后讲解整体小程序流程原理:
- 前端相关的编码规范、设计规范
- 页面切换、生命周期、数据通信等基础知识
- 双线程架构、webview 渲染、PageFrame 模板、通讯系统、生命周期、跨端框架等
- 掘金小册推荐 https://juejin.cn/book/6982013809212784676
背景
进程:操作系统进行进行资源分配和调度的基本单位
线程:操作系统能够进行运算调度的最小单位,其是进程中的一个执行任务(控制单元),负责当前进程中程序的执行
- 微信开发者工具底层是 Chrome XWeb 内核,进行开发跨平台的桌面应用(如QQ、WeChat客户端)。
- 左上角选择,
微信开发者工具 -> 调试 -> 调试微信开发者工具
,和谷歌调试界面几乎一模一样(利用这个工具,我们后续可以处理一些内部报错的调试BUG)。 - 接下来,我们利用脚本遍历开发工具的 webview 元素。
// 获取所有的 webviewdocument.getElementsByTagName('webview')// 打开调试的 webviewdocument.getElementsByTagName('webview')[0].showDevTools(true, null)
- webview 的含义如下:当前小程序的页面 两个渲染层 逻辑层 调试器
- 新开一个页面就会新增一个 webview,微信限制最多10个以保证性能问题的底层原因
- 利用命令打开一个 webview 页面进行具体的调试
- 页面的样式、调试库、渲染库、开发工具的配置…
- body 下面的标签就是利用 小程序开发的 Exparser 框架将 dom 转换为自定义组件的一套规则
- 每个 webview 都加载了这么多文件,是如何保证高效运作的?
快速渲染 PageFrame
- 利用 page frame 模板生成 webview 视图。
- 基本的 webview 模板和之前打开的一致,但是包含一些注释占位符,后续会被编译为具体的执行 js 文件。
加载流程如下:
- 首页启动时,即第一次通过
pageframe.html
生成内容后,后台服务会缓存pageframe.html
模板首次生成的html内容。- 非首次新打开页面时,页面请求的
pageframe.html
内容直接走后台缓存。 - 非首次新打开页面时,
pageframe.html
页面引入的外链js资源(如上图所示)走本地缓存。
- 非首次新打开页面时,页面请求的
- 生成 webview 模板后,初始化 webview 地址
http://127.0.0.1:${global.proxyPort}/aboutblank?${c}
空地址,其中${c}
为对应 webview 的 id。 - 监听页面的 ready 操作,注入执行代码脚本生成最终的 webview 地址和执行代码。
编译原理
基础语法不作过多赘述,主要编译原理和rpx动态适配。WXSS/WXML
并不可以直接执行在webview
层进行渲染,通过wcsc/wcc
执行脚本编译为js文件,然后注入webview中,这里可以演示一下。
// help() 查看编译命令help()// 编译 WXSS 命令./wcsc -js index.wxss >> style.js // 编译 WXML 命令./wcc -js index.wxml >> test.js// 执行 $gwx 文件var decodeName = decodeURI("./pages/home-tab/index.wxml")var generateFunc = $gwx(decodeName)generateFunc()
var eps = 1e-4var transformRPX = window.__transformRpx__ || function (number, newDeviceWidth) { if (number === 0) return 0 number = (number / BASE_DEVICE_WIDTH) * (newDeviceWidth || deviceWidth) number = Math.floor(number + eps) if (number === 0) { if (deviceDPR === 1 || !isIOS) { return 1 } else { return 0.5 } } return number }
-
wcsc 将 WXSS 编译为 JS 文件。
-
JS 文件注入到 WebView 中。
-
逻辑层执行 JS 文件,主要是设备信息获取(宽度、高度、横屏)、特定规则 rpx 适配为 px,写入编译后的 CSS 文件到 head style 头部。
-
WCC 将 WXML 编译为 JS 文件。
-
但是文件作了压缩混淆,本质逻辑执行
$gwx
函数。 -
generateFunc
就是接受动态数据,并生成虚拟 DOM 树的函数,DOM 树已存在的数据直接渲染为 wx-view/wx-text,需要 JS 脚本异步获取的数据采取 tag: virtual 虚拟 DOM 元素进行占位,等到获取后端数据之后直接填充 => 真实 DOM。
Exparser
WebComponents Shadow Dom
Exparser是微信小程序的组件组织框架,内置在小程序基础库中,为小程序提供各种各样的组件支撑。内置组件和自定义组件都有Exparser组织管理。
这部分后面单独抽离一篇文章进行讲解…
通讯系统
在正式讲解小程序的通讯系统前,先来熟悉一下小程序的 发布 - 订阅模式
美团一面:你了解发布-订阅模式吗?「每天搞透一道JS手写题💪Day8」 - 掘金
概述:引入中间平台进行注册和通知,有效解决了观察者维护列表导致的解耦不彻底问题
观察者通过 on 向 EventBus 注册事件,然后 Subject 通过 emit 向 EventBus 发射事件,由 EventBus 来向观察者更新。本质上是维护一个 events 对象,通过事件名注入到 events,每个 事件名 里面都有相应的回调函数,发布之后会分发到相应事件 回调函数 的方法。
class PubSub { constructor() { this.events = {}; } subscribe(event, callback) { if (typeof event !== 'string') { throw new Error('Event name must be a string'); } if (typeof callback !== 'function') { throw new Error('Callback must be a function'); } if (!this.events[event]) { this.events[event] = []; } this.events[event].push(callback); } unsubscribe(event, callback) { if (typeof event !== 'string') { throw new Error('Event name must be a string'); } if (typeof callback !== 'function') { throw new Error('Callback must be a function'); } if (!this.events[event]) return; this.events[event] = this.events[event].filter(cb => cb !== callback); } publish(event, data) { if (typeof event !== 'string') { throw new Error('Event name must be a string'); } if (!this.events[event]) return; this.events[event].forEach(callback => callback(data)); }}
微信开发者工具里面选择的基础库为微信中的基础库版本(后面标识了兼容性和用户的数量),可用于微信开发者工具内的调试。每个版本的微信客户端都会自带一个版本的小程序基础库,而不是微信客户端带着所有版本的基础库,所以开发时选择的版本尽量和客户端的一致(最新的为好),避免 API 兼容性问题。
接下来,我们利用 openVendor() 找到 基础库进行解析,找到原始的 wxvpkg 然后利用 Github 工具进行反编译出源码,https://github.com/csj5588/wxappUnpacker
构建后的结果在 dist 文件下,本质上还是渲染层和逻辑层两个线程脚本之间的交互流程,后续在深入探讨。
跨端框架
由于小程序语法较为原始,工程化工具不支持,市面上太多平台的小程序需要适配开发,基于此市面上的第三方框架也慢慢进入大众的视野,目前来说流行的就是 taro 和 uni-app。
下面先来介绍下框架的分类:
预编译
本质上是利用 DSL(语法规则) + 语法解析,将一些逻辑转换为小程序支持的语法,但是这样存在一些问题:
- 如果React或者Vue后期再出一些新特性的话,预编译框架都需要进行语法解析扩展编写。
- 兼容问题,比如小程序不支持的一些属性,如果不支持,预编译框架要进行兼容。
半编译半运行
Taro 介绍 | Taro 文档
Taro 特点总结
- 采用 TypeScript (TS) 加上部分 Rust 进行开发
- 底层标准参考 微信小程序,兼容性较好,其他平台的兼容性未知
- 对 React 和 Webpack 语法有良好的兼容性支持,Vue 3 加上 Vite 新推出,也可以用
- 调试开发需要下载不同平台的工具,无法直接导出运行
- 在 GitHub 上搜索近两年 stars 30+ 的开源项目,资源只有一页,显示资源较少
- 不支持使用 Vue 3 的 scoped CSS 样式隔离语法,这可能需要更详细的介绍
资源链接
Taro UI | O2Team
NutUI - 移动端组件库
组件库选择建议
- Taro 组件库:由 Taro 维护,但不推荐安装
- NutUI 组件库:多端组件库分离,推荐作为开发的首选
uni-app
uni-app官网
相关资源链接
- uview-plus 3.0 - 全面兼容nvue的uni-app生态框架 - uni-app UI框架
- GitHub - Moonofweisheng/wot-design-uni: 一个基于Vue3+TS开发的uni-app组件库,提供70+高质量组件,支持暗黑模式、国际化和自定义主题。
- 页面 | uni-app官网
uni-app 特点总结
本质上有两种体系:
- 一种是 uniapp + HBuilder 工具构建的一套自己的规范,其中包含:uts(TypeScript 超集)、uni-modules (依赖管理)、uvue(编译语言)、uniCloud(云服务),最终这些被 uni-app x 统一集成,需要重新学习一套标准、时间成本和难度过高,编辑器也不如前端常用的编辑器 VSCode、WebStorm
- 另一种可以借助原本的命令行执行,和其他的前端项目一致,可以借助第三方优质模板,但是注意 app 部分打包仍然需要 HBuilder 开发工具(推荐)
汇总
维度 | Taro | 得分 | Uni-app | 得分 |
---|---|---|---|---|
社区文档 | 清晰明确 | 8 | 较为混乱、自有体系和前端不兼容 | 6 |
上手成本 | VUE 版本上手容易、React 较高 | 8 | 底层采用 VUE 开发,熟悉程度更高 | 9 |
开源项目 pushed:>2023-09-01 stars:>30 | 68 | 7 | 143 | 10 |
Github 活跃 | stars:35.3k+ issues:1.2k + 1w+ | 10 | stars:39.9k+ issues:0.9k + 3k+ | 8 |
三方组件库 | 官方组件库统一,资源优质 NutUI VUE 6k+ | 9 | 官方组件库较为简陋、社区UI生态较差、大部分收费、一部分不敢用 | 5 |
TS 支持 | 兼容性优秀 | 9 | 兼容性优秀 | 8 |
VUE 语法兼容 | 兼容大部分 VUE 语法 | 7 | 兼容大部分 VUE 语法 | 8 |
React 语法兼容 | 支持 | 0 | 不支持 | 0 |
总计 | 🥇🏅🎖️ | 58 | 54 |
跨端开发框架深度横评
下周安排
开源商城项目拆解
- 腾讯有数
- gz-yami - Overview
- EastWorld