【小程序 - 大智慧】深入微信小程序的核心原理

news2024/12/23 9:30:00

5.png


目录

  • 课程目标
  • 背景
  • 双线程架构
  • WebView 结构
  • 快速渲染 PageFrame
  • 编译原理
  • Exparser
  • 通讯系统
  • 生命周期
  • 基础库解包
  • 跨端框架
    • 预编译
    • 半编译半运行
    • 运行时框架
  • 主流技术
    • Taro
    • uni-app
    • 汇总
  • 下周安排


课程目标

本次课程主要通过后台管理小程序回顾一下小程序的高阶语法,然后讲解整体小程序流程原理:

  • 前端相关的编码规范、设计规范
  • 页面切换、生命周期、数据通信等基础知识
  • 双线程架构、webview 渲染、PageFrame 模板、通讯系统、生命周期、跨端框架等
  • 掘金小册推荐 https://juejin.cn/book/6982013809212784676

背景

https://zh.wikipedia.org/zh-cn/%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F

微信小程序于 2017 年上线,当时企业普遍把 H5 作为公众号的引流入口,公众号正火,但是当时由于 H5 不安全、性能不好、占用存储空间大的问题,微信需要设计一个全新的系统进行迭代。

双线程架构

面试官:说说什么是进程?什么是线程?区别? | web前端面试 - 面试官系列

进程:操作系统进行进行资源分配和调度的基本单位

线程:操作系统能够进行运算调度的最小单位,其是进程中的一个执行任务(控制单元),负责当前进程中程序的执行

微信小程序的设计和前端浏览器是不一致的,它隔离了 JS 操作层 和 界面渲染层,前端是互通的,可以通过 window document 任意的调用 dom 元素和执行脚本,但是小程序作为微信的工具,势必不允许像 web 端这样自由的操作空间,所以:

  1. App Service(逻辑层):单纯执行 JavaScript 的沙箱环境,无法操作 Dom 和访问浏览器 Api ,只能借助 Native 异步查询 DOM 元素并操作,小程序的 WXML 和 WXSS 也会被编译为 JS 脚本进行注入操作。由于小程序内部只有一个 App 实例,通过这个 App 主线程 进行调度
  2. Web View(渲染层):单纯页面的展示,模拟 app 多页面模式,具有多个 webview ,可以暂时抽象成 浏览器的 tab 页面(并非 VUE 的单页面结构,像 App 多页面结构)
  3. Native(微信客户端):利用微信缓存机制,提前注入 微信 SDK、接口请求、组件渲染(wx.request、wx.showToast 都是存放在这里)

WebView 结构

  1. 第一阶段是启动一个 WebView,在iOS和 Android 系统上,操作系统启动WebView都需要一小段时间。
  2. 第二阶段是在 WebView 中初始化基础库,此时还会进行一些基础库内部优化,以提升页面渲染性能。
  3. 第三阶段是注入小程序 WXML 结构和 WXSS 样式,使小程序能在接收到页面初始数据之后马上开始渲染页面(这一阶段无法在小程序启动前执行)。
  4. 每一个页面都独立运行在一个页面层级上。小程序启动时仅有一个页面层级,每次调用 wx.navigateTo,都会创建一个新的页面层级,wx.navigateBack 会销毁一个页面层级。

  1. 微信开发者工具底层是 Chrome XWeb 内核,进行开发跨平台的桌面应用(如QQ、WeChat客户端)。
  2. 左上角选择,微信开发者工具 -> 调试 -> 调试微信开发者工具,和谷歌调试界面几乎一模一样(利用这个工具,我们后续可以处理一些内部报错的调试BUG)。
  3. 接下来,我们利用脚本遍历开发工具的 webview 元素。
// 获取所有的 webview
document.getElementsByTagName('webview')

