关于三种布局
1.流式布局
2.弹性布局
3.响应式布局
加载过慢请开启缓存(浏览器默认开启)

1.新建组件:在根目录下新建components文件夹,在该文件夹下新建组件文件夹,右键新建Component
2.组件的引入:全局引入在app.json中,新增 “usingComponents”: {},在其中引入组件名及对应的路径,如:
"usingComponents": {
"timeline":"/components/timeline/timeline"
}
如果是局部引入,则在对应的页面json中添加如上代码
3.组件的使用:在页面中将组件名作为标签使用,如:
<timeline/>
4.组件与页面的区别:
"component": true,
组件:
Component({})
页面:
Page({})
组件:
data:{},
methods: {
getlist(){}
}
页面:
data:{},
getlist(){}
默认情况下,自定义组件的样式只对当前组件生效,不会影响到组件之外的UI结构,小程序页面的样式也不会影响到组件的样式,防止样式之间的互相影响
默认情况下,自定义组件的样式隔离特性能够防止组件内外样式互相干扰的问题,如果希望外界能够控制组件内部的样式,可以通过styleIsolation修改组件的样式隔离选项。
在options中修改:
Component({
options:{
styleIsolation:'isolated'
}
})
在组件的json文件中添加如下配置:
{
"styleIsolation":"isolated"
}
styleIsolation的可选值:
| 可选值 | 默认值 | 描述 |
|---|---|---|
| isolated | 是 | 表示启用样式隔离,在自定义组件内外,使用 class 指定的样式将不会相互影响(一般情况下的默认值); |
| apply-shared | 否 | 表示页面 wxss 样式将影响到自定义组件,但自定义组件 wxss 中指定的样式不会影响页面; |
| shared | 否 | 表示页面 wxss 样式将影响到自定义组件,自定义组件 wxss 中指定的样式也会影响页面和其他设置了 apply-shared 或 shared 的自定义组件。(这个选项在插件中不可用。) |
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更倾向于存储外界传递到组件中的数据
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]
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
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
array.toString()
返回数组的所有值用逗号隔开的字符串
let arr = [1, 5, 6, 8, 7]
let arr1 = arr.toString()
console.log(arr1)//1,5,6,8,7
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]
array.reverse()
返回颠倒顺序后的数组
let arr = [1, 5, 6, 8, 7]
arr.reverse()
console.log(arr)//[7, 8, 6, 5, 1]
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]
array.every(function(currentValue,index,arr), thisValue)
| 参数 | 描述 |
|---|---|
| function(currentValue, index,arr) | 必须。函数,数组中的每个元素都会执行这个函数函数参数: |
| currentValue | 必须。当前元素的值 |
| index | 可选。当前元素的索引值 |
| arr | 可选。当前元素属于的数组对象 |
| thisValue | 可选。对象作为该执行回调时使用,传递给函数,用作 “this” 的值。如果省略了 thisValue ,”this” 的值为 “undefined” |
返回布尔值。如果所有元素都通过检测返回 true,否则返回 false。
every() 方法使用指定函数检测数组中的所有元素:
注意: 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
array.some(function(currentValue,index,arr),thisValue)
| 参数 | 描述 |
|---|---|
| function(currentValue, index,arr) | 必须。函数,数组中的每个元素都会执行这个函数函数参数: |
| currentValue | 必须。当前元素的值 |
| index | 可选。当前元素的索引值 |
| arr | 可选。当前元素属于的数组对象 |
| thisValue | 可选。对象作为该执行回调时使用,传递给函数,用作 “this” 的值。如果省略了 thisValue ,”this” 的值为 “undefined” |
返回布尔值。如果数组中有元素满足条件返回 true,否则返回 false。
some() 方法会依次执行数组的每个元素:
注意: 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
array.shift()
let arr = [2, 8, 14, 9, 25, 6, 32]
arr.shift()
console.log(arr)// [8, 14, 9, 25, 6, 32]
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]
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]
array.pop()
let arr = [14, 9, 25, 6, 32]
arr.pop()
console.log(arr)//[14, 9, 25, 6]
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]
注意: 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]
find() 方法为数组中的每个元素都调用一次函数执行:
注意: 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
findIndex() 方法为数组中的每个元素都调用一次函数执行:
注意: 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
如果对象是数组返回 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
该方法将从头到尾地检索数组,看它是否含有对应的元素。开始检索的位置在数组 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
如果要检索的元素没有出现,则该方法返回 -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
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的新增方法.
类数组对象,最基本的要求就是具有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]
**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(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)
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
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()
},
}
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++
},
},
},
主体部分由三个文件组成,必须放在项目的根目录,如下:
| 文件 | 必填 | 作用 |
|---|---|---|
| app.js | 是 | 小程序逻辑 |
| app.json | 是 | 小程序公共设置( 严格的JSON语法,不能用单引号,不能写注释,否则编译报错 ) |
| app.wxss | 否 | 小程序公共样式表( 拓展了rpx尺寸单位,微信专属响应式像素 ) |
页面由四个文件组成,分别是:
| 文件类型 | 必填 | 作用 |
|---|---|---|
| js | 是 | 页面逻辑 ( 微信小程序没有window和document对象 ) |
| wxml | 是 | 页面结构 ( XML语法,不是HTML语法 ) |
| wxss | 否 | 页面样式表 ( 拓展了rpx尺寸单位,微信专属响应式像素 ) |
| json | 否 | 页面配置 ( 严格的JSON语法,不能用单引号,不能写注释,否则编译报错 ) |
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.页面渲染完成
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 | 设置样式版本 |
用于设置小程序的状态栏、导航条、标题、窗口背景色。
注意:页面的
.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 |
https://mp.weixin.qq.com/debug/wxadoc/dev/framework/config.html#tabbar
如果小程序是一个多 tab 应用(客户端窗口的底部或顶部有 tab 栏可以切换页面),可以通过 tabBar 配置项指定 tab 栏的表现,以及 tab 切换时显示的对应页面。
Tip:
属性说明:
| 属性 | 类型 | 必填 | 默认值 | 描述 |
|---|---|---|---|---|
| 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 时,此参数无效 |
在页面配置中,可以修改全局配置的部分信息,用来指定当前页面的一些特有配置
| 属性 | 类型 | 默认值 | 描述 |
|---|---|---|---|
| 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中设置该项 |
| wxml | html | |
|---|---|---|
| 标签名称 | view,text,image,navigator | div,span,img,a |
| 属性节点 | <a href="#">超链接</a> |
<navigator url="/apges/home/home">超链接</navigator> |
与 CSS 相比,WXSS 扩展的特性有:
尺寸单位(rpx rem)
vw和vh是css3中的新单位,是一种视窗单位,在小程序中同样适用
样式导入
提供了全局的样式和局部样式(项目根目录下的app.wxss会作用于所有小程序页面,局部页面的.wxss样式仅对当前页面生效)
wxss仅支持部分css选择器
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
使用@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); */
}
特别需要注意的是小程序 不支持通配符 * ,因此以下代码无效!
*{
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) | 选择某个索引的标签 |
原声小程序不支持 less ,其他基于小程序的框架基本都支持,如 wepy , mpvue , taro ,uni-app等。
但是仅仅因为一个less功能,而去引用框架,肯定是不可取的。因此可以使用以下方式来实现
1. 编辑器是 vscode
2. 安装插件 easy less
3. 在vs code的设置中加如下,配置
{
"workbench.sideBar.location": "left",
"less.compile": {
"outExt":".wxss"
}
}
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(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" />
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 控制属性绑定一个数组,即可使用数组中各项的数据重复渲染该组件。
默认数组的当前项的下标变量名默认为 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:if,也可以将 wx:for 用在<block/>标签上,以渲染一个包含多节点的结构块。例如:
<block wx:for="{{[1, 2, 3]}}">
<view> {{index}}: </view>
<view> {{item}} </view>
</block>
如果列表中项目的位置会动态改变或者有新的项目添加到列表中,并且希望列表中的项目保持自己的特征和状态(如 input 中的输入内容,switch 的选中状态),需要使用 wx:key 来指定列表中项目的唯一的标识符。(不需要用插值语法表示,直接使用即可)
wx:key 的值以两种形式提供
*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="" 来判断是否需要渲染该代码块:
<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>
因为 wx:if 是一个控制属性,需要将它添加到一个标签上。如果要一次性判断多个组件标签,可以使用一个 <block/> 标签将多个组件包装起来,并在上边使用 wx:if 控制属性。
<block wx:if="{{true}}">
<view> view1 </view>
<view> view2 </view>
</block>
注意: <block/> 并不是一个组件,它仅仅是一个包装元素,不会在页面中做任何渲染,只接受控制属性。
wx:if vs hiddenhidden: 为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可以在该文件中使用目标文件定义的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 目标文件中定义的 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 可以将目标文件除了 <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>
如bindtap,当用户点击该组件的时候会在该页面对应的 Page 中找到相应的事件处理函数。
<view id="tapTest" data-hi="Weixin" bindtap="tapName"> Click me! </view>
Page({
tapName: function(event) {
console.log(event)
}
})
事件分为冒泡事件和非冒泡事件:
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。
代表事件的类型。
页面打开到触发事件所经过的毫秒数。
触发事件的源组件。
| 属性 | 类型 | 说明 |
|---|---|---|
| id | String | 事件源组件的id |
| dataset | Object | 事件源组件上由data-开头的自定义属性组成的集合 |
事件绑定的当前组件。
| 属性 | 类型 | 说明 |
|---|---|---|
| id | String | 当前组件的id |
| dataset | Object | 当前组件上由data-开头的自定义属性组成的集合 |
说明: target 和 currentTarget 可以参考上例中,点击 inner view 时,handleTap3 收到的事件对象 target 和 currentTarget 都是 inner,而 handleTap2 收到的事件对象 target 就是 inner,currentTarget 就是 middle。
在组件节点中可以附加一些自定义数据。这样,在事件中可以获取这些自定义的节点数据,用于事件的逻辑处理。
在 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 // 大写会转为小写
}
})
在基础库版本 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 是一个数组,每个元素为一个 Touch 对象(canvas 触摸事件中携带的 touches 是 CanvasTouch 数组)。 表示当前停留在屏幕上的触摸点。
| 属性 | 类型 | 说明 |
|---|---|---|
| identifier | Number | 触摸点的标识符 |
| pageX, pageY | Number | 距离文档左上角的距离,文档的左上角为原点 ,横向为 X 轴,纵向为 Y 轴 |
| clientX, clientY | Number | 距离页面可显示区域(屏幕除去导航条)左上角距离,横向为 X 轴,纵向为 Y 轴 |
| 属性 | 类型 | 说明 | 特殊说明 |
|---|---|---|---|
| identifier | Number | 触摸点的标识符 | |
| x, y | Number | 距离 Canvas 左上角的距离,Canvas 的左上角为原点 ,横向为 X 轴,纵向为 Y 轴 |
changedTouches 数据格式同 touches。 表示有变化的触摸点,如从无变有(touchstart),位置变化(touchmove),从有变无(touchend、touchcancel)。
自定义事件所携带的数据,如表单组件的提交事件会携带用户的输入,媒体的错误事件会携带错误信息,详见组件定义中各个事件的定义。
点击事件的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(){}//监听页面卸载,一个页面只调用一次
npm install echarts --save
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]
}
]
});
通常来说,需要在 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>
如果图表容器不存在宽度和高度,或者,你希望图表宽度和高度不等于容器大小,也可以在初始化的时候指定大小。
<div id="main"></div>
<script type="text/javascript">
var myChart = echarts.init(document.getElementById('main'), null, {
width: 600,
height: 400
});
</script>
监听图标容器的大小并改变图表大小
<script type="text/javascript">
var myChart = echarts.init(document.getElementById('main'));
window.onresize = function() {
myChart.resize();
};
</script>
除了直接调用 resize() 不含参数的形式之外,还可以指定宽度和高度,实现图表大小不等于容器大小的效果。
myChart.resize({
width: 800,
height: 400
});
假设页面中存在多个标签页,每个标签页都包含一些图表。当选中一个标签页的时候,其他标签页的内容在 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)
},
var chart = echarts.init(dom, 'dark');
其他的主题,没有内置在 ECharts 中,需要自己加载
在echarts资源/主题构建工具中下载需要的主题并引入,在echarts初始化时,使用主题。可以使用多个不同的主题。例如:
let myChart = echarts.init(document.getElementById("School"),'macarons');


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')
调色盘,可以在 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'
]
// ...
}
]
};
在鼠标悬浮到图形元素上时,一般会出现高亮的样式。默认情况下,高亮的样式是根据普通样式自动生成的。但是高亮的样式也可以自己定义,主要是通过 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,
},
],
直接的样式设置是比较常用设置方式。纵观 ECharts 的 option 中,很多地方可以设置 itemStyle、lineStyle、areaStyle、label 等等。这些的地方可以直接设置图形元素的颜色、线宽、点的大小、标签的文字、标签的样式等等。上面的示例中已举例
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
]
}
]
};

