今天是我参加字节青训营的第五天,通过这节课,我们将了解一些小程序相关技术原理,主要从发展历程、业务价值、技术解析和相关拓展四个方面来介绍。
发展历程
总体来说,小程序的发展仍在继续,因为小程序相较于app的多种优势,未来,全网小程序的数量将会继续攀升。
小程序是超级app发展到一个阶段的必然产物,因为这些超级app想要构筑更多的场景让更多的人使用,只靠自己做是永远也做不完的,所以需要开放出来给其他开发者做。所以目前小程序的生态也基本是围绕各个超级app来的,这里放上当下主流的小程序生态。
业务价值
这里首先我们来了解一些小程序与Web的区别。
与Web的区别
小程序基于便利平台管控的目的,主要有以下三点不同:
- 有固定的语法以及统一的版本管理,平台可以更方便的进行审核。
- 平台能够控制各个入口,如二维码、文章内嵌、端内分享。入口上也能带来更好的用户体验。
- 小程序基于特殊的架构,在流畅度上比Web更好,有更优秀的跳转。
三大价值
- 渠道价值
由于小程序的便捷性,依托于超级平台,小程序能够充分为很多场景导流。
- 业务探索价值
相比于原生app来说,小程序的开发难度和成本都降低很多,这就创造了很多场景开发者能够用小程序来快速试错,不断探索新的业务价值。
- 数字升级价值
从轻消费类的快餐、茶饮到地产汽车等大宗消费,小程序都展示了良好的容错空间。我们线下场景的小程序覆盖范围很广。
技术解析
第三方开发应用最简单最方便的方式就是WebView + JSBridge。
- WebView:可以理解为app内置的浏览器,可以在app内展示网页。
- JSBridge:开发者希望能够通过JS调用更多app上的功能,这时就需要JSBridge。它是
JS和native代码之间的桥梁,让两者能够沟通相互调用。
实现JSBridge的方式:
- 代码注入
- url拦截
使用小程序的方式来展现某平台的网页就要做到资源离线化,这时不得不考虑的问题就是怎样才能管控保证平台的安全。
if (Date.now()>new Date('2022-02-02')){
document.body.innerHTML='<h1>hello world</h1>'
}
要提高小程序的安全性,我们首先要考虑三个设计目标:
1.开发门槛低。(HTML + JS + CSS) 2.接近原生的使用体验
- 资源加载:通过资源离线化解决。
- 渲染:将JS和渲染分离顺带。
通过这种结构,我们让JS和渲染分别处于两个不同的进程,这在一定程度上可以解决操作频繁时的页面卡顿问题。
- 页面切换:在每次切换页面时保留之前的页面,降低成本。
3.能够保证安全可控。
独立的JS沙箱:在之前的网络安全相关课程中,其中一种攻击方式就是插入JS脚本来操作DOM以达到恶意攻击目的。那么相应的,我们可以将DOM的api都禁用,这是我们只需要解决不操作DOM如何渲染页面的问题。 通常的,我们只需要关系数据流而不需要操作具体的DOM就可以根据数据来渲染页面。主要实现流程如下图:
小程序语法
- 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:
view{
background-color:"red";
width:750px;
}
相关拓展
跨段框架:适用于复杂应用构建,一次开发可以跨多端。下面举一些例子:
跨端框架的原理
不论什么框架,都逃不开编译时和运行时两种实现形式。
- 编译时
<View>{foo ? <view /> : <Text />}</View>
<view><block tt:if={foo}><view /></block><block tt:else<text /></bolck></view>
编译时方案有个天然的缺陷,无法完全抹平差异。各种跨端框架的用法的十分多样,而且会不断添加新的特性,而小程序本省却有很多的限制,在转换过程中,很多特性没有办法进行转换,所以就要给框架的书写代码加上很多限制,这其实背离了我们的初衷,因而当下更多的趋向于采用运行时的方案。
- 运行时
<template name="REMAX_TPL_cancas">
<canvas
animation="{{i.props['animation']}}"
binderror="{{i.props['binderror']}}"
bindlongtap="{{i.props['bindlongtap']}}"
bindtouchcancel="{{i.props['bindtouchcancel']}}"
bindtouchend="{{i.props['bindtouchend']}}"
bindtouchmove="{{i.props['bindtouchmove']}}"
bindtouchstart="{{i.props['bindtouchstart']}}"
canvas-id="{{i.props['canvas-id']}}"
class="{{i.props['class']}}"
disabled-scroll="{{i.props['disabled-scroll']}}"
height="{{i.props['height']}}"
id="{{i.props['id']}}"
style="{{i.props['style']}}"
type="{{i.props['type']}}"
width="{{i.props['width']}}"
>
<block tt:for="{{i.children}}" tt:key="{{id}}">
<template is="{{'REMAX_TPL_' + i.nodes[item].type}}" data="{{i: i.nodes[item]}}" />
</block>
</canvas>
</template>
运行时的方案主要依赖于虚拟DOM和template组件两个部分,所以在采用这种方案时,我们需要传递虚拟DOM的各种属性到渲染层,所以在一些场景下相比小程序原生语法性能可能会更差。(比如长列表)