render函數的妙用

render函數的初步瞭解

demo:未用render函數html

demo:使用render以後vue

<div id="app">
        <child :levle="level">
            梅花香自苦寒來!!!
        </child>
</div>

<template id="hdom">
    <h1 v-if="level==1">
        <slot></slot>
    </h1>
    <h2 v-if="level==2">
           <slot></slot>
    </h2>
    <h3 v-if="level==3">
        <slot></slot>
    </h3>
</template>
複製代碼
<script>
        // 需求:父組件給子組件傳遞數據如1,2,3.以插槽的形式讓子組件的內容就會變成h1h2h3
        var app = new Vue({
            el: "#app",
            data: {
                level: 1
            },
            components: {
                'child': {
                    props:['level'],
                    template: "#hdom",

                }
            }
        })
    </script>
複製代碼

深度截圖_選擇區域_20190813192416.png

這樣看起是沒有問題的,可是控制條會報錯:模板編譯錯誤.緣由是:template下只容許有一個子節點api

應該改temlate在原來的基礎上加一個div標籤包裹起來:數組

<template id="hdom">
        <div>
            <h1 v-if="level==1">
                <slot></slot>
            </h1>
            <h2 v-if="level==2">
                <slot></slot>
            </h2>
            <h3 v-if="level==3">
                <slot></slot>
            </h3>
        </div>

    </template>
複製代碼

這樣寫有個弊端: 好比我想在頁面以leve爲3也就是以h3去展現內容,可是h1和h3也會渲染出來,當程序走到level=1的時候先去看一下v-if的判斷,發現不知足又將h1移除.另外一方面,咱們須要把h1和h2的代碼也要寫在template中致使代碼很冗餘bash

因而,Vue給咱們提供了一個render函數,有了render函數咱們就不須要將每種狀況都寫在template中了,經過render函數就能夠構造出咱們想要的組件了app

<div id="app">
        <child :level="level">
            梅花香自苦寒來!!!
        </child>
    </div>
    
    <script>

        var app = new Vue({
            el: "#app",
            data: {
                level: 3
            },
            components: {
                'child': {
                    props: ['level'],
                    // 使用render函數進行定義組件
                    render:function(createElement){            
                        return createElement('h'+this.level,this.$slots.default)
                    }

                }
            }
        })
    </script>
複製代碼

render函數的第一個參數

demo:render函數的第一個參數框架

render函數的做用仍是用來構建一個組件的,以前是經過template,慢慢了解render函數了以後就會發現比原來的template進步的太多了dom

在render函數的方法中,參數必須是createElement,createElement的類型是function,render函數的第一個參數能夠是 String | Object | Function,並且第一個參數是必選ssh

  • 若是第一個參數的類型是String表明html標籤函數

  • 若是第一個參數的類型是Object表明一個含有數據選項的hash,如 template:"

    "

  • 若是第一個參數是Function它就返回一個包含數據選項的對象(和Object有點像)

<div id="app">
        <child></child>
 </div>
 
 <script>
        var app = new Vue({
            el: "#app",
            data: {

            },
            components: {
                'child': {
                    render: function (createElement) {
                        //第一二參數爲String表明html標籤
                        // return createElement('div')
                        //第一個參數爲Object表明一個含有數據選項
                        // return createElement({
                        //     template: "<p>使用render建立組件</p>"
                        // })
                        // 第一個參數爲Function表明返回一個包含數據選項的對象
                        let domObject = function () {
                            return { 
                                template: "<p>使用render建立組件</p>"
                             }
                        }
                        return createElement(domObject())
                    }
                }
            }
        })
    </script>
複製代碼

render函數的第二個參數

dom:render函數的第二個參數

render函數的第二個參數是可選的不是必須的,第二個參數是數據對象主要是組件中的模板進行一些屬性相關的設置,這個數據對象中經常使用的屬性有class:|style:|attrs:|domProps:|on:,其中class是給組件的模板加class,style是給它設置樣式,attrs是設置除了class和style以外的屬性,domProps是用來寫原生的DOM屬性,on用來如click:function(){}或者input: function(){}之類

