文章目录
- 小程序项目想下载项目的可以下载看看~
- 弹性盒布局和移动端适配的一些知识
- 微信小程序的适配方案
- 什么是响应式和自适应
-
- 小程序配置
-
- 小程序框架接口
-
- 数据绑定
- 小程序获取,查询,元素/组件的信息
- vue和小程序都是单向数据流
-
- App.json当中配置路由,路由跳转及需要注意的点
-
- 小程序使用分包
-
- 微信小程序生命周期
- 小程序使用npm包步骤
-
- 小程序的ajax请求发送和二次封装
-
- 小程序使用自定义组件
-
- tabBar底部导航的实现
- js文件使用this.data.xxx和this.xxx和this.setData的区别
-
- 元素当中id和data-xxx在辨别数据的使用
- 文本超出显示省略号或文本超出两行后显示省略号
-
- 小程序的轻提示(Toast)
- 小程序当中视频的操作
-
- 小程序的video组件设置为100%出现黑色边框
- scroll-view滚动到指定位置并且有过渡效果
-
- 用户转发分享和自定义转发分享内容
-
- 微信小程序背景音频的播放
-
- 微信小程序的授权登录和获取openid
-
- 小程序更新数据状态的区别
- 小程序的数据存储
-
- 小程序scroll-view的下拉刷新
-
- 为什么小程序没有引入css样式都会生效?
- @import 和import 引入css区别和小程序引入css?
-
- 微信小程序动态类(class)
- 使用事件委托,children层存在嵌套时无法获取标识符id,非嵌套时可以如期获取,嵌套获取不到标识符id的解决办法
-
- 其他知识点
- js操作的键值为变量的时候,需要使用中括号
- 判断类型的时候,老师说最好是全等来判断
- 位移运算符转化为数字
- object的toString和Array的toString和其他的toString
-
- 设计模式之单例模式,工厂模式简说
-
- 如果遍历的是数组里面的对象,那么修改遍历时候的遍历项,会影响原数组
- 数组的includes,indexOf,find,findIndex ,concat ,slice,splice
-
- calc计算高度时候的一个坑,因为符号问题
- display:flex和float不能同时使用!!!因为display:flex开启后是内容撑开容器,float就无法浮动了,因为没有位置了
- 开启定位后子元素相当于父元素水平垂直居中
- 设置旋转的中心点
- 小程序生命周期的onLoad当中的参数options,用于路由传参
- 在组件 wxss 中不应使用 ID 选择器、属性选择器和标签名选择器。
小程序项目想下载项目的可以下载看看~
下载地址
- github地址: https://github.com/superBiuBiuMan/Wechat_NetEase_CloudMusic
- gitee地址: https://gitee.com/superBiuBiu/Wechat_NetEase_CloudMusic
弹性盒布局和移动端适配的一些知识
微信小程序的适配方案
- 小程序适配单位:
rpx - 小程序规定任何屏幕小的宽度为
750rpx
- 所以小程序会根据屏幕的宽度不同自动计算rpx值的大小
- 在
iPhone6 情况下(其实也就是DPR为2的情况下) 1px = 2rpx - 举个例子,比如设计师在iPhone6尺寸(375*667)下设计UI图,那么有一张图片的大小为
100px * 50px ,那么在微信小程序当中对应的rpx是多少呢? 相当于计算下方关系,当然,不需要我们自己去计算,微信小程序会自动转换 - 计算得结果等于200,所以是
200rpx
什么是响应式和自适应
- 一句话:响应式对不同页面做出代码变更,自适应不做代码变更
响应式
-
像素达到临界点就使用另外一套样式,通过媒体查询的方式,也就是据屏幕的大小自动的调整页面的展现方式 -
响应式的概念应该是覆盖了自适应,但是包括的东西更多了
自适应
- 百分比布局,宽度使用百分比,文字使用em,rem等
- 自适应是为了解决如何才能在不同大小的设备上呈现相同的网页
小程序配置
全局配置
- @官网全局配置API地址
- 小程序根目录下的
app.json 文件用来对微信小程序进行全局配置。文件内容为一个 JSON 对象,有以下属性: - 比如说配置页面底部的导航
页面配置
- @官网页面配置API
app.json 当中的部分配置项和对单个页面的配置项(二者通用)- 在全局配置当中需要有
window 字段,而单个页面的配置项.json文件当中是不需要添加window 字段的
全局配置(app.json ),需要添加window 字段
为某一个页面配置(比如说video页面video.json ),就不需要添加window 字段了
sitemap
小程序框架接口
App.js
-
@官网API接口 -
App.js 只有一个,所以执行App({...}) 在里面,而在每一个页面执行的是Page({...}) 函数 -
可以配置一些生命周期,并且可以用来全局数据globalData -
App.js当中内容代码如下
App({
onLaunch (options) {
},
onShow (options) {
},
onHide () {
},
onError (msg) {
console.log(msg)
},
globalData: 'I am global data'
})
- 如果需要获取App.js所生成的实例化对象,其他js文件需要调用
getApp() 函数,经常用来读取设置的全局数据
var appInstance = getApp()
console.log(appInstance.globalData)
xxx.js(不同页面不同的xxx.js)
-
@官网API -
每一个页面js当中执行的是Page({...}) 函数,App.js 只有一个,执行App({...}) 在里面 -
不管有多少页码,每一个页面的xxx.js 基本内容差不多都是这样子
Page({
data: {
},
onLoad(options) {
},
onReady() {
},
onShow() {
},
onHide() {
},
onUnload() {
},
onPullDownRefresh() {
},
onReachBottom() {
},
onShareAppMessage() {
}
})
WXML语法
- @官方API
- 和vue不同的是,vue一般对于变量是要在属性前添加
v-bind 或者简写为: 的,微信小程序则需要通过{{ js代码 }} 来操作变量
数据绑定
<view> {{message}} </view>
Page({
data: {
message: 'Hello MINA!'
}
})
列表渲染
-
@官方API -
微信小程序如果需要操作的话,也需要绑定key 值
- 绑定key的时候,不需要书写
{{ }} 符号,直接传入key值,比如循环的每一项为item,里面有一个字段为id,那么传入wx:key 的时候只需要wx:key="id" ,而不是wx:key="{{id}}" -
wx:for 语句有别与vue,在vue当中需要手动指明循环的变量,而微信小程序如果在不指定的情况下,循环项目自动命名为item ,当前循环的索引自动命名为index ,也可以使用如下代码去重命名,不过,一般都是二三层循环的时候才去,一般都是默认情况 使用 wx:for-item 可以指定数组当前元素的变量名,
使用 wx:for-index 可以指定数组当前下标的变量名:
<view wx:for="{{array}}" wx:key="index"> {{item}} </view>
Page({
data: {
array: [1, 2, 3, 4, 5]
}
})
条件渲染
<view wx:if="{{length > 5}}"> 1 </view>
<view wx:elif="{{length > 2}}"> 2 </view>
<view wx:else> 3 </view>
block wx:if
- 可以用于包括要判断的元素,并且不做渲染,这样子就不用在外面套一层
view 再去判断是否渲染了 <block/> 并不是一个组件,它仅仅是一个包装元素,不会在页面中做任何渲染,只接受控制属性。
WXS文件
事件系统/事件
- @官方API-介绍
- @官方API - 事件分类
- 在学事件之前,我们先回顾理解下标准事件流和非标准事件流
- 除上官网公布的事件之外的其他组件自定义事件如无特殊声明都是非冒泡事件,如 form 的submit事件,input 的input事件,scroll-view 的scroll事件,(详见各个组件)
标准事件流
- 捕获: 从最外层祖先开始捕获
- 执行: 从最内层开始执行事件(执行相同的事件)
- 冒泡:从最内层开始执行,从内向外执行的过程交冒泡
非标准事件流
事件格式及示例
bind + 事件名 (不会阻止事件冒泡)catch + 事件名 (会阻止事件冒泡)- 常见的事件
tap ,就相当于PC端的click
示例
- 当用户点击该组件的时候会在该页面对应的 Page 中找到相应的事件处理函数。
<view id="tapTest" data-hi="Weixin" bindtap="tapName"> 单击我 </view>
- 在相应的 Page 定义中写上相应的事件处理函数,参数是event。
Page({
tapName: function(event) {
console.log(event)
}
})
打印输出event
{
"type":"tap",
"timeStamp":895,
"target": {
"id": "tapTest",
"dataset": {
"hi":"Weixin"
}
},
"currentTarget": {
"id": "tapTest",
"dataset": {
"hi":"Weixin"
}
},
"detail": {
"x":53,
"y":14
},
"touches":[{
"identifier":0,
"pageX":53,
"pageY":14,
"clientX":53,
"clientY":14
}],
"changedTouches":[{
"identifier":0,
"pageX":53,
"pageY":14,
"clientX":53,
"clientY":14
}]
}
数据绑定
- 小程序没有自带的双向绑定,但是可以通过this.setData来实现修改数据后视图也变化
- this.data.key = value修改data当中的数据,注意和vue进行区分**,vue为什么不用加一层data,那是因为vue做了数据劫持**
- 这样子是不会引起视图的改变的! 小程序没有自带的双向绑定
- 如果想要触发视图中数据的更新,那么就需要借助
setData 这个方法用了,setData 的机制去把视图层和逻辑层做一个“中转站”两边连接起来。 注意这里的回调函数必须要用箭头函数,否者this指向不正确
如图
小程序获取,查询,元素/组件的信息
-
@官方API - boundingClientRect -
@官方API -createSelectorQuery -
其功能类似于 DOM 的 getBoundingClientRect -
获取元素节点信息语法格式 NodesRef.boundingClientRect(function callback)
function callback
回调函数,在执行 SelectorQuery.exec 方法后,节点信息会在 callback 中返回。
返回对象
属性 类型 说明
id string 节点的 ID
dataset Object 节点的 dataset
left number 节点的左边界坐标
right number 节点的右边界坐标
top number 节点的上边界坐标
bottom number 节点的下边界坐标
width number 节点的宽度(包括padding,包括border)
height number 节点的高度(包括padding,包括border)
-
wx.createSelectorQuery()
- 返回一个 SelectorQuery 对象实例。在自定义组件或包含自定义组件的页面中,应使用
this.createSelectorQuery() 来代替。 -
获取元素节点信息和查询节点案例
wxml
<view class="list-container">查看我多大吧</view>
wxss
.list-container{
width: 300rpx;
height: 300rpx;
padding: 10rpx;
background-color: red;
border: 6rpx solid blue;
}
js
onLoad(options) {
wx.createSelectorQuery().select(".list-container").boundingClientRect((res)=>{
console.log(res);
}).exec();
},
输出结果
bottom: 166,
dataset:{},
height: 166,
id: "",
left: 0,
right: 166,
top: 0,
width: 166
vue和小程序都是单向数据流
什么是单向数据流
- 在父传子的前提下,父组件的数据发生会通知子组件自动更新
- 子组件内部,不能直接修改父组件传递过来的props => props是只读的
vue
小程序
- 单向数据流
- 同步修改 model数据结构 和 视图 同时修改,通过
this.setData
App.json当中配置路由,路由跳转及需要注意的点
注意点
- App.json当中的配置项
pages 在配置路由页面的时候不可以再加上 /
错误示范如下
{
"pages": [
"/pages/index/index",
"/pages/logs/logs",
"/pages/test/test"
],
"sitemapLocation": "sitemap.json"
}
正确示范如下
{
"pages": [
"pages/index/index",
"pages/logs/logs",
"pages/test/test"
],
"sitemapLocation": "sitemap.json"
}
wx.redirectTo({
url:"/pages/logs/logs"
})
- 不同的路由跳转会有所区别,有的是不允许跳转到 tabbar 页面 的
小程序使用分包
具体步骤和示例
打包之前的app.json的pages配置项
"pages": [
"pages/index/index",
"pages/songDetail/songDetail",
"pages/video/video",
"pages/search/search",
"pages/recommendSong/recommendSong",
"pages/personal/personal",
"pages/login/login"
],
(1).为分包建立文件夹
这里就建立一个名字叫做mysubpackages文件夹
(2).将除了启动页(tabBar)的页面文件夹放置在刚刚创建的文件夹当中
(3).app.json配置项修改
app.json当中的pages配置项只留下主包
"pages": [
"pages/index/index",
"pages/video/video",
"pages/personal/personal"
],
将分包写入app.json当中的subpackages配置项
subpackages 中,每个分包的配置有以下几项:
字段 类型 说明
root String 分包根目录
name String 分包别名,分包预下载时可以使用
pages StringArray 分包页面路径,相对于分包根目录
完整路径为root+pages
independent Boolean 分包是否是独立分包
app.json当中subpackages配置项
"subpackages": [
{
"root": "mysubpackages",
"name": "所有分包",
"pages": [
"login/login",
"recommendSong/recommendSong",
"search/search",
"songDetail/songDetail"
]
}
],
(4).更改跳转路径
toSearchPage(){
wx.navigateTo({
url:"/mysubpackages/login/login"
});
},
(5).目录结构
微信小程序生命周期
- @官方API
- 复习下vue的生命周期
- beforeCreated,created,beforeMount,mounted,beforeUpdate,updated,beforeDestroy,destroyed
- 小程序生命周期
- onLoad
- onShow(会重复出现)
- onReady
- onHide 路由重定向的时候如果选择了比如说
wx.navigatorTo 之类的保活的,就会产生隐藏生命周期 - onUnload 相当于是销毁的生命周期
- 具体的图片
小程序使用npm包步骤
(1).初始化
npm init -y
(2).勾选使用npm(有的项目需要勾选,现在没有这个选项了)
(3).下载需要的包
npm install moment --save
(4).引入包
import moment from "moment"
(5).引入包后构建npm
- 开发工具 —> 工具 —> 构建npm
- 会将node_modules中的包打包到miniprogram_npm中
如果步骤错了,会报这个错误module 'pages/boundingClientRect/moment.js' is not defined, require args is 'moment''
只需要再次构建下npm就可以
- 注意
- 测试的时候引入
axios 包,然后按照这个流程发现不行,可能是小程序不支持axios 吧
小程序的ajax请求发送和二次封装
- @官方API
- 需要注意的是
- 在h5的时候,全局对象为window,但是在微信小程序是没有window的,取而代之的全局对象是wx
- 所以是用
wx.xxxx 来调用微信提供的方法 - wx.request规定必须是https协议 ,最大并发为10
ajax请求发送
发送请求示例代码
wx.request({
url: 'example.php',
data: {
x: '',
y: ''
},
header: {
'content-type': 'application/json'
},
success (res) {
console.log(res.data)
}
})
二次封装
import config from "./config";
export default (url, data = {}, method = "get") => {
return new Promise((resolve, reject) => {
wx.request({
url: config.host + url,
method,
data,
header: {
},
success(res) {
},
fail(reason) {
reject(reason);
}
});
});
}
小程序使用自定义组件
(1)建立文件夹
- 1.先依次建立一个文件夹叫
componets/NavHeader
(2)右键新建Component
- 2.1.在
NavHeader 文件夹右键,选中新建Component ,输入与文件夹同名的名称 - 2.2.
NavHeader 文件夹当中的结构和普通的页面结构一样,唯一不同的是不会自动在app.json当中的配置项pages中注册,因为不是页面嘛
(3)构建NavHeader的结构
-
3.1 书写NavHeader结构并使用NavHeader.js 当中的propertyies属性
- 可以看到,选择
新建Component 生成的js文件会自动添加如下内容,我们使用的多的,就是 properties
Component({
properties: {
title:{
type:String,
value:"未传入时候的默认值-title"
},
desc:{
type:String,
value:"未传入时候的默认值-desc"
}
},
data: {
},
methods: {
}
})
-
3.2 在NavHeader.wxml 中使用NavHeader.js 的properties属性
<view>
<text>标题:{{title}}</text>
<text>描述:{{desc}}</text>
</view>
(4)其他组件注册使用自定义组件
- 在要使用此组件的页面的xxx.json里面的配置项目
usingCompoents 去注册,比如下方的index.json当中注册使用组件NavHeader
index.json当中使用自定义组件NavHeader(相当于注册组件吧有点)
{
"usingComponents": {
"NavHeader":"/components/NavHeader/NavHeader"
}
}
(5使用并传值
index.wxml中使用(也就是xxx.wxml中使用)
<NavHeader title="我是标题" desc="我是描述"></NavHeader>
tabBar底部导航的实现
-
@官方API- tabBar -
tabBar 配置项用的比较多的
color : tab 上的文字默认颜色,仅支持十六进制颜色selectedColor :tab 上的文字选中时的颜色,仅支持十六进制颜色backgroundColor :tab 的背景色,仅支持十六进制颜色list : tab 的列表,详见 list 属性说明,最少 2 个、最多 5 个 tab -
注意的是
- 假如有需求需要计算一个容器的高度比如通过
calc 计算,那么在计算高度的时候,比如减去一部分的高度,这个时候可以忽略tabBar的高度,而不用减去,因为微信小程序会自动处理
示例 app.json 当中添加配置项
{
"tabBar": {
"color": "#333",
"selectedColor": "#d43c33",
"backgroundColor": "#fff",
"list":
[
{
"pagePath": "pages/index/index",
"text": "首页",
"iconPath": "/static/images/tabs/tab-home.png",
"selectedIconPath": "/static/images/tabs/tab-home-current.png"
},
{
"pagePath": "pages/video/video",
"text": "视频",
"iconPath": "/static/images/tabs/video.png",
"selectedIconPath": "/static/images/tabs/video-current.png"
},
{
"pagePath": "pages/personal/personal",
"text": "个人中心",
"iconPath": "/static/images/tabs/tab-my.png",
"selectedIconPath": "/static/images/tabs/tab-my-current.png"
}
]
},
}
效果图
js文件使用this.data.xxx和this.xxx和this.setData的区别
Page({
data: {
},
})
this.data.xxx
this.data.xxx 操作当前配置项当中data的属性值,但是操作不会更新视图
this.xxx
- 操作当前由Page生成的实例化对象上的属性
- 可以用来绑定一个共用,唯一的值,比如说单例
this.setData
this.setData({ value: 'leaf' }) 可以更新配置项当中data的属性值,并且会引发视图更新
this.setData({
要修改的data当中的key值: 新值
})
元素当中id和data-xxx在辨别数据的使用
- 有时候我们想一个函数被二个不同功能的组件所触发,但是所处理的功能不同,就可以为组件添加id或者是data-xxx来区分
id 区分和data-xxx 区分主要区别就是id 只能有一个,而data-xxx 可以有多个- 在回调函数的事件对象当中通过
currentTarget 或者target 来获取id或者data-xxx属性
- currentTarget和target的区别主要在于是否是事件委派,如果没有事件委派,那么二者没有区别
- 如果有事件委派/委托,区别如下
currentTarget 是绑定事件的元素(父元素)target : 是触发事件的元素(子元素)
有如下结构
可以看到,test.wxml 的二个view组件都赋予了同一个回调函数,那么如何区分呢?这里添加了id 和data-index
<text>\n</text>
<text>\n</text>
<text>\n</text>
<view bindtap="handleTouch" id="one" data-index="0">我是组件1</view>
<text>\n</text>
<view bindtap="handleTouch" id="two" data-index="1">我是组件2</view>
test.js内容
handleTouch(event){
console.log(event);
},
我们任意单击一个view ,输出查看下事件对象event ,可以看到,currentTarget和target记录着一些数据,正好是我们所设置的数据,那么就可以以此来区分是哪一个组件触发的了
完整js代码如下
handleTouch(event) {
let id = event.target.id;
if (id === 'one') {
console.log("组件1触发的");
}
if (id === 'two') {
console.log("组件2触发的");
}
},
当然,也可以通过data-xxx 来区分
handleTouch(event) {
let index = event.target.dataset.index;
if (index === '0') {
console.log("组件1触发的");
}
if (index === '1') {
console.log("组件2触发的");
}
},
文本超出显示省略号或文本超出两行后显示省略号
文本超出显示省略号
white-space:nowrap;
text-overflow:ellipsis;
over-flow:hidden
示例
<!DOCTYPE html>
<html lang="en">
<head>
<title>Document</title>
<style>
.show{
width: 200px;
height: 50px;
border: 1px solid red;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
</style>
</head>
<body>
<div class="show">
本人也是经过了深思熟虑,在每个日日夜夜思考这个问题。 这种事实对本人来说意义重大,相信对这个世界也是有一定意义的。 那么, 既然如此, 问题的关键究竟为何? 就我个人来说,对我的意义,不能不说非常重大
</div>
</body>
</html>
文本超出两行后显示省略号
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
示例
<!DOCTYPE html>
<html lang="en">
<head>
<title>Document</title>
<style>
.show {
width: 200px;
border: 1px solid red;
text-overflow: ellipsis;
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
</style>
</head>
<body>
<p class="show">
本人也是经过了深思熟虑,在每个日日夜夜思考这个问题。 这种事实对本人来说意义重大,相信对这个世界也是有一定意义的。 那么, 既然如此, 问题的关键究竟为何? 就我个人来说,对我的意义,不能不说非常重大
</p>
</body>
</html>
小程序的轻提示(Toast)
- @官方API - Toast
- 和其他不同的就是小程序的轻提示除了
none ,其他都只限于7个汉字的长度 - 并且小程序的轻提示-
wx.showLoading 必须要调用wx.hideToast 进行手动关闭才可以,其他的就不用
小程序当中视频的操作
-
@官方API - video -
想要操作视频,比如说跳转到指定位置,倍速播放的时候,就需要对视频(video)进行操作了 -
大致操作就是先创建属于这个视频的上下文对象,然后对上下文对象进行操作
- 设计到视频的操作有很多,可以查看官方API的说明~
bindtimeupdate : 播放进度变化时触发bindfullscreenchange : 视频进入和退出全屏时触发 -
下方步骤为操作一个video的基本步骤
(0)wxml基本结构
<view>
<video id="1" src="http://vfx.mtime.cn/Video/2019/03/18/mp4/190318214226685784.mp4"></video>
<button bindtap="handlePlay">播放/暂停视频1</button>
</view>
(1)创建视频上下文对象
onLoad(options) {
this.videoContext = wx.createVideoContext("1");
},
(2)控制视频的播放/暂停/跳转
handlePlay() {
this.videoContext.play();
this.videoContext.seek(40);
},
补充:由于微信问题,视频播放下一个的时候不会自动暂停上一个视频,所以这里需要使用视频上下文进行处理
<!--pages/videotest/videotest.wxml-->
<text>视频测试</text>
<view>
<!-- 视频1 -->
<!-- video添加id,用于获取上下文对象-->
<video id="1" src="http://vfx.mtime.cn/Video/2019/03/18/mp4/190318214226685784.mp4" bindplay="handlePlay"></video>
</view>
<view>
<!-- 视频2 -->
<!-- video添加id,用于获取上下文对象-->
<video id="2" src="http://vfx.mtime.cn/Video/2019/03/19/mp4/190319104618910544.mp4" bindplay="handlePlay"></video>
</view>
js 代码
handlePlay(event) {
let videoId = event.target.id;
if(this.videoContext){
this.videoContext.pause();
}
this.videoContext = wx.createVideoContext(videoId);
},
小程序的video组件设置为100%出现黑色边框
如图
scroll-view滚动到指定位置并且有过渡效果
- @官网API -scroll-view
- 实现滚动到指定位置的二个属性
scroll-into-view : 值应为某子元素id(id不能以数字开头)。设置哪个方向可滚动,则在哪个方向滚动到该元素scroll-with-animation : 在设置滚动条位置时使用动画过渡 - 实现原理
- 通过动态设置
scroll-into-view 的值来实现
效果
代码实现
wxml
<scroll-view class="nav-tab" enable-flex scroll-x scroll-into-view="{{'scroll'+selectedId}}" scroll-with-animation>
<view class="nav" wx:for="{{navList}}" wx:key="id" id="{{'scroll'+item.id}}">
<view class="nav-item {{selectedId==item.id?'active':''}}" bindtap="changNavTab" id="{{item.id}}">
{{item.name}}
</view>
</view>
</scroll-view>
js
Page({
data: {
navList: [{
"id": 58100,
"name": "现场",
"url": null,
"relatedVideoType": null,
"selectTab": false,
"abExtInfo": null
},
{
"id": 60100,
"name": "翻唱",
"url": null,
"relatedVideoType": null,
"selectTab": false,
"abExtInfo": null
},
{
"id": 1101,
"name": "舞蹈",
"url": "",
"relatedVideoType": null,
"selectTab": false,
"abExtInfo": null
},
{
"id": 58101,
"name": "听BGM",
"url": null,
"relatedVideoType": null,
"selectTab": false,
"abExtInfo": null
},
{
"id": 262158,
"name": "万有引力",
"url": null,
"relatedVideoType": null,
"selectTab": false,
"abExtInfo": null
},
{
"id": 261121,
"name": "告白",
"url": null,
"relatedVideoType": null,
"selectTab": false,
"abExtInfo": null
},
{
"id": 259132,
"name": "云村放映厅",
"url": null,
"relatedVideoType": null,
"selectTab": false,
"abExtInfo": null
},
{
"id": 259129,
"name": "超燃联盟",
"url": null,
"relatedVideoType": null,
"selectTab": false,
"abExtInfo": null
},
{
"id": 264120,
"name": "热歌看得见",
"url": null,
"relatedVideoType": null,
"selectTab": false,
"abExtInfo": null
},
{
"id": 243125,
"name": "#歌手#",
"url": "",
"relatedVideoType": null,
"selectTab": false,
"abExtInfo": null
},
{
"id": 243123,
"name": "致敬英雄",
"url": "",
"relatedVideoType": null,
"selectTab": false,
"abExtInfo": null
},
{
"id": 254120,
"name": "滚石唱片行",
"url": "",
"relatedVideoType": null,
"selectTab": false,
"abExtInfo": null
},
{
"id": 249121,
"name": "宫崎骏",
"url": "",
"relatedVideoType": null,
"selectTab": false,
"abExtInfo": null
},
],
selectedId:'58100'
},
changNavTab(event){
this.setData({
selectedId:event.target.id
})
},
})
wxss
.nav-tab{
display: flex;
white-space: nowrap;
margin-top: 15rpx;
height: 60rpx;
}
.nav-tab .nav {
padding: 0 10rpx;
margin: 0 10rpx;
height: 60rpx;
}
.nav-tab .nav .nav-item{
padding-bottom: 7rpx;
font-size: 28rpx;
}
.nav-tab .nav .nav-item.active{
border-bottom: 1rpx solid #ee5d44;
}
用户转发分享和自定义转发分享内容
如果想要区分是当前页面调用了button触发的分享还是右上角转发菜单触发的分享,只需要在onShareAppMessage 回调当中结构出from ,通过from判断
onShareAppMessage({from}) {
if (from === 'menu') {
} else {
}
}
button实现
- 通过设置button的
open-type 属性为share,即成为了一个分享功能的按钮,其实这样子已经可以使用了,但是我们可以自定义分享的图片和标题~ - 想要自定义,就需要在对应页面的js文件当中添加
onShareAppMessage 回调 - @官网API-onShareAppMessage
wxml
<button open-type="share">单击我分享</button>
js
Page({
onShareAppMessage() {
return {
title:"我来分享啦~~~我是自定义内容",
imageUrl:"https://dreamlove.top/img/favicon.png"
}
}
})
自定义内容后的效果
微信小程序背景音频的播放
(1)获取全局唯一的背景音频管理对象,返回BackgroundAudioManager 实例
onLoad(options) {
this.musicManager = wx.getBackgroundAudioManager();
},
(2)操作BackgroundAudioManager 实例
- @官方API - BackgroundAudioManager 实例
- 比较常用的实例属性
startTime : 音频开始播放的位置(单位:s)。src :音频的数据源(2.2.3 开始支持云文件ID)。默认为空字符串,当设置了新的 src 时,会自动开始播放,目前支持的格式有 m4a, aac, mp3, wav。title 😗*(必填)**音频标题,用于原生音频播放器音频标题(必填)。原生音频播放器中的分享功能,分享出去的卡片标题,也将使用该值。duration :当前音频的长度(单位:s),只有在有合法 src 时返回。(只读)currentTime :当前音频的播放位置(单位:s),只有在有合法 src 时返回。(只读) - 比较常用的方法
play() : 播放音乐pause() : 暂停音乐seek(跳转到的位置) : 跳转到指定位置(单位为s)onTimeUpdate(callback) :监听背景音频播放进度更新事件,只有小程序在前台时会回调。onEnded() :监听背景音频自然播放结束事件
onLoad(options) {
this.musicManager = wx.getBackgroundAudioManager();
this.musicManager.src = " http://downsc.chinaz.net/Files/DownLoad/sound1/201906/11582.mp3";
this.musicManager.title = "测试音频";
},
微信小程序的授权登录和获取openid
wx.getUserProfile( 2022 年 10 月 25 日 24 时后 会失效)
(1).button按钮绑定回调
<button bindtap="handleUserInfo">单击我申请授权</button>
(2).自定义一个函数,这里取名 handleUserInfo 并且添加wx.getUserProfile方法 需要注意的是,事件名都是小写
handleUserInfo() {
wx.getUserProfile({
desc: '请求授权',
success: (res) => {
console.log("用户同意授权");
console.log(res);
},
fail: (reason) => {
console.log("用户拒绝授权");
console.log(reason);
}
})
},
(3).处理授权结果
{errMsg: "getUserProfile:fail getUserProfile:fail auth deny"}
获取openid
(1).wx.login获取登录凭证
handleOpenid() {
wx.login({
success: (res) => {
let code = res.code;
}
})
},
(2).有了登录凭证,再通过auth.code2Session获取openid
- 这里我没有自己后台服务器,就直接这样子用了,其实可以通过后台来调用,这样子就不会泄露自己的开发信息了
//获取openid
handleOpenid() {
wx.login({
success: (res) => {
//获取登录凭证
let code = res.code;
if (code) {
//凭证存在,接着请求换取openid
wx.request({
url: 'https://api.weixin.qq.com/sns/jscode2session',
data: {
//登陆换取的凭证
js_code: code,
//小程序的appid
appid: "",
//小程序的APPSecret
secret: "",
//授权类型,固定值authorization_code
grant_type: "authorization_code",
},
success: (res) => {
console.log("获取openid成功");
console.log(res);
},
fail: (error) => {
console.log("获取openid失败", error);
}
})
}
}
})
},
(3).处理结果
小程序更新数据状态的区别
for(循环请求数据){
let 请求的数据 = 发送请求;
this.setData({
topList:resultArray
});
}
- 统一更新
- 优点: 减少更新的次数,只更新1次
- 缺点: 网络较差的时候用户等待时间过长,可能会看到白屏
let 全部数据 ;
for(循环请求数据){
let 请求的数据 = 发送请求;
将请求的数据添加到全部数据
}
this.setData({
topList:全部数据
});
小程序的数据存储
-
@官方API - 操作数据存储 -
不同小程序的数据存储相互独立,互不干扰. @官方解释网站 -
同一个微信用户,同一个小程序 storage 上限为 10MB。storage 以用户维度隔离,同一台设备上,A 用户无法读取到 B 用户的数据;不同小程序之间也无法互相读写数据。 -
插件隔离策略
- 同一小程序使用不同插件:不同插件之间,插件与小程序之间 storage 不互通。
- 不同小程序使用同一插件:同一插件 storage 不互通。
-
与h5的storage不同的是,h5的需要转化为json格式的字符串后存储,而小程序可以直接存储对象,也可以存储json格式字符串
wx.setStorageSync(存数据)
- 将数据存储在本地缓存中指定的 key 中。会覆盖掉原来该 key 对应的内容。除非用户主动删除或因存储空间原因被系统清理,否则数据都一直可用。单个 key 允许存储的最大数据长度为 1MB,所有数据存储上限为 10MB。
示例 - 直接存储对象
let data = {
"objects": [{
"ID": "1",
"JobTitle": "Software Engineer",
"EmailAddress": "Gil_West3007@elnee.tech",
"FirstNameLastName": "Gil West"
},
{
"ID": "2",
"JobTitle": "Associate Professor",
"EmailAddress": "Leslie_Little2936@naiker.biz",
"FirstNameLastName": "Leslie Little"
},
]
}
wx.setStorageSync('userInfo', data.objects);
调试器信息 - Storage项
wx.getStorageSync(取数据)
let data1 = wx.getStorageSync('userInfo');
console.log("获取的数据",data1);
调试器输出信息
小程序scroll-view的下拉刷新
- @官方API-scroll-view组件
- scroll-view需要开启和设置的属性
refresher-triggered : 设置当前下拉刷新状态,true 表示下拉刷新已经被触发,false 表示下拉刷新未被触发(会显示相应的效果)refresher-enabled : 是否开启自定义下拉刷新,true开启,false关闭(默认) - scroll-view需要添加的回调或监听
bindrefresherrefresh :自定义下拉刷新被触发 - scroll-view需要实现上拉加载更多只需要添加
bindscrolltolower 滚动到底部/右边时触发就可以 - 顺带一提
- 使用
calc 计算scroll-view的高度的时候,calc的计算符号和数字之间必须要分开来才可以,否者计算会无效!并且如果小程序有tabBar(底部导航),可以不用减去底部导航的高度(小程序会自动处理)
示例
效果图
wxml
<scroll-view class="item-container"
scroll-y refresher-enabled
refresher-triggered="{{isRefresh}}"
bindrefresherrefresh="handlePullRefresh"
>
<view class="item">我是内容</view>
<view class="item">我是内容</view>
<view class="item">我是内容</view>
<view class="item">我是内容</view>
<view class="item">我是内容</view>
</scroll-view>
js
Page({
data: {
isRefresh:false,
},
handlePullRefresh(){
setTimeout(()=>{
this.setData({
isRefresh:false
});
wx.showToast({
title: '刷新成功!',
});
},1000);
},
})
wxss
.item-container{
border: 1rpx solid red;
height: 400rpx;
}
为什么小程序没有引入css样式都会生效?
-
在html开发的时候,我们会通过@import或者link标签来引入css文件,但是微信小程序却不用我们去引入,那是为什么? -
我们在app.json 的配置项pages 已经书写了一条,比如下面代码,我们知道,所有页面都需要写在pages 当中 {
"pages": [
"pages/index/index",
],
"sitemapLocation": "sitemap.json"
}
-
在我们进入页面,比如说index 页面的时候,小程序回去寻找pages/index 文件夹,然后依次访问,寻找与访问页面同名的文件,index.wxml, index.wxss ,index.js ,index.json
- 所以你可以试试看,把
index.wxss 改为index11.wxss ,然后再去访问index 页面,就会丢失样式
@import 和import 引入css区别和小程序引入css?
- 一句话@import引入css样式在html文件,或者css文件当中,
- 而import是用来引入模块的,当然,如果有webpack工具也是可以通过import引入css文件的
@import的用法
<link rel="stylesheet" type="text/css" href="css文件路径"/>
<style type="text/css">
@import url(css文件路径);
</style>
@import url(css文件路径);
@import区别
- link是XHTML标签,除了加载CSS外,还可以定义RRS等其他事务。@import属于CSS范畴,只能加载CSS
- link引用CSS样式,是和页面加载同步进行加载,@import是等页面加载完后才开始加载。
- link是XHTML标签,无兼容问题;@import是在CSS2.1提出的,低版本的浏览器不支持。
- link支持使用Javascript控制DOM去改变样式;而@import不支持。
小程序引入css
示例
@import "common.wxss";
.middle-p {
padding:15px;
}
微信小程序动态类(class)
<div :class="{ colorRed: isShow }">哈哈哈</div>
data() {
return {
isShow: true
}
}
.colorRed {
color:red;
}
.colorBlue{
color:blue;
}
<!--pages/classdongtai/classdongtai.wxml-->
<text class="item {{activeIndex === 0?'active':''}}">动感超人</text>
data: {
activeIndex:0
},
.item.active{
color: red;
}
使用事件委托,children层存在嵌套时无法获取标识符id,非嵌套时可以如期获取,嵌套获取不到标识符id的解决办法
- 有人提出这个问题,然后我自己也遇到了这个问题,就来记录下
- 先说嵌套时候的解决办法
- mark属性简介
- 在基础库版本 2.7.1 以上,可以使用
mark 来识别具体触发事件的 target 节点。此外, mark 还可以用于承载一些自定义数据(类似于 dataset )。 - 当事件触发时,事件冒泡路径上所有的
mark 会被合并,并返回给事件回调函数。(即使事件不是冒泡事件,也会 mark 。)
委派子元素层存在嵌套获取不到标识符id的情况的演示
- 本来想通过事件委派,然后通过
event.target.id 来获取是哪一个元素所触发的,这样子方便寻找数据,但是实际情况却是,当单击到了子元素嵌套的元素的时候,就获取不到id 了
演示动画,可以看到,单击子元素内部的索引号或者邮箱地址的时候,出现获取不到id 的情况
委派子元素层存在嵌套获取id的办法-通过mark属性
实现代码
wxml
<view class="list-container" bindtap="handleClick" >
<view class="item" wx:for="{{userInfo}}" wx:key="id" mark:index="{{index}}">
<text>索引号{{index}}</text>
<text>邮箱地址:{{item.EmailAddress}}</text>
</view>
</view>
js
handleClick(event){
console.log(event.mark.index);
},
wxss
.list-container{
border: 1rpx solid red;
}
.list-container .item{
margin-bottom: 10rpx;
border: 1px solid blue;
}
演示效果
其他知识点
js操作的键值为变量的时候,需要使用中括号
Page({
data: {
priority: {
'vip': 1111,
'user': 1,
},
},
onLoad(options) {
let type = 'vip';
this.setData({
[type]:9999
});
console.log(this.data.priority);
},
})
判断类型的时候,老师说最好是全等来判断
位移运算符转化为数字
- 这里使用无符号右移运算符转换为数字~
- 如果转换过程中有字母,则返回0
var a = '12';
let afterA = a >>> 0;
console.log(afterA);
console.log(typeof afterA);
var b = 'a12a';
let afterB = b >>> 0;
console.log(afterB);
console.log(typeof afterB);
var c = '12a';
let afterC = c >>> 0;
console.log(afterC);
console.log(typeof afterC);
- 补充
! 一个感叹号表示取反!! 二个感叹号转化为布尔值!!! 三个感叹号表示转化为布尔值并取反
object的toString和Array的toString和其他的toString
object的toSting(经常用来判断数据类型)
- 如果不是object,而是function,需要通过
Object.prototype.toSting.call() 来调用 - 通过toString()返回类型后可以通过
slice(8,-1) 来获取具体类型,代码功能为从索引为8的开始取,直到倒数第二个
let userInfo = {
"ID": "1",
"JobTitle": "Software Engineer",
"EmailAddress": "Gil_West3007@elnee.tech",
"FirstNameLastName": "Gil West"
};
console.log(userInfo.toString());
console.log("数据类型取值", userInfo.toString().slice(8, -1));
let userList = ['用户1', '用户2'];
console.log(Object.prototype.toString.call(userList));
console.log("数据类型取值", Object.prototype.toString.call(userList).slice(8, -1));
array当中的toString
- array当中的toString会返回以逗号分割的字符串
let userList = ['用户1','用户2','用户3'];
console.log(userList.toString());
console.log(typeof userList.toString());
用户1,用户2,用户3
string
函数的toString
- function的toString()方法将返回函数的文本定义
function sayName(){
console.log("我叫将军大人");
}
console.log(sayName.toString());
console.log(typeof sayName.toString());
这个应该是字符串
function sayName(){
console.log("我叫将军大人");
}
string
Date的toString
- Date的toString()方法将返回一个具有可读性的日期时间字符串
var time = new Date();
console.log(time.toString());
console.log(typeof time.toString());
Object的toStirng和Array的toString.html:33 Thu Jul 14 2022 09:57:49 GMT+0800 (中国标准时间)
string
正则的toString
- RegExp的toString()方法与function的toString()方法类似,将返回正则表达式的文本定义
var reg = new RegExp(/\w{5,10}/);
console.log(reg.toString());
console.log(typeof reg.toString());
/\w{5,10}/
string
设计模式之单例模式,工厂模式简说
单例模式
- 创建多个对象的情况下,使用一个变量来保存,始终只有一个对象
- 当创建新的对象的时候就会把之前的对象覆盖掉
- 可以节省内存空间
工厂模式
如果遍历的是数组里面的对象,那么修改遍历时候的遍历项,会影响原数组
const array1 = [{
name: '李白1',
age: 18
}, {
name: '李白2',
age: 19
}, {
name: '李白3',
age: 20
}, {
name: '李白4',
age: 21
}];
const found = array1.find(element => element.age > 20);
found.age = 1888;
console.log(found);
console.log(JSON.stringify(array1));
数组的includes,indexOf,find,findIndex ,concat ,slice,splice
includes
includes() 方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true ,否则返回 false 。
const array1 = [1, 2, 3];
console.log(array1.includes(2));
const pets = ['cat', 'dog', 'bat'];
console.log(pets.includes('cat'));
console.log(pets.includes('at'));
indexOf
indexOf() 方法返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1。
const beasts = ['ant', 'bison', 'camel', 'duck', 'bison'];
console.log(beasts.indexOf('bison'));
console.log(beasts.indexOf('bison', 2));
console.log(beasts.indexOf('giraffe'));
find
- 方法返回数组中满足提供的测试函数的第一个元素的值,找到后就会停止查找,否则返回
undefined 。
const array1 = [5, 12, 8, 130, 44];
let index = 0
const found = array1.find(element => {
index++;
return element > 10
});
console.log("查找的次数", index);
console.log('搜索结果', found);
findIndex
findIndex() 方法返回数组中满足提供的测试函数的第一个元素的索引,找到后就会停止查找,若没有找到对应元素则返回-1。
const array1 = [5, 12, 8, 130, 44];
let index = 0
const found = array1.findIndex(element => {
index++;
return element > 10
});
console.log("查找的次数", index);
console.log('搜索结果', found);
concat
concat() 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。
const array1 = ['a', 'b', 'c'];
const array2 = ['d', 'e', 'f'];
const array3 = array1.concat(array2);
console.log(array3);
slice
slice() 方法返回一个新的数组对象,这一对象是一个由 begin 和 end 决定的原数组的浅拷贝(包括 begin ,不包括end )相当于左闭右开。原始数组不会被改变。
const animals = ['ant', 'bison', 'camel', 'duck', 'elephant'];
console.log(animals.slice(1,3));
console.log(animals.slice(2));
console.log(animals.slice(2, 4));
console.log(animals.slice(1, 5));
console.log(animals.slice(-2));
console.log(animals.slice(2, -1));
console.log(animals.slice());
splice
splice() 方法通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组。
const months = ['Jan', 'March', 'April', 'June'];
months.splice(1, 0, 'Feb');
console.log(months);
months.splice(4, 1, 'May');
console.log(months);
calc计算高度时候的一个坑,因为符号问题
.video-scroll{
height:calc(100vh-83rpx-60rpx-15rpx)
}
.video-scroll{
height:calc(100vh - 83rpx - 60rpx - 15rpx)
}
display:flex和float不能同时使用!!!因为display:flex开启后是内容撑开容器,float就无法浮动了,因为没有位置了
开启定位后子元素相当于父元素水平垂直居中
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.father{
width: 300px;
height: 300px;
background-color: red;
position: relative;
}
.son{
width: 100px;
height: 100px;
background-color: blue;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
}
</style>
</head>
<body>
<div class="father">
<div class="son">
</div>
</div>
</body>
</html>
设置旋转的中心点
-
参考的文章1 https://developer.mozilla.org/en-US/docs/Web/CSS/transform-origin -
参考的文章2 http://www.360doc.com/content/20/0710/23/25947829_923464975.shtml -
在没有设置旋转中心的时候,我们设置transform:rotate(45deg); 都是围绕元素中点来旋转的,如下图
- 旋转动画
transform-origin:0 0 并设置transform:rotate(45deg) 效果
小程序生命周期的onLoad当中的参数options,用于路由传参
- 原生小程序url有长度限制,如果传参内容过长会自动截取掉
- options.id可以传入的id参数
在组件 wxss 中不应使用 ID 选择器、属性选择器和标签名选择器。
如题~
|