目录
4.1.3在slot中可以使用当前组件的props和data
1 复用与组件化
1.1 组件概述
Vue的组件能够在代码复用上得胜,一处理编写,到处使用,可以把一个复杂的页面分解成N个组件组成,有了组件后可以像搭积木一样构建出复杂的页面(和QT、Java Swing、Android界面组件等界面编程思想类似)
组件:vue组件可以是一段html代码,也可以html和js组合代码,还可以是html、js和css的组合体,如:
<template>
<div class="model-data-content">
组件的html代码
</div>
</template>
<script>
export default {
data() {
return {
roleData: {
name: '',
remark: '',
}
}
},
methods: {
}
}
</script>
<style scoped>
.model-data-content{
background-color: #363e4f
}
</style>
组件复用:
- 同一个Vue组件可以在同一个应用程序中多次使用
- 同一个Vue组件可以被不同的应用程序使用
div>
<demo-component></demo-component>
<demo-component></demo-component>
</div>
1.2 创建和使用组件
vue的组件遵循先创建再使用的原则,可以全局创建,也可以局部创建
全局创建:
<body>
<div id="app">
<!-- 2. 使用组件 -->
<demo-component></demo-component>
</div>
</body>
<script>
// 1. 创建全局组件
Vue.component('demo-component', {
template: '<h3> this is a component</h3>'
})
new Vue({
el: '#app'
})
</script>
局部创建:
<body>
<div id="app">
<!-- 2. 使用组件 -->
<demo-component></demo-component>
</div>
</body>
<script>
new Vue({
el: '#app',
components: {
// 1. 创建局部组件
'demo-component': {
template: '<h3> this is a component</h3>'
}
}
})
</script>
1.3 组件对象剖析
Vue组件实质上一个VueComponent类的对象,而VueComponent是Vue的子类,因此组件继承了Vue对象的所有方法与属性
<body>
<div id="app">
<demo-component></demo-component>
</div>
</body>
<script>
let demoComponent = Vue.component('demo-component', {
template: '<h3> this is a component</h3>'
})
new Vue({
el: '#app'
})
// 打印组件对象
console.dir(demoComponent)
</script>
组件对象的template属性以HTML字符串形式给出:
<body>
<div id="app">
<demo-component></demo-component>
</div>
</body>
<script>
let demoComponent = Vue.component('demo-component', {
template: `
<div style="background-color: darkgrey;">
版权所有@百度
</div>
`
})
new Vue({
el: '#app'
})
</script>
组件对象的template属性以template标签形式给出:
<body>
<div id="app">
<demo-component></demo-component>
</div>
<template id="footer">
<div style="background-color: darkgrey;">
版权所有@百度
</div>
</template>
</body>
<script>
let demoComponent = Vue.component('demo-component', {
template: '#footer'
})
new Vue({
el: '#app'
})
</script>
1.3.1 组件对象的data属性
在创建组件时,可以使用任何有效的Vue对象属性,常用的就是data属性
data属性需要注意的有两点:
- 注意一:组件中的data属性必须以函数出现,否则程序出错
- 注意二:data属性中的函数必须返回一个独有对象,否则会有对象共享的bug问题
<body>
<div id="app">
<demo-component></demo-component>
</div>
<template id="footer">
<div>
name:{{name}}</br>
age:{{age}}</br>
</div>
</template>
</body>
<script>
let demoComponent = Vue.component('demo-component', {
template: '#footer',
data (){
return {name: 'admin', age: 18} // data属性必须以函数出现
}
})
new Vue({
el: '#app'
})
</script>
data属性中的函数必须返回一个独有对象,如果共享一个对象数据时会产生bug:
<body>
<div id="app">
<demo-component></demo-component>
<hr/>
<demo-component></demo-component>
</div>
<template id="footer">
<div>
name:{{name}}</br>age:{{age}}</br>
<button @click="name = 'haijun'">修改</button>
</div>
</template>
</body>
<script>
let data = {name: 'admin', age: 18}
let demoComponent = Vue.component('demo-component', {
template: '#footer',
data: function (){
return data
}
})
new Vue({
el: '#app'
})
</script>
正确的写法:
<body>
<div id="app">
<demo-component></demo-component>
<hr/>
<demo-component></demo-component>
</div>
<template id="footer">
<div>
name:{{name}}</br>age:{{age}}</br>
<button @click="name = 'haijun'">修改</button>
</div>
</template>
</body>
<script>
let demoComponent = Vue.component('demo-component', {
template: '#footer',
data: function (){
return {name: 'admin', age: 18}
}
})
new Vue({
el: '#app'
})
</script>
1.3.2 组件的其它属性
在创建组件时,可以使用任何有效的Vue对象属性,如data属性、computed属性、methods属性等
- computed:Vue组件中使用的计息属性
- methods属性:Vue组件中使用的自定义方法(函数)
- 其它属性:生命周期回调方法(钩子方法)、过滤器
2 使用porps父子组件传值
2.1 props属性
组件实例之间是独立的,不能在子组件的模板内直接使用父组件的数据
<body>
<div id="app">
<demo-component></demo-component>
</div>
</body>
<script>
let demoComponent = Vue.component('demo-component', {
template: `
<div>this message is from parent : {{message}} </div>
`
})
new Vue({
el: '#app',
data: {
message: 'hello world'
}
})
</script>
这时会报错:组件内部message没定义
props属性:
父元素的数据需要通过 props 传递到子组件中
- 子组件中通过 props 属性,声明待接收的数据名称
- 父元素中使用组件时,通过HTML属性为子组件传递数据
<body>
<div id="app">
<demo-component message="hello world"></demo-component>
</div>
</body>
<script>
let demoComponent = Vue.component('demo-component', {
template: `
<div>this message is from parent : {{message}} </div>
`,
props: ['message'] // 声明要传入该属性的数据
})
new Vue({
el: '#app',
data: {
message: 'hello world'
}
})
</script>
- 注意一:定义子组件时props属性内声明的数据名称是驼峰命名,但是在父元素传入时属性名要用中划线命名
<body>
<div id="app">
<demo-component user-name="admin"></demo-component>
</div>
</body>
<script>
let demoComponent = Vue.component('demo-component', {
template: `
<div>this message is from parent : {{userName}} </div>
`,
props: ['userName'] // 声明要传入该属性的数据
})
new Vue({
el: '#app',
data: {
message: 'hello world'
}
})
</script>
- 注意事项二:若 props指明数据,但使用时未传递该数据,则在组件中该数据为 undefined
- 注意事项三:若传递了未在 props 里声明的数据,则该数据会被当作组件的HTML属性处理
2.2 v-bind属性绑定
可以使用 v-bind 来动态地将 prop 绑定到父元素的数据 ,每当父元素的数据变化时,该变化也会传递给子组件
<body>
<div id="app">
<demo-component user-name="admin"></demo-component>
<demo-component :user-name="name"></demo-component>
</div>
</body>
<script>
let demoComponent = Vue.component('demo-component', {
template: `
<div>this message is from parent : {{userName}} </div>
`,
props: ['userName'] // 声明要传入该属性的数据
})
var vm = new Vue({
el: '#app',
data: {
name: 'haijun'
}
})
</script>
修改父组件的name数据值:
v-bind 另一个作用是可以传递 非字符串型数据(若不使用v-bind形式, 则为字符串数据)
如:对象、true或false、数值型数据
<body>
<div id="app">
<demo-component is-show="true" :is-color="isColor"></demo-component>
</div>
</body>
<script>
let demoComponent = Vue.component('demo-component', {
template: `
<div>this message is from parent : isShow->{{typeof(isShow)}} isColor->{{typeof(isColor)}}</div>
`,
props: ['isShow', 'isColor'] // 声明要传入该属性的数据
})
var vm = new Vue({
el: '#app',
data: {
isColor: true
}
})
</script>
在实际业务场景中,一种是父组件传递的初始值过来,子组件将它作为初始化值保存起来,在自己的作用域下可以随意使用和修改
<body>
<div id="app">
<demo-component :size="size"></demo-component>
</div>
</body>
<script>
let demoComponent = Vue.component('demo-component', {
template: `
<p :style="myStyle">子组件的内容</p>
`,
props: ['size'], // 声明要传入该属性的数据
data(){
return {
myStyle: {
fontSize: this.size + 'px', // 父组件传递的初始值过来,子组件将它作为初始化值保存起来
color: 'red'
}
}
}
})
var vm = new Vue({
el: '#app',
data: {
size: 29
}
})
</script>
另一种情况就是prop作为需要被转变的原始值传入,这种情况可以用计算属性
<body>
<div id="app">
<demo-component :size="size"></demo-component>
</div>
</body>
<script>
let demoComponent = Vue.component('demo-component', {
template: `
<p :style="myStyle">子组件的内容</p>
`,
props: ['size'], // 声明要传入该属性的数据
computed: {
myStyle(){
return {
fontSize: this.size + 'px', // 用计算属性将prop作为需要被转变的原始值传入
color: 'red'
}
}
},
})
var vm = new Vue({
el: '#app',
data: {
size: 29
}
})
</script>
2.3 prop传值时数据验证
props选项的值,除了是一个数组外,当props需要验证时,就需要是一个对象的形式了
<body>
<div id="app">
<demo-component :username="'admin'" :age="22" :is-married="true" :say="sayHello"></demo-component>
</div>
<template id="comp">
<div>
<p>用户名:{{username}}</p>
<p>婚否:{{isMarried}}</p>
<p>爱好:{{hobbies}}</p>
<p><button @click="say">讲话</button></p>
</div>
</template>
</body>
<script>
Vue.component('demo-component', {
template: '#comp',
props: {
username: String,
age: {
type: Number,
default: 18,
validator: function(val){
return val > 0 && val < 200
}
},
isMarried: {
type: Boolean,
required: true
},
hobbies: {
type: Array,
default: function(){
return [1, 2, 3]
}
},
say: {
type: Function
}
},
})
var vm = new Vue({
el: '#app',
data: {
isMarried: false
},
methods: {
sayHello: function(){
alert('hello')
}
},
})
由于在props中对age进行验证,在使用组件时如果age的值传为2000,则报下面的错:
3 组件间的通信
组件之间必然需要相互通信:
- 父组件可能要给子组件下发数据
- 子组件则可能要将它内部发生的动态行为告知父组件
- 非父子组件之间也可能需要数据和行为的通信
3.1 父子组件间的通信
父子组件间通信
➢父元素通过 props 向子组件传递数据
➢子组件通过 事件 给父组件触发更新行为
子组件向父组件发送消息,使用Vue自定义事件机制
- 子组件使用 $emit 向父组件触发事件
- 父组件使用 $on 或 v-on 监听子组件事件
<body>
<div id="app">
<!-- 2 在父组件中监听子组件中事件 -->
<demo-component value="hello world" @child-event='handleEvent'></demo-component>
</div>
<template id="comp">
<div>
this is a component! {{value}}
<button @click="emitEvent">触发事件</button>
</div>
</template>
</body>
<script>
var vm = new Vue({
el: '#app',
components: {
'demo-component': {
template: '#comp',
props: ['value'],
methods: {
emitEvent: function(){
// 1 触发事件
this.$emit('child-event')
}
},
}
},
methods:{
handleEvent(){
alert('接收到子组件触发的事件!')
}
}
})
注意:自定义事件名要用中划线命名
流程:
- 子线程触发一个自定义事件
- 在父元素中使用v-on指令监听事件,交给父元素的函数处理
触发事件时携带附加数据
子组件向父组件发送消息,使用Vue自定义事件机制:
➢子组件向父组件发送事件消息时,可以一并传递其它附加数据
➢父组件直接在事件监听函数中通过形参获取这些附加数据参数(也可以 使用 arguments [ ] 数组接收)
<body>
<div id="app">
<!-- 2 在父组件中监听子组件中事件 -->
<demo-component value="hello world" @child-event='handleEvent'></demo-component>
</div>
<template id="comp">
<div>
this is a component! {{value}}
<button @click="emitEvent">触发事件</button>
</div>
</template>
</body>
<script>
var vm = new Vue({
el: '#app',
components: {
'demo-component': {
template: '#comp',
props: ['value'],
methods: {
emitEvent: function(){
// 1 触发事件
// 参数1 事件名
// 参数2 传递给父元素的数据
this.$emit('child-event',"child component data")
}
},
}
},
methods:{
handleEvent(params){
alert('接收到子组件触发的事件!' + params)
// 或者也可以使用arguments对象接收参数值
alert('接收到子组件触发的事件!' + arguments[0])
}
}
})
</script>
也可以使用arguments对象访问传过来的参数:
<body>
<div id="app">
<!-- 2 在父组件中监听子组件中事件 -->
<demo-component value="hello world" @child-event='handleEvent'></demo-component>
</div>
<template id="comp">
<div>
this is a component! {{value}}
<button @click="emitEvent">触发事件</button>
</div>
</template>
</body>
<script>
var vm = new Vue({
el: '#app',
data: {
msg: 'default value'
},
components: {
'demo-component': {
template: '#comp',
props: ['value'],
methods: {
emitEvent: function(){
// 1 触发事件
// 参数1 事件名
// 参数2 传递给父元素的数据
this.$emit('child-event',"child component data")
}
},
}
},
methods:{
handleEvent(params){
// 接收子元素触事件携带的数据
//this.msg = params
this.msg = arguments[0]
}
}
})
</script>
3.2 父子组件之间的访问
3.2.1 在子组件中访问父组件
在子组件中,可以通过 $parent 或 $root 访问当前组件的父 元素或根元素实例对象(Vue对象)
➢this.$parent 表示当前组件的父元素组件
➢this.$root 表示当前组件所在的根元素组件
示例:子组件修改父组件的data数据
<body>
<div id="app">
<demo-component1 :msg="msg"></demo-component1>
<demo-component2></demo-component2>
</div>
<template id="comp1">
<div>
this is a component1! {{msg}}
</div>
</template>
<template id="comp2">
<div>
this is a component2!
<button @click="updateParentComponent">更新组件1的数据</button>
</div>
</template>
</body>
<script>
const DemoComponent1 = {
template: '#comp1',
props: ['msg'],
methods: {
updateParentComponent(){
this.$root.msg = 'msg value from child1'
}
},
}
const DemoComponent2 = {
template: '#comp2',
methods: {
updateParentComponent(){
// 子组件中修改父组件的数据
//this.$parent.msg = 'msg value from child2'
this.$root.msg = 'msg value from child2'
}
},
}
var vm = new Vue({
el: '#app',
data: {
msg: 'default value'
},
components: {
DemoComponent1,DemoComponent2
},
})
</script>
3.2.2 父组件中访问子组件
父元素使用子组件时,可以添加 ref属性,这样在父元素内部即可通过 this.$refs 访问相关子元素组件
<body>
<div id="app">
<demo-component1 ref="c1"></demo-component1>
</div>
<template id="comp1">
<div>
this is a component1! {{name}}
</div>
</template>
</body>
<script>
const DemoComponent1 = {
template: '#comp1',
data(){
return {name: 'admin'}
}
}
var vm = new Vue({
el: '#app',
data: {
msg: 'default value'
},
components: {
DemoComponent1
},
mounted() {
// 在父元素中获取子组件c1
console.dir(this.$refs.c1)
},
})
</script>
<body>
<div id="app">
<demo-component1 :msg="msg" ref="c1" ></demo-component1>
<demo-component2></demo-component2>
</div>
<template id="comp1">
<div>
this is a component1! {{name}}
<br/>
{{msg}}
</div>
</template>
<template id="comp2">
<div>
this is a component2!
<button @click="updateParentComponent">更新组件1的数据</button>
</div>
</template>
</body>
<script>
const DemoComponent1 = {
template: '#comp1',
props: ['msg'],
data(){
return {
name: ' name default value in component1 '
}
}
}
const DemoComponent2 = {
template: '#comp2',
methods: {
updateParentComponent(){
// 先访问父组件,再由父组件访问子组件
this.$parent.$refs.c1.name = 'this value from comp2'
}
},
}
var vm = new Vue({
el: '#app',
data: {
msg: 'default value'
},
components: {
DemoComponent1,DemoComponent2
},
})
</script>
应用场景:
➢一般不建议使用此方式直接修改父元素或子元素的内容
➢在非父子组件数据通信时,可以使用此方式进行模拟
组件A 和 组件B 之间进行数据通信(在组件A中,要动态修改组件B显示的内容)
• 可以在组件A中,动态修改父元素中的值
• 组件B待修改的值,即为父元素中的指定值,从而达到组件A修改组件B数据的目的
3.3 非父子组件通信
非父子组件通信有两种方式:
- 数据共享(vux状态管理)
- 事件总线
3.3. 1 事件总线
使用一个空的 Vue对象 在不同组件之间触发和监听自定义事件
- 创建一个空的Vue对象
- 在组件A中使用 $emit 触发事件
- 在组件B中使用 $on 监听接收事件
注意:
✓监听过程应该写在 created() 回调函数中
✓在监听函数内部,this表示事件总线对象
<body>
<div id="app">
<child-component1></child-component1>
<child-component2></child-component2>
</div>
<template id="comp1">
<div>
<button @click="updateChild2">更新组件2的内容</button>
</div>
</template>
<template id="comp2">
<div>
{{name}}
</div>
</template>
</body>
<script>
// 1 创建事件总线对象(vue对象)
var bus = new Vue()
const childComponent1 = {
template: '#comp1',
methods: {
updateChild2: function(){
// 2 使用事件总线触发自定义事件
bus.$emit('comp1-event', 'update name from comp1 by bus!')
}
},
}
const childComponent2 = {
template: '#comp2',
data(){
return {name: 'child2'}
},
created: function(){
// 3 监听事件总线的事件
_this = this
bus.$on('comp1-event', function(parms){
console.log('处理comp1-event事件')
console.log(parms)
_this.name = parms
})
}
}
var vm = new Vue({
el: '#app',
data: {
isColor: true
},
components: {childComponent1, childComponent2}
})
</script>
注意:监听函数中this表示的是bus对象
4 组件插槽(slot)
4.1 插槽的基本使用
在定义组件时,可以在组件内部使用 <slot> 标签定义插槽 ➢父元素中可以通过插槽向子组件传递HTML内容
<body>
<div id="app">
<demo-component> <!-- 2 使用组件时向solt传递内容 -->
<h3>this is parent content by slot</h3>
</demo-component>
<demo-component> <!-- 2 使用组件时向solt传递内容 -->
<p>this is parent content by slot</p>
</demo-component>
</div>
<template id="comp">
<div>
<p>this is a component!</p>
<slot></slot> <!-- 1 在组件中定义solt -->
<p>other content!</p>
</div>
</template>
</body>
<script>
var vm = new Vue({
el: '#app',
components: {
'demo-component': {
template: '#comp'
}
}
})
</script>
4.1.1 为solt提供默认值
在定义组件时,可以在组件内部使用 <slot> 标签定义插槽, 子组件通过插槽对外提供默认值
<body>
<div id="app">
<demo-component></demo-component>
</div>
<template id="comp">
<div>
<p>this is a component!</p>
<slot>defult content of solt!</slot> <!-- 定义solt时提供默认内容 -->
<p>other content!</p>
</div>
</template>
</body>
<script>
var vm = new Vue({
el: '#app',
components: {
'demo-component': {
template: '#comp'
}
}
})
</script>
4.1.2 定义带有名字的solt
在定义组件时,可以在组件内部使用 <slot> 标签定义插槽
➢定义插槽的时候,可以为插槽指定 name 属性,称之为命名插槽
➢父元素通过 slot="" 属性,指明要使用的是哪一个插槽
<body>
<div id="app">
<demo-component>
<div slot="header">为名为header的插槽提供内容</div>
<div>
为没有名字的插槽提供内容
</div>
</demo-component>
</div>
<template id="comp">
<div>
<slot name="header">defult header content of solt!</slot>
<p> content!</p>
<slot name="footer">defult footer content of solt!</slot>
<slot>默认solt</slot>
</div>
</template>
</body>
<script>
var vm = new Vue({
el: '#app',
components: {
'demo-component': {
template: '#comp'
}
}
})
</script>
4.1.3在slot中可以使用当前组件的props和data
在定义组件时,可以在组件内部使用 <slot> 标签定义插槽
组件插槽内部可以使用当前组件的props和data数据,不能使用父元素数据
<body>
<div id="app">
<demo-component>
<div></div>
</demo-component>
</div>
<template id="comp">
<div>
<slot>默认solt, 在slot中访问当前组件的data数据 {{name}}</slot>
</div>
</template>
</body>
<script>
var vm = new Vue({
el: '#app',
components: {
'demo-component': {
template: '#comp',
data(){
return {name: 'admin'}
}
}
}
})
</script>
父元素中可以使用父元素的data数据,但是不能使用组件的数据
<body>
<div id="app">
<demo-component></demo-component>
</div>
<template id="comp">
<div>
<slot>默认solt, 在slot中访问data数据 {{name}}</slot>
</div>
</template>
</body>
<script>
var vm = new Vue({
el: '#app',
components: {
'demo-component': {
template: '#comp',
data(){
return {name: 'admin'}
}
}
}
})
</script>
4.2 作用域插槽
4.2.1 作用域插槽的用途
<body>
<div id="app">
<demo-component1 :books="books"></demo-component1>
</div>
<template id="comp1">
<ul>
<li v-for="(item, index) in books">
{{item.name}}
</li>
</ul>
</template>
</body>
<script>
const DemoComponent1 = {
template: '#comp1',
props: ['books']
}
var vm = new Vue({
el: '#app',
data: {
books:[
{name: 'Java讲义', status: '0'},
{name: 'Oracle实战经典', status: '1'},
{name: 'Java实战经典', status: '1'},
{name: 'Java编程思想', status: '0'}
]
},
components: {
DemoComponent1
},
})
</script>
这时要求在前面要加一个对号:怎么办?
作用域插槽:
子组件向父元素通过 props 传递数据
➢使用场景:父元素要自定义渲染效果,需要使用到子组件中的数据
➢使用流程:
✓在子组件内,<slot>元素之上通过 v-bind: 创建作用域插槽
✓父元素中,通过 slot-scope 属性,引用子组件传递的prop数据
• 注意:使用 es2015提供的 解构语法
<body>
<div id="app">
<demo-component1 :books="books">
<!-- 接收组件传递过来的作用域插槽 -->
<template slot-scope="{todo}">
<span v-if="todo.status == 1 " >*</span>
{{todo.name}}
</template>
</demo-component1>
<br/>
<demo-component1 :books="books">
<!-- 2 接收组件传递过来的作用域插槽 -->
<template slot-scope="{todo}">
</template>
</demo-component1>
</div>
<template id="comp1">
<ul>
<li v-for="(item, index) in books">
<!-- 1 传递给父元素的props是todo -->
<slot v-bind:todo="item">{{item.name}}</slot>
</li>
</ul>
</template>
</body>
<script>
const DemoComponent1 = {
template: '#comp1',
props: ['books']
}
var vm = new Vue({
el: '#app',
data: {
books:[
{name: 'Java讲义', status: '0'},
{name: 'Oracle实战经典', status: '1'},
{name: 'Java实战经典', status: '1'},
{name: 'Java编程思想', status: '0'}
]
},
components: {
DemoComponent1
},
})
</script>
4.3 访问slot
在组件中可以使用$slots来访问插槽
5. 组件中的v-model(双向绑定)
5.1 v-model基础回顾
Vue框架中表单控件可以使用 v-model 实现数据的双向绑定
➢当表单数据发生改变时,绑定的数据变量也一并发生改变
➢当数据变量发生改变时,表单数据也一并发生改变
v-model工作原理:
<body>
<div id="app">
<div>
<h3>v-model工作原理</h3>
<input type="text" v-bind:value="name" v-on:input="name = $event.target.value">
<div>{{name}}</div>
</div>
</div>
</body>
<script>
const DemoComponent1 = {
template: '#comp1',
props: ['books']
}
var vm = new Vue({
el: '#app',
data: {
name: 'admin'
}
})
</script>
5.2 在组件中使用v-model
组件中定义v-model:使用 v-bind:value 和 v-on:input 事件 进行处理
➢组件要接收 名为 value 的 prop数据,并使用 v-bind:value 绑定到表 单元素上
➢自定义组件,要手动 emit 父元素的 input事件 ,并传递新的数据值
使用组件时,直接使用 v-model 绑定数据即可
<body>
<div id="app">
{{name}}<br/>
<!-- 在父组件中使用组件的v-model -->
<demo-component1 v-model="name"></demo-component1>
</div>
<template id="comp1">
<div>
组件中的输入框:
<input type="text" v-bind:value="value" v-on:input="handleInput($event.target.value)"/>
</div>
</template>
</body>
<script>
const DemoComponent1 = {
template: '#comp1',
props: ['value'],
methods: {
handleInput: function(val){
// 触发input事件
this.$emit('input', val)
}
},
}
var vm = new Vue({
el: '#app',
data: {
name: 'admin'
},
components:{DemoComponent1}
})
</script>
组件中定义v-model:使用 v-bind:value 和 v-on:input 事件 进行处理
可以在组件内使用 model 属性 指定其它的 prop 和 事件
注意:某些表单元素中,value属性和input事件可能有其他作用(如单选/复选框)
6 组件的其它高级用法
6.1 动态组件
可以使用 <component> 标签添加动态组件
➢使用场景:程序中动态切换组件(选项卡、路由)
➢<component>标签可以渲染其它任意组件
➢通过 is="" 属性渲染其它组件
✓一般通过 v-bind:is 绑定,在程序中切换动态组件
✓当 is的属性值数据发生改变时,会自动渲染为其它组件
<body>
<div id="app">
<div>
{{currentComponent}}
<button @click="currentComponent = 'DemoComponent1'">组件1</button>
<button @click="currentComponent = 'DemoComponent2'">组件2</button>
</div>
<div>
<component :is="currentComponent"></component>
</div>
</div>
<template id="comp1">
<div>
组件1内容
</div>
</template>
<template id="comp2">
<div>
组件2内容
</div>
</template>
</body>
<script>
const DemoComponent1 = {
template: '#comp1',
}
const DemoComponent2 = {
template: '#comp2',
}
var vm = new Vue({
el: '#app',
data: {
// 当前组件
currentComponent: 'DemoComponent1'
},
components:{DemoComponent1, DemoComponent2}
})
</script>
➢默认情况下,动态组件会重新渲染组件(重新生成组件对象),从而导致程序性能下降,导致原始组件的状态数据丢失
➢可以使用 <keep-alive> 标签包围 <component> 标签,从而避免 反复重复渲染问题,同时会保持原组件状态数据
<body>
<div id="app">
<div>
{{currentComponent}}
<button @click="currentComponent = 'DemoComponent1'">组件1</button>
<button @click="currentComponent = 'DemoComponent2'">组件2</button>
</div>
<div>
<keep-alive>
<component :is="currentComponent"></component>
</keep-alive>
</div>
</div>
<template id="comp1">
<div>
<label>用户名:</label><input type="text" name="username"/>
</div>
</template>
<template id="comp2">
<div>
组件2内容
</div>
</template>
</body>
<script>
const DemoComponent1 = {
template: '#comp1',
}
const DemoComponent2 = {
template: '#comp2',
}
var vm = new Vue({
el: '#app',
data: {
// 当前组件
currentComponent: 'DemoComponent1'
},
components:{DemoComponent1, DemoComponent2}
})
</script>
6.2 递归组件
在组件内部可以嵌套调用其他组件,甚至可以递归地调用当前自 身组件
➢递归组件,需要有递归的结束条件,否则会产生无穷递归
➢递归组件,需要在组件定义中,给出 name属性,表示组件的名称(标签名称)
<body>
<div id="app">
<menu-component :menus="menusData"></menu-component>
</div>
<template id="menu-comp">
<ul>
<li v-for="menu in menus">
{{menu.name}}
<menu-component v-if="menu.children" :menus="menu.children"></menu-component>
</li>
</ul>
</template>
</body>
<script>
const MenuComponent = {
name: 'MenuComponent', // 递归组件必须提供name属性
template: '#menu-comp',
props: ['menus']
}
var vm = new Vue({
el: '#app',
data: {
menusData: [
{id: '10001', name: "自然人信息列表"},
{id: '10002', name: "企业信息列表", children: [
{id: '1000210001', name: "基本信息", children: null},
{id: '1000210002', name: "资质信息", children: null},
{id: '1000210003', name: "业绩信息", children: null},
{id: '1000210004', name: "执业人员", children: [
{id: '100021000410001', name: "基本信息", children: null},
{id: '100021000410001', name: "资格信息", children: null},
{id: '100021000410001', name: "业绩信息", children: null}
]},
]},
{id: '10001', name: "其它", children: []},
]
},
components:{MenuComponent}
})
</script>
注意:递归组件必须提供name属性
6.3 内联模板
在使用组件时,在组件标签内使用inline-template属性,组件就会把它的内容当作模板,而不是把它当用内容分发
<body>
<div id="app">
<child-component :message="message" inline-template>
<div>
<p>{{msg}}</p>
<p>{{message}}</p>
</div>
</child-component>
</div>
</body>
<script>
Vue.component('child-component', {
data: function(){
return {
msg: '在子组件声明的数据'
}
},
props:['message']
})
new Vue({
el: '#app',
data: {
message: '在父组件中声明的数据'
},
})
</script>
6.4 异步组件
当项目很大,组件非常多时,一开始把所有组件加载是没必要的,这时Vue允许将组件定义为一个工厂函数,动态解析组件
<body>
<div id="app">
<child-component :message="message"></child-component>
</div>
</body>
<script>
// 1. 使用Vue.extend()方法定义一个组件类
let DemoComponent = Vue.extend({
template: '<div>hello {{name}}</div>',
data(){
return {
name: 'world'
}
}
})
// 2. 创建组件对象,手动挂载到dom对象上
// 方式一:
//new DemoComponent().$mount('#app'), 或
// 方式二:
/*
new DemoComponent({
el:'#app'
})
*/
// 方式三:
let demoComponent = new DemoComponent().$mount()
document.getElementById('app').appendChild(demoComponent.$el)
</script>
6.5 $nextTick
<body>
<div id="app">
<div id="title" v-if="isShow">这里是标题</div>
<button @click="getDom">获取dom对象</button>
</div>
</body>
<script>
new Vue({
el: '#app',
data:{
isShow: false
},
methods: {
getDom(){
alert(1)
this.isShow = true
// 获取dom对象
let titleHtml = document.getElementById('title').innerHTML
console.log(titleHtml)
}
},
})
</script>
这时,在获取dom时报错,原因:
Vue在观察到data变化时并不是直接更新DOM,而是开启一个队列,缓冲同一事件循环中的所有数据改变,在缓冲时会去除重复数据, 避免不必要的计算和DOM操作,然后在$nextTick中Vue刷新队列并执行实际工作
<body>
<div id="app">
<div id="title" v-if="isShow">这里是标题</div>
<button @click="getDom">获取dom对象</button>
</div>
</body>
<script>
new Vue({
el: '#app',
data:{
isShow: false
},
methods: {
getDom(){
alert(1)
this.isShow = true
this.$nextTick(function(){
// 获取dom对象
let titleHtml = document.getElementById('title').innerHTML
console.log(titleHtml)
})
}
},
})
</script>