vue单页面引入CDN链接

不想在index.html文件中全局引入CDN资源,那么如何在Vue单文件组件中引入?下面来瞅瞅~

虚拟DOM创建

Vue 通过创建一个虚拟 DOM 来追踪自己要改变的真实 DOM

  • 什么是虚拟DOM?
return createElement('h1', this.blogTitle)

createElement实际返回的是createNodeDescription而非实际上的DOM元素,因为它所包含的信息会告诉Vue页面上需要渲染什么样的节点,包括及其子节点的描述信息。这样的节点称为“虚拟节点 (virtual node)”,也常简写为“VNode”。“虚拟 DOM”是整个 VNode 树(由 Vue 组件树建立起来)的称呼。

  • createElement函数参数

createElement函数中可以使用模板中的功能,它接收的参数有:

// @returns {VNode}
createElement(
  // {String | Object | Function}
  // 一个 HTML 标签名、组件选项对象,或者
  // resolve 了上述任何一种的一个 async 函数。必填项。
  'div',

  // {Object}
  // 一个与模板中属性对应的数据对象。可选。
  {
      // 与 `v-bind:class` 的 API 相同,
      // 接受一个字符串、对象或字符串和对象组成的数组
      'class': {
        foo: true,
        bar: false
      },
      // 与 `v-bind:style` 的 API 相同,
      // 接受一个字符串、对象,或对象组成的数组
      style: {
        color: 'red',
        fontSize: '14px'
      },
      // 普通的 HTML 特性
      attrs: {
        id: 'foo'
      },
      // 组件 prop
      props: {
        myProp: 'bar'
      },
      // DOM 属性
      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',
      // 其它特殊顶层属性
      key: 'myKey',
      ref: 'myRef',
      // 如果你在渲染函数中给多个元素都应用了相同的 ref 名,
      // 那么 `$refs.myRef` 会变成一个数组。
      refInFor: true
},

  // {String | Array}
  // 子级虚拟节点 (VNodes),由 `createElement()` 构建而成,
  // 也可以使用字符串来生成“文本虚拟节点”。可选。
  [
    '先写一些文字',
    createElement('h1', '一则头条'),
    createElement(MyComponent, {
      props: {
        someProp: 'foobar'
      }
    })
  ]
)

渲染函数render

Vue 推荐在绝大多数情况下使用模板来创建HTML,然而在一些场景中,你需要发挥 JavaScript 最大的编程能力,这时可以用渲染函数,它比模板更接近编译器。

  • 类型

(createElement: () => VNode) => VNode

渲染函数接收一个createElement方法作为第一个参数来创建VNode;如果组件是一个函数组件,渲染函数还会接收一个额外的 context 参数,为没有实例的函数组件提供上下文信息。

例子:假设我们要生成一些带锚点的标题

<h1>
  <a name="hello-world" href="#hello-world">
    Hello world!
  </a>
</h1>
  • 模板实现
<anchored-heading :level="1">Hello world!</anchored-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
    }
  }
})

这里用模板并不是最好的选择:不但代码冗长,而且在每一个级别的标题中重复书写了<slot></slot>,在要插入锚点元素时还要再次重复。

  • render实现
<anchored-heading :level="1">Hello world!</anchored-heading>
Vue.component('anchored-heading', {
  render: function (createElement) {
    return createElement(
      'h' + this.level,   // 标签名称
      this.$slots.default // 子节点数组
    )
  },
  props: {
    level: {
      type: Number,
      required: true
    }
  }
})

这样代码就精简很多,需要注意的是向组件中传递不带v-slot指令的子节点时,比如anchored-heading中的 Hello world!,这些子节点被存储在组件实例中的$slots.default中。

Vue单页面引入CDN链接

在了解了render函数createElement函数的基础上,想要实现Vue单页面引入CDN链接就简单很多了。

  • 首先采用createElement创建不同资源类型(以js、css为例)的VNode
// js CDN
createElement('script', {
    attrs: {
        type: 'text/javascript',
        src: this.cdn
    }
})
// css CDN
createElement('link', {
    attrs: {
        rel: 'stylesheet',
        type: 'text/css',
        href: this.cdn
    }
})
  • 然后基于上述VNode,采用render创建函数式组件remote-jsremote-css
components: {
    'remote-js': {
        render(createElement) {
            return createElement('script', {
                attrs: {
                    type: 'text/javascript',
                    src: this.cdn
                }
            })
        },
        props: {
            cdn:  {
                type: String,
                required: true
            }
        }
    },
    'remote-css': {
        render(createElement) {
            return createElement('link', {
                attrs: {
                    rel: 'stylesheet',
                    type: 'text/css',
                    href: this.cdn
                }
            })
        },
        props: {
            cdn:  {
                type: String,
                required: true
            }
        }
    }
}
  • Vue单页面引入
<template>
    <div class="my-page">
        <remote-js cdn="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></remote-js>
        <remote-css cdn="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/css/bootstrap.min.css"></remote-css>
    </div>
</template>
  • 彩蛋(=。=)

如果你觉得render函数写起来很费劲的话,就可以利用Bable插件,在Vue 中使用 JSX ,让我们可以无限接近于模板语法。上述代码就变成下面这样了,好像是顺眼了一丢丢吧:

components: {
    'remote-js': {
        render(h) {
            return (
                <script type= 'text/javascript' src={this.cdn}></script>
            )
        },
        props: {
            cdn:  {
                type: String,
                required: true
            }
        }
    },
    'remote-css': {
        render(h) {
            return (
                <link rel='stylesheet' type='text/css' href={this.cdn} />
            )
        },
        props: {
            cdn:  {
                type: String,
                required: true
            }
        }
    }
}

Ps:将 h 作为 createElement 的别名是 Vue 生态系统中的一个通用惯例,实际上也是 JSX 所要求的。

最终效果就是只有访问当前页(my-page)时,CDN资源才会加载,不会像以前放在index.htmlmain.js中那样全局加载。

参考资料

1、render:https://cn.vuejs.org/v2/guide/render-function.html
2、createElement:https://cn.vuejs.org/v2/guide/render-function.html#createElement-%E5%8F%82%E6%95%B0
3、如何在VUE单页面引入CSS、JS(CDN链接):https://blog.csdn.net/kielin/article/details/86649074

posted @ 2019-12-07 16:05  Dreamsqin  阅读(13281)  评论(0编辑  收藏  举报