小程序这个也经常开发要掌握(一)

举报
龙哥手记 发表于 2022/09/17 14:55:33 2022/09/17
【摘要】 《JavaScript》系列,第二十七篇希望你持续关注哦!

基础部分

微信公众平台

https://mp.weixin.qq.com/

注册时可选择类型:订阅号、服务号、小程序、企业微信

每个邮箱仅能注册一个小程序。

个人类型小程序:无法使用微信支付、无法使用卡包功能

小程序文档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 全局配置

全局配置API

wxml 相关介绍

wxmlAPI

<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 相关介绍

wxssAPI

尺寸单位:rpx,根据屏幕宽度自适应。

引入外部wxss:@import ’...‘

js相关介绍

WXS(WeiXin Script)是小程序的一套脚本语言

wxsAPI

绑定点击事件

<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

API

{
  "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 // 显示区域的竖直滚动位置
})

组件内的方法

Component(Object object)

组件生命周期

lifetimes

// 生命周期
lifetimes: {
    ready() { // 在组件在视图层布局完成后执行
        ...
    }
},
组件所在页面的生命周期
Component({
  pageLifetimes: {
    show: function() {
      // 页面被展示
    },
    hide: function() {
      // 页面被隐藏
    },
    resize: function(size) {
      // 页面尺寸变化
    }
  }
})

组件对数据的监听

observers

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-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')

消息提示框

showToast

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名称进行修改,可以另起一
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。