LOADING...

加载过慢请开启缓存(浏览器默认开启)

loading

mynote

个人笔记

关于三种布局

2024/3/5

1.流式布局

2.弹性布局

3.响应式布局

阅读全文

微信小程序组件的定义与使用

2022/12/1

1.自定义组件

1.新建组件:在根目录下新建components文件夹,在该文件夹下新建组件文件夹,右键新建Component

2.组件的引入:全局引入在app.json中,新增 “usingComponents”: {},在其中引入组件名及对应的路径,如:

 "usingComponents": {
    "timeline":"/components/timeline/timeline"
  }

如果是局部引入,则在对应的页面json中添加如上代码

3.组件的使用:在页面中将组件名作为标签使用,如:

    <timeline/>

4.组件与页面的区别:

  • 组件的json文件中的component为true
 "component": true,
  • 组件的js文件中调用的是Component()函数,页面调用的是Page()函数
组件:
Component({})
页面:
Page({})
  • 组件的事件处理函数需要定义到methods节点中,页面则是放在data同层级下
组件:
data:{},
methods: {
    getlist(){}
}
页面:
data:{},
getlist(){}

2.组件样式隔离:

默认情况下,自定义组件的样式只对当前组件生效,不会影响到组件之外的UI结构,小程序页面的样式也不会影响到组件的样式,防止样式之间的互相影响

  • app.wxss中的样式对组件无效
  • 只有class选择器会有样式隔离的效果,id选择器,属性选择器,标签选择器不受样式隔离的影响

默认情况下,自定义组件的样式隔离特性能够防止组件内外样式互相干扰的问题,如果希望外界能够控制组件内部的样式,可以通过styleIsolation修改组件的样式隔离选项。

  • 在options中修改:

    Component({
        options:{
            styleIsolation:'isolated'
        }
    })
    
  • 在组件的json文件中添加如下配置:

    {
        "styleIsolation":"isolated"
    }
    

    styleIsolation的可选值:

    可选值 默认值 描述
    isolated 是 表示启用样式隔离,在自定义组件内外,使用 class 指定的样式将不会相互影响(一般情况下的默认值);
    apply-shared 否 表示页面 wxss 样式将影响到自定义组件,但自定义组件 wxss 中指定的样式不会影响页面;
    shared 否 表示页面 wxss 样式将影响到自定义组件,自定义组件 wxss 中指定的样式也会影响页面和其他设置了 apply-shared 或 shared 的自定义组件。(这个选项在插件中不可用。)

    3.组件通信

    1.properties

    小程序中,properties是组件的对外属性,用来接收外界传递到组件中的数据

    父组件js
    data:{
         steplist: [{
          taskName: '申请人发起',
          passFlag: 1
        }, {
          taskName: '银行人员审批',
          passFlag: 1
    
        }, {
          taskName: '银行人员办结',
          passFlag: 1
    
        }]
    }
    父组件wxml
    <timeline  steplist="{{steplist}}"/>
    
    
    组件js中通过properties接收
    Component({
        peoperties:{
            //接收方式1:完整定义属性方式,当需要指定默认值时可采用
            steplist:{
                type:Array,//属性类型
                value:null//属性默认值
            },
            //接收方式2:简化定义属性方式,不需要指定属性默认值时可采用
            steplist:Array
        }
    })
    

    2.data和properties的区别

    在小程序中properties属性和data的用法相同,都是可读可写的:

    • data更倾向于存储组件的私有数据

    • properties更倾向于存储外界传递到组件中的数据

阅读全文

Js数组相关

2022/11/30

1.splice() 方法

splice() 方法用于添加或删除数组中的元素。这种方法会改变原始数组。

array.splice(index,howmany,item1,.....,itemX):

参数 描述
index 必需。规定从何处添加/删除元素。该参数是开始插入和(或)删除的数组元素的下标,必须是数字。
howmany 可选。规定应该删除多少元素。必须是数字,但可以是 “0”。如果未规定此参数,则删除从 index 开始到原数组结尾的所有元素。
item1, …, itemX 可选。要添加到数组的新元素

如果从 arrayObject 中删除了元素,则返回的是含有被删除的元素的数组。

1.从指定位置删除元素

    //从第三个位置开始删除两个元素
    let arr = [1, 2, 3, 4]
    console.log(arr.splice(2, 2))// [1, 2]
    
    let arr = [1, 2, 3, 4]
    console.log(arr.splice(2, 0))// [1, 2, 3, 4]

2.添加元素

        let arr = [1, 2, 3, 4]
        arr.splice(2, 0, 9, 8, 6)
        console.log(arr)//[1, 2, 9, 8, 6, 3, 4]

3.替换元素

        let arr = [1, 2, 3, 4]
        arr.splice(1, 2, 9, 8)
        console.log(arr)//[1, 9, 8, 4]

2.slice:截取

slice(start,end):这种方法不会改变原始数组。

start:start:开始位置的索引

end:结束位置的索引(但不包含该索引位置的元素)

        let arr = [1, 2, 3, 4]
        let arr2 = arr.slice(0, 2)
        console.log(arr, arr2)//[1, 2, 3, 4] , [1, 2]
        
        let str = 'hello world'
        let str1 = str.slice(0, -2)
        console.log(str1)//hello wor

3.join():将数组元素转为字符串

array.join(separator)

参数 描述
separator 可选。指定要使用的分隔符。如果省略该参数,则使用逗号作为分隔符。

返回一个字符串。该字符串是通过把 arrayObject 的每个元素转换为字符串,然后把这些字符串连接起来,在两个元素之间插入 separator 字符串而生成的。

        let arr = [1, 5, 6, 8, 7]
        let arr1 = arr.join()
        console.log(arr1)//1,5,6,8,7
        
        let arr = [1, 5, 6, 8, 7]
        let arr1 = arr.join('-')
        console.log(arr1)//1-5-6-8-7

4.toString(): 将数组转换为字符串

array.toString()

返回数组的所有值用逗号隔开的字符串

        let arr = [1, 5, 6, 8, 7]
        let arr1 = arr.toString()
        console.log(arr1)//1,5,6,8,7

5.sort:数组排序

array.sort(sortfunction):

sort() 方法用于对数组的元素进行排序。

1.排序顺序可以是字母或数字,并按升序或降序。

2.默认排序顺序为按字母升序。

3.使用数字排序,你必须通过一个函数作为参数来调用。函数指定数字是按照升序还是降序排列。

参数 描述
sortfunction 可选。规定排序顺序。必须是函数。
        //字母顺序
        let fruits = ['Banana', 'Orange', 'Apple', 'Mango']
        fruits.sort()
        console.log(fruits)// ['Apple', 'Banana', 'Mango', 'Orange']
        
        //字母逆序
        let fruits = ['Banana', 'Orange', 'Apple', 'Mango']
        fruits.sort()
        fruits.reverse()
        console.log(fruits)
        let arr = [1, 3, 2, 6, 4, 7]
        arr.sort()
        console.log(arr)// [1, 2, 3, 4, 6, 7]
        //数字顺序
        let arr = [1, 3, 2, 6, 4, 7]
        arr.sort((a, b) => a - b)
        console.log(arr)// [1, 2, 3, 4, 6, 7]
        //数字逆序
        let arr = [1, 3, 2, 6, 4, 7]
        arr.sort((a, b) => b - a)
        console.log(arr)//[7, 6, 4, 3, 2, 1]       

6.reverse() :颠倒数组中元素的顺序

array.reverse()

返回颠倒顺序后的数组

        let arr = [1, 5, 6, 8, 7]
        arr.reverse()
        console.log(arr)//[7, 8, 6, 5, 1]

7.concat() :于连接两个或多个数组。

array1.concat(array2,array3,…,arrayX)

参数 描述
array2, array3, …, arrayX 必需。该参数可以是具体的值,也可以是数组对象。可以是任意多个。

返回一个新的数组。该数组是通过把所有 arrayX 参数添加到 arrayObject 中生成的。如果要进行 concat() 操作的参数是数组,那么添加的是数组中的元素,而不是数组。

        let arr = [1, 5, 6, 8, 7]
        let arr1 = [2, 3, 6]
        let arr2 = [3, 5, 9]
        let arr3 = arr.concat(arr1, arr2)
        console.log(arr3)//[1, 5, 6, 8, 7, 2, 3, 6, 3, 5, 9]

8.every:用于检测数组所有元素是否都符合指定条件

array.every(function(currentValue,index,arr), thisValue)
参数 描述
function(currentValue, index,arr) 必须。函数,数组中的每个元素都会执行这个函数函数参数:
currentValue 必须。当前元素的值
index 可选。当前元素的索引值
arr 可选。当前元素属于的数组对象
thisValue 可选。对象作为该执行回调时使用,传递给函数,用作 “this” 的值。如果省略了 thisValue ,”this” 的值为 “undefined”

返回布尔值。如果所有元素都通过检测返回 true,否则返回 false。

every() 方法使用指定函数检测数组中的所有元素:

  • 如果数组中检测到有一个元素不满足,则整个表达式返回 false ,且剩余的元素不会再进行检测。
  • 如果所有元素都满足条件,则返回 true。

注意: every() 不会对空数组进行检测。

注意: every() 不会改变原始数组。

        let arr = [2, 8, 14, 9, 25, 6, 32]
        let flag = arr.every((item) => {
          return item > 10
        })
        console.log(flag)//false
        
        let arr = [2, 8, 14, 9, 25, 6, 32]
        let flag = arr.every((item) => {
          return item < 40
        })
        console.log(flag)//true

9.some:用于检测数组中的元素是否满足指定条件

array.some(function(currentValue,index,arr),thisValue)
参数 描述
function(currentValue, index,arr) 必须。函数,数组中的每个元素都会执行这个函数函数参数:
currentValue 必须。当前元素的值
index 可选。当前元素的索引值
arr 可选。当前元素属于的数组对象
thisValue 可选。对象作为该执行回调时使用,传递给函数,用作 “this” 的值。如果省略了 thisValue ,”this” 的值为 “undefined”

返回布尔值。如果数组中有元素满足条件返回 true,否则返回 false。

some() 方法会依次执行数组的每个元素:

  • 如果有一个元素满足条件,则表达式返回true , 剩余的元素不会再执行检测。
  • 如果没有满足条件的元素,则返回false。

注意: some() 不会对空数组进行检测。

注意: some() 不会改变原始数组。

        let arr = [2, 8, 14, 9, 25, 6, 32]
        let flag = arr.some((item) => {
          return item > 10
        })
        console.log(flag)//true
        
        let arr = [2, 8, 14, 9, 25, 6, 32]
        let flag = arr.some((item) => {
          return item > 40
        })
        console.log(flag)//false

10.shift:用于把数组的第一个元素从其中删除,并返回第一个元素的值。

此方法改变数组的长度!
array.shift()
        let arr = [2, 8, 14, 9, 25, 6, 32]
        arr.shift()
        console.log(arr)// [8, 14, 9, 25, 6, 32]

11.unshift() :向数组的开头添加一个或更多元素,并返回新的长度。

array.unshift(item1,item2, ..., itemX)
        let arr = [14, 9, 25, 6, 32]
        arr.unshift(2, 4, 5)
        console.log(arr)//[2, 4, 5, 14, 9, 25, 6, 32]

12.push:向数组的末尾添加一个或多个元素,并返回新的长度。

