虚拟DOM

浏览器在解析HTML文档的时候,会将文档中的元素、注释和文本等标记按照它们的层级关系组织成一课数,这就是我们所熟知的DOM树

那么虚拟DOM是什么呢?虚拟DOM是使用普通的JavaScript 对象来描述的 ==DOM ==元素,在Vue 中,每一个虚拟节点都是一 VNode 的实例。而Vue.js 之所以执行性能高,就是因为采用了 虚拟 DOM机制

虚拟DOM对象就是普通的JavaScript对象,访问JavaScript对象要比访问真实的DOM 要快的多,Vue 在更新真实的 DOM 前,会比较更新前后 虚拟DOM 结构中有差异的部分,然后采用异步更新队列的方式将差异部分更新到真实的 DOM

真实的 DOM 结构

<div id="app">
    <h1>hello,vue</h1>
</div>

Vue.js的虚拟 DOM 创建的JavaScript对象:

    var vNode = {
        tag:'div',
        data:{
            attrs:{
                id:'app'
            }
        },
        children:{
            //h1节点
        }
    }

render函数

在 Vue 实例中,我们大多数使用模板来构建HTML,但是有些使用我们需要使用JavaScript进行编程,那么我们可以使用render()函数。render()函数的返回值一定是createElement()方法,8就而 createElement()方法用于创建一个虚拟节点 (virtual node)

createElement有三个参数,第一个参数是必须的,其参数类型可以是字符串(HTML标签名),对象(数组选项对象)和函数对象(解析前两者之一的 async 函数)。详情使用方法,和参数详解如下:

render(createElement) {
                return createElement("h1", {
                    // 与 `v-bind:class` 的 API 相同,
                    // 接受一个字符串、对象或字符串和对象组成的数组
                    'class': {
                        foo: true,
                        bar: false
                    },
                    // 与 `v-bind:style` 的 API 相同,
                    // 接受一个字符串、对象,或对象组成的数组
                    style: {
                        color: 'red',
                        fontSize: '14px'
                    },
                    // 普通的 HTML attribute
                    attrs: {
                        id: 'foo'
                    },
                    // 组件 prop
                    props: {
                        myProp: 'bar'
                    },
                    // DOM property
                    domProps: {
                        innerHTML: 'baz'
                    },
                    // 事件监听器在 `on` 内,
                    // 但不再支持如 `v-on:keyup.enter` 这样的修饰器。
                    // 需要在处理函数中手动检查 keyCode。
                    on: {
                        click: this.clickHandler
                    },
                    // 仅用于组件,用于监听原生事件,而不是组件内部使用
                    // `vm.$emit` 触发的事件。
                    nativeOn: {
                        click: this.nativeClickHandler
                    },
                    // 自定义指令。注意,你无法对 `binding` 中的 `oldValue`
                    // 赋值,因为 Vue 已经自动为你进行了同步。
                    directives: [{
                        name: 'my-custom-directive',
                        value: '2',
                        expression: '1 + 1',
                        arg: 'foo',
                        modifiers: {
                            bar: true
                        }
                    }],
                    // 作用域插槽的格式为
                    // { name: props => VNode | Array<VNode> }
                    scopedSlots: {
                        default: props => createElement('span', props.text)
                    },
                    // 如果组件是其它组件的子组件,需为插槽指定名称
                    slot: 'name-of-slot',
                    // 其它特殊顶层 property
                    key: 'myKey',
                    ref: 'myRef',
                    // 如果你在渲染函数中给多个元素都应用了相同的 ref 名,
                    // 那么 `$refs.myRef` 会变成一个数组。
                    refInFor: true
                },{//子节点对象});
            }

简单来说 ,createElement函数的第一个参数是要创建的元素节点的名字或者组件选项;第二个参数是元素的属性集合(包括普通属性、porp、事件属性、自定义指令等);第三个参数是子节点的信息,以数组形式输出,如果该元素只有文本子节点,那么直接以字符串形式给出,如果还有其他子元素,则继续调用 createElement 函数

createElement函数并不是 DOM API 中的document.createElement()方法,该函数是 Vue.js 中的函数,用来返回描述节点信息及其子节点信息的一个对象,即虚拟节点。

组件树中的所有 VNode 必须是唯一的,如下所示案例,这种就是不合法的

render: function (createElement) {
  var myParagraphVNode = createElement('p', 'hi')
  return createElement('div', [
    // 错误 - 重复的 VNode
    myParagraphVNode, myParagraphVNode
  ])
}

如果想要重复很多次的元素/组件,可以使用工厂函数来实现

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

JavaScript 代替模板功能

在之前使用模板构建 HTML 的时候,我们可以使用指令。而通过 render 函数创建模板时,就无法提供这些指令,所以我们只能自己编写JavaScript 来实现。

v-if 和 v-for
<ul v-if="items.length">
  <li v-for="item in items">{{ item.name }}</li>
</ul>

想要通过 render 函数完成上述 HTML页面的构建,过程如下所示:

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.')
  }
}

判断参数 item 的长度,如果长度为 0 ,则返回一个 p 标签,如果长度不为 0 ,则返回一个 ul 标签,并且循环创建 li 子节点

v-model
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)
      }
    }
  })
}

v-model 的本质是把 value的值作为prop,同时监听 input 事件,而上述代码就是按照 v-model的逻辑实现的。

Logo

前往低代码交流专区

更多推荐