vue初学——深入理解vue组件
Posted try0929
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了vue初学——深入理解vue组件相关的知识,希望对你有一定的参考价值。
深入理解vue组件
1、细节点一,vue组件和h5规范冲突
举个例子比较能说明问题:
- 现在是要写个表格,不用组件的形式如下:
<table>
<tbody>
<tr><td>1</td></tr>
</tbody>
</table>
- 要使用组件化,就创建一个全局组件:
Vue.component(‘row‘,{
template: ‘<tr><td>1</td></tr>‘
})
- 然后就在表格里用row来代替:
<table>
<tbody>
<row></row>
<row></row>
<row></row>
</tbody>
</table>
- 结果从页面上来看是没有问题,但是f12打开看代码却出现了问题,row创建的代码并没有在表格里,而是在表格外面,可以看到严重损坏了模块性:
- 而这是为什么呢,是由于h5规定在tbody下只能装tr,而这里的row让浏览器无法正确渲染。
如何解决?(is语法)
- 这里可以依然用tr标签,但在里面用vue的is语法绑定表示这个原件实际上就是子组件创建的:
<table>
<tbody>
<tr is=‘row‘></tr>
</tbody>
</table>
这样就解决了这个问题了。
- 实际上,在其他h5中规定了只能接某种标签时,也可以用这种方式;如ol和ul中的li,select中的option。
2、子组件定义data不能定义成对象,只能定义成函数
- 如题,这是由于,子组件通常会被调用多次,而根组件一般只会被调用一次,要保证子组件中的数据不会发生冲突 应该用两套数据,注意这个函数返回一个对象,对象里面保存着数据。
3、ref的使用
- 先用两个子组件分别实现两个计数器:
Vue.component(‘counter‘, {
template: ‘<div @click="handleClick">{{ number }}</div>‘,
data: function() { //记得子组件里data要是函数
return {
number: 0
}
},
methods: {
handleClick: function() {
this.number++;
}
}
})
- 现在要在外面设置一个变量来实时监控这两个计数器的值,算出总和。那也就是说要将值传入根组件,之前有提到过,也就是要**子组件的事件被触发时用this.(emit(‘事件名‘)来触发根组件的事件**。这里就在handleClick里加上一句``this.)emit(‘change‘);``,即在根组件里触发了change事件。
- 触发事件之后如何计算total?由于是每次number变化就要计算一次total,所以可以在子组件中用ref来表示一个子组件的引用,而在根组件的实例中可以通过this.$refs.ref名来获得子组件的一个引用。获得引用之后就好说了,直接再在后面添加属性就能获得,代码如下:
//在两个子组件用ref引用
<counter @change=‘handleChange‘ ref=‘one‘></counter>
<counter @change=‘handleChange‘ ref=‘two‘></counter>
//接着在根组件中添加方法来处理数据
handleChange: function() {
this.total = this.$refs.one.number + this.$refs.two.number;
}
-
ref加在dom元素上是直接获得该dom元素,加在子组件上是获得该子组件的一个引用。
-
之前做TodoList的时候也提到过要从子组件向根组件传值,那个时候用的是函数的参数传递,而这里是直接获得了子组件的引用 更加方便。之前也提到,根组件向子组件传值是要在子组件里设置一个props属性来存放数据。
4、父子组件数据传递
1)父组件->子组件,通过属性传值
- 单向数据流:子组件不要自行随意修改父组件传递过来的值,因为若是传的如对象这样的值,修改了一次 在其他子组件中也修改了
- 那如何解决,很简单,直接在data中创建一个父组件传来的值的副本即可,修改自己的数据并不会影响原来的数据
<div id=‘root‘>
<counter :count="0"></counter>
<counter :count="0"></counter> //通过v-bind绑定数据
</div>
var counter = {
props: [‘count‘],
data: function() { //记得要是函数哦
return {
number: this.count
}
},
template: ‘<div @click="handleClick">{{number}}</div>‘,
methods: {
handleClick: function() {
this.number++; //这里用自己创造的副本来改变
}
}
}
var vm = new Vue({
el: ‘#root‘,
components: {//记得局部组件要在父组件内注册
counter: counter
}
})
2)子组件->父组件,通过触发事件
- 子组件有值发生变化时,就触发父组件的事件(可以自己命名),然后在父组件内监听该事件即可;
- 在触发父组件事件时,后面那个参数就是传进父组件事件处理函数的参数:
//在上面的handleClick中加上
this.$emit(‘change‘, 1); //后面这个参数是用来传值的
//在父组件内监听
<counter :count="0" @change=‘handleChange‘></counter>
<counter :count="0" @change=‘handleChange‘></counter>
//父组件内处理
handleChange: function(step) {
this.total += step;
}
5、组件参数校验
- 之前说到,父组件传递过来的值,子组件要设置一个props属性来接收,而这个props属性的值可以不一定要为数组,也可以是一个对象,可以限制父组件传值的范围:
var child = {
props: {
content: {
type: [String], //限制了只能是字符串
required: true, //必须要传值
default: ‘Hello, world‘, //默认字符串为这个
validator: function(value) { //限制长度等
return value.length>5;
}
}
},
template: ‘<div>{{content}}</div>‘
}
- 子组件中的props是用来接收值的,props中没有这个名字则说明是非props特性,可以把它和之前的自定义属性看作是一样的,所以 props特性用来传值并不会显示在html代码中,而非props特性的属性从而会显示在html中。
6、给组件绑定原生事件
- 在子组件里绑定事件,都是写在子组件的template里,这里就是默认绑定的原生事件,因为是绑定到了原生dom上;
- 那之前在 比如说
<counter></counter>
里绑定的事件,就是自定义的事件,若是写<counter @click=‘handleClick‘></counter>
,这样绑定后点击并不能触发原生的click事件,这是由于counter不是原生dom; - 解决方式也很简单,在上面直接写
<counter @click.native=‘handleClick‘></counter>
即可。
7、非父子组件传值(Bus/总线/发布订阅模式/观察者模式)
之前组件间传值都是在父子组件(详见上面),那非父子组件如何传值呢?一层一层传未免过于复杂,这里介绍一种Bus方式,这里要实现:点击其中一个子组件,另一个子组件也要变成这个样子。
-
首先给Vue类的prototype的bus属性创建一个Vue实例,所以这个bus也是Vue的一个实例;
-
依然给子组件的template添加click事件,而在这个回调函数里触发bus的change事件,其中传参为当前的ct;
-
Vue的所有实例都有bus属性,所以触发了change事件所有子组件都能感知到,就在子组件内利用生命周期钩子mounted来让Vue实例的属性bus来接收change事件,这里传入了一个参数,就可以用来改变子组件的值;mounted函数是在 挂在子组件之后 调用的函数。
<div id="root">
<child :content=‘"TRY"‘></child>
<child :content=‘"ARASHI"‘></child>
</div>
<script>
Vue.prototype.bus = new Vue();
Vue.component (‘child‘, {
props: [‘content‘],
data: function() {
return {
ct: this.content
}
},
template: ‘<div @click="handleClick">{{ct}}</div>‘,
methods: {
handleClick: function() {
this.bus.$emit(‘change‘,this.ct);
}
},
//mounted生命周期钩子 在挂载子组件之后自动调用
mounted: function() {
var _this = this; //要保存这个this,不然后面函数会找不到这个指向
this.bus.$on(‘change‘, function(value){
_this.ct = value;
})
}
})
var vm = new Vue({
el: ‘#root‘
})
</script>
8、vue中的插槽
从之前的介绍中可知,可以用template来初始化子组件,但如果一切都在template中写 阅读性会十分低,有一种方式可以直接在html的子组件标签内部插入内容,这就是插槽(slot)。使用方法也不难,就直接在下面代码里讲:
//html部分
<div id="root">
<child>
<div slot=‘header‘>传进来的header</div>
<div slot=‘footer‘>传进来的footer</div>
</child>
</div>
//子组件部分
Vue.component(‘child‘,{
template: `<div>
<slot name=‘header‘></slot>
<div>模板的内容</div>
<slot name=‘footer‘></slot>
<slot name=‘default‘>默认内容</slot>
</div>`
})
var vm = new Vue({
el: ‘#root‘
})
定义域插槽
- 通常出现在需要循环显示的时候,目的是不在子组件template中将循环的每一项的格式(如p h1等)规定,而是在html中再来具体规定;
- 在template中规定好循环的数组,以及用v-bind定好属性等于循环的每个元素,再在html中子组件具体例化的里面用template标签(后台不会显示的)设置属性slot-scope的值来接收来自子组件传来的值,里面就是具体格式,看代码吧说着好难懂...
<div id="root">
<child>
//标准格式 就是要用template标签 加上属性值设定来获取子组件传来的值,这个prop可以随便设置,只要后面{{}}里用它即可
<template slot-scope=‘prop‘>
<h1>{{prop.it}}</h1>
</template>
</child>
</div>
<script>
Vue.component(‘child‘,{
data: function() {
return {
list: [4,3,2,1]
}
},
//template里面的:it=item是为了让父组件接收到循环的值
template: ` <div>
<slot
v-for="item of list"
:it=item
></slot>
</div>`
})
var vm = new Vue({
el: ‘#root‘
})
</script>
9、动态组件与v-once指令
动态组件(is)
- 现在是有两个组件,每次只显示一个,点击一次下面的button就切换到另一个,最容易想到的写法:
<div id="root">
<child-one v-if="type===‘child-one‘"></child-one>
<child-two v-if="type===‘child-two‘"></child-two>
<button @click=‘handleBtnClick‘>change</button>
</div>
<script>
Vue.component(‘child-one‘,{
template: ‘<div>child-one</div>‘
})
Vue.component(‘child-two‘,{
template: ‘<div>child-two</div>‘
})
var vm = new Vue({
el: ‘#root‘,
data: {
type: ‘child-one‘
},
methods: {
handleBtnClick: function() {
this.type = (this.type===‘child-one‘?‘child-two‘:‘child-one‘);
}
}
})
</script>
- 可以看到,在html中是在子组件实例中用到了v-if来判断这次使用哪一个组件,现在可以用动态组件来实现,同样的代码,只需将两行组件实例给删掉,取而代之用
<component :is=‘type‘></component>
; - 还记得之前讲过的is属性的用法吗?就是可以直接将该元素绑定到不同的子组件上,而在这里,绑定到哪个子组件上就由type决定。
v-once指令(提高代码效率)
- 每次浏览器在渲染页面时,上面这样切换时都会将其中一个子组件删掉然后添加上另外一个,每次这样重新渲染再删去效率不太高,所以引入了v-once;
- 最开始的代码不要变,只是在两个子组件的template里加上 v-once即可,这样以后,每次渲染新的子组件时就会将该子组件放入内存,等到下次又需要它了,并不需要重新创建一个子组件,直接从内存取出来之前的即可,提高了静态内容的展示效率。
以上是关于vue初学——深入理解vue组件的主要内容,如果未能解决你的问题,请参考以下文章