array.push(item1, item2, ..., itemX)
        let arr = [14, 9, 25, 6, 32]
        arr.push(2, 4, 5)
        console.log(arr)//[14, 9, 25, 6, 32, 2, 4, 5]

13.pop:用于删除数组的最后一个元素并返回删除的元素。

array.pop()
        let arr = [14, 9, 25, 6, 32]
        arr.pop()
        console.log(arr)//[14, 9, 25, 6]

14.map:返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。

array.map(function(currentValue,index,arr), thisValue)
参数 描述
function(currentValue, index,arr) 必须。函数,数组中的每个元素都会执行这个函数函数参数:
currentValue 必须。当前元素的值
index 可选。当前元素的索引值
arr 可选。当前元素属于的数组对象
thisValue 可选。对象作为该执行回调时使用,传递给函数,用作 “this” 的值。如果省略了 thisValue ,”this” 的值为 “undefined”
        let arr = [14, 9, 25, 6, 32]
        let arr1 = []
        arr.map((item) => {
          item = item + 1
          arr1.push(item)
        })
        console.log(arr, arr1)//[14, 9, 25, 6, 32]  [15, 10, 26, 7, 33]

15.filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。

注意: filter() 不会对空数组进行检测。

注意: filter() 不会改变原始数组。

array.filter(function(currentValue,index,arr), thisValue)
参数 描述
function(currentValue, index,arr) 必须。函数,数组中的每个元素都会执行这个函数函数参数:
currentValue 必须。当前元素的值
index 可选。当前元素的索引值
arr 可选。当前元素属于的数组对象
thisValue 可选。对象作为该执行回调时使用,传递给函数,用作 “this” 的值。如果省略了 thisValue ,”this” 的值为 “undefined”
        let arr = [14, 9, 25, 6, 32]
        let arr1 = arr.filter((item) => {
          return item > 20
        })
        console.log(arr, arr1)//[14, 9, 25, 6, 32] (2) [25, 32]

16.find:返回通过测试(函数内判断)的数组的第一个元素的值。

find() 方法为数组中的每个元素都调用一次函数执行:

  • 当数组中的元素在测试条件时返回 true 时, find() 返回符合条件的元素,之后的值不会再调用执行函数。
  • 如果没有符合条件的元素返回 undefined

注意: find() 对于空数组,函数是不会执行的。

注意: find() 并没有改变数组的原始值。

array.find(function(currentValue, index, arr),thisValue)
       //返回通过测试(函数内判断)的数组的第一个元素的值。之后的值不会再调用执行函数。
       let arr = [14, 9, 25, 6, 32]
        let arr1 = arr.find((item) => {
          return item > 20
        })
        console.log(arr, arr1)//[14, 9, 25, 6, 32] 25
        
        //没有符合条件的元素返回 undefined
        let arr = [14, 9, 25, 6, 32]
        let arr1 = arr.find((item) => {
          return item > 40
        })
        console.log(arr, arr1)//[14, 9, 25, 6, 32] undefined

17.findIndex() 方法返回传入一个测试条件(函数)符合条件的数组第一个元素位置。

findIndex() 方法为数组中的每个元素都调用一次函数执行:

  • 当数组中的元素在测试条件时返回 true 时, findIndex() 返回符合条件的元素的索引位置,之后的值不会再调用执行函数。
  • 如果没有符合条件的元素返回 -1

注意: findIndex() 对于空数组,函数是不会执行的。

注意: findIndex() 并没有改变数组的原始值。

array.findIndex(function(currentValue, index, arr), thisValue)
        //返回符合条件的数组第一个元素位置。之后的值不会再调用执行函数。
        let arr = [14, 9, 25, 6, 32]
        let index = arr.findIndex((item) => {
          return item > 20
        })
        console.log(arr, index)//[14, 9, 25, 6, 32] 2
        
        //如果没有符合条件的元素返回 -1
        let arr = [14, 9, 25, 6, 32]
        let index = arr.findIndex((item) => {
          return item > 40
        })
        console.log(arr, index)//[14, 9, 25, 6, 32] -1

18.isArray() 方法用于判断一个对象是否为数组。

如果对象是数组返回 true,否则返回 false。

Array.isArray(obj)
        let arr = [14, 9, 25, 6, 32]
        let obj = {
          name: '张三',
          sex: '男',
        }
        let num = 18
        let x = Array.isArray(arr)
        let y = Array.isArray(obj)
        let z = Array.isArray(num)
        console.log(x, y, z)//true false false

19.indexOf() 方法可返回数组中某个指定的元素位置。

该方法将从头到尾地检索数组,看它是否含有对应的元素。开始检索的位置在数组 start 处或数组的开头(没有指定 start 参数时)。如果找到一个 item,则返回 item 的第一次出现的位置。开始位置的索引为 0。

如果在数组中没找到指定元素则返回 -1。

array.indexOf(item,start)
参数 描述
item 必须。查找的元素。
start 可选的整数参数。规定在数组中开始检索的位置。它的合法取值是 0 到 stringObject.length - 1。如省略该参数,则将从字符串的首字符开始检索。

返回元素在数组中的位置,如果没有搜索到则返回 -1。

        let arr = [14, 9, 25, 6, 32]
        let arr1 = [
          {
            name: '张三',
            age: 18,
          },
          {
            name: '李四',
            age: 16,
          },
          {
            name: '王五',
            age: 21,
          },
        ]
        let index = arr.indexOf(9)
        //数组元素为对象时查找不了
        let index2 = arr1.indexOf({
          name: '李四',
          age: 16,
        })
        console.log(index, index2)//1 -1
        
        
        let arr = [14, 6, 9, 25, 6, 32]
        let index = arr.indexOf(6)
        console.log(index)//1

20.lastIndexOf() 方法可返回一个指定的元素在数组中最后出现的位置,从该字符串的后面向前查找。

如果要检索的元素没有出现,则该方法返回 -1。

该方法将从尾到头地检索数组中指定元素 item。开始检索的位置在数组的 start 处或数组的结尾(没有指定 start 参数时)。如果找到一个 item,则返回 item 从尾向前检索第一个次出现在数组的位置。数组的索引开始位置是从 0 开始的。

如果在数组中没找到指定元素则返回 -1。

array.lastIndexOf(item,start)
参数 描述
item 必需。规定需检索的字符串值。
start 可选的整数参数。规定在字符串中开始检索的位置。它的合法取值是 0 到 stringObject.length - 1。如省略该参数,则将从字符串的最后一个字符处开始检索。

如果在 stringObject 中的 fromindex 位置之前存在 searchvalue,则返回的是出现的最后一个 searchvalue 的位置。

        let arr = [14, 6, 9, 25, 6, 32]
        let lastIndex = arr.lastIndexOf(6)
        console.log(lastIndex)//4

21.includes() 方法用来判断一个数组是否包含一个指定的值,如果是返回 true,否则false。

arr.includes(searchElement)
arr.includes(searchElement, fromIndex)
参数 描述
searchElement 必须。需要查找的元素值。
fromIndex 可选。从该索引处开始查找 searchElement。如果为负值,则按升序从 array.length + fromIndex 的索引开始搜索。默认为 0。

如果找到指定值返回 true,否则返回 false。

        let arr = [14, 6, 9, 25, 6, 32]
        let flag = arr.includes(6)
        console.log(flag)//true
阅读全文

Es6

2022/11/30

1.Array.from()

就是将一个类数组对象或者可遍历对象转换成一个真正的数组,也是ES6的新增方法.

类数组对象,最基本的要求就是具有length属性的对象。

1.将类数组对象转换为真正数组(属性名为数字类型):

    let obj1 = {
      '0': 'james',
      '1': 'kobe',
      'length': 2
    }
    let arr4 = Array.from(obj1)
    console.log(arr4);//["james", "kobe"]

2.将类数组对象转换为真正数组(属性名为字符串类型):

   let obj2 = {
      'name1': 'james',
      'name2': 'kobe',
      length: 2
    }
    let arr5 = Array.from(obj2)
    console.log(arr5);//[undefined, undefined]

3.没有length属性:

    let obj3 = {
      '0': 'james',
      '1': 'kobe'
    }
    let arr6 = Array.from(obj3)
    console.log(arr6);//[]

由上可知,类数组对象中必须具有length属性,用于指定数组的长度。如果没有length属性,那么类数组转换后的数组是一个空数组;
类数组对象的属性名必须为数值型或者是字符串型的数字

4.Array.from还可以接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组。

 let arr = [1, 3, 5, 7]
    let arr2 = Array.from(arr, item => {
      return item *= 2
    })
    console.log(arr2)//[2, 6, 10, 14]

5.将Set结构的数据转换为真正的数组:

    let arr = [1, 3, 5, 7]
    let arr2 = new Set(arr)
    let arr3 = Array.from(arr2)
    console.log(arr2, arr3)//Set(4) {1, 3, 5, 7}  (4) [1, 3, 5, 7]

6.将字符串转换为数组

    let str = "happy birthday"
    let arr = Array.from(str)
    console.log(arr)//["h", "a", "p", "p", "y", " ", "b", "i", "r", "t", "h", "d", "a", "y"]

7.Array.from参数是一个真正的数组,Array.from会返回一个一模一样的新数组:

    let arr = [1, 2, 3, 4]
    let arr2 = Array.from(arr)
    console.log(arr2)//[1, 2, 3, 4]
阅读全文

Js字符串相关

2022/11/30

将字符串转为数组

**1.split:**split()方法用于将给定字符串拆分为字符串数组,该方法是使用参数中提供的指定分隔符将其分隔为子字符串。

str.split(separator, limit):

separator: 可选。字符串或正则表达式,从该参数指定的地方分割 string Object。

limit: 可选。该参数可指定返回的数组的最大长度。如果设置了该参数,返回的子串不会多于这个参数指定的数组。如果没有设置该参数,整个字符串都会被分割,不考虑它的长度。

    let str = 'happy new year'
    let arr1 = str.split('')
    let arr2 = str.split(' ')
    console.log(arr1, arr2)

输出结果:

arr1: ["h", "a", "p", "p", "y", " ", "n", "e", "w", " ", "y", "e", "a", "r"] 
arr2: ["happy", "new", "year"]

2.Array.from():

    let str = "happy birthday"
    let arr = Array.from(str)
    console.log(arr)//["h", "a", "p", "p", "y", " ", "b", "i", "r", "t", "h", "d", "a", "y"]

删除字符串前后的空格

3.trim():trim方法用来删除字符串前后的空格

字符串大小写转换方法

    let str = 'HELLO WORLD'
    let str2 = 'hello world'
    let xiaoxie = str.toLowerCase()
    let daxie = str2.toUpperCase()
    console.log(xiaoxie, daxie)//hello world HELLO WORLD

slice:截取

slice(start,end):

参数 描述
start 必须。 要抽取的片断的起始下标,第一个字符位置为 0。如果为负数,则从尾部开始截取。
end 可选。 紧接着要截取的片段结尾的下标。若未指定此参数,则要提取的子串包括 start 到原字符串结尾的字符串。如果该参数是负数,那么它规定的是从字符串的尾部开始算起的位置。slice(-2) 表示提取原数组中的倒数第二个元素到最后一个元素(包含最后一个元素)。
        let str = 'hello world'
        let str1 = str.slice(2, 8)
        console.log(str1)//llo wo
        
        //截取最后两个字符串
        let str = 'hello world'
        let str1 = str.slice(-2)
        console.log(str1)