<div id="app">
        <child></child>
</div>

<script>
        var app = new Vue({
            el: "#app",
            data: {

            },
            components: {
                'child': {
                    render: function (createElement) {
                        return createElement({template:"<div>我是主要內容</div>"},{
                            class:{
                                foo: true,
                                baz: false
                            },
                            style:{
                                color:'red',
                                fontSize:'30px',

                            },
                            // attrs用來寫除了style和class以外的屬性或正常的html上的屬性
                            attrs:{
                                id: "foo",
                                src:"https://www.baidu.com"
                            },
                            // domProps用來寫原生的DOM屬性
                            domProps:{
                                innerHTML: '<span style="color: blue;font-size: 45px;"> 我被替換了</span>'
                            }

                        })

                    }
                }
            }
        })
    </script>
複製代碼

render函數的第三個參數

demo:render的第三個參數

第三個參數表明子節點格式是可選的能夠是String或Arry,createElement()自己就是建立節點的意思

<div id="app">
    <child></child>
</div>
複製代碼
var app = new Vue({
            el: "#app",
            data: {

            },
            components: {
                'child': {
                    render: function (createElement) {
                        return createElement('div',[
                            createElement('h1','好像更喜歡原生的JS做爲初學者'),
                            createElement('h3','不喜歡框架,若是哪一天框架被ko了,咱們還能剩點什麼')
                        ])

                    }
                }
            }
        })
複製代碼

this.$slots在render函數中的應用

demo:this.$slots在render函數中的應用

<div id="app">
        <child>
            <h1 slot="header">我是標題</h1>
            <p>我是正文</p>
            <p>我是正文2</p>
            <h5 slot="footer">我是頁面的底部信息</h5>
        </child>
</div>

<script>
        var app = new Vue({
            el: "#app",
            data: {

            },
            components: {
                'child': {
                    render: function (createElement) {
                        var header = this.$slots.header   //  this.$slots.header返回的原本就是一個包含VNODE的數組
                        var main = this.$slots.default    // this.$slot.default就是用來放插槽中沒有使用slot=""分類的容器
                        var footer = this.$slots.footer
                        return createElement('div', [
                            createElement('header', header),
                            createElement('main', main),
                            createElement('footer', footer)
                        ])
                    }

                }
            }
            
        })
    </script>
複製代碼

createElement(‘header’,header), 返回的就是VNODE var header = this.$slots.header; //–這返回的內容就是含有=VNODE的數組

  • this.$slot.default就是用來放插槽中沒有使用slot=""分類的容器

  • 第三個 參數存的就是虛擬節點 VNODE也就是例子中的"[]"中建立的子節點

return createElement('div',[
    createElement('header',header),
    createElement('main',main),
    createElement('footer',footer)
])
複製代碼
  • 這些子節點是div的子節點,單都是虛擬節點,爲何須要虛擬節點呢?用JS去操做DOM樹有一個缺陷,每一次去操做DOM,以前全部的html都會去重繪,虛擬節點VNODE實際上是JavaScript的對象,Vue只要檢測到哪一個VNODE發生變化,就只去更新哪一個VNODE,不用再像以前那樣把全部的htm重繪,因此效率會更高

  • createElement('header',header)它返回的也是VNODE,那不是說createElement的第三個參數是String或者Array嗎?這裏的header原本就是包含VNODE的數組

  • var header = this.$slots.header返回的原本就是一個包含VNODE的數組

  • 咱們能夠經過dubugger調試出這裏的header的類型:

深度截圖_選擇區域_20190814065633.png

深度截圖_選擇區域_20190814065703.png

  • 咱們不妨再看一下DOM結構

深度截圖_選擇區域_20190814070320.png

  • 經過插槽咱們能夠自有的控制插槽中內容的顯示的

