【笔记】自定义指令和render函数

2,728 阅读3分钟

自定义指令

directive

注册

全局注册

<div id="app">
  <input type="text" v-focus>
</div>
<script>
  //注册一个自定义指令
Vue.directive('focus',{
  //将被绑定的元素插入到DOM中
  inserted: function(el){
    el.focus()
  }
})
var app = new Vue({
  el: '#app',
})
</script>

局部注册

var app = new Vue({
  el: '#app',
  directives: {
    focus: {
      inserted: function(el){
        el.focus()
      }
    }
  }
})

钩子函数

  • inserted: 被绑定元素插入父节点
  • bind:
  • update:
  • componentUpdate:
  • unbind:

钩子函数参数

  • el:指令所绑定元素。
  • binding: 一个对象
    • name:指令名
    • value: 指令绑定的值
    • oldValue: 指令绑定的前一个值
    • expression: 字符串形式的指令表达式
    • arg:传给指令的参数
    • modifiers: 一个包含修饰符的对象
  • vnode:vue编译的虚拟节点
     <div id="app" v-demo:foo.a.b='message'>
    </div>
    <script>
        Vue.directive('demo',{
            bind: function(el,binding,vnode){
                var s = JSON.stringify
                el.innerHTML = 
                'name: '+ s(binding.name) + '<br>' +
                'value: ' + s(binding.value) + '<br>' +
                'expression: ' + s(binding.expression) + '<br>' +
                'arg: ' + s(binding.arg) + '<br>' +
                'modifiers: ' + s(binding.modifiers)
            }
        })
        var app = new Vue({
            el: '#app',
            data:{
                message: 'hello'
            }
        })
    </script>
结果:
name: "demo"
value: "hello"
expression: "message"
arg: "foo"
modifiers: {"a":true,"b":true}

render函数

不使用模板,使用render函数可以用js来构建DOM

default包括了所有没有在具名插槽中的节点或v-slot:default中的内容,向组件传递不带v-slot指令的子节点时,这些子节点被存储在组件实例中的$slots.default中

vm.$slots用来被访问插槽分发的内容,每个具名插槽有其相应的property

Vue通过建立一个虚拟DOM对真实DOM发生变化保存追踪,createElement包含的信息会告诉Vue页面上需要渲染什么样的节点,包括以及其子节点的信息。这样的节点即虚拟节点

      <div id="app">
        <h1>
            <a href="">
                hello world
            </a>
        </h1>
        <my-component :level='level'>
            hello Vue
        </my-component>
    </div>
    <script>
        Vue.component('my-component',{
            render: function(createElement){
      //createElement不是一个实际的DOM,
      //其包含的信息告诉vue页面上需要渲染什么样的节点,包括其子节点的描述信息,
      //它就是虚拟节点
                return createElement(
                    'h'+this.level,
                    this.$slots.default
      //通过$slots.default访问不带v-slot的子节点hello vue
                )
            },
            props: {
                level: {
                    type: Number,
                    required: true
                }
            }
        })
        var app = new Vue({
            el: '#app',
            data: {
                level: 2
            }
        })
    </script>

render函数第一个参数

createElement参数的类型必选 String | Object | Function

    <div id="app">
        <child></child>
    </div>
    <script>
        Vue.component('child',{
            render: function(createElement){
                // return createElement('h1','hello')

                // return createElement({
                //     template: '<div>hello vue</div>'
                // })

                var domFun = function(){
                    alert(typeof createElement)
                    return {
                        template: '<div>hello world</div>'
                    }
                }
                return createElement(domFun())
            }
        })
        var app = new Vue({
            el: '#app'
        })
    </script>

render函数的第二个参数

第二个参数是数据对象,只能是Object,常用属性 class|style|domProps|attrs|on

    <div id="app">
        <child></child>
    </div>
    <script>
        Vue.component('child',{
            render: function(createElement){
                return createElement({
                    template: '<a>hello world</a>'
                },{
                   style: {
                       color: 'red',
                       fontSize: '20px',
                       height: '30px',
                       backgroundColor: 'yellow'
                    },
                    attrs: {
                        id: 'foo',
                        href: 'http://baidu.com'
                    },
                    class: {
                        foo: true,
                        baz: false
                    },
      //domProps用来写原生DOM属性
                    domProps: {
                        innerHTML: '<span style="color: blue;">hello vue</span>'
                    }
                })
            }
        })
        var app = new Vue({
            el: '#app'
        })
    </script>