join():将数组元素转为字符串

array.join(separator)

参数 描述
separator 可选。指定要使用的分隔符。如果省略该参数,则使用逗号作为分隔符。

返回一个字符串。该字符串是通过把 arrayObject 的每个元素转换为字符串,然后把这些字符串连接起来,在两个元素之间插入 separator 字符串而生成的。

        let arr = [1, 5, 6, 8, 7]
        let arr1 = arr.join()
        console.log(arr1)//1,5,6,8,7
        
        let arr = [1, 5, 6, 8, 7]
        let arr1 = arr.join('-')
        console.log(arr1)//1-5-6-8-7
阅读全文

Elementui日期选择器禁用日期

ElementUI 2022/11/30

picker-options:

shortcuts 设置快捷选项,需要传入 { text, onClick } 对象用法参考 demo 或下表 Object[]
disabledDate 设置禁用状态,参数为当前日期,要求返回 Boolean Function
cellClassName 设置日期的 className Function(Date)
firstDayOfWeek 周起始日 Number, 1 到 7,默认7
onPick 选中日期后会执行的回调,只有当 daterange 或 datetimerange 才生效 Function({ maxDate, minDate })

这里通过disabledDate来设置禁用时间

今天之前的时间禁用:time.getTime() < Date.now() - 8.64e7

六个月后的时间禁用: time.getTime() > new Date().getTime() + 3600 * 1000 * 24 * 180

3600秒 * 1000毫秒 * 24小时 * 180天,其他禁用天数一次类推

              <el-form-item label="承诺到款日期" prop="contractNumber">
                <el-date-picker
                  v-model="form.promisePayDate"
                  type="date"
                  :picker-options="pickerOptions"
                  placeholder="选择日期"
                  style="width: 100%"
                />
              </el-form-item>
              
              data(){
                  return{
                       pickerOptions: {
                       disabledDate: (time) => {
                       return (
                       time.getTime() > new Date().getTime() + 3600 * 1000 * 24 * 180 ||
                       time.getTime() < Date.now() - 8.64e7
                     )
                    },
                   },
                  }
              }

禁用多时间:通过接口获取可以选择的时间,其他时间一律禁用

                <el-date-picker
                  v-model="form.reserveDate"
                  type="date"
                  style="width: 100%"
                  placeholder="选择日期"
                  :picker-options="pickerOptions"
                  :disabled="isEdit"
                  @change="changeDate"
                />
                
                data(){
                    return{
                         pickerOptions:{}
                    }
                }
                
                methods:{
                     async getDateList() {
                     const res = await getReserveDateList({
                           pageNo: 1,
                           pageSize: -1,
                          })
                     //可以选择的时间范围
                     this.dateList = []
                     res.data.records.map((item) => {
                       if (
                       parseTime(item.reserveDate, '{y}-{m}-{d}') >=
                       parseTime(new Date(), '{y}-{m}-{d}')
                       ) {
                         this.dateList.push(item.reserveDate)
                        }
                       })

                       this.pickerOptions = Object.assign({}, this.pickerOptions, {
                       disabledDate: (time) => {
                       //今日前的时间不可选择
                       let disabled = time.getTime() < new Date().getTime() - 86400000
                       //在可选择的时间范围内的时间不禁用,其他时间禁用
                       let flag = true 
                       for (let i = 0; i < this.dateList.length; i++) {
                       if (
                            parseTime(time, '{y}-{m}-{d}') ==
                            parseTime(this.dateList[i], '{y}-{m}-{d}')
                          ) {
                            flag = false
                          }
                        }

                     return disabled || flag
                     },
                   })
                     this.$forceUpdate()
                   },
                }
阅读全文

TypeError: Cannot Read Properties of Null (Reading Level)

ElementUI 2022/11/8

el-cascader 反复渲染报错

解决方法

给组件设置 key(:key=”resetCascader”),在data里面设置初始值为0,当组件数据发生变化时,监听改变这个 el-cascader 的 key 值(this.resetCascader++)

       <el-form-item prop="repairTypeTwo" class="newFormItem">
              <span slot="label" class="formTitle">维修类别</span>
              <el-cascader
              :key="resetCascader"//给组件设置 key
              v-model="form.repairTypeTwo"
              style="width: 100%"
              :options="repairTypeOneList"
              filterable
              @change="handleTypeChange"
             />
         </el-form-item>
              
              
              
      data(){
          return{
             resetCascader: 0,
           }
      }
              
              
    watch: {
      //el-cascader 反复渲染报错问题
      'form.repairTypeTwo': {
        deep: true,
        handler(newVal) {
          this.resetCascader++
        },
      },
    },
阅读全文

微信小程序

2022/11/8

1. 主体文件结构

主体部分由三个文件组成,必须放在项目的根目录,如下:

文件 必填 作用
app.js 是 小程序逻辑
app.json 是 小程序公共设置( 严格的JSON语法,不能用单引号,不能写注释,否则编译报错 )
app.wxss 否 小程序公共样式表( 拓展了rpx尺寸单位,微信专属响应式像素 )

2. 页面文件结构

页面由四个文件组成,分别是:

文件类型 必填 作用
js 是 页面逻辑 ( 微信小程序没有window和document对象 )
wxml 是 页面结构 ( XML语法,不是HTML语法 )
wxss 否 页面样式表 ( 拓展了rpx尺寸单位,微信专属响应式像素 )
json 否 页面配置 ( 严格的JSON语法,不能用单引号,不能写注释,否则编译报错 )

3. 其他文件

project.config.json : 项目的配置文件

sitemap.json : 用来配置小程序及其页面是否允许被微信索引(上线的时候使用)

{
  "rules":[{
    "action": "allow",
    "page": "path/to/page"
  }, {
    "action": "disallow",
    "page": "*"
  }]
}

小程序启动过程:

1.把小程序的代码包下载到本地

2.解析app.json全局配置文件

3.执行app.js小程序入口文件,调用App()创建小程序实例

4.渲染小程序首页

5.小程序启动完成

页面渲染过程

1.加载解析页面的.json配置文件

2.加载页面的.wxml模板和.wxss样式

3.执行页面的.js文件,调用Page()创建页面实例

4.页面渲染完成

2. 微信小程序-app.json

1. app.json 配置项列表

https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html

{
  "pages": [
    "pages/index/index",
    "pages/logs/index"
  ],
  "window": {
    "navigationBarTitleText": "Demo"
  },
  "tabBar": {
    "color": "#dddddd",
    "selectedColor": "#3cc51f",
    "borderStyle": "black",
    "backgroundColor": "#ffffff",
    "list": [{
      "pagePath": "page/component/index",
      "iconPath": "image/icon_component.png",
      "selectedIconPath": "image/icon_component_HL.png",
      "text": "组件"
    }, {
      "pagePath": "page/API/index/index",
      "iconPath": "image/icon_API.png",
      "selectedIconPath": "image/icon_API_HL.png",
      "text": "接口"
    }]
  },
  "networkTimeout": {
    "request": 10000,
    "downloadFile": 10000
  },
  "debug": true,
  "navigateToMiniProgramAppIdList": [
    "wxe5f52902cf4de896"
  ]
}

//如果要新增页面可以直接在pages里面编辑

app.json文件用来对微信小程序进行全局配置,决定页面文件的路径、窗口表现、设置网络超时时间、设置多 tab 等。

属性 类型 必填 描述
pages String Array 是 设置页面路径
window Object 否 设置默认页面的窗口表现
tabBar Object 否 设置底部 tab 的表现
debug Boolean 否 设置是否开启 debug 模式
style 设置样式版本

2 .window配置

用于设置小程序的状态栏、导航条、标题、窗口背景色。

注意:页面的.json只能设置 window 相关的配置项,以决定本页面的窗口表现,所以无需写 window 这个键。

属性 类型 默认值 描述
navigationBarBackgroundColor HexColor #000000 导航栏背景颜色,如”#000000”
navigationBarTextStyle String white 导航栏标题颜色,仅支持 black/white
navigationBarTitleText String 导航栏标题文字内容
backgroundColor HexColor #ffffff 窗口的背景色,可以通过开启 “enablePullDownRefresh”:true来查看窗口颜色
backgroundTextStyle String dark 下拉刷新的背景字体、loading 图的样式,仅支持 dark/light
enablePullDownRefresh Boolean false 是否开启下拉刷新,详见页面相关事件处理函数。
onReachBottomDistance Number 50 页面上拉触底事件触发时距页面底部距离,单位为px

3. tabBar

https://mp.weixin.qq.com/debug/wxadoc/dev/framework/config.html#tabbar

如果小程序是一个多 tab 应用(客户端窗口的底部或顶部有 tab 栏可以切换页面),可以通过 tabBar 配置项指定 tab 栏的表现,以及 tab 切换时显示的对应页面。

Tip:

  1. 当设置 position 为 top 时,将不会显示 icon。
  2. tabBar 中的 list 是一个数组,只能配置最少2个、最多5个 tab,tab 按数组的顺序排序。

属性说明:

属性 类型 必填 默认值 描述
color HexColor 是 tab 上的文字默认颜色
selectedColor HexColor 是 tab 上的文字选中时的颜色
backgroundColor HexColor 是 tab 的背景色
borderStyle String 否 black tabbar上边框的颜色, 仅支持 black/white
list Array 是 tab 的列表,详见 list 属性说明,最少2个、最多5个 tab
position String 否 bottom 可选值 bottom、top,设置成top是无图标

其中 list 接受一个数组,数组中的每个项都是一个对象,其属性值如下:

属性 类型 必填 说明
pagePath String 是 页面路径,必须在 pages 中先定义
text String 是 tab 上按钮文字
iconPath String 否 图片路径,icon 大小限制为40kb,建议尺寸为 81px * 81px,当 postion 为 top 时,此参数无效
selectedIconPath String 否 选中时的图片路径,icon 大小限制为40kb,建议尺寸为 81px * 81px ,当 postion 为 top 时,此参数无效

4.页面配置–.json

在页面配置中,可以修改全局配置的部分信息,用来指定当前页面的一些特有配置

属性 类型 默认值 描述
navigationBarBackgroundColor HexColor #000000 导航栏背景颜色,如”#000000”
navigationBarTextStyle String white 导航栏标题颜色,仅支持 black/white
navigationBarTitleText String 导航栏标题文字内容
backgroundColor HexColor #ffffff 窗口的背景色,可以通过开启 “enablePullDownRefresh”:true来查看窗口颜色
backgroundTextStyle String dark 下拉刷新的背景字体、loading 图的样式,仅支持 dark/light
enablePullDownRefresh Boolean false 是否开启下拉刷新,详见页面相关事件处理函数。
onReachBottomDistance Number 50 页面上拉触底事件触发时距页面底部距离,单位为px
disableScroll Boolean false 设置为true则页面整体不能上下滚动,只在页面配置中有效,无法在app.json中设置该项

3.wxml语法

wxml html
标签名称 view,text,image,navigator div,span,img,a
属性节点 <a href="#">超链接</a> <navigator url="/apges/home/home">超链接</navigator>

4.样式

