文档章节

Vue.js-渲染函数 & JSX

tianyawhl
 tianyawhl
发布于 2017/09/06 15:26
字数 2090
阅读 43
收藏 0
点赞 0
评论 0

Vue推荐在绝大多数情况下使用template来创建你的Html,然而在一些场景中,你真的需要JavaScript的完全编程的能力,这就是、render函数,它比template更接近编译器
使用template例子

<body class="">
    <div id="example">
        <my-component v-bind:level="1">hello</my-component>
    </div>
    <script src="js/vue.js"></script>
    <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>
    </script>
    <script>
    Vue.component("my-component", {
        template: "#anchored-heading-template",
        props: {
            level: {
                //type: Number,也可不加
                //required: true
            }
        }
    })
    var app = new Vue({
        el: "#example"
    })
    </script>
</body>

在这种场景中使用template并不是最好的选择:首先代码冗长,为了在不同级别的标题中插入锚点元素,我们需要重复的使用<slot></slot>
虽然模板在大多数组件中都非常好用,但是在这里它就不是那么简洁了,那么我们来尝试使用render函数重写上面的例子

<body class="">
    <div id="example">
        <my-component v-bind:level="2">HELO</my-component>
    </div>
    <script src="js/vue.js"></script>
    <script>
    Vue.component("my-component", {
        render: function(createElement) {
            return createElement("h" + this.level, this.$slots.default)
        },
        props: {
            level: {
                //type: Number,
                //required: true
            }
        }
    })
    var app = new Vue({
        el: "#example"
    })
    </script>
</body>

这样代码简单清晰多了,在这个例子中,你需要知道当你不使用slot属性向组件中传递内容时,如上面的"hello world",这些子元素被存储在组件实例的$slots.default

结点、树、以及虚拟DOM

<body class="">
    <div id="example">
        <my-component v-bind:level="1">

          Helloworld!
          <h2>
              woqu
              <h3>what</h3>
          </h2>
        
        </my-component>
    </div>
    <script src="js/vue.js"></script>
    <script>
    var getChildrenTextContent = function(children) {
        return children.map(function(node) {
            return node.children ? getChildrenTextContent(node.children) : node.text
        }).join("")
    }
    Vue.component("my-component", {
        render: function(createElement) {
            var headingId = getChildrenTextContent(this.$slots.default)

                .toLowerCase().replace(/\W+/g, '-').replace(/^\-|\-$/g, '')
            return createElement(
                "h" + this.level, [
                    createElement("a", {
                        attrs: {
                            name: headingId,
                            href: "#" + headingId
                        }
                    }, this.$slots.default)
                ]

            )
        },
        props: {
            level: {
                type: Number,
                required: true
            }
        }
    })
    var app = new Vue({
        el: "#example"
    })
    </script>
</body>

组件树中所有VNodes必须是唯一的,这意味着,下面的render function是无效的
render: function (createElement) {
  var myParagraphVNode = createElement('p', 'hi')
  return createElement('div', [
    // 错误-重复的VNodes
    myParagraphVNode, myParagraphVNode
  ])
}

如果你真的需要重复很多次的元素/组件,你可以使用工厂函数来实现。例如:下面这个例子render函数
完美的渲染了10个重复的段落  其中{length: 10}理解为Array like,即类数组对象(包含length属性)。

<body class="">
    <div id="example">
        <my-component></my-component>
    </div>
    <script src="js/vue.js"></script>
    <script>
    Vue.component("my-component",{
        render:function(createElement){
            return createElement("div",
                Array.apply(null,{length:10}).map(function(){
                    return createElement("p","hi")
                }))
        }
    })
     var app= new Vue({
        el:"#example"
     })
    </script>
</body>

使用JavaScript代替模板功能,由于使用原生的JavaScript来实现某些东西很简单,Vue的render函数没有提供专用的API,比如,template中的v-if和v-for

<body class="">
    <div id="example">
        <my-component></my-component>
    </div>
    <script src="js/vue.js"></script>
    <script>
    Vue.component("my-component", {
        render: function(createElement) {
            if (this.items.length) {
                return createElement("ul", this.items.map(function(item) {
                    return createElement("li", item.name)
                }))
            } else {
                return createElement("p", "No items found.")
            }
        },
        data: function() {
            return {
                items: [
                    { name: "lily" },
                    { name: "join" },
                    { name: "jack" }
                ]
            }

        }
    })
    var app = new Vue({
        el: "#example",
    })
    </script>
</body>

也可以

