一、组件
(一)什么是组件
组件(Component)是 Vue.js最强大的功能之一。组件可以扩展 HTML元素,封装可重用的代码组件是自定义元素(对象)。
(二)创建组件的两种方式
官方推荐组件标签名是使用-
连接的组合词,例如:<my-hello></my-hello>
。
1、使用构造器创建组件
使用这种方式创建组件首先需要使用Vue.extend()
创建一个组件构造器,然后使用Vue.component(标签名,组件构造器)
,根据组件构造器来创建组件。
//1.创建构造器
var MyComponent=Vue.extend({
template:'<h3>Hello World</h3>'
});
//2.创建组件
Vue.component('my-hello',MyComponent);
//3.使用组件
<div id="app">
<my-hello></my-hello>
</div>
这种创建组件的方式比较麻烦,使用的较少。
2、直接创建组件
使用Vue.component(标签名,组件模板)
,根据组件构造器来创建组件。
//1.创建组件
Vue.component('my-world', {
template: '<h2>hello vue.js</h2>'
});
//2.使用组件
<div id="app">
<my-world></my-world>
</div>
(三)组件的分类
组件分为全局组件和局部组件。
1、全局组件
使用Vue.component()
创建的组件都是全局组件。这样的组件在任何组件内都能使用。上面我们创建就是全局组件。
2、局部组件
局部组件一般都是定义在实例的选项中,称为实例的子组件。相应的,实例也被称为父组件。
//1.定义组件
new Vue({
el: '#app',
components: {
dawei: {
template: '<h2>my name is dawei</h2>'
}
}
});
//2.使用组件
<div id="app">
<dawei></dawei>
</div>
(四)引用模板
很多时候我们的template
模板中需要存放很多标签内容,这样的话写起来会很麻烦。这时候我们可以使用template
标签。
用法如下:
<template id="wbs"> //使用template标签
<div>
<h2>hello {{msg}}</h2>
<ul>
<li v-for="value in arr">
{{value}}
</li>
</ul>
</div>
</template>
new Vue({
el: '#app',
components: {
'my-dawei': {
template: '#wbs', //选择template标签
data() {
return {
msg: 'vue.js',
arr: ["a", "b", "c", "d"]
}
}
}
}
});
这里涉及到的几个知识点得着重提一下:
- 在
template
模板中,所有的元素必须放置在一个根元素中,要不然会报错。例子中我们将元素放置在了<div>
标签中。 - 组件中的
data
选项必须是一个函数类型,使用return
返回所有的数据。
(五)动态组件
很多时候项目中需要在某一个地方动态的使用不同的组件,这时候就需要使用动态组件。
动态组件的使用需要绑定is
属性:
<component :is="flag"></component>
简单示例:
//点击按钮显示不同的组件
<div id="app">
<button type="button" @click="flag='my-a'">显示a组件</button>
<button type="button" @click="flag='my-b'">显示b组件</button>
<component :is="flag"></component> //传入flag
</div>
new Vue({
el: '#app',
data: {
flag: 'my-a' //给flag赋值
},
components: {
'my-a': {
template: '<p>我是a组件</p>',
},
'my-b': {
template: '<p>我是b组件</p>'
}
}
});
(六)keep-alive组件
使用keep-alive
组件缓存非活动组件,可以保留状态,避免重新渲染,默认每次都会销毁非活动组件并重新创建。
使用范例:
<keep-alive>
<component :is="flag"></component>
</keep-alive>
<div id="app">
<button type="button" @click="flag='my-x'">x</button>
<button type="button" @click="flag='my-y'">y</button>
<keep-alive>
<component :is="flag"></component>
</keep-alive>
</div>
new Vue({
el: '#app',
data: {
flag: 'my-x'
},
components: {
'my-x': {
template: '<p>{{x}}</p>',
data() {
return {
x: Math.random()
}
}
},
'my-y': {
template: '<p>{{y}}</p>',
data() {
return {
y: Math.random()
}
}
}
}
});
这样的话,第一次产生的随机数就会被缓存,再次切换的时候也不会发生改变。
二、 组件间数据传递
(一)父子组件
在一个组件内部定义另一个组件,那么这对组件称为父子组件。子组件只能在父组件内部使用。默认情况下,每个组件实例的作用域是独立的,子组件无法访问父组件中的数据,同样,父组件也无法访问子组件中的数据。
<div id="app">
<my-a></my-a>
<!-- 父组件 -->
</div>
<template id="a">
<div>
<p>{{msg}}</p>
<my-b></my-b> <!-- 在父组件中调用子组件 -->
</div>
</template>
<template id="b">
<div>
<p>{{mydata}}</p>
</div>
</template>
<script>
new Vue({ //根组件
el: '#app',
components: { //子组件写在components选项中
"my-a": { //b组件的父组件
template: "#a",
data() {
return {
msg: '我是父组件',
}
},
components: { //子组件写在父组件的components选项中
"my-b": {
template: "#b",
data() {
return {
mydata: "我是子组件"
}
}
}
}
}
}
});
</script>
(二)组件间数据传递(通信)
1、子组件访问父组件的数据
步骤:
- a、调用子组件时,绑定想要获取的父组件中的数据
- b、在子组件内部,使用props选项声明获取的数据,即接收来自父组件的数据
改进上面的例子:
<div id="app">
<my-a></my-a>
</div>
<template id="a">
<div>
<p>{{msg}}</p>
<p>这是要传递给子组件的值:{{myname}}</p>
<my-b :name="myname"></my-b> <!-- 绑定子组件想要获取的数据 -->
</div>
</template>
<template id="b">
<div>
<p>{{mydata}}</p>
<p>这是父组件传递过来的数据:{{name}}</p>
</div>
</template>
<script>
new Vue({
el: '#app',
data: {},
components: {
"my-a": {
template: "#a",
data() {
return {
msg: '我是a组件',
myname: '子组件b你好,我是父组件a'
}
},
components: {
"my-b": {
template: "#b",
data() {
return {
mydata: "我是b组件"
}
},
props: ["name"] //子组件使用props声明想要获取的数据
}
}
}
}
});
</script>
2、父组件访问子组件的数据
步骤:
- a 在子组件中使用vm.$emit(事件名,数据)触发一个自定义事件,将数据发送给父组件,事件名自定义
- b 父组件在使用子组件的地方监听子组件触发的事件,并在父组件中定义方法,用来获取数据
//子组件‘my-b’内部
methods:{
send(){//使用$emit()触发一个事件,发送数据,this指当前子组件实例
this.$emit('e-world', this.senddata);
}
}
//在调用子组件的地方监听子组件触发的事件,调用自己的方法获取数据
<my-b @e-world="getData"></my-b>
methods: {
getData(data) { //参数是子组件传递过来的数据
this.revicedata = data;
}
}
3、单向数据流
props是单向数据绑定的,当父组件数据发生变化时,将传递给子组件,但是不会反过来。而且不允许子组件直接修改父组件中的数据,强制修改会报错。
解决方案:
- 如果子组件想把它作为局部数据来使用,可以将数据存入另一个变量中再操作,不影响父组件中的数据
- 如果子组件想修改数据并且同步更新到父组件,两个方法:
- 使用.sync显式地触发一个更新事件(1.0版本中支持,2.0版本中不支持,2.3版本又开始支持)
//使用.sync
<my-b :name.sync="myname"></my-b>
//子组件修改父组件传入的值name,触发update更新事件
this.$emit('update:name', "vuejs");
- 可以将父组件中的数据包装成对象,然后在子组件中修改对象的属性(因为对象是引用类型,指向同一个内存空间),推荐使用这种方式。
data() {
return { //将要传递的数据放入message对象中
message: {
hello: '子组件b你好,我是父组件a'
}
}
}
<my-b :message="message"></my-b> //传递这个对象给子组件
methods: { //在子组件内部修改这个值,这样就会同步传递给父组件。
edit() {
this.message.hello = "hahahahh";
}
}
4. 非父子组件间的通信
非父子组件间的通信,可以通过一个空的Vue实例作为中央事件总线(事件中心),用它来触发事件和监听事件,从而实现非父子组件间的通信。
var Event = new Vue(); //空vue实例
methods: {
send() { //触发emit事件
Event.$emit("hello", this.asmsg);
}
}
mounted() { //在子组件的钩子函数中监听该事件
Event.$on('hello', data => { //获取值
this.bsmsg = data;
})
}
三、slot内容分发
用来获取组件中的原内容
var vm = new Vue({
el: '#app',
components: {
'my-hello': {
template: '#hello'
}
}
});
<div id="app">
<my-hello>hello vue.js</my-hello>
</div>
<template id="hello">
<div>
<slot>如果没有原内容,则显示该内容</slot>
</div>
</template>
如果组件标签中没有内容就会显示slot中的内容,这也就是所谓的单个插槽。
还可以对显示的内容进行分组,这就是具名插槽,可以操作标签组中的内容:
<div id="app">
<my-hello>
<ul slot="s1">
<li>aaa</li>
<li>bbb</li>
<li>ccc</li>
</ul>
<ol slot="s2">
<li>111</li>
<li>222</li>
<li>333</li>
</ol>
</my-hello>
</div>
<template id="hello">
<div>
<slot name="s2"></slot> //为插槽指定名称 将名为s2的内容放置在这里
<p>hello vue.js</p>
<slot name="s1"></slot> //将名为s1的内容放置在这里
</div>
</template>
这样,就可以对组件中的内容实时操作。