前言
本篇文章的主要目的是,对未开发过小程序的同学或者对小程序感兴趣的同学,能够通过自己的文章,对小程序和小程序的开发流程有一定的了解
小程序简介
什么是小程序(mini program)
说到小程序,其实大家并不会很陌生,我们日常生活中也会在经常用,比如我们现在每天都会用的健康宝小程序;
定义
小程序简单来说就是一种不需要下载安装即可使用的应用,它实现了应用触手可及的梦想,用户通过扫一扫或者搜一搜即可打开应用,体现了用完即走的理念,用户不用太担心安装软件太多问题;
小程序发展史
字节小程序发展前景
平台条件
小程序想要发展起来,就离不开大的流量平台,而我们公司的巨头产品抖音、头条等拥有大量流量;因此,字节跳动小程序的平台条件无疑是很好的。
平台方向及发展意图
字节掌握大量的流量和内容,会流量变现,目前更多的变现方式是通过广告,开辟了一个新的领域,打造自己独立的电商体系,而小程序可以通过绑定头条各产品完成变现闭环,小程序是做电商最好的载体。
小程序和web端开发的区别
相同点
- 使用 js 开发逻辑层、使用 css 控制样式、使用html 进行页面渲染
不同点
- web 开发中每个页面的 js 线程与渲染线程是互斥的;小程序中 js 的运行进程和渲染进程是独立的,视图的渲染更新不会影响并不会阻塞 js 的执行;js的逻辑执行同样不会阻塞页面的视图渲染更新;
- 运行环境不同:web 运行环境为浏览器,需要处理不同浏览器之间的差异;小程序的环境差异主要是指不同小程序的宿主与不同的操作系统间的差异;
- 启动:web开发打开页面可直接通过 http 地址直接访问; 举例:小程序通过协议(如:snssdk1374://microapp)调起小程序后,小程序会以包的形式下发到当前打开小程序的 APP 上,并且装载本地的 js/xml/css等文件打开小程序,因此小程序不能像 web 端一样使用 location 等方式跳转页面,也不存在跨域;
- 小程序的 js 是运行在 jscore 中的而非 webview 中,所以无 DOM BOM 对象,无法使用 window 等浏览器提供的全局变量;小程序中也无法使用 Npm
- 代码上传:web 开发中代码一般会部署在自己的 CDN 或者 自己的服务器中,对于上传代码没有特殊限制;字节小程序代码是统一上传到字节服务器上统一进行管理,下发也是通过自己服务器统一下发的,所以开发过程中需要使用字节开发者工具进行代码上传
小程序开发流程
开发前准备
- 在字节开发者平台上注册一个账号
2.创建小程序
-
主体认证 & 完善信息
- 配置开发协作者
-
配置好相关接口域名白名单
-
安装字节开发者工具(这里简要介绍一下开发者工具的使用方式)
开发调试
- 新建项目
- 开发者工具关联小程序
- 编写代码
- 预览调试
- 上传代码
打包编译测试& 提交审核
小技巧:程序目前最大打包体积是 4 M 如果项目中有很多静态图片、字体文件等需要上传至 CDN & 去除项目中不必要的依赖包 最好都是按需引入
- 上传代码
- 本地编译
- 小程序开发者工具点击上传代码,发布测试版本
- 生成二维码
- 宿主 APP 进行扫码测试
- 测试版本无问题后,提交上线审核(一般1~2个工作日,需要提供小程序主要功能截图&勾选发布线上宿主,目前只有抖音头条等已上线的宿主,若发现申请上线的宿主不包含本次上线宿主,需要沟通小程序运营修改配置)
完成以上几个步骤,恭喜你的第一个小程序就完成了 !!!
小程序开发实践
基础概念
scheme:
一种模式,App内统一页面的跳转的跳转定义的路由规则,通过 scheme 吊起 App 的页面跳转;一般规则为:scheme://host/path?query=value
举例(巨量App):snssdk1374://host/path?query1=value1&query2=value2
jsbridge:
小程序和 Native 之间通信的桥梁
webview:
一种组件,用来承载网页的容器;目前小程序的一个特点是无法外跳 H5 页面,通过 webview 组件可以切入到小程序内部,铺满这个页面
小程序开发语言
逻辑层
主要工作:数据绑定、事件分发、生命周期、路由管理的操作
- app.js:小程序入口
- Page(): 页面入口
- Component(): 组件入口
小程序原理
小程序架构
小程序架构分为视图层和逻辑层,视图层每一个page都会是一个独立的 webview,小程序不允许打开超过 5 个以上的页面,会引起性能问题;视图层和逻辑层的数据通过 jsbridge 来进行实现;逻辑层如果需要更新某些数据通过jsbridge更新到视图层,视图层进行更新;同理视图层有事件产生通过jsbridge实现传递到逻辑层触发对应事件; 小程序的逻辑层可以调用一些API,如果是webview支持不了的,是由native支持的,native支持的过程也是通过jsbridge实现的;逻辑层主要运行在一个没有DOM BOM 对象的沙箱环境中,只能通过调用API 来实现和视图层和 Native 层交互;
小程序编译
为什么需要编译呢?
我们的 view button 组件如何渲染到页面上以及页面如何加载 ttml ttss ,简单理解为把原始代码转换为可以运行的代码称之为编译
小程序的源文件由 ttml (模板文件) ttss(样式文件) js 组成;视图层主体为page-frame.html,逻辑层主体为app-service.js
功能
- 基础功能
- 编译 ttml ttss js json
- 业务能力
- 热更新
- 生成二进制文件
- 本地调试
模板文件
所有.ttml文件最终会被编译成一个js函数,函数的返回结果就是这个.ttml结构文件对应的vdom对象,举例:
此处是将 TTML 转换成一系列的 render 函数,放于视图层中,待传入路由后调用特定的 render 函数来渲染为真正的dom节点
ttml 文件
<view class="foo" data-var="{{ bar }}">Hello, {{ user }}</view>
小程序经过编译后,会在 page-frame 中生成一个 对应的函数
` var render = function (ttData) {
var bar = ttData.bar,
user = ttData.user;
return [__h(
"tt-view",
{ className: "foo", "data-var": bar },
"Hello, ",
user
)];
render函数就是 page/index 对应的vdom对象生成函数;当render函数接收到数据ttData变化时,就会进行diff -> patch -> render的,传统的vdom更新操作。其中的标签“tt-view”,是使用的polymer的自定义标签组件。渲染出tt-view之后,又交给polymer渲染出真实dom。
JS编译
js部分将会和API、通信模块等一起打包到app-service.js为主体的文件中
babel编译
- 解析(parse): 代码字符串转换成抽象语法树
- 转换(transform): 对抽象的语法树进行转换(插件处理)
- 生成(generate): 根据变化后的抽象语法树生成代码字符串
ttss编译
Postcss编译过程:
- 解析: 将 css 字符串解析成抽象语法树
- 转换: 对抽象语法树进行转换操作,主要是通过( postcss插件)
- 生成 : 根据变换后的抽象语法树再生成新的 css
json转换
主要是将多个 json 文件最终合并转换为一个 config.json文件
目录结构
生命周期
应用生命周期
onLaunch
监听小程序初始化
当小程序初始化完成,触发onLaunch, 全局只触发一次
onShow
监听小程序显示
当小程序启动,或者从后台进入前台显示
onHide
监听小程序隐藏
小程序从前台到后台
PS: 前台、后台定义: 当用户点击左上角关闭,或者按了设备 Home 键离开,小程序此时并没有直接销毁,而是进入了后台;当再次进入或再次打开小程序,又会从后台进入前台。
页面生命周期
onLoad
页面加载时调用,一个页面只会调用一次
onShow
页面显示,每次打开页面都会重新调用一次
onReady
页面初次渲染完成,一个页面只会调用一次,页面已经准备妥当,可以与视图层交互
onHide
页面隐藏,当切换路由navigateTo或者切换底部 tab 时调用
onUnload
页面卸载,当redirectTo或navigateBack的时候调用
组件
组件类型
内置组件
基础组件,比如 button、view、text 等大部分组件,直接在页面引入使用即可
自定义组件
- 定义:内部组件的能力封装给开发者,让开发者可以封装符合业务的组件进行复用, 主要还是由 ttml ttss js json 文件构成。
- 创建:在 json 文件中将 component 设置 true
-
注册:在自定义组件的 js 文件中需要使用 Component 来注册组件
-
使用:需要在页面的 json 文件中进行引用声明,此时需要提供每个自定义组件的标签名和对应的自定义组件文件路径
路由
小程序启动
运行机制
小程序启动会有两种情况,一种是「冷启动」,一种是「热启动」。 假如用户已经打开过某小程序,然后在一定时间内再次打开该小程序,此时无需重新启动,只需将后台态的小程序切换到前台,这个过程就是热启动;冷启动指的是用户首次打开或小程序被主动销毁后再次打开的情况,此时小程序需要重新加载启动。
注意:
- 小程序没有重启的概念
- 当小程序进入后台,客户端会维持一段时间的运行状态,超过一定时间后会被主动销毁
小程序运行环境
字节跳动小程序,运行在字节跳动提供的小程序 SDK 上,但是由于 SDK 嵌入的宿主不同,小程序 SDK 提供的能力集合可能会根据宿主的不同而变化。
在 iOS 中,小程序的逻辑层 javascript 代码运行在 webWorker 中,视图层由 WKWebView 进行渲染。 在 Android 中,小程序的逻辑层 javascript 代码运行在 V8 中,视图层由 Mobile Chrome 进行渲染。
小程序与宿主交互
小程序在宿主上打开流程
- 需要知道小程序相关信息,包括appid,代码包地址等,这些信息可以保存在一个scheme中。该scheme一般由服务端下发,格式如下scheme://host/path?query=value
- 客户端拿到 scheme 后,就可以调用sdk中的MiniAppOpenTool.openAppbrand(..),进行打开小程序
- MiniAppOpenTool.openAppbrand最终会 打开MiniappTabActivity0或MiniappTabActivity1,2,3,4。这里每个小程序和小游戏都是在各自的独立进程中运行,目前最多只能打开5个新的进程。要打开第6个进程时,会先杀死最早创建的进程,再重新创建新的进程运行小程序或小游戏。
- 打开MiniappTabActivityX,该Activity是一个代理Activity,实际会调用具体的Activity。如果是小程序,会调用TTAppbrandTabUI;如果是小游戏,会调用TTAppbrandGameActivity
小程序和宿主通信
通过 bridge 进行通信
小程序踩坑&经验总结
开发者工具
- 开发者工具上样式和功能显示正常,但是真机调试时,有时会出现偏差
- 解决办法: 最终测试都需要以真机测试为准
-
开发者工具中为方便调试开发,可提前配置好编译模式
- 开发者工具中提供的真机测试二维码有效期为 1d
开发
- 布局尽量使用弹性布局,使用弹性布局能够更好的从整体上达到效果,兼容会更好
- 由于小程序性能的问题,在动态改变元素的宽高或位置时,会出现动画卡顿,可以使用 throttle,提高稳定性
- 页面中的自定义 component 过多时,会导致页面渲染有点卡顿
- 解决办法:可以使用 hidden 代替 tt:if 来避免重新渲染组件,一般来说tt:if 有更高的切换消耗而 hidden 有更高的初始渲染消耗。因此,如果需要频繁切换的情景下,用 hidden 更好,如果在运行时条件不大可能改变则 tt:if 较好
- tt.chooseImage 上传图片api,当用户首次拒绝授权时,不会二次调起相册,需要主动提示用户
- tt.uploadFile 各字段对应参数要注意& 在使用前需要配置域名白名单
-
字体文件需要在 app.js中引入
- 相关接口Api 权限申请要提前,审核流程比较慢
- 提前下载好 charles 抓包工具,方便调试时定位问题
- 真机调试时,右上角配置里可以开启小程序调试模式
- 部分接口 API 在安卓、ios 支持度和返回参数可能不一致,需要以每个机型真机测试为准
- 小程序 SDK 支持人脸识别功能,通过调用 tt.startFacialRecognitionVerify,有以下注意点
- 需要宿主 APP 提前接入小程序 SDK
- 需要宿主 APP 接入 Ailab 活体识别 SDK
- setData传输数据过多。导致更新卡顿。
- 尽量减少setData的传输数据,只传输视图使用到的数据,裁剪其他冗余数据
打包编译
- 执行打包编译命令后,需要手动复制project.config.js文件到 dist 中
- 字节小程序打包体积最大为 4M ,建议项目中静态文件和字体文件等上传CDN,使用的组件库等也建议按需引入,减少打包体积
小程序开发痛点
- 打包编译部署发布测试包比较麻烦,是否可以自动化部署(正在调研,近期会与 addone 平台同学了解一下)
- 基础种类还不够丰富
- 跨界面数据传递(多页面应用)
- 使用全局变量
- 使用本地缓存
- 使用url query-string
- 不支持 NPM
- 直接将需要引用的包直接复制到项目中
- ES6 ES7 支持不完整
- ES6支持大部分
- ES7 不支持
- 不支持的有: includes、async、await、Proxy
- ttss
- 无法使用本地资源
- 无法使用级联
- 不支持 less sass
主流开发框架
Taro实现多端编译原理
Taro1 & 2
整体思想:采用的是编译原理的思想,对一个输入的源代码进行语法分析、语法树构建,之后对语法树进行转换操作再解析生成目标代码
实现过程:
Taro3
整体思想:不同于 1 和 2 版本,采用的是解释型架构,主要是通过在小程序端模拟实现DOM、BOM的API来让前端框架直接运行在小程序环境中,从而达到小程序和H5统一的目的,对于生命周期、组件库、API、路由等差异,采用定义统一标准,各端负责各自实现的方式来抹平差异。
整体架构:
参考链接
https://www.infoq.cn/article/wawopvwhxb5nwq8f9sw4 (我眼中的小程序前世今生)
https://mp.weixin.qq.com/s?__biz=MzAxMTI4MTkwNQ==&mid=2650833967&idx=1&sn=e94b3e45ee1646ce0e8647d272a1eb8e&chksm=80b750b1b7c0d9a790740c4d896ec61a3e529b71c8ebe6f9c54367e35af8afc7e2aa87f01e2d&scene=27#wechat_redirect (App vs 小程序)
https://drive.google.com/file/d/18DSzrmSpYD7kcZj-BpBhrSW_DH2oJcLj/view(深入浅出小程序渲染)
https://drive.google.com/file/d/10lLDBK4txqvlCV2IU1g9xFGvu19UPn4t/view(小程序渲染)
https://drive.google.com/file/d/1ZKekXpBaUU6KWVgS_JINYw8CD6aCrup7/view(小程序及开发原理简介)
https://drive.google.com/file/d/1_hQ4lleqL8EkeDpp9luNxMfcjw6ZgFpL/view(小程序的编译设计与实践)
https://drive.google.com/file/d/1q7Bc_1TSLXHShtmwIWimNmHNVYy1B_Pi/view(字节小程序组件的实现)
https://drive.google.com/file/d/1kK-3DYryJFdM5182KcoH9fhq-geWKzNm/view(小程序 TTML 解析原理)