与 CSS 相比,WXSS 扩展的特性有:

  • 尺寸单位(rpx rem)

  • vw和vh是css3中的新单位,是一种视窗单位,在小程序中同样适用

  • 样式导入

  • 提供了全局的样式和局部样式(项目根目录下的app.wxss会作用于所有小程序页面,局部页面的.wxss样式仅对当前页面生效)

  • wxss仅支持部分css选择器

    1.尺寸单位

    1.rpx(responsive pixel):

    ​ 可以根据屏幕宽度进行自适应。规定屏幕宽为750rpx。如在 iPhone6 上,屏幕宽度为375px,共有750个物理像素,则750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素。

    2.vw和vh:

    ​ 是css3中的新单位,是一种视窗单位,在小程序中也同样适用。

    小程序中,窗口宽度固定为100vw,将窗口宽度平均分成100份,1份是1vw

    小程序中,窗口高度固定为100vh ,将窗口高度平均分成100份,1份是1vh

    2.样式引入

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

    /** common.wxss **/
    .small-p {
      padding:5px;
    }
    
    /** app.wxss **/
    @import "common.wxss";
    .middle-p {
      padding:15px;
    }
    
      .box2{
          border: 1px solid red;
          width: 600rpx;
          height: 600rpx;
          /*background-image可以指定网络图片,但是不可以指定本地图片*/
          background-image: url("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1586254613140&di=33474e16f9fc5342f7a1e2be72f287f6&imgtype=0&src=http%3A%2F%2Fdmimg.5054399.com%2Fallimg%2Fpkm%2Fpk%2F22.jpg");
          /* background-image: url(../../icons/timg.jpg); */
      }
    

3.选择器

特别需要注意的是小程序 不支持通配符 * ,因此以下代码无效!


  *{
    margin:0;
    padding:0;   
    box-sizing:border-box;
}

目前支持的选择器有:

选择器 样例 描述
.class .container 选择所有拥有class=”container”的组件
#id #main 选择拥有id=”main”的组件
element view 选择所有view组件
element,element view,text 选择所有文档的view组件和所有text组件
element element view text 选择view 下的所有text组件
::after view::after 在view组件后边插入内容
::before view::before 在view组件前边插入内容
nth-child(n) view:nth-child(n) 选择某个索引的标签

4.小程序中使用less

原声小程序不支持 less ,其他基于小程序的框架基本都支持,如 wepy , mpvue , taro ,uni-app等。
但是仅仅因为一个less功能,而去引用框架,肯定是不可取的。因此可以使用以下方式来实现

1. 编辑器是 vscode
2. 安装插件 easy less
3. 在vs code的设置中加如下,配置

{
    "workbench.sideBar.location": "left",
    "less.compile": {        
      "outExt":".wxss"
    }
}
  1. 在要编写样式的地方新建 less 文件,如 index.less ,然后正常编辑即可。

5.js文件分类

1.app.js:

是整个小程序项目的入口文件,通过调用App()函数来启动整个小程序

2.页面的.js文件:

是页面的入口文件,通过调用Page()函数来创建并运行页面

3.普通的.js文件:

普通的功能模块文件,用来封装公共的函数或属性供页面使用

4.小程序不是运行在浏览器中,所以没有window和document对象

1.小程序不是运行在浏览器中,所以没有window和document对象
console.log(window);
console.log(document);

2.小程序js的一些额外成员
App({})方法:用于创建应用程序实例,指定应用程序的生命周期
Page()方法:用于创建页面对象
getApp()方法:用于获取全局的应用程序对象
getCurrentPages()方法:用于获取当前页面的调用栈 
   # https://developers.weixin.qq.com/miniprogram/dev/framework/app-service/route.html
wx : 微信小程序获取API的对象

5.import 和 include(小程序支持CommonJS规范)

1.小程序支持CommonJS规范
exports.aa 或者module.exports导出
require 引入

<import src="a.wxml"/>   作用可以引入目标文件中定义的template,然后在当前的wxml中使用template。注意点不能使用引入目标文件中所引入的第三方的template,比如A import B,B import C,A可以使用B中声明的template,但是A不可以使用C中声明的template

<include src="header.wxml"/>  作用可以引入目标文件中除了template wxs之外的所有代码

wxs语法

WXS(WeiXin Script)是小程序的一套脚本语言,结合 WXML,可以构建出页面的结构。

wxml中无法调用在页面的.js中定义的函数,但是可以调用wxs中定义的函数。WXS存在的意义就是充当computed/filter这类功能。经常配合Musttache语法进行使用,但是不能作为组件的事件回调函数

wxs有自己的数据类型,wxs不支持es6语法,并且遵循CommonJS规范

WXS 与 JavaScript 是不同的语言,有自己的语法,并不和 JavaScript 一致。

WXS 代码可以编写在 wxml 文件中的 <wxs> 标签内,或以 .wxs 为后缀名的文件内。

wxs的运行环境和其他JavaScript代码是隔离的,wxs中不能调用js中定义的函数,也不能调用小程序中提供的API

在IOS设备上,小程序内的WXS会比JavaScript代码快2-20倍,但在android设备上,两者运行效率无差异

https://developers.weixin.qq.com/miniprogram/dev/reference/wxs/

<!-- 声明一个wxs模块 : 不支持es6  需要指定模块的名字 module,方便在wxml中访问模块中的成员 -->
<wxs module="dateFormat">
  //准备一个时间格式化的方法
  var format = function(date){
      //根据时间戳使用小程序提供的getDate()方法得到一个时间
      var time = getDate(date);
      var year = time.getFullYear();
      var month = time.getMonth() + 1;
      var date = time.getDate();
      var hour = time.getHours();
      var minute = time.getMinutes();
      var second = time.getSeconds();
      month = month < 10 ? "0" + month : month;
      date = date < 10 ? "0" + date : date;
      hour = hour < 10 ? "0" + hour : hour;
      minute = minute < 10 ? "0" + minute : minute;
      second = second < 10 ? "0" + second : second;
      return year + "-" + month + "-" + date + " " + hour + ":" + minute + ":" + second;
  };

  var format2 = function(msg){
    var reg = getRegExp('小', "g");
    return msg.replace(reg, "大");
  }

  var msg = "hello world";

  //将外部需要使用的信息导出
  module.exports = {
    format : format,
    format2:format2
    msg:msg
  }
</wxs>
<text>{{dateFormat.format(now)}}</text>
<text>{{dateFormat.format2("小小的")}}</text>
<text>{{dateFormat.msg}}</text>

使用外联的wxs脚本

1.必须为<wxs>标签添加module和src属性

2.module用来指定模块的名称

3.src用来指定要引入的脚本的路径,且必须是相对路径

utils/tools.wxs
var toLower = function (str) {
  return str.toLowerCase();
};
module.exports = {
  toLower: toLower,
};


    index.wxml中
    <view>
        {{m2.toLower('HH')}}
    </view>
    <wxs module="m2" src="../../utils/index.wxs" />

5.wxml语法参考

1.数据绑定

WXML 中的动态数据均来自对应 Page 的 data。

简单绑定

数据绑定使用 Mustache 语法(双大括号)将变量包起来,可以作用于:

内容

<view> {{ message }} </view>
Page({
  data: {
    message: 'Hello MINA!'
  }
})

组件属性(需要在双引号之内)

<view src="{{imgurl}}"> </view>
Page({
  data: {
    imgurl: '/assets/imgs/icon-daokuanrenling.png'
  }
})

控制属性(需要在双引号之内)

<view wx:if="{{condition}}"> </view>
Page({
  data: {
    condition: true
  }
})

关键字(需要在双引号之内)

true:boolean 类型的 true,代表真值。

false: boolean 类型的 false,代表假值。

特别注意:不要直接写 checked=”false”,其计算结果是一个字符串,转成 boolean 类型后代表真值。

<checkbox checked="{{false}}"> </checkbox>

三元运算

<view hidden="{{flag ? true : false}}"> Hidden </view>

算数运算

<view> {{a + b}} + {{c}} + d </view>
Page({
  data: {
    a: 1,
    b: 2,
    c: 3
  }
})
view中的内容为 3 + 3 + d。

逻辑判断

<view wx:if="{{length > 5}}"> </view>

字符串运算

<view>{{"hello" + name}}</view>
Page({
  data:{
    name: 'MINA'
  }
})

数据路径运算

<view>{{object.key}} {{array[0]}}</view>
Page({
  data: {
    object: {
      key: 'Hello '
    },
    array: ['MINA']
  }
})

组合

也可以在 Mustache 内直接进行组合,构成新的对象或者数组。

数组

<view wx:for="{{[zero, 1, 2, 3, 4]}}"> {{item}} </view>
Page({
  data: {
    zero: 0
  }
})

最终组合成数组[0, 1, 2, 3, 4]。

对象

<template is="objectCombine" data="{{for: a, bar: b}}"></template>
Page({
  data: {
    a: 1,
    b: 2
  }
})

最终组合成的对象是 {for: 1, bar: 2}

也可以用扩展运算符 ... 来将一个对象展开

<template is="objectCombine" data="{{...obj1, ...obj2, e: 5}}"></template>
Page({
  data: {
    obj1: {
      a: 1,
      b: 2
    },
    obj2: {
      c: 3,
      d: 4
    }
  }
})

最终组合成的对象是 {a: 1, b: 2, c: 3, d: 4, e: 5}。

如果对象的 key 和 value 相同,也可以间接地表达。

<template is="objectCombine" data="{{foo, bar}}"></template>
Page({
  data: {
    foo: 'my-foo',
    bar: 'my-bar'
  }
})

最终组合成的对象是 {foo: 'my-foo', bar:'my-bar'}。

注意:上述方式可以随意组合,但是如有存在变量名相同的情况,后边的会覆盖前面,如:

<template is="objectCombine" data="{{...obj1, ...obj2, a, c: 6}}"></template>
Page({
  data: {
    obj1: {
      a: 1,
      b: 2
    },
    obj2: {
      b: 3,
      c: 4
    },
    a: 5
  }
})

最终组合成的对象是 {a: 5, b: 3, c: 6}。

注意: 花括号和引号之间如果有空格,将最终被解析成为字符串

<view wx:for="{{[1,2,3]}} ">
  {{item}}
</view>

等同于

<view wx:for="{{[1,2,3] + ' '}}">
  {{item}}
</view>

列表渲染

wx:for

在组件上使用 wx:for 控制属性绑定一个数组,即可使用数组中各项的数据重复渲染该组件。

默认数组的当前项的下标变量名默认为 index,数组当前项的变量名默认为 item

<view wx:for="{{array}}">
  {{index}}: {{item.message}}
</view>
Page({
  data: {
    array: [{
      message: 'foo',
    }, {
      message: 'bar'
    }]
  }
})

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

使用 wx:for-index 可以指定数组当前下标的变量名:

<view wx:for="{{array}}" wx:for-index="idx" wx:for-item="itemName">
  {{idx}}: {{itemName.message}}
</view>

wx:for 也可以嵌套,下边是一个九九乘法表

<view wx:for="{{[1, 2, 3, 4, 5, 6, 7, 8, 9]}}" wx:for-item="i">
  <view wx:for="{{[1, 2, 3, 4, 5, 6, 7, 8, 9]}}" wx:for-item="j">
    <view wx:if="{{i <= j}}"> {{i}} * {{j}}="{{i" j}} < view> < code>

block wx:for

类似 block wx:if,也可以将 wx:for 用在<block/>标签上,以渲染一个包含多节点的结构块。例如:

