vue virtual Dom

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>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值