文章目录
- 上午 - 9点 - 12点 - 3小时
- 第44个视频 - javascript高阶函数的使用
- 第45个视频 - v-model的使用和原理
- 第46个视频 - v-model结合radio类型使用
- 第47个视频 - v-model和checkbox的结合使用
- 第48个视频 - v-model结合select类型的使用
- 第49个视频 - input中的值绑定
- 第50个视频 - v-model的修饰符的使用
- 第51个视频 - 组件化的实现和使用步骤
- 第52个视频 - 组件化的基本使用过程
- 第53个视频 - 全局组件和局部组件
- 下午 - 14:30 - 18:00 - 3个半小时
- 第54个视频 - 父组件和子组件的区分
- 第55个视频 - 注册组件的语法糖写法
- 第56个视频 - 组件template抽离的写法
- 第57个视频 - 为什么组件data必须是函数
- 第58个视频 - 父子组件通信 - 父传子 - props
- 第59个视频 - 父子组件通信 - props驼峰标识
- 第60个视频 - 父子组件通信 - 子传父(自定义事件)
- 第61个视频 - 项目演示
- 第62个视频 - 知识回顾
上午 - 9点 - 12点 - 3小时
第44个视频 - javascript高阶函数的使用
第一种方法:
第二种方法:
第三种方法:reduce
编程范式的分类有:命令式编程,声明式编程。
编程范式的分类有:面向对象编程(js当中多态体现太灵活,TypeScript跟java很像,也有接口),函数式编程。
面向对象编程的第一公民是对象。
函数式编程的第一公民是函数。
函数式编程的好处是可以进行很多的链式编程。
举个例子,下面的需求用传统的方法,就没有函数式编程简单。
像是上面的做法,传统的做法,是挺麻烦的。
有三个高阶函数:filter、map、reduce。
什么是高阶函数呢?就是函数的参数,也是一个函数,这种套路。
filter函数
map函数
reduce函数
看一下reduce的源码:
我们看到源码当中定义了两个函数,源码是typescript的,d表示的是定义的意思,definition。
在js当中如果定义了两个函数,下面的函数,会把上面的函数覆盖掉。
但是它这里不会被覆盖。因为它是typescript。
定义两个函数,叫做函数的重载。
我们看上面的源码可以知道,reduce函数可以传入两个参数,一个是回调函数,一个是初始化值。
需求用高阶函数实现
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script src="../js/vue.js"></script>
<script>
const nums = [10, 20, 111, 222, 444, 40, 50];
/*
* filter函数是会自动遍历数组的
* filter函数的参数,要求传入一个回调函数。
* filter的回调函数必须返回一个布尔值。
* 如果返回的是true,函数内部会自动将这次回调的n,加入到新的数组中。
* 如果返回的是false,函数内部就会自动过滤掉n。
* 场景:如果你需要对一个数组进行过滤,就使用filter
* */
let newNums = nums.filter(function (n) {
return n < 100;
});
console.log(newNums); // [10. 20. 40, 50]
/*
* map函数是会自动遍历数组的。
* map函数的参数,要求传入一个回调函数
* 每个遍历的元素都会被回调函数处理成新的值,
* 然后新的值会组成新的数组。
* 场景:如果你需要对一个数组当中的每个元素做统一的操作,就使用map
* */
let new2Nums = newNums.map(function (n) {
return n * 2;
});
console.log(new2Nums); // [20, 40, 80, 100]
/*
* reduce函数是会自动遍历数组的。
* 源码中关于reduce有两个定义。
* 1、reduce函数的参数,要求传入1个参数,是回调函数
* 2、reduce函数的参数,要求传入2个参数,一个是回调函数,一个是初始化值。
* 这个回调函数有两个值,第一个参数上一次返回的值previousValue,第二个参数是当下的值currentValue。
* reduce函数的返回是一个值。
* 场景:如果你需要对数组中所有元素进行汇总,所有相乘,所有相加等场景。
* */
let new3Nums = new2Nums.reduce(function (preValue, n) {
return preValue + n
}, 0);
console.log(new3Nums); // 240
// 假设new2Nums = [20, 40, 80]
// 第一次:preValue - 0 , n - 20 return x
// 第二次:preValue - x ,n - 40 return y
// 第三次:preValue - y , n - 80 return z
</script>
</body>
</html>
链式编程
上面的代码,阅读性还是比较差的。
我们还可以这样写:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script src="../js/vue.js"></script>
<script>
const nums = [10, 20, 111, 222, 444, 40, 50];
let total = nums.filter(function (n) {
return n < 100;
}).map(function (n) {
return n * 2;
}).reduce(function (preValue, n) {
return preValue + n;
}, 0);
</script>
</body>
</html>
这就叫做函数式编程。函数就是函数编程的第一公民。
箭头函数
用箭头函数的话,还能够简化上面的链式编程代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script src="../js/vue.js"></script>
<script>
const nums = [10, 20, 111, 222, 444, 40, 50];
let total = nums.filter(n => n < 100).map(n => n * 2).reduce((pre, n) => pre + n);
</script>
</body>
</html>
之前的购物车案例总价格计算使用reduce
考试
问题:三个高阶函数,filter,map,reduce都是用在什么场景。
答案:在数组或对象遍历的场景,filter是对每个元素需要过滤的场景,map是对每个元素统一进行一个操作的场景,reduce是对每个元素进行汇总的场景。
第45个视频 - v-model的使用和原理
数据已经被绑定到了input里面了。
-
之前我们是通过 mustache语法将data当中的数据,绑定到了某个元素的内容的位置。
-
现在我们是在表单元素的标签属性里面添加了v-model之后,就把data当中的数据绑定到了表单当中。
而且v-model是一种双向绑定。
- 以前我们的mustache是一种响应式。就是将data里面的数据,在页面上面显示。
- 但是界面上的数据改变,并不会影响我们的data当中的数据。
- 现在我们的v-model是一种双向绑定,我们将data里面的数据和表单元素进行了绑定。
- 我们的data数据发生改变,我们的表单元素当中的内容,也会发生改变。
- 我们的表单元素当中的数据发生改变,我们的data当中的数据也会发生改变。
示例代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<input type="text" v-model="message">
{{message}}
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '张润'
},
})
</script>
</body>
</html>
v-model的原理和本质
先自己手动来实现双向绑定。
第一步,先将data里面的message,绑定到input当中,这个要用到动态绑定属性。
第二步,我们需要把input当中用户输入的值,绑定到我们的message上。
input输入框是有一个默认的事件的,这个事件就叫做input。
就好像button按钮有一个事件叫做点击,叫做click是一样的。
我们可以在input输入框上面监听它的input事件,然后拿到用户输入的值。
监听它的input事件,我们可以通过v-on来实现,那么怎么拿到用户输入的值呢?
当页面上产生了事件的时候,浏览器会为我们生成一个事件对象event。
这个event当中的target中的value,这里就是保存着用户在input输入框中的输入。
具体的代码就如下所示:
具体的代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<!--<input type="text" v-model="message">-->
<input type="text" :value="message" @input="valueChange">
<h2>{{message}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '江疏影'
},
methods: {
valueChange(event) {
this.message = event.target.value;
}
}
});
</script>
</body>
</html>
对input的小小总结
第一点:我们之前学过label和input的绑定。label标签的for属性和input标签的id属性设置为一个值,这样就可以将label和input进行绑定。
这样拿到的效果,就是点击label的文字,input自动获得聚焦。input的value属性就是显示的文本,input的placeholder就是没有输入的时候默认显示的文本。
第二点:input输入框是有一个对应的事件就是input,用户输入的值,就是浏览器event对象target的value属性值中保存。
下面的截图就是浏览器的input事件对象:
根据我的测试,现在浏览器当中,用户的输入产生的事件对象是InputEvent,数据是存放在event.data属性当中。
第三点:我们的v-bind到现在为止,遇到了这么几种情况。
- 动态绑定属性的:
- img标签的src
- a标签的href
- button标签的disabled
- input标签的value。
- 动态绑定样式的,倒是还没有遇到什么玩意。
第四点:我们的事件监听,现在也遇到了这么几种情况:
- button或者div的click事件
- submit的提交事件,我们通过.stop修饰符。
- 事件冒泡的时候,我们通过.prevent修饰符。
- 键盘输入的事件,我们通过.{keyCode},最经典的就是enter的事件。
- 组件的点击事件。
- input的input事件。
这给我提了个醒,需要好好研究一下浏览器事件对象
。
回归v-model的本质
我们刚才通过了v-on事件监听input事件,通过v-bind绑定input的value属性。
通过这两个指令,实现了v-model的效果。
我们对上面的代码修改一下:
我们事件绑定的时候,不要用方法,我们直接用表达式:
v-model的本质总结
考试
问题:v-model的本质可以相当于哪两个指令的结合?
答案:v-bind和v-on
第46个视频 - v-model结合radio类型使用
radio比较常见的就是让用户选择一下性别男女这种场景。
表单
- input:text、radio
- textarea
input radio和v-model结合
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<label for="male">
<input type="radio" id="male" name="sex" value="男" v-model="sex">男
</label>
<label for="female">
<input type="radio" id="female" name="sex" value="女" v-model="sex">女
</label>
<h2>您选择的性别是:{{sex}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '',
sex: ''
},
})
</script>
</body>
</html>
小知识点
- 如上面的例子,一旦两个input radio都使用v-model绑定了同一个数据,那么radio的name属性可以没有。
- html当中,radio是必须加上name属性,才能够保障单选框的互斥。
- 如果在data的sex默认值是一个
男
,这样男对应的radio,就是默认选中的。
考试
问题:input radio都有哪些常见的属性值?
答案:
- type:radio、text
- id:和label的for配合
- name:radio互斥
- value:选中的值
第47个视频 - v-model和checkbox的结合使用
checkbox是复选框,是比较特殊。
两种情况。
单个单选框和多选框。
v-model结合单选框
单选框会在什么场景用到呢?
就是很多网站有:同意协议,这种地方用到。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<label for="agree">
<input type="checkbox" id="agree" v-model="isAgree">同意协议
</label>
<h2>您选择的是:{{userAgree}}</h2>
<button :disabled="!isAgree">下一步</button>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '',
isAgree: false
},
computed: {
userAgree(){
if (this.isAgree){
return '同意协议'
}else{
return '不同意协议'
}
}
}
})
</script>
</body>
</html>
效果图如下:
v-model结合多选框
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<input type="checkbox" value="张润" v-model="hobbies">张润<br>
<input type="checkbox" value="王柯" v-model="hobbies">王珂<br>
<input type="checkbox" value="赵柯" v-model="hobbies">赵柯<br>
<input type="checkbox" value="梁缘" v-model="hobbies">梁缘<br>
<input type="checkbox" value="舒砚" v-model="hobbies">舒砚<br>
<input type="checkbox" value="于越" v-model="hobbies">于越<br>
<input type="checkbox" value="周放" v-model="hobbies">周放<br>
<h2>您喜欢的美女是:{{hobbies}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '',
hobbies: []
},
})
</script>
</body>
</html>
效果图如下:
考试
问题:单选框,v-model对应的是data当中的什么类型的数据?多选框,v-model对应的是data当中的什么类型的数据?
答案:单选框是boolean值,多选框是数组。
第48个视频 - v-model结合select类型的使用
select类型就是下拉框。
降到这里的时候,王红元开始闲聊。
说郭德纲相声当中,问了捧哏一个问题:你知道曹操的父亲是谁吗?
是曹香蕉。因为三国里面有一句话是我与你父相交甚好
。
选择一个的效果
选择一个的代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<!--选择一个-->
<!--现在网站,展示select这种类型的玩意,很少,现代的网站。-->
<select name="abc" id="" v-model="pretty">
<option value="张芷溪">张芷溪</option>
<option value="唐艺昕">唐艺昕</option>
<option value="孙嘉璐">孙嘉璐</option>
<option value="谈莎莎">谈莎莎</option>
<option value="张熙媛">张熙媛</option>
<option value="郑罗茜">郑罗茜</option>
<option value="王文娜">王文娜</option>
</select>
<h2>您选择的美女是:{{pretty}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '',
pretty: '王文娜'
},
})
</script>
</body>
</html>
选择多个的效果
按住ctrl键,选择多个
选择多个的代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<!--选择多个-->
<select name="abc" v-model="pretty" multiple>
<option value="张芷溪">张芷溪</option>
<option value="唐艺昕">唐艺昕</option>
<option value="孙嘉璐">孙嘉璐</option>
<option value="谈莎莎">谈莎莎</option>
<option value="张熙媛">张熙媛</option>
<option value="郑罗茜">郑罗茜</option>
<option value="王文娜">王文娜</option>
</select>
<h2>您选择的美女是:{{pretty}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '',
pretty: []
},
})
</script>
</body>
</html>
考试
问题:select怎么设置选择多个?
答案:在select标签当中添加multiple属性。
第49个视频 - input中的值绑定
讲一个概念。
王红元看官方网站的时候,看到的值绑定
。
一开始,不知道值绑定什么意思?
后来发现,其实非常简单。
值绑定的意思就是从一个最开始的数据当中读取值,然后进行绑定,然后进行v-model的双向数据绑定。
值绑定的效果
值绑定的代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<label v-for="item in pretties" :for="item">
<input type="checkbox" :value="item" :id="item" v-model="pretty">{{item}}<br>
</label>
<h2>您选择的美女是:{{pretty}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '',
pretty: [],
pretties: ['张芷溪', '唐艺昕', '孙嘉璐', '谈莎莎', '张熙媛', '郑罗茜', '王文娜']
},
})
</script>
</body>
</html>
总结
考试
问题:从以前到现在,王红元演示过哪些v-bind绑定属性的例子了
答案:img标签的src,a标签的href,button标签的disabled,input标签的value、id,label的for属性
第50个视频 - v-model的修饰符的使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<!--1、.lazy修饰符-->
<!--现在的方式,动态绑定太频繁了,用户还没有输入完成,你就绑定了,你是实时绑定的-->
<!--.laze修饰符使用的场景就是:用户输入完成了之后敲了回车,或者input输入框失去焦点的时候-->
<input type="text" v-model.lazy="message">
<h2>您的输入是:{{message}}</h2>
<!--2、.number修饰符-->
<!--默认的input的数据输入,v-model绑定的时候得到的就是string类型-->
<!--我们的需求场景是,想要拿到用户的输入是一个number类型。-->
<input type="number" v-model.number="age">
<h2>您的输入是:{{age}},数据类型是{{typeof age}}</h2>
<!--3、.trim修饰符-->
<!--意思就是去掉首尾空格。-->
<input type="text" v-model.trim="person">
<h2>您的输入是:{{person}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '孟子义',
age: 26,
person: '',
},
});
</script>
</body>
</html>
到这里为止,基础语法的部分就讲解完成了:
考试
问题:罗列三个v-model的修饰符
答案:lazy、number、trim。
第51个视频 - 组件化的实现和使用步骤
- angular的组件化思想是:一个模块是一个组件。
- 数据结构有很多:栈、堆、链表、图、树
- 树结构当中,用得最多的是
二叉树
。任何的树结构都可以被抽象成为一个二叉树的。 二叉树
当中,用得比较多的,就是二叉搜索树
。二叉搜索树
当中为了保障树的平衡的话,还有二三四树
、红黑树
。- 有机会可以学习一下,参考王红元的简书。面试大公司,面试数据结构还是很多的。
- 树结构当中,用得最多的是
- 这是一个组件化的思想,后面还会降到一个模块化的思想,要注意进行区分对比。
es6当中继承的写法
考试
问题:组件化和模块化是一个东西吗?
答案:不是。
第52个视频 - 组件化的基本使用过程
注册组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<cpn></cpn>
</div>
<script src="../js/vue.js"></script>
<script>
// ES6语法
// 以前在es5当中定义字符串,可以使用单引号,也可以使用双引号。
// 这时候换行必须搞个+号
// const str = 'abc' +
// 'abc';
// const str2 = `abc
// cdefghjklmnopqrstuvwxyz`
// 在es6当中,可以使用反引号。``。 支持换行的。
//1、创建组件构造器对象
const cpnConstructor = Vue.extend({
template: `
<div>
<h2>我是孟子义</h2>
<p>我是北京电影学院校花</p>
<p>我长得真漂亮</p>
</div>`
});
//2、注册组件
// Vue.component('组件的标签名',组件构造器)
Vue.component('cpn', cpnConstructor);
//3、使用组件 <cpn></cpn>
const app = new Vue({
el: '#app',
data: {
message: ''
},
});
</script>
</body>
</html>
效果如下:
考试
问题:注册组件的三个步骤是什么?
答案:第一步:创建组件构造器,Vue.extend(),传入一个参数就是对象,对象里面写template属性,属性值是html代码。
第二步:注册逐渐,使用Vue.components(),传入两个参数,第一个参数是自定义标签字符串,第二个组件构造器。
第三步:使用自定义组件标签在vue管理范围内,使用自定义组件。
第53个视频 - 全局组件和局部组件
讲一下全局组件和局部组件:talk is cheap,show me the code。
代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<cpn></cpn>
<cpn></cpn>
<cpn></cpn>
</div>
<div id="app2">
<cpn></cpn>
</div>
<script src="../js/vue.js"></script>
<script>
//1、创建组件构造器
const cpnC = Vue.extend({
template:`
<div>
<h2>我是孟子义</h2>
<p>我是韫秋,哈哈哈哈哈哈</p>
</div>
`
})
//2、注册组件(这种注册组件的方式,就是全局组件)
//全局组件意味着,可以在多个vue的实例当中使用。
Vue.component('cpn',cpnC)
const app = new Vue({
el: '#app',
data: {
message: ''
},
})
const app2 = new Vue({
el: '#app2'
})
</script>
</body>
</html>
效果:
如何注册局部组件
注册在vue的实例内部:
const app = new Vue({
el: '#app',
data: {
message: ''
},
components: {
//属性名cpn,就是使用组件时候的标签名
//属性值cpnC,
cpn: cpnC
}
})
考试
问题:怎么注册局部组件
答案:在vue实例当中options对象中,使用components属性的对象值中,注册局部组件。开发当中,局部组件使用得最多。开发当中一般也就只有一个vue实例,很少有多个vue实例的。
下午 - 14:30 - 18:00 - 3个半小时
第54个视频 - 父组件和子组件的区分
父子组件代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<cpn2></cpn2>
</div>
<script src="../js/vue.js"></script>
<script>
// 第一个组件构造器
const cpnC1 = Vue.extend({
template: `
<div>
<h2>我是儿子</h2>
<p>哈哈哈哈哈</p>
</div>
`
});
// 第二个组件构造器
const cpnC2 = Vue.extend({
template: `
<div>
<h2>我是父亲</h2>
<p>哈哈哈哈哈</p>
<cpn1></cpn1>
</div>
`,
components: {
cpn1: cpnC1
}
});
const app = new Vue({
el: '#app',
data: {
message: ''
},
components: {
cpn2: cpnC2
}
});
</script>
</body>
</html>
【小知识点】上面的代码中,div#app中已经不可以使用cpn1了,因为cpn1没有在vue实例当中注册。
效果如下:
一个默认的说法是:Vue实例也可以看成是一个组件。Vue实例看成是最顶层的组件,就是root组件。
考试
问题:一个组件1注册在组件2当中,组件2注册在vue实例当中,那么可以在vue实例当中使用组件1吗?
答案:不可以。因为组件1并没有在vue实例当中注册。vue实例使用组件2的时候,组件2已经编译好了。会解析组件2的模版,会发现里面有一个组件1,会优先去自己注册的components当中去找一下组件1的template,如果没有找到,就会去全局组件当中找,找到之后,就会编译。在vue实例当中使用组件2的时候,组件2的内容都已经编译完成了,vue实例根本就不知道有什么组件1的存在。
在本质上,vue会把组件的template都会渲染成render函数的。
第55个视频 - 注册组件的语法糖写法
全局组件注册和局部组件注册的语法糖的写法,请看下面的代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<cpn1></cpn1>
<cpn2></cpn2>
</div>
<script src="../js/vue.js"></script>
<script>
// 全局组件注册的传统写法
// const cpnC1 = Vue.extend({
// template: `
// <div>
// <h2>我是标题</h2>
// <p>哈哈哈哈哈</p>
// </div>
// `
// })
// Vue.component('cpn1',cpnC1)
// 全局组件注册的语法糖写法
Vue.component('cpn1',{
template: `
<div>
<h2>我是标题1</h2>
<p>哈哈哈哈哈</p>
</div>
`
})
// 局部组件注册的语法糖写法
const app = new Vue({
el: '#app',
data: {
message: ''
},
components: {
'cpn2': {
template: `
<div>
<h2>我是标题2</h2>
<p>呵呵呵呵呵</p>
</div>
`
}
}
})
</script>
</body>
</html>
考试
问题:vue注册组件的语法糖,本质是什么?
答案:从写法上,省略了Vue.extend()这个步骤。直接把原来的Vue.extend()括号里面,需要传递的对象参数,
放在了Vue.component(自定义标签字符串,构造器)
构造器的位置。
第56个视频 - 组件template抽离的写法
之前注册组件的时候,感觉代码很乱,这是因为js代码当中包含了html代码。
之前jquery创建某个标签,再给标签里面塞进去各种属性,是一个情况和道理,看起来乱。
第一种分离写法 - 通过script标签
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<cpn></cpn>
</div>
<!--第一种写法-->
<script type="text/x-template" id="cpn">
<div>
<h2>我是标题</h2>
<p>我是内容,哈哈哈哈</p>
</div>
</script>
<script src="../js/vue.js"></script>
<script>
// 1 - 传统写法
// Vue.component('cpn',{
// template: `
// <div>
// <h2>我是标题</h2>
// <p>我是内容,哈哈哈哈</p>
// </div>
// `
// })
Vue.component('cpn',{
template: '#cpn'
})
const app = new Vue({
el: '#app',
data: {
message: ''
},
})
</script>
</body>
</html>
第二种分离写法 - template标签
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<cpn></cpn>
</div>
<!--第二种写法-->
<template id="cpn">
<div>
<h2>我是标题</h2>
<p>我是内容,哈哈哈哈</p>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
// 1 - 传统写法
// Vue.component('cpn',{
// template: `
// <div>
// <h2>我是标题</h2>
// <p>我是内容,哈哈哈哈</p>
// </div>
// `
// })
Vue.component('cpn',{
template: '#cpn'
})
const app = new Vue({
el: '#app',
data: {
message: ''
},
})
</script>
</body>
</html>
考试
问题:组件注册时候,template分离写法有什么?
答案:在html部分,通过script标签或者template标签写template,内容放在标签内,然后给标签设置id属性,最后把id放在组件注册的template属性值中。
第57个视频 - 为什么组件data必须是函数
组件内部是不能够vue实例当中的数据的。
组件的data属性
- 组件的data属性值不能够是一个对象,必须是一个函数。
- 而且这个函数的返回值必须是一个对象。
组件当中现在有了template属性,components属性,data属性,其实也有methods,也有生命周期函数,很像是一个vue实例,底层组件本身的原型,就是指向vue的。
封装计数器小案例为一个组件
先讲一个需求:把原来的计数器案例,封装成一个组件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<cpn></cpn>
<cpn></cpn>
<cpn></cpn>
</div>
<template id="cpn">
<div>
<h2>当前计数为:{{counter}}</h2>
<button @click="increment">+</button>
<button @click="decrement">-</button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
Vue.component('cpn', {
template: '#cpn',
data() {
return {
counter: 0
};
},
methods: {
increment() {
return this.counter++
},
decrement(){
return this.counter--
}
}
});
const app = new Vue({
el: '#app',
data: {
message: ''
},
});
</script>
</body>
</html>
效果如下:
先做一个类比解释为什么data必须是函数
我们在页面中调用了三次组件。这三个组件都是不同的data对象,这是因为data是一个函数,返回值是一个对象。
我们可以看下面的代码:
<script>
// 函数在执行的时候,都会在自己的栈空间,创建很多新的变量。
function abc() {
return {
name: '许晴',
age: 40
}
}
let obj1 = abc()
let obj2 = abc()
let obj3 = abc()
obj1.name = '王琳'
obj2.age = '39'
console.log(obj1);
console.log(obj2);
console.log(obj3);
</script>
- 上面我们定义了一个函数是abc,函数的返回值是一个对象。
- 然后我们调用了三次函数,函数的返回值给了变量接收。
- 然后我们修改了obj1的name,修改了obj2的age,然后打印这三个对象。
- 得到的是下面的结果:
从结果可以知道,这是三个不同的对象,虽然是一个函数调用了三次,但是是返回了三个对象。
如果我们把上面的代码修改成下面的样子:
<script>
const obj = {
name: '许晴',
age: 40
}
function abc() {
return obj
}
let obj1 = abc()
let obj2 = abc()
let obj3 = abc()
obj1.name = '王琳'
obj2.age = '39'
console.log(obj1);
console.log(obj2);
console.log(obj3);
</script>
执行的结果是:
上面的写法,函数调用了三次,返回的就是同一个对象。
解释为什么data必须是函数
我自己的总结:函数是具有隔离性的,对象是没有隔离性的。
- 组件是可以复用的。
- 组件的data如果是一个对象,多个组件操作的是一个对象,那乱套了。
- 组件的data是一个函数,具有隔离性,这样就保障组件的正确复用。
考试
问题:面试题,为什么组件的data属性必须是一个函数,不能是一个对象。
答案:因为组件是复用的,对象没有隔离性,会多个组件相互影响。
第58个视频 - 父子组件通信 - 父传子 - props
需求场景描述
什么叫通信,就是传递数据。
- 举例场景,开发一个界面,有一个轮播图、tabbar、列表(列表里面还有各个元素)什么的。
- 我们开发列表的时候,肯定是组件化开发的,步骤肯定是,第一步,向服务器请求列表数据。
- 这个时候有一个问题,我们请求的列表数据,在哪里请求?
- 列表是一个组件,列表中每个元素是一个组件。我们在什么地方写网络请求?
- 一般情况下,我们是在最外面的组件发送网络请求的。
- 拿到数据后,可以放在最外面的组件的data中。
- 但是展示数据,是要交给列表中小组件元素进行展示的。
- 这个时候,我们需要做,把数据,从最外面的组件传递到子组件。这个就是父传子的通信。
- 父传子:props
- 子传父:自定义事件,emit发出一个事件。
父传子第一种方式-数组-代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<cpn :childmovies="movies" :childmessage="message"></cpn>
</div>
<template id="cpn">
<div>
<h2>{{childmessage}}</h2>
<ul>
<li v-for="item in childmovies">{{item}}</li>
</ul>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const cpn = {
template: '#cpn',
data(){return {}},
methods: {},
props: ['childmovies','childmessage']
};
const app = new Vue({
el: '#app',
data: {
message: '王子文',
movies: ['海王', '海贼王', '海尔兄弟']
},
components: {
// 'cpn':cpn
cpn
}
});
</script>
</body>
</html>
父传子理解
- 父组件中调用子组件,是通过子组件的标签,子组件相当于一个页面元素,有自己的属性的。
- 子组件的属性,在子组件内部,通过props来进行设置。
- 父组件可以给子组件动态绑定属性,通过v-bind给子组件绑定上它的自定义属性。类比给img动态绑定src属性。完成数据传输。
父传子第一种方式-数组-评价
父传子第二种方式-对象-数据验证
父传子第二种方式-对象-默认值设置-必传设置
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<cpn :childmovies="movies"></cpn>
</div>
<template id="cpn">
<div>
<h2>{{childmessage}}</h2>
<ul>
<li v-for="item in childmovies">{{item}}</li>
</ul>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const cpn = {
template: '#cpn',
data(){return {}},
methods: {},
props: {
//1. 类型的限制
// childmovies: Array,
// childmessage: String,
//2. 提供一些默认值
childmessage: {
type: String,
default: '乔欣',
required: false
},
//类型是对象或者数组的时候,默认值必须是一个函数
childmovies: {
type: Array,
// default: [],//这种写法在vue 2.5.3以下不会错,从2.5.17以上这样写会报错。
default(){
return []
},
required: false
}
}
};
const app = new Vue({
el: '#app',
data: {
// message: '王子文',
movies: ['海王', '海贼王', '海尔兄弟']
},
components: {
// 'cpn':cpn
cpn
}
});
</script>
</body>
</html>
父传子props写法的总结
开发当中,很少用数组形式的写法,也就是很少用第一种,比较常见的是用对象形式的写法,也就是第二种。
考试
问题:父子组件怎么通信
答案:父组件中调用子组件,通过给子组件动态绑定属性,传递父组件data当中的数据。子组件的属性,定义在props中,可以接收父组件绑定的数据。props有很多种写法,每个属性都可以设置type、default、required等。其中要注意属性type是数组或对象时候,default的写法。
第59个视频 - 父子组件通信 - props驼峰标识
父传子,通信的过程中,如果子组件的props对象中的元素,属性是驼峰标识。
在父组件调用子组件,进行动态属性绑定的时候,v-bind是不支持驼峰的。
这是关于props如果是驼峰属性,在父组件中动态绑定时候,怎么用的代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<cpn :c-info="info" :child-my-message="message"></cpn>
</div>
<template id="cpn">
<!--
Vue规定,子组件中包含很多元素时,必须包含一个根元素。
一般情况下,定义子组件模板,要有一个外层的div。
-->
<div>
<ul>
<li v-for="(value,key) in cInfo">{{key}}-{{value}}</li>
</ul>
<h2>{{childMyMessage}}</h2>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
// 注册局部子组件
const cpn = {
template: '#cpn',
methods: {},
data(){
return {}
},
props: {
cInfo: {
type: Object,
default(){
return {}
},
required: false
},
childMyMessage: {
type: String,
default: ''
}
}
}
const app = new Vue({
el: '#app',
data: {
message: '孟子义',
info: {
name: '乔欣',
age: 26,
height: 1.72
}
},
components: {
cpn
}
})
</script>
</body>
</html>
【锚点】我的小总结
第一,前面感觉比较乱的原因,是props的写法,介绍了两种,一种是数组写法,一种是对象写法。一般都是用对象写法。
第二,对象写法props对象中,属性是对象或数组的时候,需要写成一个函数,返回一个对象的形式。
第三,对象写法props中,属性对象可以有:type(可以自定义对象类型),default,required。属性可以是一个校验函数。
第四,子组件的template中必须包含一个根元素。
第五,子组件props中属性名是驼峰标识,可以在子组件的template中用,在v-bind进行动态绑定时候不支持,要转换。
第六,子组件的data属性必须是一个函数,不能是一个对象。
【锚点】我的小思维导图 - props写法总结
考试
问题:子组件中props中的属性,使用驼峰标识,在template标签中可以使用吗?在父组件动态绑定属性时,可以使用吗?
答案:可以。不可以。
第60个视频 - 父子组件通信 - 子传父(自定义事件)
需求场景
- 父传子:最外层大组件,网络请求到数组,给侧边栏小组件和列表小组件,传输数据,让他们进行展示。
- 子传父:一旦我的侧边栏小组件发生了点击,切换了页面,告诉外面的大组件,侧边栏组件里面点击了某个类别,让大组件去请求数据。
- 所以,子传父,的业务场景是:子组件发生了事件,数据是谁,父组件去网络请求吧。
子传父实现方式
- 子组件内部,第一步监听点击事件,第二通过点击事件函数发送自定义事件。
- 父组件内部,监听子组件的自定义事件即可。
驼峰问题
- 子组件发送的自定义事件,使用驼峰命名,例如itemClick。
- 父组件监听的时候:
- 使用@itemClick监听,在html单页面这种方式中不行,在脚手架中可以,如下图。
- 因为脚手架中组件是编译的,变成render函数,编译过程中,能够正常解析。
- 使用@item-click监听,不行。
- 使用@itemClick监听,在html单页面这种方式中不行,在脚手架中可以,如下图。
父传子和子传父的对比
- 父传子
- 父组件给子组件传递data数据,在父组件内,给子组件动态绑定属性v-bind。不支持驼峰。支持驼峰转换为横杠。
- 子组件内部在props中定义属性。
- 子传父
- 子组件内部监听默认事件,然后发送自定义事件。
- 父组件监听v-on子组件的自定义事件。不支持驼峰,不支持驼峰转换为横杠(脚手架支持)。
在这个瞬间,外面下着雨,我想你想得要死。
我现在心理强大了一些,我会去找你的。
分解-加工-记录-回顾。
【锚点】总览回顾
基础语法回顾
- 插值语法:mustache、v-once、v-text、v-html、v-pre、v-cloak
- 动态绑定:v-bind动态绑定属性(img/src、a/href、button/disabled、input/id/value、label/for)、v-bind动态绑定样式
- 计算属性:和methods的对比
- 事件监听:v-on参数传递问题、修饰符
- 条件判断:v-if、v-else-if、v-else、v-show
- 循环遍历:v-for设置key的性能优化问题
- 双向绑定:v-model修饰符
组件化回顾
- 组件options对象
- el
- data
- methods
- components
- template
- props
- 声明周期函数
- 父子通信
- 父传子:父组件动态绑定v-bind子组件的自定义属性,子组件的自定义属性在props中。
- 子传父:父组件事件监听v-on子组件的自定义事件,子组件的自定义事件在methods中通过this.$emit发送。
子传父的演示
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--父组件模版-->
<div id="app">
<cpn @itemclick="cpnClick"></cpn>
</div>
<!--子组件模版-->
<template id="cpn">
<div>
<button v-for="item in categories" @click="btnClick(item)">
{{item.name}}
</button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
// 子组件
const cpn = {
template: '#cpn',
data(){
return {
categories: [
{id: 'aaa', name: '熟女'},
{id: 'bbb', name: '人妻'},
{id: 'ccc', name: '御姐'},
{id: 'ddd', name: '萝莉'}
]
}
},
methods: {
btnClick(item){
// 子组件发射自定义事件
this.$emit('itemclick',item)
}
}
}
// 父组件
const app = new Vue({
el: '#app',
data: {
message: ''
},
methods: {
cpnClick(item){
console.log('cpn组件内部发生了点击',item);
}
},
components: {
cpn
}
})
</script>
</body>
</html>
效果如下:
考试
问题:子组件发送自定义事件并且传递了参数,父组件监听自定义事件,监听时候不写参数可以吗?
答案:可以的。在button点击监听浏览器默认事件时候,不写参数,会默认传入浏览器默认事件。监听子组件自定义事件,不写参数,也会默认传入子组件自定义事件。
第61个视频 - 项目演示
父子通信几个属性
项目演示
王红元的项目地址是HYMall。
项目效果,如下图所示:
第62个视频 - 知识回顾
- 12月18日星期二,讲了,邂逅js和基础语法
- 12月20日星期四,讲了,基础语法
- 12月21日星期五,讲了,组件化开发