<block wx:for="{{[1, 2, 3]}}">
  <view> {{index}}: </view>
  <view> {{item}} </view>
</block>

wx:key

如果列表中项目的位置会动态改变或者有新的项目添加到列表中,并且希望列表中的项目保持自己的特征和状态(如 input 中的输入内容,switch 的选中状态),需要使用 wx:key 来指定列表中项目的唯一的标识符。(不需要用插值语法表示,直接使用即可)

wx:key 的值以两种形式提供

  1. 字符串,代表在 for 循环的 array 中 item 的某个 property,该 property 的值需要是列表中唯一的字符串或数字,且不能动态改变。
  2. 保留关键字 *this 代表在 for 循环中的 item 本身,这种表示需要 item 本身是一个唯一的字符串或者数字。

当数据改变触发渲染层重新渲染的时候,会校正带有 key 的组件,框架会确保他们被重新排序,而不是重新创建,以确保使组件保持自身的状态,并且提高列表渲染时的效率。

如不提供 wx:key,会报一个 warning, 如果明确知道该列表是静态,或者不必关注其顺序,可以选择忽略。

示例代码

在开发者工具中预览效果

<switch wx:for="{{objectArray}}" wx:key="unique" style="display: block;"> {{item.id}} </switch>
<button bindtap="switch"> Switch </button>
<button bindtap="addToFront"> Add to the front </button>

<switch wx:for="{{numberArray}}" wx:key="*this" style="display: block;"> {{item}} </switch>
<button bindtap="addNumberToFront"> Add to the front </button>
Page({
  data: {
    objectArray: [
      {id: 5, unique: 'unique_5'},
      {id: 4, unique: 'unique_4'},
      {id: 3, unique: 'unique_3'},
      {id: 2, unique: 'unique_2'},
      {id: 1, unique: 'unique_1'},
      {id: 0, unique: 'unique_0'},
    ],
    numberArray: [1, 2, 3, 4]
  },
  switch: function(e) {
    const length = this.data.objectArray.length
    for (let i = 0; i < length; ++i) {
      const x = Math.floor(Math.random() * length)
      const y = Math.floor(Math.random() * length)
      const temp = this.data.objectArray[x]
      this.data.objectArray[x] = this.data.objectArray[y]
      this.data.objectArray[y] = temp
    }
    this.setData({
      objectArray: this.data.objectArray
    })
  },
  addToFront: function(e) {
    const length = this.data.objectArray.length
    this.data.objectArray = [{id: length, unique: 'unique_' + length}].concat(this.data.objectArray)
    this.setData({
      objectArray: this.data.objectArray
    })
  },
  addNumberToFront: function(e){
    this.data.numberArray = [ this.data.numberArray.length + 1 ].concat(this.data.numberArray)
    this.setData({
      numberArray: this.data.numberArray
    })
  }
})

注意事项

当 wx:for 的值为字符串时,会将字符串解析成字符串数组

<view wx:for="array">
  {{item}}
</view>

等同于

<view wx:for="{{['a','r','r','a','y']}}">
  {{item}}
</view>

注意: 花括号和引号之间如果有空格,将最终被解析成为字符串

<view wx:for="{{[1,2,3]}} ">
  {{item}}
</view>

等同于

<view wx:for="{{[1,2,3] + ' '}}" >
  {{item}}
</view>

条件渲染

wx:if

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

<view wx:if="{{condition}}"> True </view>

也可以用 wx:elif 和 wx:else 来添加一个 else 块:

<view wx:if="{{length > 5}}"> 1 </view>
<view wx:elif="{{length > 2}}"> 2 </view>
<view wx:else> 3 </view>

block wx:if

因为 wx:if 是一个控制属性,需要将它添加到一个标签上。如果要一次性判断多个组件标签,可以使用一个 <block/> 标签将多个组件包装起来,并在上边使用 wx:if 控制属性。

<block wx:if="{{true}}">
  <view> view1 </view>
  <view> view2 </view>
</block>

注意: <block/> 并不是一个组件,它仅仅是一个包装元素,不会在页面中做任何渲染,只接受控制属性。

wx:if vs hidden

hidden: 为true时隐藏,为false时显示

因为 wx:if 之中的模板也可能包含数据绑定,所以当 wx:if 的条件值切换时,框架有一个局部渲染的过程,因为它会确保条件块在切换时销毁或重新渲染。

同时 wx:if 也是惰性的,如果在初始渲染条件为 false,框架什么也不做,在条件第一次变成真的时候才开始局部渲染。

相比之下,hidden 就简单的多,组件始终会被渲染,只是简单的控制显示与隐藏。

一般来说,wx:if 有更高的切换消耗而 hidden 有更高的初始渲染消耗。因此,如果需要频繁切换的情景下,用 hidden 更好,如果在运行时条件不大可能改变则 wx:if 较好。

模板

WXML提供模板(template),可以在模板中定义代码片段,然后在不同的地方调用。

定义模板

使用 name 属性,作为模板的名字。然后在<template/>内定义代码片段,如:

<!--
  index: int
  msg: string
  time: string
-->
<template name="msgItem">
  <view>
    <text> {{index}}: {{msg}} </text>
    <text> Time: {{time}} </text>
  </view>
</template>

使用模板

使用 is 属性,声明需要的使用的模板,然后将模板所需要的 data 传入,如:

<template is="msgItem" data="{{...item}}"/>
Page({
  data: {
    item: {
      index: 0,
      msg: 'this is a template',
      time: '2016-09-15'
    }
  }
})

is 属性可以使用 Mustache 语法,来动态决定具体需要渲染哪个模板:

<template name="odd">
  <view> odd </view>
</template>
<template name="even">
  <view> even </view>
</template>

<block wx:for="{{[1, 2, 3, 4, 5]}}">
  <template is="{{item % 2 == 0 ? 'even' : 'odd'}}"/>
</block>

模板的作用域

模板拥有自己的作用域,只能使用 data 传入的数据以及模板定义文件中定义的 <wxs /> 模块。

引用

WXML 提供两种文件引用方式import和include。

import

import可以在该文件中使用目标文件定义的template,如:

在 item.wxml 中定义了一个叫item的template:

<!-- item.wxml -->
<template name="item">
  <text>{{text}}</text>
</template>

在 index.wxml 中引用了 item.wxml,就可以使用item模板:

<import src="item.wxml"/>
<template is="item" data="{{text: 'forbar'}}"/>

import 的作用域

import 有作用域的概念,即只会 import 目标文件中定义的 template,而不会 import 目标文件 import 的 template。

如:C import B,B import A,在 C 中可以使用 B 定义的template,在 B 中可以使用 A 定义的template,但是 C 不能使用 A 定义的template。

<!-- A.wxml -->
<template name="A">
  <text> A template </text>
</template>
<!-- B.wxml -->
<import src="a.wxml"/>
<template name="B">
  <text> B template </text>
</template>
<!-- C.wxml -->
<import src="b.wxml"/>
<template is="A"/>  <!-- Error! Can not use tempalte when not import A. -->
<template is="B"/>

include

include 可以将目标文件除了 <template/> <wxs/> 外的整个代码引入,相当于是拷贝到 include 位置,如:

<!-- index.wxml -->
<include src="header.wxml"/>
<view> body </view>
<include src="footer.wxml"/>
<!-- header.wxml -->
<view> header </view>
<!-- footer.wxml -->
<view> footer </view>

事件

什么是事件

  • 事件是视图层到逻辑层的通讯方式。
  • 事件可以将用户的行为反馈到逻辑层进行处理。
  • 事件可以绑定在组件上,当达到触发事件,就会执行逻辑层中对应的事件处理函数。
  • 事件对象可以携带额外信息,如 id, dataset, touches。

事件的使用方式

  • 在组件中绑定一个事件处理函数。

如bindtap,当用户点击该组件的时候会在该页面对应的 Page 中找到相应的事件处理函数。

<view id="tapTest" data-hi="Weixin" bindtap="tapName"> Click me! </view>
  • 在相应的 Page 定义中写上相应的事件处理函数,参数是event。
Page({
  tapName: function(event) {
    console.log(event)
  }
})

事件分类

事件分为冒泡事件和非冒泡事件:

  1. 冒泡事件:当一个组件上的事件被触发后,该事件会向父节点传递。
  2. 非冒泡事件:当一个组件上的事件被触发后,该事件不会向父节点传递。

WXML的冒泡事件列表:

类型 触发条件 绑定方式
touchstart 手指触摸动作开始
touchmove 手指触摸后移动
touchcancel 手指触摸动作被打断,如来电提醒,弹窗
touchend 手指触摸动作结束
tap 手指触摸后马上离开 bindtap或bind:tap
longpress 手指触摸后,超过350ms再离开,如果指定了事件回调函数并触发了这个事件,tap事件将不被触发 1.5.0
longtap 手指触摸后,超过350ms再离开(推荐使用 longpress 事件代替)
transitionend 会在 WXSS transition 或 wx.createAnimation 动画结束后触发
animationstart 会在一个 WXSS animation 动画开始时触发
animationiteration 会在一个 WXSS animation 一次迭代结束时触发
animationend 会在一个 WXSS animation 动画完成时触发
touchforcechange 在支持 3D Touch 的 iPhone 设备,重按时会触发 1.9.90

注:除上表之外的其他组件自定义事件如无特殊声明都是非冒泡事件,如 form 的submit事件,input 的input事件,scroll-view 的scroll事件,(详见各个组件)

类型 绑定方式 事件描述
tap bindtap或bind:tap
input bindinput或bind:input
change bindchange或bind:change

普通事件绑定

事件绑定的写法类似于组件的属性,如:

<view bindtap="handleTap">
    Click here!
</view>

如果用户点击这个 view ,则页面的 handleTap 会被调用。

事件绑定函数可以是一个数据绑定,如:

<view bindtap="{{ handlerName }}">
    Click here!
</view>

此时,页面的 this.data.handlerName 必须是一个字符串,指定事件处理函数名;如果它是个空字符串,则这个绑定会失效(可以利用这个特性来暂时禁用一些事件)。

自基础库版本 1.5.0 起,在大多数组件和自定义组件中, bind 后可以紧跟一个冒号,其含义不变,如 bind:tap 。基础库版本 2.8.1 起,在所有组件中开始提供这个支持。

绑定并阻止事件冒泡

除 bind 外,也可以用 catch 来绑定事件。与 bind 不同, catch 会阻止事件向上冒泡。

例如在下边这个例子中,点击 inner view 会先后调用handleTap3和handleTap2(因为 tap 事件会冒泡到 middle view,而 middle view 阻止了 tap 事件冒泡,不再向父节点传递),点击 middle view 会触发handleTap2,点击 outer view 会触发handleTap1。

<view id="outer" bindtap="handleTap1">
  outer view
  <view id="middle" catchtap="handleTap2">
    middle view
    <view id="inner" bindtap="handleTap3">
      inner view
    </view>
  </view>
</view>

互斥事件绑定

自基础库版本 2.8.2 起,除 bind 和 catch 外,还可以使用 mut-bind 来绑定事件。一个 mut-bind 触发后,如果事件冒泡到其他节点上,其他节点上的 mut-bind 绑定函数不会被触发,但 bind 绑定函数和 catch 绑定函数依旧会被触发。

