小程序项目想下载项目的可以下载看看~

下载地址

弹性盒布局和移动端适配的一些知识

微信小程序的适配方案

  • 小程序适配单位: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当中内容代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
App({
onLaunch (options) {
// Do something initial when launch.
},
onShow (options) {
// Do something when show.
},
onHide () {
// Do something when hide.
},
onError (msg) {
console.log(msg)
},
//存储全局数据
globalData: 'I am global data'
})
  • 如果需要获取App.js所生成的实例化对象,其他js文件需要调用getApp()函数,经常用来读取设置的全局数据
1
2
3
// other.js
var appInstance = getApp()
console.log(appInstance.globalData) // I am global data

xxx.js(不同页面不同的xxx.js)

  • @官网API

  • 每一个页面js当中执行的是Page({...})函数,App.js 只有一个,执行App({...})在里面

  • 不管有多少页码,每一个页面的xxx.js基本内容差不多都是这样子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// pages/test/test.js
Page({

/**
* 页面的初始数据
*/
data: {

},

/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {

},

/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {

},

/**
* 生命周期函数--监听页面显示
*/
onShow() {

},

/**
* 生命周期函数--监听页面隐藏
*/
onHide() {

},

/**
* 生命周期函数--监听页面卸载
*/
onUnload() {

},

/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {

},

/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {

},

/**
* 用户点击右上角分享
*/
onShareAppMessage() {

}
})

WXML语法

  • @官方API
  • 和vue不同的是,vue一般对于变量是要在属性前添加v-bind 或者简写为:的,微信小程序则需要通过{{ js代码 }}来操作变量
数据绑定
1
2
<!--wxml-->
<view> {{message}} </view>
  • 提供数据的一方
    1
    2
    3
    4
    5
    6
    // page.js
    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,也可以使用如下代码去重命名,不过,一般都是二三层循环的时候才去,一般都是默认情况

    1
    2
    3
    使用 wx:for-item 可以指定数组当前元素的变量名,

    使用 wx:for-index 可以指定数组当前下标的变量名:
1
2
<!--wxml-->
<view wx:for="{{array}}" wx:key="index"> {{item}} </view>
1
2
3
4
5
6
// page.js
Page({
data: {
array: [1, 2, 3, 4, 5]
}
})
条件渲染
  • @官方API

  • 在框架中,使用 wx:if="" 来判断是否需要渲染该代码块:

1
2
3
<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事件,(详见各个组件)
标准事件流
  • 捕获: 从最外层祖先开始捕获
  • 执行: 从最内层开始执行事件(执行相同的事件)
  • 冒泡:从最内层开始执行,从内向外执行的过程交冒泡
非标准事件流
  • IE浏览器专属
  • 特点:没有捕获阶段
事件格式及示例
  • bind + 事件名 (不会阻止事件冒泡)
  • catch + 事件名 (会阻止事件冒泡)
  • 常见的事件tap,就相当于PC端的click

示例

  • 当用户点击该组件的时候会在该页面对应的 Page 中找到相应的事件处理函数。
1
2
3
4
5
<!--
id以及h5的自定义属性data-xxx
是为了在同一件事件回调函数当中可以区分是哪一个组件调用了
-->
<view id="tapTest" data-hi="Weixin" bindtap="tapName"> 单击我 </view>
  • 在相应的 Page 定义中写上相应的事件处理函数,参数是event。
1
2
3
4
5
Page({
tapName: function(event) {
console.log(event)
}
})