// 打开调试的 webview
document.getElementsByTagName('webview')[0].showDevTools(true, null)
  1. webview 的含义如下:当前小程序的页面 两个渲染层 逻辑层 调试器
  2. 新开一个页面就会新增一个 webview,微信限制最多10个以保证性能问题的底层原因
  3. 利用命令打开一个 webview 页面进行具体的调试
  4. 页面的样式、调试库、渲染库、开发工具的配置…
  5. body 下面的标签就是利用 小程序开发的 Exparser 框架将 dom 转换为自定义组件的一套规则
  6. 每个 webview 都加载了这么多文件,是如何保证高效运作的?

快速渲染 PageFrame

  1. 利用 page frame 模板生成 webview 视图。
  2. 基本的 webview 模板和之前打开的一致,但是包含一些注释占位符,后续会被编译为具体的执行 js 文件。

加载流程如下:

  1. 首页启动时,即第一次通过 pageframe.html 生成内容后,后台服务会缓存 pageframe.html 模板首次生成的html内容。
    1. 非首次新打开页面时,页面请求的 pageframe.html 内容直接走后台缓存。
    2. 非首次新打开页面时,pageframe.html 页面引入的外链js资源(如上图所示)走本地缓存。
  2. 生成 webview 模板后,初始化 webview 地址 http://127.0.0.1:${global.proxyPort}/aboutblank?${c} 空地址,其中 ${c} 为对应 webview 的 id。
  3. 监听页面的 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-4
var 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
  }
  1. wcsc 将 WXSS 编译为 JS 文件。

  2. JS 文件注入到 WebView 中。

  3. 逻辑层执行 JS 文件,主要是设备信息获取(宽度、高度、横屏)、特定规则 rpx 适配为 px,写入编译后的 CSS 文件到 head style 头部。

  4. WCC 将 WXML 编译为 JS 文件。

  5. 但是文件作了压缩混淆,本质逻辑执行 $gwx 函数。

  6. generateFunc 就是接受动态数据,并生成虚拟 DOM 树的函数,DOM 树已存在的数据直接渲染为 wx-view/wx-text,需要 JS 脚本异步获取的数据采取 tag: virtual 虚拟 DOM 元素进行占位,等到获取后端数据之后直接填充 => 真实 DOM。

Exparser

WebComponents Shadow Dom

Exparser是微信小程序的组件组织框架,内置在小程序基础库中,为小程序提供各种各样的组件支撑。内置组件和自定义组件都有Exparser组织管理。

这部分后面单独抽离一篇文章进行讲解…

通讯系统

在正式讲解小程序的通讯系统前,先来熟悉一下小程序的 发布 - 订阅模式

美团一面:你了解发布-订阅模式吗?「每天搞透一道JS手写题💪Day8」 - 掘金

概述:引入中间平台进行注册和通知,有效解决了观察者维护列表导致的解耦不彻底问题

观察者通过 onEventBus 注册事件,然后 Subject 通过 emitEventBus 发射事件,由 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));
  }
}

在微信小程序执行过程中,Native层(客户端层)分别向渲染层与逻辑层注入 WeixinJSBridge 以达到线程通讯的目的,前面也提到了 webview 脚本注入的原理,WeixinJSBridge 提供了如下几个方法:

  • invoke - 调用 Native API,以api方式调用开发工具提供的基础能力,并提供对应api执行后的回调
  • invokeCallbackHandler - Native 传递 invoke 方法回调结果
  • on - 用来收集小程序开发者工具触发的事件回调
  • publish - 渲染层发布消息,用来向逻辑业务层发送消息,也就是说要调用逻辑层的事件方法
  • subscribe - 订阅逻辑层消息
  • subscribeHandler - 视图层和逻辑层消息订阅转发
  • setCustomPublishHandler - 自定义消息转发

  1. WXML 渲染为虚拟 DOM,添加 addEventListener 进行事件解析。
  2. 逻辑层解析的时候会利用 JSBridge subscribe 订阅事件名称到渲染层,执行逻辑还在逻辑层,只是把名称和具体代码在渲染层作了映射。
  3. 触发事件,渲染层发布消息 publish 携带方法名经过 JSBridge 到达逻辑层。
  4. 逻辑层执行将 Data 以 JSON 字符串格式的数据经过 JSBridge 渲染给 WXML。

