Vue render深切窺伺之謎

簡介

在運用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
    原文地址: https://segmentfault.com/a/1190000014346110
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