Vue render深入开发讲解


Posted in Javascript onApril 13, 2018

简介

在使用Vue进行开发的时候,大多数情况下都是使用template进行开发,使用template简单、方便、快捷,可是有时候需要特殊的场景使用template就不是很适合。因此为了很好使用render函数,我决定深入窥探一下。各位看官如果觉得下面写的有不正确之处还望看官指出,你们与我的互动就是写作的最大动力。

场景

官网描述的场景当我们开始写一个通过 level prop 动态生成 heading 标签的组件,你可能很快想到这样实现:

<script type="text/x-template" id="anchored-heading-template">
 <h1 v-if="level === 1">
  <slot></slot>
 </h1>
 <h2 v-else-if="level === 2">
  <slot></slot>
 </h2>
 <h3 v-else-if="level === 3">
  <slot></slot>
 </h3>
 <h4 v-else-if="level === 4">
  <slot></slot>
 </h4>
 <h5 v-else-if="level === 5">
  <slot></slot>
 </h5>
 <h6 v-else-if="level === 6">
  <slot></slot>
 </h6>
</script>
Vue.component('anchored-heading', {
 template: '#anchored-heading-template',
 props: {
  level: {
   type: Number,
   required: true
  }
 }
})

在这种场景中使用 template 并不是最好的选择:首先代码冗长,为了在不同级别的标题中插入锚点元素,我们需要重复地使用 <slot></slot>。

虽然模板在大多数组件中都非常好用,但是在这里它就不是很简洁的了。那么,我们来尝试使用 render 函数重写上面的例子:

Vue.component('anchored-heading', {
 render: function (createElement) {
  return createElement(
   'h' + this.level,  // tag name 标签名称
   this.$slots.default // 子组件中的阵列
  )
 },
 props: {
  level: {
   type: Number,
   required: true
  }
 }
})

简单清晰很多!简单来说,这样代码精简很多,但是需要非常熟悉 Vue 的实例属性。在这个例子中,你需要知道当你不使用 slot 属性向组件中传递内容时,比如 anchored-heading 中的 Hello world!,这些子元素被存储在组件实例中的 $slots.default中。

createElement参数介绍

接下来你需要熟悉的是如何在 createElement 函数中生成模板。这里是 createElement 接受的参数:

createElement(
 // {String | Object | Function}
 // 一个 HTML 标签字符串,组件选项对象,或者
 // 解析上述任何一种的一个 async 异步函数,必要参数。
 'div',

 // {Object}
 // 一个包含模板相关属性的数据对象
 // 这样,您可以在 template 中使用这些属性。可选参数。
 {
  // (详情见下一节)
 },

 // {String | Array}
 // 子节点 (VNodes),由 `createElement()` 构建而成,
 // 或使用字符串来生成“文本节点”。可选参数。
 [
  '先写一些文字',
  createElement('h1', '一则头条'),
  createElement(MyComponent, {
   props: {
    someProp: 'foobar'
   }
  })
 ]
)

深入 data 对象

有一件事要注意:正如在模板语法中,v-bind:class 和 v-bind:style ,会被特别对待一样,在 VNode 数据对象中,下列属性名是级别最高的字段。该对象也允许你绑定普通的 HTML 特性,就像 DOM 属性一样,比如 innerHTML (这会取代 v-html 指令)。

{
 // 和`v-bind:class`一样的 API
 'class': {
  foo: true,
  bar: false
 },
 // 和`v-bind:style`一样的 API
 style: {
  color: 'red',
  fontSize: '14px'
 },
 // 正常的 HTML 特性
 attrs: {
  id: 'foo'
 },
 // 组件 props
 props: {
  myProp: 'bar'
 },
 // DOM 属性
 domProps: {
  innerHTML: 'baz'
 },
 // 事件监听器基于 `on`
 // 所以不再支持如 `v-on:keyup.enter` 修饰器
 // 需要手动匹配 keyCode。
 on: {
  click: this.clickHandler
 },
 // 仅对于组件,用于监听原生事件,而不是组件内部使用
 // `vm.$emit` 触发的事件。
 nativeOn: {
  click: this.nativeClickHandler
 },
 // 自定义指令。注意,你无法对 `binding` 中的 `oldValue`
 // 赋值,因为 Vue 已经自动为你进行了同步。
 directives: [
  {
   name: 'my-custom-directive',
   value: '2',
   expression: '1 + 1',
   arg: 'foo',
   modifiers: {
    bar: true
   }
  }
 ],
 // Scoped slots in the form of
 // { name: props => VNode | Array<VNode> }
 scopedSlots: {
  default: props => createElement('span', props.text)
 },
 // 如果组件是其他组件的子组件,需为插槽指定名称
 slot: 'name-of-slot',
 // 其他特殊顶层属性
 key: 'myKey',
 ref: 'myRef'
}

