• vue render函数


    基础

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

    <h1>
    <a name="hello-world" href="#hello-world">
    Hello world!
    </a>
    </h1>
     
    在html层,我们决定这样定义组件接口:
    <anchored-heading :level="1">Hello world!</anchored-heading>
     

    当我们开始写一个通过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>;
     
    虽然模板在大多数组件中都非常好用,但是在这里它就不是最简洁的了。那么,我们来尝试使用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中。如果你还不了解,在深入render函数前,需要了解实例的API.
     
    createElement元素
    第二件需要熟悉的是如何在createElement函数中生成模板。这里是createElement接受的参数:
    // @returns {VNode}
    createElement(
    // {String | Object | Function}
     // 一个 HTML 标签字符串,组件选项对象,或者一个返回值类型为String/Object的函数,必要参数
     'div',
     
    // {Object}
     // 一个包含模板相关属性的数据对象
     // 这样,您可以在 template 中使用这些属性.可选参数.
    {
       // (详情见下一节)
    },
     
    // {String | Array}
     // 子节点 (VNodes),由 `createElement()` 构建而成,
    // 或简单的使用字符串来生成“文本结点”。可选参数。
    [
    '先写一些文字',
    createElement('h1', '一则头条'),
    createElement(MyComponent, {
    props: {
    someProp: 'foobar'
    }
    })
    ]
    )
     
    深入data object函数
    有一件事要注意:正如在模板语法中,v-bind:class和v-bind:style,会被特别对待一样,在VNodeV数据对象中,下列属性名是级别最高的字段。该对象也允许你绑定普通的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
    },
    // 自定义指令. 注意事项:不能对绑定的旧值设值
     // 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 指定名称
     slot: 'name-of-slot',
    // 其他特殊顶层属性
    key: 'myKey',
    ref: 'myRef'
    }
     

    约束

    VNodes必须唯一

    组件树中的所有VNodes必须是唯一的。这意味着下面的render函数是无效的

    render:function(creatElement){

    var myParagraphVNode = createElement('p', 'hi');

    return createElement('div', [
       // 错误-重复的VNodes
       myParagraphVNode, myParagraphVNode
    ])

    }

    如果你真的需要重复很多次的元素/组件,你可以使用工厂函数来实现,例如,下面这个例子中render函数完美有效的渲染了20个重复的段落:

    render:function(createElement){

    return createElement('div',Array.apply(null,{length:20}).map(function(){

    return createElement('p','hi');

    }))

    }

    使用javascript代替模板功能

    v-if and v-for

    由于使用原生的javascript来实现某些东西很简单,vue的render函数没有提供专用的API,比如:template中的v-if 和v-for.

    <ul v-if="items.length">
    <li v-for="item in items">{{ item.name }}</li>
    </ul>
    <p v-else>No items found.</p>
    这些都会在render函数中被javascript的if/else和map重写
    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.')
    }
     
    v-model
    render函数中没有与v-model相应的api,你必须自己来实现相应的逻辑:
    render:function(createElement){
    var self = this;
    return createElement('input',{
    domprops:{
    vlaue:self.value
    },
    on:{
    input:function(event){
    self.value = event.target.value;
    self.$emit('input',event.target.value);
    }
    }
    })
    }
     
    这就是深入底层要付出的,尽管麻烦一些,但相对于v-model来说,你可以更灵活的控制。
     
    事件&按键修饰符
    对于.passive .capture 和.once事件修饰符。vue提供了相对应的前缀可以用于on:
    Modifier(s)Prefix
    .passive &
    .capture !
    .once ~
    .capture.once or
    .once.capture
    ~!
    on: {
    '!click': this.doThisInCapturingMode,
    '~keyup': this.doThisOnce,
    `~!mouseover`: this.doThisOnceInCapturingMode
    }
     
    对于其他的修饰符,前缀不是很重要,因为你可以直接在事件处理函数中使用事件方法:

    Modifier(s)Equivalent in Handler
    .stop event.stopPropagation()
    .prevent event.preventDefault()
    .self if (event.target !== event.currentTarget) return
    Keys:
    .enter.13
    if (event.keyCode !== 13) return (change 13 to another key code for other key modifiers)
    Modifiers Keys:
    .ctrl.alt.shift.meta
    if (!event.ctrlKey) return (change ctrlKey to altKeyshiftKey, or metaKey, respectively)

    下面是使用了所有修饰符的例子:

    on:{

    keyup:function(event){

    //如果触发事件的元素不是事件绑定的元素

    //则返回

    if(event.target !== event.currentTarget)return

    // 如果按下去的不是enter键或者
       // 没有同时按下shift键
       // 则返回

    if(!event.shiftKey || event.keyCode !== 13)return 

    // 阻止 事件冒泡
      event.stopPropagation()
       // 阻止该元素默认的keyup事件
       event.preventDefault()
    // ...

    }

    }

    slots

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

    render:function(createElement){

    createElement('div',this.$slots.default)

    }

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

    render:function(createElement){

    return createElement('div',[this.$scopedSlots.default({text:this.msg})])

    }

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

    render:function(createElement){

    return createElement('div',[

    createElement('child',{

    scopedSlots:{

    default:function(props){

    return createElement('span',props.text)

    }

    }

    })

    ])

    }

    jsx

    如果你写了很多render函数,可能会觉得痛苦:

    createElement(
    'anchored-heading', {
    props: {
    level: 1
    }
    }, [
    createElement('span', 'Hello'),
    ' world!'
    ]
    )
    特别是模板如此简单的情况下:
    <anchored-heading :level="1">
    <span>Hello</span> world!
    </anchored-heading>
    这就是为什么会有一个babel插件,用于在vue中使用jsx语法的原因,它可以让我们回到更接近于模板的语法上。
    import AnchoredHeading from './AnchoredHeading.vue'
     
    new Vue({
    el: '#demo',
    render (h) {
    return (
    <AnchoredHeading level={1}>
    <span>Hello</span> world!
    </AnchoredHeading>
    )
    }
    })
     
    将 h 作为 createElement 的别名是 Vue 生态系统中的一个通用惯例,实际上也是 JSX 所要求的,如果在作用域中 h 失去作用, 在应用中会触发报错。
     
    函数化组件
    之前创建的锚点标题组件是比较简单的,没有管理或者监听任何传递给它的状态,也没有生命周期方法。它只是一个接受参数的函数。
    在这个例子中,我们标记组件为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子节点的数组
    • slots:slots对象
    • data:传递给组件的data对象
    • parent:对父组件的引用
    • listeners:(2.3.0+)一个包含了组件上所注册的v-on侦听器的对象,这只是一个指向data.on的别名
    • injections:(2.3.0+)如果使用了inject选项,则该对象包含了应当被注入的属性

    在添加functional:true之后,锚点标题组件的render函数之间,简单更新增加context参数。

     
  • 相关阅读:
    第三次冲刺
    第二次冲刺
    第一次冲刺
    Beta版本的发布
    第七次冲刺
    SpringMVC+Spring+mybatis 项目实践
    JSP显示新闻
    一个简单的静态邮箱登录界面
    Java Web 基础
    总结
  • 原文地址:https://www.cnblogs.com/susanws/p/7424401.html
Copyright © 2020-2023  润新知