小程序开发整理
使用
uni-app跨端开发框架,代码写法与vue2一致。
一、与web开发的区别
1. 运行方式不同
npm run dev:mp-weixin后,用微信开发者工具打开dist中工程。
2. 标签与web开发不同
标签的对应关系
| 小程序中使用 | web中使用 | 
|---|---|
| view | div | 
| text | span | 
| image | img | 
| navigator | a | 
此外,uni-app 还提供scroll-view、swiper等内置组件。 | 
3. 运行环境从浏览器变成v8引擎
不能使用document,window、localstorage、cookie等浏览器自带对象,但是可以使用 uni 提供的 api 替代。
3.1 获取dom元素
需要用到SelectorQuery 对象。
const query = uni.createSelectorQuery().in(this);
query.select('#id').boundingClientRect(data => {
  console.log("得到布局位置信息" + JSON.stringify(data));
  console.log("节点离页面顶部的距离为" + data.top);
}).exec();
// select 返回 NodesRef 对象,即为节点信息。
注:我们常用的
ref获取dom,不能用在uni-app内置组件上,只能用在自定义组件上。
3.2 数据缓存
本地数据缓存
// 异步
uni.setStorage({key: '', data: '', success: ()=>{}, fail: ()=>{}, complete: ()=>{}})
uni.getStorage({key: '', success: ()=>{}, fail: ()=>{}, complete: ()=>{}})
uni.removeStorage({key: '', success: ()=>{}, fail: ()=>{}, complete: ()=>{}})
uni.clearStorage()
// 同步
uni.setStorageSync({key: '', data: ''})
uni.getStorageSync({key: ''})
uni.removeStorageSync({key: ''})
uni.clearStorageSync()
// 获取当前 storage 相关信息
uni.getStorageInfo({success: (keys, currentSize, limitSize)=>{}})
const {keys, currentSize, limitSize} = uni.getStorageInfoSync();
4. 工程目录的区别
projectName/src
    ├── components
    ├── pages
    ├── ...
    ├── pages.json                    
    └── manifest.json
4.1 新增 pages.json
pages.json 文件用来对 uni-app 进行全局配置,决定页面文件的路径、组件自动引入规则、分包加载配置、窗口样式、原生的导航栏、底部的原生tabbar 等。
1)组件自动引入规则
"easycom": {
    "^tp-(.*)": "@/tp-view-ui/components/tp-$1/tp-$1.vue"
},
通过uni-app的easycom,只要组件安装在项目的 components 目录下,并符合 components/组件名称/组件名称.vue 目录结构。就可以不用引用、注册,直接在页面中使用。
- kebab-case (短横线分隔命名)
 - PascalCase (首字母大写命名)
 
2)页面文件路径
编译后,pages的第一个路径会成为首页
"pages": [
    {
        "path": "pages/index/indexA",
        "style": {
            "navigationBarTitleText": "AAA",
            "backgroundColor": "#FFFFFF"
        }
    },
    {
        "path": "pages/index/indexB",
        "style": {
            "navigationBarTitleText": "BBB",
            "backgroundColor": "#FFFFFF"
        }
    }
],
3)小程序的分包加载机制
微信小程序每个分包的大小是2M,总体积一共不能超过20M。
在小程序启动时,默认会下载主包并启动主包内页面,当用户进入分包内某个页面时,会把对应分包自动下载下来,下载完成后再进行展示。
"subPackages": [
    {
        "root": "pages/xxxA",
        "pages": [{
            "path": "list/list",
            "style": {}
        }]
    }, 
    {
        "root": "pages/xxxB",
        "pages": [{
            "path": "detail/detail",
            "style": {}
        }]
    }
],
4.2 新增 manifest.json
manifest.json 文件是应用的配置文件,用于指定应用的名称、图标、权限等。
"mp-weixin": {
    /* 微信小程序特有相关 */
    "appid": "wx303f35140da12d22",
    "setting": {
        "urlCheck": true, // 检查安全域名和 TLS 版本
        "minified": true, // 上传代码时自动压缩
        "postcss": true, // 上传代码时样式自动补全
        "es6": true // ES6 转 ES5
    },
    "usingComponents": true, // 全局自定义组件
    "permission": { // 小程序接口权限相关设置
        "scope.userLocation": {
            "desc": "你的位置信息将用于小程序位置接口的效果展示"
        }
    }
},
appid:微信小程序的AppID,登录 微信公众平台 申请。
5. CSS 注意事项
5.1 像素单位
uni-app 默认为 rpx。rpx 即响应式 px,一种根据屏幕宽度自适应的动态单位。
一般UI上手机模型宽度为375px,换算关系为 1px = 2rpx。
5.2 布局
推荐使用 flex 布局。
比如上下子元素高度不固定,中间子元素占据剩余空间:

.flex {
    display: flex;
    flex-direction: column;
    height: 600px; /* 父元素一定要指定高度 */
    width: 375px;
}
.flex-box1 {
    height: 200px;
    background: #b7dff0;
}
.flex-box2 {
    background-color: #bfa;
    flex: auto;
}
.flex-box3 {
    height: 200px;
    background: #b7dff0;
}
5.3 小程序组件的视图层级
组件内元素被shadow-root包裹,修改样式时需要做/deep/穿透。

