原生微信小程序
2021/8/2大约 14 分钟
原生微信小程序
基本语法
<view> {{message}} </view>
<view hidden="true"> {{message}} </view> // hidden属性隐藏
<text> Hello World </text>
<swiper>
<swiper-item autoplay indicator-dots circular>//轮播图自动播放、圆点、循环
<navigator url="___"> //跳转的页面路径
<image src="___" mode="widthfix"><image> //图片宽高自适应,保持原有比例,然后根据原图大小控制swiper的大小即可达到较好的展示效果
</navigator>
</swiper-item>
</swiper>
Page({
data: {
message: 'Hello MINA!',
list:['1','2','3','4'],
scrollTop:0
}
})
<view wx:for="{{_____}}" wx:key="__"> {{item}} </view> //内容可直接使用item
<block></block> //占位符
<view wx:if="{{view == 'WEBVIEW'}}"> WEBVIEW </view>
<view wx:elif="{{view == 'APP'}}"> APP </view>
<view wx:else="{{view == 'MINA'}}"> MINA </view>
<scroll-view scroll-y scroll-top="{{scrollTop}}">//每次切换页面时,用this.setData({})让将该页scrollTop变为0即可到达顶部
<view
wx:for="{{list}}"
>{{item}}</view>
</scroll-view>小程序规范
小程序 组件名称命名规则.小程序的组件名称命名规则遵循一定的规范,以确保代码的可读性和可维护性。以下是小程序组件名称命名的一般规则:
- 使用小写字母,组件名称应该全部使用小写字母,不要包含大写字母或者驼峰命名法。
- 使用连字符“-”分隔单词,组件名称中的多个单词之间使用连字符“-”进行分隔,例如"custom-component"。
- 避免使用与现有HTML标签相同的名称,为了避免与现有的HTML标签相冲突,最好不要使用与HTML标签相同的名称来命名组件。
- 使用具有描述性的名称,组件名称应该具有描述性,能够清晰地表达组件的作用或功能,便于其他开发者理解和使用。
- 不要使用保留关键字,避免在组件名称中使用JavaScript或小程序的保留关键字,以免引起命名冲突。总的来说,小程序组件名称的命名应该简洁明了,具有描述性,避免冲突和保留关键字,并且符合小程序的命名规范。这样可以提高代码的可读性和可维护性,也有利于团队协作开发。希望这些信息能够帮助到你。

