Vue基础-Day04
声明式编程(命令式编程):Vue中写模板的代码风格
组件化开发
基本概念介绍
组件:其实就是对网页功能的封装(对界面封装 html+css+js)。
本质:其实也是一个vue实例,组件这个实例不支持el选项,其他选项都支持
- 把一个页面拆分多个组件,组件分别开发完成可以组成一个完整的页面。
组件化带来的好处:
- 方便分配任务
- 方便后期的维护
- 方便代码的复用
总结: 组件化开发:把一个复杂的页面拆分为很多独立的组件,每一个组件封装为独立的功能(html/css/js),再通过组件的组合形成一个更加复杂的页面功能。方便代码的重用(别人写好的组件,拿过来直接使用即可)
定义(注册)组件
- 全局注册
Vue.component(参数一,参数二)
- 参数一表示组件的名称
- 参数二表示组件的配置选项
- data 表示组件需要用到的数据,组件中的data属性值必须是函数,函数的返回值是对象(该对象用于提供组件的数据)
- template 组件的模板(标签中填充数据的这种代码)
// 自定义全局组件
Vue.component('my-component-test', {
data () {
return {
info: 'jerry'
}
},
// template: '<div>{{info}}</div>'
template: `
<div>
{{info}}
</div>
`
})
<div id="app">
<div>{{msg}}</div>
<div>
<!-- 使用组件 -->
<my-component-test></my-component-test>
</div>
</div>
总结:
- 先定义再使用
- 定义的时候需要起一个名字,并且添加相关的配置选项(data/template….)
- data必须是一个函数
- 起的名字就是组件的标签名称
- 局部注册
const MyComponentTest = {
data () {
return {
info: 'spike'
}
},
template: `
<div>
{{info}}
</div>
`
}
let vm = new Vue({
el: '#app',
data: {
msg: 'hello'
},
components: {
'my-component-test': MyComponentTest
}
})
总结:
- 局部组件定义时,需要components选项( 左侧key是组件的名称,右侧value是组件的配置信息)
- 组件的具体配置信息可以抽取到独立的对象里面
组件命名规则
组件的命名规则,说白了,就是这么给组件取名字,在模板中怎么使用组件,的一些约定。
- 以横线分隔写法
count-add
<div id="app">
<!-- 使用组件 -->
<count-add></count-add>
</div>
<script>
Vue.component('count-add',组件配置对象)
new Vue({
el: '#app'
})
</script>
- 单词首字母大写写法
CountAdd
<div id="app">
<!-- 使用组件 -->
<count-add></count-add>
</div>
<script>
Vue.component('CountAdd',组件配置对象)
new Vue({
el: '#app'
})
</script>
总结:
- 定义组件时,组件起名字有两种方式:1,短横线方式;2、单词首字母都大写
- 现阶段:使用组件时,只能用小写的短横线方式,不可以使用首字符大写方式(后续可以)
组件之间嵌套
目标:熟悉组件之间的嵌套规则
<div id="app">
<com-parent></com-parent>
</div>
<script src="./vue.js"></script>
<script>
// 父组件
Vue.component('com-parent',{
template: `<div class="parent"> 父组件 <com-child></com-child></div>`
})
// 子组件
Vue.component('com-child',{
template: `<div class="child"> 子组件 </div>`
})
// 根实例
new Vue({
el: '#app'
})
</script>
总结:
- 组件之间可以无限嵌套:从而可以形成:父子、兄弟、祖孙。。。
- 组件之间的这些关系靠模板的标签嵌套形成
- 所有的组件的模板必须有唯一的跟元素
- 局部的含义:只能在定义它的组件的模板中使用
组件之间数据交互介绍
组件与组件之间数据,是相互独立的,当然有需要组件与组件之间进行数据通信的情况。
- 传递数据的方式和组件与组件之前的关系有关。
- 父组件 传值 子组件
- 子组件 传值 父组件
- 非父子关系,组件传值
- 组件本身的数据是相互隔离的,如果完全隔离,那么组件之间的联系就弱,无法实现复杂的功能,所有必须有一种机制:保证组件之间可以方便的进行数据交互。
总结:
- 组件之间的数据是需要交互,否则交互能很有限
- 主要存在的交互关系
- 父子关系
- 父向子传值
- 子向父传递
- 非父子关系(后续单独介绍)
父组件向子组件传值
目标:熟悉父组件向子组件传值用法
父组件向子组件传值
通过标签属性传值(属性值可以是静态的,也可以是动态绑定)
<!--父组件通过标签的属性向子组件传递数据-->
<com-child info='tom' :abc='abc'></com-child>
子组件通过props选项接收值
Vue.component('com-child', {
// 子组件通过props属性值接收父组件传递过来的数据
props: ['info', 'abc'],
data () {
return {
msg: 'child'
}
}
}
案例代码
/*
父组件向子组件传值
*/
Vue.component('com-parent', {
data () {
return {
msg: 'parent',
abc: 'jerry'
}
},
template: `
<div>
<div>{{msg}}</div>
<!--父组件通过标签的属性向子组件传递数据-->
<com-child info='tom' :abc='abc'></com-child>
</div>
`
})
Vue.component('com-child', {
// 子组件通过props属性值接收父组件传递过来的数据
props: ['info', 'abc'],
data () {
return {
msg: 'child'
}
},
template: `
<div>
<div>{{msg}}</div>
<div>{{info}}</div>
<div>{{abc}}</div>
</div>
`
})
new Vue({
el: '#app',
data: {
msg: 'hello'
}
})
</script>
总结:
- 父组件通过标签的属性向子组件传值(可以是动态绑定的,也可以是静态的)
- 子组件通过props接收父组件传递过来的值
- props传递的数据是只读的(单向数据流)
总结
- 前后端交互
- json-server包的基本使用
- 测试json-server提供的接口(熟悉Restful接口的风格)
- 图书管理案例-接口版(Promise和Async函数)
- 图书列表展示
- 添加图书
- 删除图书
- 搜索图书
- 侦听器用法(监听data中数据的变化)
- 组件化开发
- 理解组件化开发的基本概念
- 如何定义组件
- 全局组件
- 局部组件
- 如何使用组件
- 当做自定义标签使用
- 标签的命名规则:1、短横线方式;2、首字母大写方式
- 组件的嵌套:模板的嵌套可以形成复杂的组件关系
- 组件的模板必须有唯一的根节点
- 组件之间需要数据的交互
- 父子关系
- 父向子传值:props (只读的)
- 子向父传值
- 非父子关系(vuex 后续讲解)
- 父子关系
子组件向父组件传值
目标:熟悉子组件向父组件传值用法
// 组件化:子组件向父组件传值
// 创建父子组件
Vue.component('my-parent', {
data() {
return {
msg: 'hello:'
}
},
template: `
<div>
<div>父组件模板{{msg}}</div>
<!-- 1、父组件通过自定义事件的方式获取子组件传递过来的数据 -->
<my-child @get-info='handleInfo'></my-child>
</div>
`,
methods: {
handleInfo(v) {
this.msg = this.msg + v
}
}
})
Vue.component('my-child', {
data() {
return {
info: 'tom'
}
},
template: `
<div>
<div>子组件模板{{info}}</div>
<button @click='handleClick'>点击</button>
</div>
`,
methods: {
handleClick() {
// 点击按钮时把info的值传递给父组件
// 参数一表示自定义事件的名称
// 参数二表示传递过去的数据
this.$emit('get-info', this.info)
}
}
})
new Vue({
el: '#app',
data: {}
})
总结:子组件向父组件传递数据
- 子组件向父组件传递数据
- 参数一表示触发的事件名称
- 参数二表示传递的数据
this.$emit('get-info', this.info)
- 父组件接收子组件传递过来的值
- 父组件触发事件后自动调用函数,通过函数的形参可以获取子组件传递过来的值
<com-child @get-info='handleInfo'></com-child>
methods: { handleInfo (v) { // 获取子组件传递过来的数据 this.msg = this.msg + v }}
总结:通过自定义事件的方式子向父组件传值
- 触发事件:this.$emit
- 监听事件:标签上进行事件监听
组件的多次使用
目标:熟悉组件多次使用时,数据的访问问题
多次使用组件相当于创建了不同的组件实例对象,彼此之间的数据是相互独立的。
<div id="app"> <my-component-test></my-component-test> <my-component-test></my-component-test> <my-component-test></my-component-test></div>
Vue.component('my-component-test', { data () { return { count: 0 } }, methods: { handleAdd () { this.count += 1 } }, template: ` <div> <div>总数:{{count}}</div> <div> <button @click='handleAdd'>点击</button> </div> </div> `})new Vue({ el: '#app', data: { msg: 'hello' }})
总结:组件的多个实例的数据是相互独立的,不是共享的。
投票案例
目标:通过案例掌握父子之间的传值的应用场景
- 父向子传值:基于标签的属性
<my-person uname='张三'></my-person>
- 子向父传值:基于自定义事件监听
<my-person @update-total='handleTotal'></my-person>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.poll {
width: 300px;
height: 300px;
border: 1px solid blue;
text-align: center;
}
.persons {
display: flex;
}
.person {
flex: 1;
border: 1px solid orange;
text-align: center;
height: 260px;
}
</style>
</head>
<body>
<div id="app">
<my-poll></my-poll>
</div>
<script src="lib/vue.js"></script>
<script>
// 组件化-组件的多次使用
// 投票根组件
Vue.component('my-poll', {
data() {
return {
total: 0
}
},
template: `
<div class='poll'>
<div>当前参与投票的总数{{total}}</div>
<div class='persons'>
<my-person uname='张三' @update-total='handleTotal'></my-person>
<my-person uname='李四' @update-total='handleTotal'></my-person>
</div>
</div>
`,
methods: {
handleTotal(n) {
// 修改票的总数
this.total += n
}
}
})
Vue.component('my-person', {
props: ['uname'],
data() {
return {
count: 0
}
},
template: `
<div class='person'>
<div>{{uname}}的票数{{count}}</div>
<button @click='handleClick'>投票</button>
</div>
`,
methods: {
handleClick() {
// 修改本组件的自己的票数
this.count += 1
// 同时需要影响总票数
this.$emit('update-total', 2)
}
}
})
new Vue({
el: '#app',
data: {}
})
</script>
</body>
</html>
总结:投票场景(子向父传值;父向子传值)
ref直接操作组件实例
目标:熟悉ref直接操作组件实例的步骤
- ref可以直接操作DOM元素
- ref也可以直接操作组件的实例对象
<body>
<div id="app">
<div ref='myinfo'>hello</div>
<button @click='handleClick'>点击</button>
<my-com-test ref='mytest'></my-com-test>
</div>
<script src="lib/vue.js"></script>
<script>
// 组件化-通过ref直接操作组件实例
Vue.component('my-com-test', {
data() {
return {
msg: 'nihao'
}
},
template: `
<div>
<div>ref直接操作组件实例</div>
</div>
`,
methods: {
showMsg() {
console.log(this.msg + 'tom')
}
}
})
new Vue({
el: '#app',
data: {},
methods: {
handleClick() {
// console.log(this.$refs.myinfo.innerHTML)
// this.$refs.mytest表示组件my-com-test的实例对象
// 通过ref得到组件实例对象后,可以直接访问data中的数据和methods中的方法
// console.log(this.$refs.mytest.msg)
this.$refs.mytest.showMsg()
}
}
})
</script>
</body>
总结:
- ref可以直接操作DOM元素
- ref也可以直接操作组件的实例对象
- 通过组件的实例对象可以访问data中的数据和methods中的方法(也可以访问其他特性)
组件的生命周期
目标:熟悉组件的生命周期概念,以及相关函数的应用场景
- 生命周期:一种事物从生到死的过程
- 组件的生命周期:组件从创建到销毁的过程(包括更新)
- 创建阶段
- beforeCreate
- created
- beforeMount
- mounted
- 更新阶段
- beforeUpdate
- updated
- 销毁阶段
- beforeDestroy
- destroyed
- 创建阶段
- Vue组件创建、更新和销毁的过程中会执行一系列的函数,这些函数是自动触发的,这些函数还有另一种说法:钩子函数(生命周期函数)
- 所有的生命周期函数都是自动触发的,不允许显示调用。
创建阶段
- created
- 典型场景:初始化data中的数据(调用接口)
- mounted
- 典型场景:直接操作DOM
更新阶段
- beforeUpdate 更新前触发
- updated 更新后触发
注意:更新指的是组件的data和props中的数据的更新 典型应用场景:实现更新的加载提示效果
销毁阶段
- beforeDestroy 销毁前触发
- destroyed 销毁后触发
典型应用场景:销毁组件不再使用的资源(比如销毁定时任务)
总结
- 为什么需要组件化开发?方便重用;方便协作;方便维护
- 如何定义组件?全局定义;局部定义
- 如何使用组件?作为自定义标签使用
- 组件的命名规则:短横线方式;首字符大写方式
- 组件之间的关系:父子关系;非父子关系(通过模板的标签嵌套)
- 关于全局和局部的区别:使用的范围不同
- 组件之间的数据交互(组件本身的数据是隔离的,只有组件内部可以使用)
- 父组件向子组件传递数据:props
- props数据是只读的(单向数据流)
- 子组件向父组件传递数据:自定义事件
- 父组件监听自定义事件
- 子组件触发自定义事件 $emit
- 父组件向子组件传递数据:props
- 组件的多次使用(数据是独立的,而不是共享的)
- 通过ref直接操作组件实例的用法
- 组件的生命周期:创建阶段;更新阶段;销毁阶段
- created : 此时可以操作data中的数据和methods中定义的方法,但是不可以操作DOM
- mounted:此时可以操作DOM