二、uni-app 的生命周期
1. 应用生命周期
只能在
App.vue里监听应用的生命周期
| 函数名 | 说明 | 
|---|---|
| onLaunch | 当uni-app 初始化完成时触发(全局只触发一次) | 
| onShow | 当 uni-app 启动,或从后台进入前台显示 | 
| onHide | 当 uni-app 从前台进入后台 | 
| onError | 当 uni-app 报错时触发 | 
| onUniNViewMessage | 对 nvue 页面发送的数据进行监听,可参考 nvue 向 vue 通讯
 | 
| onUnhandledRejection | 对未处理的 Promise 拒绝事件监听函数(2.8.1+) | 
| onPageNotFound | 页面不存在监听函数 | 
| onThemeChange | 监听系统主题变化 | 
2. 页面生命周期
| 函数名 | 说明 | 
|---|---|
| onLoad | 监听页面加载,其参数为上个页面传递的数据,参数类型为 Object(用于页面传参) | 
| onShow | 监听页面显示。页面每次出现在屏幕上都触发,包括从下级页面点返回露出当前页面 | 
| onReady | 监听页面初次渲染完成。注意如果渲染速度快,会在页面进入动画完成前触发 | 
| onHide | 监听页面隐藏 | 
| onUnload | 监听页面卸载 | 
| onResize | 监听窗口尺寸变化 | 
| ... | .... | 
微信小程序的页面生命周期

使用 onLoad 来页面传参
//在起始页面跳转到test.vue页面并传递参数
uni.navigateTo({
	url: 'test?id=1&name=uniapp'
});
// 在test.vue页面接受参数
export default {
	onLoad(option) { //option为object类型,会序列化上个页面传递的参数
		console.log(option.id); //打印出上个页面传递的参数。
		console.log(option.name); //打印出上个页面传递的参数。
	}
}
- 参数复杂的情况
 
uni.navigateTo({
	url: 'test?data=' + JSON.stringify(obj)
});
onLoad: function (option) {
	const data = JSON.parse(option.data);
}
- 参数中出现空格等特殊字符,比如 url
 
uni.navigateTo({
	url: 'test?data=' + encodeURIComponent(JSON.stringify(obj))
});
onLoad: function (option) {
	const data = JSON.parse(decodeURIComponent(option.data));
}
除了
onLoad,还可以用vuex,uni.$on(eventName,callback),uni.$emit(eventName,OBJECT)进行页面通讯。
3. 组件生命周期
与 vue 标准组件的生命周期相同
| 函数名 | 说明 | 平台差异说明 | 
|---|---|---|
| beforeCreate | 在实例初始化之前被调用。 | |
| created | 在实例创建完成后被立即调用。 | |
| beforeMount | 在挂载开始之前被调用。 | |
| mounted | 挂载到实例上去之后调用。 | |
| beforeUpdate | 数据更新时调用,发生在虚拟 DOM 打补丁之前。 | 仅H5平台支持 | 
| updated | 由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。 | 仅H5平台支持 | 
| beforeDestroy | 实例销毁之前调用。在这一步,实例仍然完全可用。 | |
| destroyed | Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。 | 
三、特点功能整理
1. 二维码显示
使用插件 tki-qrcode,uni-app 二维码生成器。
安装
npm i tki-qrcode -S
引入
import tkiQrcode from "tki-qrcode";
export default {
    components: {tkiQrcode}
}
使用
<tki-qrcode
            ref="qRCodeRef"
            :val="qrcodeUrl"
            :size="424"
            :onval="true"
            :loadMake="true"
            :showLoading="true"
            @result="getQrImgUrl" />
属性
| 属性名 | 类型 | 默认值 | 可选值 | 说明 | 
|---|---|---|---|---|
| size | Number | 200 | 生成的二维码大小 | |
| unit | String | upx | px | 大小单位尺寸 | 
| val | String | 二维码 | 要生成的内容 | |
| lv | Number | 3(一般不用设置) | 容错级别 | |
| onval | Boolean | false | 监听val值变化自动重新生成二维码 | |
| loadMake | Boolean | false | 组件初始化完成后自动生成二维码,val需要有值 | |
| showLoading | Boolean | true | false | 是否显示loading | 
| loadingText | String | 二维码生成中 | loading文字 | |
| ... | ... | ... | ... | ... | 
方法
| 方法名 | 参数 | 默认值 | 说明 | 
|---|---|---|---|
| _makeCode() | 生成二维码 | ||
| _clearCode() | 清空二维码(清空二维码会触发result回调 返回值为空) | ||
| _saveCode() | 保存二维码到图库 | 
事件
| 事件名 | 返回值 | 说明 | 
|---|---|---|
| result | 生成的图片base64或图片临时地址 | 返回二维码路径 注:_clearCode()后返回空 | 
2. 二维码卡片下载
使用插件xWxmlToCanvas,根据微信小程序wxml-to-canvas封装。
安装
npm i x-wxml-to-canvas -S
引入
实测引入node_modules内插件,条件编译时会走H5,而不是MP(小程序)。只有复制到项目内,才会走小程序。
import xWxmlToCanvas from './components/x-wxml-to-canvas/x-wxml-to-canvas';
export default {
    components: {xWxmlToCanvas}
}
使用
<xWxmlToCanvas
               ref="xWxmlToCanvas" 
               :hide="true" 
               :useCORS="true" 
               :width="311" 
               :height="518" 
               :xStyle="myStyle" 
               :xWxml="myWxml">