小程序点击事件
//.wxml
<view
wx:for="{{list}}"
wx:for-item="item"
wx:for-index="index"
wx:key="*this"
bindtap="getIndex" //bindtap触发点击事件函数
data-index="{{index}}" //传入Index
data-src="{{item}}" //传入点击的相应数据
class="{{currentIndex===index?'active':''}}" //通过点击事件改变view的class
>{{item}}
</view>
//for循环大图预览图片
<view wx:for="{{item.image}}"
wx:for-item="item2"
wx:for-index="index"
wx:key="*this"
data-list="{{item.image}}" //传入当前正在遍历的数组
data-index="{{index}}" //传入index
data-src="{{item2}}" //传入图片url //和点击事件同级 重点!!!:要传入的数据必须和点击事件同级才能拿到!
class="user-comments-images"
bindtap="previewImg"> //预览事件
<image wx:if="{{item2}}" src="{{item2}}" mode="widthFix"></image>
</view>
//预览方法
previewImg(e){
wx.previewImage({
current: e.currentTarget.dataset.src,
urls: e.currentTarget.dataset.list
})
},
//.js
Page({
data:{
list:['123','234','345','456'],
currentIndex: 0;
},
getIndex(e){ //事件参数event
const {index}=e.currentTarget.dataset; //接收
this.setData({
currentIndex:index;
})
}
})异步请求与封装
//request/index.js内封装
export const request=function(params)({
const baseUrl='https://api-hmugo-web.itheima.net/api/public/v1' //定义公共部分的url
return new Promise((resolve,reject)=>{
wx.request({
...params,
url:baseUrl+params.url, //给url添加完整
success:(result)=>{
resolve(result);
};
fail:(err)=>{
reject(err);
};
})
})
})
//index.js
注:小程序内引入js文件要填写至.js
import { request } from "../../request/index"
Page({
data:{
list:[]
},
// 1.
// onLoad:function(options){
// request({ //调用封装的请求方法来请求数据
// url:"_____" //此处url请求数据可能需要在 mp.weixin.qq.com 处添加合法域名,或其他方法,才能访问到数据
// })
// .then((res)=>{
// this.setData({ //改写data里的数据用this.setData({}),如果在data:{}之外,用this.__
// list:res.___
// })
// })
// }
//2.
async onLoad:function(option){ //async await解决promise 和 .then回调地狱
const res=await request({url:'___'},data:"其他请求参数"); //这行执行完才会执行后面的
this.setData({ //改写data里的数据用this.setData({}),如果在data:{}之外,用this.__
list:res.data.message
})
}
})小程序其他杂项知识点
//iconfont图标的类引入方式,后即可直接用类名来实现icon显示
@import "___" //路径
//初始化页面,原生小程序不支持通配符选择器 app.wxss
page,view,text,swiper,swiper-item,image,navigator{
padding: 0;
margin: 0;
box-sizing: border-box;
}
//主题色的定义与引用
//app.wxss
page{
--themeColor:#ff2d4a;
font-size: 28rpx;
}
image{
width: 100%
}
//其他页面引用
.view{
background-color: var(--themeColor)
}
//小程序标签默认宽高 1px === 2rpx
小程序默认宽度为750rpx
swiper标签默认为 100% * 150rpx
image标签默认为 320rpx * 240rpx
// 为了在遮罩出现的时候禁止用户滑动页面,如下处理: catchtouchmove
<view catchtouchmove="true">我是弹出的遮罩层</view>
//在微信小程序开发中经常用到input组件,但是如果按下面的wxml代码去写,input的边框是不会显示的,只有在鼠标点过去的时候才会有个光标
.input1{
border-color: black;
border-style: soild
}
//cachtap和bindtap区别:阻止穿透和不阻止穿透,不冒泡和冒泡
//小程序数组对象赋值方法
data:{
priceArea:[]
}
this.setData({
['priceArea[0]']:value
})
//小程序text的decode解码,特殊符号
<text decode='true'> < > & '    </text>
对应: <>&'
//做业务时遇到一个问题,在弹窗弹起来的时候(弹窗内包含scrollview,scrollview触底,引起底层view滑动),但是在复杂一点的情况下还是会导致滚动穿透。使用官方的page-meta组件可直接操作Page组件,给它的wxss样式overflow动态设置hidden or visible or auto 就可以控制整个页面是否可以滚动,直接在页面加上下面的代码,即可控制<page></page>
<page-meta page-style="overflow: {{visible ? 'hidden' : 'visible'}}" />
// 获取用户头像
wx.showModal({ //是否已授权
title: '温馨提示',
content: '亲,授权微信登录后才能正常使用小程序哦',
success: (res)=>{
if(res.confirm){
wx.getUserProfile({
desc: '用于获取评论头像', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
lang: 'zh_CN',
success: (res) => {
console.log(res.userInfo);
},
fail: (err) => {
console.log(err)
}
})
}else if(res.cancel){ //点击取消
}
},
fail: ()=>{},
complete: ()=>{}
});小程序less
<view class="floor_list"> //使用easy less插件 进行相应设置后将自动生成wxss文件
<navigator
wx:for="{{item1.product_list}}"
wx:for-item="item2"
wx:for-index="index2"
wx:key="image_width"
>
<image mode="{{index2===0?'widthFix':'scaleToFill'}}" src="{{item2.image_src}}"></image>
</navigator>
</view>
//.wxss
.floor_list{
overflow: hidden;
navigator{
float: left; //左浮动
width: 33.33%; //控制为三列
&:nth-last-child(-n+4){ //五张图,选后四张
height: calc(33.33vw*386/232/2); //用calc()根据第一张图计算后四张的高度
border-left: 10rpx solid white; //左边框
}
&:nth-child(2), //第二三张
&:nth-child(3){
border-bottom: 10rpx solid white; //下边框
}
image{ //继承父级宽高
width: 100%;
height: 100%;
}
}
}
//第二行文字省略显示 针对第二行溢出文字...显示
[自适应布局display:-webkit-box的用法](参照https://www.cnblogs.com/zzz-knight/p/11635268.html)
display: -webkit-box;
overflow: hidden;
-webkit-box-orient: vertical;
-webkit-line-clamp:2;小程序缓存技术
web中的本地存储和 小程序的本地存储技术的区别:
写法
1.web: localStorage.setItem("key","value"); localStorage.getItem("key"); localStorage.removeItem("key")
2.小程序:wx.setStorageSync("key","value"); wx.getStorageSync("key");
存储区别
1.web:不管存入的是什么数据类型的数据,都会先调用toString()方法,把数据变成字符串,再存入进去
2.小程序:不存在类型转换的操作,存什么类型,取的就是什么类型//写数据
wx.setStorageSync("cate",{time:Date.now(),data:value})//记录了写数据时的时间戳,和value
//读数据
const Cates=wx.getStorageSync("cate")//注意读取的名字要对应
if(!Cates){
this.getCategoryData()//本地无数据,发送请求获取
}else{ //有数据是否过期
if(Date.now() - Cates.time>1000*10){//设为10秒过期
this.getCategoryData() //超时了,过期了
}else{ //未超时,未过期
//依据本地缓存数据进行渲染
this.cates=Cates.data
let leftList=this.cates.map(v=>v.cat_name)
let rightList=this.cates[0].children
this.setData({
leftList,
rightList
})
}
}小程序页面跳转传参
//category.wxml
<navigator
wx:for="{{item1.children}}"
wx:for-item="item2"
wx:for-index="index2"
wx:key="cat_id"
url="/pages/goods_list/goods_list?cid={{item2.cat_id}}" //跳转至goods_list页面时传递cid过去
>
<image mode="widthFix" src="{{item2.cat_icon}}"></image>
<view class="goods_name">{{item2.cat_name}}</view>
</navigator>
//goods_list.js
Page({
data: {
},
onLoad: function (options) {
console.log(options) //接收到参数"{ cid : 3 }"
},
function(){
wx.navigateTo({
url:"/pages/.../...?..."
})
}
})小程序组件引用与父子间传值
//goods_list.json
{
"usingComponents": {
"SearchInput":"../../components/SearchInput/SearchInput", //引入的组件
"Tabs":"../../components/Tabs/Tabs"
},
"navigationBarTitleText": "商品列表",
"enablePullDownRefresh":true, //可下拉刷新
"backgroundTextStyle":"dark" //刷新点颜色
}
//goods_list.js
Page({
data: {
tabs:[
{
id:0,
value:"综合",
isActive:true
},
{
id:1,
value:"销量",
isActive:false
},
{
id:2,
value:"价格",
isActive:false
}
]
},
handleTabsItemChange(e){} //接受子级传来的事件
})
//goods_list.wxml
<Tabs tabs={{tabs}} bindtabsItemChange="{{handleTabsItemChange}}"></Tabs> //父传子tabs 子传父:bind+事件名="方法"
// /components/Tabs/Tabs.js
Component({
properties: {
tabs:{ //接收父级数据
type:Array,
value:[]
}
},
methods:{
handleItemTap(e){
const {index}=e.currentTarget.dataset;
this.triggerEvent("tabsItemChange",{index}); //this.triggerEvent传给父级 事件名为tabsItemChange
}
}
},
})
// /components/Tabs/Tabs.wxml
<view class="title_item {{item.isActive?'active':''}}"
wx:for="{{tabs}}"
wx:key="id"
bindtap="handleItemTap" //触发点击事件及传入index
data-index="{{index}}"
>
{{item.value}}
</view>小程序生命周期回调、事件处理函数
//下拉事件、上滑触底事件
onPullDownRefresh,onReachBottom,
//屏幕滑动事件,用到了屏幕滚动事件的话,可能会用到的一个api是wx.createSeletorQuery
onPageScroll:function(e){ // 尽量避免在这个函数里频繁使用this.setData({}),
console.log(e.scrollTop); // 打印当前页面滚动条的位置信息
}
//用到了上面的屏幕滚动事件的话,可能会用到的一个api是wx.createSeletorQuery(可以获取到id某个元素的相关信息),示例如下
const query = wx.createSelectorQuery()
query.select('#the-id').boundingClientRect()
query.selectViewport().scrollOffset()
query.exec(function(res){
res[0].top // #the-id节点的上边界坐标
res[1].scrollTop // 显示区域的竖直滚动位置
})
// 用wx.createSeletorQuery时可能会遇到元素大小信息获取不准确的情况,可以在外层加一个定时器,延时获取,减小元素大小信息获取不准确的情况的概率
setTimeout(() => { wx.createSelectorQuery().select('#sale_info').boundingClientRect(function (rect) {
rect.height;
}).exec((res) => {
if (res[0].top) {
this.setData({
console.log(res[0].top);
})
}
})
}, 300);wxs的使用
创建.wxs文件(.wxs文件内部只能写ES5的语法,而且有些工具函数的用法与原生js有不同,具体请查看官方文档(框架)关于wxs的介绍),配合.wxml文件进行引入使用
小程序第三方库vant/weapp使用
- 使用小程序自带终端或命令行窗口调至相应文件夹,执行npm install @vant/weapp -S production;(npm install @vant/weapp)
- 开发工具里设置允许使用npm
- 开发工具左上角:工具=>构建npm
- 查weapp文档:https://vant-contrib.gitee.io/vant-weapp/#/home
小程序样式适配
圆角适配:transform: translateY(0);
水平对齐:vertical-align: top;
小程序样式相关
slot插槽:插槽有无内容(该插槽需要同控制的DOM在同一父节点下),对其他DOM样式的调整:
.slot: empty + .default {
display: block;
}vw的使用,直接使用vw来动态获取手机宽高
item: last-of-type {}小程序富文本
<rich-text nodes="nodes"></rich-text> // 富文本渲染
const node = []; // [ {}, {} ]格式
const node1 = { // 创建node节点
name: "span",
attrs: { style: " color: #26ce8a; font-size: 14px; " },
children: [{
type: "text",
text: key1
}]
}
node.push(node1);
const node2 = {
name: "span",
attrs: { style: "color: black; font-size: 14px;" },
children: [{
type: "text",
text: key2
}]
}
node.push(node2)顶部fix
page {
padding-top: 'fix节点高';
}
.view {
position: fixed;
}navigation设置
{
"navigationBarTextStyle": "white",
"navigationStyle": "custom"
}小程序获取手机配置
const info = wx.getSystemInfoSync(); // 可以把手机相关信息存至app.js里,方便访问及使用
console.log(info.statusBarHeight);
// 小程序标题栏默认44px小程序插槽配置
<view class="left"><slot name="left-slot"></slot></view>
<view class="center"><slot name="center-slot"></slot></view>options: {
multipleSlots: true; // 当使用到多个插槽时,必须加上
}<compon>
<view slot="left-slot"></view>
<view slot="center-slot"></view>
</compon>小程序音乐组件(推荐使用wx.getBackgroundAudioManager())
多查官方文档
多封装可复用代码,适当的自己进行重构代码,封装思想重要
// audioContext常用方法
// const audioContext = wx.createInnerAudioContext(); // 前台播放,一旦退出则停止播放
const audioContext = wx.getBackgroundAudioManager(); // 背景播放,可以在后台播放,控制栏也可看到相应的信息
注意!!!
必须设置的audioContext.src一定要和audioContext.title放在一起!!!!!(不能一个在同步中,一个在异步中,报的错居然是'Do not set same key \"165920\" in wx:key.')(排了四五个小时的bug,崩溃。。);
// app.json
{
"requiredBackgroundModes": ["audio"],
}
audioContext.play();
audioContext.pause();
audioContext.onCanplay(() => {});
audioContext.autoplay = true;
audioContext.onTimeUpdate(() => { // 重要!!!,歌曲播放的时间点
console.log(audioContext.currentTime);
})
audioContext.onPlay(() => {
ctx.isPlaying = true;
})
audioContext.onPause(() => {
ctx.isPlaying = false;
})
audioContext.seek(Number); // 单位为秒
// 配合silder组件
<slider bindchange = "__" bindchanging = "__"></slider>CSS显示两行,溢出...隐藏
.content {
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
display: -moz-box;
-moz-line-clamp: 2;
-moz-box-orient: vertical;
word-wrap: break-word;
word-break: break-all;
white-space: normal;
overflow: hidden;
}小程序事件捕获,阻止冒泡
bindtap="handleBtnClick"
handleBtnClick: function(event) {
playerStore.dispatch("controllMusicPlaying");
event.stopPropagation(); // 使用的是bindtap不支持该方法
},
catchtap="handleBtnClick"; // 使用catchtap可直接阻止冒泡小程序登录

