动态组件
有的时候,在不同组件之间进行动态切换是非常有用的,比如在一个新闻网站实现多新闻内容选项卡,可以通过 Vue 的 component元素加一个特殊的is 特性来实现:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id='app'>
<button v-for='tab in tabs' :key='tab' @click='current_tab=tab'>{{tab}}</button>
<!-- <component is='tab-Home'></component> -->
<component :is='new_tap'></component>
</div>
<script src='./js/vue.js'></script>
<script>
Vue.component('tab-home', {
template: '<div>家的组件</div>'
})
Vue.component('tab-canteen', {
template: '<div>食堂的组件</div>'
})
Vue.component('tab-company', {
template: '<div>公司的组件</div>'
})
const app = new Vue({
el: '#app',
data: {
current_tab: 'home',
tabs: ['home', 'canteen', 'company']
},
computed: {
new_tap() {
return 'tab-' + this.current_tab
}
}
})
</script>
</body>
在上述示例中,component相当于一个占位符,帮我们把位置占着,我们输入什么组件就显示什么组件
is后面的内容可以包括1.一个组件的名字 2.一个组件的选项对象
组件名注册名称要求
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id='app'>
<!-- <myDemo></myDemo> -->
<my-demo></my-demo>
</div>
<script src='./js/vue.js'></script>
<script>
Vue.component('myDemo', {
template: '<div>mydemo</div>'
})
// Vue.component('my-demo', {
// template: '<div>my-demo</div>'
// })
const app = new Vue({
el: '#app',
data: {
}
})
</script>
</body>
</html>
props属性名大小写要求
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id='app'>
{{name}}
<cpn :give-cpn='name' :give_title='title'></cpn>
</div>
<script src='./js/vue.js'></script>
<script>
Vue.component('cpn', { //app的子组件
template: '<div><h1>{{giveCpn}}{{give_title}}</h1>123</div>',
props: ['giveCpn', 'give_title']
// props: {
// give_cpn: String,
// give_hello: {
// type: String,
// default: '我是默认值',
// },
// }
})
const app = new Vue({
el: '#app',
data: {
name: '我是app里面的内容',
title: 'hello'
}
})
</script>
</body>
</html>
组件的属性Prop之单向数据流
所有的 prop 都使得其父子prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。
每次父级组件发生更新时,子组件中所有的prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。
<body>
<div id='app'>
{{title}}
<cpn :give_cpn='title'></cpn>
</div>
<template id="demo">
<div>
<h1>props: {{give_cpn}}</h1>
<input type="text" v-model='give_cpn'>
</div>
</template>
<script src='./js/vue.js'></script>
<script>
const app = new Vue({
el: '#app',
data: {
title: 'hello啊!树哥.'
},
components: {
cpn: {
template: '#demo',
props: {
give_cpn: {
type: String,
default: '我是默认值'
}
},
}
}
})
</script>
</body>
如图所示,控制台向我们发出了警告,因为是警告,并不是报错,所以效果还是有的,从警告信息中我们可以知道,vue不希望我们在子组件中对props进行修改,并且推荐我们使用data或者computed计算属性去解决修改props的业务
一般来说, 会改变Prop的场景有如下两种:
- 这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。在这种情况下,最好定义一个本地的 data 属性并将这个 prop 用作其初始值:
<body>
<div id='app'>
{{title}}
<cpn :give_cpn='title'></cpn>
</div>
<template id="demo">
<div>
<h1>props: {{give_cpn}}</h1>
<input type="text" v-model='give_cpn'>
<h1>data: {{d_give_cpn}}</h1>
<input type="text" v-model='d_give_cpn'>
</div>
</template>
<script src='./js/vue.js'></script>
<script>
const app = new Vue({
el: '#app',
data: {
title: 'hello啊!树哥.'
},
components: {
cpn: {
template: '#demo',
props: {
give_cpn: {
type: String,
default: '我是默认值'
}
},
data() {
return {
d_give_cpn: this.give_cpn
}
}
}
}
})
</script>
</body>
我们还可以尝试一下,修改vue实例里面的数据
<body>
<div id='app'>
{{title}}
<cpn :give_cpn='title' @give_cpn_change='give_cpn_change'></cpn>
</div>
<template id="demo">
<div>
<!-- 绑定v-model,vue警告不要这样做 -->
<h1>props: {{give_cpn}}</h1>
<input type="text" v-model='give_cpn'>
<!-- 通过data修改 -->
<h1>data: {{d_give_cpn}}</h1>
<input type="text" v-model='d_give_cpn'>
<!-- 修改vue实例数据 -->
<hr>
<h1>data: {{d_give_cpn}}</h1>
<input type="text" :value='d_give_cpn' @input='fn_give_cpn'>
</div>
</template>
<script src='./js/vue.js'></script>
<script>
const app = new Vue({
el: '#app',
data: {
title: 'hello啊!树哥.'
},
methods: {
give_cpn_change(value) {
this.title = value
}
},
components: {
cpn: {
template: '#demo',
props: {
give_cpn: {
type: String,
default: '我是默认值'
}
},
data() {
return {
d_give_cpn: this.give_cpn
}
},
methods: {
fn_give_cpn(event) {
this.d_give_cpn = event.target.value
this.$emit('give_cpn_change', this.d_give_cpn)
}
}
}
}
})
</script>
</body>
2.这个 prop 以一种原始的值传入且需要进行转换。在这种情况下,最好使用这个 prop 的值来定义一个计算属性
<body>
<div id='app'>
{{title}}
{{num}}
<cpn :give_cpn='title' :give_num='num'></cpn>
</div>
<template id="demo">
<div>
<h1>props: {{give_cpn}}</h1>
<input type="text" v-model='give_cpn'>
<h1>data: {{d_give_cpn}}</h1>
<input type="text" v-model='d_give_cpn'>
<hr>
<!-- <h1>data: {{give_num}}</h1> -->
<h1>computed: {{give_num_com}}</h1>
</div>
</template>
<script src='./js/vue.js'></script>
<script>
const app = new Vue({
el: '#app',
data: {
title: 'hello啊!树哥.',
num: 123
},
components: {
cpn: {
template: '#demo',
props: {
give_cpn: {
type: String,
default: '我是默认值'
},
give_num: {
type: Number,
default: 400
}
},
data() {
return {
d_give_cpn: this.give_cpn
}
},
computed: {
give_num_com() {
let new_num = this.give_num * 100
return new_num
}
}
}
}
})
</script>
</body>
组件的属性Prop之非 Prop 的特性
一个非 prop 特性是指传向一个组件,但是该组件并没有相应prop 定义的特性。因为显式定义的prop 适用于向一个子组件传入信息,然而组件库的作者并不总能预见组件会被用于怎样的场景。这也是为什么组件可以接受任意的特性,而这些特性会被添加到这个组件的根元素上。
最常见的就是Vue项目和其他的UI框架结合一起用, 比如layui,elementUI啥的
<body>
<div id='app'>
<cpn :class='main' :get_msg='msg'></cpn>
</div>
<template id="demo">
<div type='world'>{{get_msg}}</div>
</template>
<script src='./js/vue.js'></script>
<script>
const app = new Vue({
el: '#app',
data: {
main: 'main',
msg: '我爱你,你爱我,蜜雪冰城甜蜜蜜',
},
components: {
cpn: {
template: '#demo',
props: {
get_msg: {
type: String,
default: '默认值'
},
}
}
}
})
</script>
</body>
组件的属性Prop之替换/合并已有的特性
对于绝大多数特性来说,从外部提供给组件的值会替换掉组件内部设置好的值。所以如果传入type=“hello" 就会替换掉type=“world" 并把它破坏!庆幸的是,class和 style特性会稍微智能一些,即两边的值会被合并起来,从而得到最终的值:
<body>
<div id='app'>
<cpn class="222" type='hello'></cpn>
</div>
<template id="demo">
<div class="111" type='world'>111</div>
</template>
<script src='./js/vue.js'></script>
<script>
const app = new Vue({
el: '#app',
data: {
},
components: {
cpn: {
template: '#demo'
}
}
})
</script>
</body>
组件的属性Prop之禁用特性继承
如果你不希望组件的根元素继承特性,你可以在组件的选项中设置inheritAttrs: false
<body>
<div id='app'>
<cpn :class='main' :get_msg='msg' type='world'></cpn>
</div>
<template id="demo">
<div type='hello'>{{get_msg}}</div>
</template>
<script src='./js/vue.js'></script>
<script>
const app = new Vue({
el: '#app',
data: {
main: 'main',
msg: '我爱你,你爱我,蜜雪冰城甜蜜蜜',
},
components: {
cpn: {
inheritAttrs: false,
template: '#demo',
props: {
get_msg: {
type: String,
default: '默认值'
},
}
}
}
})
</script>
</body>
插槽的基本用法
Vue 实现了一套内容分发的API,这套 API 的设计灵感源自WebComponents 规范草案,将 元素作为承载分发内容的出口。它允许你像这样合成组件:当组件渲染的时候,将会被替换为我们自己再模板标签里面写的字。插槽内可以包含任何模板代码,包括HTML:
<body>
<div id='app'>
<cpn>
<div type='123'>456</div>
</cpn>
</div>
<template id="demo">
<div>
<div>我是cpn</div>
<slot></slot>
</div>
</template>
<script src='./js/vue.js'></script>
<script>
const app = new Vue({
el: '#app',
data: {
},
components: {
cpn: {
template: '#demo'
}
}
})
</script>
</body>
如果 没有包含一个 元素,则该组件起始标签和结束标签之间的任何内容都会被抛弃。
插槽的编译作用域
父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
<body>
<div id='app'>
<cpn>
<div type='1
3'>456</div>
<i>{{msg_obj}}</i>
</cpn>
</div>
<template id="demo">
<div>
<div>我是cpn</div>
<i>{{msg}}</i>
<slot></slot>
</div>
</template>
<script src='./js/vue.js'></script>
<script>
const app = new Vue({
el: '#app',
data: {
msg_obj: '插槽外信息内容'
},
components: {
cpn: {
template: '#demo',
data() {
return {
msg: '插槽内信息内容'
}
}
}
}
})
</script>
</body>
插槽的默认内容
有时为一个插槽设置具体的后备(也就是默认的) 内容是很有用的,它只会在没有提供内容的时候被渲染。
<body>
<div id='app'>
<cpn>
<!-- <div type='123'></div> -->
<!-- <i>{{msg_obj}}</i> -->
</cpn>
</div>
<template id="demo">
<div>
<div>我是cpn</div>
<i>{{msg}}</i>
<slot>我是默认内容</slot>
</div>
</template>
<script src='./js/vue.js'></script>
<script>
const app = new Vue({
el: '#app',
data: {
msg_obj: '插槽外信息内容'
},
components: {
cpn: {
template: '#demo',
data() {
return {
msg: '插槽内信息内容'
}
}
}
}
})
</script>
</body>
具名插槽
有时我们需要多个插槽 元素有一个特殊的特性:name。这个特性可以用来定义额外的插槽:一个不带name 的出口会带有隐含的名字“default”。
比如说我们现在有一个需求是:要把头部区域和底部区域用插槽的形式填入,如果我们直接填入的话:
<body>
<div id='app'>
<cpn>
<template>
头部
</template>
<template>
底部
</template>
</cpn>
</div>
<template id="demo">
<div>
<div>我是cpn</div>
<header>
<slot></slot>
</header>
<footer>
<slot></slot>
</footer>
</div>
</template>
<script src='./js/vue.js'></script>
<script>
const app = new Vue({
el: '#app',
data: {
msg_obj: '插槽外信息内容'
},
components: {
cpn: {
template: '#demo',
data() {
return {
msg: '插槽内信息内容'
}
}
}
}
})
</script>
</body>
结果是:
这显然不是我们想要的结果,我们可以给插槽取一个名字:
<body>
<div id='app'>
<cpn>
<template v-slot:header>
头部
</template>
<template v-slot:footer>
底部
</template>
</cpn>
</div>
<template id="demo">
<div>
<div>我是cpn</div>
<header>
<slot name='header'></slot>
</header>
<footer>
<slot name='footer'></slot>
</footer>
</div>
</template>
<script src='./js/vue.js'></script>
<script>
const app = new Vue({
el: '#app',
data: {
msg_obj: '插槽外信息内容'
},
components: {
cpn: {
template: '#demo',
data() {
return {
msg: '插槽内信息内容'
}
}
}
}
})
</script>
</body>