<body class="">
    <div id="example">
        <my-component v-bind:items="items"></my-component>
    </div>
    <script src="js/vue.js"></script>
    <script>
    Vue.component("my-component", {
        props:["items"],
        render: function(createElement) {
            if (this.items.length) {
                return createElement("ul", this.items.map(function(item) {
                    return createElement("li", item.name)
                }))
            } else {
                return createElement("p", "No items found.")
            }
        },
       
    })
    var app = new Vue({
        el: "#example",
        data:{
            items: [
                    { name: "lily" },
                    { name: "join" },
                    { name: "jack11" }
                ]
        }
    })
    </script>
</body>

render函数中没有与v-model相应的API,你必须自己来实现相应的逻辑

<body class="">
    <div id="app">
        <anchored-heading v-bind:value="data" v-on:input="show($event)"></anchored-heading>
        {{data}}
    </div>
    <script src="js/vue.js"></script>
    <script>
    Vue.component('anchored-heading', {
        props: ["value"],
        render: function(createElement) {
            var self = this;
            return createElement("input", {
                domProps: {
                    value: self.value
                },
                on: {
                    input: function(event) {
                        self.$emit('input', event.target.value)
                    }
                }
            })
        },

    })
    var app = new Vue({
        el: '#app',
        data: {
            data: "hello"
        },
        methods: {
            show: function(a) {
                this.data = a
                console.log(a)
            }
        }
    })
    </script>
</body>

你可以从this.$slots获取VNodes列表中的静态内容

<body class="">
    <div id="example">
        <my-component>
            <h2>hello slot</h2>
            <span>spantext</span>
        </my-component>
    </div>
    <script src="js/vue.js"></script>
    <script>
    Vue.component("my-component", {
        render: function(createElement) {
            return createElement("div", this.$slots.default)
        }
    })
    var app = new Vue({
        el: "#example",
    })
    </script>
</body>
渲染成
<div id="example">
        <div>
            <h2>hello slot</h2> <span>spantext</span>
        </div>
</div>

还可以从this.$scopedSlots中获得能用作函数的作用域插槽,这个函数返回VNodes:

<body class="">
    <div id="example">
        <my-component v-bind:msg="msg">
            <template scope="props">{{props.text}}</template>
        </my-component>
    </div>
    <script src="js/vue.js"></script>
    <script>
    Vue.component("my-component", {
        props: ["msg"],
        render: function(createElement) {
            return createElement("div", [this.$scopedSlots.default({
                text: this.msg + "lily"
            })])
        }
    })
    var app = new Vue({
        el: "#example",
        data: {
            msg: "this is the text"
        }
    })
    </script>
</body>
渲染为
    <div id="example">
        <div>this is the textlily</div>
    </div>

如果要用渲染函数向子组件中传递作用域插槽,可以利用 VNode 数据中的 scopedSlots域:

<body class="">
    <div id="app">
    <ele>
	  
	</ele>
    </div>
    <script src="js/vue.js"></script>
   <script>
    Vue.component('ele', {
      render: function(createElement) {
        return createElement('div', [
          createElement('child', {
            scopedSlots: {
              default: function(props) {
                return [
                  createElement('div', '来自父组件'),
                  createElement('span', props.text)
                ];
              }
            }
          })
        ]);
      }
    });
    Vue.component('child', {
      render: function(createElement) {
        return createElement('b', this.$scopedSlots.default({text: '我是组件'}));
      }
    });
    new Vue({
      el: '#app'
    });
  </script>
</body>

渲染成

    <div id="app">
        <div><b><div>来自父组件</div><span>我是组件</span></b></div>
    </div>

函数化组件
之前创建的锚点标题组件比较简单,没有管理或者监听任何传递给他的信息,也没有生命周期方法,它只是一个接收参数的函数
在这个例子中,我们标记组件为functional,这意味它是无状态(没有data),无实例(没有this上下文),一个函数化组件就像这样
Vue.component('my-component', {
  functional: true,
  // 为了弥补缺少的实例
  // 提供第二个参数作为上下文
  render: function (createElement, context) {
    // ...
  },
  // Props 可选
  props: {
    // ...
  }
})

注意:在2.3.0之前的版本中,如果一个函数式组件想要接受props,则props选项是必须的。在2.3.0或者以上的版本中,你可以省略props选项,所有组件上的属性都会被自动解析为props
组件需要的一切都是通过上下文传递,包括:
props:提供props的对象
children:VNode子节点的数组
slot:slots对象
data:传递给组件的data对象
parent:对父组件的引用
listeners:(2.3.0+)一个包含了组件上所注册的v-on诊听器的对象。这只是一个指向data.on的别名
injections:(2.3.0+)如果使用了inject选项,则该对象包含了应该被注入的属性

