本章将深入讲解 Vue 组件使用的细节点,从父子组件的参数传递及校验入手,逐步深入到非父子组件间传值、插槽、作用域插槽、动态组件等内容的讲解。
4-1 使用组件的细节点
is
属性
table 层级结构不对
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <div id="app"> <table> <tbody> <row></row> <row></row> <row></row> </tbody> </table> </div> <script> Vue.component('row',{ template: "<tr><td>this is row</td></tr>" }) var app = new Vue({ el: "#app", }) </script>
|
is 属性;https://v2.cn.vuejs.org/v2/api/#is
适用于 table,ol,ul, section 等标签上,解决dom结构不符合语意化问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <div id="app"> <table> <tbody> <tr is="row"></tr> <tr is="row"></tr> <tr is="row"></tr> </tbody> </table> </div> <script> Vue.component('row',{ template: "<tr><td>this is row</td></tr>" }) var app = new Vue({ el: "#app", }) </script>
|
子组件data数据
在组件中定义 data
的时候必须是一个函数,不能是对象,他不同于跟组件只调用一次,子组件可能会在很多地方调用多次,但是每个地方的数据又不能冲突,所以通过一个函数来返回对象的目的就是每一个子组件都拥有一个独立的数据存储,不会出现每个子组件之间互相影响的情况
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <div id="app"> <table> <tbody> <tr is="row"></tr> <tr is="row"></tr> <tr is="row"></tr> </tbody> </table> </div> <script> Vue.component('row',{ data: function(){ return { mess: "this is row" } }, template: '<tr><td>{{mess}}</td></tr>' }) var app = new Vue({ el: "#app", data: { mess:"hello world" } }) </script>
|
课下阅读官方文档 is 章节内容
ref
/引用
标签上使用ref
获取 DOM 节点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <div id="app"> <div @click="hanleBtnClick" ref="hello" > hello world </div> </div> <script> var app = new Vue({ el: "#app", data: { mess:"hello world" }, methods: { hanleBtnClick: function() { alert(this.$refs.hello.innerHTML) } }, }) </script>
|
组件上使用ref
获取的是子组件的引用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| <div id="app"> <counter ref="one" @change="handleChange"></counter> <counter ref="two" @change="handleChange"></counter> <div>{{total}}</div> </div> <script> Vue.component('counter', { template: '<div @click="handleClick">{{number}}</div>', data: function () { return { number: 0 } }, methods: { handleClick: function () { this.number++ this.$emit('change') } } }) var app = new Vue({ el: "#app", data: { total: 0 }, methods: { handleChange: function () { console.log(this.$refs.one.number) console.log(this.$refs.two.number) this.total = this.$refs.one.number + this.$refs.two.number } } }) </script>
|
课下阅读官方文档 ref 章节内容
4-2 父子组件间的数据传递
父->子 传值
父组件通过属性的方式向子组件传递数据,子组件通过 props
的方式接受数据
单项数据流
父组件可以随意的向子组件传递参数,但是子组件只能使用父组件传递过来的参数,不能反过来修改父组件的参数。
如果传递过来的是 Object
的数据,如果改变了数据的话,此数据可能还被其他数据所引用,改变数据的话会对其他组件产生影响,所以不允许这么做
子组件的 data
一定要是一个函数
子->父 传值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| <div id="app">
<counter :count="countOne" @total="handleTotal"></counter> <counter :count="countTwo" @total="handleTotal"></counter> <div>{{total}}</div> </div> <script> var counter = { props:['count'], data: function(){ return { number: this.count } }, template: '<div @click="handleClick">{{number}}</div>', methods: { handleClick: function(){ this.number = this.number + 1 this.$emit("total",1) } }, } var app = new Vue({ el: "#app", data: { countOne: 1, countTwo: 2, total: 3 }, components: { counter: counter }, methods: { handleTotal: function(data1){ this.total += data1 } } }) </script>
|
课下阅读官方文档 通过 Prop 向子组件传递数据 章节内容
4-3 组件参数校验与非 props 特性
组件参数校验
父传值的数据类型是可以约束(校验)的
相关知识点记录在下面代码中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| <div id="app"> <child :content="mess"></child> </div> <script> Vue.component('child',{ props:{ content: { type: [String,Number], required: true, default: "required 配置为 false的默认值", validator: function(value){ console.log(value) console.log(value.length) return value.length > 5 } } }, template: '<div>{{content}}</div>' })
var app = new Vue({ el: "#app", data: { mess: '1234567890' } }) </script>
|
props 特性
props 特性
通过 props
接收的数据能直接通过插值表达式的方式使用
- 父组件传,子组件接,直接插值表达式用
- 不会把属性显示在
dom
标签中
非 props 特性
- 父组件传,子组件不接,就是如果不进行
props
接收的话是不能直接使用数据的
- 组件/父元素 的属性会展示着子组件的最外层标签上
课下阅读官方文档 Prop 章节内容
4-4 给组件绑定原生事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| <div id="app"> <child @click.native="ysClick"></child> </div> <script> Vue.component('child',{ template: '<div>child</div>', methods: { }, }) var app = new Vue({ el: "#app", data: { mess:"hello world" }, methods: { ysClick: function(){ alert("ysClick/原生事件") }, } }) </script>
|
课下阅读官方文档 将原生事件绑定到组件 章节内容
4-5 非父子组件间的传值
vuex
放在后面项目中讲解
总线机制(Bus/总线/发布订阅模式/观察者模式)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| <div id="app"> <child content="Xiao"></child> <child content="Dongxier"></child> </div> <script> Vue.prototype.bus = new Vue() Vue.component('child',{ data: function(){ return { selfContent: this.content } }, props: { content: String }, template: '<div @click="handleClick">{{selfContent}}</div>', methods: { handleClick: function(){ this.bus.$emit('change',this.selfContent) } }, mounted: function(){ var _this = this; this.bus.$on('change',function(msg){ _this.selfContent = msg }) } }) var app = new Vue({ el: "#app", data: { mess:"hello world" } }) </script>
|
4-6 在Vue中使用插槽
父组件向子组件优雅的传递dom结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <div id="app"> <child> <body-content> <div class="header" slot="header">header</div> <div class="footer" slot="footer">footer</div> </body-content> </child> </div> <script> Vue.component('body-content',{ template: `<div> <slot name="header">默认内容</slot> <div>hello </div> <slot name="footer">默认内容</slot> <slot> <h1>默认插槽内容/可以是标签内容</h1> </slot> </div>` }) var app = new Vue({ el: "#app", data: { mess:"hello world" } }) </script>
|
课下阅读官方文档 slot 章节内容
课下阅读官方文档 插槽 章节内容
在 2.6.0 中,我们为具名插槽和作用域插槽引入了一个新的统一的语法 (即 v-slot
指令)。它取代了 slot
和 slot-scope
这两个目前已被废弃但未被移除且仍在文档中的 attribute。新语法的由来可查阅这份 RFC。
4-7 作用域插槽
当子组件做循环,或某一部分他的dom结构由外部传入进来的时候用作用域卡槽
子组件可以向父组件里面传数据,父组件想接收的话必须在外层使用 template ,同时通过 slot-scope=”属性的名字” 来接收传递过来的所有数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| <div id="app"> <child> <template slot-scope="props"> <li>{{props.item}} -- hello</li> </template> </child> </div> <script> Vue.component('child',{ data: function(){ return { list: [1,2,3,4,5,6,7,8,9] } }, template: `<div> <ol> <slot v-for='item of list' :item=item > {{item}} </slot> </ol> </div>` })
var app = new Vue({ el: "#app", data: { mess:"hello world" } }) </script>
|
4-8 (新)动态组件与v-once指令
动态组件
component 动态组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| <div id="app"> <h2>直接使用组件</h2> <child-one v-if="type == 'child-one'"></child-one> <child-two v-if="type == 'child-two'"></child-two> <h2>使用component动态组件</h2> <component :is="type"></component> <button @click="handleBtnClick">切换</button>
<h2>直接使用component动态组件</h2> <component :is="type1"></component> </div> <script> Vue.component('child-one',{ template: '<div>child-one</div>' })
Vue.component('child-two',{ template: '<div>child-two</div>' })
Vue.component('child-three',{ template: '<div>child-three</div>' })
var app = new Vue({ el: "#app", data: { type: 'child-one', type1: 'child-three', }, methods: { handleBtnClick: function(){ this.type = (this.type == 'child-one' ? this.type = 'child-two' : this.type = 'child-one') console.log(this.type) } }, }) </script>
|
v-once指令
只对内部的模版渲染一次,后期数据发生了改变也不会进行渲染
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| <div id="app" v-once> <h2>直接使用组件</h2> <child-one v-if="type == 'child-one'"></child-one> <child-two v-if="type == 'child-two'"></child-two> <h2>使用component动态组件</h2> <component :is="type"></component> <button @click="handleBtnClick">切换</button>
<h2>直接使用component动态组件</h2> <component :is="type1"></component> </div> <script> Vue.component('child-one',{ template: '<div>child-one</div>' })
Vue.component('child-two',{ template: '<div>child-two</div>' })
Vue.component('child-three',{ template: '<div>child-three</div>' })
var app = new Vue({ el: "#app", data: { type: 'child-one', type1: 'child-three', }, methods: { handleBtnClick: function(){ this.type = (this.type == 'child-one' ? this.type = 'child-two' : this.type = 'child-one') console.log(this.type) } }, }) </script>
|
template模版
template 里面的内容,中间没有内容的话可以写单标签就行
1 2 3 4 5 6 7
| template: ` <div> <child-one v-if="type == 'child-one'"></child-one> <child-two v-if="type == 'child-two'"></child-two> <button @click="handleBtnClick">切换</button> </div> `,
|
等于
1 2 3 4 5 6 7
| template: ` <div> <child-one v-if="type == 'child-one'"/> <child-two v-if="type == 'child-two'"/> <button @click="handleBtnClick">切换</button> </div> `,
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| <div id="app"></div> <script> Vue.component('child-one',{ template: '<div>child-one</div>' })
Vue.component('child-two',{ template: '<div>child-two</div>' })
var app = new Vue({ el: "#app", data: { type: 'child-one', type1: 'child-three', }, template: ` <div> <child-one v-if="type == 'child-one'"/> <child-two v-if="type == 'child-two'"/> <button @click="handleBtnClick">切换</button> </div> `, methods: { handleBtnClick: function(){ this.type = (this.type == 'child-one' ? this.type = 'child-two' : this.type = 'child-one') console.log(this.type) } }, }) </script>
|
课下阅读官方文档 动态组件 & 异步组件 章节内容
4-9 (新)章节小结
阅读官方文档 基础及深入了解组件 两部分内容
复杂/后期项目讲解
4-10 【讨论题】组件究竟是什么?
组件是Vue.js最强大的功能之一。组件可以扩展HTML元素,封装可重用的代码。在较高层面上,组件是自定义的元素,Vue.js的编译器为它添加特殊功能。在有些情况下,组件也可以是原生HTML元素的形式,以is特性扩展