打印输出event

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
{
"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

  • 获取元素节点信息语法格式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    NodesRef.boundingClientRect(function callback)

    //返回对象的信息均为px格式

    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

1
2
<!--pages/boundingClientRect/boundingClientRect.wxml-->
<view class="list-container">查看我多大吧</view>

wxss

1
2
3
4
5
6
7
8
/* pages/boundingClientRect/boundingClientRect.wxss */
.list-container{
width: 300rpx;
height: 300rpx;
padding: 10rpx;
background-color: red;
border: 6rpx solid blue;
}

js

1
2
3
4
5
6
7
onLoad(options) {
//select查询单个结果
// 也有selectAll查询全部结果
wx.createSelectorQuery().select(".list-container").boundingClientRect((res)=>{
console.log(res);
}).exec();
},

输出结果

1
2
3
4
5
6
7
8
bottom: 166,
dataset:{},
height: 166,
id: "",
left: 0,
right: 166,
top: 0,
width: 166

vue和小程序都是单向数据流

什么是单向数据流

  • 在父传子的前提下,父组件的数据发生会通知子组件自动更新
  • 子组件内部,不能直接修改父组件传递过来的props => props是只读的

vue

  • 单向数据流

  • 但是实现了双向数据绑定 v-model 和 input事件结合使用

  • 修改状态数据

    • this.key = value
  • 同步修改 model数据结构 和 视图同时修改

小程序

  • 单向数据流
  • 同步修改 model数据结构 和 视图 同时修改,通过this.setData

App.json当中配置路由,路由跳转及需要注意的点

注意点

  • App.json当中的配置项pages在配置路由页面的时候不可以再加上 /

错误示范如下

1
2
3
4
5
6
7
8
9
// 不要在前面添加 /
{
"pages": [
"/pages/index/index",
"/pages/logs/logs",
"/pages/test/test"
],
"sitemapLocation": "sitemap.json"
}

正确示范如下

1
2
3
4
5
6
7
8
{
"pages": [
"pages/index/index",
"pages/logs/logs",
"pages/test/test"
],
"sitemapLocation": "sitemap.json"
}
  • 在路由跳转的时候,必须要加上 / 才可以正确跳转
1
2
3
4
//关闭当前页面,跳转路由
wx.redirectTo({
url:"/pages/logs/logs"
})
  • 不同的路由跳转会有所区别,有的是不允许跳转到 tabbar 页面

小程序使用分包

  • @官方API - 全局配置(app.json)当中的subpackages配置项

  • @官方API - 使用分包和示例

  • 特点

    • 加载小程序的时候先加载主播,当需要访问分包的页面时候才加载分包内容
    • 分包的页面可以访问主包的文件,数据,图片等资源
    • 目前小程序分包大小有以下限制:
      • 整个小程序所有分包大小不超过 20M
      • 单个分包/主包大小不能超过 2M
  • 主包

    • 主包通常放置启动页(tabBar)页面
  • 分包

    • 除了主包,其他就都是分包了
  • 常规分包和独立分包

    • 常规分包

      • 开发者通过在app.jsonsubpackages字段声明项目分包结构
      • 特点
        • 加载小程序的时候先加载主包,当需要访问分包页面的时候才加载分包内容
        • 分包的页面可以访问主包的文件,数据,图片等资源
    • 独立分包

      • app.jsonsubpackages字段当中的每一个配置项中添加independent为true

      • 特点

        • 独立分包可以单独访问分包的内容,不需要下载主包
        • 独立分包不能依赖主包或者其他包的内容
      • 独立分包使用场景

        • 通常某些页面和当前小程序的其他页面关联不大的时候可以进行独立分包
        • 比如: 临时加的广告页 或者是 活动页

使用常规分包的具体步骤和示例

1
2
3
4
5
6
7
8
9
10
打包之前的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).为分包建立文件夹
1
这里就建立一个名字叫做mysubpackages文件夹
(2).将除了启动页(tabBar)的页面文件夹放置在刚刚创建的文件夹当中

  • 注意,这里为了演示,其实应该将这里文件夹放置在pages文件夹下方的,也就是/mysubpackages/pages文件夹下方,和主包的目录结构保持一致的
(3).app.json配置项修改

app.json当中的pages配置项只留下主包

1
2
3
4
5
 "pages": [
"pages/index/index",
"pages/video/video",
"pages/personal/personal"
],

将分包写入app.json当中的subpackages配置项

  • subpackages 中,每个分包的配置有以下几项:
1
2
3
4
5
6
字段	类型	说明
root String 分包根目录
name String 分包别名,分包预下载时可以使用
pages StringArray 分包页面路径,相对于分包根目录
完整路径为root+pages
independent Boolean 分包是否是独立分包

app.json当中subpackages配置项

1
2
3
4
5
6
7
8
9
10
11
12
"subpackages": [
{
"root": "mysubpackages",
"name": "所有分包",
"pages": [
"login/login",
"recommendSong/recommendSong",
"search/search",
"songDetail/songDetail"
]
}
],
(4).更改跳转路径
1
2
3
4
5
6
7
//跳转到搜索界面
toSearchPage(){
wx.navigateTo({
// url: '/pages/search/search',
url:"/mysubpackages/login/login"
});
},
(5).目录结构

分包预下载

