Vue中的组件
组件是可复用的Vue实例,且带有一个名字,因为组件是可复用的Vue实例,所以和new Vue()
接收相同的参数,Vue使用Vue.component()
注册全局组件,使用components
属性注册局部组件
组件的基本使用
- 由于HTML大小写不敏感,所以定义组件名称时的驼峰命名法在HTML中使用短横线连接
- 由于HTML规范,比如tbody中只能是tr等,可能导致组件渲染时可能结构就会出问题,可以使用
is
属性来指定使用的是组件,is
属性方式可以使用驼峰命名法,也可使用短横线连接 - 为了组件的复用性,数据互相独立,要求组件中的
data
参数必须是一个方法 template
中必须有一个根元素
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>hello world</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script><!-- CDN方式引入Vue -->
</head>
<body>
<div id="app">
<count-component></count-component> <!-- 使用全局组件 -->
<count-component></count-component> <!-- 复用组件 -->
<div is="countComponent"></div> <!-- 使用is方式使用组件,可采用驼峰命名法,也可采用短横线连接 -->
<hello-component></hello-component> <!-- 使用局部子组件 -->
</div>
<script>
Vue.component("countComponent",{ //注册全局组件
template: `
<div>
<h1 @click="add">{{count}}</h1>
<strong>测试是否必须有根元素</strong>
</div>
`, //组件模板
data(){ return { count: 0 } }, //组件绑定数据,data必须是方法
methods: { add(){ this.count++; } } //组件中的方法
});
var app = new Vue({ //创建Vue实例
el: "#app",
components: { //注册局部组件
helloComponent: { template: `<h1>hello component</h1>` }
}
});
</script>
</body>
</html>
组件之间的信息传递
父子组件之间的信息传递
- 父组件通过属性的方式向子组件传递信息,子组件通过
props
属性进行接收 - 子组件通过
this.$emit("自定义事件名",...参数)
事件触发的方式向父组件传递信息,父组件通过监听该事件来接收
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>hello world</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script><!-- CDN方式引入Vue -->
</head>
<body>
<div id="app">
<test-component arg="hello world" @custom="fun"></test-component> <!-- 使用绑定方式传递的是JavaScript代码,所以是数字 -->
</div>
<script>
Vue.component("testComponent", { //注册全局组件
props: ["arg"], //props用于接收接收父组件传递进来的数据
template: `<h1 @click="$emit('custom', 'bye world')">{{arg}}</h1>`, //组件模板
});
var app = new Vue({ //创建Vue实例
el: "#app",
methods: {
fun(arg){ console.log(arg); } //接收到子组件触发传递的参数
}
});
</script>
</body>
</html>
prop
- 由于HTML大小写不敏感,所以在HTML中使用短横线连接,在子组件接收属性时使用驼峰命名法
- 单向数据流,即子组件不能修改父组件传递的信息,因为当传递参数是对象时,是引用传递,当前子组件修改后会影响到其他地方使用该对象
- 将
props
定义为对象,可为传递参数提供一个带有验证需求的对象,就可以对传入 - 对于未接收的属性,会再子组件的根部添加上该子定义的属性,可在组件中使用
inheritAttrs: false
对此特性进行关闭
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>hello world</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script><!-- CDN方式引入Vue -->
</head>
<body>
<div id="app">
<test-component :count="10" data-test="test"></test-component> <!-- 使用绑定方式传递的是JavaScript代码,所以是数字 -->
<test-component count="100" data-test="test"></test-component> <!-- 未使用绑定方式传递的是字符串 -->
<verify-component
content-one="100"
:content-two="100"
data-test="test"
></verify-component>
</div>
<script>
Vue.component("testComponent", { //注册全局组件
props: ["count"], //字符串数组方式没有任何类型校验
data() { return { number: this.count } }, //组件绑定数据,不允许子组件进行修改传递的数据,所以拷贝父组件传递的参数
template: `<h1 @click="number++">{{number}}</h1>` //组件模板
});
Vue.component("verifyComponent", { //注册全局组件
props: {
"contentOne": String, //此时传递必须为字符串,类型是原生构造函数
"contentTwo": [Number, String], //此时传递必须为数字或字符串
"contentThree": {
type: String, //参数类型
required: false, //是否为必须参数
default: "100", //未传入时的默认值
validator(value){return true} //自定义校验函数,若通过返回true即可,value是传入的参数
}
},
inheritAttrs: false, //关闭非prop特性
template: `<h1>{{contentOne}}、{{contentTwo}}、{{contentThree}}</h1>` //组件模板
});
var app = new Vue({ //创建Vue实例
el: "#app"
});
</script>
</body>
</html>
自定义事件
- 由于HTML大小写不敏感,所以事件的命名只能使用短横线连接,不能使用驼峰命名法
- 默认组件上的事件都是自定义事件,可使用
.native
事件修饰符可为子组件绑定原生事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>hello world</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script><!-- CDN方式引入Vue -->
</head>
<body>
<div id="app">
<test-component @child-event="handle"></test-component>
<click-component @click="handle"></click-component>
<native-component @click.native="handle"></native-component>
</div>
<script>
Vue.component("testComponent", { //注册全局组件
template: `<h1 @click="$emit('child-event')">test component</h1>` //事件名只能使用短横线
});
Vue.component("clickComponent", { //注册全局组件
template: `<h1 @click="$emit('click')">click component</h1>` //必须向父组件传递自定义事件,此时click是自定义事件
});
Vue.component("nativeComponent", { //注册全局组件
template: `<h1>native component</h1>` //使用.native可为组件监听原生事件
});
var app = new Vue({ //创建Vue实例
el: "#app",
methods: {
handle(){ console.log("事件触发"); }
}
});
</script>
</body>
</html>
非父子组件之间的信息传递
将Vue实例绑定在Vue的原型上,这样所有创建出的Vue实例都会多带一个辅助的Vue实例,并且所以Vue实例中的辅助Vue实例是同一个,通过该辅助Vue实例进行事件的监听和触发即可达到非父子组件的信息传递,该模式也就是观察者模式,Vue中也叫总线
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>hello world</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script><!-- CDN方式引入Vue -->
</head>
<body>
<div id="app">
<h1>click 后会将tow中的内容修改</h1>
<component-one content="one中的内容"></component-one>
<component-two></component-two>
</div>
<script>
Vue.prototype.bus = new Vue(); //挂载Vue实例到Vue原型上
Vue.component("componentOne",{ //注册全局组件
props: ["content"],
template: `<h1 @click="clickHandle">component one:{{content}}</h1>`,
methods: {
clickHandle(){
this.bus.$emit("click",this.content); //使用bus进行自定义事件的触发
}
}
});
Vue.component("componentTwo",{ //注册全局组件
data(){ return {content: "two原来的内容"} },
template: `<h1>component tow :{{content}}</h1>`,
mounted(){ //当元素挂在到页面上之后
this.bus.$on("click",(content)=>{ //监听bus的自定义事件
this.content = content;
})
}
});
var app = new Vue({ //创建Vue实例
el: "#app"
});
</script>
</body>
</html>
列表渲染组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>hello world</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script><!-- CDN方式引入Vue -->
</head>
<body>
<div id="app">
<table>
<tbody>
<tr is="count-component" v-for="(item,index) in counts" :key="item" @delete="deleteHandle"
:count="item" :index="index"> <!-- 由于HTML5的规范,这里只能使用is方式才能正确渲染 -->
</tr> <!-- 列表渲染组件,并传递数组值和索引给子组件 -->
</tbody>
</table>
</div>
<script>
Vue.component("countComponent",{ //注册全局组件
props: ["count","index"], //props用于接收接收父组件传递进来的数据,但是不允许子组件进行修改传递的数据
template: `<tr><td @click="$emit('delete',index)">{{count}}</td></tr>`, //组件模板
});
var app = new Vue({ //创建Vue实例
el: "#app",
data(){ return { counts: [0, 1, 2, 4, 5] } },
methods: {
deleteHandle(index){
this.counts.splice(index,1); //删除数组中该索引内容
}
}
});
</script>
</body>
</html>
访问子组件实例或子元素
为子元素或子组件使用ref
属性进行标记,之后在父元素或父组件中的this.$refs
对象中拿到该子元素或子组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>hello world</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script><!-- CDN方式引入Vue -->
</head>
<body>
<div id="app">
<div ref="hello" @click="clickHandle">hello world</div>
<hello-component ref="component" @click.native="clickHandle"></hello-component>
</div>
<script>
Vue.component("helloComponent",{ //注册全局组件
template: `<h1>hello component</h1>`,
});
var app = new Vue({ //创建Vue实例
el: "#app",
methods: {
clickHandle(){
console.log(this.$refs.hello); //子元素
console.log(this.$refs.component); //子组件
}
}
});
</script>
</body>
</html>
插槽
用于向组件中传递DOM元素,提高了组件的多样性
简单插槽使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>hello world</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script><!-- CDN方式引入Vue -->
</head>
<body>
<div id="app">
<solt-component></solt-component> <!-- 未传入DOM元素的插槽将显示插槽中的默认内容 -->
<solt-component>
<strong>传入插槽中的内容</strong> <!-- 传入DOM元素的插槽将替换插槽中的默认内容 -->
</solt-component>
</div>
<script>
Vue.component("soltComponent",{ //注册全局组件
template: `<p>
<strong>hello solt</strong>
<slot><em>default content</em></slot>
</p>`,
});
var app = new Vue({ //创建Vue实例
el: "#app"
});
</script>
</body>
</html>
具名插槽
在Vue 2.6.0中,具名插槽和作用域插槽引入了一个新的统一的语法 (即
v-slot
指令)。它取代了slot
和slot-scope
这两个目前已被废弃但未被移除
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>hello world</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script><!-- CDN方式引入Vue -->
</head>
<body>
<div id="app">
<anonymity-slot>
<strong>left</strong>
<strong>right</strong>
</anonymity-slot>
<!-- 此时会发现两个标签分别被渲染了两遍,这是因为一个slot中的内容其实是组件包裹的所有DOM元素 -->
<named-slot>
<template v-slot:left> <!-- 具名插槽使用template进行包裹,并使用v-slot:帮i的那个插槽名-->
<strong>left</strong>
</template>
<template v-slot:default> <!-- 对于匿名插槽,Vue会分配一个default的名字,当然不写template会默认渲染到匿名插槽中 -->
<strong>default</strong>
</template>
<template #right> <!-- 也可使用 #插槽名 的简写形式-->
<strong>right</strong>
</template>
</named-slot>
<!-- 此时会发现正确渲染了,但是这种方式有点繁琐,若对应的插槽名刚好是HTML标签,也可以使用下面方式简写 -->
<html-slot>
<header>
<strong>header</strong>
</header>
<strong>default</strong>
<footer>
<strong>footer</strong>
</footer>
</html-slot>
</div>
<script>
//匿名插槽
Vue.component("anonymitySlot",{ //注册全局组件,希望外部传递两个slot
template: `<p>
<slot></slot>
<em>hello solt</em>
<slot></slot>
</p>`,
});
//具名插槽
Vue.component("namedSlot",{ //注册全局组件,希望外部传递两个slot
template: `<p>
<slot name="left"></slot>
<slot></slot>
<slot name="right"></slot>
</p>`,
});
Vue.component("htmlSlot",{ //注册全局组件,希望外部传递两个slot
template: `<p>
<slot name="header"></slot>
<slot></slot>
<slot name="footer"></slot>
</p>`,
});
var app = new Vue({ //创建Vue实例
el: "#app"
});
</script>
</body>
</html>
作用域插槽
在Vue 2.6.0中,具名插槽和作用域插槽引入了一个新的统一的语法 (即
v-slot
指令)。它取代了slot
和slot-scope
这两个目前已被废弃但未被移除
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>hello world</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script><!-- CDN方式引入Vue -->
</head>
<body>
<div id="app">
<scope-slot>
<template #default="slotProps"><!-- 绑定slotProps对象,插槽绑定数据会挂在该对象上 -->
<li>{{slotProps}}</li>
</template>
</scope-slot>
</div>
<script>
Vue.component("scopeSlot",{ //注册全局组件
data(){ return { array: [1, 2, 3] } }, //插槽绑定的数据,父级可通过绑定对象方式访问到
template: `<ul>
<slot v-for="item in array" :item="item"></slot>
</ul>`,
});
var app = new Vue({ //创建Vue实例
el: "#app"
});
</script>
</body>
</html>
Vue中自带组件
component
component是Vue自带的组件,用于做动态组件,使用is
属性进行绑定组件名称(不仅使用component标签可实现动态组件,任意标签都可以,但是component更加语义化)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>hello world</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script><!-- CDN方式引入Vue -->
</head>
<body>
<div id="app">
<component :is="componentType"></component> <!-- 不仅使用component标签可实现动态组件,任意标签都可以,但是component更加语义化 -->
<p>keep-alive组件可使组件不会直接销毁再重建,而是会缓存在内存中,所以输入框中内容还在</p>
<keep-alive>
<component :is="componentType"></component>
</keep-alive>
<button @click="clickHandle">切换</button>
</div>
<script>
Vue.component("componentOne",{ //注册全局组件
template: `<div>componentOne:<input type="text"></div>`,
});
Vue.component("componentTwo",{ //注册全局组件
template: `<div>componentTwo</div>`,
});
var app = new Vue({ //创建Vue实例
el: "#app",
data(){ return { componentType: "componentTwo" } },
methods: {
clickHandle(){
this.componentType = this.componentType === "componentOne" ? "componentTwo" : "componentOne";
}
}
});
</script>
</body>
</html>
keep-alive
keep-alive是Vue自带的组件,用于缓存DOM或组件,可接收以下两个属性,可是数组、正则和逗号分隔的字符串
include
:匹配组件会被缓存exclude
:匹配组件不会会被缓存
于此同时,使用keep-alive
组件包裹的组件会多出以下两个生命周期钩子
activated
:被keep-alive缓存的组件激活时调用deactivated
:被keep-alive缓存的组件停用时调用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>hello world</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script><!-- CDN方式引入Vue -->
</head>
<body>
<div id="app">
<h2>使用keep-alive缓存的组件mounted只会执行一次,destroyed不会执行,因为被缓存了并不会频繁创建和销毁</h2>
<h2>activated和deactivated会被频繁执行</h2>
<keep-alive>
<component :is="componentType"></component>
</keep-alive>
<keep-alive exclude="componentOne"> <!-- 排除componentOne的缓存 -->
<component :is="componentType"></component>
</keep-alive>
<button @click="clickHandle">切换</button>
</div>
<script>
Vue.component("componentOne",{ //注册全局组件
template: `<div>componentOne:<input type="text"></div>`,
activated(){console.log("activated");},
deactivated(){console.log("deactivated");},
mounted(){console.log("mounted");},
destroyed(){console.log("destroyed");}
});
Vue.component("componentTwo",{ //注册全局组件
template: `<div>componentTwo</div>`,
});
var app = new Vue({ //创建Vue实例
el: "#app",
data(){ return { componentType: "componentTwo" } },
methods: {
clickHandle(){
this.componentType = this.componentType === "componentOne" ? "componentTwo" : "componentOne";
}
}
});
</script>
</body>
</html>
Comments NOTHING