小程序技术|青训营笔记

114 阅读8分钟

这是我参与「第四届青训营 」笔记创作活动的的第17天

课程目标

  • 认识和了解小程序的业务产品价值
  • 学习和掌握小程序相关技术原理

小程序最大的价值就是产品价值,它构建了一个更加完整的生态也扩充的更多的业务场景,这是小程序最最最重要的价值。


发展历程

image.png

  • 2017、2018年是小程序发展初期,这个阶段最早由微信开始探索;因为是微信出品,所以小程序还未发布就已经有了很高的关注度。
    • 17年1月份小程序正式发布,但这时候还没有完全对个人开发者开放,主要针对企业开放,直到3月份,小程序正式面向个人开发者开放,自此,小程序数量进入爆发期。
    • 17年4月份,小程序码的新型图形码的到来,实现线下场景和线上小程序的沟通串联。
    • 17年9月份,支付宝小程序也开始了公测,标志着各大厂进入小程序领域开始竞争,围绕小程序构建和丰富独属于自己的生态。
    • 17年12月份轻度小游戏上线(跳一跳)。
    • 18年1月份,因为小程序可以打开 app ,这也标志着小程序可以为其它 app 引流的功能开放。

image.png

  • 18年各大厂相继上线自己的小程序,加速布局小程序生态。
  • 19年小程序被列入腾讯的战略,微信也丰富了小程序的入口以此来开放更多的流量。
  • 19年9月份,微信贴片广告标志着小程序正式开始商业化建设(业务最终都是为了赚钱),随着小程序越来越复杂,小程序4M 的限制无法满足需求。
  • 19年11月份,小程序开发包总包上限12M ,让开发者可以构建更加复杂的小程序应用。

image.png

  • 2020年疫情的出现加速各种小程序的出现,同时也为小程序赋予了直播和小商店等更多的属性,为小程序的商业化带来更多的可行性。
  • 越来越多的场景出现导致小程序整体也逐渐发展的越来越成熟。

核心数据

image.png 这是2020年底的数据,可以看出小程序的数量特别的庞大。

小程序生态

小程序是超级 app 发展到一个阶段的必然产物,因为这些超级 app 想要构建更多的场景,让更多人用只靠自己开发的程序是永远做不完的,所以需要开放出来给其他开发者做,小程序目前的生态也基本上是围绕各大超级 app 来的。


业务价值

与 web 区别

为了平台管控:

  1. 有着固定的语法以及统一的版本管理,平台可以更方便的进行审核。
  2. 平台能够控制各个入口,例如:二维码、文章内嵌、端内分享。入口上也能带来更好的用户体验。
  3. 小程序基于特殊的架构,在流畅度上比 web 更好,有更优秀的跳转体验。

三大价值

  1. 渠道价值。由于小程序的便捷性,依托于超级平台,小程序能够充分为很多场景导流,如美团和美团优选微信小程序带来的流量分别是40%和80%。
  2. 业务探索价值。相比原生 APP 来说,小程序的开发难度和成本都降低的很多,这就创造了很多场景开发者能够用小程序来快速试错,不断探索新的业务价值。
  3. 数字升级价值。线下到线上如何做?从轻消费类的快餐、茶饮到地产汽车等大宗消费,小程序都展示了良好的容错空间。线下场景的小程序覆盖范围广。

技术解析

小程序原理

怎么样让第三方开发应用最简单最方便?
        使用 webview 和 JSBridege 技术来开发。 webview 可以简单理解为 app 内置的浏览器,可以在 app 内浏览网页,但是除了 web 本身,想让开发者能够通过 JS 调用更多 app 上的功能怎么办?例如:打开相机、地图等,单靠 web api 是做不到的,这时候就需要 JSBridege , JSBridege 是 js 和 native 代码之间的桥梁,能让两者相互沟通相互调用,实现 JSBridege 的方式有很多,例如:代码注入、 url 拦截等。

这种方式的问题?

  1. 无网络的情况体验不佳
    • 解决:资源离线化。把整个资源下载到 app 上,网页求换也可以通过这种方式解决。
  2. 网页切换体验不佳(不管怎样, web 都要从远程加载资源)
  3. 如何管控保证安全
    • 对外开放平台先不说功能十分齐全,最重要的一点就是保证平台的安全,因为你永远无法杜绝有人恶意在你的平台上作恶。
    • 如果由人来审核,那么我们要把所有的网络链接都管控起来,经过审核的链接才可以在平台打开,先不考虑数量的问题,网页的动态性就无法解决。
      例如:审核是在2022-02-02之前完成的,然后到2022-02-02当天网页就变了。
    if(Date.now() > new Date('2022-02-02')) {
        document.body.innerHTML = '<h1>hello world</h1>'
    }
    

