Vue-组件
组件
组件可以把大的框架按照HTML拆分成小块功能实现,并实现内容复用。
同时组件之间还要传递数据或参数。当然组件也有很多缺点,最后介绍模块开发的单文件组件
组件注册和使用
注册
1 | <!--全局注册组件--> |
注意局部注册的组件在其子组件中不可用,如果需要使用,需要在子组件中声明
组件命名推荐:字母全小写且必须包含一个连字符。避免和当前以及未来的 HTML 元素相冲突。
使用-挂载组件
一般直接标签使用即可。但在某些情况下会受到HTML限制,比如<table>
内规定只允许是<tr>
,<td>
,<th>
等元素,所以在<table>
内使用组件是无效的。这时候可以使用is属性
来挂载组件。1
2
3
4
5
6
7
8
9
10<!--普通使用-->
<div id="app">
<my-component></my-component>
</div>
<!--is属性挂载组件-->
<div id="app">
<table>
<tbody is="my-component"></body>
</table>
</div>
动态挂载组件
Vue 提供了一个特殊的元素<component>
用来动态地挂载不同的组件,使用is
特性来选择要挂载的组件。1
2<!-- 组件会在 `currentTabComponent` 改变时改变 -->
<component v-bind:is="currentTabComponent"></component>
手动挂载实例
Vue提供Vue.extend
和$mount()
两个方法来手动挂载一个实例。
Vue.extend
是基础Vue构造器,创建一个“子类”,参数时一个包含组件选项的对象- 如果Vue实例在实例化时没有收到
el
选项,他也处于“未挂载”状态 $mount
手动挂载一个未挂载的实例
1 | <div id = "mount-div"></div> |
模块化中使用组件
在模块化中使用其他组件,需要在局部注册之前导入每个你想使用的组件1
2
3
4
5
6
7
8
9
10
11//ComponentB.vue
import ComponentA from './ComponentA'
import ComponentC from './ComponentC'
export default {
components: {
ComponentA,
ComponentC
},
// ...
}
现在ComponentA
和ComponentC
都可以在ComponentB
的模板中使用了。
另外可以实现一些基础模块的全局化使用:戳这里
组件选项
因为组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 data
、computed
、watch
、methods
以及生命周期钩子等。仅有的例外是像 el
这样根实例特有的选项。
常用的有:
template
模板data
跟实例稍有区别,必须是函数,将数据return出去computed
计算属性methods
动作components
组建中嵌套组件
1 | Vue.component('my-component',{ |
组件中return的data如果引用了一个外部的变量,那么这个变量就不是自己独有的,而是所有相同组件共享的,牵一发而动全身。
如果想要各自独立,就需要组件通讯这个变量,再重新赋值。
组件通讯
父->子 props
通过props正向传输数据(父组件向子组件传递数据及参数)。父组件中的数据变化会传递给子组件,反过来不行。
在组件中,使用选项props来声明需要从父级接收的数据,值分为两种
- 数组:传递值
- 对象:传递的值需要数据类型验证
额外的,每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。
props
中的数据类似于data,只是前者props来源于父级,后者是组件自己的数据,作用于是组件本身。
两者都可以在模板template
、计算属性computed
、methods
中使用
数组使用
1 | <div id="app"> |
由于HTML特性不区分大小写,在
props中的warningText
在DOM中赋值时需要用warning-text="提示信息"
,即<my-component warning-text="提示信息"></my-component>
对象使用——数据验证
自带验证的数据类型有:
- String
- Number
- Boolean
- Object
- Array
- Function
1 | Vue.component('my-component',{ |
验证失败时,在开发版本下会在控制台抛出一条警告
动态参数传入
利用 v-bind
动态更新组件1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<div id="app">
<!-- :message 等于 v-bind:message -->
<my-component :message="parentMessage"></my-component>
</div>
<script>
Vue.component('my-component',{
props: ['message'],
template: '<div>{{ message }}</div>'
})
var app = new Vue({
el: '#app',
data: {
parentMessage: ''
}
})
</script>
直接传递数字、布尔值、数组、对象 需要使用
v-bind
,即:message="[1,2,3]"
不然message="[1,2,3]"
传递的是字符串
如果你想要将一个对象的所有属性都作为prop
传入,你可以使用不带参数的 v-bind
(取代v-bind:prop-name
)。例如,对于一个给定的对象post
:1
2
3
4
5
6
7
8
9
10
11
12//数据
post: {
id: 1,
title: 'My Journey with Vue'
}
//v-bind="对象"
<blog-post v-bind="post"></blog-post>
//等价于
<blog-post
v-bind:id="post.id"
v-bind:title="post.title"
></blog-post>
应用-单向数据流
由于在JavaScript中对象和数组是引用类型,指向同一个内存空间,所以prop中的变量是对象和数组时,在子组件内改变是会影响父组件的
使用场景:从父组件传递数据进来,保存原值,还可以可以随意操作1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19<div id="app">
<my-component :init-count="1"></my-component>
<!-- 变为短横分割命名 / v-bind传入数字-->
</div>
<script>
Vue.component('my-component',{
props: ['initCount'],
template: '<div>{{ count }}</div>'
data:function () {
reutrn {
count: this.initCount
}
}
})
var app = new Vue({
el: '#app'
})
</script>
还可以类似的使用computed
转变传入值
子->父 $emit $on
子组件需要向父组件传递数据时可以使用自定义事件——$emit()
和$on()
- 子组件用
$emit()
来触发事件,父组件用$on()
来监听子组件的事件 - 父组件也可以直接在子组件的自定义标签上使用
v-on
来监听子组件触发的自定义事件
跟组件和 prop 不同,事件名不存在任何自动化的大小写转换。而是触发的事件名需要完全匹配监听这个事件所用的名称。所以建议始终使用 kebab-case 的事件名。
1 | <!-- 此处父组件接受,执行handleGetTotal函数 --> |
以上的 父函数 处理方式是一个方法。如果是在语句中处理,子函数抛出的参数被放在$event
变量中
1 | <!-- 增加字体大小 --> |
$emit
使用方法:1
$emit(自定义事件名称,抛出参数)
组件使用 v-model
v-model:total
与 this.$emit('input',this.counter)
实现子组件动态更新父组件数据。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18<my-component v-model="total"></my-component>
<script>
//组件内
template:'<button @click="handleClick">+1</button>'
data:function(){
return {counter:0}
}
methods: {
handleClick: function() {
this.counter++;
this.$emit('input',this.counter);
}
}
//new Vue父组件内
data: {
total: 0
}
</script>
此处$emit
使用input
作为特殊事件名,而父组件并没有使用@input="hander"
监听,也没有对应的method中有hander这个函数来更新参数total。
v-model:total
可以说就是一种语法糖。他就是相当于完成了以上的操作,自定义事件input接受子组件的请求,并且完成对应total的数据更新。一个组件上的v-model
默认会利用名为value
的prop和名为input
的事件
v-model 双向数据绑定
其实现的功能就跟 在父组件内部中使用v-model 一样,会同步数据。需要满足两个要求:
- 子函数 props 接受一个value属性
- 有新的value时触发input事件
1 |
|
非父子组件通讯
BUS数据总线
利用中间vue,形成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<div id="app">
{{ message }}
<component-a></component-a>
</div>
<script>
var bus = new Vue();
Vue.component('component-a', {
template: '<button @click="handleEvent">传递事件</button>',
methods: {
handleEvent: function () {
//通过bus把事件on-message
bus.$emit('on-message','来自组件component-a的内容')
}
}
});
vat app = new Vue({
el: '#app',
data: {
message: ''
},
mounted: function(){
var _this = this;
//在实例初始化时,监听来自bus实例的事件
bus.$on('on-message', function (msg){
_this.message = msg;
})
}
})
</script>
BUS可以实现任何组件间的通讯,包括父子,兄弟,跨级。还可以添加data、methods、computed等选项,这都都是公用的。
父链 子链 根
在子组件中,使用this.$parent
可以直接访问该组件的父实例和组件
在父组件中,使用this.$children
可以访问他的所有的子组件
在子组件中,使用this.$root
可以用访问他的根实例
1 | //this.$parent |
子组件索引
在使用$children
会返回全部子组件,另外Vue提供了子组件索引的方法来定位组件 ———— ref1
2
3
4
5
6
7
8
9
10//为子组件设定索引名称
<component-a ref="comA"></component-a>
...
methods: {
handleRef: function() {
//定位子组件
var msg = this.$refs.comA.message;
concole.log(msg);
}
}
依赖注入
可以把依赖注入看作一部分“大范围有效的 prop”,除了:
- 祖先组件不需要知道哪些后代组件使用它提供的属性
- 后代组件不需要知道被注入的属性来自哪里
provide
选项允许我们指定我们想要提供给后代组件的数据/方法。
在任何后代组件里,我们都可以使用
inject`选项来接收指定的我们想要添加在这个实例上的属性
1 | //父组件 |
slot(插槽)
slot————内容分发,父组件向子组件分发HTML内容
- 父组件需要分发的内容卸载子组件标签内
- 子组件模板(template)中使用
<slot>
标签 - 当父组件没有插入内容时,
<slot>
标签中的内容默认出现
1 | <div id="app"> |
具名 slot
具有name属性的slot,可以为slot识别分类插入至子组件模板的不同地方
- 父组件中用
slot="Name"
作为标签的属性 - 子组件中
<slot name="Name">
作为对应插槽位置 - 具有name属性的slot,可以与普通slot混合使用
1 | <div id="app"> |
其他
$nextTick
解决异步更新队列产生的不同步问题
Vue在观察到数据变化时并不是直接更新DOM,而是开启一个队列,并缓冲在同一时间循环中发生的所有数据改变。在缓冲时会取出重复数据,从而避免不必要的计算和DOM操作。然后在下一个时间循环tick中,Vue刷新队列并执行实际(已去重的)工作。1
2
3
4
5
6//更改DOM
this.$nextTick(function (){
//读取新更新的DOM,需要使用$nextTick
var text = document.getElementById('div').innerHTML;
console.log(text);
})
在动态组件上使用 keep-alive
动态组件是用is
特性来切换不同组件:1
<component v-bind:is="currentTabComponent"></component>
这种情况下每次切换组件都会创建一个新的currentTabComponent
实例。不会记录实例状态,解决这个问题,比如保存页面状态,可以用<keep-alive>
元素将动态组件包裹起来
单文件组件
通常使用 Vue.component
来定义全局组件,紧接着用 new Vue({ el: '#container '})
在每个页面内指定一个容器元素。这种方式有很多缺点:
- 全局定义 (Global definitions) 强制要求每个 component 中的命名不得重复
- 字符串模板 (String templates) 缺乏语法高亮,在 HTML 有多行的时候,需要用到丑陋的
- 不支持 CSS (No CSS support) 意味着当 HTML 和 JavaScript 组件化时,CSS 明显被遗漏
- 没有构建步骤 (No build step) 限制只能使用 HTML 和 ES5 JavaScript, 而不能使用预处理器,如 Pug (formerly Jade) 和 Babel
文件扩展名为.vue
的single-file components
(单文件组件) 为以上所有问题提供了解决方法,并且还可以使用 webpack 或 Browserify 等构建工具。
.vue
文件格式如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19<template>
<p>{{greeting}}</p>
</template>
<script>
module.exports = {
data: function(){
return{
greeting: 'hello'
}
}
}
</script>
<style scoped>
p {
font-size: 2em;
text-align: center;
}
</style>