如何理解Vue的render函式

洪定倫發表於2017-08-29

第一個引數(必須) – {String | Object | Function}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>render</title>
    <script src="https://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
</head>
<body>
    <div id="app">
        <elem></elem>
    </div>
    <script>
        Vue.component(`elem`, {
            render: function(createElement) {
                return createElement(`div`);//一個HTML標籤字元
                /*return createElement({
                    template: `<div></div>`//元件選項物件
                });*/
                /*var func = function() {
                    return {template: `<div></div>`}
                };
                return createElement(func());//一個返回HTML標籤字元或元件選項物件的函式*/
            }
        });
        new Vue({
            el: `#app`
        });
    </script>
</body>
</html>

第二個引數(可選) – {Object}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>render</title>
    <script src="https://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
</head>
<body>
    <div id="app">
        <elem></elem>
    </div>
    <script>
        Vue.component(`elem`, {
            render: function(createElement) {
                var self = this;
                return createElement(`div`, {//一個包含模板相關屬性的資料物件
                    `class`: {
                        foo: true,
                        bar: false
                    },
                    style: {
                        color: `red`,
                        fontSize: `14px`
                    },
                    attrs: {
                        id: `foo`
                    },
                    domProps: {
                        innerHTML: `baz`
                    }
                });
            }
        });
        new Vue({
            el: `#app`
        });
    </script>
</body>
</html>

第三個引數(可選) – {String | Array}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>render</title>
    <script src="https://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
</head>
<body>
    <div id="app">
        <elem></elem>
    </div>
    <script>
        Vue.component(`elem`, {
            render: function(createElement) {
                var self = this;
                // return createElement(`div`, `文字`);//使用字串生成文字節點
                return createElement(`div`, [//由createElement函式構建而成的陣列
                    createElement(`h1`, `主標`),//createElement函式返回VNode物件
                    createElement(`h2`, `副標`)
                ]);
            }
        });
        new Vue({
            el: `#app`
        });
    </script>
</body>
</html>

兩種元件寫法對比

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>render</title>
    <script src="https://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
</head>
<body>
    <div id="app">
        <ele></ele>
    </div>
    <script>
        /*Vue.component(`ele`, {
            template: `<div id="elem" :class="{show: show}" @click="handleClick">文字</div>`,
            data: function() {
                return {
                    show: true
                }
            },
            methods: {
                handleClick: function() {
                    console.log(`clicked!`);
                }
            }
        });*/
        Vue.component(`ele`, {
            render: function(createElement) {
                return createElement(`div`, {
                    `class`: {
                        show: this.show
                    },
                    attrs: {
                        id: `elem`
                    },
                    on: {
                        click: this.handleClick
                    }
                }, `文字`);
            },
            data: function() {
                return {
                    show: true
                }
            },
            methods: {
                handleClick: function() {
                    console.log(`clicked!`);
                }
            }
        });
        new Vue({
            el: `#app`
        });
    </script>
</body>
</html>

this.$slots用法

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>render</title>
    <script src="https://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
</head>
<body>
    <div id="app">
        <blog-post>
            <h1 slot="header"><span>About Me</span></h1>
            <p>Here`s some page content</p>
            <p slot="footer">Copyright 2016 Evan You</p>
            <p>If I have some content down here</p>
        </blog-post>
    </div>
    <script>
        Vue.component(`blog-post`, {
            render: function(createElement) {
                var header = this.$slots.header,//返回由VNode組成的陣列
                    body = this.$slots.default,
                    footer = this.$slots.footer;
                return createElement(`div`, [
                    createElement(`header`, header),
                    createElement(`main`, body),
                    createElement(`footer`, footer)
                ])
            }
        });
        new Vue({
            el: `#app`
        });
    </script>
</body>
</html>

使用props傳遞資料

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>render</title>
    <script src="https://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
</head>
<body>
    <div id="app">
        <ele :show="show"></ele>
        <ele :show="!show"></ele>
    </div>
    <script>
        Vue.component(`ele`, {
            render: function(createElement) {
                if (this.show) {
                    return createElement(`p`, `true`);
                } else {
                    return createElement(`p`, `false`);
                }
            },
            props: {
                show: {
                    type: Boolean,
                    default: false
                }
            }
        });
        new Vue({
            el: `#app`,
            data: {
                show: false
            }
        });
    </script>
</body>
</html>

VNodes必須唯一

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>render</title>
    <script src="https://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
