之前创建的组件是比较简单,没有管理或者监听任何传递给他的状态,也没有生命周期方法。它只是一个接收参数的函数。
在下面这个例子中,我们标记组件为 functional,这意味它是无状态 (没有响应式数据),无实例 (没有 this 上下文)。
一个 函数式组件 就像这样:
Vue.component('my-component', {
functional: true,
// Props 可选
props: {
// ...
},
// 为了弥补缺少的实例
// 提供第二个参数作为上下文
render: function (createElement, context) {
// ...
}
})
注意:在 2.3.0 之前的版本中,如果一个函数式组件想要接受 props,则 props
选项是必须的。在 2.3.0 或以上的版本中,你可以省略 props
选项,所有组件上的属性都会被自动解析为 props。
在 2.5.0 及以上版本中,如果你使用了单文件组件,那么基于模板的函数式组件可以这样声明:
<template functional>
</template>
组件需要的一切都是通过上下文传递,包括:
props
:提供所有 prop 的对象children
: VNode 子节点的数组slots
: 返回所有插槽的对象的函数data
:传递给组件的数据对象,作为createElement
的第二个参数传入组件parent
:对父组件的引用listeners
: (2.3.0+) 一个包含了所有在父组件上注册的事件侦听器的对象。这只是一个指向data.on
的别名。injections
: (2.3.0+) 如果使用了inject
选项,则该对象包含了应当被注入的属性。
在添加 functional: true
之后,锚点标题组件的 render 函数之间简单更新增加 context
参数,this.$slots.default
更新为 context.children
,之后this.level
更新为 context.props.level
。
因为函数式组件只是一个函数,所以渲染开销也低很多。然而,对持久化实例的缺乏也意味着函数式组件不会出现在 Vue devtools 的组件树里。
在上面多个context的属性中,其中context.props、context.data、context.children这3个用的比较多。 context.props用来传递数据,context.data传递属性如class、id等,context.children指插槽的默认值this.$slots.default。
例一
<body>
<div id="app">
<my-function-button v-bind:class="className" id="btn1">button</my-function-button>
</div>
<script src="js/vue.js"></script>
<script>
Vue.component("my-function-button",{
functional:true,
render:function(createElement,context){
return createElement("button",context.data,context.children)
}
})
new Vue({
el: '#app',
data: {
className:"btnname"
},
});
//context.data 是用来传递属性的,传递数据是context.props
</script>
</body>
渲染成 :<button id="btn1" class="btnname">button</button>
例二
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>AdminLTE 2 | Morris.js Charts</title>
<!-- Tell the browser to be responsive to screen width -->
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<!-- Morris charts -->
</head>
<body>
<div id="app">
<smart-list :items="items" v-bind:number="1" class="specialColor">hi</smart-list>
</div>
<script src="js/vue.js"></script>
<script>
// 组件1
var EmptyList = {
template: "<p>Empty list</p>"
}
// 组件2
var Tablist ={
props:["chuandiData"],
template:`
<ul>
<li v-for="item in chuandiData">{{item.name}}</li>
</ul>
`
}
// 组件3
var component3 = {
template:`
<div> this is component3 </div>
`
}
Vue.component("smart-list", {
functional: true,
props: {
items: {
type: Array,
required: true
}
},
render: function(createElement, context) {
console.log(context)
function appropriateListComponent() {
var items = context.props.items
if (items.length === 0) {
return EmptyList
}
if (items.length == 3) return Tablist
}
return createElement(
appropriateListComponent(),
{
props:{
chuandiData:context.props.items
}
}
)
}
})
new Vue({
el: '#app',
data: {
items: [{
name: 'a',
id: 0
},
{
name: 'b',
id: 1
},
{
name: 'c',
id: 2
}
]
},
});
//context.data 是用来传递属性的,传递数据是context.props
</script>
</body>
</html>
渲染成:
<ul>
<li>a</li>
<li>b</li>
<li>c</li>
</ul>
例三
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>AdminLTE 2 | Morris.js Charts</title>
<!-- Tell the browser to be responsive to screen width -->
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<!-- Morris charts -->
</head>
<body>
<div id="app">
<smart-list :items="items" v-bind:number="1" class="specialColor">hi</smart-list>
</div>
<script src="js/vue.js"></script>
<script>
// 组件1
var EmptyList = {
template: "<p>Empty list</p>"
}
// 组件2
var Tablist = {
template: `
<ul>
<li><slot></slot></li>
</ul>
`
}
Vue.component("smart-list", {
functional: true,
props: {
items: {
type: Array,
required: true
}
},
render: function(createElement, context) {
console.log(context)
function appropriateListComponent() {
var items = context.props.items
if (items.length === 0) {
return EmptyList
}
if (items.length == 3) return Tablist
}
return createElement(
appropriateListComponent(),
context.children
)
}
})
new Vue({
el: '#app',
data: {
items: [{
name: 'a',
id: 0
},
{
name: 'b',
id: 1
},
{
name: 'c',
id: 2
}
]
},
});
//context.data 是用来传递属性的,传递数据是context.props
</script>
</body>
</html>
渲染成:<ul><li>hi</li></ul>
例四
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>AdminLTE 2 | Morris.js Charts</title>
<!-- Tell the browser to be responsive to screen width -->
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<!-- Morris charts -->
<link rel="stylesheet" href="../../dist/css/Basic.css">
<link rel="stylesheet" href="../../dist/css/lanrenzhijia.css">
</head>
<body>
<div id="app">
<smart-item :data1="data">hi</smart-item>
<button @click="change('img')">切换为图片为组件</button>
<button @click="change('video')">切换为视频为组件</button>
<button @click="change('text')">切换为文本组件</button>
</div>
<script src="js/vue.js"></script>
<script>
// 图片组件选项
var ImgItem = {
props: ['data2'],
render: function(createElement) {
return createElement('div', [
createElement('p', '图片组件'),
createElement('img', {
attrs: {
src: this.data2.url
}
})
]);
}
}
// 视频组件
var VideoItem = {
props: ['data2'],
render: function(createElement) {
return createElement('div', [
createElement('p', '视频组件'),
createElement('video', {
attrs: {
src: this.data2.url,
controls: 'controls',
autoplay: 'autoplay'
}
})
]);
}
};
/*纯文本组件*/
var TextItem = {
props: ['data2'],//2、接收1传过来的props对象
render: function(createElement) {
return createElement('div', [
createElement('p', '纯文本组件'),
createElement('p', this.data2.content)
]);
}
};
Vue.component('smart-item', {
functional: true,
render: function(createElement, context) {
function getComponent() {
var data = context.props.data1;
if (data.type === 'img') return ImgItem;
if (data.type === 'video') return VideoItem;
return TextItem;
}
return createElement(
getComponent(), {
props: {
data2: context.props.data1 //getComponent()返回的是组件,props是用来传递数据给返回的组件,如:TextItem组件
}
}
,context.children //可以不需要
)
},
props: {
data1: {
type: Object,
required: true
}
}
});
new Vue({
el: '#app',
data: {
data: {}
},
methods: {
change: function(type) {
if (type === 'img') {
this.data = {
type: 'img',
url: 'https://raw.githubusercontent.com/iview/iview/master/assets/logo.png'
}
} else if (type === 'video') {
this.data = {
type: 'video',
url: 'http://vjs.zencdn.net/v/oceans.mp4'
}
} else if (type === 'text') {
this.data = {
type: 'text',
content: '这是一段纯文本'
}
}
}
},
created: function() {
this.change('text');
}
});
//
</script>
</body>
</html>