换而言之,所有 mut-bind 是“互斥”的,只会有其中一个绑定函数被触发。同时,它完全不影响 bind 和 catch 的绑定效果。

例如在下边这个例子中,点击 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>

事件的捕获阶段

自基础库版本 1.5.0 起,触摸类事件支持捕获阶段。捕获阶段位于冒泡阶段之前,且在捕获阶段中,事件到达节点的顺序与冒泡阶段恰好相反。需要在捕获阶段监听事件时,可以采用capture-bind、capture-catch关键字,后者将中断捕获阶段和取消冒泡阶段。

在下面的代码中,点击 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。

<view id="outer" bind:touchstart="handleTap1" capture-catch:touchstart="handleTap2">
  outer view
  <view id="inner" bind:touchstart="handleTap3" capture-bind:touchstart="handleTap4">
    inner view
  </view>
</view>

事件对象

如无特殊说明,当组件触发事件时,逻辑层绑定该事件的处理函数会收到一个事件对象。

BaseEvent 基础事件对象属性列表:

属性 类型 说明 基础库版本
type String 事件类型
timeStamp Integer 事件生成时的时间戳
target Object 触发事件的组件的一些属性值集合
currentTarget Object 当前组件的一些属性值集合
mark Object 事件标记数据 2.7.1

CustomEvent 自定义事件对象属性列表(继承 BaseEvent):

属性 类型 说明
detail Object 额外的信息

TouchEvent 触摸事件对象属性列表(继承 BaseEvent):

属性 类型 说明
touches Array 触摸事件,当前停留在屏幕中的触摸点信息的数组
changedTouches Array 触摸事件,当前变化的触摸点信息的数组

特殊事件: canvas 中的触摸事件不可冒泡,所以没有 currentTarget。

type

代表事件的类型。

timeStamp

页面打开到触发事件所经过的毫秒数。

target

触发事件的源组件。

属性 类型 说明
id String 事件源组件的id
dataset Object 事件源组件上由data-开头的自定义属性组成的集合

currentTarget

事件绑定的当前组件。

属性 类型 说明
id String 当前组件的id
dataset Object 当前组件上由data-开头的自定义属性组成的集合

说明: target 和 currentTarget 可以参考上例中,点击 inner view 时,handleTap3 收到的事件对象 target 和 currentTarget 都是 inner,而 handleTap2 收到的事件对象 target 就是 inner,currentTarget 就是 middle。

dataset

在组件节点中可以附加一些自定义数据。这样,在事件中可以获取这些自定义的节点数据,用于事件的逻辑处理。

在 WXML 中,这些自定义数据以 data- 开头,多个单词由连字符 - 连接。这种写法中,连字符写法会转换成驼峰写法,而大写字符会自动转成小写字符。如:

  • data-element-type ,最终会呈现为 event.currentTarget.dataset.elementType ;
  • data-elementType ,最终会呈现为 event.currentTarget.dataset.elementtype 。

示例:

<view data-alpha-beta="1" data-alphaBeta="2" bindtap="bindViewTap"> DataSet Test </view>
Page({
  bindViewTap:function(event){
    event.currentTarget.dataset.alphaBeta === 1 // - 会转为驼峰写法
    event.currentTarget.dataset.alphabeta === 2 // 大写会转为小写
  }
})

mark

在基础库版本 2.7.1 以上,可以使用 mark 来识别具体触发事件的 target 节点。此外, mark 还可以用于承载一些自定义数据(类似于 dataset )。

当事件触发时,事件冒泡路径上所有的 mark 会被合并,并返回给事件回调函数。(即使事件不是冒泡事件,也会 mark 。)

代码示例:

在开发者工具中预览效果

<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 和 dataset 很相似,主要区别在于: mark 会包含从触发事件的节点到根节点上所有的 mark: 属性值;而 dataset 仅包含一个节点的 data- 属性值。

细节注意事项:

  • 如果存在同名的 mark ,父节点的 mark 会被子节点覆盖。
  • 在自定义组件中接收事件时, mark 不包含自定义组件外的节点的 mark 。
  • 不同于 dataset ,节点的 mark 不会做连字符和大小写转换。

touches

touches 是一个数组,每个元素为一个 Touch 对象(canvas 触摸事件中携带的 touches 是 CanvasTouch 数组)。 表示当前停留在屏幕上的触摸点。

Touch 对象

属性 类型 说明
identifier Number 触摸点的标识符
pageX, pageY Number 距离文档左上角的距离,文档的左上角为原点 ,横向为 X 轴,纵向为 Y 轴
clientX, clientY Number 距离页面可显示区域(屏幕除去导航条)左上角距离,横向为 X 轴,纵向为 Y 轴

CanvasTouch 对象

属性 类型 说明 特殊说明
identifier Number 触摸点的标识符
x, y Number 距离 Canvas 左上角的距离,Canvas 的左上角为原点 ,横向为 X 轴,纵向为 Y 轴

changedTouches

changedTouches 数据格式同 touches。 表示有变化的触摸点,如从无变有(touchstart),位置变化(touchmove),从有变无(touchend、touchcancel)。

detail

自定义事件所携带的数据,如表单组件的提交事件会携带用户的输入,媒体的错误事件会携带错误信息,详见组件定义中各个事件的定义。

点击事件的detail 带有的 x, y 同 pageX, pageY 代表距离文档左上角的距离。

事件绑定修改数据

 bindtap="handleclick"
 
 handleclick(){
      this.setData({
      count: this.data.count + 1
    })
 }

事件传参

小程序中的事件传参比较特殊,不能在绑定事件的同时为事件处理函数传递参数,因为小程序会把bindtap的属性值统一当作事件名称来处理。例如:下面代码将会被认为调用了一个名称为toLink(item.link)的函数,

 bindtap="toLink(item.link)"

可以为组件提供data-*自定义属性传参,其中*代表的是参数的名字,示例如下

    
bindtap="toLink"
data-link="{{item.link}}"

  toLink(e) {
    console.log("点击了图标,要跳转页面", e.currentTarget.dataset.link)
  },

bindinput的语法格式

实现文本框和data之间的数据同步

        <input
             placeholder="请输入"
             auto-focus
             bind:input="handleinput"
             value="{{msg}}"
    
        handleinput(e) {
            this.setData({
            msg: e.detail.value
            })
         },
    

网络数据请求

跨域问题只存在于基于浏览器的Web开发中,小程序的宿主环境不是浏览器而是微信客户端,所以小程序中没有跨域问题

wx.request({
  url: 'example.php', //请求地址
  method:"get",//请求参数
  data: {  //请求参数
    x: '',
    y: ''
  },
  header: { //设置请求头
    'content-type': 'application/json' // 默认值
  },
 dataType:"json", //请求返回结果的数据类型
 success (res) {//请求成功的回调
    console.log(res.data)
  },   
fail: function(res) {},// 请求失败执行的回调函数
complete: function(res) {},// 接口调用结束的回调函数(调用成功、失败都会执行)
})

页面导航

1.声明式导航

  • 在页面上生命一个navigator导航组件

  • 通过点击navigator组件实现页面跳转

  • url表示要跳转的页面的地址,必须以/开头;open-type表示跳转的方式,如tabbar页面必须用switchTab,默认为navigate

  • open-type 说明 备注
    navigate 对应 wx.navigateTo 或 wx.navigateToMiniProgram 的功能 打开新页面,新页面会压栈
    redirect 对应 wx.redirectTo 的功能 页面重定向,新页面不会压栈,而是把原来的页面替换掉
    switchTab 对应 wx.switchTab 的功能 跳转tabBar页面,并关闭其他所有非 tabBar 页面
    reLaunch 对应 wx.reLaunch 的功能 重新加载指定页面,页面全部出栈,只留下新的页面
    navigateBack 对应 wx.navigateBack 或 wx.navigateBackMiniProgram (基础库 2.24.4 版本支持)的功能 回到上一个页面,通过delta指定回退的层数,默认值为1,当delta超出历史记录,返回首页
    exit 退出小程序,target="miniProgram"时生效

    ​

    
       <!--hover-class="none" 跳转到Tabbar页面-->
        <navigator url="/pages/index/index" open-type="switchTab" >跳转index</navigator>
        <!--hover-class="none"后退导航-->
        <navigator url="/pages/index/index" open-type="navigateBack" delta="1">跳转index</navigator>
        <view>
        <!--hover-class="none" 导航传参-->
       <navigator url="/pages/demo2/demo2?name=zhangsan&age=12" hover-class="none">跳转demo2</navigator>
    </view>
    
    //2.demo2.js
    //在onload方法中通过options来获取页面1传递的参数
      onLoad: function (options) {
        console.log(options)
      },
    

    ​

2.编程式导航

wx.navigateTo({
  url: '/pagex/test/test?id=1',//导航传参
})
接收参数
onLoad(options){
    console.log(options)//options就是导航传递过来的参数
}

导航模式

调用小程序的导航API实现页面的跳转

<view>
   <navigator url="/pages/demo2/demo2?name=zhangsan&age=12" open-type="redirectTo" >跳转demo2</navigator>
</view>

navigate   打开新页面,新页面会压栈
redirect    页面重定向,新页面不会压栈,而是把原来的页面替换掉
navigateBack   回到上一个页面
reLaunch    重新加载指定页面,页面全部出栈,只留下新的页面
switchTab   跳转tabBar页面,并关闭其他所有非 tabBar 页面

//  https://segmentfault.com/a/1190000014996318?utm_source=index-hottest


//注意点:
navigate, redirect 只能打开非 tabBar 页面。
switchTab 只能打开 tabBar 页面。
reLaunch 可以打开任意页面。

//回退两层,可以通过getCurrentPages()方法来查看栈中的页面个数
wx.navigateBack({
    //当delta超出历史记录,返回首页
    delta:2
})

页面事件

1.下拉刷新:移动端中手指在屏幕下滑从而重新加载页面数据

全局开启下拉刷新:在app.json的window节点中,将enablePullDownRefresh设置为true

局部开启下拉刷新(推荐):在页面的json文件中将enablePullDownRefresh设置为true

{
  "usingComponents": {},
  "enablePullDownRefresh":true,//开启下拉刷新
  "backgroundColor":"#D8BFD8",//窗口的背景色
  "backgroundTextStyle":"light"//loading的样式颜色
}

在js中的onPullDownRefresh中可以监听到下拉刷新的操作,在这里进行数据的处理,但是下拉刷新后刷新的loading会一直显示,需要调用 wx.stopPullDownRefresh()停止下拉刷新的操作

2.上拉触底:移动端中手指在屏幕上滑从而重新加载更多数据,可以通过onReachBottomDistance来配置上拉触底触发事件的距离,默认为50px

在js中的onReachBottom中可以监听到上拉触底事件,这里应该进行节流的处理,上一个请求未处理完毕,不允许进行下一次请求

生命周期

应用生命周期:小程序从启动=>运行=>销毁的过程

页面生命周期:每个页面加载=>渲染=>销毁的过程

页面的生命周期范围较小,应用生命周期的范围较大,可以在特定的时间点执行某些特定的操作

应用生命周期


onLaunch:function(){}//小程序初始化完成,会触发onLaunch(全局只触发一次)
onShow:function(){}//小程序启动,或从后台进入前台显示,会触发onShow
onHide:function(){}//小程序从前台进入后台,会触发onHide

页面生命周期