生命周期

  1. onLaunch(App onLaunch) App 主应用开始初始化,逻辑线程开始执行。
  2. onLoad(Object query) 页面加载时触发,一个页面只会调用一次,可以在onLoad的参数中获取打开当前页面路径中的参数。
  3. onShow() 页面显示/切入前台时触发。
  4. onHide() 页面隐藏/切入后台时触发。如 wx.navigateTo 或底部 tab 切换到其他页面,小程序切入后台等。
  5. onReady() 页面初次渲染完成时触发。一个页面只会调用一次,代表页面已经准备妥当,可以和视图层进行交互。
  6. onUnload() 页面卸载时触发。如 wx.redirectTowx.navigateBack 到其他页面时。
  • wx.navigateTo 方式是创建新的 webview(添加新的页面栈,新加入的页面从头渲染,之前的页面 onHide 隐藏,为了使用 navigateBack 走缓存 onShow)。
  • wx.redirectTo 以及 wx.navigateBack 是通过更新自身 webview 进行页面转换的,所以当前页面会进行卸载操作,并且重新生成新页面。

基础库解包

微信开发者工具里面选择的基础库为微信中的基础库版本(后面标识了兼容性和用户的数量),可用于微信开发者工具内的调试。每个版本的微信客户端都会自带一个版本的小程序基础库,而不是微信客户端带着所有版本的基础库,所以开发时选择的版本尽量和客户端的一致(最新的为好),避免 API 兼容性问题。

接下来,我们利用 openVendor() 找到 基础库进行解析,找到原始的 wxvpkg 然后利用 Github 工具进行反编译出源码,https://github.com/csj5588/wxappUnpacker

构建后的结果在 dist 文件下,本质上还是渲染层和逻辑层两个线程脚本之间的交互流程,后续在深入探讨。

跨端框架

由于小程序语法较为原始,工程化工具不支持,市面上太多平台的小程序需要适配开发,基于此市面上的第三方框架也慢慢进入大众的视野,目前来说流行的就是 taro 和 uni-app。

下面先来介绍下框架的分类:

预编译

画板

本质上是利用 DSL(语法规则) + 语法解析,将一些逻辑转换为小程序支持的语法,但是这样存在一些问题:

  • 如果React或者Vue后期再出一些新特性的话,预编译框架都需要进行语法解析扩展编写。
  • 兼容问题,比如小程序不支持的一些属性,如果不支持,预编译框架要进行兼容。

半编译半运行

基本上运行时的框架都是参考Vue的框架才可以达到运行时的目的,注意小程序是不支持直接操作DOM元素的,所以只需要将Vue的patch挂载真实DOM流程修改为小程序setData的逻辑,template转换为WXML,style适配下规则就可以。

mpvue (github地址请参见)是一个使用Vue.js开发小程序的前端框架。框架基于Vue.js核心,mpvue修改了Vue.js的runtime和compiler实现,使其可以运行在小程序环境中,从而为小程序开发引入了整套Vue.js开发体验。

下面我们来看一下源码是如何实现的?

  1. 修改了 vue 的 patch 阶段进行 this.$updateDataToMP()方法。
  2. this.$updateDataToMP()方法就进行了setData的一个调用,还进行了diffData数据的比对和throttle函数优化双线程通讯的性能。

运行时框架

运行时框架主要解决需要 template 转换为 WXML 这部分逻辑,这部分可以借助小程序的template模版机制来进行解决。

这样的话我们只需要进行 root 数据的操作即可,操作完成后利用 setData 渲染机制 发送到渲染层进行操作即可,Taro4 我看编译的结果似乎也是按照这样的格式操作的,后续再看看。

主流技术

Taro

Taro 介绍 | Taro 文档

Taro 特点总结

  • 采用 TypeScript (TS) 加上部分 Rust 进行开发
  • 底层标准参考 微信小程序,兼容性较好,其他平台的兼容性未知
  • ReactWebpack 语法有良好的兼容性支持,Vue 3 加上 Vite 新推出,也可以用
  • 调试开发需要下载不同平台的工具,无法直接导出运行
  • GitHub 上搜索近两年 stars 30+ 的开源项目,资源只有一页,显示资源较少
  • 不支持使用 Vue 3scoped 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:>3068714310
