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
 
 