onLoad:function(options){}//监听页面加载,一个页面只调用一次
onShow:function(){}//监听页面显示
onReady:function(){}//监听页面初次渲染完成,一个页面只调用一次
onHide:function(){}//监听页面隐藏
onUnLoad:function(){}//监听页面卸载,一个页面只调用一次
阅读全文

Echarts的入门整理

2022/11/7

1.安装方式

npm install echarts --save

2.引入 ECharts

import * as echarts from 'echarts';
// 基于准备好的dom,初始化echarts实例
var myChart = echarts.init(document.getElementById('main'));
// 绘制图表
myChart.setOption({
  title: {
    text: 'ECharts 入门示例'
  },
  tooltip: {},
  xAxis: {
    data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
  },
  yAxis: {},
  series: [
    {
      name: '销量',
      type: 'bar',
      data: [5, 20, 36, 10, 10, 20]
    }
  ]
});

3.图表容器及大小

1.在 HTML 中定义有宽度和高度的父容器(推荐)

通常来说,需要在 HTML 中先定义一个 <div> 节点,并且通过 CSS 使得该节点具有宽度和高度。初始化的时候,传入该节点,图表的大小默认即为该节点的大小,除非声明了 opts.width 或 opts.height 将其覆盖。需要注意的是,使用这种方法在调用 echarts.init 时需保证容器已经有宽度和高度了。

<div id="main" style="width: 600px;height:400px;"></div>
<script type="text/javascript">
  var myChart = echarts.init(document.getElementById('main'));
</script>
2.指定图表的大小

如果图表容器不存在宽度和高度,或者,你希望图表宽度和高度不等于容器大小,也可以在初始化的时候指定大小。

<div id="main"></div>
<script type="text/javascript">
  var myChart = echarts.init(document.getElementById('main'), null, {
    width: 600,
    height: 400
  });
</script>
3.响应容器大小的变化

监听图标容器的大小并改变图表大小

<script type="text/javascript">
  var myChart = echarts.init(document.getElementById('main'));
  window.onresize = function() {
    myChart.resize();
  };
</script>
4.为图表设置特定的大小

除了直接调用 resize() 不含参数的形式之外,还可以指定宽度和高度,实现图表大小不等于容器大小的效果。

myChart.resize({
  width: 800,
  height: 400
});

4.容器节点被销毁以及被重建时

假设页面中存在多个标签页,每个标签页都包含一些图表。当选中一个标签页的时候,其他标签页的内容在 DOM 中被移除了。这样,当用户再选中这些标签页的时候,就会发现图表“不见”了。

本质上,这是由于图表的容器节点被移除导致的。即使之后该节点被重新添加,图表所在的节点也已经不存在了。

正确的做法是,在图表容器被销毁之后,调用 echartsInstance.dispose 销毁实例,在图表容器重新被添加后再次调用 echarts.init 初始化。

完整基础用法:

    async mounted() {
      window.addEventListener('resize', () => {
        this.myChart.resize()
      })
    },
     //收支情况柱状图
      drawTotalChart() {
        if (
          this.myChart != null &&
          this.myChart != '' &&
          this.myChart != undefined
        ) {
          this.myChart.dispose()
        }
        let chartDom = document.getElementById('totalmain')
        this.myChart = echarts.init(chartDom)

        let option = {
          color: ['rgba(96, 154, 240, 1)', 'rgba(107, 204, 212, 1)'],
          tooltip: {
            trigger: 'axis',
            axisPointer: {
              type: 'shadow',
            },
          },
          grid: {
            top: 50,
            left: 0,
            right: 0,
            bottom: 20,
            containLabel: true,
          },
          legend: {
            data: ['收入', '支出'],
            x: 'left',
            padding: [0, 0, 0, 0],
            itemStyle: {},
          },
          xAxis: {
            type: 'category',
            data: this.monthList,
          },
          yAxis: {},
          series: [
            { data: this.totalincome, type: 'bar', name: '收入' },
            { data: this.totalspend, type: 'bar', name: '支出' },
          ],
        }
        option && this.myChart.setOption(option)
      },
    

5.ECharts 中的样式

1.颜色主题
  • ECharts 中默认内置了两套主题: light dark。可以像这样切换成深色模式:
var chart = echarts.init(dom, 'dark');
  • 其他的主题,没有内置在 ECharts 中,需要自己加载

  • 在echarts资源/主题构建工具中下载需要的主题并引入,在echarts初始化时,使用主题。可以使用多个不同的主题。例如:

let myChart = echarts.init(document.getElementById("School"),'macarons');

  • 注意:使用时不能自己指定color,否则主题的颜色将失效,变为指定的颜色

  import '@/utils/purple-passion'
  import '@/utils/infographic'
  
  let chartDom = document.getElementById('paymentMain')
  this.paymentMyChart = echarts.init(chartDom, 'infographic')
   
  let chartDom = document.getElementById('totalmain')
  this.myChart = echarts.init(chartDom, 'purple-passion')
2.调色盘

调色盘,可以在 option 中设置。它给定了一组颜色,图形、系列会自动从其中选择颜色。 可以设置全局的调色盘,也可以设置系列自己专属的调色盘。

option = {
  // 全局调色盘。
  color: [
    '#c23531',
    '#2f4554',
    '#61a0a8',
    '#d48265',
    '#91c7ae',
    '#749f83',
    '#ca8622',
    '#bda29a',
    '#6e7074',
    '#546570',
    '#c4ccd3'
  ],

  series: [
    {
      type: 'bar',
      // 此系列自己的调色盘。
      color: [
        '#dd6b66',
        '#759aa0',
        '#e69d87',
        '#8dc1a9',
        '#ea7e53',
        '#eedd78',
        '#73a373',
        '#73b9bc',
        '#7289ab',
        '#91ca8c',
        '#f49f42'
      ]
      // ...
    },
    {
      type: 'pie',
      // 此系列自己的调色盘。
      color: [
        '#37A2DA',
        '#32C5E9',
        '#67E0E3',
        '#9FE6B8',
        '#FFDB5C',
        '#ff9f7f',
        '#fb7293',
        '#E062AE',
        '#E690D1',
        '#e7bcf3',
        '#9d96f5',
        '#8378EA',
        '#96BFFF'
      ]
      // ...
    }
  ]
};
3.高亮的样式:emphasis

在鼠标悬浮到图形元素上时,一般会出现高亮的样式。默认情况下,高亮的样式是根据普通样式自动生成的。但是高亮的样式也可以自己定义,主要是通过 emphasis 属性来定制。emphsis 中的结构,和普通样式的结构相同,例如:

         series: [
            {
              name: '报销',
              type: 'pie',
              radius: '90%',
              hoverAnimation: false,
              emphasis: {
                itemStyle: {
                  // 高亮时点的颜色。
                  color: 'skyblue',
                },
                label: {
                  show: true,
                  // 高亮时标签的文字。
                  formatter: 'This is a emphasis label.',
                },
              },
              label: {
                normal: {
                  show: true,
                  formatter: '{d}%',
                  position: 'inner',
                  textStyle: {
                    align: 'center',
                    baseline: 'middle',
                    fontFamily: '微软雅黑',
                    fontSize: 14,
                    color: 'white',
                  },
                },
                labelLine: {
                  show: false,
                },
              },
              data: this.data.reservationReimburseItemList,
            },
          ],
4.直接的样式设置

直接的样式设置是比较常用设置方式。纵观 ECharts 的 option 中,很多地方可以设置 itemStyle、lineStyle、areaStyle、label 等等。这些的地方可以直接设置图形元素的颜色、线宽、点的大小、标签的文字、标签的样式等等。上面的示例中已举例

6.坐标轴

1.x 轴、y 轴

x 轴和 y 轴都由轴线、刻度、刻度标签、轴标题四个部分组成。部分图表中还会有网格线来帮助查看和计算数据

普通的二维数据坐标系都有 x 轴和 y 轴,通常情况下,x 轴显示在图表的底部,y 轴显示在左侧,一般配置如下:

option = {
  xAxis: {
    // ...
  },
  yAxis: {
    // ...
  }
};

在二维数据中,轴也可以有多个。ECharts 中一般情况下单个 grid 组件最多只能放两个 x/y 轴,多于两个 x/y 轴需要通过配置 offset 属性防止同个位置多个轴的重叠。两个 x 轴显示在上下,两个 y 轴显示在左右两侧。

option = {
  xAxis: {
    type: 'time',
    name: '销售时间'
    // ...
  },
  yAxis: [
    {
      type: 'value',
      name: '销售数量'
      // ...
    },
    {
      type: 'value',
      name: '销售金额'
      // ...
    }
  ]
  // ...
};

轴线:ECharts 提供了轴线 axisLine 相关的配置,我们可以根据实际情况调整,例如轴线两端的箭头,轴线的样式等。

option = {
  xAxis: {
    axisLine: {
      symbol: 'arrow',
      lineStyle: {
        type: 'dashed'
        // ...
      }
    }
    // ...
  },
  yAxis: {
    axisLine: {
      symbol: 'arrow',
      lineStyle: {
        type: 'dashed'
        // ...
      }
    }
  }
  // ...
};

刻度:ECharts 提供了轴线 axisTick 相关的配置,我们可以根据实际情况调整,例如刻度线的长度,样式等。

option = {
  xAxis: {
    axisTick: {
      length: 6,
      lineStyle: {
        type: 'dashed'
        // ...
      }
    }
    // ...
  },
  yAxis: {
    axisTick: {
      length: 6,
      lineStyle: {
        type: 'dashed'
        // ...
      }
    }
  }
  // ...
};

刻度标签:ECharts 提供了轴线 axisLabel 相关的配置,我们可以根据实际情况调整,例如文字对齐方式,自定义刻度标签内容等。

option = {
  xAxis: {
    axisLabel: {
      formatter: '{value} kg',
      align: 'center'
      // ...
    }
    // ...
  },
  yAxis: {
    axisLabel: {
      formatter: '{value} 元',
      align: 'center'
      // ...
    }
  }
  // ...
};

示例:图左侧的 y 轴代表东京月平均气温,右侧的 y 轴表示东京降水量,x 轴表示时间。两组 y 轴在一起,反映了平均气温和降水量间的趋势关系。

option = {
  tooltip: {
    trigger: 'axis',
    axisPointer: { type: 'cross' }
  },
  legend: {},
  xAxis: [
    {
      type: 'category',
      axisTick: {
        alignWithLabel: true
      },
      data: [
        '1月',
        '2月',
        '3月',
        '4月',
        '5月',
        '6月',
        '7月',
        '8月',
        '9月',
        '10月',
        '11月',
        '12月'
      ]
    }
  ],
  yAxis: [
    {
      type: 'value',
      name: '降水量',
      min: 0,
      max: 250,
      position: 'right',
      axisLabel: {
        formatter: '{value} ml'
      }
    },
    {
      type: 'value',
      name: '温度',
      min: 0,
      max: 25,
      position: 'left',
      axisLabel: {
        formatter: '{value} °C'
      }
    }
  ],
  series: [
    {
      name: '降水量',
      type: 'bar',
      yAxisIndex: 0,
      data: [6, 32, 70, 86, 68.7, 100.7, 125.6, 112.2, 78.7, 48.8, 36.0, 19.3]
    },
    {
      name: '温度',
      type: 'line',
      smooth: true,
      yAxisIndex: 1,
      data: [
        6.0,
        10.2,
        10.3,
        11.5,
        10.3,
        13.2,
        14.3,
        16.4,
        18.0,
        16.5,
        12.0,
        5.2
      ]
    }
  ]
};

