1.组件系统
- 前端本身就是从后端分离出来 -> 量大【 项目任务重 】 -> 团队开发 -> 项目合【 难点 】 -> 如果分离出去任务是一个独立的整体,那么引用的时候就不会出现太大的问题 -> 组件【 独立的整体 】
- 组件其实是一个html、css、js等的一个聚合体
- Vue定义组件:Vue.extend() -> 帮助Vue扩展了组件的概念
- Vue.extend() 得到的是一个构造函数VueComponent( options )
const Hello = Vue.extend({ // 组件选项 //template选项就是组件的模板 - 虚拟DOM结构 template: ` <div> <button @click = "aa"> 点击 </button> <p> Vue Hello Component </p> </div> `, methods: { aa () { alert('aa') } } })- VueComponent这个构造函数我们不进行new实例化,我们希望组件是以标签化的形式展示
<Hello/> --> <div></div> <Banner></Banner>- options即我们之前在new Vue( options )
- Vue组件的结构
- 树形结构
- 根组件: 使用 new Vue() 来充当, 用来表示
- 组件的使用形式
- 标签化: 组件使用类似标签的形式
- 组件既可以是单标签,也可以双标签(用单标签有时会出错,双标签更好)
- 组件命名形式
- 大驼峰形式
- swiper-item(用-连接时用小写)
2.组件注册
-
Vue.extend(option)
-
全局注册(常用)
const Hello = Vue.extend({ // 组件选项 //template选项就是组件的模板 - 虚拟DOM结构 template: ` <div> <button @click = "aa"> 点击 </button> <p> Vue Hello Component </p> </div> `, methods: { aa () { alert('aa') } } }) Vue.component('Hello', Hello )//'Hello'即设定标签化的标签名<Hello></Hello>或<Hello/> //根据大驼峰形式命名,也可设为'VueHello',但标签名要改为<vue-hello></vue-hello>或<vue-hello/> -
局部注册
const Hello = Vue.extend({ template: '<div> 组件选项 </div>' }) new Vue({ el: '#app', data: {}, methods: { }, components: { // Hello: 组件的options Hello } }) -
组件注册简写
//全局注册 Vue.component('Hello',{ template: '<div> 组件注册简写 </div>' }) new Vue({ el: '#app', data: {}, //局部注册 components: { 'Demo': { template: '<div> 局部注册简写 </div>' } }, })
3.组件使用规则
-
直接父子级管理标签问题
-
html有一些直接父子级管理标签: ul>li / table>tr>td
-
当我们使用组件来代替子级元素时,组件会在父级外渲染
-
解决问题: 使用is属性来解决 , is属性可以将一个组件绑定给一个元素
<body> <div id="app"> <table> <tr> <td>1</td> <td>2</td> <td>3</td> </tr> <!-- <Hello/> 直接用组件便签引入组件会在父级外渲染 --> <tr is = "Hello"></tr> </table> </div> </body> <script> Vue.component('Hello',{ template: ` <tr> <td> 4 </td> <td> 5 </td> <td> 6 </td> </tr> ` }) new Vue({ el: '#app', data: {}, methods: { }, }) </script>
-
4.动态缓存
-
动态组件
-
可以变化的组件,用
component来表示 -
应用场景:后台管理系统
<body> <div id="app"> <select name="" id="" @change = "change"> <option value="HelloA"> A </option> <option value="HelloB"> B </option> <option value="HelloC"> C </option> </select> <!-- 动态组件 --> <component :is = "type"></component> </div> </body> <script> Vue.component('HelloA',{ template: '<div> A的内容 </div>' }) Vue.component('HelloB',{ template: '<div> B的内容 </div>' }) Vue.component('HelloC',{ template: '<div> C的内容 </div>' }) new Vue({ el: '#app', data: { type: 'HelloA' }, methods: { change ( e ) { this.type = e.target.value } }, }) </script>
-
-
动态缓存
-
当通过is属性在组件之间切换的时候,你有时会想保持这些组件的状态,以避免反复重渲染导致的性能问题
-
通过
keep-alive, 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们<!-- 只需要在外面加一层keep-alive --> <keep-alive :include = "type"> <component :is = "type"></component> </keep-alive> <!-- include 和 exclude 属性允许组件有条件地缓存。二者都可以用逗号分隔字符串、正则表达式或一个数组来表示(使用正则和数组需指令v-bind) --> <keep-alive include="a,b"></keep-alive> <keep-alive :include="/a|b/"></keep-alive> <keep-alive :include="['a', 'b']"></keep-alive> -
相关属性
include- 字符串或正则表达式。只有名称匹配的组件会被缓存。exclude- 字符串或正则表达式。任何名称匹配的组件都不会被缓存。max- 数字。最多可以缓存多少组件实例。
-
5.组件选项
-
template选项 - 组件的模板
-
template中只能有一个根元素
-
组件中的template选项可通过标签放入body部分
<body> <div id="app"> <Hello></Hello> <template> <p> 你好 </p> </template> </div> <template id="hello"> <!-- 唯一根元素 --> <div> <p> template标签在实例范围外使用 </p> </div> </template> </body> <script> /* * template标签在实例范围外使用 * 缺点: template标签将来会在渲染结构中出现,不会消失 * template标签在实例范围内使用 * 可以被解析,将来不会渲染到页面中 */ Vue.component('Hello',{ template: '#hello' }) new Vue({ el: '#app', data: {}, methods: { }, }) </script>
-
-
data选项
-
组件的data选项是个函数,因为组件是一个独立的整体,我们希望它的数据也是独立的,使用函数会有独立作用域
-
因为data选项要经过数据劫持处理,data的返回值必须是一个对象
data () { return { money: 10000 } }
-
-
其他选项如methods,computed,watch,directives,filters用法与根组件相同
6.组件嵌套
-
组件中嵌套组件,使用形式如下
<body> <div id="app"> <!-- 父组件 --> <Father/> </div> <template id="father"> <div> <h3> father组件 </h3> <!-- 子组件 --> <Son/> </div> </template> <template id="son"> <div> <h5> Son组件 </h5> </div> </template> </body> <script> // 全局注册 Vue.component('Father',{ template: '#father' }) Vue.component('Son',{ template: '#son' }) new Vue({ el: '#app', data: {}, // 局部注册 components: { Father: { template: '#father', components: { Son: { template: '#son' } } } }, }) </script>
7.组件通信
7.1 父-->子组件通信
-
在父组件模板中,使用单项数据绑定,将父组件数据赋值给子组件的自定义属性
-
子组件只可以使用接收到的数据,不可以更改 -> 单项数据流
-
子组件通过props属性来接收绑定在自己身上的自定义属性
- 这个属性在子组件模板中相当于全局变量,可以直接使用
- props属性接收的数据再生命周期的初始阶段获取不到,故无法在mounted钩子函数中操作接收的数据
<body> <div id="app"> <Father/> </div> <!-- 父组件 -- start --> <template id="father"> <div> <h3> 父组件 </h3> <!-- 在父组件模板中,使用单项数据绑定,将父组件数据赋值给子组件的自定义属性 --> <Son :money = "money"/> </div> </template> <!-- 父组件 -- end --> <!-- 子组件 --start --> <template id="son"> <div> <h5> 子组件 </h5> <p> 我老爸给了我: {{ money }} </p> </div> </template> <!-- 子组件 --end --> </body> <script> /* 子组件只可以使用接收到的数据,不可以更改 -> 单项数据流*/ Vue.component('Father',{ template: '#father', data () { return { money: 2000 } } }) Vue.component('Son',{ template: '#son', /* 子组件通过props属性来接收绑定在自己身上的自定义属性 */ props: ['money'] // 这个属性在子组件模板中相当于全局变量,可以直接使用 }) new Vue({ el: '#app', data: {}, methods: { }, }) </script>
7.2 子-->父组件通信
-
在父组件模板中,使用自定义事件绑定父组件的方法
-
在子组件中使用this.$emit()触发自定义事件,并将子组件的数据作为参数传递给父组件
<body> <div id="app"> <Father/> </div> <template id="father"> <div> <h3> father - 组件 </h3> <p> 我的小金库有: {{ gk }} </p> <hr> <!-- 在父组件模板中,使用自定义事件绑定父组件的方法 - get是自定义事件,名称随意 --> <Son @get = "changeGk"/> </div> </template> <template id="son"> <div> <h5> son - 组件 </h5> <button @click = "give"> 给老爸红包 </button> </div> </template> </body> <script> Vue.component('Father',{ template: '#father', data () { return { gk: 0 } }, methods: { changeGk ( val ) { this.gk = val } } }) Vue.component('Son',{ template: '#son', data () { return { hongbao: 8888 } }, methods: { give () { // 在这里调用自定义事件 get // this.$emit('get',参数1,参数2,参数3 ) this.$emit('get', this.hongbao ) } } }) new Vue({ el: '#app', data: {}, methods: { }, }) </script>
7.3 非父子级通信
-
兄弟组件(同一个父级组件)通信
-
通过子父级通信原理,在子组件a中使用this.$emit()触发父级自定义事件
-
给子组件b的ref属性一个名字如son,父组件通过this.$refs.son可直接操作子组件b
<body> <div id="app"> <Father/> </div> <template id="father"> <div> <h3> 父组件 </h3> <hr> <!-- 给子组件的ref设定标识名 --> <Son ref = "son"></Son> <hr> <Girl @kick = "kick"/> </div> </template> <template id="son"> <div> <h4> Son组件 </h4> <img v-if = "f" src="xxx" alt=""> </div> </template> <template id="girl"> <div> <h4> Girl组件 </h4> <button @click = "hit"> 打 </button> </div> </template> </body> <script> Vue.component('Father',{ template: '#father', methods: { kick () { // 这个方法中执行son组件的changeF this.$refs.son.changeF() } } }) Vue.component('Son',{ template: '#son', data () { return { f: false } }, methods: { changeF () { this.f = !this.f } } }) Vue.component('Girl',{ template: '#girl', methods: { hit () { this.$emit('kick') } } }) new Vue({ el: '#app', data: {}, methods: { }, }) </script>
-
-
无关联组件通信
-
新建Vue实例如bus
-
在组件a中的mounted钩子函数中定义bus的自定义事件,可通过该事件对组件a进行数据修改
-
在组件b中触发bus的自定义事件,达到通信的效果
const bus = new Vue() // 事件总线 Vue.component('Son',{ template: '#son', data () { return { f: false } }, mounted () { // 表示组件已经挂载结束,也就是表示这个组件已经渲染到了页面 // console.log('mounted') bus.$on('cry',() => {// cry事件被触发时会修改son组件的数据 this.f = !this.f }) } }) Vue.component('Girl',{ template: '#girl', methods: { hit () {//调用hit函数时即触发bus的cry事件 bus.$emit('cry') } } })
-
8.生命周期
-
Vue生命周期可以分为三个阶段,分别为: 初始化、更新阶段、销毁阶段
-
Vue生命周期钩子函数一共有11个,重点有8个
-
是组件就有生命周期
8.1初始阶段
-
初始阶段有四个钩子函数
- beforeCreate 实例被创建之前,为实例事件和生命周期做准备
- created 组件创建结束,初始化数据【 给data的数据做数据劫持,实现深入响应式原理 】
- beforeMount vdom虚拟dom生成,
- mounted 真实dom生成 (常用)
-
若实例或组件没有el/template选项,需要在实例创建结束之后通过 $mount 手动进行挂载
new Vue({ data: { info: '相亲' }, methods: {}, beforeCreate () {}, created () {}, beforeMount () {}, mounted () {}).$mount('#app') -
初始阶段自动执行,且只执行一次
-
created,beforeMount,mounted函数里都可以修改数据,一般都在mounted函数中操作
8.2 更新阶段
-
更新阶段有两个钩子函数
- beforeUpdate 监听 data 中数据是否改变,如果改变那么就执行更新阶段
- updated 更新结束,(重新生成VDOM,并通过diff算法比对新旧VDOM,获得patch补丁对象,然后渲染成真实DOM )
8.3 销毁阶段
- 销毁阶段有两个钩子函数
- beforeDestroy 销毁之前
- destroyed 销毁组件无法销毁开启在window上的事件,如window.onscroll,setInterval等
8.4 其他三个钩子函数
- 缓存的状态
- activated keep-alive 组件激活时调用。
- deactivated keep-alive 组件停用时调用。
- 错误捕获 errorCaptured
- 当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回
false以阻止该错误继续向上传播。 - 你可以在此钩子中修改组件的状态。因此在模板或渲染函数中设置其它内容的短路条件非常重要,它可以防止当一个错误被捕获时该组件进入一个无限的渲染循环。
- 当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回
基础决定未来,一步一个脚印