App({
onLaunch: async function() {
// 让用户默认登录(先判断缓存是否有token)
const token = wx.getStorageSync(TOKEN_KEY);
if(token) { // 有token
// 判断token是否错误或者过期
const checkResult = await checkToken(token);
// 判断session是否过期
const isSessionExpire = await checkSession();
if(checkResult.errCode || !isSessionExpire) {
this.loginAction();
}
} else { // 无token
this.loginAction();
}
},
loginAction:async function() {
// 1.获取登录code
const code = await getLoginCode();
console.log(code);
// 2.发送code给服务器,获取token
const result = await codeToToken(code);
const token = result.token;
wx.setStorageSync(TOKEN_KEY, token)
}
})字符串base64加密
小程序使用
// base64加密
export const stringToBase64 = (str: string) => {
const textEncoder = new TextEncoder(); // 创建一个TextEncoder对象
const uint8Array = textEncoder.encode(str); // 将字符串转换为Uint8Array
return Taro.arrayBufferToBase64(uint8Array.buffer);
};
// 解密
export const Base64ToString = (base64: string) => {
const arrayBuffer = Taro.base64ToArrayBuffer(base64);
// 使用DataView和TextDecoder将ArrayBuffer转换为字符串
const dataView = new DataView(arrayBuffer);
const decoder = new TextDecoder("utf-8");
return decoder.decode(dataView);
};web端使用
const str = '123qwe'
window.btoa(str) // btoa加密 MTIzcXdl
const base64Str = 'MTIzcXdl'
window.atob(base64Str) // atob解密 123qwe小程序获取用户信息
<button open-type="getUserInfo" bindgetuserinfo="handleGetUserInfo">登录</button> <!-- 通过回调event参数获取,已弃用 -->
<button bindtap="handleGetUserInfo">登录</button>handleGetUserInfo: function(event) {
wx.getUserProfile({ // 必须通过回调方式才能拿到用户信息数据
desc: 'hello world',
success: res => {
console.log(res);
}
})
}小程序网络请求相关封装(仅供参考!!!,用于理解)
一定需要学会自己写,拒绝copy
/service/index.js
import { TOKEN_KEY } from '../constants/token-const'; // 'token'
const token = wx.getStorageSync(TOKEN_KEY);
const BASE_URL = 'http://__.__.__.__:9001';
// 用于获取登录的接口地址
const LOGIN_BASE_URL = "http://__.__.__.__:3000";
class JFRequest {
constructor(baseUrl, authHeader = {}) {
this.baseUrl = baseUrl;
this.authHeader = authHeader;
}
request(url, method, params, isAuth = false, header = {}) {
const finalHeader = isAuth ? { ...this.authHeader, header } : header; // 合并token和header,一起加入至请求头
return new Promise((resolve, reject) => {
wx.request({
url: this.baseUrl + url,
method: method,
data: params,
header: finalHeader,
success: function(res) {
resolve(res.data);
},
fail: reject
})
})
}
get(url, params, isAuth = false, header) {
return this.request(url, 'GET', params, isAuth, header);
}
post(url, data, isAuth = false, header) {
return this.request(url, 'POST', data, isAuth, header);
}
}
const jfRequest = new JFRequest(BASE_URL);
const jfLoginRequest = new JFRequest(LOGIN_BASE_URL, { token }); // 取得的token放入类内
export default jfRequest;
export {
jfLoginRequest
}小程序分包处理
可将不同的page归类至不同的包内,每个包有大小限制(2M)
分包完成后,该修改的引用路径应进行相应修改,该修改的跳转路径也进行相应修改
// app.json 示例
{
"subPackages": [
{
"root": "packageDetail",
"name": "pDetail",
"pages": [
"pages/detail-search/index",
"pages/detail-songs/index",
"pages/detail-video/index"
]
},
{
"root": "packagePlayer",
"name": "pPlayer",
"pages": [
"pages/music-player/index"
]
}
]
}不同的包也可以包含相应的asset,components文件夹

