前端:
客户前端,客户将数据发给后台,后台将数据存到数据库
管理前端,管理向后台索要数据,后台从数据库查找数据
一、移动端基础
1、移动端的长度单位
(1)%,相对于父元素的尺寸
(2)em,相对于父字体大小
(3)rem,相对于根字体大小
(4)vw,viewpoint width,视窗宽度,1vw等于视窗宽度的1%,如width: calc(100vw - 260px);font-size: 5vw;
(5)vh,viewpoint height,视窗高度,1vh等于视窗高度的1%,如height: calc(100vh - 260px);font-size: 5vh;
(6)vmin:vw和vh中较小的那个。做移动页面开发时,如果使用 vw、wh 设置字体大小(比如 font-size: 5vw),在竖屏和横屏状态下显示的字体大小是不一样的。
(7)vmax:vw和vh中较大的那个。vmin或vmax,是当前vw和vh中,较小或较大的那个,用vmin或vmax,使得文字大小在横竖屏下保持一致
附、媒体查询
@media screen and (min-width: 360px) and (max-width: 700px){
padding-left: 15%;
}
2、接到新(或别人的)项目时,从以下几个方面入手,
(1)根据全图,把握项目整体布局,background、margin、padding及1,2,3层,font-family、font-size1,2,3种
(2)根据全图,把握项目整体逻辑,比如跳转、本地存储的方式(vuex或localStorage)、静态数据的来源(设计稿或后台)
(3)代码过程,单图主体-写代码-单图注释-完善代码,重复此步骤
(4)联调
附、左右居中(换行也可以)
<div class="center">
<div><img src=""/></div>
<div></div>
<div></div>
<div></div>
</div>
.center{
display: flex;
flex-direction: column;
//align-item: center;//换行不居中
text-align: center;//换行居中
}
3、问题与解决
(1)iPhone12pro max,跳到新网页,新网页的位置没到顶,
原因:跳转前的网页没到顶,浏览器把前网页的位置用在后网页上;
解决:document.body.scrollTop=0;document.documentElement.scrollTop=0;
二、uniapp跨端框架
来源,https://gitee.com/dcloud/uni-app/tags?page=60
来源,https://uniapp.dcloud.net.cn/history.html
1、uniapp的产生背景
(1)2012年,DCloud开始研发小程序技术,推出了HBuilder开发工具
(2)2015年,DCloud商用了小程序技术,命名“流应用”,并将该技术标准捐给工信部,同时推进各家流量巨头接入该标准,众多开发者用该标准为“流应用”平台提供应用,供用户下载
(3)2015年9月,DCloud推进微信团队开展小程序业务
(4)2016年初,微信团队决定上线微信小程序业务,订制了自己的标准,随后包括手机厂商在内的各大流量巨头,陆续上线类似微信小程序的业务
(5)DCloud用自己的开发工具HBuilder、自己的开发框架uni-app为开发者抹平“各平台”的差异,让“一套代码”在“各APP”的客户端运行,由标准的提供者转向标准的汇总者
(6)微信开发工具开发的小程序只能在微信上运行,HBuilder用uni-app开发的小程序能在所有应用上运行,https://uniapp.dcloud.net.cn/
2、uni-app的版本发布
(1)2018年8月,uni-app1.0.0 版本发布,
(2)2021年9月,uni-app2.0.0 版本发布,
(3)2023年1月,uni-app3.0.0 版本发布,
3、uni-app的相关内容
(1)uni小程序SDK,是DCloud开发的原生SDK,提供Android、iOS两个版本,需要集成到原生工程中,使APP的服务端能运行用uni-app框架开发的小程序
(2)uniCloud,基于serverless的云开发平台,为开发者提供免费服务器,由DCloud、阿里云、腾讯云三者联合实现
(3)Uni,统一的,读you ni
(4)SDK,软件开发工具包
4、与“uniapp跨端框架”相似内容,混合类解决方案(Hybrid、RN、 flutter等)
三、vant-ui相关
1、<input type="value">
(1)button,定义可点击按钮(多数情况下,用于通过 JavaScript 启动脚本)
(2)checkbox,定义复选框
(3)file,定义输入字段和 "浏览"按钮,供文件上传
(4)hidden,定义隐藏的输入字段
(5)image,定义图像形式的提交按钮
(6)password,定义密码字段,该字段中的字符被掩码
(7)radio,定义单选按钮
(8)reset,定义重置按钮,重置按钮会清除表单中的所有数据
(9)submit,定义提交按钮,提交按钮会把表单数据发送到服务器
(10)text,定义单行的输入字段,用户可在其中输入文本,默认宽度为 20 个字符
2、深度选择器
当<style scoped>只作用于当前组件中的元素,可采用下列方式影响到子组件
(1)全局样式
<style>
.wrap .child {
color: red;
}
</style>
(2)本页样式 >>>
<style scoped>
.wrap >>> .child {
color: red;
}
</style>
(3)本页样式 /deep/
<style scoped>
.wrap /deep/ .child {
color: red;
}
</style>
3、vant-ui动态内容
(1)toast 轻提示,没有遮罩,页面居中显示,无样式变化,3秒后自动消失
(2)notify 消息提示,没有遮罩,页面顶部显示,有样式变化,3秒后自动消失
(3)NoticeBar 通知栏,没有遮罩,固定位置显示,有样式变化,不消失
(4)Popover 气泡弹出框,没有遮罩,常用于点击处显示,常用于下拉选项
(5)overlay 遮罩层,全屏罩住
(6)popup 弹出层,有遮罩,常用触底显示,确认后消失,常用于省市县、日期选择
(7)dialog 弹出框,有遮罩,常用居中显示,确认后消失
4、van-field
(1)输入框,正常
(2)文本框,type="textarea"
(3)下拉框的逻辑
输入框禁用,右侧添加三角,
点击事件让下方气泡出现、传出序号、计算-从总数据中找出的相关数据-成下拉选项,
点击事件从自身数据中找出的相关数据-成为下拉选项,气泡出现
点击确定时,给对应的v-model赋值,气泡消失
(4)其它,用插槽input,
自定义输入框,使用此插槽后,与输入框相关的属性和事件将失效
在Form组件进行表单校验时,会使用input插槽中子组件的value,而不是Field组件的value
(5)<van-uploader/>的部分属性
capture,图片选取模式,可选值为camera,直接调起摄像头
after-read,文件读取完成后的回调函数
before-read,文件读取前的回调函数,返回false可终止文件读取,支持返回Promise
4、Rule数据结构,只有一个对象项的数组
(1)required,是否为必选字段,boolean
(2)message,错误提示文案
(3)trigger,本项规则的触发时机,可选值为onChange、onBlur
(4)formatter,格式化函数,将表单项的值转换后,再用“pattern”或“validator”进行校验
(5)pattern,通过正则表达式进行校验,返回值为false时,红色的message出现在输入框的下方
(6)validator,通过函数进行校验,返回值为false时,机制让红色的message出现在输入框的下方;
返回值为promise实例时,机制向其注入resolve、reject,在reject里让红色的message出现在输入框的下方
validator:function(rule, val, callback) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
const dataNum = Date.now()
if (dataNum % 2 === 0) {
resolve('成功')
} else {
reject('失败')
}
}, 2000)
})
}
5、插槽input示例
(1)html部分
<van-form @submit="beforeSubmit" ref="form">
<template v-for="(item,idx) in formList">
<div class="sign-form" :key="idx" v-if="item.inputType == 0">
<van-field name="name" :rules="[{ validator: valiFormData, message: '请输入正确内容',index:idx }]" show-word-limit
:label="item.topicName" v-model="formData[idx]" :placeholder="'请输入'+item.topicName" :maxlength="item.numLimit" />
</div>
<div class="sign-form" :key="idx" v-if="item.inputType == 1">
<van-field rows="4" :rules="[{ validator: valiFormData, message: '请输入正确内容',index:idx }]" autosize
v-model="formData[idx]" :label="item.topicName" type="textarea" show-word-limit />
</div>
<div class="sign-form" :key="idx" v-if="item.inputType == 2">
<van-field :label="item.topicName" v-model="formData[idx]" right-icon="arrow-down" readonly placeholder="请选择"
:rules="[{ validator: valiFormData, message: '请输入正确内容',index:idx }]" @click="changeCol(idx)" />
</div>
<div class="sign-form" :key="idx" v-if="item.inputType == 3 && item.isCheckbox == 0">
<van-field :label="item.topicName" :rules="[{ validator: valiFormData, message: '请输入正确内容',index:idx }]">
<template #input>
<van-radio-group v-model="formData[idx]" direction="horizontal" class="hor-style">
<van-radio v-for="(v,k) in JSON.parse(item.optionInfo)" :name="v.option" :key="k">{{v.option}}</van-radio>
</van-radio-group>
</template>
</van-field>
</div>
<div class="sign-form" :key="idx" v-if="item.inputType == 3 && item.isCheckbox == 1">
<van-field :label="item.topicName" :rules="[{ validator: valiFormData, message: '请输入正确内容',index:idx }]">
<template #input>
<van-checkbox-group v-model="formData[idx]" :max="item.selectLimitMax ? item.selectLimitMax : 0" direction="horizontal">
<van-checkbox v-for="(value,key) in JSON.parse(item.optionInfo)" :name="value.option" shape="square" :key="key">{{value.option}}</van-checkbox>
</van-checkbox-group>
</template>
</van-field>
</div>
<div class="sign-form" :key="idx" v-if="item.inputType == 4">
<div class="van-form_card">
<div class="field__label">{{item.topicName}}</div>
<div class="field__desc">支持“图片”扩展名:png、gif、jpeg、pcx、psd、tiff</div>
</div>
<van-field name="name" type="hidden" :rules="[{ validator: valiFormData, message: '请输入正确内容',index:idx }]" v-model="formData[idx]">
<template #input>
<div class="upload-preview">
<van-image v-if="formData[idx]" width="4rem" height="3rem" fit="contain" :src="formData[idx]" />
<van-uploader :before-read="uploadFile" accept="image/png,image/gif,image/jpeg,image/pcx,image/psd,image/tiff" :name="idx">
<van-icon name="plus" />
</van-uploader>
</div>
</template>
</van-field>
</div>
<div class="sign-form" :key="idx" v-if="item.inputType == 5">
<div class="van-form_card">
<div class="field__label">{{item.topicName}}</div>
<div class="field__desc">支持“视频”扩展名:avi、MP4、rmvb</div>
</div>
<van-field name="name" type="hidden" :rules="[{ validator: valiFormData, message: '请输入正确内容',index:idx }]" v-model="formData[idx]">
<template #input>
<div class="flex-col">
<div class="video-upload" v-if="showUpload">
<span>视频上传中...</span>
<van-progress :show-pivot="false" :percentage="videoUploadPercent" />
</div>
<span class="link" v-if="formData[idx]" @click="href(idx)">{{videoName[idx]}}</span>
<van-uploader :before-read="uploadVideo" accept="*" v-if="!showUpload" :name="idx">
<van-button icon="plus" type="primary">上传视频</van-button>
</van-uploader>
</div>
</template>
</van-field>
</div>
<div class="sign-form" :key="idx" v-if="item.inputType == 6">
<div class="van-form_card">
<div class="field__label">{{item.topicName}}</div>
<div class="field__desc">支持“文件”扩展名:doc、docx、txt、pdf、pptx、ppt、xlsx、xls</div>
</div>
<van-field name="name" type="hidden" :rules="[{ validator: valiFormData, message: '请选择',index:idx }]" v-model="formData[idx]">
<template #input>
<div class="flex-col">
<span class="link" v-if="formData[idx]" @click="href(idx)">{{formData[idx]}}</span>
<van-uploader :before-read="uploadFile" accept=".doc, .docx, .txt, .pdf, .pptx, .ppt, .xlsx, .xls" :name="idx">
<van-button icon="plus" type="primary">上传文档</van-button>
</van-uploader>
</div>
</template>
</van-field>
</div>
<div class="sign-form" :key="idx" v-if="item.inputType == 7">
<van-field readonly @click="clickDatePicker(idx)" name="name" :rules="[{ validator: valiFormData, message: '请选择日期',index:idx }]"
:label="item.topicName" v-model="formData[idx]" :placeholder="'请选择'+item.topicName" />
<van-popup v-model="showDatePicker" position="bottom">
<van-picker
show-toolbar
title="选择日期"
:columns="dateCol"
@cancel="showDatePicker = false"
@confirm="dateConfirm" />
</van-popup>
<!-- van-popup默认在兄级加蒙层van-overlay,若没有出现,可能有人在项目的全局给清除了 -->
</div>
</template>
<div class="vt-foot ">
<van-button :loading="loading" loading-text="保存中..." native-type="submit" block color="#fdb235">提交</van-button>
<!-- native-type,原生button标签的type属性,表单内点击此按钮,自动触发表单的onsubmit事件 -->
<!-- loading,设置按钮为加载状态,加载状态下默认会隐藏按钮文字 -->
<!-- loading-text,设置加载状态下的文字 -->
<!-- confirm函数会弹出一个带有确定和取消按钮的对话框,点击确定返回true,点击取消返回false -->
<!-- alert函数只有一个确定按钮,点击确定后对话框消失,没有返回值 -->
</div>
</van-form>
(2)js部分
A、上传图片
uploadFile(file, detail) {
let formData = new FormData();
formData.append('file', file);
axios.post(this.$upload, formData, {
'Content-Type': 'multipart/form-data'
})
.then(res => {
if (res.data && res.data.code === 10000) {
this.formData[detail.name] = res.data.data;
let temp = res.data.data;
this.formData.splice(detail.name, 1, temp);
} else {
this.$dialog.alert({
title: '提示',
message: '上传文件失败',
theme: 'round-button',
})
.then(() => {
// on close
});
}
})
},
B、上传视频
来源,https://help.aliyun.com/document_detail/383952.html
import OSS from "ali-oss";
// 开放存储服务(OpenStorageService,简称OSS),是阿里云对外提供的海量,安全,低成本,高可靠的云存储服务。
// 用户可以通过简单的API(REST方式的接口),在任何时间、任何地点、任何互联网设备上进行数据上传和下载。
<van-uploader accept="*" v-if="!showUpload" :name="idx" :before-read="uploadVideo">
<van-button icon="plus" type="primary">上传视频</van-button>
</van-uploader>
uploadVideo(file, detail) {
let type = file.name.substring(file.name.lastIndexOf(".") + 1)
.toLowerCase()
if (this.videoType.indexOf(type) < 0 || file.type.indexOf("video") < 0) {
this.$dialog.alert({
title: '提示',
message: '仅支持 .avi, .mp4, .rmvb, .mov 格式的视频',
theme: 'round-button',
});
return;
}
let max = 1024 * 1024 * 1024;
if (file.size > max) {
this.$dialog.alert({
title: '提示',
message: '上传视频大小不能超过 1G!',
theme: 'round-button',
})
.then(() => {});
return;
}
myRequest({
tokenName: "ios",
})
.then(data => {
let client = new OSS({
region: "oss-cn-beijing",
accessKeyId: data.data.data.accessKeyId,
accessKeySecret: data.data.data.accessKeySecret,
bucket: data.data.data.bucketName,
stsToken: data.data.data.securityToken,
});
const suffix = file.name.substr(file.name.indexOf("."));
let fileUrl = `test/${new Date().getTime()}${suffix}`;
client
.multipartUpload(fileUrl, file, {
progress: (p) => {
this.videoUploadPercent = Math.round(p * 100);
this.showUpload = true;
},
partSize: 102400,
})
.then((res) => {
this.showUpload = false;
this.videoName[detail.name] = res.name;
let url = res.res.requestUrls[0];
this.formData[detail.name] = url.substring(0, url.indexOf("?"));
let temp = this.formData[detail.name];
this.formData.splice(detail.name, 1, temp);
})
.catch((err) => {
this.showUpload = false;
console.log(err);
this.$dialog.alert({
title: '提示',
message: '上传失败',
theme: 'round-button',
})
});
})
}
四、微信小程序-账号与代码管理
1、微信公众平台,https://mp.weixin.qq.com/
(1)账号分类(四种),服务号(解答用户提问,每月可群发4次)、订阅号(向用户提供资讯,每天可群发1次)、小程序(免关注,免订阅,扫码即用,用完即退)、企业微信(原企业号,用于企业办公管理)
(2)注册,小程序-测试号-游戏号&其它号的注册流程,公众平台-小程序图标(查看详情)-前往注册-立即申请-扫码(即可获得游戏号&其它号)
(3)注册,小程序-普通号-游戏号/其它号的注册流程,公众平台-小程序图标(查看详情)-前往注册-注册(账号信息)-邮箱激活-信息登记-添加类目-各种服务/工具/体育/游戏
(4)登录,APP扫描公众平台的二维码,选择其中一个账号登录
(5)开发,小程序-开发文档,https://developers.weixin.qq.com/miniprogram/dev/framework/
(6)开发,小程序-开发工具,https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html
2、账号,
(1)小程序普通号
AppID(小程序ID),wx209859
原始ID,gh_9b058
(2)小程序git账户
用户名:1371
密码:4195
(3)小程序域名
服务器域名(第三方数据库的数据)
业务域名(自己数据库的数据)
(4)小程序测试号
AppID(小程序ID),wxbdb0903
AppSecret(小程序密钥),d0b0452d67d04
(5)小游戏测试号
AppID(小程序ID),wxad135a
AppSecret(小程序密钥),2fd7568502
3、项目,https://git.weixin.qq.com
(1)创建项目,项目(组)-创建项目(组),https://git.weixin.qq.com/138/xiau1.git
(2)项目下载与上传,
====项目为空时,会有以下内容
项目初始化git命令行
====以下,git全局设置
git config --global user.name "Z_北京码农_钱成"
git config --global user.email "oncwnuJ4pkIPHnP16tr37ZtkhVxE@git.weixin.qq.com"
====以下,创建一个新的版本库(用命令克隆-线上版本库的项目,进行开发,推送)
git clone https://git.weixin.qq.com/138/xiau1.git(连接远程、克隆)
cd xiangmuzu1
touch README.md
git add README.md
git commit -m "add README"
git push -u origin master(推送到远程master分支)
====以下,现有的文件夹或Git版本库(用现有的文件夹或下载的版本库文件夹-与-线上版本库的项目-关联,进行开发,推送。如现有的文件夹里没有分支,只能推送到master分支)
cd existing_folder
git init
git remote add origin https://git.weixin.qq.com/138/xiau1.git(连接远程)
git add .
git commit -m "add README"
git push -u origin master(推送到远程master分支)
(3)代码管理
开发文档,开发-工具-开发辅助-微信开发者·代码管理
开发工具,工具-微信开发者·代码管理
删除仓库,管理后台(https://git.weixin.qq.com/)-项目-您的项目-XXXXX-设置-高级设置-仓库设置-删除项目
删除仓库,管理后台(https://git.weixin.qq.com/13/path2)-设置-高级设置-仓库设置-删除项目
删除项目组,管理后台(https://git.weixin.qq.com/)-项目组-XXXXX-设置-删除。内置的项目组无法删除
(4)版本管理,
开发工具-上传
微信公众平台-登录微信小程序-管理-版本管理-开发版本/发布
五、微信小程序-目录结构
来源,开发-指南-目录结构
来源,https://developers.weixin.qq.com/miniprogram/dev/framework/structure.html
1、一个小程序主体部分由三个文件组成,必须放在项目的根目录
(1)app.js,小程序全局逻辑
(2)app.json,小程序全局配置
(3)app.wxss,小程序全局样式表
2、一个小程序页面由四个文件组成
(1).js,页面逻辑
(2).wxml,页面结构
(3).json,页面配置
(4).wxss,页面样式表
六、微信小程序-框架
来源,开发-指南-小程序框架
来源,https://developers.weixin.qq.com/miniprogram/dev/framework/MINA.html
1、框架
(1)介绍
框架,在视图层-与-逻辑层间提供了数据传输-和-事件系统,让开发者能够专注于数据与逻辑
页面管理,框架管理页面路由,做到无缝切换,给以生命周期,开发者将页面的数据、方法、生命周期函数注册到框架中即可
数据绑定,让数据与视图保持同步
基础组件
丰富的API
(2)场景值,描述用户进入小程序的路径
(3)逻辑层,介绍、注册小程序、注册页面、页面生命周期、页面路由(页面栈表现、触发时机)、模块化、API(事件监听、同步、异步、异步返回Promise、云开发)
(4)视图层,介绍、WXML、WXSS样式、WXS脚本语言、事件系统(介绍、WXS响应事件)、简易双向绑定、基础组件、获取界面上的节点信息、响应显示区域变化、分栏模式、动画、初始渲染缓存
2、事件系统,
(1)什么是事件
事件是视图层到逻辑层的通讯方式
事件可以将用户的行为反馈到逻辑层进行处理
事件可以绑定在组件上,当达到触发事件,就会执行逻辑层中对应的事件处理函数
事件对象可以携带额外信息,如id,dataset,touches
(2)事件的使用方式,在组件中绑定一个事件处理函数
示例一:
<view id="tapTest" data-hi="Weixin" bindtap="tapName"> Click me! </view>
Page({
tapName: function(event) {
console.log(event)
}
})
示例二:
<wxs module="wxs" src="./test.wxs"></wxs>
<view id="tapTest" data-hi="Weixin" bindtap="{{wxs.tapName}}"> 绑定的WXS函数必须用{{}}括起来 </view>
以下test.wxs
function tapName(event, ownerInstance) {
console.log('tap Weixin', JSON.stringify(event))
}
module.exports = {
tapName: tapName
}
(3)事件捕获与冒泡,先捕获后冒泡
冒泡从里向外
bind,绑定事件,向上冒泡,后可以紧跟一个冒号,其含义不变,如bind:tap
catch,绑定事件,阻止向上冒泡
mut-bind,绑定事件,向上冒泡,与上级mut-bind“互斥”的,上级mut-bind不会被触发,上级的其它绑定依然会触发
捕获从外向里
capture-bind,捕获
capture-catch,中断捕获、取消冒泡
以下,冒泡与阻止冒泡,点击inner view会先后调用handleTap3和handleTap2
<view id="outer" bindtap="handleTap1">
outer view
<view id="middle" catchtap="handleTap2">
middle view
<view id="inner" bindtap="handleTap3">
inner view
</view>
</view>
</view>
以下,互斥事件,点击inner view会先后调用handleTap3和handleTap2,点击middle view会调用handleTap2和handleTap1
<view id="outer" mut-bind:tap="handleTap1">
outer view
<view id="middle" bindtap="handleTap2">
middle view
<view id="inner" mut-bind:tap="handleTap3">
inner view
</view>
</view>
</view>
以下,事件的捕获阶段,点击inner view会先后调用handleTap2、handleTap4、handleTap3、handleTap1
<view id="outer" bind:touchstart="handleTap1" capture-bind:touchstart="handleTap2">
outer view
<view id="inner" bind:touchstart="handleTap3" capture-bind:touchstart="handleTap4">
inner view
</view>
</view>
如果将上面代码中的第一个capture-bind改为capture-catch,将只触发handleTap2
(4)事件对象,如无特殊说明,当组件触发事件时,逻辑层绑定该事件的处理函数会收到一个事件对象。
BaseEvent 基础事件对象属性列表
type,代表事件的类型
timeStamp,页面打开到触发事件所经过的毫秒数
target,触发事件的源组件
id,事件源组件的id
dataset,事件源组件上由data-开头的自定义属性组成的集合
currentTarget,事件绑定的当前组件
id,当前组件的id
dataset,当前组件上由data-开头的自定义属性组成的集合
mark,识别具体触发事件的target节点,承载一些自定义数据,类似于dataset
<view mark:myMark="last" bindtap="bindViewTap">
<button mark:anotherMark="leaf" bindtap="bindButtonTap">按钮</button>
</view>
在上述WXML中,如果按钮被点击,将触发bindViewTap和bindButtonTap两个事件,事件携带的event.mark将包含myMark和anotherMark两项
Page({
bindViewTap: function(e) {
e.mark.myMark === "last" // true
e.mark.anotherMark === "leaf" // true
}
})
mark,会包含从触发事件的节点到根节点上所有的mark属性值
dataset,仅包含一个节点的data-属性值。
CustomEvent 自定义事件对象属性列表(继承 BaseEvent)
detail,额外的信息
TouchEvent 触摸事件对象属性列表(继承 BaseEvent)
touches,触摸事件,当前停留在屏幕中的触摸点信息的数组,每个元素为一个Touch对象,表示当前停留在屏幕上的触摸点
identifier,触摸点的标识符
pageX/pageY,距离文档左上角的距离,文档的左上角为原点 ,横向为X轴,纵向为Y轴
clientX/clientY,距离页面可显示区域(屏幕除去导航条)左上角距离,横向为X轴,纵向为Y轴
changedTouches,触摸事件,当前变化的触摸点信息的数组
(5)WXS响应事件
问题,有频繁用户交互的效果,产生了频繁的视图层与逻辑层交互,使小程序卡顿
解决,用WXS函数响应小程序事件,目前只能响应内置组件的事件,不支持自定义组件事件
以下,代码示例
<wxs module="test" src="./test.wxs"></wxs>
<view change:prop="{{test.propObserver}}" prop="{{propValue}}" bindtouchmove="{{test.touchmove}}" class="movable"></view>
change:prop,prop属性被设置的时候触发WXS函数
//test.wxs
module.exports = {
touchmove: function(event, instance) {},
propObserver: function(newValue, oldValue, ownerInstance, instance) {}
//以下参数的解释可能有问题
//event.instance、ownerInstance,表示触发事件的组件的 ComponentDescriptor 实例
//instance,表示触发事件的组件所在的组件的 ComponentDescriptor 实例,一般是页面实例
}
以下,ComponentDescriptor的属性示例,共有13个
方法,selectComponent;参数,selector对象;描述,返回组件的ComponentDescriptor实例
方法,selectAllComponents;参数,selector对象数组;描述,返回组件的ComponentDescriptor实例数组
3、简易双向绑定,自定义组件将自身的myValue属性双向绑定到了组件内输入框的value属性上
custom-component定义,<input model:value="{{myValue}}" />
custom-component使用,<custom-component model:my-value="{{pageValue}}" />
4、基础组件
5、获取界面上的节点信息
(1)WXML节点信息
const query = wx.createSelectorQuery()
query.select('#the-id').boundingClientRect(function(res){
res.top // #the-id 节点的上边界坐标(相对于显示区域)
})
query.selectViewport().scrollOffset(function(res){
res.scrollTop // 显示区域的竖直滚动位置
})
query.exec()
(2)WXML节点布局相交状态
以下,目标节点(用选择器 .target-class 指定)每次进入或离开页面显示区域时,触发回调函数
Page({
onLoad: function(){
wx.createIntersectionObserver().relativeToViewport().observe('.target-class', (res) => {
res.id // 目标节点 id
res.dataset // 目标节点 dataset
res.intersectionRatio // 相交区域占目标节点的布局区域的比例
res.intersectionRect // 相交区域
res.intersectionRect.left // 相交区域的左边界坐标
res.intersectionRect.top // 相交区域的上边界坐标
res.intersectionRect.width // 相交区域的宽度
res.intersectionRect.height // 相交区域的高度
})
}
})
以下,目标节点(用选择器.target-class指定)与参照节点(用选择器.relative-class指定)在页面显示区域内相交或相离,且相交或相离程度达到目标节点布局区域的20%和50%时,触发回调函数
Page({
onLoad: function(){
wx.createIntersectionObserver(this, {
thresholds: [0.2, 0.5]
}).relativeTo('.relative-class').relativeToViewport().observe('.target-class', (res) => {
res.intersectionRatio // 相交区域占目标节点的布局区域的比例
res.intersectionRect // 相交区域
res.intersectionRect.left // 相交区域的左边界坐标
res.intersectionRect.top // 相交区域的上边界坐标
res.intersectionRect.width // 相交区域的宽度
res.intersectionRect.height // 相交区域的高度
})
}
})
6、响应显示区域变化
(1)one.json,手机单页面启用屏幕旋转支持
{
"pageOrientation": "auto",
}
(2)app.json,iPad上启用屏幕旋转支持、 Windows/Mac 上启用大屏模式
{
"resizable": true,
}
(3)Media Query媒体查询
.my-class {
width: 40px;
}
@media (min-width: 480px) {
/* 仅在 480px 或更宽的屏幕上生效的样式规则 */
.my-class {
width: 200px;
}
}
(4)屏幕旋转事件
Page({
onResize(res) {
res.size.windowWidth // 新的显示区域宽度
res.size.windowHeight // 新的显示区域高度
}
})
Component({
pageLifetimes: {
resize(res) {
res.size.windowWidth // 新的显示区域宽度
res.size.windowHeight // 新的显示区域高度
}
}
})
7、分栏模式,启用分栏模式,在app.json中同时添加代码
{
"resizable": true,
"frameset": true
}
8、动画
(1)用法,this.animate(selector, keyframes, duration, callback)
(2)示例
this.animate(
'#container',
[
{ opacity: 1.0, rotate: 0, backgroundColor: '#FF0000' },
{ opacity: 0.5, rotate: 45, backgroundColor: '#00FF00'},
{ opacity: 0.0, rotate: 90, backgroundColor: '#FF0000' },
],
5000,
function(){
this.clearAnimation('#container', { opacity: true, rotate: true }, function () {
console.log("清除了#container上的opacity和rotate属性")
})
}.bind(this)
)
9、初始渲染缓存
(1)初始渲染
逻辑层初始化:载入必需的小程序代码、初始化页面this对象、将相关数据发送给视图层。
视图层初始化:载入必需的小程序代码,然后等待逻辑层初始化完毕,接收逻辑层发送的数据,渲染页面
在启动页面时,尤其是小程序冷启动、进入第一个页面时,逻辑层初始化的时间较长
(2)初始渲染缓存
在小程序页面第一次被打开后,将页面初始数据渲染结果记录下来,写入一个持久化的缓存区域
在这个页面被第二次打开时,检查缓存中是否还存有这个页面上一次初始数据的渲染结果,如果有,就直接将渲染结果展示出来
如果展示了缓存中的渲染结果,这个页面暂时还不能响应用户事件,等到逻辑层初始化完毕后才能响应用户事件
复杂组件不能被展示或不能响应交互
(3)初始渲染缓存的作用
快速展示出页面中永远不会变的部分,如导航栏
预先展示一个骨架页,提升用户体验
展示自定义的加载提示
提前展示广告
(4)静态初始渲染缓存示例
以下,one.json,单页初始渲染缓存
{
"initialRenderingCache": "static"
}
以下,app.json,所有页初始渲染缓存
{
"window": {
"initialRenderingCache": "static"
}
}
以下,静态页面
<view wx:if="{{loading}}">正在加载</view>
以下,正确的做法
Page({
data: {
loading: true
}
})
以下,错误的做法
Page({
data: {},
onLoad: function() {
this.setData({
loading: true
})
}
})
(5)静态初始渲染缓存,慎用