7.事件与行为

在 Apache ECharts 的图表中用户的操作将会触发相应的事件。开发者可以监听这些事件,然后通过回调函数做相应的处理。在 ECharts 中事件分为两种类型,一种是用户鼠标操作点击,或者 hover 图表的图形时触发的事件,还有一种是用户在使用可以交互的组件后触发的行为事件,例如在切换图例开关时触发的 ‘legendselectchanged’ 事件(这里需要注意切换图例开关是不会触发 'legendselected' 事件的),数据区域缩放时触发的 ‘datazoom’ 事件等等。

1.鼠标事件的处理

ECharts 支持常规的鼠标事件类型,包括 'click'、 'dblclick'、 'mousedown'、 'mousemove'、 'mouseup'、 'mouseover'、 'mouseout'、 'globalout'、 'contextmenu' 事件。

myChart.on('click', function(params) {
  window.open('https://www.baidu.com/s?wd=' + encodeURIComponent(params.name));
});

所有的鼠标事件包含参数 params,这是一个包含点击图形的数据信息的对象,如下格式:

type EventParams = {
  // 当前点击的图形元素所属的组件名称,
  // 其值如 'series'、'markLine'、'markPoint'、'timeLine' 等。
  componentType: string;
  // 系列类型。值可能为:'line'、'bar'、'pie' 等。当 componentType 为 'series' 时有意义。
  seriesType: string;
  // 系列在传入的 option.series 中的 index。当 componentType 为 'series' 时有意义。
  seriesIndex: number;
  // 系列名称。当 componentType 为 'series' 时有意义。
  seriesName: string;
  // 数据名,类目名
  name: string;
  // 数据在传入的 data 数组中的 index
  dataIndex: number;
  // 传入的原始数据项
  data: Object;
  // sankey、graph 等图表同时含有 nodeData 和 edgeData 两种 data,
  // dataType 的值会是 'node' 或者 'edge',表示当前点击在 node 还是 edge 上。
  // 其他大部分图表中只有一种 data,dataType 无意义。
  dataType: string;
  // 传入的数据值
  value: number | Array;
  // 数据图形的颜色。当 componentType 为 'series' 时有意义。
  color: string;
};
阅读全文

Vue3中的watch和computed

2022/10/24

a) computed

computed函数用来创造计算属性,和过去一样,它返回的值是一个ref对象。里面可以传方法,或者一个对象,对象中包含set()、get()方法

只读的计算属性:

  姓: <input type="text" v-model="person.firstName" /> <br />
  名: <input type="text" v-model="person.lastName" /> <br />
  <span>全名:{{ person.fullName }}</span>

import { reactive, computed } from "vue";
setup() {
    const person = reactive({
      firstName: "张",
      lastName: "三",
    });

    person.fullName = computed(() => {
      return person.firstName + "-" + person.lastName;
    });

    return { person };
  },

可读可改的计算属性:

   姓: <input type="text" v-model="person.firstName" /> <br />
   名: <input type="text" v-model="person.lastName" /> <br />
   全名:<input type="text" v-model="person.fullName" /> <br />
   import { reactive, computed } from "vue";
     setup() {
    const person = reactive({
      firstName: "张",
      lastName: "三",
    });
    //完整形式
    person.fullName = computed({
      get() {
        return person.firstName + "-" + person.lastName;
      },
      set(value) {
        const nameArr = value.split("-");
        person.firstName = nameArr[0];
        person.lastName = nameArr[1];
      },
    });

    return { person };
  },    
   

b) watch

与vue2中watch配置功能一致

  • 监视reactive定义的响应式数据时:oldValue无法正确获取,强制开启了深度监视(deep配置失效)
  • 监视reactive定义的响应式数据中某个属性(对象)时,deep配置有效

1.监视ref定义的一个响应式对象

 let count = ref(0);
    let msg = ref("你好啊");

    const person = reactive({
      name: "张三",
      age: 18,
      jobs: {
        job1: {
          salary: 20,
        },
      },
    });


    watch(count, (newVal, oldVal) => {
      console.log("count的值改变了", newVal, oldVal);
    });

2.监视ref定义的多个响应式对象

   watch(
      [count, msg],
      (newVal, oldVal) => {
        console.log("count或者msg的数据改变了", newVal, oldVal);
      },
      { immediate: true, deep: true }
    );
    
    

3.监视reactive定义的响应式对象的全部属性,但是这里无法正确的获取oldVal,强制开启了深度监视(deep配置无效)

  watch(person, (newVal, oldVal) => {
      console.log("person改变了", newVal, oldVal);
    },{deep:false});//deep配置无效

4.监视reactive定义的响应式数据的一个属性,这种方式可获取oldVal

 watch(
      () => person.age,
      (newVal, oldVal) => {
        console.log("person的age改变了", newVal, oldVal);
      }
    );

5.监视reactive定义的响应式数据的某些属性,这种方式可获取oldVal

    watch([() => person.age, () => person.name], (newVal, oldVal) => {
      console.log("person的age或name改变了", newVal, oldVal);
    });

6.特殊情况,监视reactive定义的响应式对象中的某个对象,这里deep配置有效,获取不到oldVal

    watch(
      () => person.jobs,
      (newVal, oldVal) => {
        console.log("person的job改变了", newVal, oldVal);
      },
      { deep: true }
    );

7.ref定义的响应式对象

如果定义的对象是基本数据,则不用加.value,例如 let count=ref(0),如果在监视时加了.value,则监视的则是0这个数字,不是我们想要的refImpl数据对象,如果定义的对象是一个对象,则需要加.value,否则监听时虽然对象内的数据改变,但整个对象没有改变(其内存地址没有发生变化),会认为没有发生变化,也就监听不到对象内数据的变化

    const person = ref({
      name: "张三",
      age: 18,
      jobs: {
        job1: {
          salary: 20,
        },
      },
    });



1.数据为对象时,需要加.value
 watch(person.value, (newVal, oldVal) => {
      console.log("person的值改变了", newVal, oldVal);
    });
2.若不加.value,则需要深度监听(deep:true),才能监听到对象的改变
    watch(
      person,
      (newVal, oldVal) => {
        console.log("person的值改变了", newVal, oldVal);
      },
      { deep: true }
    );

watch和watchEffect

a) 纯函数与副作用

effect全称叫side effect,副作用。

什么是副作用呢,一个函数运行后产生了可以影响其外部或可以看到的效果,就叫副作用,比如document. Body. Append,alert再或者是showModel(在页面中展示一个弹层),或者window.open打开一个新窗口。
Snipaste_2021-11-23_10-58-18

// 纯函数:如果函数的调用参数相同,则永远返回相同的结果。它不依赖于程序执行期间函数外部任何状态或数据的变化,必须只依赖于其输入参数。它不应修改程序的状态或引起副作用。
function priceAfterTax(productPrice) { 
    return (productPrice * 0.20) + productPrice;
}

// 副作用:一个可以被观察的副作用是在函数内部与其外部的任意交互。这可能是在函数内修改外部的变量,或者在函数里调用另外一个函数等。
var tax = 20;
function calculateTax(productPrice) {
    tax = tax/100
    return (productPrice * tax) + productPrice;
}
  • react中副作用可以这样理解:组件初始化的时候,组件根据开发者的设定,由自身驱动的第一次DOM修改,就是主作用。主作用之后,组件开始执行用户逻辑,这时你眼里的业务逻辑代码,在React眼里都是副作用。

  • vue3中的副作用与react中副作用的定义类似,响应式数据的变更造成的其他连锁反应,以及后续逻辑,这些连锁反应都叫副作用。

哪些函数没有副作用呢,只用来计算结果的函数,比如Math.max,JSON.parse,它们的运行除了返回结果外不会有其它效果,这就叫不产生副作用。

基本上可以简化的理解为副作用就是执行某种操作(副作用函数),无副作用就是执行某种计算(纯函数)。

这里watchEffect的意思就是在观察(watch)到变化后执行一些操作(effect)。

b) watchEffect的基本使用

import { watchEffect, ref } from 'vue'
setup () {
    const userID = ref(0)
    
    watchEffect(() => console.log(userID))
    
    setTimeout(() => {
      userID.value = 1
    }, 1000)

    return {
      userID
    }
 }

c) watch和watchEffect的异同

watch和watchEffect都可以侦听副作用,区别在于:

  • 第一点我们可以从示例代码中看到 watchEffect 不需要指定监听的属性,他会自动的收集依赖, 只要我们回调中引用到了 响应式的属性, 那么当这些属性变更的时候,这个回调都会执行;而 watch 只能监听指定的属性而做出变更。
  • 第二点就是 watch 可以获取到新值与旧值(更新前的值),而 watchEffect 是拿不到的。
  • 第三点是 watchEffect 如果存在的话,在组件初始化的时候就会执行一次用以收集依赖(与computed同理),而后收集到的依赖发生变化,这个回调才会再次执行;而 watch 不需要,因为他一开始就指定了依赖。

d) watchEffect的刷新时机

watchEffect会在所有的组件 update 前执行。

<template>
  <div>{{ count }}</div>
</template>

<script>
export default {
  setup() {
    const count = ref(0)

    watchEffect(() => {
      console.log(count.value)
    })

    return {
      count
    }
  }
}
</script>

在这个例子中:

  • count 会在初始运行时同步打印出来,因为watchEffect会在组件初始化的时候默认收集一次依赖
  • 更改 count 时,将在组件更新前执行副作用。

如果需要在组件更新(例如:当与模板引用一起)后重新运行侦听器副作用,我们可以传递带有 flush 选项的附加 options 对象 (默认为 'pre'):

watchEffect(
  () => {
    /* ... */
  },
  {
    flush: 'post'
  }
)

e) 停止侦听

当 watchEffect 在组件的 setup() 函数或生命周期钩子被调用时,侦听器会被链接到该组件的生命周期,并在组件卸载时自动停止。

在一些情况下,也可以显式调用返回值以停止侦听:

const stop = watchEffect(() => {
  /* ... */
})

stop()

f) 清除副作用的函数

watchEffect函数的onInvalidate方法就是用来清除副作用的,但副作用不一定是不被需要的。它可以是获取数据、事件监听或订阅、改变应用状态、修改 DOM、输出日志等等。清除副作用实际上是Vue3提供给用户的一种取消异步副作用的实现方法。

<template>
  <div>
    <div>{{count}}</div>
    <button @click="doAdd">点我+1</button>
  </div>
</template>

<script>
import { ref, watchEffect, watch } from "vue";

export default {
  setup() {
    const count = ref(1);

    const doAdd = ()=>{
      count.value++
    }

    watchEffect(onInvalidate => {
      console.log(count.value)
      // 异步api调用,返回一个操作对象
      const timer = setInterval(() => {
        console.log("timer执行了")
      }, 1000);

      //onInvalidate(fn)传入的回调会在watchEffect重新运行或者watchEffect停止的时候执行。
      onInvalidate(()=>{
        clearInterval(timer)
      })
    });

    return {
      count,
      doAdd
    };
  }
};
</script>

阅读全文
avatar
参商
yyds
img_show