小程序开发
开发小程序的技术选型
- 原生小程序开发:
- 选择框架开发小程序:
- uni-app:uni-app 是一个使用Vue 开发 所有前端应用的框架,开发者编写一套代码,可发布到iOS、Android、Web(响应式)、以及各种小程序(微信/支付宝/ 百度/头条/飞书/QQ/快手/钉钉/淘宝)、快应用等多个平台。
- taro:taro 是一个开放式 跨端 跨框架 解决方案,支持使用 React/Vue/Nerv 等框架来开发 微信 / 京东 / 百度 / 支付宝 / 字节跳动 / QQ / 飞书 小程序 / H5 / RN 等应用;在Taro3.x之后,支持Vue3、React Hook写法等;
前置知识
- 注册账号:
- 小程序的开发工具
- 微信开发者工具(必须下载)
- VSCode
项目的结构和目录

app.json:整个小程序的配置都在这里pages:所有的页面都需要在这里配置- 一般先新建一个文件夹,然后新建page
- 也可以直接在pages新建,然后保存,编辑器会帮我们创建好对应的文件
小程序的双线程模型
小程序的宿主环境:微信客户端
宿主环境为了执行小程序的各种文件:wxml文件、wxss文件、js文件
当小程序基于 WebView 环境下时,WebView 的 JS 逻辑、DOM 树创建、CSS 解析、样式计算、Layout、Paint (Composite) 都发生 在同一线程,在 WebView 上执行过多的 JS
以此为前提,小程序同时考虑了性能与安全,采用了目前称为「双线程模型」的架构。逻辑可能阻塞渲染,导致界面卡顿
双线程模型
- WXML模块和WXSS样式运行于 渲染层,渲染层使用 WebView线程渲染(一个程序有多个页面,会使用多个 WebView的线程)。
- JS脚本(app.js/home.js等)运行于逻辑层,逻辑层使 用JsCore运行JS脚本。
- 这两个线程都会经由微信客户端(Native)进行中转交互。
小程序配置文件
常见配置文件
project.config.json:项目配置文件, 比如项目名称、appid等;
sitemap.json:小程序搜索相关的;
app.json:全局配置;
page.json:页面配置;
修改本地配置文件,一般修改
project.private.config.json,并且将其添加到.gitignore中
全局app配置文件
全局配置,较多查看官网
pages: 页面路径列表
用于指定小程序由哪些页面组成,每一项都对应一个页面的 路径(含文件名) 信息。
小程序中所有的页面都是必须在pages中进行注册的。
window: 全局的默认窗口展示
用户指定窗口如何展示, 其中还包含了很多其他的属性
tabBar: 底部tab栏的展示
页面page配置文件
每一个小程序页面也可以使用 .json 文件来对本页面的窗口表现进行配置。
- 页面中配置项在当前页面会覆盖 app.json 的 window 中相同的配置项。
- https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/page.html
- 引用非自带的组件时,需要在.json中配置
- 顶部标题的特殊定制,等等
注册小程序
每个小程序都需要在 app.js 中调用 App 函数 注册小程序示例
https://developers.weixin.qq.com/miniprogram/dev/reference/api/App.html
注册App时,我们一般会做什么呢?
- 判断小程序的进入场景
- 监听生命周期函数,在生命周期中执行对应的业务逻辑,比如在某个生命周期函数中进行登录操作或者请求网络数据
- 因为App()实例只有一个,并且是全局共享的(单例对象),所以我们可以将一些共享数据放在这里;
判断打开场景
常见的打开场景:群聊会话中打开、小程序列表中打开、微信扫一扫打开、另一个小程序打开
https://developers.weixin.qq.com/miniprogram/dev/reference/scene-list.html
在onLaunch和onShow生命周期回调函数中,会有options参数,其中有scene值;
定义全局App的数据
一般存放的是常量数据,或者基本上不会改变的数据
1 | // app.js |
一般在app.js中的onLaunch中写登录逻辑
注册页面
小程序中的每个页面, 都有一个对应的js文件, 其中调用Page函数注册页面示例
- 在注册时, 可以绑定初始化数据、生命周期回调、事件处理函数等。
- https://developers.weixin.qq.com/miniprogram/dev/reference/api/Page.html
我们来思考:注册一个Page页面时,我们一般需要做什么呢?
1.在生命周期函数中发送网络请求,从服务器获取数据;
2.初始化一些数据,以方便被wxml引用展示;
3.监听wxml中的事件,绑定对应的事件函数;
4.其他一些监听(比如页面滚动、上拉刷新、下拉加载更多等);
列表渲染
在组件中,我们可以使用wx:for来遍历一个数组 (字符串 - 数字)
默认情况下,遍历后在wxml中可以使用一个变量index,保存的是当前遍历数据的下标值。
数组中对应某项的数据,使用变量名item获取。
1 | <view class="book"> |
<block />并不是一个组件,它仅仅是一个包装元素,不会在页面中做任何渲染,只接受控制属性。
默认情况下,item – index的名字是固定的
但是某些情况下,我们可能想使用其他名称,或者当出现多层遍历时,名字会重复
这个时候,我们可以指定item和index的名称:
1 | <view class="book"> |
wx:key的使用
我们看到,使用wx:for时,会报一个警告:
这个提示告诉我们,可以添加一个key来提供性能。
为什么需要这个key属性呢?
- 这个其实和小程序内部也使用了虚拟DOM有关系(和Vue、React很相似)。
- 当某一层有很多相同的节点时,也就是列表节点时,我们希望插入、删除一个新的节点,可以更好的复用节点;
wx:key 的值以两种形式提供
- 字符串,代表在 for 循环的 array 中 item 的某个 property,该 property 的值需要是列表中唯一的字符串或数字,且不能 动态改变。
- 保留关键字
*this代表在 for 循环中的 item 本身,这种表示需要 item 本身是一个唯一的字符串或者数字。
WXS
WXS(WeiXin Script)是小程序的一套脚本语言,结合 WXML,可以构建出页面的结构。
- 官方:WXS 与 JavaScript 是不同的语言,有自己的语法,并不和 JavaScript 一致。(不过基本一致)
为什么要设计WXS语言呢?
在WXML中是不能直接调用Page/Component中定义的函数的.
但是某些情况, 我们可以希望使用函数来处理WXML中的数据(类似于Vue中的过滤器),这个时候就使用WXS了
WXS使用的限制和特点:
WXS 不依赖于运行时的基础库版本,可以在所有版本的小程序中运行;
WXS 的运行环境和其他 JavaScript 代码是隔离的,WXS 中不能调用其他 JavaScript 文件中定义的函数,也不能调用小程序 提供的API;
由于运行环境的差异,在 iOS 设备上小程序内的 WXS 会比 JavaScript 代码快 2 ~ 20 倍。在 android 设备 上二者运行效率 无差异;
WXS有两种写法:
- 写在<wxs>标签中 , module:模块名必填字段
- 写在以
.wxs结尾的文件中
1 | <!-- 方法一:使用wxs标签 --> |
1 | // utils/formatPrice.wxs |
注:
- 不支持ES6写法,不可以使用const 、箭头函数等
- 必须要导出,且不可以使用解构写法
- 必须添加模块名
1 | // 时间格式化 |
currentTarget和target的区别
currentTarget:处理事件的组件(不一定是被点的那个,可能是冒泡时触发的)
target:触发事件的组件(点了谁,谁就是)
事件参数的传递
当视图层发生事件时,某些情况需要事件携带一些参数到执行的函数中, 这个时候就可以通过data-属性来完成:
- 格式:data-属性的名称=‘值’
获取:e.currentTarget.dataset.属性的名称
1 | <button bind:tap="onClick" data-name="churui" data-age="25"> 点我 </button> |
1 | onClick(event) { |
案例:
1 | <view class="tab-control"> |
1 | onTap(e) { |
- 格式:
mark:属性的名称=‘值’
获取:e.mark.属性的名称
1 | <view class="mark" bind:tap="onMarkTap" mark:name="churui"> |
1 | onMarkTap(event) { |
组件化开发
类似于页面,自定义组件由 json wxml wxss js 4个文件组成。
按照我的个人习惯, 我们会先在根目录下创建一个components文件夹;
components, 里面存放我们之后自定义的公共组件;
常见一个自定义组件 my-cpn: 包含对应的四个文件;
自定义组件的步骤
首先需要在 json 文件中进行自定义组件声明(将component 字段设 为 true 可这一组文件设为自定义组件)
在wxml中编写属于我们组件自己的模板
在wxss中编写属于我们组件自己的相关样式
在js文件中, 可以定义数据或组件内部的相关逻辑
注:
自定义组件也是可以引用自定义组件的,引用方法类似于页面引用自定义组件的方式(使用usingComponents 字段)。
自定义组件和页面所在项目根目录名 不能以“wx-”为前缀,否则会报错。
如果在app.json的usingComponents声明某个组件,那么所有页面和组件可以直接使用该组件。
组件样式的细节
课题一:组件内的样式 对 外部样式 的影响
结论一:组件内的class样式,只对组件wxml内的节点生效, 对于引用组件的Page页面不生效。
结论二:组件内不能使用id选择器、属性选择器、标签选择器
课题二:外部样式 对 组件内样式 的影响
结论一:外部使用class的样式,只对外部wxml的class生效,对组件内是不生效的
结论二:外部使用了id选择器、属性选择器不会对组件内产生影响
结论三:外部使用了标签选择器,会对组件内产生影响
课题三:如何让class可以相互影响
在自定义组件的Component对象中,可以传入一个options属性,其中options属性中有一个styleIsolation(隔离)属性。(一般取值都是默认的隔离,很少会让其相互影响)
styleIsolation有三个取值:
isolated 表示启用样式隔离,在自定义组件内外,使用 class 指定的样式将不会相互影响(默认取值);
apply-shared 表示页面 wxss 样式将影响到自定义组件,但自定义组件 wxss 中指定的样式不会影响页面;
shared 表示页面 wxss 样式将影响到自定义组件,自定义组件 wxss 中指定的样式也会影响页面和其他设置 了
组件通信
向组件传递数据
使用properties属性;
支持的类型:
String、Number、Boolean
Object、Array、null(不限制类型)
默认值:
可以通过value设置默认值;
想组建传递样式
以使用externalClasses属性
在Component对象中,定义externalClasses属性
在组件内的wxml中使用externalClasses属性中的class
在页面中传入对应的class,并且给这个class设置样式
1 | <!-- 页面 --> |
1 | /* 定义在页面的样式 */ |
在组件中使用
1 | <!-- 组件 --> |
1 | Component({ |
组件向外传递事件
使用自定义事件triggerEvent
1 | // 组件中 |
1 | <!-- 页面中 --> |
1 | onHelloClick(e) { |
注:事件名大小写敏感
页面直接调用组件
可在父组件里调用 this.selectComponent ,获取子组件的实例对象。
1 | <!-- 子组件 --> |
1 | handleTap() { |
调用时需要传入一个匹配选择器
插槽的使用
注:小程序中的插槽不支持默认值
单个插槽
实现插槽的默认值功能
1 | <!-- 插槽 --> |
样式
1 | .default { |
多个插槽
1 | <!--components/mul-slot/mul-slot.wxml--> |
1 | // components/mul-slot/mul-slot.js |
1 | <!-- 页面中使用 --> |
注意:需要在定义多插槽的组件中使用
multipleSlots: true
小程序的混入
behaviors 是用于组件间代码共享的特性,类似于一些编程语言中的 “mixins”。
我的理解:将公用方法抽离出来,其他地方可以直接使用
每个 behavior 可以包含一组属性、数据、生命周期函数和方法;
组件引用它时,它的属性、数据和方法会被合并到组件中,生命周期函数也会在对应时机被调用;
每个组件可以引用多个 behavior ,behavior 也可以引用其它 behavior ;
案例:
behavior文件
1 | // behaviors/counter.js |
自定义组件:
1 | <!--components/c-behavior/c-behavior.wxml--> |
1 | // components/c-behavior/c-behavior.js |
页面:
1 | <!-- 应用--> |
测试图片
![]()