在添加functional:true之后,锚点标题组件的render函数之间简单更新增加context参数,this.$slots.default更新为context.children,之后this.level更新为context.props.level
因为函数化组件只是一个函数,所以渲染开销也低很多。然而,对持久化实例的缺乏也意味着函数化组件不会出现在Vue devtools的组件树里,在作为包装组件时它们也同样非常有用,比如,当你需要做这些时
程序化地在多个组件中选择一个,再将children,props,data传递给子组件之前操作它们
下面是一个依赖传入props的值的smart-list组件例子,它能代表更多具体的组件

<body>
    <div id="example">
        <smart-item v-bind:data="data"></smart-item>
        <button v-on:click="change('img')">显示图片</button>
        <button v-on:click="change('video')">显示视频</button>
        <button v-on:click="change('text')">显示文本</button>
    </div>
    <script src="js/vue.js"></script>
    <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", {
                    style: {
                        width: '500px',
                    },
                    attrs: {
                        src: this.data.url,
                        controls: "controls",
                        autoplay: "autoplay"
                    },

                })
            ])
        }

    }
    //纯文本组件
    var TextItem = {
        props: ["data"],//2、接收1传过来的props对象
        render: function(createElement) {
            return createElement("div", [
                createElement("p", "这是一个文本组件"),
                createElement("div", this.data.content)
            ])
        }
    }
    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  //1、用于传给子组件 如:TextItem
                    }
                },
                context.children
            )
        },
        //props选项在2.3.0及以上版本可以省略,
        props: {
            data: {
                type: Object,
                required: true
            }
        }
    })
    var app = new Vue({
        el: "#example",
        data: {
            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>
  <script>
    Vue.component('choice', {
      template: '<div><ul><slot></slot></ul></div>'
    });

    Vue.component('item', {
      functional: true,
      render: function(h, context) {
        return h('li', {
          on: {
            click: function() {
              console.log(context.children);
			  console.log(context);
              console.log(context.parent);
              console.log(context.props)
            }
          }
        }, context.children)
      },
      props: ['value']
    })

    new Vue({
      el: '#container',
      data: {
        msg: 'hello'
      }
    });
  </script>

最后渲染成
<ul><li>test</li></ul>

 

slots()和children对比
你可能想知道为什么同时需要slots()和children。slots().default不是和children类似吗
在一些场景中,是这样,但是如果是函数式组件和下面这样的children呢
<my-functional-component>
  <p slot="foo">
    first
  </p>
  <p>second</p>
</my-functional-component>
对于这个组件,children会给你两个段落标签,而slots().default只会传递第二个匿名段落标签,slots().foo会传递第一个具名段落标签。同时拥有children和slots()
因此你可以选择让组件通过、slot()系统分发或者简单的通过children接收,让其它组件去处理

© 著作权归作者所有

共有 人打赏支持
tianyawhl
粉丝 1
博文 192
码字总数 88621
作品 0
常州
前端工程师
[译] Vue.js 还是 React?你会选择哪一个?为什么?

原文地址:Vue.js or React ? Which you would chose and why? 原文作者:evilpingwin 译文出自:掘金翻译计划 本文永久链接:github.com/xitu/gold-m… 译者:allenlongbaobao 校对者:Kyl...

allenWang ⋅ 06/17 ⋅ 0

用 TS + Vue 写了一个在 Chrome 中运行 Prettier 格式化的扩展程序

在介绍 prettier-chrome 之前请允许我先介绍一下什么是 Prettier 如果你已经知道这是什么了并且已经会配置了,那么你可以直接跳到 prettier-chrome 的介绍 什么是 Prettier ? 有见识的代码格...

u3u ⋅ 05/29 ⋅ 0

在Vue中使用JSX的正确姿势(有福利)

姿势很重要,末尾有福利 vue-antd-ui开源了一段时间后,收到了一些反馈,尤其是Form组件上线后,很多用户对JSX的使用感到迷惑和不习惯,为此专门介绍下Vue JSX的使用姿势及注意事项。 Form组...

zeka ⋅ 06/14 ⋅ 0

JavaScript 开发框架横向比对(Vue、React 和 Angular)

1 背景比对 MIT license 与 BSD-license 之间的区别是:MIT license 允许衍生软件加上我们自己的名字做推广,而 BSD license 不可以。 MVVM(Model-View-ViewModel):将其中的View 的状态和...

deniro ⋅ 05/30 ⋅ 0

Step-by-step,打造属于自己的 Vue SSR

编者按:本文由 玩弄心里的鬼 发表于掘金,已授权奇舞周刊转载 笔者最近在和小伙伴对vue项目进行ssr的升级,本文笔者将根据一个简单拿vue cli构建的客户端渲染的demo一步一步的教大家打造自己...

奇舞周刊 ⋅ 04/17 ⋅ 0

Vue2.0笔记——Vue实例

Vue实例 每个 Vue 应用都是通过用 Vue 函数创建一个新的 Vue 实例开始的: 当创建一个 Vue 实例时,你可以传入一个选项对象,就如前面所提到的如:data,methods,computed,watch等等。 一个 Vu...

逝岁月 ⋅ 04/18 ⋅ 0

你或许不知道Vue的这些小技巧

前言 用Vue开发一个网页并不难,但是也经常会遇到一些问题,其实大部分的问题都在文档中有所提及,再不然我们通过谷歌也能成功搜索到问题的答案,为了帮助小伙伴们提前踩坑,在遇到问题的时候...

OBKoro1 ⋅ 06/04 ⋅ 0

mpvue: vuejs和小程序碰撞出来的火花

微信自推出小程序以来,热度一直居高不下,各大公司开始专门开发小程序,但是小程序自定义的wxml和wxss和自己定义的语法,让被三大框架统治的前端江湖头疼不易,因为需要专门为小程序开发一...

蜗牛老湿 ⋅ 05/18 ⋅ 0

人人都能懂的Vue源码系列(二)—Vue构造函数

上篇博文中说到Vue源码的目录结构是什么样的,每个目录的作用应该也有所了解。我们知道core/instance目录主要是用来实例化Vue对象,所以我们在这个目录下去寻找Vue构造函数。果然找到了Vue构...

淼淼真人 ⋅ 05/21 ⋅ 0

如何解释vue的生命周期才能令面试官满意?

当面试官问:“谈谈你对vue的生命周期的理解”,听到这句话你是不是心里暗自窃喜:这也太容易了吧,不就是beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDes...

闰土大叔 ⋅ 04/14 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Jenkins实践3 之脚本

#!/bin/sh# export PROJ_PATH=项目路径# export TOMCAT_PATH=tomcat路径killTomcat(){pid=`ps -ef | grep tomcat | grep java|awk '{print $2}'`echo "tom...

晨猫 ⋅ 今天 ⋅ 0

Spring Bean的生命周期

前言 Spring Bean 的生命周期在整个 Spring 中占有很重要的位置,掌握这些可以加深对 Spring 的理解。 首先看下生命周期图: 再谈生命周期之前有一点需要先明确: Spring 只帮我们管理单例模...

素雷 ⋅ 今天 ⋅ 0

zblog2.3版本的asp系统是否可以超越卢松松博客的流量[图]

最近访问zblog官网,发现zlbog-asp2.3版本已经进入测试阶段了,虽然正式版还没有发布,想必也不久了。那么作为aps纵横江湖十多年的今天,blog2.2版本应该已经成熟了,为什么还要发布这个2.3...

原创小博客 ⋅ 今天 ⋅ 0

聊聊spring cloud的HystrixCircuitBreakerConfiguration

序 本文主要研究一下spring cloud的HystrixCircuitBreakerConfiguration HystrixCircuitBreakerConfiguration spring-cloud-netflix-core-2.0.0.RELEASE-sources.jar!/org/springframework/......

go4it ⋅ 今天 ⋅ 0

二分查找

二分查找,也称折半查找、二分搜索,是一种在有序数组中查找某一特定元素的搜索算法。搜素过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜素过程结束;如果某一特定元素大于...

人觉非常君 ⋅ 今天 ⋅ 0

VS中使用X64汇编

需要注意的是,在X86项目中,可以使用__asm{}来嵌入汇编代码,但是在X64项目中,再也不能使用__asm{}来编写嵌入式汇编程序了,必须使用专门的.asm汇编文件来编写相应的汇编代码,然后在其它地...

simpower ⋅ 今天 ⋅ 0

ThreadPoolExecutor

ThreadPoolExecutor public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, ......

4rnold ⋅ 昨天 ⋅ 0

Java正无穷大、负无穷大以及NaN

问题来源:用Java代码写了一个计算公式,包含除法和对数和取反,在页面上出现了-infinity,不知道这是什么问题,网上找答案才明白意思是负的无穷大。 思考:为什么会出现这种情况呢?这是哪里...

young_chen ⋅ 昨天 ⋅ 0

前台对中文编码,后台解码

前台:encodeURI(sbzt) 后台:String param = URLDecoder.decode(sbzt,"UTF-8");

west_coast ⋅ 昨天 ⋅ 0

实验楼—MySQL基础课程-挑战3实验报告

按照文档要求创建数据库 sudo sercice mysql startwget http://labfile.oss.aliyuncs.com/courses/9/createdb2.sqlvim /home/shiyanlou/createdb2.sql#查看下数据库代码 代码创建了grade......

zhangjin7 ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部