- 独立分包
- 独立分包是小程序中一种特殊类型的分包,可以独立于主包和其他分包运行。
[
{
"root": "moduleB",
"pages": [
"pages/pear",
"pages/pineapple"
],
"independent": true // 独立
}
]- 分包预下载
- 预下载分包行为在进入某个页面时触发,通过在
app.json增加preloadRule配置来控制。
- 预下载分包行为在进入某个页面时触发,通过在
{
"preloadRule": {
"pages/home-music/index": {
"network": "all",
"packages": ["packageDetail", "packagePlayer"]
}
}
}###taro小程序跳转传对象报错
直接使用JSON.stringify传递和使用JSON.parse解析会出现报错情况:Unexpected token o in JSON at position 1 at JSON.parse (<anonymous>)

encodeURIComponent编码JSON.stringify后的字符串
// 跳转前使用
Taro.navigateTo({ url: `/pages/schoolSafe/pages/aiAlarm/handleAlarm/index?clickItem=${encodeURIComponent(JSON.stringify(item))}` })JSON.parse解析decodeURIComponent解码后的字符串
import { getCurrentInstance } from '@tarojs/runtime';
// 获取实例
const instance = getCurrentInstance()
// decodeURIComponent解码
const resStr = decodeURIComponent(instance?.router?.params.clickItem as string)
// parse解析 注意不要JSON.parse(decodeURIComponent())连用,一样会报错!
console.log(JSON.parse(resStr))