微信小程序生命周期

  • @官方API - 生命周期
  • 复习下vue的生命周期
    • beforeCreated,created,beforeMount,mounted,beforeUpdate,updated,beforeDestroy,destroyed
  • 小程序生命周期
    • onLoad
    • onShow(会重复出现)
    • onReady
    • onHide 路由重定向的时候如果选择了比如说wx.navigatorTo之类的保活的,就会产生隐藏生命周期
    • onUnload 相当于是销毁的生命周期
  • 具体的图片

小程序使用npm包步骤

(1).初始化

1
npm init -y

(2).勾选使用npm(有的项目需要勾选,现在没有这个选项了)

(3).下载需要的包

1
npm install moment --save

(4).引入包

1
2
// pages/boundingClientRect/boundingClientRect.js
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 - wx.request
  • 需要注意的是
    • 在h5的时候,全局对象为window,但是在微信小程序是没有window的,取而代之的全局对象是wx
    • 所以是用wx.xxxx来调用微信提供的方法
    • wx.request规定必须是https协议 ,最大并发为10

ajax请求发送

发送请求示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
wx.request({
url: 'example.php', //仅为示例,并非真实的接口地址
data: {
x: '',
y: ''
},
header: {
'content-type': 'application/json' // 默认值
},
success (res) {
console.log(res.data)
}
})

二次封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 二次封装请求
import config from "./config";
/**
* params:
* url:请求地址
* method:请求方式(默认get)
* data:请求数据(默认 {} )
*/
export default (url, data = {}, method = "get") => {
return new Promise((resolve, reject) => {
wx.request({
url: config.host + url,
method,
data,
header: {
//写入配置项
},
success(res) {
//Do Something
},
fail(reason) {
//返回错误原因
reject(reason);
}
});
});
}

小程序使用自定义组件

(1)建立文件夹

  • 1.先依次建立一个文件夹叫componets/NavHeader

(2)右键新建Component

  • 2.1.在NavHeader文件夹右键,选中新建Component,输入与文件夹同名的名称
  • 2.2.NavHeader文件夹当中的结构和普通的页面结构一样,唯一不同的是不会自动在app.json当中的配置项pages中注册,因为不是页面嘛

image-20220713161024645

(3)构建NavHeader的结构

  • 3.1 书写NavHeader结构并使用NavHeader.js当中的propertyies属性

    • 可以看到,选择新建Component生成的js文件会自动添加如下内容,我们使用的多的,就是 properties
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    // components/NavHeader/NavHeader.js
    Component({
    /**
    * 组件的属性列表
    */
    properties: {
    //书写自定义属性
    title:{
    //指明属性类型
    type:String,
    value:"未传入时候的默认值-title"
    },
    desc:{
    type:String,
    value:"未传入时候的默认值-desc"
    }
    },

    /**
    * 组件的初始数据
    */
    data: {

    },

    /**
    * 组件的方法列表
    */
    methods: {

    }
    })

  • 3.2 在NavHeader.wxml中使用NavHeader.jsproperties属性

    1
    2
    3
    4
    5
    <!--components/NavHeader/NavHeader.wxml-->
    <view>
    <text>标题:{{title}}</text>
    <text>描述:{{desc}}</text>
    </view>

(4)其他组件注册使用自定义组件

  • 在要使用此组件的页面的xxx.json里面的配置项目usingCompoents去注册,比如下方的index.json当中注册使用组件NavHeader

index.json当中使用自定义组件NavHeader(相当于注册组件吧有点)

1
2
3
4
5
6

{
"usingComponents": {
"NavHeader":"/components/NavHeader/NavHeader"
}
}