Github 活跃stars:35.3k+ issues:1.2k + 1w+10stars:39.9k+ issues:0.9k + 3k+8
三方组件库官方组件库统一,资源优质 NutUI VUE 6k+9官方组件库较为简陋、社区UI生态较差、大部分收费、一部分不敢用5
TS 支持兼容性优秀9兼容性优秀8
VUE 语法兼容兼容大部分 VUE 语法7兼容大部分 VUE 语法8
React 语法兼容支持0不支持0
总计🥇🏅🎖️5854

跨端开发框架深度横评

下周安排

开源商城项目拆解

  • 腾讯有数
  • gz-yami - Overview
  • EastWorld

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2106726.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Django+Vue协同过滤算法图书推荐系统的设计与实现

目录 1 项目介绍2 项目截图3 核心代码3.1 需要的环境3.2 Django接口层3.3 实体类3.4 config.ini3.5 启动类3.5 Vue 4 数据库表设计5 文档参考6 计算机毕设选题推荐7 源码获取 1 项目介绍 博主个人介绍:CSDN认证博客专家,CSDN平台Java领域优质创作者&…

144. 腾讯云Redis数据库

文章目录 一、Redis 的主要功能特性二、Redis 的典型应用场景三、Redis 的演进过程四、Redis 的架构设计五、Redis 的数据类型及操作命令六、腾讯云数据库 Redis七、总结 Redis 是一种由 C 语言开发的 NoSQL 数据库,以其高性能的键值对存储和多种应用场景而闻名。本…

Vue3 实现解析markdown字段以及文件

Vue实现博客前端,需要实现markdown的解析,如果有代码则需要实现代码的高亮。 Vue的markdown解析库有很多,如markdown-it、vue-markdown-loader、marked、vue-markdown等。这些库都大同小异。这里选用的是marked。 一、安装依赖库 在vue项目…

数据权限的设计与实现系列6——前端筛选器组件Everright-filter使用探索

linear 功能探索 最终我们是需要使用 API 的方式,调用后端服务拉取数据填充筛选器组件,不过在探索阶段,直接用 API 方式,就需要构造 mock 数据,比较麻烦,因此先使用 Function 方式来进行功能验证。 组件初…

关于找不到插件 ‘org.springframework.boot:spring-boot-maven-plugin:‘的解决方案

找到项目结构后,点击库,全选所有后点击应用即可

超声波眼镜清洗机买哪款?2024超声波眼镜清洗机推荐

超声波清洗机正逐渐成为广受欢迎的清洁解决方案,它以高效、深入且细腻的清洁效果,以及操作上的简易性,赢得了消费者的广泛喜爱。不过,市面上琳琅满目的品牌、多样化的型号及波动的价格区间,确实给消费者挑选时带来了不…

C1-2 ABB二次SDK开发——手把手教登录对应的机器人控制器(图片引导操作)登录机器人控制器和刷新机器人列表

1.完成配置后我们开始进行操作 C1-1 ABB二次SDK开发——C#Window窗体-环境配置(带ABB二次开发SDK资源包)-CSDN博客文章浏览阅读95次。3.记住路径,右键C#引用,然后导入ABB.Robotics.Controllers.PC.dll。2.安装资源文件PCABB二次开…

通过组合Self-XSS + CSRF得到存储型XSS

在一次漏洞赏金挖掘中,我在更改用户名的功能点出发现了一个XSS,在修改用户名的地方添加了一个简单的XSS payload并且刷新页面: 用户设置面板 XSS证明 但是问题是这个功能配置并不是公共的,造成XSS漏洞的唯一方法是告诉受害者将其…

H5 响应式精品网站推荐导航源码

源码名称:响应式精品网站推荐导航源码 源码介绍:一款响应式精品网站推荐导航源码,可以自己修改代码替换图标图片和指向网址。背景图支持自动替换,背景图可以在img.php中修改 需求环境:H5 下载地址: http…

6、LVGL控件-线条、图片、按钮矩阵

