乐趣区

使用uniapp开发小程序比直接原生开发小程序好在哪里

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

  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,h 8 个列表项通过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,h 4 个新增列表项传输过去,实现了setData 传输量的极简化。

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

Tips:也许有些同学对传递数据从a,b,c,d,e,f,g,h 8 个列表项优化为e,f,g,h 4 个列表项,不以为然,但我们提醒,不要小看这个机制,上述只是 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 实测:性能对比数据

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

测试模型如下:

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

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

  • 界面如下:

  • 开发版本:使用微信原生、uni-app 分别开发两套代码,uni-app 使用 cli 方式默认安装。
  • 测试代码开源(Github 仓库地址:https://github.com/dcloudio/test-framework),

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

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

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

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

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

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

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

测试结果如下:

列表条数 微信原生 uni-app
200 770 641
400 876 741
600 1111 910
800 1406 1113
1000 1690 1321

说明:以 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
200 770 572 641
400 876 688 741
600 1111 855 910
800 1406 1055 1113
1000 1690 1260 1321

从测试结果可看出,经过开发者手动优化,微信原生框架可达到更好的性能,但 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

退出移动版