在render函數中使用props傳遞數據

例子出了問題

<div id="app">
        <button @click="switchShow">切換</button> {{show}}
        <child :isshow="show"> // 這裏的show是父組件的show,經過v-bind能夠把父組件的show傳給子組件

        </child>
    </div>
    
    
     <script>
        // 需求: 點擊按鈕切換圖片
        var app = new Vue({
            el: "#app",
            data: {
                show: false
            },
            methods: {
                switchShow: function () {
                    this.show = !this.show
                }
            },
            components: {
                props: ['show'],
                'child': {
                    render: function (createElement) {
                        var imgSrc
                        if (this.show) {
                            imgSrc = "./1.jpg"
                        } else {
                            imgSrc = "./2.jpg"
                        }
                        return createElement('img', {
                            attrs: {
                                src: imgSrc
                            }
                        })
                    }
                }
            }

        })
    </script>
複製代碼
<div id="app">
        <button @click="switchShow">切換</button> 
        <child :show ="show">    
            
        </child>
</div>
複製代碼

這裏的show是父組件的"show",經過v-bind能夠把父組件的show傳給子組件,但子組件中得使用props去接收一下變成子組件本身做用域可使用的"show"

回顧一下過程: 首先在父組件中的data定義一個show做爲變量初始值爲false,在按鈕上綁定方法switchShow,方法的內容爲每次點擊按鈕設置show的值爲取反,而後父組件經過v-bind把show傳給子組件,子組件用props接收.接着開始使用render函數構造組件,先定義一個imgSrc做爲圖片的路徑,當show爲true時讓imgSrc路徑爲A爲false時imgSrc路徑爲B,組件的子虛擬節點建立img標籤,並在createElement的第二個參數設置src=imgSrc

v­model在render函數中的使用

在render函數中未使用v-model會怎樣

<div id="app">
        <child :name="name" @input="showName"></child>
       {{name}}
</div>

<script>
        // 需求: 在輸入框中實時顯示 

        Vue.component('child',{
            props:['name'],
            render: function(createElement){
                debugger
                var self = this// self值得是vue實例    爲何要用self?
                return createElement('input',{
                    domProps:{
                        value: self.name
                    },
                    on: {
                        // 子組件給父組件傳遞數據
                        input: function(event){// 默認參數是原生的event事件
                            let a = this     // 此處的this爲input
                            // 子組件給父組件傳遞的數據是輸入框中的value
                            self.$emit('input',event.target.value)     // 觸發input事件  爲何要用self? 不用this?
                        }
                    }
                })
            }
        })

        var app = new Vue({
            el: "#app",
            data: {
                name:"Reagen",
            },
            methods:{
                showName:function(value){  // value是子組件觸發input事件的時候傳的值
                console.log(value)
                    this.name = value
                }
            }

        })
    </script>
複製代碼

回顧一下整個過程:

1.在vue實例的data中聲明一個name

2.建立組件,將組件插入頁面

3.在組件上綁定父組件的name,而且綁定監聽input事件

4.使用render函數構造組件(記得var self = this),建立input輸入框,使用 domProps記住初始值value: self.name,使用on監聽input事件,用$emit觸發input事件並把input輸入框中的數據傳給父組件

5.一旦在輸入框中輸入內容就會觸發子組件中綁定的showName方法,父組件張定義這showName方法,並用參數calue接收子組件傳遞的消息,同時把本身身上的name修改成子組件傳遞過來的valuethis.name = value



經過debugger看一下2處debugger下面的self和this分別指什麼

深度截圖_選擇區域_20190814092611.png

深度截圖_選擇區域_20190814094638.png

爲何須要用self? 由於在使用$emit觸發input事件是須要vue實例去觸發的,因此要使用self記住vue實例

使用v-model就會變得簡單了

在render函數中使用v-model

<div id="app">
        <child :name="name" @input="showName"></child>
       {{name}}
</div>