条件渲染

既然熟读以上api接下来咱们就来点实战。

之前这样写

//HTML
<div id="app">
  <div v-if="isShow">我被你发现啦!!!</div>
</div>
<vv-isshow :show="isShow"></vv-isshow>
//js
//组件形式      
Vue.component('vv-isshow', {
  props:['show'],
  template:'<div v-if="show">我被你发现啦2!!!</div>',
});
var vm = new Vue({
  el: "#app",
  data: {
    isShow:true
  }
});

render这样写

//HTML
<div id="app">
  <vv-isshow :show="isShow"><slot>我被你发现啦3!!!</slot></vv-isshow>
</div>
//js
//组件形式      
Vue.component('vv-isshow', {
  props:{
    show:{
      type: Boolean,
      default: true
    }
  },
  render:function(h){  
    if(this.show ) return h('div',this.$slots.default);
  },
});
var vm = new Vue({
  el: "#app",
  data: {
    isShow:true
  }
});

列表渲染

之前是这样写的,而且v-for 时template内必须被一个标签包裹

//HTML
<div id="app">
  <vv-aside v-bind:list="list"></vv-aside>
</div>
//js
//组件形式      
Vue.component('vv-aside', {
  props:['list'],
  methods:{
    handelClick(item){
      console.log(item);
    }
  },
  template:'<div>\
         <div v-for="item in list" @click="handelClick(item)" :class="{odd:item.odd}">{{item.txt}}</div>\
       </div>',
  //template:'<div v-for="item in list" @click="handelClick(item)" :class="{odd:item.odd}">{{item.txt}}</div>',错误     
});
var vm = new Vue({
  el: "#app",
  data: {
    list: [{
      id: 1,
      txt: 'javaScript',
      odd: true
    }, {
      id: 2,
      txt: 'Vue',
      odd: false
    }, {
      id: 3,
      txt: 'React',
      odd: true
    }]
  }
});

render这样写

//HTML
<div id="app">
  <vv-aside v-bind:list="list"></vv-aside>
</div>
//js
//侧边栏
Vue.component('vv-aside', {
  render: function(h) {
    var _this = this,
      ayy = this.list.map((v) => {
        return h('div', {
          'class': {
            odd: v.odd
          },
          attrs: {
            title: v.txt
          },
          on: {
            click: function() {
              return _this.handelClick(v);
            }
          }
        }, v.txt);
      });
    return h('div', ayy);

  },
  props: {
    list: {
      type: Array,
      default: () => {
        return this.list || [];
      }
    }
  },
  methods: {
    handelClick: function(item) {
      console.log(item, "item");
    }
  }
});
var vm = new Vue({
  el: "#app",
  data: {
    list: [{
      id: 1,
      txt: 'javaScript',
      odd: true
    }, {
      id: 2,
      txt: 'Vue',
      odd: false
    }, {
      id: 3,
      txt: 'React',
      odd: true
    }]
  }
});

v-model

之前的写法

//HTML
<div id="app">
  <vv-models v-model="txt" :txt="txt"></vv-models>
</div>
//js
//input
Vue.component('vv-models', {
  props: ['txt'],
  template: '<div>\
         <p>看官你输入的是:{{txtcout}}</p>\
         <input v-model="txtcout" type="text" />\
       </div>',
  computed: {
    txtcout:{
      get(){
        return this.txt;
      },
      set(val){
        this.$emit('input', val);
      }
      
    }
  }
});
var vm = new Vue({
  el: "#app",
  data: {
    txt: '', 
  }
});

render这样写

//HTML
<div id="app">
  <vv-models v-model="txt" :txt="txt"></vv-models>
</div>
//js
//input
Vue.component('vv-models', {
  props: {
    txt: {
      type: String,
      default: ''
    }
  },
  render: function(h) {
    var self=this;
    return h('div',[h('p','你猜我输入的是啥:'+this.txt),h('input',{
      on:{
        input(event){
          self.$emit('input', event.target.value);
        }
      }
    })] );
  },
});
var vm = new Vue({
  el: "#app",
  data: {
    txt: '', 
  }
});

总结

