小程序这个也经常开发要掌握(一)
基础部分
微信公众平台
注册时可选择类型:订阅号、服务号、小程序、企业微信
每个邮箱仅能注册一个小程序。
个人类型小程序:无法使用微信支付、无法使用卡包功能
小程序文档API
微信开放社区
目录说明
默认目录
pages-----------------------页面相关
index ----------------- 首页文件夹
index.js ------------首页js
index.json---------首页配置
index.wxml-------首页html
index.wxss--------首页css
utils------------------------工具相关
app.js ----------------------项目总js
app.json-------------------全局配置( 页面路由以及头部、底部导航的配置等)
app.wxss -----------------项目总样式css
project.config.json ----项目配置
代码构成
.json :配置文件,以json格式存储配置
项目中有三种配置:项目配置(project.config.json)、全局配置(app.json)、页面配置(index.json)
.wxml: 相当于html文件
.wxss: 相当于css
.js : 就是js
文件说明
project.config.json项目配置 部分代码说明
setting:{
urlCheck 是否检测安全的域名
es6 是否把es6转es5
postcss 是否把css样式自动补全
minified 是否压缩
}
app.json 全局配置
wxml 相关介绍
<view>{{motto}}</view>
循环渲染
<view wx:for="{{list}}" wx:key="{{index}}">
{{index}} {{item}}
</view>
改变for循环item和index的名称
<block wx:for="{{list}}" wx:for-item="data" wx:for-index="inx">
{{inx}} {{data}}
</block>
条件渲染 (类似vue的v-if、v-else)
<view wx:if="{{isLogin}}">已登录</view>
<view wx:else>请登录</view>
条件显示(类似vue的v-show)
<view hidden="{{isLogin}}">显示内容</view>
绑定点击事件
<button bindtap=“tapName”>按钮</button>
Page({
tapName: function(event) {
console.log(event)
}
})
...
wxss 相关介绍
尺寸单位:rpx,根据屏幕宽度自适应。
引入外部wxss:@import ’...‘
js相关介绍
WXS(WeiXin Script)是小程序的一套脚本语言
绑定点击事件
<button bindtap=“onTapHandler”>点我+1</button>
<view>{{count}}</view>
Page({
data: {
count: 0
},
onTapHandler: function() {
this.setData({
count: this.data.count++
})
}
})
阻止事件冒泡
把绑定方式 bindtap 换成 catchtap 即可。
第三方库
WeUI
weUI是一套同微信原生视觉体验一致的基础样式库
iView Weapp
一套高质量的微信小程序UI组件库
Vant Weapp
轻量、可靠的小程序UI组件库
云开发
小程序传统开发模式
客户端 -----> 服务端(后端代码、数据库)------> 运维(DB维护、文件存储、内容加速、网络防护、容器服务、负载均衡、安全加固等...)
小程序云开发模式
客户端 -----> 云开发(云函数、云数据库、云存储)
传统开发 VS 云开发
开发效率低 Serverless(无服务)
运维成本高 开发者更关注业务逻辑
无服务(Serverless)开发是未来的发展趋势
云开发三大基础能力
云函数
(相当于传统开发中的后台接口)
获取appid、获取openid、生成分享图、调用腾讯云SDK ...
云数据库
数据的增、删、改、查 ...
云存储
管理文件、上传文件、下载文件、分享文件 ...
每个小程序账号可免费创建两个环境,建议:开发环境、生成环境
云数据库能力
云开发提供了一个json数据库,提供2GB免费存储空间。
数据类型
String 字符串
Number 数字
Object 对象
Array 数组
Boolean 布尔值
GeoPoint 地理位置点
Date 时间 (精确到毫秒ms,客户端时间)
Null 空
操作云数据库
小程序控制(读写数据库受权限限制)
云函数控制(拥有所有读写数据库的权限)
控制台控制(拥有所有读写数据库的权限)
云数据库权限管理
仅创建者可写,所有人可读 (适合于文章)
仅创建者可读写 (适用于私密内容)
仅管理端可写,所有人可读(适用于商品信息)
仅管理端可读写(适用于后台敏感数据)
操作云数据库
//初始化数据库
const db = wx.cloud.database() // 小程序端初始化数据库,如果在云函数端不需要加wx
//切换环境(开发环境/生产环境)
const testDB = wx.cloud.database({
env: 'test'
})
实战课程部分
serverless(无服务)
概念:函数即服务,当需要后端服务的时候,不需要关心后端的IP地址、域名,只需要像调用普通函数一样既可以实现调用。
云开发优势
快速上线、专注核心业务、独立开发一个完整的微信小程序、不需要学习新的语言,只需要会javascript、无需运维, 节约成本、数据安全、
云开发提供能力
云函数:在云端运行的代码,微信私有协议天然鉴权 (理解:相当于后端部分)
云数据库:一个既可以在小程序端操作又可以在云函数中操作的JSON数据库
云存储:在云端存储文件,可以在云端控制台可视化管理
云调用:基于云函数免鉴权使用小程序开放接口的能力(比如说给用户推送消息等)
HTTP API:使用HTTP API开发者可在已有服务器上访问云资源,实现与云开发的互通(作用:对原有传统模式下开发的小程序,可以与云开发进行互通)
appID
每个小程序唯一的id
云开发项目默认目录结构
cloudfunctions ----------------------------云函数
callback ---------------------------------- 回调函数
config.json ---------------------------
index.js --------------------------------
package.json ------------------------
echo ----------------------------------------
login ----------------------------------------
openapi -----------------------------------
miniprogram ------------------------------- 小程序
images ------------------------------------- 图片
pages --------------------------------------- 页面
style ----------------------------------------- 样式
app.js --------------------------------------- 项目js
app.json ----------------------------------- 全局配置
app.wxss ---------------------------------- 项目样式
sitemap.json ----------------------------- (小程序SEO相关)
project.config.json ----------------------- 项目配置
云开发环境
云开发可创建两个环境,建议一个为开发环境,一个为生产环境
开发前的准备
开发工具 > 右上角详情 > 本地设置 > 调试基础库 设置为最新版本
app.js > wx.cloud.init > env 设置环境ID
project.config.json 文件说明
miniprogramRoot 小程序前端代码目录
cloudfunctionRoot 云函数代码目录
app.json
pages 设置页面 ,设置后会自动在pages目录下生成相应的目录和文件
设置底部导航按钮:
"tabBar": {
"color": "#474747", // 文字颜色
"selectedColor": "#d43c43", // 文字选中颜色
"list": [{ // 按钮列表,2-5项
"pagePath": "pages/playlist/playlist", // 按钮对应页面
"text": "音乐", // 文字
"iconPath": "images/music.png", // 图标路径
"selectedIconPath": "images/music-actived.png" // 选中图标的路径
},
{
"pagePath": "pages/blog/blog",
"text": "发现",
"iconPath": "images/blog.png",
"selectedIconPath": "images/blog-actived.png"
},
{
"pagePath": "pages/profile/profile",
"text": "我的",
"iconPath": "images/profile.png",
"selectedIconPath": "images/profile-actived.png"
}]
}
图标来自于 https://www.iconfont.cn
阿里巴巴图标库,包含矢量图标、字体图标、字体等
代码规范
很多公司借鉴的代码规范:https://github.com/airbnb/javascript
《音乐》页面开发
<!-- 轮播图组件 参数:indicator-dots 小圆点,autoplay 自动播放, interval 间隔时间,duration 动画时长 -->
<swiper indicator-dots="true" circular="true" interval="3000" duration="500">
<block wx:for="{{swiperImgUrls}}" wx:key="{{index}}"> <!-- 空节点 -->
<swiper-item>
<image src="{{item.url}}" mode="widthFix" class="img"></image>
</swiper-item>
</block>
</swiper>
自定义组件
创建组件
创建目录 components > 组件目录名称 > 右键 新建Component
引入组件
在page的json文件中:
{
"usingComponents": {
"x-playlist":"/components/playlist/playlist"
}
}
在page的wxml中:
<x-playlist> </x-playlist>
页面引入组件以及组件内部在引用子组件的方法是一样的,同样需要设置json文件。
组件传值
父组件中:在引入组件的时候自定义属性名称,并把数据传入子组件
<!-- 参数:playlist 自定义名称,传入组件的数据 -->
<x-playlist playlist="{{传入的数据}}"></x-playlist>
子组件中: 子组件的js文件:
/**
* 组件的属性列表
*/
properties: {
playlist:{ // 接收父组件传输的数据
type: Object // 数据类型
}
},
//子组件的wxml文件可直接引入数据{{playlist}}
wx:key 的使用
key的值不建议使用index,因为当数据发生变化会dom结构产生变化时,使用index的地方不会随之变化。
可以使用数据内部每项不一样的一个数值,如id
<block wx:for="{{swiperImgUrls}}" wx:key="url"> 这里url不需要双大括号,如使用index则需要{{}}
<view>
<image src="{{item.url}}" mode="widthFix" class="img"></image>
</view>
</block>
<view class="playlist-container">
<block wx:for="{{playlist}}" wx:key="_id">
<!-- 参数:playlist 自定义名称,传入组件的数据 -->
<x-playlist playlist="{{item}}"></x-playlist>
</block>
</view>
async/await 语法
目前,在云函数里,由于 Node 版本最低是 8.9,因此是天然支持 async/await 语法的。而在小程序端则不然。在微信开发者工具里,以及 Android 端手机(浏览器内核是 QQ浏览器的 X5),async/await是天然支持的,但 iOS 端手机在较低版本则不支持,因此需要引入额外的 文件。
可把这个 runtime.js 文件引用到有使用 async/await 的文件当中。
// 注意,必须命名为 regeneratorRuntime
import regeneratorRuntime from '../../utils/runtime.js'
云函数的使用
cloudfunctions目录 右键 新建 Node.js 云函数
> 输入目录名 getPlaylist
在云函数中向第三方服务器发送请求要依赖第三方库
安装依赖包
云函数目录 getPlaylist
右键 在终端打开
打开命令行 输入命令:
npm install --save request
npm install --save request-promise
github request-promise:https://github.com/request/request-promise
然后写相应代码
// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init()
const rp = require('request-promise') // 需安装依赖包
const URL = 'http://musicapi.xiecheng.live/personalized'
// 云函数入口函数
exports.main = async (event, context) => {
const playlist = await rp(URL).then((res) => {
return JSON.parse(res).result
})
console.log(playlist)
}
写完代码,云函数目录 getPlaylist
右键 上传并部署:云端安装依赖(不上传node_modules)
进行上传部署代码到云端,等待上传成功,打开云开发控制台即可看到已经上传的云函数,并可对云函数进行测试。
数据库操作
数据库> 创建集合 > playlist
// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init()
const db = cloud.database() // 初始化数据库,如果在小程序端初始化数据库需要在前面加wx.
const rp = require('request-promise') // 需安装依赖包
const URL = 'http://musicapi.xiecheng.live/personalized' // 第三方服务器地址(老师从网易云获取的数据部署在其服务器,每天的数据会更新)
const playlistCollection = db.collection('playlist') // 获取到数据库playlist集合
const MAX_LIMIT = 10 // 定义常量,获取数据库条数最大的限制
// 云函数入口函数
exports.main = async (event, context) => {
/**
* 注:
* - 关于数据库的操作都是异步操作,都需添加await关键字
* - console.log 打印在云开发控制台 云函数测试内查看
* - 单次获取数据库数据有条数限制,云函数端最多获取100条,小程序端最多获取20条
*/
// const list = await playlistCollection.get() // 获取数据库集合的数据 (因为有条数限制,不直接用此方法)
// 突破条数限制 (为了读取到全部数据然后与第三方服务器获取的数据进行对比去重)
const countResult = await playlistCollection.count() // 获取数据总条数 返回为对象
const total = countResult.total // 取得总条数
const batchTimes = Math.ceil(total / MAX_LIMIT)
const tasks = []
for(let i = 0; i < batchTimes; i++) {
let promise = playlistCollection.skip(i * MAX_LIMIT).limit(MAX_LIMIT).get() // 从第 skip 条开始取,最多取 limit 条数据
tasks.push(promise)
}
let list = {
data: []
}
if (tasks.length > 0) {
list = (await Promise.all(tasks)).reduce((acc, cur) => { // reduce数组方法 累积拼接
return {
data: acc.data.concat(cur.data)
}
})
}
// 获取第三方服务器端数据
const playlist = await rp(URL).then((res) => {
return JSON.parse(res).result
})
// 数据库与服务器数据对比去重(数据已存在数据库的无需再重复添加)
const newData = []
for(let i = 0, len1 = playlist.length; i < len1; i++) {
let flag = true
for(let j = 0, len2 = list.data.length; j < len2; j++) {
if(playlist[i].id === list.data[j].id){
flag = false
break
}
}
if(flag){
newData.push(playlist[i])
}
}
// 把数据插入数据库,需要单条插入
for (let i = 0, len = newData.length; i < len; i++) {
await playlistCollection.add({ // 给数据库集合添加数据
data: {
...newData[i],
createTime: db.serverDate(), // db.serverDate() 获取服务器时间
}
}).then((res) => { // 数据添加成功
console.log('数据添加成功')
}).catch((err) => { // 失败
console.error(err)
})
}
return newData.length // 插入多少条数据
}
查询数据库
//云函数中查询数据库的例子:
// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init()
const TcbRouter = require('tcb-router')
const db = cloud.database() // 初始化数据库
const blogCollection = db.collection('blog') // 博客的数据库集合
// 云函数入口函数
exports.main = async (event, context) => {
const app = new TcbRouter({ event }) // 初始化TcbRouter
app.router('list', async (ctx, next) => {
// skip 从第几条开始查,limit 查几条数据,orderBy(排序字段,排序方式) 排序,排序方式desc降序/asc升序
ctx.body = await blogCollection.skip(event.start).limit(event.count)
.orderBy('createTime', 'desc').get().then((res) => {
return res.data
})
})
return app.serve() // 必需返回
}
云函数调试
云控制台中可会云函数进行云端测试
在小程序调用云函数后,可查看云函数日志
定时触发云函数
如果云函数需要定时 / 定期执行,也就是定时触发,我们可以使用云函数定时触发器。配置了定时触发器的云函数,会在相应时间点被自动触发,函数的返回结果不会返回给调用方
云函数目录下新建 config.json
{
"triggers": [
{
"name": "myTriggers",
"type": "timer",
"config":"0 0 10,14,16,20 * * * *" //表示每天的10点、14点、16点、20点触发一次
}
]
}
编辑好触发器之后,要在云函数目录 > 右键 > 上传触发器
配置云函数超时时间
当云函数比较复杂的时候,默认的超时时间3秒可能不能够满足需求,可以适当的设置更为合理的时间
云开发控制台 > 云函数 > 配置 > 超时时间
上拉加载与下拉刷新
page页面json中:
"enablePullDownRefresh": true
page页面js中有这两个函数:
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function() {
this.setData({
playlist: []
})
this._getPlaylist()
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function() {
this._getPlaylist()
},
下拉刷新请求完数据后
wx.stopPullDownRefresh() // 停止下拉刷新动画
云函数路由优化tcb-router
一个用户在一个云环境只能创建50个云函数
假如小程序非常复杂,50个云函数不能够满足业务需求怎么办?
相似的请求归类到同一个云函数处理
tcb-router是一个koa风格的云函数路由库
通俗理解就是可以把很多个接口归类到同一个云函数内。
github-tcb-router: https://github.com/TencentCloudBase/tcb-router
koa洋葱模型...
安装:
在使用到tcb-router的云函数目录下打开命令行,输入命令进行安装
npm install --save tcb-router
// 云函数的 index.js
const TcbRouter = require('router'); // 必需
exports.main = (event, context) => {
const app = new TcbRouter({ event });// 必需
// app.use 表示该中间件会适用于所有的路由(全局中间件) 非必需
app.use(async (ctx, next) => { // 这个中间件表示所有路由都会调用到,而路由中间件为单独调用
ctx.data = {}; // 获取要传给小程序端的数据
ctx.data.openId = event.userInfo.openId // 这里获取到的openId将分布到所有路由
await next(); // 执行下一中间件
});
// 路由为数组表示,该中间件适用于 user 和 timer 两个路由
app.router(['user', 'timer'], async (ctx, next) => {
ctx.data.company = 'Tencent'; // 这里获取到的数据将分布到 user 和 timer 两个路由
await next(); // 执行下一中间件
});
// 路由为字符串,该中间件只适用于 user 路由
app.router('user', async (ctx, next) => {
ctx.data.name = 'heyli'; // 获取要传给小程序端的数据
await next(); // 执行下一中间件
}, async (ctx, next) => {
ctx.data.sex = 'male'; // 获取要传给小程序端的数据
await next(); // 执行下一中间件
}, async (ctx) => {
ctx.data.city = 'Foshan'; // 获取要传给小程序端的数据
// ctx.body 返回数据到小程序端
ctx.body = { code: 0, data: ctx.data}; // 要传给小程序端的数据
});
// 路由为字符串,该中间件只适用于 timer 路由
app.router('timer', async (ctx, next) => {
ctx.data.name = 'flytam';
await next(); // 执行下一中间件
}, async (ctx, next) => {
ctx.data.sex = await new Promise(resolve => {
// 等待500ms,再执行下一中间件
setTimeout(() => {
resolve('male');
}, 500);
});
await next(); // 执行下一中间件
}, async (ctx)=> {
ctx.data.city = 'Taishan';
// ctx.body 返回数据到小程序端
ctx.body = { code: 0, data: ctx.data };
});
return app.serve(); // 必需
}
小程序端:
// 调用名为 router 的云函数,路由名为 user
wx.cloud.callFunction({
// 要调用的云函数名称
name: "router",
// 传递给云函数的参数
data: {
$url: "user", // 要调用的路由的路径,传入准确路径或者通配符*
other: "xxx"
}
}).then((res) => {
console.log(res)
})
上面tcb-router代码会按照洋葱模型执行,即先从上往下逐个进入中间件,再从下往上逐个退出中间件。
本地存储(缓存)
// 存储:
wx.setStorageSync(key, data) // 同步存储(存储成功再继续下一步操作)
wx.setStorage(key, data) // 异步存储(即使存储没成功也会执行下一步代码)、
// 读取:
wx.getStorageSync(key) // 同步 (读取到数据在进行下一步操作)
wx.setStorage(key) // 异步
api设置title
wx.setNavigationBarTitle({
title: '',
})
背景播放音
BackgroundAudioManager 全局唯一的背景音频管理器
// 需要在app.json配置,才能使用后台音乐播放的能力
"requiredBackgroundModes": ["audio", "location"]
// 获取全局唯一的背景音频管理器
const backgroundAudioManager = wx.getBackgroundAudioManager()
backgroundAudioManager.src = 音频链接
backgroundAudioManager.title = 音频标题
createSelectorQuery查询节点信息
createSelectorQuery 小程序的方法,用于查询节点等操作
const query = wx.createSelectorQuery()
query.select('#the-id').boundingClientRect() // 节点的布局信息
query.selectViewport().scrollOffset()
query.exec(function(res){
res[0].top // #the-id节点的上边界坐标
res[1].scrollTop // 显示区域的竖直滚动位置
})
组件内的方法
组件生命周期
// 生命周期
lifetimes: {
ready() { // 在组件在视图层布局完成后执行
...
}
},
组件所在页面的生命周期
Component({
pageLifetimes: {
show: function() {
// 页面被展示
},
hide: function() {
// 页面被隐藏
},
resize: function(size) {
// 页面尺寸变化
}
}
})
组件对数据的监听
observers: { // 对数据的监听(数据初次加载完成也会执行)
监听的数据对象(newData){
console.log(newData)
}
},
子组件自定义事件传递给父组件
子组件js:
// 触发自定义事件 向父组件传值, 参数x(可选,传递给父组件的参数,可以是对象或其他)
this.triggerEvent('自定义事件名', 参数x)
父组件wxml:
<子组件标签 bind:自定义事件名="执行的事件" />
父组件js:
执行的事件(event) {
console.log(event.detil.参数)
}
父组件自定义事件传递给子组件
父组件wxml:
<子组件标签 class="子组件类名">
父组件JS:
// 选择组件,并传入事件和参数
this.selectComponent('.子组件类名').自定义事件名(传入参数)
子组件js:
methods: {
自定义事件名(参数x){
console.log(参数x)
}
}
兄弟组件间传递事件和传值
例子:子组件1向子组件2传递参数
父组件wxml中:
<子组件标签1 bind:自定义事件名1="执行的事件">
<子组件标签2 class="子组件2类名">
父组件js:
执行的事件(event) {
this.selectComponent('.子组件2类名').自定义事件名2(event.detil.参数x) // 向子组件2传值
}
子组件1js:
// 触发自定义事件 向父组件传值, 参数x(可选,传递给父组件的参数,可以是对象或其他)
this.triggerEvent('自定义事件名1', 参数x)
子组件2js:
methods: {
自定义事件名2(参数x){
console.log(参数x) // 接收父组件传入的值
}
}
获取手机信息
wx.getSystemInfo(Object object)
wx.getSystemInfo({
success(res){
console.log(res) //手机信息
}
})
滚动组件
<scroll-view scroll-y scroll-top="{{scrollTop}}" scroll-with-animation="true">
</scroll-view>
全局属性、方法(类似vuex)
在app.js中:
onLaunch: function () {
this.globalData = {// 设置全局属性、方法
test: 0
}
},
setGlobalData(dataItem, val) { // 设置全局属性
this.globalData[dataItem] = val
},
getGlobalData(dataItem) { // 获取全局属性
return this.globalData[dataItem]
}
在需要调用的页面js中:
const app = getApp() // 在最顶部先调用app方法
// 设置全局属性
app.setGlobalData('test', 1)
// 获取全局属性
app.getGlobalData('test')
消息提示框
wx.showToast({
title: '成功',
icon: 'success', //图标: success 成功、loading 加载中、none 无
duration: 2000
})
《发现》页面
调用组件外部的样式
components内部的组件无法直接调用外部的样式。可通过以下方式调用组件外部样式:
方法一:
父组件wxml:
<!-- iconfont 和 icon-sousuo 是传入组件内部的样式名称,iconfont(自定义名称)="iconfont(外部样式文件中定义的样式名)" -->
<x-search iconfont="iconfont" icon-sousuo="icon-sousuo"/>
子组件js:
// 组件外部样式
externalClasses: [
'iconfont', // 对应的是上面等号前面的名称
'icon-sousuo'
],
子组件wxml: 即可实现调用组件外的样式
<i class="iconfont icon-sousuo" />
注意:如果想在组件内部再次修改样式,不能够引用外部传进来的class名称进行修改,可以另起一
- 点赞
- 收藏
- 关注作者
评论(0)