<script>
        // 需求: 在輸入框中實時顯示 

        Vue.component('child',{
            props:['name'],
            render: function(createElement){
                var self = this// self值得是vue實例    爲何要用self?
                console.dir(self)
                return createElement('input',{
                    domProps:{
                        value: self.name
                    },
                    on: {
                        // 子組件給父組件傳遞數據
                        input: function(event){// 默認參數是原生的event事件
                            // 子組件給父組件傳遞的數據是輸入框中的value
                            debugger
                            let a = this                             // 此處的this指input
                            console.dir(a)

                            self.$emit('input',event.target.value)     // 觸發input事件  爲何要用self?
                        }
                    }
                })
            }
        })

        var app = new Vue({
            el: "#app",
            data: {
                name:"Reagen",
            },
            methods:{
                showName:function(value){  // value是子組件觸發input事件的時候傳的值
                console.log(value)
                    this.name = value
                }
            }

        })
    </script>
複製代碼

有了v-model就不須要showName方法了,它作了2件事情,首先是在組件上綁定了父組件的name,而後是綁定了input事件,當使用v-model後只要在input中輸入內容,它就會把輸入的內容替換以前綁定的name

總的來講v-model就是作了2件事情:接收了input的內容,把輸入的內容賦值給了綁定的name

做用域插槽在render函數中的使用

做用域插槽在render函數中的使用

<div id="app">
        <child>
            <template scope="prop">
                {{prop.text}}
            </template>

        </child>


</div>

 <script>

        Vue.component('child', {
            render: function (createElement) {
                return createElement('div', this.$scopedSlots.default({
                    text: '我是子組件傳遞過來的數據',
                    msg: 'scopetext'
                }))
            }
        })

        var app = new Vue({
            el: "#app",
            data: {

            }
        })
  </script>
複製代碼

若是想要向組件的插槽中傳遞數據:

1.定義一個template模板

  1. 在這個template模板上定義scope="prop"(其中prop爲自定的,用來接收子組件傳遞的數據)

3.用文本插值的方式prop.text(text爲子組件中傳遞的數據)

注意: 在render函數中,給插槽傳遞數據的api爲this.$scopedSlots.default()

函數化組件的應用

函數化組件的應用

<div id="app">
        
        <child value="haha" :ms="msg">

        </child>
            
</div>

<script>
        // this.text   =====>  被代替  context.props.text
        // this.$slots.default=  =====>   被代替  context.children(若是有插槽就放到child組件裏面)
        Vue.component('child',{
            functional:true  ,       // 表示當前的實例無狀態無實例(也就是沒有this,那麼傳遞過來的數據怎麼拿?)
            render: function(createElement,context){ // context爲上下文對象,經過它能夠拿到想拿到的數據如data|props|slots|parents|children
                return createElement('button',{
                    on:{
                        click:function(){
                            console.log(context)
                            console.log(context.parent)  // 指的就是父組件 #app
                            console.log(context.props.value)
                            console.log(context.props.ms)
                            debugger;
                            console.log(this.value)           // 爲何這樣拿不到? functional:true後 沒法拿到外界的數據  只能用context去獲取

                        }
                    }
                },'點擊我學習context')
            },
            props:["value","ms"]
        })

        var app = new Vue({
            el:"#app",
            data:{
               name: "reagen",
               msg:"柳暗花明"
            }
        })
    </script>

複製代碼

functional: true,表示該組件無狀態無實例,也就是說用函數化來構建組件的時候他組建中是沒有data和this的

使用函數話組件的步驟:

第一步:給functional設置爲truefunctional:true

第二步: 在使用render函數構造組件的時候將context獲取上下文對象當作render函數的參數傳進來

render: function(createElement,context){ ... }

第三步:經過context就能夠拿到父組件和子組件的全部數據了

使用函數化組件的先後最大區別在於:

以前在組件中使用this.xxx來獲取數據,如今是使用上下文對象context去獲取了

相關文章
相關標籤/搜索