</head>
<body>
    <!-- VNode必須唯一 -->
    <div id="app">
        <ele></ele>
    </div>
    <script>
        var child = {
            render: function(createElement) {
                return createElement(`p`, `text`);
            }
        };
        /*Vue.component(`ele`, {
            render: function(createElement) {
                var childNode = createElement(child);
                return createElement(`div`, [
                    childNode, childNode//VNodes必須唯一,渲染失敗
                ]);
            }
        });*/
        Vue.component(`ele`, {
            render: function(createElement) {
                return createElement(`div`, 
                    Array.apply(null, {
                        length: 2
                    }).map(function() {
                        return createElement(child)//正確寫法
                    })
                );
            }
        });
        new Vue({
            el: `#app`
        })
    </script>
</body>
</html>

v-model指令

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>render</title>
    <script src="https://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
</head>
<body>
    <div id="app">
        <el-input :name="name" @input="val=>name=val"></el-input>
        <div>你的名字是{{name}}</div>
    </div>
    <script>
        Vue.component(`el-input`, {
            render: function(createElement) {
                var self = this;
                return createElement(`input`, {
                    domProps: {
                        value: self.name
                    },
                    on: {
                        input: function(event) {
                            self.$emit(`input`, event.target.value);
                        }
                    }
                })
            },
            props: {
                name: String
            }
        });
        new Vue({
            el: `#app`,
            data: {
                name: `hdl`
            }
        });
    </script>
</body>
</html>

作用域插槽

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>render</title>
    <script src="https://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
</head>
<body>
    <div id="app">
        <ele>
            <template scope="props">
                <span>{{props.text}}</span>
            </template>
        </ele>
    </div>
    <script>
        Vue.component(`ele`, {
            render: function(createElement) {
                // 相當於<div><slot :text="msg"></slot></div>
                return createElement(`div`, [
                    this.$scopedSlots.default({
                        text: this.msg
                    })
                ]);
            },
            data: function() {
                return {
                    msg: `來自子元件`
                }
            }
        });
        new Vue({
            el: `#app`
        });
    </script>
</body>
</html>

向子元件中傳遞作用域插槽

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>render</title>
    <script src="https://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
</head>
<body>
    <div id="app">
        <ele></ele>
    </div>
    <script>
        Vue.component(`ele`, {
            render: function(createElement) {
                return createElement(`div`, [
                    createElement(`child`, {
                        scopedSlots: {
                            default: function(props) {
                                return [
                                    createElement(`span`, `來自父元件`),
                                    createElement(`span`, props.text)
                                ];
                            }
                        }
                    })
                ]);
            }
        });
        Vue.component(`child`, {
            render: function(createElement) {
                return createElement(`b`, this.$scopedSlots.default({text: `我是元件`}));
            }
        });
        new Vue({
            el: `#app`
        });
    </script>
</body>
</html>

函式化元件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>render</title>
    <script src="https://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
</head>
<body>
    <div id="app">
        <smart-item :data="data"></smart-item>
        <button @click="change(`img`)">切換為圖片為元件</button>
        <button @click="change(`video`)">切換為視訊為元件</button>
        <button @click="change(`text`)">切換為文字元件</button>
    </div>
    <script>
        // 圖片元件選項
        var ImgItem = {
            props: [`data`],
            render: function(createElement) {
                return createElement(`div`, [
                    createElement(`p`, `圖片元件`),
                    createElement(`img`, {
                        attrs: {
                            src: this.data.url
                        }
                    })
                ]);
            }
        }
        // 視訊元件
        var VideoItem = {
            props: [`data`],
            render: function(createElement) {
                return createElement(`div`, [
                    createElement(`p`, `視訊元件`),
                    createElement(`video`, {
                        attrs: {
                            src: this.data.url,
                            controls: `controls`,
                            autoplay: `autoplay`
                        }
                    })
                ]);
            }
        };
        /*純文字元件*/
        var TextItem = {
            props: [`data`],
            render: function(createElement) {
                return createElement(`div`, [
                    createElement(`p`, `純文字元件`),
                    createElement(`p`, this.data.text)
                ]);
            }
        };

        Vue.component(`smart-item`, {
            functional: true,
            render: function(createElement, context) {
                function getComponent() {
                    var 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
                }
            }
        });
        new Vue({
            el: `#app`,
            data() {
                return {
                    data: {}
                }
            },
            methods: {
                change: function(type) {
                     if (type === `img`) {
                        this.data = {
                            type: `img`,
                            url: `https://raw.githubusercontent.com/iview/iview/master/assets/logo.png`
                        }
                    } 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: function() {
                this.change(`img`);
            }
        });
    </script>
</body>
</html>

相關文章