组件(Component)是Vue.js 最强大的功能之一,它是html、css、js等的一个聚合体。封装性和隔离性非常强。
组件相当于Python中的模块,扩展HTML元素,可以重复使用的代码,使用它就是为了重复使用
例如:一个轮播图需要使用放到很多页面当中使用,一个轮播图有它自己的js、css、html,而组件就可以快捷的做出一个轮播图,有自己的js、css、html放到一起,有自己的逻辑样式这样到哪里都可以使用了,无需在写重复代码
语法
Vue.component('组件名称', { })
,第1个参数是标签名称,第2个参数是一个选项对象。全局组件注册后,任何vue实例都可以用。
组件注意事项:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Documenttitle>
<script src="../js/vue.js">script>
head>
<body>
<div id="div">
<h1>组件h1>
<button @click="handleClick">点击显示button>
<hr>
<child v-if="show==true">child>
div>
<script>
var vm = new Vue({
el:'#div',
data:{
show:false,
},
methods:{
handleClick(){
this.show=!this.show
}
}
})
//定义全局组件,一次定义,随时使用
//一但声明完成,就可以在所有的组件中直接使用,无需引入和注册
Vue.component('Child',{
//模版字符串
template:`
{{name}}
`,
/*
render 用于直接生成虚拟dom(生成标签)
在工程化中,render中可以直接写jsx,在引入一个babel可以写jsx语法(js的增强版本)
render(h) {
// h(生成的标签名称,标签中有哪些属性(没有属性就是null),子元素是什么)
let vnode = h('h3', { attrs: { name: 'abc', style: 'color:red' } }, '我是一个标签')
return vnode
}
*/
data(){
return{
name:'tom',
}
},
methods:{
clickname(){
this.name='jack'
}
}
})
script>
body>
html>
没有使用工程化时,我们使用浏览器解析标签,所以定义组件不能使用单标签写法,否则组件无法被多次执行,因为但标签写法浏览器在解析的时候觉得有问题,不再执行后续标签。而在工程化中,我们会使用很多包来编译html标签,单标签写法是允许的
单文件局部组件
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Documenttitle>
<script src="../js/vue.js">script>
head>
<body>
<div id="div">
<child>child>
div>
<script>
// 创建局部组件,它就是一个对象
// 局部组件,在创建完成后,如果你要给别人使用,一定要在配置中进行对应的配置
var child = {
template:`
自定的局部组件
{{title}}
`,
data(){
return {
title:'hello world',
}
}
}
var vm = new Vue({
el:'#div',
data:{
},
methods:{
},
//局部组件要加s
components:{
// key就是在使用时的标签名称
// value就是对应的局部组件对象
// child: child
// 简写
child
}
})
script>
body>
html>
直接编写在组件内部的局部组件
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Documenttitle>
<script src="../js/vue.js">script>
head>
<body>
<div id="div">
<child>child>
<hr>
<mcq>mcq>
div>
<script>
var child = {
template:`
自定的局部组件
{{title}}
`,
data(){
return {
title:'hello world',
}
}
}
//注意根组件与全局组件无任何关联关系,需放在根组件前面执行,否则会报错
//在全局组件内定义局部组件
Vue.component('mcq',{
template:`
我是全局组件
`,
data(){
return {}
},
//在内部定义的局部组件
components:{
xxx:{
template:`
我是局部组件
`,
data(){
return {}
},
}
}
})
var vm = new Vue({
el:'#div',
data:{
},
methods:{
},
//局部组件要加s
components:{
// key就是在使用时的标签名称
// value就是对应的局部组件对象
// child: child
// 简写
child
}
})
script>
body>
html>
组件与组件之间的嵌套使用避免不了数据之间的传递。那么Vue中组件的数据是如何传递的呢?组件间数据传递不同于Vue全局的数据传递,
组件实例的数据之间是孤立的,不能在子组件的模板内直接引用父组件的数据。
如果要把数据从父组件传递到子组件,就需要使用props属性。在Vue中,父子组件的关系可以总结为:prop向下传递,事件向上传递。
父组件通过prop给子组件下发数据,子组件通过事件给父组件发送消息。所以我们也会经常遇到组件之间需要传递数据的时候,大致分为四种情况:
1.通过自定义属性---自定义的变量不能用驼峰,不要与子组件中的变量冲突
父组件里 <child :name"name" ></child>
2. 子组件中引用props,可以指定自定义属性的类型,也可以直接用数组
props:{name:String} // props:['name'] /可以接收多个
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Documenttitle>
<script src="../js/vue.js">script>
head>
<body>
<div id="div">
<h1>组件通信之父传子:自定义属性h1>
父组件中的名字:{{name}}
<hr>
<global :name="name">global>
div>
<script>
Vue.component('global',{
template:`
我是global组件
父组件传递给子组件的:{{name}}
`,
data(){
return{}
},
props:['name',]
})
var vm = new Vue({
el:'#div',
data:{
name:'jack',
},
methods:{},
})
script>
body>
html>
上面说到父组件向子组件传值,子组件处理完这些数据后可能会返回给父组件一些数据,此时就不能使用
props来处理了而是通过自定义事件来实现。因为子组件什么时候处理完数据是不确定的,因此需要捕获子组件触发的事件得知子组件的状态。简单说就是当子组件处理数据完成之后触发事件,父组件通过$emit(‘事件名称’)捕获子组件发出的事件进行相应的处理即可。
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Documenttitle>
<script src="../js/vue.js">script>
head>
<body>
<div id="div">
<h1>组件通信之子传父:自定义事件h1>
父组件接收的名字:{{p_name}}
<hr>
<global @myevent="handleEvent">global>
div>
<script>
Vue.component('global',{
template:`
我是global组件
`,
data(){
return{
name:'jack',
}
},
methods:{
handleSend(){
this.$emit('myevent',this.name)
}
}
})
var vm = new Vue({
el:'#div',
data:{
p_name:'',
},
methods:{
handleEvent(name){
console.log('接收到了子组件传递的名字:'+name)
this.p_name=name
}
},
})
script>
body>
html>
ref被用来给元素或组组件注册引用信息
。引用信息将会被注册在父组件的$refs对象上
。如果在普通的DOM元素上使用,引用指向的就是DOM元素;如果用在子组件上,引用就指向组件实例
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Documenttitle>
<script src="../js/vue.js">script>
head>
<body>
<div id="div">
<h1>ref属性放在普通标签上h1>
名字:<input type="text" v-model="name" ref="myinput">
<p/>
<button @click="handlePrint">点击传递button>
<p/>
<hr>
<h1>ref属性放在组件上h1>
<child ref="mychild">child>
div>
<script>
Vue.component('Child',{
template:`
名字:{{name}}
年龄:{{age}}
`,
data(){
return {
name:'jack',
age:'19'
}
},
methods:{
handleClick(){
alert('名字为:'+this.name+','+'年龄为:'+this.age)
}
}
})
var vm = new Vue({
el:'#div',
data:{
name:'',
},
methods:{
handlePrint(){
// 1 ref 属性放在普通标签上,拿到标签的dom对象
/* 通过this.$refs可以拿到所有标签上写了ref属性的 标签 ,
对象类型 key值是ref对应的value值, value值是原生dom对象
*/
// console.log(this.$refs)
var i = this.$refs['myinput']
console.log(i)
// 直接修改原生dom对象的value属性,input就能看到有值了
i.value = 'hello world'
//2ref 属性放在 组件上,拿到的是 组件对象 ,就可以使用组件对象的属性和方法
console.log(this.$refs) //此时可以看到对象中有两个值,一个是普通对象,一个是组件对象
var n = this.$refs['mychild']
// 就是组件对象,可以 .属性, .方法
console.log(n._data.name) //可以通过_data拿到属性
console.log(n.age) //也可以省略_data拿到属性,和根组件是一样的
n._data.name='tom'
n.age='66'
// 重点:以后就不需要关注是子传父还是父传子了,直接通过对象取值赋值即可,而且可以主动调用子组件中的函数
setTimeout(()=>{
n.handleClick() //触发子组件的点击事件执行
},2000)
// 把子组件属性传递给父组件
this.name=n.name
}
},
})
script>
body>
html>
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Documenttitle>
<script src="../js/vue.js">script>
head>
<body>
<div id="div">
<h1>使用条件渲染实现切换h1>
<button @click="who='index'">首页button>
<button @click="who='goods'">商品页button>
<button @click="who='order'">订单页button>
<hr>
<Home v-if="who=='index'">Home>
<Goods v-else-if="who=='goods'">Goods>
<Order v-else>Order>
<p/>
<hr>
<h1>使用动态组件实现切换h1>
<button @click="type='login'">登录button>
<button @click="type='register'">注册button>
<hr>
<component :is="type">component>
div>
<script>
var vm = new Vue({
el:'#div',
data:{
who:'index',
type:'',
},
methods:{
},
components:{
Home:{
template:`
首页
`,
},
Goods:{
template:`
商品页
`,
},
Order:{
template:`
订单页
`,
},
Login:{
template:`
登录页
`,
},
Register:{
template:`
注册页
`,
data(){
return{
search:''
}
},
},
}
})
script>
body>
html>
从上述代码中,当我们在
注册组件中的input框
中写入数据,我们可以发现一旦切换重新切回来数据就丢失了
。这个是因为每一次的切换都会销毁切换前的组件
,所以数据就没有了。这时我们就需要用到缓存组件
。
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Documenttitle>
<script src="../js/vue.js">script>
head>
<body>
<div id="div">
<h1>使用动态组件实现切换h1>
<button @click="type='login'">登录button>
<button @click="type='register'">注册button>
<hr>
<keep-alive>
<component :is="type">component>
keep-alive>
div>
<script>
var vm = new Vue({
el:'#div',
data:{
type:'',
},
components:{
Login:{
template:`
登录页
`,
destroyed(){ //销毁之后执行,
console.log('执行销毁login了')
}
},
Register:{
template:`
注册页
`,
data(){
return{
search:''
}
},
destroyed(){ //销毁之后执行,
console.log('执行销毁register了')
}
},
},
})
script>
body>
html>
注意:
- 缓存组件中的元素并没有被销毁,而是处于未被激活状态,tab 切换时的性能得到很大的提升
- 缓存组件带来的问题是,组件的初始化阶段生命周期函数只执行一次
Vue 提供了 activated 和 deactivated 两个生命周期钩子函数用来判断组件处于激活状态还是未激活状态。在缓存组件中,虽然初始化生命周期钩子函数只在创建组件时执行一次,但是如果有了这两个钩子函数,我们的代码就可以通过这两个函数进行捕获
- activated 和 deactivated 只有标签中有keep-alive才有用
- keep-alive 标签有两个属性,include 和 exclude。
匿名语法:插入内容(字符或标签) 或者(default可省略) 插入内容(字符或标签)
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Documenttitle>
<script src="../js/vue.js">script>
head>
<body>
<div id="div">
<h1>slot插槽的使用h1>
<hr>
<Home>
<input type="text" placeholder="请输入">
<button>插入的按钮button>
Home>
div>
<script>
var vm = new Vue({
el:'#div',
data:{
},
components:{
Home:{
//定义个插槽,就相当于是电脑上的预留的插槽,
//或者是模版语法的block
template:`
这里是局部组件Home
局部组件结尾处
`,
}
},
})
script>
body>
html>
从上述案例上可以看到,当子组件内的slot不止一个,那么我们如何在父组件中,精确的在想要的位置,插入对应的内容呢?那就取个名字,一个不带 name 的 出口会带有隐含的名字“default”。
所谓的具名插槽就是为每个插槽取个具体的名字。对传递内容进行唯一性的标识。
具名语法:插入内容(字符或标签),
具名缩写:插入内容(字符或标签), (匿名缩写不能省略default)
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Documenttitle>
<script src="../js/vue.js">script>
head>
<body>
<div id="div">
<h1>具名slot插槽的使用h1>
<hr>
<Home>
<div slot="middle">
<input type="text" placeholder="请输入">
<button>插入的按钮button>
div>
<div slot="footer">
<h3><a href="">我们不一样a>h3>
div>
Home>
div>
<script>
var vm = new Vue({
el:'#div',
data:{
},
components:{
Home:{
//定义个插槽,就相当于是电脑上的预留的插槽,
//或者是模版语法的block
template:`
这里是局部组件Home
局部组件结尾处
`,
}
},
})
script>
body>
html>