virtual Dom
是一个轻量级的Javascript对象,在状态发生变化时,virtual Dom会进行Diff运算,来更新只需要被替换的DOM,而不是全部重绘
Render函数
render函数通过createElement参数来创建Virtual Dom,
createElement
createElement( { String | Object | Function })
import Vue from "vue/dist/vue.esm";
Vue.component("ele",{
render(createElement){
let _this = this;
let listNode;
if(this.list.length){
listNode = createElement("ul",this.list.map((item)=>{
return createElement("li",item)
}))
}else{
listNode = createElement("p","暂无聊天内容")
}
return createElement("div",[
listNode,
createElement('input',{
attrs:{
placeholder:'输入内容,按回车键发送'
},
style:{
width:'200px'
},
on:{
keyup(event){
if(event.keyCode !== 13) return;
_this.list.push(event.target.value);
event.target.value = ''
}
}
})
])
},
data(){
return {
value:""
,list:[]
}
}
})
事件修饰符和按键修饰符对应的句柄
.capture .once 事件修饰符的前缀
函数化组件
vue中的functional的布尔值,设置为true,可以使组件无状态和无实例,也就是没有data和this上下文。这样render函数返回虚拟节点可以更容易渲染,函数化组件只是一个函数,渲染开销要小的多。
在使用函数化组件时,render函数提供了第二个参数context来提供上下文。组件需要的data、props、slots、children、parent都是通过这个上下文来传递的,
<template>
<div>
<ele></ele>
<smart-item :data="data"></smart-item>
<button @click="change('img')">切换为图片组件</button>
<button @click="change('video')">切换为视频组件</button>
<button @click="change('text')">切换为纯文本组件</button>
</div>
</template>
<script>
import "../components/chat";
import Vue from "vue/dist/vue.esm";
//图片组件
var ImgItem = {
props: ["data"],
render(createElement) {
return createElement("div", [
createElement("p", "图片组件"),
createElement("img", {
attrs: {
src: this.data.url,
},
}),
]);
},
};
// 视频组件
var VideoItem = {
props: ["data"],
render(createElement) {
return createElement("div", [
createElement("p", "视频组件"),
createElement("video", {
attrs: {
src: this.data.url,
controls: "controls",
autoplay: "autoplay",
},
}),
]);
},
};
// 纯文本组件选项
var TextItem = {
props: ["data"],
render(createElement) {
return createElement("div", [
createElement("p", "纯文本组件"),
createElement("p", this.data.text),
]);
},
};
// 函数组件化
Vue.component("smart-item", {
functional: true,
render(createElement, context) {
let data
function getComponent() {
data = context.props.data;
if (data.type === "img") return ImgItem;
if (data.type === "video") return VideoItem;
return TextItem;
}
return createElement(
getComponent(),
{
props: {
data: context.props.data,
},
},
context.children
);
},
props: {
data: {
type: Object,
required: true,
},
},
});
export default {
name: "Chat",
data() {
return {
data: {},
};
},
methods: {
change(type) {
if (type === "img") {
this.data = {
type: "img",
url: "http://img04.sogoucdn.com/app/a/100520021/5dc8a510c731800ec96143b190645b6e",
};
}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(){
this.change('img')
}
};
</script>
<style lang="scss" scoped></style>
留言板
import Vue from "vue/dist/vue.esm";
Vue.component("list", {
props: {
list: {
type: Array,
default: () => {},
},
},
render(h) {
let _this = this;
let list = [];
this.list.forEach((msg, index) => {
const node = h(
"div",
{
attrs: {
class: "list-item",
},
},
[
h("span", msg.name + ":"),
h(
"div",
{
attrs: {
class: "list-msg",
},
},
[
h("p", msg.message),
h(
"a",
{
attrs: {
class: "list-reply",
},
on: {
click: () => {
_this.handleReply(index);
},
},
},
"回复"
),
]
),
]
);
list.push(node);
});
if (this.list.length) {
return h(
"div",
{
attrs: {
class: "list",
},
},
list
);
} else {
return h(
"div",
{
attrs: {
class: "list-nothing",
},
},
"留言列表为空"
);
}
},
methods: {
handleReply(index) {
console.log(index);
this.$emit("reply", index);
},
},
});
import Vue from "vue/dist/vue.esm";
Vue.component("vInput",{
props:{
value:{
type:[String,Number],
default:''
}
},
render(h){
let _this = this;
return h('div',[
h('span','昵称:'),
h('input',{
attrs:{
type:'text'
},
domProps:{
value:this.value
},
on:{
input:(event)=>{
// _this.value = event.target.value;
_this.$emit('input',event.target.value)
}
}
})
])
}
})
Vue.component("vTextarea",{
props:{
value:{
type:String,
default:''
}
},
render(h){
let _this = this;
return h("div",[
h('span','留言内容:'),
h('textarea',{
attrs:{
placeholder:'请输入留言内容'
},
domProps:{
value:this.value
},
ref:"message",
on:{
input(event){
// _this.value = event.target.value;
_this.$emit('input',event.target.value)
}
}
})
])
},
methods:{
focus(){
this.$refs.message.focus()
}
}
})
<template>
<div>
<div style="width: 500px; margin: 0 auto">
<div class="message">
<v-input v-model="username"></v-input>
<v-textarea v-model="message" ref="message"></v-textarea>
<button @click="handleSend">发布</button>
</div>
<list :list="list" @reply="handleReply"></list>
</div>
</div>
</template>
<script>
import "../components/input"
import "../components/list"
export default {
name: "Bbs",
data(){
return {
username:'',
message:'',
list:[]
}
},
methods:{
handleSend(){
if(this.username===''){
window.alert('请输入昵称');
return
}
if(this.message===''){
window.alert('请输入留言内容')
return
}
this.list.push({
name:this.username,
message:this.message
});
this.message = ''
},
handleReply(index){
console.log(1231)
let name = this.list[index].name;
this.message = '回复@'+name+':';
this.$refs.message.focus()
}
}
};
</script>
<style lang="less" scoped>
* {
padding: 0;
margin: 0;
}
.message {
width: 450px;
text-align: right;
}
.message div {
margin-bottom: 12px;
}
.message span {
display: inline-block;
width: lOOpx;
vertical-align: top;
}
.message input .message textarea {
width: 300px;
height: 32px;
padding: 0 6px;
color: #657180;
border: lpx solid #d7dde4;
border-radius: 4px;
cursor: text;
outline: none;
}
.message input:focus,
.message textarea:focus {
border: lpx solid #3399ff;
}
.message textarea {
height: 60px;
padding: 4px 6px;
}
.message button {
display: inline-block;
padding: 6px 15px;
border: lpx solid #39f;
border-radius: 4px;
color: #fff;
background-color: #39f;
cursor: pointer;
outline: none;
}
.list {
margin-top: 50px;
}
.list-item {
padding: lOpx;
border-bottom: lpx solid #e3e8ee;
overflow: hidden;
}
.list-item span {
display: block;
width: 60px;
float: left;
color: #39f;
}
.list- msg {
display: block;
margin-left: 60px;
text-align: justify;
}
.list-msg a {
color: #9ea7b4;
cursor: pointer;
float: right;
}
.list-msg a:hover {
color: #39f;
}
.list-nothing {
text-align: center;
color: #9ea7b4;
padding: 20px;
}
</style>