小程序设计目标:

  1. 开发门槛低
    • 大部分开发者都会且很容易学习的技术(HTML + CSS + JavaScript)。
  2. 接近原生的使用体验
    • 三个问题:资源加载 + 渲染 + 页面切换
    • 资源加载可以用资源离线化解决
    • 渲染:独立 JS 沙箱。
    • 页面切换可以每次切换页面时保留之前的页面,降低成本。
  3. 能够保证安全可控
    • 需要解决 web 灵活性问题, web 灵活是因为它可以用 js 来操作 DOM ,通过 js 来改界面。
    • 独立 JS 沙箱(把 DOM 的 api 都禁用掉,不允许使用。开发者没办法直接操作 DOM ,只能做一些 js 的逻辑)

不操作 DOM ,如何控制页面的渲染?
image.png 只需要关心数据流而不需要具体操作 DOM 就可以根据数据来渲染页面。

小程序整体的架构: image.png 在浏览器中,当 JS 操作频繁时动画会卡顿,因为它们是运行在同一进程中。这种结构将 JS 和渲染分离顺带解决了这个问题。
这样的通信结构决定了小程序的性能问题在数据传递。

小程序语法

以字节小程序为例:

<view tt:for="{{list}}" tt:if="{{isOpen}}" bindtap="onTap"/>
Page({
    data: {
        list: ["a", "b", "c"],
        isOpen: true
    },
    onTap: function() {
        console.log('tap me!')
    }
})

用 Page 对象包裹整个页面。

view {
    background-color: "red";
    width: 750rpx;
}

rpx (CSS 内没有)适配单位, iPhone6 写像素的话是375px ,小程序是把它划为了750rpx。

实现番茄时钟(倒计时)

<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)
    },
    onStart: function() {
        if(!this.timer) {
            this.time = DEFAULT_TIME;
            this.setTimer();
            this.setData({
                running: true
            })
        }
    },
    onReset: function() {
        clearInterval(this.timer);
        this.timer = null;
        this.setData({
            timeText: formatTime(this.time),
            running: false
        })
    }
})

image.png


相关拓展

目前的小程序跨端框架主要为了解决两个问题:

  1. 复杂应用构建。
  2. 一次开发可以跨多端。

跨端框架介绍

image.png

跨端框架原理

不论什么框架都逃不开这两种实现方式:

  • 编译时:JS 代码编译的时候转小程序的语法
  • 运行时:代码实际在客户端运行的时候来适配小程序的语法

编译时

先提一下 AST 语法树: image.png

image.png

Vue 当中的一段语法:<View>{foo ? <View /> : <Text />}</View>
对应小程序的语法: <view><block tt:if={foo}><view /></block><block tt:else><text /></block></view>

缺陷:

  • 无法完全抹平差异
    • 不论是 React 和 Vue 等各种框架,它们的用法都十分多样,而且会不断添加新的特性而小程序本身有很多的限制,在转换过程中,很多特性没有办法转换,所以就要在用框架写代码时加很多的限制,违背了初衷,现在更多的方案是采用运行时的方式。

运行时

实现主要依赖这两个部分:

  1. 虚拟 DOM
  2. template 组件

虚拟 DOM 本质上是 JS 中的一个对象,可以通过虚拟 DOM 来生成实际的 DOM 。 template 组件能帮助我们动态的生成模板。 image.png

运行时的结构: image.png 实际运行时, React 和 Vue 等框架运行在逻辑层,当它们想实际操作 DOM 时,把实际 DOM 换成虚拟 DOM ,运行完生成完虚拟 DOM 树后,把整个虚拟 DOM 树传递给渲染层,渲染层根据虚拟 DOM 的结构组合 template 生成实际要渲染的 DOM 树,小程序再根据实际的模板来创建页面。
setData 传数据多了是一个性能瓶颈,所以在实际实现的时候,这些框架也做了些优化,尽可能的减少 serData 数据传递。例如:逻辑层不必每次传整个虚拟 DOM ,只传 新增/修改/删除 的部分;长列表渲染的话,生成的 DOM 树可能很大,有的框架会做出虚拟列表,只展示能在实际页面中看得到的列表项,其余列表项能滚动到时再来渲染。

缺陷:

  • 在一些场景下相比小程序原生语法性能会更差
    • 数据量会比小程序原生语法大