1、小程序核心技术
- 页面布局: WXML (类似html);
- 页面样式: WXSS(就是css, 对原生css进行了一些增强,有些则不支持);
- 页面脚本: javascript + WXS(weixinScript)
2、小程序注册账号
入口: 微信公众平台
appid: 开发管理 / 开发设置
小程序开发工具: 文档 / 工具
3、vscode开发小程序的插件
4、小程序目录结构介绍
pages // 存放页面 utils // 基础方法存放 app.json // 小程序的关键文件,配置页面信息,以及窗口的基础配置
5、小程序的入门体验
Page({
data: {
content: 'this is test',
list: ['first', 'second', 'third']
},
// 页面的方法可以直接写在page的json中,如果是组件,那么需要写在methods中
testEvent() {
console.log('test')
},
checkEvent(event: any) {
console.log('ok', event)
}
})
相对应的wxml
<!--index.wxml-->
<text>{{content}}</text>
<view>
<text>abc</text>
<button size="mini" type='primary' bind:tap="testEvent">button</button>
</view>
<view>
<!-- block相当于vue中的template的功能 -->
<block wx:for="{{list}}" wx:for-item="per" wx:key="*this">
<!-- 阻止冒泡事件 -->
<text catch:tap="checkEvent">{{per}}{{index}}</text>
</block>
</view>
<view hidden>this is view</view>
微信小程序数据改变导致的界面刷新需要依赖setData文件进行视图层的刷新 , 这个特性需要与vue的数据截持区分开
Page({
data: {
content: 'this is test',
},
changeEvent() {
// 这个方法不仅可以改变data中的数据,也可以实现视图层的刷新
this.setData({
content: "what are you doing???"
})
console.log(this.data.content)
}
})
6、小程序配置文件
- project.config.json: 项目配置文件,比如项目名称, appid等; 文档地址:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html
- project.private.config.json 这个表示私人配置,方便进行项目配置,可以在gitignore中配置不提交git仓库
- 通常更改project.private.config.json是通过界面的配置来更改里的本地配置进行更改
- sitemap.json: 小程序搜索相关的;文档地址:https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html
- app.json: 全局配置; 文档地址:https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html
- pages: string[] => 页面路径列表
- window: object => 全局默认窗口表现
- tabBar: object => 底部 tab 栏的表现
- page.json: 页面配置; 文档地址: https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/page.html
- 页面中配置在当前页面会覆盖 app.json 的 window 中相同的配置
注意:通常来讲,小程序的展示顺序是按照app.json中的page中配置页的页面进行编译,但是为了方便开发,也可以在顶部栏的添加编译模式
7、小程序下拉刷新配置
在app.json中有一个配置
// app.json中的配置
"window": {
"backgroundTextStyle": "dark", // 定义下拉刷新的样式, 目前有light, dark两种
"enablePullDownRefresh": false // 是否开启全局下拉刷新,通常不开启全局的下拉刷新,只在页面中开启,即在page.json中配置
},
// page页面中的配置
{
"usingComponents": {},
"enablePullDownRefresh": true
}
Page({
onPullDownRefresh() {
console.log('用户下拉刷新')
// 停止刷新,默认是刷新3秒
wx.stopPullDownRefresh({
success(res) {
console.log('success', res)
},
fail() {
console.log('fail')
},
complete(res) {
console.log('complete', res)
}
})
}
})
8、小程序触底刷新配置
{
"usingComponents": {},
"onReachBottomDistance": 100 // 表示在距离底部100时触发触底事件
}
// 上面配置配合page内部的函数进行使用
Page({
// 触底时会触发这个事件
onReachBottom() {
console.log('reach Bottom')
}
})
9、App的生命周期函数
每个小程序只能实例化一个App实例,并且是全局共享(可以利用这个特性做一些简单的全局数据共享),这个App实例也是指定的生命周期函数 具体参看文档地址: https://developers.weixin.qq.com/miniprogram/dev/reference/api/App.html
在Page中如果需要获取App实例,可以使用 getApp()这个方法进行获取
获取小程序的进入场景(获取相应的场景值)
// app.ts
App<IAppOption>({
globalData: {},
onLaunch(options: any) {
console.log(options.scene) // 获取小程序是通过何种方式进入的, 也可以使用 wx.getLaunchOptionSync()进行获取
},
onShow(options: any) {
console.log(options.scene) // onShow时也会获取相当的参数
}
})
注意:相关的code可以对照 https://developers.weixin.qq.com/miniprogram/dev/reference/scene-list.html
为了方便模拟进入的code可以点击下图按钮进行模拟,不过onLaunch只会触发一次
10、微信小程序头像获取方法
微信小程序的头像获取方式已做调整,新的获取方法见链接 https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/userProfile.html
11、微信小程序从应用中获取图片和视频的方法如下
wxml
<view>
<button bind:tap="tapEvent">选择图片</button>
<image src="{{imageSrc}}" mode="aspectFit" />
</view>
js
Page({
data: {
imageSrc: 'https://up.deskcity.org/pic_source/2f/f4/42/2ff442798331f6cc6005098766304e39.jpg',
},
tapEvent() {
wx.chooseMedia({
mediaType: ['image']
}).then(res => {
console.log(res)
})
}
})
12、微信小程序的双向绑定
wxml
<!-- <input type="text" value="{{message}}" bind:input="inputEvent"/> --> // 方法一
<input type="text" model:value="{{message}}" bind:input="emptyEvent" /> // 方法二
<button bind:tap="tapEvent">点击</button>
js
{
...
tapEvent() {
console.log(this.data.message)
},
inputEvent(e: any) {
console.log(e)
},
emptyEvent(e: any) {
}
}
注意:以前常用的双向绑定是以第一种方式进行,但是第二种方式实现会报警告,为了解决不必要的警告,可以绑定一个空的input事件
13、 wxss样式编写
小程序的样式分成三种,行内样式,页面样式(页面的wxss),全局样式(app.wxss)
wxss的扩展:尺寸单位(rpx: 可以根据屏幕宽度进行自适应, 规定屏幕宽为750rpx)
如在 iphone6上,屏幕宽度为 375px, 共有750个物理像素, 则 750rpx = 375px = 750物理像素, 1rpx = 0.5px = 1物理像素
建议:开发微信小程序时设计师可以用 iPhone6 作为视觉稿的标准
14、wxml的相关语法
判断语法: wx:if ...wx:elif...wx:else
实现元素的显示隐藏可以使用 hidden 属性
<text hidden="{{sign % 3 == 0}}">haha{{sign}}</text>
列表循环
<view>
<block wx:for="{{list}}" wx:key="id" wx:for-item="abc" wx:for-index="kk">
<view>{{abc.name}}=={{abc.id}}--{{kk}}</view>
</block>
<!-- 这里也可以遍历数字或字符串 -->
<block wx:for="{{10}}" wx:key="*this">
<text>{{item}}</text>
</block>
</view>
注意:默认访问每一项用item, 访问索引用 index,如果需要改需要用到 wx:for-item, wx:for-index来改变
15、wxs的使用
WXS与javascript 是小程序的一套脚本语言, 结合WXML, 可以构建出页面的结构
限制和特点:
- wxs不依赖于运行时的基础库版本,可以在所有的版本的小程序中运行
- wxs的运行环境和其他 javascript 代码是隔离的,wxs中不能调用其他的 javascript 文件中定义的函数, 也不能调用小程序提供的 api
- 由于运行环境的差异, 在 IOS 设备上 wxs 会比 javascript 代码快 2-20倍,在 android 设置上二者运行效率差不多
wxs的写法一:
<!-- 注意:这里只能用es5的语法,不能用es6以上的语法 -->
<wxs module="format">
function parsePrice(price) {
return "¥" + price
}
// 这里必需用common.js的语法进行导出
module.exports = {
parsePrice: parsePrice
}
</wxs>
<view>
<block wx:for="{{list}}" wx:key="id">
<view><text>{{item.id}}</text><text>{{item.name}}</text><text>{{format.parsePrice(item.price)}}</text></view>
</block>
</view>
wxs的写法二:
定义一个wxs文件,内容如下
function parsePrice(price) {
return "¥" + price
}
module.exports = {
parsePrice: parsePrice
}
在wxml文件中内容如下
<!-- 注意:这里只能用es5的语法,不能用es6以上的语法 -->
<wxs module="format" src="/utils/format.wxs"></wxs>
<view>
<block wx:for="{{list}}" wx:key="id">
<view><text>{{item.id}}</text><text>{{item.name}}</text><text>{{format.parsePrice(item.price)}}</text></view>
</block>
</view>
注意:如果需要计算总价格,那么则需要把列表数据传到wxs函数中
16、事件参数的传递
在小程序中,事件参数的传递主要有两种方式:方式一,通过 data- 的方式进行传递,接收使用 event.currentTarget.dataset 进行接收; 方式二, 通过 mark: 的方式进行传递,接收使用 event.mark 进行接收
wxml
<view>
<block wx:for="{{list}}" wx:key="id">
<view>
<text>{{item.id}}</text>
<text>{{item.name}}</text>
<text>{{item.price}}</text>
<!-- 这里分别使用了两种方式进行传递 -->
<button data-info="{{item}}" mark:it="{{item}}" size="mini" type="primary" plain bind:tap="tapEvent">购买</button>
</view>
</block>
</view>
js
Page({
data: {
list: [
{ id: 1, name: 'javacript', price: 20 },
{ id: 2, name: 'php', price: 25 },
{ id: 3, name: 'java', price: 30 },
{ id: 4, name: 'python', price: 18 }
]
},
tapEvent(event: any) {
// 接收方式一
const info = event.currentTarget.dataset
// 接收方式二
const info2 = event.mark
console.log(info, info2)
}
})
注意:使用currentTarget, 尽量不要使用target
17、小程序的组件化
小程序中创建组件,这个时候在组件对应的json文件中 component属性为 true, 表示这是一个组件,并且对应的js或ts文件是component对象如下示例
// components/book-item.ts Component({ /** * 组件的属性列表 */ properties: { }, /** * 组件的初始数据 */ data: { }, /** * 组件的方法列表 */ methods: { } })
需要注意一些细节:
- 自定义组件也可以引用自定义组件,引用方法类似页面引用自定义组件的方式,(使用 usingComponents字段)
- 自定义组件和页面所在项目根目录名 不能以 "WX-" 为前缀, 否则会报错
- 如果 在app.json的usingComponents 声明某个组件,那么所有的页面和组件可以直接使用该组件
组件的样式细节
- 组件内的class样式, 只对组件wxml内的节点生效,对于引用组件的Page页面不生效
- 组件内不能使用id选择器,属性选择器,标签选择器(因为这个会导致该样式作用于组件范围以外的元素)
通常来讲,组件与页面的样式是有隔离的,但是可以配置选项让其互相影响: 在 Components对象中,可以传入一个 options属性,其中 options 属性有一个 styleIsolation (隔离) 属性
styleIsolation有三个取值
- isolated 表示启动样式隔离,在自定义组件内外,使用class 指定的样式将不会相互影响(默认取值)
- apply-shared 表示页面的 wxss 样式将影响到自定义组件,但自定义组件 wxss 中指定的样式不会影响页面
- shared 表示页面 wxss 样式将影响到自定义组件,自定义组件 wxss 中指定的样式也会影响页面和其他设置
组件的通信
页面传递数据, 样式,标签给组件分别通过 properties, externalClasses, slot进行传递
组件传递数据页面 是通过自定义事件进行传递
page wxml
<view>
<block wx:for="{{list}}" wx:key="id">
<Book-Item info="{{item}}" bind:buyEvent="itemBuyEvent"></Book-Item>
</block>
</view>
page ts
Page({
data: {
list: [
{ id: 1, name: 'javacript', price: 20 },
{ id: 2, name: 'php', price: 25 },
{ id: 3, name: 'java', price: 30 },
{ id: 4, name: 'python', price: 18 }
]
},
itemBuyEvent(item: any) {
console.log(item.detail)
}
})
组件 wxml
<view>
<text>{{info.id}}</text>
<text>{{info.name}}</text>
<text>{{info.price}}</text>
<button type="primary" size="mini" bind:tap="buyEvent">购买</button>
</view>
组件 ts
Component({
properties: {
info: {
type: Object,
value: { id: 0, name: '', price: 0 }
}
},
/**
* 组件的初始数据
*/
data: {
},
/**
* 组件的方法列表
*/
methods: {
buyEvent() {
this.triggerEvent('buyEvent', this.data.info)
}
}
})
注意: triggerEvent相当于vue中的emit, 小程序中组件向父组件传递数据的方式是一样的, properties支持的类型有: String, Number, Boolean, Object, Array,null(不限制类型) 默认值通过value进行配置
externalClasses的用法
组件ts
// components/book-item.ts
Component({
...
// 需要在组件中配置这个字段, item表示外部传入的类名
externalClasses: ['item'],
...
})
组件的 wxml
<!-- 这里可以直接使用item这个类名 -->
<view class="item">
<text>{{info.id}}</text>
<text>{{info.name}}</text>
<text>{{info.price}}</text>
<button type="primary" size="mini" bind:tap="buyEvent">购买</button>
</view>
页面调用组件
<view>
<block wx:for="{{list}}" wx:key="id">
<Book-Item info="{{item}}" item="{{index<2? 'bookItem': ''}}" bind:buyEvent="itemBuyEvent"></Book-Item>
</block>
</view>
页面scss文件
.bookItem {
color: red;
background: yellow;
font-size: 36rpx;
}
小程序中获取元素以及组件的做法
<view>
<view class="title">这个是一个标题</view>
<block wx:for="{{list}}" wx:key="id">
<Book-Item class="book" info="{{item}}" item="{{index<2? 'bookItem': ''}}" bind:buyEvent="itemBuyEvent"></Book-Item>
</block>
<button type="primary" bind:tap="getElementEvent">获取元素</button>
</view>
js文件
getElementEvent() {
// 获取元素的方法
const handle = wx.createSelectorQuery();
const title = handle.select('.title')
// 获取单个组件的方法
const book = this.selectComponent('.book')
// 获取多个组件的做法
const bookss = this.selectAllComponents('.book')
// 也可以通过事件的currentTarget或者target来获取
}
小程序中slot的使用
页面wxml(多插槽的情况)
<view>
<block wx:for="{{list}}" wx:key="id">
<Book-Item info="{{item}}" bind:buyEvent="itemBuyEvent">
<view slot="content"><text>{{item.id}}--{{item.name}}--{{item.price}}</text></view>
<button slot="action" type="warn">点击</button>
</Book-Item>
</block>
</view>
页面ts
Page({
data: {
list: [
{ id: 1, name: 'javacript', price: 20 },
{ id: 2, name: 'php', price: 25 },
{ id: 3, name: 'java', price: 30 },
{ id: 4, name: 'python', price: 18 }
]
}
})
组件wxml
<view>
<view class="content">
<slot name="content"></slot>
</view>
<view class="content-default">
<text>{{info.id}}</text>
<text>{{info.name}}</text>
<text>{{info.price}}</text>
</view>
<view class="action">
<slot name='action'>
</slot>
</view>
<view class="action-default">
<button type="primary" size="mini" bind:tap="buyEvent">购买</button>
</view>
</view>
注意:content-default与action-default是默认内容,如果没有内容的情况下,展示default中的内容,这个需要配合css的样式实现,如下代码
组件scss
.content-default,
.action-default {
display: none;
}
.content:empty+.content-default,
.action:empty+.action-default {
display: block
}
组件ts
// components/book-item.ts
Component({
properties: {
info: {
type: Object,
value: { id: 0, name: '', price: 0 }
}
},
options: {
styleIsolation: "isolated", // 样式隔离
multipleSlots: true // 实现多插槽
},
externalClasses: ['item'],
/**
* 组件的初始数据
*/
data: {
},
/**
* 组件的方法列表
*/
methods: {
buyEvent() {
this.triggerEvent('buyEvent', this.data.info)
}
}
})
小程序中的mixins --- behaviors
-
每个behaviors可以包含一组属性,数据,生命周期函数和方法
-
组件引用它时,它的属性,数据和方法会被合并到组件中; 生命周期函数也会在对应的时机被调用
-
每个组件可以引用多个behavior, behavior可以引用其他的behavior
定义count-behavios.ts文件
export const CountBehavior = Behavior({
data: {
count: 0
},
methods: {
increment() {
this.setData({
count: this.data.count + 1
})
},
decrement() {
this.setData({
count: this.data.count - 1
})
}
}
})
组件wxml
<view class="item">
<text>{{count}}</text>
<view>
<button type="primary" bind:tap="increment">+ 1</button>
<button type="warn" bind:tap="decrement">- 1</button>
</view>
</view>
组件ts
import { CountBehavior } from "../behaviors/count-behavior";
// components/book-item.ts
Component({
behaviors: [CountBehavior]
})
小程序组件中的生命周期函数
具体可见文档 组件生命周期函数
小程序的数据监听器
具体可见文档 数据监听器
18、小程序常见API
a、网络请求
注意:每个微信小程序需要事先设置通讯域名,小程序只可以跟指定的域名进行网络通信,设置地址:小程序登录后台 -- 开发管理 -- 开发设置 -- 服务器域名(域名需要进行备案)
开发阶段,为了防止不能请求,可以配置 详情 -- 本地设置 -- 不较验合法域名
对wx.request进行简单的封装
// request.ts
type RequestOptions<T> = {
url: string,
method?: 'GET' | 'POST', // 可以根据需要添加更多的HTTP方法
data?: T,
header?: Record<string, string>
}
export class Request {
baseUrl: string;
constructor(baseUrl: string) {
this.baseUrl = baseUrl;
}
private request<T, U = any>(options: RequestOptions<T>): Promise<U> {
return new Promise((resolve, reject) => {
wx.request({
url: this.baseUrl + options.url,
method: options.method || 'GET',
data: options.data,
header: options.header || {
'content-type': 'application/json'
},
success: (res) => {
if (res.statusCode === 200) {
resolve(res.data as U);
} else {
reject(res.errMsg);
}
},
fail: (err) => {
reject(err);
}
});
});
}
get<U>(url: string, params?: Record<string, any>): Promise<U> {
return this.request<undefined, U>({ url, method: 'GET', data: params });
}
post<T, U>(url: string, data: T): Promise<U> {
return this.request<T, U>({ url, method: 'POST', data });
}
// 可以根据需要添加其他方法
}
b、微信弹框(具体见链接)
c、微信分享
在进行微信分享的时候,会默认调用Page中的一个方法,如下
Page {
// 分享功能
onShareAppMessage () {
return {
title: '分享的标题',
path: 'pages/test/share' // 分享后点击进入的路由
imageUrl: '分享封面的图片,如果没有配置,则为缩略图'
}
}
}
d、获取设备信息以及地理位置
在开发过程中,我们需要获取当前设备的信息,用于收集信息或者进行一些适配工作
小程序提供了相关的api: wx.getSystemInfo(object object)
wx.getSystemInfo({
success(result) {
console.log(result)
}
})
如果需要获取地理位置的信息,那么就需要进行权限申请以及配置,具体的文档: 地理位置
在获取上以的地理位置权限后,需要添加以下的权限:权限
{
"pages": ["pages/index/index"],
"permission": {
"scope.userLocation": {
"desc": "你的位置信息将用于小程序位置接口的效果展示" // 高速公路行驶持续后台定位
}
}
}
e、小程序页面跳转的方式
界面的跳转有两种方式:通过navigator组件 和 通过 wx 的api跳转
这里先以 wx 的 api 作为讲解