8. 函数式组件和自定义块——.vue文件

376 阅读1分钟

1. 函数式组件

我们可以把函数式组件想像成组件里的一个函数,入参是渲染上下文(render context),返回值是渲染好的HTML

函数式组件拥有两个特性:

  • Stateless(无状态):组件自身是没有状态的
  • Instanceless(无实例):组件自身没有实例,也就是没有this
Vue.component('my-component', {
  functional: true,
  // Props 是可选的
  props: {
    // ...
  },
  // 为了弥补缺少的实例
  // 提供第二个参数作为上下文
  render: function (createElement, context) {
    // ...
  }
})

<template functional>
  <button
    class="btn btn-primary"
    v-bind="data.attrs"
    v-on="listeners"
  >
    <slot/>
  </button>
</template>

在一个 *.vue 文件中以单文件形式定义的函数式组件,现在对于模板编译、scoped CSS 和热重载也有了良好的支持。

要声明一个应该编译为函数式组件的模板,请将 functional 特性添加到模板块中。这样做以后就可以省略 <script> 块中的 functional 选项。

模板中的表达式会在函数式渲染上下文中求值。这意味着在模板中,prop 需要以 props.xxx 的形式访问:

<template functional>
  <div>{{ props.foo }}</div>
</template>

你可以在 parent 上访问 Vue.prototype 全局定义的属性:

<template functional>
  <div>{{ parent.$someProperty }}</div>
</template>

2. 自定义块——.vue文件

在 .vue 文件中,可以自定义语言块。应用于一个自定义块的 loader 是基于这个块的 lang 特性、块的标签名以及 webpack 配置进行匹配的。

如果指定了一个 lang 特性,则这个自定义块将会作为一个带有该 lang 扩展名的文件进行匹配。

你也可以使用 resourceQuery 来为一个没有 lang 的自定义块匹配一条规则。例如为了匹配自定义块 <foo>

{
  module: {
    rules: [
      {
        resourceQuery: /blockType=foo/,
        loader: 'loader-to-use'
      }
    ]
  }
}

如果找到了一个自定义块的匹配规则,它将会被处理,否则该自定义块会被默默忽略。

此外,如果这个自定义块被所有匹配的 loader 处理之后导出一个函数作为最终结果,则这个 *.vue 文件的组件会作为一个参数被这个函数调用。

3. 自定义块的例子

这里有一个向组件内注入 <docs> 自定义块的示例,且它是在运行时可用的。

为了注入自定义块的内容,撰写一个自定义 loader:

module.exports = function (source, map) {
  this.callback(
    null,
    `export default function (Component) {
      Component.options.__docs = ${
        JSON.stringify(source)
      }
    }`,
    map
  )
}

现在我们将会配置 webpack 来使用为 <docs> 自定义块撰写的自定义 loader。

// wepback.config.js
module.exports = {
  module: {
    rules: [
      {
        resourceQuery: /blockType=docs/,
        loader: require.resolve('./docs-loader.js')
      }
    ]
  }
}

现在可以在运行时访问被导入组件的 <docs> 块内容了。

<!-- ComponentB.vue -->
<template>
  <div>Hello</div>
</template>

<docs>
This is the documentation for component B.
</docs>
<!-- ComponentA.vue -->
<template>
  <div>
    <ComponentB/>
    <p>{{ docs }}</p>
  </div>
</template>

<script>
import ComponentB from './ComponentB.vue';

export default {
  components: { ComponentB },
  data () {
    return {
      docs: ComponentB.__docs
    }
  }
}
</script>