render函数的第三个参数

第三个参数 String | Array构建子节点

    <div id="app">
        <child></child>
    </div>
    <script>
        Vue.component('child',{
            render: function(createElement){
                return createElement('div',[
      //这里是数组,是第三个参数
                    createElement('h1','hello'),
                    createElement('h5','world')
                ])
            }
        })
        var app = new Vue({
            el: '#app'
        })
    </script>

this.slots在render函数中的应用

第三个参数存的就是VNODE createElement('header',header),返回的就是VNODE var header = this.$slots.header

    <div id="app">
       <child>
        <p slot="header">hello</p>
        <p slot='footer'>world</p>
       </child>
    </div>
    <script>
        Vue.component('child',{
            render: function(createElement){
                var header = this.$slots.header
                var footer = this.$slots.footer
                return createElement('div',[
                    createElement('header',header),
                    createElement('footer',footer)
                ])
            }
        })
        var app = new Vue({
            el: '#app'
        })
    </script>

通过props在render函数中传递数据

    <div id="app">
        <child :show='show'></child>
        <br>
        <button @click='click'>点击我</button>
    </div>
    <script>
        Vue.component('child',{
            props: ['show'],
            render: function(createElement){
                var imgsrc;
                if(this.show){
                    imgsrc = "https://lh3.googleusercontent.com/proxy/DwkPRPpr6gfUGGTEN6_alb8l9m0F0FLjG3BJUfKyF3Pt-MgD_fASJzayTPjbH4bDHVrK_pwtqWbFLCkYSHyeQyxbwjo7zU_-v5N6kZa_BpJVDNwbYV3zMWBFaBE7jMOivxBWaj46NKUjT0sk0-rpg4nt0z0U0Pb7XS3bzGUQqg4mKrBpuw" 
                }else {
                    imgsrc ='https://static.runoob.com/images/demo/demo2.jpg'
                }
                return createElement('img',{
                    attrs: {
                        src: imgsrc
                    },
                    style: {
                        width: '100px',
                        height: '100px'
                    }
                })
            }
        })
        var app = new Vue({
            el: '#app',
            data: {
                show: true
            },
            methods: {
                click: function(){
                    this.show = !this.show
                }
            }
        })
    </script>

v-model在render函数中使用

渲染函数中没有与 v-model 的直接对应——你必须自己实现相应的逻辑

    <div id="app">
        <my-component  v-model='name'></my-component>
        {{name}}
    </div>
    <script>
        Vue.component('my-component',{
            render: function(createElement){
                var self = this
                return createElement('input',{
                    domProps: {
                        value: this.name
                    },
                    on: {
                        input: function(event){
                            // 这里的this指向的是window
                            // self指向的是vue实例
                            self.$emit('input',event.target.value)
                        }
                    }
                })
            },
            props: ['name']
        })
        var app = new Vue({
            el: '#app',
            data: {
                name: 'Jane'
            }
        })
    </script>

作用域插槽在render函数中的作用

    <div id="app">
        <my-component>
            <template v-slot='prop'>
                {{prop.text}}
            </template>
        </my-component>
    </div>
    <script>
        Vue.component('my-component',{
            render: function(createElement){
                return createElement('div',this.$scopedSlots.default({
                    text: 'hello world'
                }))
            }
        })
        var app = new Vue({
            el: '#app',
            data:{
            }
        })
    </script>

vm.$scopedSlots

用来访问作用域插槽

this.$scopedSlots.default

render函数组件化

    <div id="app">
        <my-component value='haha' :msg='msg'>
            <template>
                <slot>hello</slot>
            </template>
        </my-component>
    </div>
    <script>
        Vue.component('my-component',{
            functional: true,
            //表示当前的实例无状态,没有this和data
            //context为上下文对象,通过它可以拿到数据
            render: function(createElement,context){
                return createElement('button',{
                    on: {
                        click: function(){
                            console.log(context)
                            // this.text= context.props.text
                            console.log(context.parent)
                            console.log(context.props.value)
                            console.log(context.props.msg)
                            // this.$slots.default = context.children
                            console.log(context.children)
                            console.log(this.value)
      //undefined ,functional:true后无法拿到外界的数据
                        }
                    }
                },'click')
            },
            props: ['value','msg']
        })
        var app = new Vue({
            el: '#app',
            data:{
                name: 'hello',
                msg: 'world'
            }
        })
    </script>