(5使用并传值

index.wxml中使用(也就是xxx.wxml中使用)

1
2
<!-- 使用自定义组件 -->
<NavHeader title="我是标题" desc="我是描述"></NavHeader>

tabBar底部导航的实现

  • @官方API- tabBar

  • tabBar配置项用的比较多的

    • color: tab 上的文字默认颜色,仅支持十六进制颜色
    • selectedColor:tab 上的文字选中时的颜色,仅支持十六进制颜色
    • backgroundColor:tab 的背景色,仅支持十六进制颜色
    • list: tab 的列表,详见 list 属性说明,最少 2 个、最多 5 个 tab
  • 注意的是

    • 假如有需求需要计算一个容器的高度比如通过calc计算,那么在计算高度的时候,比如减去一部分的高度,这个时候可以忽略tabBar的高度,而不用减去,因为微信小程序会自动处理

示例 app.json当中添加配置项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{
"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的区别

1
2
3
4
5
6
// pages/test/test.js
Page({
data: {

},
})

this.data.xxx

  • this.data.xxx 操作当前配置项当中data的属性值,但是操作不会更新视图

this.xxx

  • 操作当前由Page生成的实例化对象上的属性
  • 可以用来绑定一个共用,唯一的值,比如说单例

this.setData

  • this.setData({ value: 'leaf' })可以更新配置项当中data的属性值,并且会引发视图更新
1
2
3
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组件都赋予了同一个回调函数,那么如何区分呢?这里添加了iddata-index

1
2
3
4
5
6
7
<!--pages/test/test.wxml-->
<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内容

1
2
3
handleTouch(event){
console.log(event);
},

我们任意单击一个view,输出查看下事件对象event,可以看到,currentTarget和target记录着一些数据,正好是我们所设置的数据,那么就可以以此来区分是哪一个组件触发的了

完整js代码如下

1
2
3
4
5
6
7
8
9
10
handleTouch(event) {
//获取组件的id
let id = event.target.id;
if (id === 'one') {
console.log("组件1触发的");
}
if (id === 'two') {
console.log("组件2触发的");
}
},

当然,也可以通过data-xxx来区分

1
2
3
4
5
6
7
8
9
10
handleTouch(event) {
//获取组件的id
let index = event.target.dataset.index;
if (index === '0') {
console.log("组件1触发的");
}
if (index === '1') {
console.log("组件2触发的");
}
},

文本超出显示省略号或文本超出两行后显示省略号

  • 需要注意的是
    • overflow属性只会在块级元素才会生效

文本超出显示省略号

  • 关键代码就是下面这三行
1
2
3
white-space:nowrap;
text-overflow:ellipsis;
over-flow:hidden

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!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>

文本超出两行后显示省略号

  • 关键代码如下
1
2
3
4
display: -webkit-box;
-webkit-box-orient: vertical;
/* 设置超出几行省略 */
-webkit-line-clamp: 2;

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!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基本结构

1
2
3
4
5
6
<!--pages/videotest/videotest.wxml-->
<view>
<!-- 视频1 -->
<video id="1" src="http://vfx.mtime.cn/Video/2019/03/18/mp4/190318214226685784.mp4"></video>
<button bindtap="handlePlay">播放/暂停视频1</button>
</view>

(1)创建视频上下文对象

1
2
3
4
5
// pages/videotest/videotest.js 
onLoad(options) {
//创建视频上下文对象,并绑定在当前页的实例对象上
this.videoContext = wx.createVideoContext("1");
},

(2)控制视频的播放/暂停/跳转

1
2
3
4
5
6
7
8
handlePlay() {
//视频播放
this.videoContext.play();
//视频暂停
// this.videoContext.pause();
//视频跳转 - 传入的为s
this.videoContext.seek(40);
},

补充:由于微信问题,视频播放下一个的时候不会自动暂停上一个视频,所以这里需要使用视频上下文进行处理

  • 演示内容,当播放视频1的时候,又跑去播放视频2,那么视频1会被暂停播放

  • 示例wxml基本结构

    • 记得绑定bindplay
1
2
3
4
5
6
7
8
9
10
11
12
<!--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代码

1
2
3
4
5
6
7
8
9
10
11
12
handlePlay(event) {
//1.先获取下视频的id
let videoId = event.target.id;
//1.5 判断之前是否创建过了,如果创建过了,则暂停播放
if(this.videoContext){
//暂停上一个视频
this.videoContext.pause();
}
//2.创建视频的上下文对象
this.videoContext = wx.createVideoContext(videoId);

},

小程序的video组件设置为100%出现黑色边框

如图

scroll-view滚动到指定位置并且有过渡效果

  • @官网API -scroll-view
  • 实现滚动到指定位置的二个属性
    • scroll-into-view: 值应为某子元素id(id不能以数字开头)。设置哪个方向可滚动,则在哪个方向滚动到该元素
    • scroll-with-animation : 在设置滚动条位置时使用动画过渡
  • 实现原理
    • 通过动态设置scroll-into-view的值来实现

效果

代码实现

wxml

1
2
3
4
5
6
7
8
<!--pages/bar/bar.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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
// pages/bar/bar.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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* pages/bar/bar.wxss */
.nav-tab{
display: flex;
white-space: nowrap;
margin-top: 15rpx;
height: 60rpx;
/* flex-wrap: nowrap; */
}
.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判断

1
2
3
4
5
6
7
8
onShareAppMessage({from}) {
if (from === 'menu') {
//右上角转发菜单调用的分享
} else {
//为按钮调用的分享

}
}

button实现

  • 通过设置button的open-type属性为share,即成为了一个分享功能的按钮,其实这样子已经可以使用了,但是我们可以自定义分享的图片和标题~
  • 想要自定义,就需要在对应页面的js文件当中添加onShareAppMessage回调
  • @官网API-onShareAppMessage

wxml

1
2
<!--pages/share/share.wxml-->
<button open-type="share">单击我分享</button>

js

1
2
3
4
5
6
7
8
9
10
11
12
13
// pages/share/share.js
Page({
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
//如果都留空,则都采取默认值
return {
title:"我来分享啦~~~我是自定义内容",
imageUrl:"https://dreamlove.top/img/favicon.png"
}
}
})

自定义内容后的效果

默认值效果,图片为自动截取的

微信小程序背景音频的播放

(1)获取全局唯一的背景音频管理对象,返回BackgroundAudioManager 实例

  • @官方API - BackgroundAudioManager

  • 注意的是

    • 从微信客户端6.7.2版本开始,若需要在小程序切后台后继续播放音频,需要在 app.json 中配置 requiredBackgroundModes 属性。开发版和体验版上可以直接生效,正式版还需通过审核。

      1
      2
      3
      4
      5
      {
      "requiredBackgroundModes": [
      "audio"
      ],
      }
1
2
3
4
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():监听背景音频自然播放结束事件
1
2
3
4
5
6
7
8
9
onLoad(options) {
//获取全局唯一的背景音频管理对象
this.musicManager = wx.getBackgroundAudioManager();
//设置背景音频管理对象的一些属性
//当设置了src的时候,音频会自动播放
this.musicManager.src = " http://downsc.chinaz.net/Files/DownLoad/sound1/201906/11582.mp3";
//还必须要设置title属性
this.musicManager.title = "测试音频";
},

微信小程序的授权登录和获取openid

wx.getUserProfile( 2022 年 10 月 25 日 24 时后 会失效)

(1).button按钮绑定回调
1
2
<!--pages/userprofile/userprofile.wxml-->
<button bindtap="handleUserInfo">单击我申请授权</button>
(2).自定义一个函数,这里取名 handleUserInfo 并且添加wx.getUserProfile方法 需要注意的是,事件名都是小写
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// pages/userprofile/userprofile.js
handleUserInfo() {
wx.getUserProfile({
desc: '请求授权',
success: (res) => {
console.log("用户同意授权");
console.log(res);
},
fail: (reason) => {
console.log("用户拒绝授权");
console.log(reason);
}
})
},
(3).处理授权结果
  • 成功结果输出

  • 失败结果输出
1
{errMsg: "getUserProfile:fail getUserProfile:fail auth deny"}

获取openid

  • @官方API - wx.login

  • @官方API - auth.code2Session

  • wx.loginauth.code2Session结合使用

  • wx.login

    • 调用接口获取登录凭证(code)。通过凭证进而换取用户登录态信息,包括用户在当前小程序的唯一标识(openid)、微信开放平台帐号下的唯一标识(unionid,若当前小程序已绑定到微信开放平台帐号)及本次登录的会话密钥(session_key)等。用户数据的加解密通讯需要依赖会话密钥完成。
  • auth.code2Session

    • 登录凭证校验。通过 wx.login 接口获得临时登录凭证 code 后传到开发者服务器调用此接口完成登录流程。更多使用方法详见 小程序登录
  • 步骤如下

(1).wx.login获取登录凭证
1
2
3
4
5
6
7
8
handleOpenid() {
wx.login({
success: (res) => {
//获取登录凭证
let code = res.code;
}
})
},
(2).有了登录凭证,再通过auth.code2Session获取openid
  • 这里我没有自己后台服务器,就直接这样子用了,其实可以通过后台来调用,这样子就不会泄露自己的开发信息了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//获取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).处理结果
  • 授权成功输出

  • 授权失败输出这里错误的appid

小程序更新数据状态的区别

  • 实时更新(每发一次请求就更新一部分数据)
    • 优点: 用户等待时间较短
    • 缺点: 多次更新页面
1
2
3
4
5
6
7
for(循环请求数据){
let 请求的数据 = 发送请求;
//实时更新数据
this.setData({
topList:resultArray
});
}
  • 统一更新
    • 优点: 减少更新的次数,只更新1次
    • 缺点: 网络较差的时候用户等待时间过长,可能会看到白屏
1
2
3
4
5
6
7
8
9
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。

示例 - 直接存储对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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(取数据)

1
2
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(底部导航),可以不用减去底部导航的高度(小程序会自动处理)

示例

  • 模拟请求发送数据,1s后关闭下拉刷新并显示提示

效果图

wxml

1
2
3
4
5
6
7
8
9
10
11
12
13
<!--pages/refreshscroll/refreshscroll.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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// pages/refreshscroll/refreshscroll.js
Page({
/**
* 页面的初始数据
*/
data: {
isRefresh:false,//是否处于正在刷新的状态
},
handlePullRefresh(){
// console.log("用户下拉刷新了");
//当用户触发下拉刷新,data当中的'isRefresh'的值会自动被小程序更改为true
//模拟请求发送数据,1s后关闭下拉刷新并显示提示
setTimeout(()=>{
//设置当前下拉刷新状态
this.setData({
isRefresh:false
});
//显示用户提示
wx.showToast({
title: '刷新成功!',
});
},1000);
},
})

wxss

1
2
3
4
5
/* pages/refreshscroll/refreshscroll.wxss */
.item-container{
border: 1rpx solid red;
height: 400rpx;
}

为什么小程序没有引入css样式都会生效?

  • 在html开发的时候,我们会通过@import或者link标签来引入css文件,但是微信小程序却不用我们去引入,那是为什么?

  • 我们在app.json的配置项pages已经书写了一条,比如下面代码,我们知道,所有页面都需要写在pages当中

    1
    2
    3
    4
    5
    6
    {
    "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的用法

  • (1) html当中的link标签中使用
1
<link rel="stylesheet" type="text/css" href="css文件路径"/>
  • (2) html当中的style标签中使用
1
2
3
4
5
<style type="text/css">

@import url(css文件路径);

</style>
  • (3) css文件当中引入
1
@import url(css文件路径);

@import区别

  • link是XHTML标签,除了加载CSS外,还可以定义RRS等其他事务。@import属于CSS范畴,只能加载CSS
  • link引用CSS样式,是和页面加载同步进行加载,@import是等页面加载完后才开始加载。
  • link是XHTML标签,无兼容问题;@import是在CSS2.1提出的,低版本的浏览器不支持。
  • link支持使用Javascript控制DOM去改变样式;而@import不支持。

小程序引入css

  • @ 官方API - wxss
  • 使用@import语句可以导入外联样式表,@import后跟需要导入的外联样式表的相对路径,用;表示语句结束

示例

1
2
3
4
5
/** app.wxss **/
@import "common.wxss";
.middle-p {
padding:15px;
}

微信小程序动态类(class)

  • vue的时候动态类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//vue代码
<div :class="{ colorRed: isShow }">哈哈哈</div>

//js代码
data() {
return {
isShow: true
}
}

//css 类名
.colorRed {
color:red;
}
.colorBlue{
color:blue;
}
  • 小程序的动态类
1
2
3
4
5
6
7
8
9
10
11
12
<!--pages/classdongtai/classdongtai.wxml-->
<text class="item {{activeIndex === 0?'active':''}}">动感超人</text>

// pages/classdongtai/classdongtai.js
data: {
activeIndex:0
},

/* pages/classdongtai/classdongtai.wxss */
.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

1
2
3
4
5
6
7
8
<!--pages/weipaimark/weipaimark.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

1
2
3
4
5
handleClick(event){
//一旦单击到子元素内容,就会输出为空
// console.log(event.target.id);
console.log(event.mark.index);
},

wxss

1
2
3
4
5
6
7
8
9
/* pages/weipaimark/weipaimark.wxss */

.list-container{
border: 1rpx solid red;
}
.list-container .item{
margin-bottom: 10rpx;
border: 1px solid blue;
}

演示效果

其他知识点

js操作的键值为变量的时候,需要使用中括号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// pages/refreshscroll/refreshscroll.js
Page({
data: {
priority: {
// 优先级
'vip': 1111,
'user': 1,
},
},
onLoad(options) {
let type = 'vip';
this.setData({
[type]:9999
});
console.log(this.data.priority);
},

})

判断类型的时候,老师说最好是全等来判断

位移运算符转化为数字

  • 这里使用无符号右移运算符转换为数字~
  • 如果转换过程中有字母,则返回0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
var a = '12';
let afterA = a >>> 0;
console.log(afterA); //12
console.log(typeof afterA);//number

var b = 'a12a';
let afterB = b >>> 0;
console.log(afterB); //0
console.log(typeof afterB);//number

var c = '12a';
let afterC = c >>> 0;
console.log(afterC); //0
console.log(typeof afterC);//number
  • 补充
    • ! 一个感叹号表示取反
    • !! 二个感叹号转化为布尔值
    • !!! 三个感叹号表示转化为布尔值并取反

object的toString和Array的toString和其他的toString

  • toString()函数的作用是返回object的字符串表示,JavaScript中object默认的toString()方法返回字符串[object Object]。定义类时可以实现新的toString()方法,从而返回更加具有可读性的结果。

  • JavaScript对于数组对象、函数对象、正则表达式对象以及Date日期对象均定义了更加具有可读性的toString()方法:

object的toSting(经常用来判断数据类型)
  • 如果不是object,而是function,需要通过Object.prototype.toSting.call()来调用
  • 通过toString()返回类型后可以通过slice(8,-1)来获取具体类型,代码功能为从索引为8的开始取,直到倒数第二个
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//object的toString
let userInfo = {
"ID": "1",
"JobTitle": "Software Engineer",
"EmailAddress": "Gil_West3007@elnee.tech",
"FirstNameLastName": "Gil West"
};
//输出 [object Object]
console.log(userInfo.toString());

//输出 数据类型 Object
console.log("数据类型取值", userInfo.toString().slice(8, -1));

//其他的,比如说数组
let userList = ['用户1', '用户2'];

//输出 [object Array]
console.log(Object.prototype.toString.call(userList));

//输出 数据类型取值 Array
console.log("数据类型取值", Object.prototype.toString.call(userList).slice(8, -1));
array当中的toString
  • array当中的toString会返回以逗号分割的字符串
1
2
3
4
5
6
7
8
 //array的toString
let userList = ['用户1','用户2','用户3'];
console.log(userList.toString());
console.log(typeof userList.toString());

//输出结果
用户1,用户2,用户3
string
函数的toString
  • function的toString()方法将返回函数的文本定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//函数当中的toString
function sayName(){
console.log("我叫将军大人");
}
console.log(sayName.toString());
console.log(typeof sayName.toString());

//输出结果
这个应该是字符串
function sayName(){
console.log("我叫将军大人");
}

string
Date的toString
  • Date的toString()方法将返回一个具有可读性的日期时间字符串
1
2
3
4
5
6
7
8
// 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()方法类似,将返回正则表达式的文本定义
1
2
3
4
5
6
7
8
// RegExp的toString()方法
var reg = new RegExp(/\w{5,10}/);
console.log(reg.toString());
console.log(typeof reg.toString());

//输出
/\w{5,10}/
string

设计模式之单例模式,工厂模式简说

单例模式
  • 创建多个对象的情况下,使用一个变量来保存,始终只有一个对象
  • 当创建新的对象的时候就会把之前的对象覆盖掉
  • 可以节省内存空间
工厂模式
  • 根据不同的参数创建不同的对象

如果遍历的是数组里面的对象,那么修改遍历时候的遍历项,会影响原数组

  • @在线演示

  • 代码示例如下,可以看到,更改了找到的item项目后,原来的也会改变

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const array1 = [{
name: '李白1',
age: 18
}, {
name: '李白2',
age: 19
}, {
name: '李白3',
age: 20
}, {
name: '李白4',
age: 21
}];
//寻找年龄大于20的项目,返回找到的对象
const found = array1.find(element => element.age > 20);
//对寻找到的对象的年龄进行修改
found.age = 1888;
//输出查看当前寻找到的对象
console.log(found);//输出 item项目修改.html:31 {name: '李白4', age: 1888}
//将原对象转化为字符串,避免干扰
console.log(JSON.stringify(array1));//输出 [{"name":"李白1","age":18},{"name":"李白2","age":19},{"name":"李白3","age":20},{"name":"李白4","age":1888}]

数组的includes,indexOf,find,findIndex ,concat ,slice,splice

includes
  • includes() 方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回 false
1
2
3
4
5
6
7
8
9
10
11
12
13
const array1 = [1, 2, 3];

console.log(array1.includes(2));
// expected output: true

const pets = ['cat', 'dog', 'bat'];

console.log(pets.includes('cat'));
// expected output: true

console.log(pets.includes('at'));
// expected output: false

indexOf
  • indexOf()方法返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1。
1
2
3
4
5
6
7
8
9
10
11
const beasts = ['ant', 'bison', 'camel', 'duck', 'bison'];

console.log(beasts.indexOf('bison'));
// expected output: 1

// start from index 2
console.log(beasts.indexOf('bison', 2));
// expected output: 4

console.log(beasts.indexOf('giraffe'));
// expected output: -1
find
  • 方法返回数组中满足提供的测试函数的第一个元素的值,找到后就会停止查找,否则返回 undefined
1
2
3
4
5
6
7
8
9
const array1 = [5, 12, 8, 130, 44];
//统计次数
let index = 0
const found = array1.find(element => {
index++;
return element > 10
});
console.log("查找的次数", index); //输出 2
console.log('搜索结果', found); //输出 12
findIndex
  • findIndex()方法返回数组中满足提供的测试函数的第一个元素的索引,找到后就会停止查找,若没有找到对应元素则返回-1。
1
2
3
4
5
6
7
8
9
const array1 = [5, 12, 8, 130, 44];
//统计次数
let index = 0
const found = array1.findIndex(element => {
index++;
return element > 10
});
console.log("查找的次数", index); //输出 2
console.log('搜索结果', found); //输出 1
concat
  • concat() 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。
1
2
3
4
5
6
7
const array1 = ['a', 'b', 'c'];
const array2 = ['d', 'e', 'f'];
const array3 = array1.concat(array2);

console.log(array3);
// expected output: Array ["a", "b", "c", "d", "e", "f"]

slice
  • slice() 方法返回一个新的数组对象,这一对象是一个由 beginend 决定的原数组的浅拷贝(包括 begin,不包括end)相当于左闭右开。原始数组不会被改变
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const animals = ['ant', 'bison', 'camel', 'duck', 'elephant'];

console.log(animals.slice(1,3));
// expected output: Array ['bison', 'camel']

console.log(animals.slice(2));
// expected output: Array ["camel", "duck", "elephant"]

console.log(animals.slice(2, 4));
// expected output: Array ["camel", "duck"]

console.log(animals.slice(1, 5));
// expected output: Array ["bison", "camel", "duck", "elephant"]

console.log(animals.slice(-2));
// expected output: Array ["duck", "elephant"]

console.log(animals.slice(2, -1));
// expected output: Array ["camel", "duck"]

console.log(animals.slice());
// expected output: Array ["ant", "bison", "camel", "duck", "elephant"]

splice
  • splice() 方法通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组。
1
2
3
4
5
6
7
8
9
10
11
const months = ['Jan', 'March', 'April', 'June'];
months.splice(1, 0, 'Feb');
// inserts at index 1
console.log(months);
// expected output: Array ["Jan", "Feb", "March", "April", "June"]

months.splice(4, 1, 'May');
// replaces 1 element at index 4
console.log(months);
// expected output: Array ["Jan", "Feb", "March", "April", "May"]

calc计算高度时候的一个坑,因为符号问题

  • 错误的写法,运算符和操作数之间没有空格分开
1
2
3
.video-scroll{
height:calc(100vh-83rpx-60rpx-15rpx)
}
  • 正确的写法,运算符和操作数之间用空格分开
1
2
3
.video-scroll{
height:calc(100vh - 83rpx - 60rpx - 15rpx)
}

display:flex和float不能同时使用!!!因为display:flex开启后是内容撑开容器,float就无法浮动了,因为没有位置了

开启定位后子元素相当于父元素水平垂直居中

  • 原理可以看看这@文章
  • 简单来说就是盒子计算的问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<!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>

效果图

设置旋转的中心点

  • 但是有时候我们需要设置旋转中心,就需要通过transform-origin来设置了

    • 比如transform-origin:50px 50px,意思就是将中心点移动到距离容器x轴50px的位置,y轴50px的位置

  • 也可以设置为右上角顶点 transform-origin:0 0

  • 旋转动画 transform-origin:0 0并设置transform:rotate(45deg)效果

小程序生命周期的onLoad当中的参数options,用于路由传参

  • 原生小程序url有长度限制,如果传参内容过长会自动截取掉
  • options.id可以传入的id参数

在组件 wxss 中不应使用 ID 选择器、属性选择器和标签名选择器。

如题~