在 Apache ECharts 的图表中用户的操作将会触发相应的事件。开发者可以监听这些事件,然后通过回调函数做相应的处理。在 ECharts 中事件分为两种类型,一种是用户鼠标操作点击,或者 hover 图表的图形时触发的事件,还有一种是用户在使用可以交互的组件后触发的行为事件,例如在切换图例开关时触发的 ‘legendselectchanged’ 事件(这里需要注意切换图例开关是不会触发 'legendselected' 事件的),数据区域缩放时触发的 ‘datazoom’ 事件等等。
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;
};
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 };
},
与vue2中watch配置功能一致
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 }
);
effect全称叫side effect,副作用。
什么是副作用呢,一个函数运行后产生了可以影响其外部或可以看到的效果,就叫副作用,比如document. Body. Append,alert再或者是showModel(在页面中展示一个弹层),或者window.open打开一个新窗口。
// 纯函数:如果函数的调用参数相同,则永远返回相同的结果。它不依赖于程序执行期间函数外部任何状态或数据的变化,必须只依赖于其输入参数。它不应修改程序的状态或引起副作用。
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)。
import { watchEffect, ref } from 'vue'
setup () {
const userID = ref(0)
watchEffect(() => console.log(userID))
setTimeout(() => {
userID.value = 1
}, 1000)
return {
userID
}
}
watch和watchEffect都可以侦听副作用,区别在于:
watchEffect 不需要指定监听的属性,他会自动的收集依赖, 只要我们回调中引用到了 响应式的属性, 那么当这些属性变更的时候,这个回调都会执行;而 watch 只能监听指定的属性而做出变更。watchEffect 是拿不到的。computed同理),而后收集到的依赖发生变化,这个回调才会再次执行;而 watch 不需要,因为他一开始就指定了依赖。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'
}
)
当 watchEffect 在组件的 setup() 函数或生命周期钩子被调用时,侦听器会被链接到该组件的生命周期,并在组件卸载时自动停止。
在一些情况下,也可以显式调用返回值以停止侦听:
const stop = watchEffect(() => {
/* ... */
})
stop()
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>