</xWxmlToCanvas>
属性
| 字段 | 类型 | 默认值 | 描述 | 
|---|---|---|---|
| hide | Boolean | false | canvas是否在页面可见 true 通过fixed将canvas移至屏幕外 | 
| width | Number | 300 | canvas宽度,单位px | 
| height | Number | 300 | canvas高度,单位px | 
| xWxml | String | wxml 模板 | |
| xStyle | Object | {} | 样式 | 
| useCORS | Boolean | true | canvas 图片跨域 | 
wxml 模板
- 
支持
view、text、image三种标签,通过 class 匹配 style 对象中的样式。 - 
文字必须用
text标签包含,否则不显示。动态文字需要动态赋值宽高,否则会截断不显示。 
const wxml = (projectName, inviteToChainStore, inviteExpiredTime, imgUrl) => `
    <view class="invite-card vertical-center pos-r">
        <text class="fw-b project-name">${projectName}</text>
        <image class="qrcode" src="https://www.cnblogs.com/culciful/archive/2022/12/21/${imgUrl}"></image>
        <text class="invite-desc fw-b">${inviteToChainStore}</text>
        <view class="footer">
            <text class="expired-time ta-c text">${inviteExpiredTime}</text>
            <text class="text ta-c">TP-LINK商云</text>
        </view>
    </view>
`;
imgUrl 通过tki-qrcode组件获取。
样式
对象属性值为对应 wxml 标签的 class 驼峰形式。需为每个元素指定 width 和 height 属性,否则会导致布局错误。
存在多个 className 时,位置靠后的优先级更高,子元素会继承父级元素的可继承属性。
元素均为 flex 布局。left/top 等 仅在 absolute 定位下生效。
const style = (rowLength) => {
    let textWidth = 212;
    return {
        "verticalCenter": {
            display: 'flex',
            flexDirection: 'column',
            alignItems: 'center',
            textAlign: 'center',
        },
        "inviteCard": {
            width: 311, // 单位为 px
            height: 518,
            position: 'relative',
            backgroundColor: '#fff',
            borderRadius: 16,
        },
        "projectName": {
            marginTop: 32,
            fontSize: 16,
            lineHeight: '1.2',
            height: rowLength * 22,
            width: textWidth,
            fontWeight: 600,
        },
 		...
    };
};
方法
| 方法名 | 描述 | 返回值 | 
|---|---|---|
| renderToCanvas | 将wxml渲染至页面 | 无 | 
| canvasToTempFilePath | 将canvas转为图片地址 (H5端 Canvas 内绘制的图像需要支持跨域访问才能成功) | promise函数,返回 图片地址,h5为base64 | 
| getCanvasImage | renderToCanvas 与 canvasToTempFilePath 合并 | promise函数,返回 图片地址,h5为base64 | 
| saveImageToPhotosAlbum | 图片保存至本地 | promise函数,返回 true / flase | 
this.$refs.xWxmlToCanvas.renderToCanvas().then(() => {
    this.$refs.xWxmlToCanvas.canvasToTempFilePath().then(res => {
        this.$refs.xWxmlToCanvas.saveImageToPhotosAlbum(res);
    });
});
真机调试不支持
canvas type2d,可用预览模式。
3. 退出页面时拦截显示弹窗
3.1 开启小程序页面返回询问对话框
watch: {
    isChanged:{
        handler: function(val) {
            //#ifdef MP-WEIXIN
            if (val) {
                wx.enableAlertBeforeUnload({ // 只有真机会出现
                    message: this.$t('pStoreInfo.quitConfirm')
                });
            } else {
                wx.disableAlertBeforeUnload();
            }
            //#endif
        },
        immediate: true
    }
}
样式简单时可以使用。

3.2 微信小程序视图容器 page-container
“假页”容器组件,效果类似于 popup 弹出层,页面内存在该容器时,当用户进行返回操作,关闭该容器不关闭页面。
<tp-safe-view has-cutline class="u-h-full">
	<!--弹窗内容-->  	
    <page-container
        :show="show"
        position="center"
        @beforeleave="beforeleave">
       <!--页面主体内容-->
    </page-container>
</tp-safe-view>
beforeleave() {} // 离开前触发
四、其他事项记录
- 
条目之类的有滚动条时和底部or底部按钮不一定有距离,但拉到底要有
80rpx - 
分页加载一般请求条数为10、20
 - 
预览模式,在真机右上角三个点打开调试使用 vConsole
 - 
页面在
onLoad获取数据传递给组件,而组件在created生命周期使用该数据,有时会出现组件未获取到值的情况。