崔红保

崔红保 查看完整档案

北京编辑北京科技大学  |  信息与计算科学 编辑DCloud  |  前端架构师 编辑 www.keep-running.cn 编辑
编辑

正在认真做一个产品

个人动态

崔红保 赞了文章 · 4月15日

可能是 PHP 面试最靠谱的资料了

CURD 写了好几年了,一直也没有什么拿得出手的作品,去年面试被虐菜之后,于是开始写这个 PHP 面试问答,写完第一版之后发到 Github 上,关注的人蛮多的。然而面试依然被虐菜,于是继续写第二版,现在写完了,发 SF 上求拍砖,各路大神可以提点建议啥的。后面还会继续更新的,原因嘛你肯定知道的

结合实际 PHP 面试,汇总自己遇到的问题,以及网上其他人遇到的问题,尝试提供简洁准确的答案 网络、数据结构与算法、PHP、Web、MySQL、Redis、Linux、安全、设计模式、架构、面试等部分

里程碑

  • Github 搜索 php 面试 排名第一,目前 Star 为 332 枚
  • 汇总问题超过 150 个,尝试提供简洁靠谱答案,大部分都有了
  • 269 次 Commits,参考出版书籍 24 本

后续计划

  • 数据结构与算法篇完善
  • 完善非技术部分内容,软实力也蛮重要的
  • 主要是完善延伸深度剖析,深究原理还是很重要的

传送门

《 PHP 面试问答》 https://github.com/colinlet/PHP-Interview-QA

觉得不错的话,star 一波,达成 500 star 小目标

结束语

当然,有了这份最(不)靠谱的资料,工作还是依然找不到的~~

查看原文

赞 32 收藏 24 评论 0

崔红保 收藏了文章 · 4月10日

跨端开发框架深度横评之2020版

又是一年四月天,距离上次发布跨端开发框架深度横评已过去整整一年。

这一年,小程序在用户规模及商业化方面都取得了极大的成功。微信小程序日活超过3亿,支付宝、百度、字节跳动小程序的月活也纷纷超过3亿。

对应小程序开发领域,这一年也发生了巨大变化。开发框架从单纯的微信小程序开发,过渡到多端框架成为标配,进一步提升开发效率成为开发者的强烈需求。

这一年 mpvue 停止更新,Taro开始探索 taro nextuni-app 产品和生态持续完善,微信新推出了支持H5和微信小程序的 kbone 框架...

去年的深度横评中很多老将已经退出江湖,一些新秀吸引眼球,因此,是时候来一波2020版的新横评了。

评测目标筛选

跨端框架是一个重投入工作,在各框架的1年多的比拼中,很多框架因为投入不足而逐渐被开发者放弃,uni-apptaro依靠持续的大力度投入,成为了市场主流。

taro 在稳定版的基础之上,最近也推出了 taro next,这2个版本差异较大,本次会分别评测。

kbone 框架虽面世不久,但毕竟是微信官方发布,关注的人不少,故本次将 kbone 加入评测目标。

所以,本次评测的对象(按发布时间排序):

本次评测除了运行性能等实测数据外,尽可能得通过框架官网及github、掘金、腾讯课堂等三方社区公开采集数据,希望给大家一个综合全面的评估依据。

功能实现

tarouni-app 是比较典型的多端框架,发布到各个端均可。而 kbone 只支持微信小程序和H5。

tarouni-app 均将常用接口及组件封装了成了跨端API和跨端组件,组件规范沿用微信小程序的规范,部分平台特有API,这两个框架亦有应对方案:

  • taro:支持与小程序代码混写,可通过混写的方式调用框架尚未封装的小程序新增API
  • uni-app:支持条件编译,可在条件编译代码块中,随意调用各个平台新增的API及组件

tarouni-app 可以不受限的调用各家小程序平台所有的API及组件。

kbone 沿用web的开发习惯,使用html标签及js api;涉及微信特有api时,可通过process.env.isMiniprogram判断环境,然后编写微信原生代码。对于html中没有标签可替代的微信内置组件(如swiper),需要使用 wx-component 标签或者使用 wx- 前缀,这样的内置组件会被包裹一层自定义组件,带来相应的性能开销。

除了接口、组件之外,我们以微信小程序为例,找几个典型能力对比框架支持度:

框架tarouni-appkbone
微信自定义组件⭕️⭕️⭕️
三方插件⭕️⭕️
分包加载⭕️⭕️⭕️
sitemap⭕️⭕️⭕️
wxs⭕️
云开发⭕️⭕️⭕️

补充说明:

  • 如果在 Taro 项目引用了小程序原生的第三方组件,那么该项目将不再具备多端转换的能力,例如,如果使用了微信小程序的第三方组件,那么项目只能转换成微信小程序,转义成其他平台会失效,详见taro官网
  • uni-app 中使用微信自定义组件的话,支持编译发行到App/H5/微信小程序/QQ小程序4个平台,详见uni-app官网
  • taro 不支持 wxs 的依据:#2959
  • kbone 不支持微信三方插件的依据:#58;不支持wxs的依据:#129
  • 云开发在微信平台,三个框架都支持,但 taro/kbone仅支持微信小程序平台,uni-app支持App/H5/小程序所有平台使用云开发,详见下方 serverless/云开发 章节。

wxs是提升性能体验的重要利器,除了微信小程序的wxs外,还有支付宝的SJS、百度的Filter,这些高级技术 uni-app 均完善支持。参考:谜之wxs,uni-app如何用它大幅提升性能

从如上功能对比来看:微信原生 ~ uni-app > taro > kbone

运行性能