本篇文章目录导航 ♠♠ LVGL控件-线条、图片、按钮矩阵 ♣♣♣♣ 一、LVGL 线条部件 ♦♦♦♦♦♦♦♦ 1.1 线条部件组成部分 ♦♦♦♦♦♦♦♦ 1.2 线条部件基本API ♦♦♦♦♦♦♦♦ 1.3 实验小演示 ♣♣♣♣ 二、LVGL 图片部件 ♦♦♦♦♦♦♦♦ 2.1 图片部件组成部分 ♦♦…

.NET 一款免杀的白名单Shellcode加载器

01阅读须知 此文所提供的信息只为网络安全人员对自己所负责的网站、服务器等(包括但不限于)进行检测或维护参考,未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作。利用此文所提供的信息而造成的直接或间接后果和损失&#xf…

论 LLMs 如何解决长文本问题?

一、长文本的核心问题与解决方向 1.1 文本长度与显存及计算量之关系 要研究清楚长文本的问题,首先应该搞清楚文本长度在模型中的地位与影响。那么我们便以 Decoder-base 的模型为例来进行分析 1.1.1 模型参数量 Decoder-base 的模型主要包括 3 个部分&#xff1…

基于Java语言的光伏运维管理系统

背景 ‌光伏发电系统主要由‌‌太阳电池板(组件)、‌控制器和‌逆变器‌三大部分组成,主要部件由电子元器件构成。此外,光伏发电系统还包括‌变压器、‌光伏方阵以及相关辅助设施等。‌ 光伏发电系统是利用光伏电池的光生伏特效应…

Java-数据结构-链表-习题(三)(๑´ㅂ`๑)

文本目录: ​❄️一、习题一: ▶ 思路: ▶ 代码: ​❄️二、习题二: ▶ 思路: ▶ 代码: ​❄️三、习题三: ▶ 思路: ▶ 代码: ​❄️四、习题四&#xf…

包的相关知识

1. java定义了一种名字空间,称之为包:package。一个类总属于某个包,类名只是一个简写,真正的完整类名应该是”包名.类名“。 2. 在Java虚拟机执行的时候,JVM只看完整类名,只要包名不同,类就不同…

keysight346A安捷伦346B噪声源HP346B-18Ghz

keysight346A安捷伦346B噪声源HP346B-18Ghz Agilent 346B | HP-346B 噪声源|惠普|安捷伦|噪声头|HP-346B 品牌:美国安捷伦 Agilent | 美国惠普 HP Agilent 346B选件H01高ENR噪声源 Agilent 346B选件H01有高的ENR(典型值为21dB )适于测量噪声系数很大的…

【媒体邀约】论企业宣传与媒体合作

传媒如春雨,润物细无声,大家好,我是51媒体网胡老师。 在探讨企业宣传与媒体合作的策略和实施时,可以从以下结构进行论述: 一、前言 企业宣传与媒体合作在当代商业环境中扮演着至关重要的角色。随着信息科技的发展和媒…

第二证券:有风险!筹码集中股出炉,这10股股东数骤降

深圳华强:存在商场心境过热的风险 昨日晚间,深圳华强发布《股票生意失常不坚定及严峻失常不坚定暨风险提示公告》。公司在公告中提示,近期公司股价短期涨幅较大,明显违背商场走势,存在商场心境过热的风险。但公司基本…

景区智慧公厕系统能给景区带来什么价值?

在当今数字化时代,景区智慧公厕系统正逐渐成为提升景区品质和游客体验的重要组成部分。 一、智慧公厕系统大屏功能 智慧公厕系统的大屏界面功能丰富多样。它可以实时显示公厕内的布局图,清晰地标明各个厕位的使用情况,让游客一目了然。同时&a…

【Google Play】Via浏览器5.8.1最新国际版(如何鉴别是否官方?)

via 浏览器,为您的安卓设备带来清爽无打扰的上网体验,不会推送新闻和其他内容,让您的设备保持纯净。体积小巧,内存占用极低,确保您的安卓设备如同新机般运行流畅。简约设计,是极简主义爱好者和技术达人的首…