render函数使用的是JavaScript 的完全编程的能力,在性能上是占用绝对的优势,小编只是对它进行剖析。至于实际项目你选择那种方式进行渲染依旧需要根据你的项目以及实际情况而定。

Javascript 相关文章推荐
网页里控制图片大小的相关代码
Jun 13 Javascript
JQery 渐变图片导航效果代码 漂亮
Jan 01 Javascript
setTimeout自动触发一个js的方法
Jan 15 Javascript
javascript实现可拖动变色并关闭层窗口实例
May 15 Javascript
JavaScript的Date()方法使用详解
Jun 09 Javascript
详解js私有作用域中创建特权方法
Jan 25 Javascript
JS实现保留n位小数的四舍五入问题示例
Aug 03 Javascript
webpack配置文件和常用配置项介绍
Apr 28 Javascript
关于Vue源码vm.$watch()内部原理详解
Apr 26 Javascript
vue动态绑定class的几种常用方式小结
May 21 Javascript
基于Web Audio API实现音频可视化效果
Jun 12 Javascript
Vue打包部署到Nginx时,css样式不生效的解决方式
Aug 03 Javascript
用vue2.0实现点击选中active其他选项互斥的效果
Apr 12 #Javascript
vue组件的写法汇总
Apr 12 #Javascript
jQuery实现文件编码成base64并通过AJAX上传的方法
Apr 12 #jQuery
基于vue-simplemde实现图片拖拽、粘贴功能
Apr 12 #Javascript
基于angular6.0实现的一个组件懒加载功能示例
Apr 12 #Javascript
JavaScript实现简单的文本逐字打印效果示例
Apr 12 #Javascript
jQuery实现鼠标点击处心形漂浮的炫酷效果示例
Apr 12 #jQuery
You might like
PHP模板引擎Smarty的缓存使用总结
2014/04/24 PHP
php一个解析字符串排列数组的方法
2015/05/12 PHP
jQuery技巧大放送 学习jquery的朋友可以看下
2009/10/14 Javascript
jquery下jstree简单应用 - v1.0
2011/04/14 Javascript
基于jQuery的动态增删改查表格信息,可左键/右键提示(原创自Zjmainstay)
2012/07/31 Javascript
javascript函数作用域学习示例(js作用域)
2014/01/13 Javascript
干货分享:让你分分钟学会javascript闭包
2015/12/25 Javascript
JavaScript读二进制文件并用ajax传输二进制流的方法
2016/07/18 Javascript
jQuery dataTables与jQuery UI 对话框dialog的使用教程
2016/09/02 Javascript
jQuery EasyUI tree 使用拖拽时遇到的错误小结
2016/10/10 Javascript
解析如何利用iframe标签以及js制作时钟
2016/12/08 Javascript
AngularJS中的Promise详细介绍及实例代码
2016/12/13 Javascript
js实现的xml对象转json功能示例
2016/12/24 Javascript
微信小程序倒计时功能实例代码
2018/07/17 Javascript
小程序实现多选框功能
2018/10/30 Javascript
vue+canvas实现炫酷时钟效果的倒计时插件(已发布到npm的vue2插件,开箱即用)
2018/11/05 Javascript
javascript实现智能手环时间显示
2020/09/18 Javascript
[59:26]DOTA2上海特级锦标赛D组资格赛#1 EG VS VP第二局
2016/02/28 DOTA
CentOS安装pillow报错的解决方法
2016/01/27 Python
Python实现约瑟夫环问题的方法
2016/05/03 Python
python如何使用正则表达式的前向、后向搜索及前向搜索否定模式详解
2017/11/08 Python
pyqt5 使用cv2 显示图片,摄像头的实例
2019/06/27 Python
python类的实例化问题解决
2019/08/31 Python
详解html5 canvas常用api总结(二)--绘图API
2016/12/14 HTML / CSS
加拿大便宜的隐形眼镜商店:Clearly
2016/09/15 全球购物
《月光启蒙》教学反思
2014/03/01 职场文书
马智宇结婚主持词
2014/04/01 职场文书
代理协议书
2014/04/22 职场文书
我为党旗添光彩演讲稿
2014/09/10 职场文书
汇报材料怎么写
2014/12/30 职场文书
2015年音乐教学工作总结
2015/07/22 职场文书
工作转正自我鉴定范文
2019/06/21 职场文书
Python上下文管理器Content Manager
2021/06/26 Python
Nginx配置文件详解以及优化建议指南
2021/09/15 Servers
Redis Stream类型的使用详解
2021/11/11 Redis
mysql数据库实现设置字段长度
2022/06/10 MySQL