我们继续沿用去年的测试模型,看看一年来,各家小程序开发框架的性能是否有提升。详细如下:

  • 开发内容:开发一个仿微博小程序首页的复杂长列表,支持下拉刷新、上拉翻页、点赞。
  • 界面如下:

  • 开发版本:一共开发了5个版本,包括微信原生版、taro版、uni-app版、kbone版,按照官网指引通过cli方式默认安装。
  • taro 目前稳定版本是2.0版,但近期在宣传跨框架的taro next,故我们基于同样的 react代码,同时测试了taro 2.0 和 taro next 两个版本的数据。
  • 测试代码开源(Github仓库地址:https://github.com/dcloudio/test-framework),

Tips:若有同学觉得测试代码写法欠妥,欢迎提交 PR 或 Issus

  • 测试机型:红米 Redmi 6 Pro、MIUI 11.0.5 稳定版(最新版)、微信版本 7.0.12(最新版)
  • 测试环境:每个框架开始测试前,杀掉各App进程、清空内存,保证测试机环境基本一致;每次从本地读取静态数据,屏蔽网络差异。

我们以上述仿微博小程序为例,测试2个容易出性能问题的点:长列表加载、大量点赞组件的响应。

长列表加载

仿微博的列表是一个包含很多组件的列表,这种复杂列表对性能的压力更大,很适合做性能测试。

从触发上拉加载到数据更新、页面渲染完成,需要准确计时。人眼视觉计时肯定不行,我们采用程序埋点的方式,制定了如下计时时机:

  • 计时开始时机:交互事件触发,框架赋值之前,如:上拉加载(onReachBottom)函数开头
  • 计时结束时机:页面渲染完毕(微信setData回调函数开头)

Tips:setData回调函数开头可认为是页面渲染完成的时间,是因为微信setData定义如下(微信规范):

测试方式:从页面空列表开始,通过程序自动触发上拉加载,每次新增20条列表,记录单次耗时;固定间隔连续触发 N 次上拉加载,使得页面达到 20*N 条列表,计算这 N 次触发上拉到渲染完成的平均耗时。

测试结果如下:

说明:以400条微博列表为例,从页面空列表开始,每隔1秒触发一次上拉加载(新增20条微博),记录单次耗时,触发20次后停止(页面达到400条微博),计算这20次的平均耗时,结果微信原生在这20次 触发上拉 -> 渲染完成 的平均耗时为538毫秒,最快的uni-app是446毫秒,最慢的kbone是4057毫秒

大家初看这个数据,可能比较疑惑,别急,下方有详细说明

说明1:为何 taro next/kbone 测试数据不完整?

因为 taro nextkbone 采用的是动态渲染方案,这类方案在页面复杂、组件较多时,会大量增加页面 dom 节点数量,甚至超出微信的 dom 节点数限制(如下告警信息)。我们在 红米手机(Redmi 6 Pro)上实测,页面组件超过600个时,taro nextkbone 实现的仿微博App就会报出运行异常,并停止渲染(页面白屏),故这两个测试框架在组件较多时,测试数据不完整。这也就意味着,当页面组件太多时,无法使用这2个框架。

dom limit exceeded please check if there's any mistake you've made

另外,kbone官网有如下介绍:

kbone 是使用一定的性能损耗来换取更为全面的 Web 端特性支持。

taro nextkbone的测试数据,明显和taro 2.0uni-app不是一个量级的。

如果你的应用是长列表场景,那taro nextkbone就明显不太合适。

说明2:为什么测试数据显示uni-app 会比微信原生框架的性能略好呢?

这个问题在去年的测评中,已解释过。为了避免新同学迷惑,这里再次说明一下。

微信原生框架耗时主要在setData调用上,开发者若不单独优化,则每次都会传递大量数据;而 uni-apptaro 都在调用setData之前自动做diff计算,每次仅传递变动的数据。

例如当前页面有20条数据,触发上拉加载时,会新加载20条数据,此时原生框架通过如下代码测试时,setData会传输40条数据

data: {
    listData: []
},
onReachBottom() { //上拉加载
    let listData = this.data.listData;
    listData.push(...Api.getNews());//新增数据
    this.setData({
        listData
    }) //全量数据,发送数据到视图层
}

开发者使用微信原生框架,完全可以自己优化,精简传递数据(每次仅传递变化的20条数据),比如修改如下:

data: {
    listData: []
},
onReachBottom() { //上拉加载
    // 通过长度获取下一次渲染的索引
    let index = this.data.listData.length;
    let newData = {}; //新变更数据
    Api.getNews().forEach((item) => {
        newData['listData[' + (index++) + ']'] = item //赋值,索引递增
    }) 
    this.setData(newData) //增量数据,发送数据到视图层
}

经过如上优化修改后,再次测试,微信原生框架性能数据如下:

从测试结果可看出:

  • 经过开发者手动优化,微信原生框架可达到更好的性能;
  • uni-app相比微信原生,性能接近,算是一个数量级;并且随着数据量增加,性能消耗增加不明显,从438到454,只有16毫秒的变化
  • taro 2.0随着数据量越大,性能损耗随着增大,从595到790,有将近200毫秒的变化;
  • taro nextkbone相比之下,差距就比较大了。

这个结果,和web开发类似,web开发也有原生js开发、vuereact框架等情况。如果不做特殊优化,原生js写的网页,性能经常还不如vuereact框架的性能。

也恰恰是因为Vuereact框架的优秀,性能好,开发体验好,所以原生js开发已经逐渐减少使用了。

说明3:为何今年的性能测试数据和去年的不同?

细心的同学会发现,同样的测试手机,同样的测试代码,为何今年的性能数据会比去年的数据有大幅提升?

  • taro、uni-app及微信原生,三个框架的数据都有大幅提升,400条记录时,至少都有300毫秒的优化
  • uni-app及优化后的微信原生,随着数据量的增加,耗时数据变化并不明显,但去年是很明显的线性增长

其实,通过微信原生工程的数据对比,就能得出结论:2019年,微信针对小程序运行时做了大幅性能优化。

这对开发者来说应该是个好消息,小程序性能体验更佳,可承载更好的用户业务。

复杂长列表加载下一页评测结论:微信原生开发(手动优化) ~ uni-app > 微信原生开发(未手动优化) ~ taro 2.0 > taro next > kbone

点赞组件响应速度

长列表中的某个组件,比如点赞组件,点击时是否能及时的修改未赞和已赞状态?是这项测试的评测点。

测试方式:

  • 选中某微博,点击“点赞”按钮,实现点赞状态状态切换(已赞高亮、未赞灰色),
  • 点赞按钮 onclick函数开头开始计时,setData回调函数开头结束计时;

在红米手机(Redmi 6 Pro)上进行多次测试,求其平均值,结果如下:

说明:也就是在列表数量为400时,微信原生开发的应用,点赞按钮从点击到状态变化需要26毫秒。

测试结果数据说明:

  • taro next/kbone 测试数据不完整的原因同上,在组件较多时,页面已经不再渲染了
  • taro 2.0版本、uni-app和微信原生在点赞组件上的性能体验接近,但taro next和kbone有较大的性能差距,这个也是因为动态运行时框架导致的。

组件数据更新性能测评:uni-app ~ taro 2.0 > taro next > kbone

综上,本性能测试做了2个测试,长列表加载和组件状态更新,综合2个实验,结论如下:

微信原生开发(手动优化) ~ uni-app > 微信原生开发(未手动优化) ~ taro 2.0 > taro next > kbone

跨端支持

这三个框架都是为了解决平台同构问题,跨端的比较是必需的。

tarouni-app 相对比较成熟,支持主流的所有平台。kbone 只支持微信小程序和 Web 端。我们重点比较一下 tarouni-app

小程序平台

tarouni-app 均支持微信、支付宝、百度、字节跳动小程序,功能基本可以拉齐。

双方都有不少大厂案例,taro有京东、货拉拉、淘票票等公司小程序案例,uni-app有腾讯、华为、vivo、联想、中华英才网等公司小程序案例。

App平台

  • 能力方面

taro与微信小程序引擎拉齐度较低,很多功能需要开发者分别在iOS和Android上做原生开发才能实现。比如App端很常见的三方登录、支付、分享等能力,taro并未封装。

uni-app则在基础引擎层面提供了丰富的能力,还提供了丰富的插件市场,可切实提升开发者的效率。

  • 性能方面

taro在App端使用了react native的渲染引擎,虽然渲染层ui是原生的,但在实时交互和高响应要求的UI操作方面表现一直不佳,js层和视图层的通信损耗让很多开发者深感无力。

uni-app的App引擎同时给开发者提供了原生渲染引擎和小程序引擎的双选方案,并且由于发明了renderjs技术,以及支持wxs、bindingx等技术,解决了js层和视图层的通信损耗问题,在高响应要求的UI操作方面有更好的性能表现。比如这类canvas动画:

  • 开发体验方面

taro的开发者需自行搭建iOS/Android开发环境,比较繁琐,(官方原文地址):

uni-app可以做到让前端开发者不依赖原生工程师独立完成App。其开发的小程序,可以更平滑的变成可商用的App。

使用跨平台开发的核心诉求在于提升效率,如果一个App的开发由前端、iOS、Android等3拨工程师协作完成,其实效率反而非常低。

另外,uni-app还提供了uni小程序sdk,这个工具可以帮助原生App快速搭建自己的小程序平台。这是其他框架所未提供的。

H5平台

taro的H5平台在一年来的进步较多,可用性大幅提升。但相比于uni-app,目前仍然缺失对wxs和小程序组件的支持。

快应用

taro支持快应用的时间比uni-app早。

但快应用发展到2020年有了一些变化,uni-app针对新的形势,提供了2个发行到快应用的方案(当前两个版本都处于社区维护状态):

  • quickapp-vue版:使用 Vue开发快应用。此方案由小米主导,但华为快应用暂不支持。
  • quickapp-light版:基于小程序架构的快应用(Light版),详见https://www.hellohub.cn。此方案由华为主导,但小米快应用暂不支持。

跨端灵活性

跨端开发,离不开条件编译。因为不能用统一来抹杀各个平台的特色。

优良的条件编译能力对各端开发的灵活度至关重要,可以让开发者在共享和个性化之间游刃有余。

tarouni-appkbone 均支持在js代码通过process.env判断平台,然后编写平台特有代码。

taro额外支持统一接口的多端文件编码方式,以及在样式文件中使用ifdef条件编译。

uni-app是全面可条件编译的,目录、文件、配置、组件、js、css,所有一切均可通过ifdef条件编译。

跨端支持小结结论:uni-app > taro > kbone

开发体验

tarouni-appkbone均支持cli模式,可以在主流前端工具中开发,且基本都带有d.ts的语法提示库。三个框架均支持主流的vuereact语法,配套的ide工具链较丰富,着色、校验、格式化完善。

相比微信原生,这三个开发框架的开发体验都更为优秀。

但在开发工具维度上,明显高出一截的框架是uni-app,其出品公司同时也是HBuilderX的出品公司DCloud.io,HBuilderX为uni-app做了很多优化,代码提示、转到定义、easycom、运行调试...故uni-app的开发效率、易用性非其他框架可及。

开发体验维度,对比结果:uni-app > taro,kbone

serverless/云开发

serverless是目前炙手可热的一个概念,被称为下一代的云技术,是真正的”云“。

微信率先将 serverless 技术引入小程序开发领域,即云开发,帮助开发者云端一体的完成业务。随后,支付宝、百度都上线了自己的云开发。根据微信公开的数据,已经有50万开发者在使用微信云开发了。

不过小程序厂家主导的云开发存在一个天然限制,就是和平台绑定过紧,无法和其它平台共享数据。

我们以微信云开发为例,如果你仅开发微信小程序,数据独家存在微信平台,那没问题;但如果你同时还有App或其他家小程序,此时微信小程序的数据存储在微信平台,其它平台的数据存储在开发者自己的服务器上,此时就出现了数据割裂。假设一个用户先使用小程序,个人数据存储在微信平台;有了粘性后升级到App,此时App端无法读取微信平台的数据,则用户就无法查看之前在小程序上的历史数据,甚至在App平台需要重新注册。这种情况对开发者是不利的。

因此,跨端的 serverless 方案才是开发者的最优解。

目前主流框架对云开发的支持情况:

  • Taro:仅支持微信小程序,详见小程序云开发模板
  • uni-app:DCloud 联合阿里云、腾讯云,提供基于 serverless 模式和 js 编程的云开发平台,支持App/H5/小程序所有平台,详见uniCloud
  • kbone:仅支持微信小程序,详见云开发

serverless 维度上,uni-app大幅领先其它框架。

插件市场

一个开发框架能否成功,除了框架自身的高度产品化外,开发者生态的构建也至关重要。

uni-app 于2018年底率先推出插件市场,支持前端组件、js sdk、页面模板、项目模板、原生插件等多种类型,且提供了赞赏、付费购买等手段,刺激轮子作者的创作激情。目前市场上已发布插件接近1500个,众多插件下载量都在万次以上。

Taro 于 2019年5月上线物料市场,目前市场上已发布物料90个;从热门榜单来看,下载量并不大,下载最多的也就数百。

kbone目前还没有插件市场。

Tips:

  • 插件数量及下载量数据采集时间为 2020.04.03 16:00

插件市场维度,uni-app独领风骚。

学习资源

除了各大框架官网外,开发者通常还会通过视频教程、社区博客等方式系统学习。

相关学习资源的丰富程度,也能侧面反映一个框架的受欢迎程度,故我们采集了几个三方站点的数据。

视频教程

框架腾讯课堂网易云课堂慕课网
taro412
uni-app16161

Tips:

  • 视频教程数据采集时间为2020.04.05 22:00

开发交流

除了入门的学习资源,开发期的交流也很重要,这个我们主要看官方组织的社区和交流群。

社区论坛

uni-app问答社区,帖子丰富,沉淀较多;目前已沉淀2万多相关帖子,每日更新帖子数百篇,月uv百万。

对于习惯使用 github issue反馈问题的用户,uni-app同样支持,目前累计有1391个issues。

Taro 早期基于github issue进行产品Bug管理,目前累计已有近4898个issue;后于2019年5月上线开发者社区,和物料市场上线时间相同,目前沉淀1300+帖子,每日更新帖子在10个左右,相关数据计算方法如下:

  • 帖子总数:Taro 社区顶部选择板块,计算每个板块下所有主题总数,如下图。
  • 每日更新帖子数:根据帖子列表中的最后回复时间,计算24小时内有回复或评论的主题总数

kbone 在微信开放社区中新增了一个Kbone官方框架的专区,因产品发布较晚,目前只有一百多个帖子。

总结一下社区帖子及issue数据,情况如下(采集时间为 2020.04.03 23:00):

交流群

框架微信群QQ群交流群开发者(预估)
taro16-8k
uni-app2040+90k
kbone-10.5k

Tips:

  • Taro 有16个微信群是根据 Taro 官网上显示Taro 开发交流 15 群 已满推论而出,每个微信群500人,交流群人数: 500*16 = 8000人
  • uni-app 官网 QQ群有35个,微信群20个,还有十几个专项QQ群,其中有30个QQ群达到2000人,交流群人数: 30 2000 + 5 1000 + 20*500 + 5000 = 90000人
  • kbone 在 github 的 readme中有一个qq交流群,申请加入时显示500人已满

除了交流群外,DCloud对外公布的uni-app的开发者数量达到百万人,暂未看到tarokbone公布此类数据。

总体而言,开发交流维度比较结果如下:uni-app > taro > kbone

其它指标

github

框架star贡献者
taro24.6k122
uni-app19.7k72
kbone2.7k7

在开源社区方面,Taro做的还是非常成功的,它吸引了更多开发者为其贡献代码、文档。

百度指数

通过index.baidu.com,可查看主流框架的搜索指数,它代表了网友的搜索量和相关文章的收录量。目前kbone尚未收录到百度指数中,如下是近期 uni-apptaro的百度指数对比图:

结语

所有的评测都只是提供决策依据,最后的结果还是要依赖开发者的团队技术栈、业务诉求、未来规划等。

不过作为一篇评测文章的结语,我们还是要给出自己的建议:

  • 如果你熟悉React,不懂Vue.js,推荐Taro;
  • 如果你熟悉Vue.js,则推荐 uni-app;
  • 如果你已经有H5代码,只想增加微信小程序平台,并且对性能要求不高,可以考虑kbone;
  • 如果你的业务涉及多端,更推荐 uni-app;
  • 如果你希望通过 serverless 方案快速上线业务,推荐 uni-app。

如有读者认为本文中任何评测失真,欢迎在这里报 issuse

查看原文

崔红保 关注了用户 · 4月10日

盲选C @roll_c

关注 19

崔红保 发布了文章 · 4月10日

跨端开发框架深度横评之2020版

又是一年四月天,距离上次发布跨端开发框架深度横评已过去整整一年。

这一年,小程序在用户规模及商业化方面都取得了极大的成功。微信小程序日活超过3亿,支付宝、百度、字节跳动小程序的月活也纷纷超过3亿。

对应小程序开发领域,这一年也发生了巨大变化。开发框架从单纯的微信小程序开发,过渡到多端框架成为标配,进一步提升开发效率成为开发者的强烈需求。

这一年 mpvue 停止更新,Taro开始探索 taro nextuni-app 产品和生态持续完善,微信新推出了支持H5和微信小程序的 kbone 框架...

去年的深度横评中很多老将已经退出江湖,一些新秀吸引眼球,因此,是时候来一波2020版的新横评了。

评测目标筛选

跨端框架是一个重投入工作,在各框架的1年多的比拼中,很多框架因为投入不足而逐渐被开发者放弃,uni-apptaro依靠持续的大力度投入,成为了市场主流。

taro 在稳定版的基础之上,最近也推出了 taro next,这2个版本差异较大,本次会分别评测。

kbone 框架虽面世不久,但毕竟是微信官方发布,关注的人不少,故本次将 kbone 加入评测目标。

所以,本次评测的对象(按发布时间排序):

本次评测除了运行性能等实测数据外,尽可能得通过框架官网及github、掘金、腾讯课堂等三方社区公开采集数据,希望给大家一个综合全面的评估依据。

功能实现

tarouni-app 是比较典型的多端框架,发布到各个端均可。而 kbone 只支持微信小程序和H5。

tarouni-app 均将常用接口及组件封装了成了跨端API和跨端组件,组件规范沿用微信小程序的规范,部分平台特有API,这两个框架亦有应对方案:

  • taro:支持与小程序代码混写,可通过混写的方式调用框架尚未封装的小程序新增API
  • uni-app:支持条件编译,可在条件编译代码块中,随意调用各个平台新增的API及组件

tarouni-app 可以不受限的调用各家小程序平台所有的API及组件。

kbone 沿用web的开发习惯,使用html标签及js api;涉及微信特有api时,可通过process.env.isMiniprogram判断环境,然后编写微信原生代码。对于html中没有标签可替代的微信内置组件(如swiper),需要使用 wx-component 标签或者使用 wx- 前缀,这样的内置组件会被包裹一层自定义组件,带来相应的性能开销。

除了接口、组件之外,我们以微信小程序为例,找几个典型能力对比框架支持度:

框架tarouni-appkbone
微信自定义组件⭕️⭕️⭕️
三方插件⭕️⭕️
分包加载⭕️⭕️⭕️
sitemap⭕️⭕️⭕️
wxs⭕️
云开发⭕️⭕️⭕️

补充说明:

  • 如果在 Taro 项目引用了小程序原生的第三方组件,那么该项目将不再具备多端转换的能力,例如,如果使用了微信小程序的第三方组件,那么项目只能转换成微信小程序,转义成其他平台会失效,详见taro官网
  • uni-app 中使用微信自定义组件的话,支持编译发行到App/H5/微信小程序/QQ小程序4个平台,详见uni-app官网
  • taro 不支持 wxs 的依据:#2959
  • kbone 不支持微信三方插件的依据:#58;不支持wxs的依据:#129
  • 云开发在微信平台,三个框架都支持,但 taro/kbone仅支持微信小程序平台,uni-app支持App/H5/小程序所有平台使用云开发,详见下方 serverless/云开发 章节。

wxs是提升性能体验的重要利器,除了微信小程序的wxs外,还有支付宝的SJS、百度的Filter,这些高级技术 uni-app 均完善支持。参考:谜之wxs,uni-app如何用它大幅提升性能

从如上功能对比来看:微信原生 ~ uni-app > taro > kbone

运行性能

我们继续沿用去年的测试模型,看看一年来,各家小程序开发框架的性能是否有提升。详细如下:

  • 开发内容:开发一个仿微博小程序首页的复杂长列表,支持下拉刷新、上拉翻页、点赞。
  • 界面如下:

  • 开发版本:一共开发了5个版本,包括微信原生版、taro版、uni-app版、kbone版,按照官网指引通过cli方式默认安装。
  • taro 目前稳定版本是2.0版,但近期在宣传跨框架的taro next,故我们基于同样的 react代码,同时测试了taro 2.0 和 taro next 两个版本的数据。
  • 测试代码开源(Github仓库地址:https://github.com/dcloudio/test-framework),

Tips:若有同学觉得测试代码写法欠妥,欢迎提交 PR 或 Issus

  • 测试机型:红米 Redmi 6 Pro、MIUI 11.0.5 稳定版(最新版)、微信版本 7.0.12(最新版)
  • 测试环境:每个框架开始测试前,杀掉各App进程、清空内存,保证测试机环境基本一致;每次从本地读取静态数据,屏蔽网络差异。

我们以上述仿微博小程序为例,测试2个容易出性能问题的点:长列表加载、大量点赞组件的响应。

长列表加载

仿微博的列表是一个包含很多组件的列表,这种复杂列表对性能的压力更大,很适合做性能测试。

从触发上拉加载到数据更新、页面渲染完成,需要准确计时。人眼视觉计时肯定不行,我们采用程序埋点的方式,制定了如下计时时机:

  • 计时开始时机:交互事件触发,框架赋值之前,如:上拉加载(onReachBottom)函数开头
  • 计时结束时机:页面渲染完毕(微信setData回调函数开头)

Tips:setData回调函数开头可认为是页面渲染完成的时间,是因为微信setData定义如下(微信规范):

测试方式:从页面空列表开始,通过程序自动触发上拉加载,每次新增20条列表,记录单次耗时;固定间隔连续触发 N 次上拉加载,使得页面达到 20*N 条列表,计算这 N 次触发上拉到渲染完成的平均耗时。

测试结果如下:

说明:以400条微博列表为例,从页面空列表开始,每隔1秒触发一次上拉加载(新增20条微博),记录单次耗时,触发20次后停止(页面达到400条微博),计算这20次的平均耗时,结果微信原生在这20次 触发上拉 -> 渲染完成 的平均耗时为538毫秒,最快的uni-app是446毫秒,最慢的kbone是4057毫秒

大家初看这个数据,可能比较疑惑,别急,下方有详细说明

说明1:为何 taro next/kbone 测试数据不完整?

因为 taro nextkbone 采用的是动态渲染方案,这类方案在页面复杂、组件较多时,会大量增加页面 dom 节点数量,甚至超出微信的 dom 节点数限制(如下告警信息)。我们在 红米手机(Redmi 6 Pro)上实测,页面组件超过600个时,taro nextkbone 实现的仿微博App就会报出运行异常,并停止渲染(页面白屏),故这两个测试框架在组件较多时,测试数据不完整。这也就意味着,当页面组件太多时,无法使用这2个框架。

dom limit exceeded please check if there's any mistake you've made

另外,kbone官网有如下介绍:

kbone 是使用一定的性能损耗来换取更为全面的 Web 端特性支持。

taro nextkbone的测试数据,明显和taro 2.0uni-app不是一个量级的。

如果你的应用是长列表场景,那taro nextkbone就明显不太合适。

说明2:为什么测试数据显示uni-app 会比微信原生框架的性能略好呢?

这个问题在去年的测评中,已解释过。为了避免新同学迷惑,这里再次说明一下。

微信原生框架耗时主要在setData调用上,开发者若不单独优化,则每次都会传递大量数据;而 uni-apptaro 都在调用setData之前自动做diff计算,每次仅传递变动的数据。

例如当前页面有20条数据,触发上拉加载时,会新加载20条数据,此时原生框架通过如下代码测试时,setData会传输40条数据

data: {
    listData: []
},
onReachBottom() { //上拉加载
    let listData = this.data.listData;
    listData.push(...Api.getNews());//新增数据
    this.setData({
        listData
    }) //全量数据,发送数据到视图层
}

开发者使用微信原生框架,完全可以自己优化,精简传递数据(每次仅传递变化的20条数据),比如修改如下:

data: {
    listData: []
},
onReachBottom() { //上拉加载
    // 通过长度获取下一次渲染的索引
    let index = this.data.listData.length;
    let newData = {}; //新变更数据
    Api.getNews().forEach((item) => {
        newData['listData[' + (index++) + ']'] = item //赋值,索引递增
    }) 
    this.setData(newData) //增量数据,发送数据到视图层
}

经过如上优化修改后,再次测试,微信原生框架性能数据如下:

从测试结果可看出:

  • 经过开发者手动优化,微信原生框架可达到更好的性能;
  • uni-app相比微信原生,性能接近,算是一个数量级;并且随着数据量增加,性能消耗增加不明显,从438到454,只有16毫秒的变化
  • taro 2.0随着数据量越大,性能损耗随着增大,从595到790,有将近200毫秒的变化;
  • taro nextkbone相比之下,差距就比较大了。

这个结果,和web开发类似,web开发也有原生js开发、vuereact框架等情况。如果不做特殊优化,原生js写的网页,性能经常还不如vuereact框架的性能。

也恰恰是因为Vuereact框架的优秀,性能好,开发体验好,所以原生js开发已经逐渐减少使用了。

说明3:为何今年的性能测试数据和去年的不同?

细心的同学会发现,同样的测试手机,同样的测试代码,为何今年的性能数据会比去年的数据有大幅提升?

  • taro、uni-app及微信原生,三个框架的数据都有大幅提升,400条记录时,至少都有300毫秒的优化
  • uni-app及优化后的微信原生,随着数据量的增加,耗时数据变化并不明显,但去年是很明显的线性增长

其实,通过微信原生工程的数据对比,就能得出结论:2019年,微信针对小程序运行时做了大幅性能优化。

这对开发者来说应该是个好消息,小程序性能体验更佳,可承载更好的用户业务。

复杂长列表加载下一页评测结论:微信原生开发(手动优化) ~ uni-app > 微信原生开发(未手动优化) ~ taro 2.0 > taro next > kbone

点赞组件响应速度

长列表中的某个组件,比如点赞组件,点击时是否能及时的修改未赞和已赞状态?是这项测试的评测点。

测试方式:

  • 选中某微博,点击“点赞”按钮,实现点赞状态状态切换(已赞高亮、未赞灰色),
  • 点赞按钮 onclick函数开头开始计时,setData回调函数开头结束计时;

在红米手机(Redmi 6 Pro)上进行多次测试,求其平均值,结果如下:

说明:也就是在列表数量为400时,微信原生开发的应用,点赞按钮从点击到状态变化需要26毫秒。

测试结果数据说明:

  • taro next/kbone 测试数据不完整的原因同上,在组件较多时,页面已经不再渲染了
  • taro 2.0版本、uni-app和微信原生在点赞组件上的性能体验接近,但taro next和kbone有较大的性能差距,这个也是因为动态运行时框架导致的。

组件数据更新性能测评:uni-app ~ taro 2.0 > taro next > kbone

综上,本性能测试做了2个测试,长列表加载和组件状态更新,综合2个实验,结论如下:

微信原生开发(手动优化) ~ uni-app > 微信原生开发(未手动优化) ~ taro 2.0 > taro next > kbone

跨端支持

这三个框架都是为了解决平台同构问题,跨端的比较是必需的。

tarouni-app 相对比较成熟,支持主流的所有平台。kbone 只支持微信小程序和 Web 端。我们重点比较一下 tarouni-app

小程序平台

tarouni-app 均支持微信、支付宝、百度、字节跳动小程序,功能基本可以拉齐。

双方都有不少大厂案例,taro有京东、货拉拉、淘票票等公司小程序案例,uni-app有腾讯、华为、vivo、联想、中华英才网等公司小程序案例。

App平台

  • 能力方面

taro与微信小程序引擎拉齐度较低,很多功能需要开发者分别在iOS和Android上做原生开发才能实现。比如App端很常见的三方登录、支付、分享等能力,taro并未封装。

uni-app则在基础引擎层面提供了丰富的能力,还提供了丰富的插件市场,可切实提升开发者的效率。

  • 性能方面

taro在App端使用了react native的渲染引擎,虽然渲染层ui是原生的,但在实时交互和高响应要求的UI操作方面表现一直不佳,js层和视图层的通信损耗让很多开发者深感无力。

uni-app的App引擎同时给开发者提供了原生渲染引擎和小程序引擎的双选方案,并且由于发明了renderjs技术,以及支持wxs、bindingx等技术,解决了js层和视图层的通信损耗问题,在高响应要求的UI操作方面有更好的性能表现。比如这类canvas动画:

  • 开发体验方面

taro的开发者需自行搭建iOS/Android开发环境,比较繁琐,(官方原文地址):

uni-app可以做到让前端开发者不依赖原生工程师独立完成App。其开发的小程序,可以更平滑的变成可商用的App。

使用跨平台开发的核心诉求在于提升效率,如果一个App的开发由前端、iOS、Android等3拨工程师协作完成,其实效率反而非常低。

另外,uni-app还提供了uni小程序sdk,这个工具可以帮助原生App快速搭建自己的小程序平台。这是其他框架所未提供的。

H5平台

taro的H5平台在一年来的进步较多,可用性大幅提升。但相比于uni-app,目前仍然缺失对wxs和小程序组件的支持。

快应用

taro支持快应用的时间比uni-app早。

但快应用发展到2020年有了一些变化,uni-app针对新的形势,提供了2个发行到快应用的方案(当前两个版本都处于社区维护状态):

  • quickapp-vue版:使用 Vue开发快应用。此方案由小米主导,但华为快应用暂不支持。
  • quickapp-light版:基于小程序架构的快应用(Light版),详见https://www.hellohub.cn。此方案由华为主导,但小米快应用暂不支持。

跨端灵活性

跨端开发,离不开条件编译。因为不能用统一来抹杀各个平台的特色。

优良的条件编译能力对各端开发的灵活度至关重要,可以让开发者在共享和个性化之间游刃有余。

tarouni-appkbone 均支持在js代码通过process.env判断平台,然后编写平台特有代码。

taro额外支持统一接口的多端文件编码方式,以及在样式文件中使用ifdef条件编译。

uni-app是全面可条件编译的,目录、文件、配置、组件、js、css,所有一切均可通过ifdef条件编译。

跨端支持小结结论:uni-app > taro > kbone

开发体验

tarouni-appkbone均支持cli模式,可以在主流前端工具中开发,且基本都带有d.ts的语法提示库。三个框架均支持主流的vuereact语法,配套的ide工具链较丰富,着色、校验、格式化完善。

相比微信原生,这三个开发框架的开发体验都更为优秀。

但在开发工具维度上,明显高出一截的框架是uni-app,其出品公司同时也是HBuilderX的出品公司DCloud.io,HBuilderX为uni-app做了很多优化,代码提示、转到定义、easycom、运行调试...故uni-app的开发效率、易用性非其他框架可及。

开发体验维度,对比结果:uni-app > taro,kbone

serverless/云开发

serverless是目前炙手可热的一个概念,被称为下一代的云技术,是真正的”云“。

微信率先将 serverless 技术引入小程序开发领域,即云开发,帮助开发者云端一体的完成业务。随后,支付宝、百度都上线了自己的云开发。根据微信公开的数据,已经有50万开发者在使用微信云开发了。

不过小程序厂家主导的云开发存在一个天然限制,就是和平台绑定过紧,无法和其它平台共享数据。

我们以微信云开发为例,如果你仅开发微信小程序,数据独家存在微信平台,那没问题;但如果你同时还有App或其他家小程序,此时微信小程序的数据存储在微信平台,其它平台的数据存储在开发者自己的服务器上,此时就出现了数据割裂。假设一个用户先使用小程序,个人数据存储在微信平台;有了粘性后升级到App,此时App端无法读取微信平台的数据,则用户就无法查看之前在小程序上的历史数据,甚至在App平台需要重新注册。这种情况对开发者是不利的。

因此,跨端的 serverless 方案才是开发者的最优解。

目前主流框架对云开发的支持情况:

  • Taro:仅支持微信小程序,详见小程序云开发模板
  • uni-app:DCloud 联合阿里云、腾讯云,提供基于 serverless 模式和 js 编程的云开发平台,支持App/H5/小程序所有平台,详见uniCloud
  • kbone:仅支持微信小程序,详见云开发

serverless 维度上,uni-app大幅领先其它框架。

插件市场

一个开发框架能否成功,除了框架自身的高度产品化外,开发者生态的构建也至关重要。

uni-app 于2018年底率先推出插件市场,支持前端组件、js sdk、页面模板、项目模板、原生插件等多种类型,且提供了赞赏、付费购买等手段,刺激轮子作者的创作激情。目前市场上已发布插件接近1500个,众多插件下载量都在万次以上。

Taro 于 2019年5月上线物料市场,目前市场上已发布物料90个;从热门榜单来看,下载量并不大,下载最多的也就数百。

kbone目前还没有插件市场。

Tips:

  • 插件数量及下载量数据采集时间为 2020.04.03 16:00

插件市场维度,uni-app独领风骚。

学习资源

除了各大框架官网外,开发者通常还会通过视频教程、社区博客等方式系统学习。

相关学习资源的丰富程度,也能侧面反映一个框架的受欢迎程度,故我们采集了几个三方站点的数据。

视频教程

框架腾讯课堂网易云课堂慕课网
taro412
uni-app16161

Tips:

  • 视频教程数据采集时间为2020.04.05 22:00

开发交流

除了入门的学习资源,开发期的交流也很重要,这个我们主要看官方组织的社区和交流群。

社区论坛

uni-app问答社区,帖子丰富,沉淀较多;目前已沉淀2万多相关帖子,每日更新帖子数百篇,月uv百万。

对于习惯使用 github issue反馈问题的用户,uni-app同样支持,目前累计有1391个issues。

Taro 早期基于github issue进行产品Bug管理,目前累计已有近4898个issue;后于2019年5月上线开发者社区,和物料市场上线时间相同,目前沉淀1300+帖子,每日更新帖子在10个左右,相关数据计算方法如下:

  • 帖子总数:Taro 社区顶部选择板块,计算每个板块下所有主题总数,如下图。
  • 每日更新帖子数:根据帖子列表中的最后回复时间,计算24小时内有回复或评论的主题总数

kbone 在微信开放社区中新增了一个Kbone官方框架的专区,因产品发布较晚,目前只有一百多个帖子。

总结一下社区帖子及issue数据,情况如下(采集时间为 2020.04.03 23:00):

交流群

框架微信群QQ群交流群开发者(预估)
taro16-8k
uni-app2040+90k
kbone-10.5k

Tips:

  • Taro 有16个微信群是根据 Taro 官网上显示Taro 开发交流 15 群 已满推论而出,每个微信群500人,交流群人数: 500*16 = 8000人
  • uni-app 官网 QQ群有35个,微信群20个,还有十几个专项QQ群,其中有30个QQ群达到2000人,交流群人数: 30 2000 + 5 1000 + 20*500 + 5000 = 90000人
  • kbone 在 github 的 readme中有一个qq交流群,申请加入时显示500人已满

除了交流群外,DCloud对外公布的uni-app的开发者数量达到百万人,暂未看到tarokbone公布此类数据。

总体而言,开发交流维度比较结果如下:uni-app > taro > kbone

其它指标

github

框架star贡献者
taro24.6k122
uni-app19.7k72
kbone2.7k7

在开源社区方面,Taro做的还是非常成功的,它吸引了更多开发者为其贡献代码、文档。

百度指数

通过index.baidu.com,可查看主流框架的搜索指数,它代表了网友的搜索量和相关文章的收录量。目前kbone尚未收录到百度指数中,如下是近期 uni-apptaro的百度指数对比图:

结语

所有的评测都只是提供决策依据,最后的结果还是要依赖开发者的团队技术栈、业务诉求、未来规划等。

不过作为一篇评测文章的结语,我们还是要给出自己的建议:

  • 如果你熟悉React,不懂Vue.js,推荐Taro;
  • 如果你熟悉Vue.js,则推荐 uni-app;
  • 如果你已经有H5代码,只想增加微信小程序平台,并且对性能要求不高,可以考虑kbone;
  • 如果你的业务涉及多端,更推荐 uni-app;
  • 如果你希望通过 serverless 方案快速上线业务,推荐 uni-app。

如有读者认为本文中任何评测失真,欢迎在这里报 issuse

查看原文

赞 33 收藏 13 评论 6

崔红保 发布了文章 · 3月23日

uni-app黑魔法:小程序自定义组件运行到H5平台

引言

移动互联网的初期,囿于设备硬件性能限制,流量以原生App为主,iOS、Android是当时两大平台。

随着硬件及OS的更新换代,H5可承载的体验逐步完善,为提高开发效率、节约资源(复用代码)以及热更新等目的,Hybrid模式成为主流;以及轻应用、服务号等平台的助推,H5网页流量暴涨,成为第三大平台。

2017年1月9日,微信发布小程序,历经3年发展,在今年主题为”未完成 Always Beta“的微信公开课 PRO上,微信团队披露,2019年小程序日活跃用户超过 3 亿,全年累计成交额达8000亿,同比增长超160%。

看到小程序如此惊人的增长力,我们有理由相信,有中国特色的小程序互联网时代已经到来,微信小程序也已成为继iOS、Android、H5之后的第四大流量平台。

平台分裂,为不同平台编写相同的业务代码,是件无趣的事情。

有追求的程序员,一直在探索代码复用的方案,Hybrid App即是代表。

而在如今的小程序时代,对于同样基于WEB技术的H5和小程序,如何实现代码复用,是很多前端工程师探索的方向。业内也已有不少成熟方案,从场景上来说,大致分为三类:

  1. 基于跨端框架,从头开发,一套代码,发行多个平台,比如DCloud出品的uni-app、京东凹凸实验室的taro
  2. 复用H5代码,转换H5代码在小程序环境中执行;适用于有H5平台沉淀,未开发小程序或小程序完善度较低的开发者;
  • 美团的mpvue框架是早期探索解决这个问题的代表,但因小程序不支持dom操作,故mpvue适用于vue的无dom操作的H5代码转换;
  • 最近微信官方推出的kbone,也是为了解决“把 Web 端的代码挪到小程序环境内执行”;不过,kbone 相比 mpvue 前进了一步(当然也有了新的性能缺陷),因为:

    kbone实现了一个适配器,在适配层里模拟出了浏览器环境,让 Web 端的代码可以不做什么改动便可运行在小程序里。
  1. 复用小程序代码,转换小程序代码在web环境中运行;适用于有小程序代码沉淀,未开发H5或H5平台完善度较低的开发者;这个方向业内成熟的方案还比较少。

uni-app近期支持了小程序自定义组件运行到H5平台,是对如上第三种场景的一种探索。

需求场景

鉴于小程序的低成本获客特征,很多厂商选择先开发小程序,验证业务模式后,再扩展至H5、App等其它平台。

开发者虽可借助转换器将小程序代码转换为uni-app项目(或其它跨端框架项目),快速实现多平台发行;但不少开发者是不敢轻易决策将跨端版本替换之前线上的小程序版本的,毕竟线上版本已稳定运行了一段时间。

常选的方案是:让原生小程序版本和uni-app跨端版本并行一段时间,微信平台继续使用原生版本,其它平台使用uni-app跨端版本;经过一段时间验证uni-app版本稳定后,再使用uni-app版替换掉原生小程序版本。

在这段并行的时间内,开发者需要同时维护微信原生、uni-app两个版本,新增业务需编写两份逻辑相同的代码,重复劳动,成本叠加,如何改善?

借助uni-app 支持将微信小程序组件运行到H5平台的特性,我们给出一种思路:

  • 开发者在原生小程序项目中,将新增业务以自定义组件的方式开发,优先上线小程序;
  • 拷贝小程序组件的wxml/wxss/js/json文件到uni-app 项目下,通过uni-app的编译器及运行时,保证小程序自定义组件在H5平台的正确运行。

这个方案的好处是:

  • 优先小程序开发,毕竟小程序早已上线,有存量用户
  • 复用小程序组件,新增业务仅需开发一套代码即可,降低开发成本

不止自己开发的小程序组件,业内开源的三方小程序组件,均可复制到uni-app项目项目中,运行到H5平台。

另外,部分公司的产品经理,会要求不同平台有不同的交互,但核心业务逻辑是相同的,开发者常会通过维护不同项目的方式来满足产品经理需求。此时,采取如上方案,同样可满足多个项目复用相同业务逻辑的诉求。

实际上,uni-app之前已支持将小程序自定义组件运行到App平台,对于有小程序组件沉淀或优先小程序的开发者来说,这是个好消息,一套业务组件,快速运行到iOS、Android、H5、微信小程序这四大流量平台(实际上也可运行到QQ小程序平台)。

uni-app 引用小程序组件演示

uni-app项目中使用自定义组件的方法很简单,分为三步:

1、拷贝小程序自定义组件到uni-app项目根目录下的wxcomponents文件夹下

2、在 pages.json 对应页面的 style -> usingComponents 引入组件,如:

{
    "pages": [
        {
            "path": "index/index",
            "style": {
                "usingComponents": {
                     "custom": "/wxcomponents/custom/index"
                }
            }
        }
    ]
}

3、在页面中使用自定义组件,如:

<!-- 页面模板 (index.vue) -->
<view>
    <!-- 在页面中对自定义组件进行引用 -->
    <custom name="uni-app"></custom>
</view>

方案实现思路

简单介绍下uni-app的多端发行原理。

uni-app基于 Vue.js runtime,页面文件遵循 Vue.js 单文件组件 (SFC) 规范,天然对H5的支持比较好,发行到H5平台时,先通过vue-loader解析.vue文件,导出 Vue.js 组件选项对象,然后在运行时补充规范实现:

  • 组件:框架提供内置组件(view/swiper/picker等)的实现,保证平台UI及交互的一致性
  • 接口:在H5平台封装框架接口,比如路由跳转,showToast等界面交互
  • 生命周期:Vue.js的理念是一切皆为组件,没有应用和页面的概念;框架需创造出应用及页面的概念,模拟onLaunch、onShow等钩子

uni-app发行到小程序平台时,逻辑又有不同,主要工作有2块:

  • 编译器:将.vue文件拆分成wxml/wxss/js/json4个原生页面文件
  • 运行时:Vue.js和小程序都是逻辑视图层框架,都有数据绑定功能;运行时会实现Vue.js到小程序的数据同步,及小程序到Vue.js的事件代理

小程序自定义组件类似小程序原生的页面开发,一个自定义组件同样由wxml/wxss/js/json 4个文件组成,另有单独的组件规范(如Component 构造器、Behaviors特性等)。

所以,小程序自定义组件运行到H5平台,可借助uni-app已有平台功能快速实现:

  • 编译阶段:将wxml/wxss/js/json4个文件合并为.vue文件(类似 uni-app 发行到小程序的逆过程),然后调用uni-app发行H5平台的编译过程,通过vue-loader解析.vue文件,导出 Vue.js 组件选项对象
  • 运行阶段:实现 Component 构造器、Behaviors特性,模拟自定义组件特有的生命周期

编译:转换文件(mp2vue)

小程序自定义组件发行到H5平台,在编译环节主要有2项工作:

  1. 将自定义组件的wxml/wxss/js/json 4个文件组成,编译转换成.vue文件,即小程序转vue,可简写为mp2vue
  2. 通过vue-loader解析.vue文件,导出 Vue.js 组件选项对象

其中,步骤2是Vue.js项目的标准编译过程,略过不提;我们重点阐述步骤1。

mp2vue将4个独立wxml/wxss/js/json 的文件合并成一个.vue文件,并组装成 templatescriptstyle 这种三段式的结构,流程包括:

  1. wxml文件生成template节点,同时完成指令、事件等模板语法转换
  2. js/json文件生成script节点,同时完成组件注册过
  3. wxss文件生成style节点,自动转换部分css兼容语法
  4. 合并为.vue文件

具体实现上,uni-app编译前先扫描wxcomponents目录,若存在则认为有小程序自定义组件,启动文件转换工作(uni-migration插件来完成):

//加载转换器
const migrate = require('@dcloudio/uni-migration') 
//扫描wxcomponents目录
const wxcomponents = path.resolve(process.env.UNI_INPUT_DIR, 'wxcomponents') 
if (fs.existsSync(wxcomponents)) { 
  migrate(wxcomponents, false, {
    silent: true 
  }) // 转换 mp-weixin 小程序组件
}

接着开始对wxml/wxss/js/json文件逐个解析,并合并为一个.vue文件:

module.exports = function transformFile(input, options) {
    //首先转换json文件,判断是否为组件
  const [jsCode, isComponent] = transformJsonFile(filepath + '.json', deps)
  options.isComponent = isComponent
    //转换 wxml 文件
  const [templateCode, wxsCode = '', wxsFiles = []] = transformTemplateFile(filepath + templateExtname, options)
    //转换wxss文件
  const styleCode = transformStyleFile(filepath + styleExtname, options, deps) || ''
  //转换js文件
  const scriptCode = transformScriptFile(filepath + '.js', jsCode, options, deps)
    // 生成合并后的.vue文件源码
  return [
    `${commentsCode}<template>
${templateCode}
</template>
${wxsCode}
<script>
${scriptCode}
</script>
<style platform="mp-weixin">
${styleCode}
</style>`,
    deps,
    wxsFiles
  ]
}

进一步细节说明,wxml文件转为template节点时,需完成各项指令、事件等模板语法的转换,例如:

小程序自定义组件Vue组件描述
wx:ifv-if条件渲染
wx:forv-for列表渲染
bindtap@click元素点击事件

将一个最简自定义组件,按照如上流程转换,结果示意如下:

运行时:模拟小程序组件环境

uni-app的编译器并不转换小程序组件的 JS 代码,依然保留Component构造器的写法,甚至其中的API依然是wx.开头的方式,这些都依赖uni-app在H5平台的运行时来解决,主要有如下几部分内容:

  • Component构造器:解析小程序组件的各种选项配置,转换为Vue组件定义,包括变通实现其中的差异部分,如小程序组件特有的”组件所在页面的生命周期“
  • Behaviors特性:转换为Vue的混入(mixin)
  • 数据响应:在H5平台实现setData接口及this.data.xx = yy的数据通讯机制
  • API前缀:可在运行时通过代理机制,自动将wx.xx替换为uni.xx,这个比较简单,不详述

Component构造器

uni-app在H5平台定义了一个Component函数,执行到小程序的Component构造器函数后,开始循环解析其属性,并转换成Vue组件属性,流程示意代码如下:

export function Component (options) {
  const componentOptions = parseComponent(options)
  componentOptions.mixins.unshift(polyfill)
  componentOptions.mpOptions.path = global['__wxRoute']
  initRelationsHandler(componentOptions)
  global['__wxComponents'][global['__wxRoute']] = componentOptions
}

export function parseComponent (mpComponentOptions) {
  const {
    data,
    options,
    methods,
    behaviors,
    lifetimes,
    observers,
    relations,
    properties,
    pageLifetimes,
    externalClasses
  } = mpComponentOptions

  const vueComponentOptions = {
    mixins: [],
    props: {},
    watch: {},
    mpOptions: {
      mpObservers: []
    }
  }

    // 开始逐个解析所有属性
  parseData(data, vueComponentOptions)
  parseOptions(options, vueComponentOptions)
  parseMethods(methods, vueComponentOptions)
  parseBehaviors(behaviors, vueComponentOptions)
  parseLifetimes(lifetimes, vueComponentOptions)
  parseObservers(observers, vueComponentOptions)
  parseRelations(relations, vueComponentOptions)
  parseProperties(properties, vueComponentOptions)
  parsePageLifetimes(pageLifetimes, vueComponentOptions)
  parseExternalClasses(externalClasses, vueComponentOptions)
  parseLifecycle(mpComponentOptions, vueComponentOptions)
  parseDefinitionFilter(mpComponentOptions, vueComponentOptions)
    // 返回 Vue 组件
  return vueComponentOptions
}

在这个过程中,需处理小程序自定义组件和 Vue组件的属性对应关系及细节差异,如小程序组件的lifetimes

小程序自定义组件Vue/uni-app描述
createdonServiceCreated小程序的created触发时,可以访问子组件信息,而Vuecreated访问不到,故需uni-app框架映射到其它时机(onServiceCreated)执行
attachedonServiceAttached同上
readymountedVue 生命周期
moved-Vue中不存在该钩子,暂不支持转换
detacheddestroyedVue 生命周期

小程序的pageLifetimes(组件所在页面的生命周期)在Vue中是没有的,需要映射为uni-app封装的页面生命周期:

小程序自定义组件uni-app描述
readyonPageShow页面被展示时执行
hideonPageHide页面被隐藏时执行
resizeonPageResize页面尺寸变化时执行

Behaviors特性的实现过程,类似Component构造器,不再赘述。

数据响应

Vue和小程序都有一套数据绑定系统,但机制不同,比如在Vue体系下,数据赋值是这样的:

this.a = 1

但在小程序中,数据赋值方式则是这样的:

this.setData({
    a:1
}) //响应式
this.data.a = 2 //非响应式

另外,小程序和Vue在数据的propertiesobserver等方面都存在不少差异,经过我们评估,若将小程序的数据响应用法直接映射到Vue体系下,复杂度较高且有性能压力,故uni-app在H5平台按照微信的语法规范,单独实现了一套数据响应系统。

// 小程序的setData在H5平台的实现
function setData (data, callback) {
  if (!isPlainObject(data)) {
    return
  }
  Object.keys(data).forEach(key => {
    if (setDataByExprPath(key, data[key], this.data)) {
      !hasOwn(this, key) && proxy(this, SOURCE_KEY, key);
    }
  });
  this.$forceUpdate();//数据变化,强制视图更新(响应式)
  isFn(callback) && this.$nextTick(callback);
}

setData挂载到 vm 对象上,可通过this.setData这种小程序的方式调用;同时将数据绑定到data属性上,支持this.data.xx的访问方式。

export function initState (vm) {
  const instanceData = JSON.parse(JSON.stringify(vm.$options.mpOptions.data || {}))
  vm[SOURCE_KEY] = instanceData
  
  //vm对象上挂载 setData 方法,实现小程序方法
    vm.setData = setData 
    
  const propertyDefinition = {
    get () {
      return vm[SOURCE_KEY]
    },
    set (value) {
      vm[SOURCE_KEY] = value
    }
  }
    //小程序用法,可通过this.data.xx访问
  Object.defineProperties(vm, {
    data: propertyDefinition,
    properties: propertyDefinition
  })

  Object.keys(instanceData).forEach(key => {
    proxy(vm, SOURCE_KEY, key)
  })
}

虽然数据响应是uni-app自己实现的,但渲染依然使用了Vue框架的render函数,此时需小程序规范中的this.data.xx和Vue规范中的this.xx保持一致,通过代理的方式实现:

// mp/polyfill/state/proxy.js
const sharedPropertyDefinition = {
  enumerable: true,
  configurable: true
};

function proxy (target, sourceKey, key) {
  sharedPropertyDefinition.get = function proxyGetter () {
    return this[sourceKey][key]
  };
  sharedPropertyDefinition.set = function proxySetter (val) {
    this[sourceKey][key] = val;
  };
  Object.defineProperty(target, key, sharedPropertyDefinition);
}

这里仅列出了主要的几步,中间涉及细节很多;部分无法通过Vue扩展机制实现的功能,只好修改Vue.js的内核源码,比如updateProperties支持、小程序wxsexternalClasses等功能在H5平台的支持,都需要定制部分 Vue.js runtime 源码。

结语

本文分享了uni-app将微信小程序自定义组件发行到H5平台的实现思路,希望对大家有所启发。

但这种方案,归根到底是为了解决多套项目并存时的业务重复开发的问题。

如果你是从头开发,我们建议直接选择业内成熟的跨端框架,既可以保持一套代码,更省力的维护,还可以借助框架的成熟生态(如跨端UI库插件市场),基于成熟轮子,快速完成业务的上线开发;

uni-app框架代码,包括小程序组件发行到H5平台的代码,全部开源在github,如果大家对本文逻辑有疑问,欢迎提交issue交流。

查看原文

赞 2 收藏 0 评论 0

崔红保 赞了回答 · 1月16日

uni-app的一个小问题

可以转换啊,有教程,有转换器:
https://ask.dcloud.net.cn/art...

关注 3 回答 2

崔红保 回答了问题 · 1月16日

使用uni-app打包的APP,能否接入腾讯广告联盟的SDK呢

uni-app目前已支持腾讯广点通、头条穿山甲、360联盟等广告源,建议查看uni-AD

关注 2 回答 1

崔红保 发布了文章 · 2019-10-28

使用uni-app开发小程序,比直接原生开发小程序好在哪里

小程序原生开发有不少槽点:

  1. 原生wxml开发对Node、预编译器、webpack支持不好,影响开发效率和工程构建流程。所以大公司都会用框架开发
  2. 微信定义的这套语法,wxml、wxs,以及wx:if等语法,私有化太强。不如正经学vue,学会了全端通用,而不是只为微信小程序
  3. vue生态里有太多周边工具,可以提高开发效率,比如ide、校验器、三方库。。。而微信的开发者工具和专业编辑器相比实在不好用,个性化设置也非常少

作为前端工程师,除了微信小程序,还要开发web、其他小程序甚至App,人们不喜欢来回切换开发工具和变更语法思考方式。

uni-app自然可以解决这些问题,但开发者又经常有些顾虑:

  1. 怕使用uni-app后,微信小程序里有的功能无法实现,受制于uni-app的更新
  2. 怕性能不如原生WXML
  3. 怕框架不成熟,跳到坑里
  4. 担心社区生态不完善

本文从开发者关心的功能、性能、学习门槛、开发体验、生态、可扩展性等维度,逐个分析对比,给予说明。

1.功能实现

开发者最常问的问题:如果小程序迭代升级,新增了一批API,但uni-app框架未及时更新,该怎么办?

其实这是误解,uni-app不限制底层API 调用;在小程序端,uni-app支持直接编写微信原生代码。

类比传统web开发,如果vue、react等框架的使用,造成开发者无法操作浏览器提供的所有api,那这样的框架肯定是不成熟的。小程序开发也一样,uni-app框架中,同样可调用微信提供的所有原生代码。

故如果存在某些API(平台特有或新增API),uni-app尚未封装,开发者可直接在uni-app中编写微信原生API,即wx.开头的各种API。

举个例子,目前uni-app虽然尚未封装跨平台的广告(ad)组件,但开发者在小程序端依然可以使用微信<ad>组件来展现广告,代码示例如下:

 <view>
    <view class="title">微信官方banner广告</view>
    <view style="min-height: 50px;">
        <!-- uni-app尚未封装,但可直接使用微信原生的ad组件-->
        <ad unit-id="adunit-01b7axxxbf53d74e"></ad>
    </view>
    <view class="title">微信官方视频广告</view>
    <view style="min-height: 50px;">
        <!-- uni-app尚未封装,但可直接使用微信原生的ad组件-->
        <ad unit-id="adunit-9f340xxx64533" ad-type="video" ad-theme="white"></ad>
    </view>
</view>

小程序端运行效果如下:

包括微信小程序自定义组件、WXS、云开发这些复杂用法,在uni-app里一样全面支持。

所以,结论是:使用uni-app框架开发,在功能上和原生小程序开发没有区别,不会有任何限制。

2. 性能体验

开发者常问的第二个问题:三方框架,内部大多做了层层封装,这些封装是否会增加运行负载,导致性能下降?

同样是多虑了,uni-app不会导致性能下载,甚至对很多环节做了自动优化,很多场景下性能体验比微信原生开发更好。

类似使用vue.js开发web,不但不会造成性能比原生js差,反而由于虚拟dom和差量更新技术的运用,在大多数场景下,比开发者手动写代码操作dom的性能还好。

小程序中需要频繁的写setData代码来更新数据,这里很重要的就是差量数据更新。如果不做差量,代码性能不好,如果每处逻辑都判断差量数据更新,那代码写起来太麻烦了。

使用uni-app,底层自动差量数据更新,简单而高性能。

我们从优化理论、实测数据两个维度来仔细说明。

2.1 理论:框架优化方案

为提高性能体验,小程序从架构设计层面做了很多工作:

  • 逻辑层、视图层分离,避免JS运算阻塞视图渲染
  • 单独定义组件标签(wxml),减少DOM复杂度
  • 精简样式(wxss),提升渲染性能
  • 复杂组件原生化(video/map等),解决web组件的功能/体验缺失

通过这些规范约束,大幅提升了小程序的整体性能体验,但依然存在不少性能坑点,其中以setData最为频繁普遍。

这里引用微信官方的描述,简单介绍一下setData背后的工作原理:

小程序的视图层目前使用 WebView 作为渲染载体,而逻辑层是由独立的 JavascriptCore 作为运行环境。在架构上,WebView 和 JavascriptCore 都是独立的模块,并不具备数据直接共享的通道。当前,视图层和逻辑层的数据传输,实际上通过两边提供的 evaluateJavascript 所实现。

为简化开发,微信将evaluateJavascript调用封装成了setData JS方法,实现视图层和逻辑层的数据传输,数据流示意图如下:

setData的执行会受到很多因素的影响,setData每次传递数据量过大或频繁被调用(见微信官方介绍),都可能引发性能体验问题。

幸运的是,uni-app在这两个方面都有优化。

2.1.1 减少 setData 传递数据量

假设当前页面有一个列表(初始值为a,b,c,d),现在要向列表后追加4个新列表项(e,f,g,h),我们分别以微信原生、uni-app 两种模式编写代码。

小程序原生代码:

page({
    data:{
        list:['a','b','c','d']
    },
    change:function(){
        let newData = ['e','f','g','h'];
        this.data.list.push(...newData);
        this.setData({
            list:this.data.list
        })
    }
})

如上微信原生代码,change方法执行时,会将list中的a,b,c,d,e,f,g,h8个列表项通过setData全部传输过去。

uni-app 代码:

export default{
    data(){
        return {
            list:['a','b','c','d']
        }
    },
    methods:{
        change:function(){
            let newData = ['e','f','g','h'];
            this.list.push(...newData)
        }
    }
}

如上uni-app代码,change方法执行时,仅会将list中的e,f,g,h4个新增列表项传输过去,实现了setData传输量的极简化。

uni-app借鉴了 westore JSON Diff库,在调用setData之前,会先比对历史数据,精确、高效计算出有变化的差量数据,然后再调用setData,仅传输变化的数据,这样就实现 setData 传递数据量的最小化,大幅提高通讯性能。

Tips:也许有些同学对传递数据从a,b,c,d,e,f,g,h8个列表项优化为e,f,g,h4个列表项,不以为然,但我们提醒,不要小看这个机制,上述只是demo示例。

  • 在实际列表场景中,每个列表项可能包含缩略图、标题、摘要、时间等各种信息,每个列表项数据都会更大(假设为1k);
  • 假设当前页面有20个列表项,连续上拉4次后,页面变成100条记录;如果再次上拉,页面变成120条记录时,情况会有不同
  • 上述微信原生的方式,将120条记录数据(120k)全部传输过去
  • 上述 uni-app 模式,仅会将新增的20条(101 ~ 120)记录数据(20k)传输过去,数据量是原生方式的1/6!
  • 当页面列表项数据越多,这个差别就越大,页面有200条记录时,uni-app传递数据量会变成微信原生数据传递量的1/10!

2.1.2 减少 setData 调用频次

假设我们有更改多个变量值的需求,我们分别以微信原生、uni-app 两种模式编写代码。

小程序原生代码:

change:function(){
    this.setData({a:1});
    this.setData({b:2});
    this.setData({c:3});
    this.setData({d:4});
}

如上四次调用setData,就会引发4次逻辑层、视图层数据通讯

uni-app 代码:

change:function(){
    this.a = 1;
    this.b = 2;
    this.c = 3;
    this.d = 4;
}

如上uni-app的代码,最后会被合并成{"a":1,"b":2,"c":3,"d":4}一条数据,然后仅调用一次setData完成所有数据传递,大幅降低了setData的调用频次。

uni-app之所以有这样的优势,是因为 uni-app 基于 Vue Runtime 深度定制实现,并借助了 Vue 的 nextTick 机制。

2.2 实测:性能对比数据

有了如上的理论分析,我们接着进行真机实测,用数据来对比。

测试模型如下:

  • 开发内容:开发一个仿微博小程序首页的复杂长列表,支持下拉刷新、上拉翻页、点赞。

仿微博的列表是一个包含很多组件的列表,这种复杂列表对性能的压力更大,很适合做性能测试。

  • 界面如下:

Tips:若有同学觉得测试代码写法欠妥,欢迎提交 PR 或 Issus,本项目下还有其它框架的测试代码,开发者可忽略

  • 测试机型:红米 Redmi 6 Pro、MIUI 10.2.2.0 稳定版(最新版)、微信版本 7.0.3(最新版)
  • 测试环境:每个框架开始测试前,杀掉各App进程、清空内存,保证测试机环境基本一致;每次从本地读取静态数据,屏蔽网络差异。

从触发上拉加载到数据更新、页面渲染完成,需要准确计时。人眼视觉计时肯定不行,我们采用程序埋点的方式,制定了如下计时时机:

  • 计时开始时机:交互事件触发,框架赋值之前,如:上拉加载(onReachBottom)函数开头
  • 计时结束时机:页面渲染完毕(微信setData回调函数开头)

Tips:setData回调函数开头可认为是页面渲染完成的时间,是因为微信setData定义如下(微信规范):

字段类型必填描述
dataObject这次要改变的数据
callbackFunctionsetData引起的界面更新渲染完毕后的回调函数

测试方式:从页面空列表开始,通过程序自动触发上拉加载,每次新增20条列表,记录单次耗时;固定间隔连续触发 N 次上拉加载,使得页面达到 20*N 条列表,计算这 N 次触发上拉到渲染完成的平均耗时。

测试结果如下:

列表条数微信原生uni-app
200770641
400876741
6001111910
80014061113
100016901321

说明:以400条微博列表为例,从页面空列表开始,每隔1秒触发一次上拉加载(新增20条微博),记录单次耗时,触发20次后停止(页面达到400条微博),计算这20次的平均耗时,结果微信原生在这20次 触发上拉 -> 渲染完成 的平均耗时为876毫秒,uni-app是741毫秒。

这个数据,可能违反了很多人的直觉,uni-app 的性能竟然比微信原生还好!

不必疑惑,这就是上面理论分析章节中,减少setData传递数据量优化方案的结果;微信原生每次传递全量数据,而uni-app在调用setData之前会自动做diff计算,每次仅传递变动的数据。

开发者使用微信原生框架,完全可以自己优化,精简传递数据,比如修改如下:

data: {
    listData: []
},
onReachBottom() { //上拉加载
    // 通过长度获取下一次渲染的索引
    let index = this.data.listData.length;
    let newData = {}; //新变更数据
    Api.getNews().forEach((item) => {
        newData['listData[' + (index++) + ']'] = item //赋值,索引递增
    }) 
    this.setData(newData) //增量数据,发送数据到视图层
}

经过如上优化修改后,再次测试,微信原生框架性能数据如下:

组件数量微信原生框架(优化前)微信原生框架(优化后)uni-app
200770572641
400876688741
6001111855910
800140610551113
1000169012601321

从测试结果可看出,经过开发者手动优化,微信原生框架可达到更好的性能,但 uni-app相比微信原生,性能差距并不大。

但原生开发需要开发者熟悉小程序通讯机制,有意识的去编写代码,精简数据;uni-app自动处理,自然是更省心。

这个结果,和web开发类似,web开发也有原生js开发、vue、react框架等情况。如果不做特殊优化,原生js写的网页,性能经常还不如vue、react框架的性能。

也恰恰是因为Vuereact框架的优秀,性能好,开发体验好,所以原生js开发已经逐渐减少使用了。

通过本章节性能优化的理论分析及数据实测,我们可以输出这么个结论:

  • uni-app 不会增加小程序运行负载,不会拉低运行性能
  • uni-app 自动处理了很多性能优化点,对不懂性能调优或不熟悉小程序架构设计的开发者,更友好,更省心

3.社区生态

3.1 周边轮子

小程序是脱离web自造生态,很多web生态中轮子无法使用。

微信小程序还是有周边生态的,而其他几家小程序平台的生态基本没建起来。

uni-app的周边生态非常丰富,在插件市场有近800个插件,详见 ext.dcloud.net.cn

首先uni-app兼容小程序的生态,各种自定义组件均可直接引入使用。在此基础上,uni-app的插件市场,有更多vue组件,同时可跨多端使用,并且性能优秀。

这使得uni-app的生态成为最丰富的小程序开发生态。

比如富文本解析、图表等组件,uni-app的插件性能均超过了wxparse、wx-echart等微信小程序组件。

如果开发者需要丰富和高性能的组件,更应该使用uni-app,而不是原生小程序开发。

3.2 活跃的QQ/微信群和论坛

uni-app官方有 70 个开发者QQ/微信交流群(大多2千人群,近10万开发者),三方群更多。

问答社区,每天有数百篇帖子。活跃度与微信小程序官方论坛相同,远超过其他小程序官方论坛。

uni-app三方培训活跃,腾讯课堂官方都为uni-app制作了课程,各种培训网站到处可见免费或收费的uni-app培训视频教程。

4.学习门槛、开发体验

首先微信原生的开发语法,既像React ,又像Vue,有点不伦不类,对于开发者来说,等于又要学习一套新的语法,大幅提升了学习成本,这一直被大家所诟病。

uni-app则对开发者更为友好,简单来说是 vue的语法 + 小程序的api。

它遵循Vue.js语法规范,组件和API遵循微信小程序命名,这些都属于通用技术栈,学习它们是前端必备技能,uni-app没有太多额外学习成本。

有一定 Vue.js 和微信小程序开发经验的开发者可快速上手 uni-app

没学过vue的同学,也不用掌握vue的全部,只需了解vue基础语法、数据绑定、列表渲染、组件等,其他如路由、loader、cli、node.js、webpack并不需要学。

因为HBuilderX工具搭配uni-app可以免终端开发,可视化创建项目、可视化安装组件和扩展编译器,也就是uni-app的学习门槛,比web开发的vue.js还低。

开发体验层面,微信原生开发相比uni-app有较大差距,主要体现在:

  • 更为强大的组件化开发能力:vue的组件开发比小程序自定义组件开发的体验要好很多
  • 应用状态管理:uni-app支持vuex
  • 使用 Sass 等 CSS 预处理器
  • 完整的 ES Next 语法支持
  • 自定义构建策略

开发工具维度,差距更大:

  • 微信开发者工具被吐槽无数
  • uni-app的出品公司,同时也是HBuilder的出品公司,DCloud.io。HBuilder/HBuilderX系列是四大主流前端开发工具(可对比百度指数),其为uni-app做了很多优化,故uni-app的开发效率、易用性非微信原生开发可及。

这里可以输出一个结论:如果你需要工程化能力,那就直接忘了微信原生开发吧。

5.未来扩展性

虽然当前产品仅要求发布到微信小程序,但若有一天,老板和外来的一个和尚喝完咖啡,转身就要求覆盖阿里、百度、字节跳动等各家小程序平台,此时程序员该怎么办?

难道真的每个平台到处搬砖吗?

此时,uni-ap的跨端功能将成为程序员的自救神器,基于uni-app开发的小程序,无需修改,即可同时发布到多家小程序,甚至App、H5平台。这不是梦想,而是现实。大家可依次扫描如下8个二维码,亲自体验最全面的跨平台效果!。

6.结语

uni-app微信
功能相同相同
性能常规场景更优需要自己编写复杂代码才能提高性能
社区生态丰富,更多高性能组件丰富
开发体验纯vue体验,高效、统一;工程化能力强语法私有化;工程化能力弱
多端能力同时支持H5、多家小程序、跨平台App只能用于微信小程序

结论:只开发微信小程序,也应该使用uni-app

查看原文

赞 6 收藏 1 评论 0

崔红保 赞了文章 · 2019-09-17

uni-app微信小程序接入人脸核身SDK


这几天使用uni-app开发某银行的一个微信小程序,需要集成接入腾讯云的人脸核身SDK,如上图所示,记录下整合接入过程及踩的一些坑,帮助后面需要的朋友们。关于uni-app接入人脸核身SDK有不懂的地方可以在下面提问,看到会及时回复。

申请服务

不是所有的企业都能够申请的,需要符合以下行业要求的客户才能申请
政务:政府机构或事业单位
金融:银行、保险
医疗:公立医疗机构
运营商:电信运营商
教育:公立教育机构
交通:航空、客运、网约车、交通卡、共享交通、轨道交通、租车
旅游:酒店
物流:快递、邮政、物流

由于SDK会调用小程序原生的wx.startFacialRecognitionVerify方法,所以总共得申请2个服务
SDK服务申请人脸核身服务
小程序查看申请流程(需要发送邮件申请,使用该服务的小程序的appid,后面开发也是用的这个)
重要的事情说3遍
以上这2个服务都需要申请,缺一不可。
以上这2个服务都需要申请,缺一不可。
以上这2个服务都需要申请,缺一不可。

下载SDK

由于不是我申请的,所以怎么下载我也不知道,听群里的人说的是SDK腾讯云下发给客户的。

SDK目录结构

image.png

SDK接入

参考腾讯云文档的接入方法:https://cloud.tencent.com/document/product/1007/31071
文档是针对原生小程序写的,所以页面引入的方法有所不同
由于uni-app不支持直接引入小程序的原生页面,所以这里能想到的就是将它当作成一个微信小程序的组件,然后uni-app的页面引入这个组件

解压引入SDK

在uni-app项目中新建wxcomponents目录,将SDK解压后放到该目录
image.png
pages.jsonglobalStyle中全局引入小程序的组件,注意引用的路径

"usingComponents": {
  "verify-mpsdk": "/wxcomponents/verify_mpsdk/index/index"
}

image.png

新建人脸核身页面

pages中新建人脸核身的页面face(名字可以随意,根据自己的需要起名),
pages.json中配置页面
image.png
face页面中引入verify-mpsdk组件
image.png
最终的人脸核身的页面访问就是/pages/face/face

初始化SDK

在需要的页面初始化SDK,如有个页面需要点击按钮进行人脸核身,就在这个页面进行初始化。
这个直接照着文档快速入门中的来就行了,这里就直接使用uni-app默认的index页面,
适当修改下即可,大概代码如下:

<template>
  <view class="content">
    <button type="primary"
      @tap="gotoVerify">
      进入人脸核身
    </button>
  </view>
</template>

<script>
    export default {
        data() {
            return {
                BizToken: ''
            }
        },
        onLoad() {
            // 初始化慧眼实名核身组件
            const Verify = require('@/wxcomponents/verify_mpsdk/main.js')
            Verify.init()
        },
        methods: {
            // 单击进入人脸核身按钮时,触发该函数
            gotoVerify () {
                this.BizToken = '' // 这里需要我们去客户后端调用DetectAuth接口获取BizToken
                // 调用实名核身功能
                wx.startVerify({
                        data: {
                            token: this.BizToken // BizToken
                        },
                        success: (res) => { // 验证成功后触发
                                // res 包含验证成功的token, 这里需要加500ms延时,防止iOS下不执行后面的逻辑
                                setTimeout(() => {
                                    // 验证成功后,拿到token后的逻辑处理,具体以客户自身逻辑为准
                                    console.log(res)
                                }, 500)
                        },
                        fail: (err) => {  // 验证失败时触发
                                // err 包含错误码,错误信息,弹窗提示错误
                                setTimeout(() => {
                                        console.log(err)
                                        wx.showModal({
                                            title: "提示",
                                            content: err.ErrorMsg,
                                            showCancel: false
                                        })
                                }, 500)
                        }
                })
            }
        }
    }
</script>

注意下这里的BizToken,需要调用后端服务接口来获取,
需要后端的同学调用腾讯云提供的DetectAuth来返回前端需要的BizToken
调试开发阶段我们可以先通过腾讯云提供的工具
API 3.0 Explorer
直接来获取这个BizToken
如果服务申请成功后控制台一般能找到SecretIdSecretKeyRuleId
注意EndpointRegion选择的地区得保持和申请时选择的地区一致。
填写完成后点击在线调用中的发送请求按钮,如果填的都对的话返回信息里面会有BizToken
拿到BizToken后就可以直接使用了,修改下上面的代码:
xxxxxxxxxxxxxxxxx就是拿到的BizToken

this.BizToken = 'xxxxxxxxxxxxxxxxx' // 这里需要我们去客户后端调用DetectAuth接口获取BizToken

image.png

开发调试

上面都做完后就可以进行调试了
需要先在项目中manifest.json中配置上小程序的appid,这个appid就是上面申请服务中的appid,不然无法开启调试。
image.png
然后运行到微信开发工具(这里就不多说了),如果提示不是开发人员,就让该appid的管理员将你加到开发组里面就行了。
运行成功后点击开发者工具的真机调试,扫描二维码开启真机调试模式。
接下来就是踩坑了,会出现各种问题。

踩坑及解决方法

Component is not found in path

这里开发者工具里面都是显示正常的,不会报这个错,
手机扫码进入调试后控制台会出现这个报错,
提示组件找不到,但是我们的路径都是对的,
Component is not found in path "wxcomponents/verify_mpsdk/index/index"
image.png
问题出在这里将verify_mpsdk当成自定义组件了,
小程序自定义组件引入的时候需要在文件JSON中指定"component": true
找到wxcomponents\verify_mpsdk\index\index.json文件,加入"component": true即可
重新开启调试扫码后上面的报错就没了。 
image.png

navigateTo:fail page

点击按钮调用gotoVerify后会报一个页面找不到的错
navigateTo:fail page "verify_mpsdk/index/index?isNotice=false" is not found
image.png
SDK默认的是跳转验证页面的地址是verify_mpsdk/index/index
文档找了半天也没找到相应的配置地址,最后在SDK里面搜索找到了这个地址。
所以只需要把这个地址改成我们所需要的地址就行了。
找到wxcomponents\verify_mpsdk\main.js,里面搜索verify_mpsdk/index/index,
找到后修改成上面人脸核身页面的地址pages/face/face
保存后重试就能跳转到人脸核身的页面了。

无操作、无报错大坑

进入人脸核身的页面后会发现啥操作都没,控制台也没报错,
image.png
一度认为我自己弄的有问题,搞了好久也没弄好,也提了个工单(腾讯云工单反馈率还是很快的,几分钟后就有人回复了,这点赞一个),
将代码和相关操作在工单里描述了下,对方也觉得的没问题,按照快速入门的代码应该是没问题的,对方也没找到啥问题,就让我加了一个腾讯云慧眼小助手的微信,
本想着下午加人家看看啥问题的,中午吃完饭闲着的时候将SDK里面的文件都格式化后终于在index.js里面找到问题所在了。
wxcomponents\verify_mpsdk\index\index.js文件中有个onLoad生命周期,
image.png
正常原生微信小程序进入到这个页面的时候会执行onLoad里面的代码,
但是我们上面将这个SDK当作是一个自定义组件了,
在uni-app中组件是不存在onLoad这个生命周期的,这个是页面所属的生命周期。
找到问题所在就好解决了,我们可以在人脸核身的页面pages/face/face手动执行onLoad
修改下pages/face/face的代码,如下:

<template>
  <view class="face">
    <verify-mpsdk ref="verifyMpsdk"></verify-mpsdk>
  </view>
</template>

<script>
    export default {
        data() {
            return {
                
            }
        },
        onLoad(i) {
            // 页面onLoad的时候手动调用
            this.$refs.verifyMpsdk.onLoad(i)
        }
    }
</script>

保存后重试,就能正常显示了
image.png

SDK图片异常

点击快速验证进入下一步及后面的步骤的时候发现,页面的图片都挂掉了不显示,
一开始我一直用的真机调试,页面上也不会出现破图,控制台也不会报图片异常的错误,
导致我不知道怎么进行拍摄身份证,以为会自动识别身份证然后自动下一步,
最后在开发者工具里面跑了一遍才知道是图片找不到了,然后拍照的图片按钮自然也就显示不了了。
image.png
image.png
最后在SDK里面搜索/verify_mpsdk/images,在下面文件中找到关键词,
wxcomponents\verify_mpsdk\templates\ocr\ocr.wxml
image.png
既然这种形式导致运行的时候图片找不到,我们可以把SDK所用的图片都复制到项目的static目录里
static中新建verify_mpsdk目录,将SDK中的图片即wxcomponents\verify_mpsdk\images
复制到static\verify_mpsdk中,最终形成以下目录形式
image.png
最后将wxcomponents\verify_mpsdk\templates\ocr\ocr.wxml中的/verify_mpsdk/images批量替换成
/static/verify_mpsdk/images后重试即可,然后就都正常了。
image.png
image.png

完整流程

最后用真机调试完整跑一把
image.png
image.png
image.png
image.png

备注:如果最上面的wx.startFacialRecognitionVerify服务没有申请到此时点击下一步的会弹出一个无权限的弹窗无法进行下一步

image.png
image.png

这里就是活体人脸检测了,需要将脸对准框框,点击开始后需要读几个数字,

image.png

最后验证通过后会回到之前的页面(调用gotoVerify()方法的页面),
验证成功后,会拿到一个BizToken
可以在wx.startVerify回调函数success中打印自行查看。
拿到BizToken后可以调用后端的接口,后端通过调用 GetDetectInfo 接口获取并返回本次核身的详细信息,包括身份证上的信息和身份证证图片等信息。
前端拿到这些信息后根据自己的程序需要做处理。

结语

整合过程中遇到不少问题,百度加google也找不到相关的详细信息,
人脸核身的相关文档都很简单,出现问题后无从下手,只能慢慢自己摸索解决了,
最后写篇文章记录下整个过程,也能帮到后面需要集成这个SDK的朋友们。

查看原文

赞 29 收藏 22 评论 8

崔红保 发布了文章 · 2019-09-16

谜之wxs,uni-app如何用它大幅提升性能

小程序技术领域,有几个谜一样的存在:微信的WXS、支付宝的SJS、百度的Filter

很多开发者都不明白为什么要造这种语言脚本的轮子出来,甚至很多开发者根本不知道它们的存在。

其实几大小程序平台创造它们,都是为了解决性能问题,但不得不吐槽下,设计的实在是很难用,文档也语焉不详。

uni-app支持将WXSSJSFilter编译到这3家小程序平台,同时还在App和H5实现了WXS的解析。为什么做这些事?也是为了性能。

比如uni-ui组件库中的swiperaction组件,就是列表项向左滑动时拉出几个挤压式联动的菜单按钮,这种流畅的跟手动画,正是借助于WXS机制实现的。

微信为何要创造WXS

WXS(WeiXin Script)是微信创造的一套脚本语言,它的官方说法是:“WXS 与 JavaScript 是不同的语言,有自己的语法,并不和 JavaScript 一致”。

那微信为何要脱离 JavaScript ,单独创造一套语言呢?这要从微信小程序的底层逻辑(运行环境)讲起。

小程序的运行环境分为逻辑层和视图层,分别由2个线程管理,其中:

  • WXML 模板和 WXSS 样式工作在视图层,界面使用 WebView 进行渲染
  • JavaScript代码工作在逻辑层,运行在JsCore或v8里

小程序在视图层与逻辑层两个线程间提供了数据传输和事件系统。这样的分离设计,带来了显而易见的好处:

  • 逻辑和视图分离,即使业务逻辑计算非常繁忙,也不会阻塞渲染和用户在视图层上的交互

但同时也带来了明显的坏处:

  • 视图层(webview)中不能运行JS,而逻辑层JS又无法直接修改页面DOM,数据更新及事件系统只能靠线程间通讯,但跨线程通信的成本极高,特别是需要频繁通信的场景

什么是需要频繁通讯的场景?最典型的例子就是用户持续交互的情况,比如触摸、滚动等。我们以侧滑菜单为例,假设在页面上滑动A元素,要求B元素跟随移动,一次滑动操作(touchmove)的响应过程如下:

  1. touchmove 事件从视图层(Webview)传递到逻辑层,中间会由微信客户端(Native)做中转

  1. 逻辑层处理 touchmove 事件,计算需移动的位置,然后再通过 setData 传递到视图层,中间同样会由微信客户端(Native)做中转

一次 touchmove 的响应需要经过 视图层、Native、逻辑层三者之间2个完整来回的通信,通信的耗时开销较大,用户的交互就会出现延时卡顿的情况。

除了滚动、拖动交互外,在for循环里对数据做格式修改,也会造成逻辑层和视图层频繁通讯。

其实这类通信损耗问题,在业内由来已久,react native和weex都有类似问题,weex提供了bindingx来解决。

但对于小程序来讲,这类问题解决起来更容易。因为其实视图层的webview,是有js环境的,只不过过去不给开发者开放。

如果在视图层的js直接处理滚动或拖动交互、直接处理数据格式,就能避免大量通信损耗。

但对于小程序平台而言,大量开放webview里的js编写,违反了它的初衷,比如开发者会直接操作dom,影响性能体验。所以小程序平台提出一种新规范,限制webview里可运行的js的能力。这就是wxs、sjs、filter的由来。

从本质来讲,wxs、sjs、filter是一种被限制过的、运行在视图层webview里的js。它并不是真的发明了一种新语言。

WXS特征及适用场景

WXS具备如下特征:

  • WXS是可以在视图层(webview)中运行的JS
  • WXS无法直接修改业务数据,仅能设置当前组件的classstyle,或者对数据进行格式化。要修改逻辑层的数据,需要通过 callMethod,传递参数给逻辑层
  • WXS是被限制过的JavaScript,可以进行一些简单的逻辑运算
  • WXS可以监听touch事件,处理滚动、拖动交互

故可以得出WXS的适用场景,主要包括:

  • 用户交互频繁、仅需改动组件样式(比如布局位置),无需改动数据内容的场景,比如侧滑菜单、索引列表、滚动渐变等
  • 数据格式处理,比如文本、日期格式化,或者国际化。通过WXS可以模拟实现Vue框架的过滤器,如下是一个通过wxs实现首字母大写的示例:
<wxs module="m1">
  //首字母大写
  var capitalize = function(value) {
    if (!value) return ''
    value = value.toString()
    return value.charAt(0).toUpperCase() + value.slice(1)
  }
  module.exports = {
    capitalize: capitalize
  }
</wxs>
<view class="content">
  <view class="text-area">
    <!-- title 为当前页面 data 中定义的初始数据 -->
    <text class="title">{{m1.capitalize(title)}}</text>
  </view>
</view>

uni-app如何支持WXS

uni-app遵循Vue单文件组件(SFC)规范,组件/样式/脚本是写在一个.vue文件中的,但微信小程序是多文件分离(wxml/wxss/js/json)的,所以在微信端的主要工作是扩展vue-template-compiler,解析template/style/script节点,并正确生成到对应的wxml/wxss/js文件中,具体编译工作如下图:

Tips-1:关于<wxs>标签重构为<script lang="wxs">的说明:

.vue文件中的<wxs>标签及内嵌WXS代码,在主流前端开发工具(vscode/HBuilderX等)中,均无法实现语法提示、代码高亮及格式化,故uni-app<wxs module="m1">重构为<script module="m1" lang="wxs">,便捷实现了语法提示、代码高亮等,如下为vscode/HBuilderX中对于<wxs>标签重构前后的代码高亮对比,明显重构为<script lang="wxs">后,开发体验更佳:

Tips-2:鉴于Vue的自定义标签规范,我们建议将<wxs><script lang="wxs">)和template平级编写

编译器的具体解析扩展工作,这里不详述,仅给出wxs生成的示例代码,让大家有个直观理解:

createFilterTag (filterTag, {
    content,
    attrs
  }) {
    content = content.trim()
    if (content) { //<wxs>标签内直接编写 wxs 代码
      return `<${filterTag} module="${attrs.module}">
        ${content}
        </${filterTag}>`
    } else if (attrs.src) { //外联 .wxs 文件
      return `<${filterTag} data-original="${attrs.src}" module="${attrs.module}"></${filterTag}>`
    }
  }

在保证编译正确的情况下,微信小程序运行时会正确解析并执行WXS脚本,框架runtime无需干预。

基于 WXS 提升性能体验的实现示例

下面的gif显示的内容,是借助 WXS 实现的一个swipeaction组件示例,列表项向左滑动时拉出几个挤压式联动的菜单按钮,跟手动画、回弹动画都很自然流畅。

这里简单给出主要实现思路:

  1. 在 vue 中引用 wxs 文件,并绑定 touch 事件
<template>
  <view class="uni-swipe_content">
      <!-- 可滑动的菜单项容器,绑定touch事件 -->
    <view :data-position="pos" class="move-hock"
      @touchstart="swipe.touchstart" @touchmove="swipe.touchmove" @touchend="swipe.touchend" @change="change">
      <view class="uni-swipe_box">
        <slot />
      </view>
      <view class="uni-swipe_button-group move-hock">
          <!-- 滑动后,右侧挤压式的联动菜单按钮-->
        <view v-for="(item,index) in options" :data-button="btn" :key="index"  class="button-hock">
        {{ item.text }}
        </view>
      </view>
    </view>
  </view>
</template>
<script module="swipe" lang="wxs" data-original="./index.wxs"></script>
  1. 在 wxs 文件中,处理 touch 事件逻辑,通过 translateX 移动元素位置
function touchstart(e, ins) {
    //记录开始位置及动画状态
  var pageX = e.touches[0].pageX;
  ....
}

function touchmove(e, ownerInstance) {
  var instance = e.instance;
  var pageX = e.touches[0].pageX;//获取当前移动位置
  //计算偏移位置
  var x = Math.max(-instance.getState().position[1].width, Math.min((value), 0));
  //设置左侧元素移动位置
  instance.setStyle({transform: 'translateX(' + x + 'px)'}) 
  
  //循环右侧挤压式联动菜单
  var btnIns = ownerInstance.selectAllComponents('.button-hock');
  for (var i = 0; i < btnIns.length; i++) {
    ...
    //设置每个联动菜单的移动位置
    btnIns[i].setStyle({transform: 'translateX(' + (arr[i - 1] + value * (arr[i - 1] / position[1].width)) + 'px)'})
    ...
  }
}

function touchend(e, ownerInstance) {
  var instance = e.instance;
  var state = instance.getState()
  //根据当前移动位置,实现菜单项的自动展开或回弹
  move(state.left, -40, instance, ownerInstance)
}

该示例的完整源码参考github

在这段代码中,响应手势并移动菜单,是在视图层直接完成的。而不用wxs的传统写法,实现这个功能就会很卡。首先是视图层接收到touch事件,然后传递给逻辑层,逻辑层的js响应touch事件,判断移动距离,再通知视图层更新界面元素的位置。在持续的拖动过程中,视图层和逻辑层不停交互通信,无法做到跟手的顺滑。

虽然我们了解了wxs的原理,但老实讲,wxs挺难用的,直到现在,大多数开发者仍然不会用它。比较合适的做法,还是一些框架的作者对它进行封装。uni-app提供的uni-ui组件库,就是这样做的,开发者只需要按标准vue组件的方式去引用uni uiswiperaction组件,就能得到流畅的滑动跟手菜单。

更多平台的兼容性

uni-app的App端也是一个小程序引擎,为了在App端实现流畅的跟手拖动,也实现和兼容了wxs。

其实H5平台倒不存在逻辑层和视图层通讯折损的问题,但为了平台兼容性拉齐,uni-app在H5端也实现了wxs机制。

这样编写wxs代码,在uni-app中可同时运行在App端、H5端、微信小程序端。

百度小程序的Filter过滤器和支付宝小程序的SJS,成熟度还比较低,目前只能处理基本的数据格式过滤,还不能响应touch等交互事件。

至于头条和QQ小程序,还不支持类WXS机制。

期待其他小程序平台尽快补齐这个重要功能,实现体验的提升。

uni-app目前也支持单独编写百度小程序的Filter过滤器和支付宝小程序的SJS,这两种脚本无法跨多端,仅支持自有平台。开发者若需使用,可分别编写wxs/filter/sjs脚本,然后依次通过script引用,uni-app编译器会根据目标平台,分别编译发行,如下为示例代码:
示例代码要有条件编译

<!-- App/H5/微信小程序平台调用wxs脚本 -->
<script module="utils" lang="wxs" data-original="./utils.wxs"></script>
<!-- 百度小程序平台调用filter.js脚本 -->
<script module="utils" lang="filter" data-original="./utils.filter.js"></script>
<!-- 支付宝小程序平台调用sjs脚本 -->
<script module="utils" lang="sjs" data-original="./utils.sjs"></script>

后续

用运行在视图层的js解决通讯阻塞,可能很多人都没意识到。希望本文能给大家解惑,解开WXS之谜。

其实小程序的性能体验优化,仍然有大量空间。DCloud团队在这个领域研究了6年,清楚小程序技术架构的优势,也清楚当前的问题。我们会继续分享这些问题及对应的解决方案,为小程序产业发展贡献力量。

本文涉及的uni-uiswiperaction组件,代码开源在https://github.com/dcloudio/uni-uiuni-app框架代码开源在 https://github.com/dcloudio/uni-app,欢迎大家 star 或提交 pr。

查看原文

赞 3 收藏 1 评论 0

认证与成就

  • 获得 83 次点赞
  • 获得 7 枚徽章 获得 0 枚金徽章, 获得 1 枚银徽章, 获得 6 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

  • mui

    最接近原生APP体验的高性能前端框架,github上star数过万

  • uni-app

    uni-app 是一个使用 Vue.js 开发跨平台应用的前端框架,开发者编写一套代码,可编译到iOS、Android、H5、小程序等多个平台。

注册于 2013-11-23
个人主页被 2.8k 人浏览