一、 @click.stop与@click.prevent
1.1 @click.stop
问题:父元素中添加了一个click事件,其下面的子元素中也添加了click事件,此时,我想点击子元素获取子元素的点击事件,但却触发的是父元素的事件:
此时,我们就需要使用@click.stop:阻止事件冒泡方法来解决这个问题
<view class="footer-box" @click="clickCard">
<view @click.stop="footerClick('喜欢')"><text class="footer-box__item">喜欢</text></view>
<view @click.stop="footerClick('评论')"><text class="footer-box__item">评论</text></view>
<view @click.stop="footerClick('分享')"><text class="footer-box__item">分享</text></view>
</view>
1.2 @click.prevent
@click.prevent:阻止事件的默认行为,例如:在代码里写入一个a标签,点击会跳转到目标链接网页中
<view class="example-body">
<a href="http://www.baidu.com">百度</a>
</view>
如果我们不想让它跳转但还想使用a标签的话,此时就需要使用@click.prevent方法:
<view class="example-body">
<a href="http://www.baidu.com" @click.prevent='notLink'>百度</a>
</view>
二、 ES6 对象解构
2.1 使用对象解构处理动态名称属性
将键作为参数传递时,可以编写一个返回User对象属性值的函数。使用[]来接受参数,js会根据这个键对从对象中检索!
const User = {
name: 'No Silver Bullet',
age: '18',
contact:{
phone:'10',
}
}
function getPropertyValue(key) {
const { [key]: returnValue } = User;
return returnValue;
}
const contact = getPropertyValue('contact');
const name = getPropertyValue('name');
console.log(contact, name); // 空 No Silver Bullet
2.2 在循环中使用对象解构
若遍历数组并想要使用每个员工对象的属性值。
const staff = [
{
'name': '爱分享的Alex',
'age': 16
},
{
'name': '搞前端的Alex',
'age': 18
},
{
'name': '敲代码的Alex',
'age': 20
}
];
for(let {name, age} of staff) {
console.log(`${name} 今年${age}岁!!!`);
}
三、build 过程取消 console、debugger
3.1 配置文件修改
修改build/webpack.prod.conf.js配置文件,找到UglifyJsPlugin配置,在compress中添加如下代码即可。
new UglifyJsPlugin({
uglifyOptions: {
compress: {
warnings: false,
// 打包的时候移除console、debugger
drop_debugger: true, // 移除debugger
drop_console: true, // 移除console
pure_funcs: ['console.log','console.info']
}
},
sourceMap: config.build.productionSourceMap,
parallel: true
}),
更优的配置方式如下:
new UglifyJsPlugin({
uglifyOptions: {
compress: {
warnings: false,
// 打包的时候移除console、debugger
drop_debugger: process.env.NODE_ENV=== 'production', // 移除debugger
drop_console: process.env.NODE_ENV=== 'production', // 移除console
warnings: process.env.NODE_ENV=== 'production', // 移除告警信息
pure_funcs: ['console.log','console.info']
}
},
sourceMap: config.build.productionSourceMap,
parallel: true
}),
四、 js map根据value获取key
paramsMap: {
orderType: {
'0': '咨询',
'1': '投诉',
'2': '举报',
'3': '建议',
'4': '求助',
'5': '表扬',
},
subjectType: {
'LB': '劳保',
'XW': '消委',
'GA': '公安',
'GT': '国土',
'CG': '城管',
'GJJ': '公积金',
'ZH': '综合',
},
},
4.1 根据key获取value
getParamValue(paramType, code) {
if (!Object.prototype.hasOwnProperty.call(this.paramsMap, paramType)) {
return '参数类型错误';
}
return this.paramsMap[paramType][code];
},
this.getParamValue('orderType', '1');
4.2 根据value获取key
getParamCode(paramType, value, compare = (a, b) => a === b) {
if (!Object.prototype.hasOwnProperty.call(this.paramsMap, paramType)) {
return '参数类型错误';
}
return Object.keys(this.paramsMap[paramType]).find(k => compare(this.paramsMap[paramType][k], value))
}
this.getParamCode('subjectType', '公安');
五、 集合 Set 和 Map
5.1 集合的概念
集合是由一组无序且唯一(元素不能重复)的项组成的。这个数据结构使用了与有限集合相同的数学概念,应用在计算机的数据结构中。特点:key和value相同,没有重复的value.
5.2 Set集合
ES6提供了数据结构set,它类似于数组,但是成员的值都是唯一的,没有重复的值 。Set 本身是一个构造函数,用来生成 Set 数据结构。
5.21 Set类的方法:
- Set.add(value)——Set.add(value) 添加一个数据,返回Set结构本身
- Set.delete(value) set.delete(value) 删除指定数据,返回一个布尔值,表示删除是否成功
- Set.has(value)——判断该值是否为set的成员,返回一个布尔值
- Set.clear()——清除所有的数据,没有返回值
5.22 Set的遍历器
- keys() 返回键名的遍历器,
- values() 返回键值的遍历器,
- entries() 返回键值对的遍历器
5.23 forEach() 使用回调函数遍历每个成员
const s = new Set([1,2,3,4]);
console.log(s.keys()); // SetIterator { 1, 2, 3, 4 }
console.log(s.values()); //SetIterator { 1, 2, 3, 4 }
console.log(s.entries()); //SetIterator { [ 1, 1 ], [ 2, 2 ], [ 3, 3 ], [ 4, 4 ] }
//该方法可以接收三个参数,分别是:键值,健名,set本身
s.forEach(function(value,key,set){
console.log(value+'lina');
console.log(set)
})
5.24 利用Set为数组去重——直接将要给数组放入到,set的构造方法中即可:
const arr = [1,2,3,45,2,3,4,13,5,7,1,3,2]
const s = new Set(arr);
console.log(s); // Set { 1, 2, 3, 45, 4, 13, 5, 7 }
console.log(arr); //[ 1, 2, 3, 45, 2, 3, 4, 13, 5, 7, 1, 3, 2 ]
console.log([...s]);//将set转成数组,结果是[ 1, 2, 3, 45, 4, 13, 5, 7 ]
5.3 Map数据结构
Map数据结构概述 字典:是用来存储不重复key的Hash结构,不同于集合(Set)的是,字典使用的是[键,值]的形式来存储数据的
5.31 Map类的方法:
- set(key,value) 设置键名key对应的键值为value,然后返回整个Map结构,如果key已经有值,则键值会被更新,否则就生成该键
- get(key)——get方法读取key对应的键值,如果找不到key,返回undefined
- delete(key) 删除某个键,删除成功放回true,如果删除失败,返回false
- has(key) 方法返回一个布尔值,表示某个键是否在当前Map对象之中
- clear() 清楚Map结构中的所有数据,没有返回值
Map的遍历器 keys() 返回键名的遍历器;values() 返回键值的遍历器;entries() 返回键值对的遍历器;forEach() 使用回调函数遍历每个成员
5.32 Map的遍历器
- keys() 返回键名的遍历器;
- values() 返回键值的遍历器;
- entries() 返回键值对的遍历器;
5.33 forEach() 使用回调函数遍历每个成员
const m = new Map([
['a',1],
['b',2],
['c',3]
]);
console.log(m.keys());//MapIterator { 'a', 'b', 'c' }
console.log(m.values()); //MapIterator { 1, 2, 3 }
console.log(m.entries()); //MapIterator { [ 'a', 1 ], [ 'b', 2 ], [ 'c', 3 ] }
//该方法可以接收三个参数:值,键,Map本身
m.forEach(function (value, key, Map) {
console.log(value);
console.log(key);
})
5.34 Map注意事项:
- 两个NaN本身是不相等的,但在Map结构中,NaN作为键的话,视为相等的(同一个键)
- 如果Map结构中的key是一个对象的情况下,每个对象都是不同的键,即使是两个空的对象,因为两个对象的地址值不同,以后引用别人的插件,使用对象作为键,就能
避免同名碰撞的情况 Map 结构转为数组结构——比较快速的方法是使用扩展运算符(…)。数组结构转为Map结构——将数组传入 Map 构造函数,就可以转为 Map- Map结构转为对象——如果所有 Map 的键都是字符串,它可以转为对象
const myMap = new Map();
myMap.set('yes', true)
myMap .set('no', false);
console.log(myMap); //Map { 'yes' => true, 'no' => false }
const obj = strMapToObj(myMap);
console.log(obj); //{ yes: true, no: false }
function strMapToObj(strMap) {
let obj = Object.create(null);
for (let [k,v] of strMap) {
obj[k] = v;
}
return obj;
}
- 对象转为Map结构
function objToStrMap(obj) {
let strMap = new Map();
for (let k of Object.keys(obj)) {
strMap.set(k, obj[k]);
}
return strMap;
}
const map = objToStrMap({yes: true, no: false});
console.log(map); //Map { 'yes' => true, 'no' => false }
- Map结构转为JSON对象 Map 转为 JSON 要区分两种情况。
一种情况是,Map 的键名都是字符串,这时可以选择转为对象 JSON。 需要先将其转成对象
function strMapToJson(strMap) {
return JSON.stringify(strMapToObj(strMap));
}
function strMapToObj(strMap) {
let obj = Object.create(null);
for (let [k,v] of strMap) {
obj[k] = v;
}
return obj;
}
let myMap = new Map().set('yes', true).set('no', false);
console.log(strMapToJson(myMap)); //{"yes":true,"no":false}
另一种情况是,Map 的键名有非字符串,这时可以选择转为数组 JSON
function mapToArrayJson(map) {
return JSON.stringify([...map]);
}
let myMap = new Map().set(true, 7).set({foo: 3}, ['abc']);
console.log(mapToArrayJson(myMap)); //[[true,7],[{"foo":3},["abc"]]]
JSON转为Map结构——JSON 转为 Map,正常情况下,所有键名都是字符串
function jsonToStrMap(jsonStr) {
return objToStrMap(JSON.parse(jsonStr));
}
function objToStrMap(obj) {
let strMap = new Map();
for (let k of Object.keys(obj)) {
strMap.set(k, obj[k]);
}
return strMap;
}
console.log(jsonToStrMap('{"yes": true, "no": false}')); //Map { 'yes' => true, 'no' => false }
有一种特殊情况,整个 JSON 就是一个数组,且每个数组成员本身,又是一个有两个成员的数组。这时,它可以一一对应地转为Map。这往往是数组转为 JSON 的逆操作。
function jsonToMap(jsonStr) {
return new Map(JSON.parse(jsonStr));
}
console.log(jsonToMap('[[true,7],[{"foo":3},["abc"]]]')); //Map { true => 7, { foo: 3 } => [ 'abc' ] }
六、 JavaScript 浮点数精度计算
6.1 浮点数精度问题
6.11 判断浮点数是否相等
//通过isEqual工具方法判断数值是否相等
function isEqual(number1, number2, digits){
digits = digits == undefined? 10: digits; // 默认精度为10
return number1.toFixed(digits) === number2.toFixed(digits);
}
console.log(isEqual(1.0-0.7, 0.3)); //true
//原型扩展方式,更喜欢面向对象的风格
Number.prototype.isEqual = function(number, digits){
digits = digits == undefined? 10: digits; // 默认精度为10
return this.toFixed(digits) === number.toFixed(digits);
}
console.log((1.0-0.7).isEqual(0.3)); //true
6.12 浮点数的运算
目录:/common/base.js
//加法函数,用来得到精确的加法结果
//说明:javascript的加法结果会有误差,在两个浮点数相加的时候会比较明显。这个函数返回较为精确的加法结果。
//调用:accAdd(arg1,arg2)
//返回值:arg1加上arg2的精确结果
const accAdd = (arg1,arg2) => {
var r1,r2,m;
try{r1=arg1.toString().split(".")[1].length}catch(e){r1=0}
try{r2=arg2.toString().split(".")[1].length}catch(e){r2=0}
m=Math.pow(10,Math.max(r1,r2))
return (arg1*m+arg2*m)/m
}
//给Number类型增加一个add方法,调用起来更加方便。
Number.prototype.add = function (arg){
return accAdd(arg,this);
}
//减法函数,用来得到精确的减法结果
//说明:javascript的加法结果会有误差,在两个浮点数相加的时候会比较明显。这个函数返回较为精确的减法结果。
//调用:accSub(arg1,arg2)
//返回值:arg1减去arg2的精确结果
const accSub = (arg1,arg2) => {
var r1,r2,m,n;
try{r1=arg1.toString().split(".")[1].length}catch(e){r1=0}
try{r2=arg2.toString().split(".")[1].length}catch(e){r2=0}
m=Math.pow(10,Math.max(r1,r2));
//last modify by deeka
//动态控制精度长度
n=(r1>=r2)?r1:r2;
return ((arg1*m-arg2*m)/m).toFixed(n);
}
//除法函数,用来得到精确的除法结果
//说明:javascript的除法结果会有误差,在两个浮点数相除的时候会比较明显。这个函数返回较为精确的除法结果。
//调用:accDiv(arg1,arg2)
//返回值:arg1除以arg2的精确结果
const accDiv = (arg1,arg2) => {
var t1=0,t2=0,r1,r2;
try{t1=arg1.toString().split(".")[1].length}catch(e){}
try{t2=arg2.toString().split(".")[1].length}catch(e){}
while(Math){
r1=Number(arg1.toString().replace(".",""))
r2=Number(arg2.toString().replace(".",""))
return (r1/r2)*Math.pow(10,t2-t1);
}
}
//给Number类型增加一个div方法,调用起来更加方便。
Number.prototype.div = function (arg){
return accDiv(this, arg);
}
//乘法函数,用来得到精确的乘法结果
//说明:javascript的乘法结果会有误差,在两个浮点数相乘的时候会比较明显。这个函数返回较为精确的乘法结果。
//调用:accMul(arg1,arg2)
//返回值:arg1乘以arg2的精确结果
const accMul = (arg1,arg2) => {
var m=0,s1=arg1.toString(),s2=arg2.toString();
try{m+=s1.split(".")[1].length}catch(e){}
try{m+=s2.split(".")[1].length}catch(e){}
return Number(s1.replace(".",""))*Number(s2.replace(".",""))/Math.pow(10,m)
}
//给Number类型增加一个mul方法,调用起来更加方便。
Number.prototype.mul = function (arg){
return accMul(arg, this);
}
页面引用:
import { accAdd, accSub, accMul, accDiv } from '@/common/base.js';
console.log(accAdd(1.79, 0.12)); //1.91
console.log(accSub(1.79, 0.12)); //1.67
console.log(accMul(1.79, 0.12)); //0.2148
console.log(accDiv(1.79, 0.12)); //14.916666666666666
6.2 整数精度问题
在 JavaScript 中 Number类型统一按浮点数处理,整数是按最大54位来算最大(253 - 1,Number.MAX_SAFE_INTEGER,9007199254740991) 和最小(-(253 - 1),Number.MIN_SAFE_INTEGER,-9007199254740991)安全整数范围的。所以只要超过这个范围,就会存在被舍去的精度问题。
6.21、解决方案
6.211 类库
通常这种对精度要求高的计算都应该交给后端去计算和存储,因为后端有成熟的库来解决这种计算问题。
前端有几个不错的类库:
Math.js
Math.js 是专门为 JavaScript 和 Node.js 提供的一个广泛的数学库。它具有灵活的表达式解析器,支持符号计算,配有大量内置函数和常量,并提供集成解决方案来处理不同的数据类型 像数字,大数(超出安全数的数字),复数,分数,单位和矩阵。 功能强大,易于使用。
- decimal.js
为 JavaScript 提供十进制类型的任意精度数值。
- 4.1.3 big.js
七、# $OPTIONS
vue实例属性$options用来获取定义在data外的数据和方法。
<script>
export default {
name: "optionsTest",
data() {
return {
};
},
//在data外面定义的属性和方法通过$options可以获取和调用
name: "CSDN",
age: 12,
testMethod() {
console.log("shq5785");
},
created() {
console.log(this.$options.name); // CSDN
console.log(this.$options.age); //12
this.$options.testMethod(); // shq5785
},
</script>
八、 应用 qs 插件实现参数格式化
8.1 在vue项目开发过程中,使用axios请求后台时,后台无法获取前端传参数据。
8.2 qs 是一个增加了一些安全性查询字符串解析和序列化字符串的库。
8.21
安装qs
npm install qs
8.22 在组件中应用
import qs from 'qs'
或定义为全局组件:
main.js引入qs
import qs from 'qs'
全局属性配置,在任意组件内可以使用this.$qs获取qs对象
Vue.prototype.$qs = qs
8.23 具体应用
主要使用qs.parse(),qs.string。
- qs.parse()是将URL
解析成对象形式; - qs.stringify()是将
对象序列化成URL的形式,以&进行拼接;
例子:
this.username = 'alex';
this.password = '123456';
let data = qs.stringify({
"username":this.username,
"password":this.password
});
序列化后的结构如下:
username=alex&password=123456
8.24 解析
8.241 解析对象
//解析对象
let res = this.$qs.parse('query[name]=zhangsan')
console.log(res)
//控制台输出结果:{query:{name:'zhangsan'}}
//解析嵌套对象
res = this.$qs.parse('query[name][nickname]=小三儿')
console.log(res)
//控制台输出结果:{query:{name:{nickname:'小三儿'}}}
//当使用嵌套对象时,qs 在默认情况下最多解析到的深度是第五层(注:从第一个方括号到算起,到第五个方括号)
res = this.$qs.parse('a[b][c][d][e][f][g][h][i]=j')
console.log(res)
//控制台输出结果:
// a: {
// b: {
// c: {
// d: {
// e: {
// f: {
// '[g][h][i]': 'j'
// }
// }
// }
// }
// }
// }
// depth 参数指定解析深度
//当 qs 用于解析用户输入的时候,解析深度的限制有助于减轻用户的滥用行为
res = this.$qs.parse('a[b][c][d][e][f][g][h][i]=j', { depth: 1 })
console.log(res)
//控制台输出结果:
// a: {
// b: {
// {[c][d][e][f][g][h][i]:'j'}
// }
// }
// }
// ignoreQueryPrefix忽略查询字符串开头的 ?
res = this.$qs.parse('?a=b&c=d', { ignoreQueryPrefix: true })
console.log(res)
//控制台输出结果:{a:'b':c:'d'}
8.242 解析数组
//用[]解析数组
let res = this.$qs.parse('a[]=b&a[]=c')
console.log(res)
//控制台输出结果:{a:['b','c']}
//指定数组索引
res = this.$qs.parse('a[0]=b&a[1]=c')
console.log(res)
//控制台输出结果:{a:['b','c']}
//要将字符串解析成数组而不是对象,那么[]之间的值必须是一个数字
//在创建具有特定索引的数组时,qs会将稀疏数组压缩为仅保留其顺序的现有值
res = this.$qs.parse('a[1]=b&a[15]=c')
console.log(res)
//控制台输出结果:{a:['b','c']}
//空字符串也是一个值,并将被保留
res = this.$qs.parse('a[]=&a[]=b')
console.log(res)
//控制台输出结果:{a:['','c']}
res = this.$qs.parse('a[0]=b&a[1]=&a[2]=c')
console.log(res)
//控制台输出结果:{a:['b',','c']}
//qs 限制数组最大索引为 20,任何索引大于20的数组成员都将被转换为以索引为键的对象
res = this.$qs.parse('a[100]=b')
console.log(res)
//控制台输出结果:{a:['100','b']}
//arrayLimit 选项可以修改默认限制
res = this.$qs.parse('a[1]=b', { arrayLimit: 0 })
console.log(res)
//控制台输出结果:{a:['1','b']}
//设置 parseArrays 为 false,字符串不解析成数组
res = this.$qs.parse('a[1]=b', { parseArrays: false })
console.log(res)
//控制台输出结果:{a:{'1','b'}}
//如果混合使用两种格式,qs 会将字符串解析为对象:
res = this.$qs.parse('a[0]=b&a[b]=c')
console.log(res)
//控制台输出结果:{a:{'0':'b',b:'c'}
//创建元素为对象的数组
res = this.$qs.parse('a[][b]=c')
console.log(res)
//控制台输出结果:{a:[{b:'c'}]}
8.243 序列化
//对象序列化后
let res = this.$qs.stringify({ a: 'b' })
console.log(res)
//控制台输出结果:a=b
//对象序列化后进行URI编码后输出
res = this.$qs.stringify({ a: { b: 'c' } })
console.log(res)
//控制台输出结果:a%5Bb%5D=c
res = this.$qs.parse(res)
console.log(res)
//控制台输出结果:{a:{b:c}}
//设置 encode 为 false,禁止URI编码
res = this.$qs.stringify({ a: { b: 'c' } },{encode:false})
console.log(res)
//控制台输出结果:a[b]=c
//设置 encodeValuesOnly 为 true,禁止URI编码
res = this.$qs.stringify( { a: 'b', c: ['d', 'e=f'], g: [['h'], ['i']] }, { encodeValuesOnly: true })
console.log(res)
//控制台输出结果:a=b&c[0]=d&c[1]=e%3Df&g[0][0]=h&g[1][0]=i
//当 encode 被设置为true时,设置encoder 选项自定义编码方式
res = this.$qs.stringify( { a: { b: 'c' } },{encoder:function (str) {
return "a-b-c"
}})
console.log(res)
//控制台输出结果:a-b-c=a-b-c
//当数组被序列化时,默认显示索引
res = this.$qs.stringify({ a: ['b', 'c', 'd'] })
console.log(res)
//控制台输出结果:a%5B0%5D=b&a%5B1%5D=c&a%5B2%5D=d 即:a[0]=b&a[1]=c&a[2]=d
//设置 indices 为 false 不显示索引
res = this.$qs.stringify({ a: ['b', 'c', 'd'] },{indices:false})
console.log(res)
//控制台输出结果:a=b&a=c&a=d
//设置 arrayFormat 选项指定数组输出格式
res = this.$qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'indices' })
console.log(res)
//控制台输出结果:a%5B0%5D=b&a%5B1%5D=c 即:a[0]=b&a[1]=c
res = this.$qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'brackets' })
console.log(res)
//控制台输出结果:a%5B%5D=b&a%5B%5D=c 即:a[]=b&a[]=c
res = this.$qs.stringify({ a: { a: ['b', 'c'] }},{ arrayFormat: 'repeat' })
console.log(res)
//控制台输出结果:a%5Ba%5D=b&a%5Ba%5D=c 即:a[a]=b&a[a]=c
//对象序列化时,默认使用 []
res = this.$qs.stringify({ a: { b: { c: 'd', e: 'f' } } })
console.log(res)
//控制台输出结果:a%5Bb%5D%5Bc%5D=d&a%5Bb%5D%5Be%5D=f 即:a[b][c]=d&a[b][e]=f
//空字符串和null值将被省略,但是=会保留
//值为 undefined 的属性将会被完全忽略
//默认情况下,null 值被视为空对象
res = this.$qs.stringify({ a:'',b: null, c: undefined })
console.log(res)
//控制台输出结果:a=&b=
//没有值的键将什么也不返回(例如空对象或数组)
res = this.$qs.stringify({ a: [] } )
console.log(res)
//控制台输出结果:
res = this.$qs.stringify({ a: {} } )
console.log(res)
//控制台输出结果:
res = this.$qs.stringify({ a: [{}] } )
console.log(res)
//控制台输出结果:
res = this.$qs.stringify({ a: { b: []} } )
console.log(res)
//控制台输出结果:
res = this.$qs.stringify({ a: { b: {}} } )
console.log(res)
//控制台输出结果:
//ddQueryPrefix 设置为 true可以在查询字符串前面加 ?
res = this.$qs.stringify({ a: 'b', c: 'd' }, { addQueryPrefix: true })
console.log(res)
//控制台输出结果:?a=b&c=d
//序列化日期对象
var date = new Date(7);
res = this.$qs.stringify({ a: date })
console.log(res)
//控制台输出结果:a=1970-01-01T00%3A00%3A00.007Z //%3A对应的字符为:
res = this.$qs.stringify({ a: date }, { serializeDate: function (d) { return d.getTime(); } })
console.log(res)
//控制台输出结果:a=7
//使用 sort 选项来修改键的顺序
function alphaSort(a, b) {
return a.localeCompare(b);
}
res = this.$qs.stringify({ a: 'c', z: 'y', b : 'f' }, { sort: alphaSort })
console.log(res)
//控制台输出结果:a=c&b=f&z=y
九、 父子组件元素获取、方法互相调用
9.1 父组件访问子组件(推荐 $refs)
9.11 使用 $children
在父组件中使用 this.$children 拿到的是一个数组类型,它包含所有子组件实例。
<div id="app">
<cpn></cpn>
<cpn></cpn>
<button @click="btnClick">按钮</button>
</div>
<template id="cpn">
<div>
<h1>我是子组件</h1>
</div>
</template>
<script>
let vm = new Vue({
el: "#app",
data: {},
methods: {
btnClick() {
//1.拿到所有子组件,是一个数组
console.log(this.$children);
//2.拿到一个组件实例,可以直接访问子组件中的方法和 data 中的数据
this.$children[0].showMessage();
console.log(this.$children[0].name);
}
},
components: {
cpn: {
template: '#cpn',
data() {
return {
name: 'webchang'
}
},
methods: {
showMessage() {
console.log('我是子组件');
}
}
}
}
});
</script>
9.12 使用 $refs
使用$children 的缺陷如下:
通过 $children 访问子组件时,是一个数组类型,访问其中的子组件必须通过索引值。
但是当子组件过多,我们需要拿到其中一个时,往往不能确定它的索引值,甚至还可能会发生变化。
有时候,我们想明确获取其中一个特定的组件,这个时候就可以使用 $refs
$refs 的使用
通过设置子组件的ref,父组件通过this.$refs.xxx.method_name(data)调用子组件方法,data参数可选。
$refs 和 ref 指令通常是一起使用的。
首先,我们在子组件上添加一个 ref 属性,相当于给某一个子组件绑定一个特定的ID。
其次,this.$refs 拿到的是所有标有 ref 属性的子组件(如果一个子组件实例没有 ref 属性,通过这种方式是拿不到的),最后拿到的是一个对象,属性名是子组件实例的 ref 属性,属性值是该组件实例。
通过 this.$refs.ID 就可以访问到该组件。
示例代码:
<div id="app">
<cpn ref="child1"></cpn>
<cpn ref="child2"></cpn>
<!-- 这个子组件实例没有 ref 属性,通过 this.$refs 方式拿不到这个组件实例 -->
<cpn></cpn>
<button @click="btnClick">按钮</button>
</div>
<template id="cpn">
<div>
<h1>我是子组件</h1>
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
let vm = new Vue({
el: "#app",
data: {
message: "hello"
},
methods: {
btnClick() {
console.log(this.$refs)
console.log(this.$refs.child1)
console.log(this.$refs.child1.name)
this.$refs.child1.showMessage('父组件')
}
},
components: {
cpn: {
template: '#cpn',
data() {
return {
name: 'webchang'
}
},
methods: {
showMessage(value) {
console.log("子组件方法被调用,调用者:" + value)
}
}
}
}
});
</script>
9.2 子组件调用父组件方法(推荐 $emit)
9.21 方法一:this.$parent.event
直接在子组件中通过this.$parent.event来调用父组件的方法。示例代码:
父组件
<template>
<div>
<child></child>
</div>
</template>
<script>
import child from './components/dam/child';
export default {
components: {
child
},
methods: {
fatherMethod(value) {
console.log("父组件方法被调用,调用者:" + value)
}
}
};
</script>
子组件
<template>
<div>
<button @click="childMethod()">点击</button>
</div>
</template>
<script>
export default {
methods: {
childMethod() {
this.$parent.fatherMethod('子组件');
}
}
};
</script>
9.22 方法二: $emit
在子组件里用$emit向父组件触发一个事件,父组件监听这个事件。
父组件
<template>
<div>
<child @fatherMethod="fatherMethod"></child>
</div>
</template>
<script>
import child from '~/components/dam/child';
export default {
components: {
child
},
methods: {
fatherMethod(id) {
console.log('测试传值',id);
}
}
};
</script>
子组件
<template>
<div>
<button @click="childMethod()">点击</button>
</div>
</template>
<script>
export default {
methods: {
childMethod() {
this.$emit('fatherMethod',id:1);
}
}
};
</script>
9.23 方法三:方法传参
父组件把方法传入子组件中,在子组件里直接调用这个方法。
父组件
<template>
<div>
<child :fatherMethod="fatherMethod"></child>
</div>
</template>
<script>
import child from '~/components/dam/child';
export default {
components: {
child
},
methods: {
fatherMethod() {
console.log('测试');
}
}
};
</script>
子组件
<template>
<div>
<button @click="childMethod()">点击</button>
</div>
</template>
<script>
export default {
props: {
fatherMethod: {
type: Function,
default: null
}
},
methods: {
childMethod() {
if (this.fatherMethod) {
this.fatherMethod();
}
}
}
};
</script>
十、 Vue 项目调试技能
JetBrains系列WebStorm下Vue项目进行调试的2种方法:debugger和Vue-devtools。
10.1 debugger
debugger是谷歌浏览器提供的调试语句,其主要是通过停止JS的执行,相当于设置断点。它的使用方法很简单, 只需要在我们的JS语句中, 插入一行debugger; 即可。
在JS代码编写的过程中,我们都会通过浏览器的调试模式(F12)来检查代码逻辑是否正确,大多数我们都是通过设置断点来进行调试。
应用debugger调试Vue项目,需要在项目中需要的位置写debugger,项目运行后,打开浏览器按F12,在chrome sources页签中就会直接进入断点,至此可以进行单步、跳步调试了。
10.2 Vue-devtools
该调试工具为针对Chrome浏览器而设计的开源调试工具(Github地址),可以自行将该项目下载下来然后编译,并将生成后的chrome插件安装至chrome中,步骤如下: 找到谷歌浏览器的扩展程序功能,勾选开发者模式,然后我们将插件文件夹里的shells>chorme文件夹直接拖到页面中,完成安装。
- 应用
devtools调试工具,还需要在vue项目中man.js配置:
Vue.config.devtools = true;
- 安装后, 需要关闭浏览器, 再重新打开, 才能使用;
- 如果调试插件安装后,
vue面板未出现,再到vue-devtools文件夹下执行一遍npm run dev。