这是我参与「第四届青训营 」笔记创作活动的的第16天
初识小程序
课程目标
先看下课程目标:
-
认识和了解小程序的业务产品价值
-
学习和掌握小程序相关技术原理
为什么我们讲技术之前要先讲下产品价值,这是因为小程序带来的更大的价值就是它的产品价值。 它帮助例如微信这种超级app构筑了更加完整的生态,扩充了更多的业务场景,这才是小程序最最重要的价值。当然它也也在技术上带来了很多价值,我们后面都会讲。其实不论什么技术,最终都是要为业务服务的,技术本身不存在优劣,只存在是否适合。
我们这门课一共分为四个部分,前两部分跟技术无关,主要帮助大家了解小程序的发展历程以及业务价值。
01. 发展历程
第一部分除了发展历程,我们也简单看下当前小程序的一些数据和生态。
17、18年是小程序的发展初期,这个阶段最早由微信开始进行探索。17年1月小程序正式发布,这个时候小程序就有了很高的的关注度,但这个时候还没有完全对个人开发者开放,到3月份,小程序正式面向个人开发者开放,自此,小程序数量进入爆发期。4月份最重要的带来了称为小程序码的新型图形码,为什么说小程序码很重要。因为它的到来真正能够让线下场景和线上小程序沟通串联起来。 9月份支付宝小程序也开始了公测,标志着各大厂竟相进入到小程序领域开始竞争,因为围绕着小程序各大超级app能够构筑和丰富独属于自己的生态。12月份轻度游戏,小游戏上线,跳一跳风靡一时,不知道大家都有没有玩过。 进入18年,小程序在1月份带来了打开app的能力,这也标志着小程序为其他引流功能的功能开放。
同时在18年,百度小程序,qq小程序、头条小程序(现在叫字节小程序),都相继上线。“巨头”加速布局小程序生态。 19年,小程序被纳入腾讯最高战略,同时微信为小程序带来更加丰富的入口,开放更多的流量,如,微信主页下拉出现小程序桌面,微信搜索也可以搜索到小程序,同时微信公众号也可以自由挂载小程序,这些入口意味着更多的场景渗透。 9月份,小程序开放贴片广告,正式开始商业化建设,其实对所有企业来说业务最终都是为了赚钱嘛。随着小程序越来越复杂,小程序包4M的限制越来越无法满足,所以在11月份小程序开发包的总包上线上升至12M,让开发者能够构建更加复杂的小程序应用。
进入2020年疫情的出现也加速了各种小程序的出现,同时微信为小程序赋予了直播和小商店更多的属性,为小程序的商业化带来更多的可行性。总之就是越来越多的场景,小程序整体也逐渐发展的越来越成熟。
小程序虽然出现不久,但是数量已经十分庞大,以后也只会更多。
小程序是超级App发展到一个阶段的必然产物,因为这些超级App想要构筑更多的场景,让更多的人用只靠自己做是永远做不完的,所以需要开放出来给其他开发者做。所以小程序目前的生态也基本是围绕各个超级app来的,比如腾讯系和阿里系小程序等等。
02. 业务价值
第二部分在说业务价值之前会简单和Web做个对比。
与Web的区别
-
有着固定的语法以及统一的版本管理,平台可以更方便的进行审核。
-
平台能够控制各个入口,如二维码,文章内嵌,端内分享。入口上也能带来更好的用户体验。
-
小程序基于特殊的架构,在流畅度上比Web更好,有更优秀的跳转体验。
三大价值
-
渠道价值:由于小程序的便捷性,依托于超级平台,小程序能够充分为很多场景导流,如美团和美团优选微信小程序带来的流量占比分别是40%和80%。
-
业务探索价值:相比原生APP来说,小程序的开发难度和成本都降低的很多,这就创造了很多场景开发者能够用小程序来快速试错,不断探索新的业务价值。
-
数字升级价值:线下到线上如何做?从轻消费类的快餐、茶饮到地产汽车等大宗消费,小程序都展示了良好的容错空间。我们线下场景的小程序覆盖范围很广。
03. 技术解析
第三部分会讲下小程序的原理、语法,以及会带大家一起看下一个简单的番茄时钟在小程序中要怎么实现。
小程序原理
下面来到我们同学更关心的小程序技术的讲解部分。 首先我们抛开小程序不想,我们自己一步步来设计我们的方案,我们先一起来看一个问题:
我现在有一个超级app,比如说抖音,微信,我要让外部的开发者在我的平台上开发三方应用,怎样是最简单的?
第三方开发应用最简单最方便的方式
WebView + JSBridge
很多同学可能也想到了最简单的是使用我们的Web技术来开发,其实没错。这里写的是WebView和JSBridege。 简单解释下这两个词,WebView我们可以简单理解为app内置的浏览器,我们可以在app内展示网页,但是除了Web本身,我们想让开发者能够通过js调用更多app上的功能怎么办 App上的功能比如打开相机,打开地图等,这些单靠WebAPI本身做不到,这就需要用到我们的JSBridege了,顾名思义JSBridge就是js和native代码之间的桥梁,让两者能够相互沟通调用,实现JSBridge的方式有很多,如代码注入,url拦截等,感兴趣的同学可以自己查下,我们不展开说了。总之它的作用就是让js和native代码能够相互沟通和调用。
那么我们这种方式有没有什么问题那?
-
无网络的情况体验不佳
-
网页切换体验不佳
-
如何管控保证安全
提出问题,资源离线化。最重要的一点如何管控保证安全,我们对外开放先不说功能是否齐全,最重要的一点就是要保证平台的安全,因为你永远无法杜绝有人恶意在你的平台作恶。这个问题是很大的,我们先想下我们的方案,比如我们可以靠人来审核,我们把所有的网页链接都管控起来,经过我们审核的链接才可以在平台打开,先不考虑数量的问题,网页的动态性我们无法解决。
如何管控保证安全?
看下这段代码。
if (Date.now() > new Date('2022-02-02')) {
document.body.innerHTML = '<h1>hello world</h1>'
}
这样其实在审核时看到的内容和实际上线时看到的内容是不一样的,这是由Web它的动态性和灵活性决定的,所以到这里我们的Web方案有一些走不通,那么我们就需要设计一种新的方案,来解决我们这些问题,这个方案需要有如下特点:
-
开发门槛低
-
接近原生的使用体验
-
能够保证安全可控
先说三个设计的目标。然后逐个思考能够达到这个目标的方案。
开发门槛低,就是大部分开发者都会,并且很容易学习的技术。没错,就是我们的前端三板斧。那你说 React 或者 Vue 的门槛算不算低,这个仁者见仁,智者见智,但总体来说还是我们的三板斧门槛更低。
HTML + JS + CSS
接近原生的使用体验
资源加载 + 渲染 + 页面切换
我们要解决这三个问题,资源加载我们可以用资源离线化来解决,渲染的问题我们等下说,页面切换的问题我们可以每次切换页面保留之前的页面,降低成本。我们可以使用这个结构:
多WebView
利用多个WebView来承接页面,每个页面都用一个WebView来承接,切换页面只是切换WebView,并不会把其他的页面给销毁掉,这样能尽可能减少切换页面时重新渲染的成本。
安全管控
要解决安全管控的问题就要先解决Web灵活性的问题,Web之所以灵活是因为它可以通过JS操作Dom,也就是用JS来改变页面,为了解决这个问题我们就不能让开发者来通过JS来操作Dom,我们需要一个独立的环境,即独立JS沙箱,把Dom的API都禁用掉,都不允许使用,在沙箱内只能做一些JS的逻辑,没有办法使用Web上Dom的API。
这又带来一个问题,不操作Dom如何渲染页面。其实这个方案,之前很多框架都有,比如React,我们只需要关心数据流而不需要操作具体的Dom就可以根据数据来渲染页面,这样一个结构
我们这三个问题都提出了相应的方案,下面我们把这些合在一起。 形成这样的方案:
我们前面渲染问题还留了个坑,在浏览器中,当js操作频繁的时候我们的动画就会卡顿,因为他们是在同一进程中的,我们这种结构将js和渲染分离顺带解决了这个问题。这样的通信结构,决定了小程序的性能问题在于数据传递。
小程序语法
TTML(对应HTML)
<view
tt:for="{{list}}"
tt:if="{{isOpen}}"
bindtap="onTap"
/>
JS
Page({
data: {
list: ["a", "b", "c"],
isOpen: true
}
onTap: function() {
console.log('tap me!')
}
})
TTSS(对应CSS)
view {
background-color: "red";
width: 750rpx; // 1rpx = 0.5px 小程序小屏幕,通过这个来解决手机端适配的问题
}
实现番茄时钟
TTML
<view class="container">
<view class="clock">
{{ timeText }}
</view>
<button tt:if="{{ running }}" class="button" bindtap="onReset">重置</button>
<button tt:else class="button" bindtap="onStart">开始</button>
</view>
const DEFAULT_TIME = 25 * 60;
function formatTime (time) {
const minutes = Math.floor(time / 60);
const seconds = time % 60;
const mText = `0${minutes}`.slice(-2);
const sText = `0$[seconds}`.slice(-2);
return `${mText} : ${sText}`
}
Page({
data: {
timeText: formatTime(DEFAULT_TIME),
running: false,
},
setTimer: function () {
this.timer = setInterval(() => {
this.time = this.time - 1;
if (this.time < 0) {
clearInterval(this.timer);
return;
}
this.setData({
timeText: formatTime(this.time),
})
},1000)
},
// ...more
})
Page({
// ...more
onStart: function () {
if (!this.timer) {
this.time = DEFAULT_TIME;
this.setTimer();
this.setData({
running: true,
})
}
},
onReset: function() {
clearInterval(this.timer);
this.timer = null;
this.time = DEFAULT_TIME;
this.setData({
timeText: formatTime(this.time),
running: false,
})
}
})
04. 相关拓展
最后一部分会围绕当前一些比较火热的小程序跨端框架来带大家一起看下这些跨端框架的原理是怎样的。
跨端框架介绍
目前的小程序跨端框架主要为了解决两个问题:
-
复杂应用构建
-
一次开发可以跨多端
跨端框架有哪些?
跨端框架原理
下面我们来说下跨端框架的原理,其实不论什么框架,都逃不开这两种实现方式,解释编译时和运行时。
我们先来说下编译时的方案。
编译时
说编译时方案时我们需要了解ast,这里先说下ast
Vue
<View>{foo ? <View /> : <Text />}</View>
ast分析后发现有一个判断,然后替换为小程序语法。
小程序
<view>block tt:if={foo}><view /></block><block tt:else><text /></block></view>
编译时方案有个天然的缺陷,无法完全抹平React和Vue这种框架语法和小程序语法之间的差异,不论是React和Vue等各种框架,它们的用法都十分多样,而且会不断添加新的特性,而小程序本身确有很多的限制,那么在转换过程中,很多特性没有办法进行转换,所以就要给框架的书写代码的时候加上很多限制,这背离了我们的初衷,所有现在更多的方案采用运行时的方案。
运行时
运行时的方案能够实现主要依赖这两个部分,一个是虚拟DOM,一个Template组件。
先说过程,当然运行时的方案也不是完美的,我们前面在讲小程序原理的时候不是说setData是小程序的性能瓶颈吗,运行时的方案,因为要传递虚拟DOM的各种属性到渲染层。
在一些场景下相比小程序原生语法性能会更差。
比如长列表,当然现在针对各种场景出现了有很多优化手段。
总结
完结撒花♪(^∇^*)
不论什么技术,我们如果能了解它的来源,发展历程以及底层的运行逻辑,那都可以帮助我们成长吸收这些思想,最终形成属于我们自己的东西。
近一个月的课程收获颇丰,听着字节的讲师们传授的课程确实感觉与在学校上课大不相同,期待第五届青训营O(∩_∩)O哈哈~