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-sharedshared 的自定义组件。(这个选项在插件中不可用。)

    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 选中日期后会执行的回调,只有当 daterangedatetimerange 才生效 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:elifwx: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 提供两种文件引用方式importinclude

import

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

在 item.wxml 中定义了一个叫itemtemplate

<!-- 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 会先后调用handleTap3handleTap2(因为 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 起,除 bindcatch 外,还可以使用 mut-bind 来绑定事件。一个 mut-bind 触发后,如果事件冒泡到其他节点上,其他节点上的 mut-bind 绑定函数不会被触发,但 bind 绑定函数和 catch 绑定函数依旧会被触发。

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

例如在下边这个例子中,点击 inner view 会先后调用 handleTap3handleTap2 ,点击 middle view 会调用 handleTap2handleTap1

<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-bindcapture-catch关键字,后者将中断捕获阶段和取消冒泡阶段。

在下面的代码中,点击 inner view 会先后调用handleTap2handleTap4handleTap3handleTap1

<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 中,如果按钮被点击,将触发 bindViewTapbindButtonTap 两个事件,事件携带的 event.mark 将包含 myMarkanotherMark 两项。

Page({
  bindViewTap: function(e) {
    e.mark.myMark === "last" // true
    e.mark.anotherMark === "leaf" // true
  }
})

markdataset 很相似,主要区别在于: 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.navigateTowx.navigateToMiniProgram 的功能 打开新页面,新页面会压栈
    redirect 对应 wx.redirectTo 的功能 页面重定向,新页面不会压栈,而是把原来的页面替换掉
    switchTab 对应 wx.switchTab 的功能 跳转tabBar页面,并关闭其他所有非 tabBar 页面
    reLaunch 对应 wx.reLaunch 的功能 重新加载指定页面,页面全部出栈,只留下新的页面
    navigateBack 对应 wx.navigateBackwx.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.widthopts.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 中,很多地方可以设置 itemStylelineStyleareaStylelabel 等等。这些的地方可以直接设置图形元素的颜色、线宽、点的大小、标签的文字、标签的样式等等。上面的示例中已举例

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