# 组件化开发3

# 插槽

# 插槽内容

Vue 事件了一套内容分发的 API,它允许你这样合成组件

<navigation-link url="/profile">Your Profile</navigation-link>
<a v-bind:href="url" class="nav-link">
  <slot></slot>
</a>

等价于

<a v-bind:href="url" class="nav-link">Your Profile</a>

# 编译作用域

作为一条规则,请记住:

父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。

# 后备内容

为了将“submit”作为所谓后备(默认)内容,我们可以将它放在<slot>标签内,类似 HTML 标签的 alt 替代文本

<button type="submit">
  <slot>Submit</slot>
</button>

# 具名插槽

有时我们需要多个插槽,这种情况我们利用<slot>的一个特殊属性:name,例如

子组件

<!-- <base-layout> 组件 -->
<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>

而向具名插槽提供内容的时候,我们可以在一个<template>元素上使用 v-slot 指令,以 v-slot 的参数提供名称

<base-layout>
  <template v-slot:header>
    <h1>Here might be a page title</h1>
  </template>

  <p>A paragraph for the main content.</p>
  <p>And another one.</p>

  <template v-slot:footer>
    <p>Here's some contact info</p>
  </template>
</base-layout>

# 作用域插槽

为了让 user 在父级的插槽内容中可用,我们可以将 user 作为 <slot> 元素的一个 attribute 绑定上去:

<span>
  <slot v-bind:user="user">{{ user.lastName }}</slot>
</span>

绑定在 <slot> 元素上的 attribute 被称为插槽 prop。现在在父级作用域中,我们可以使用带值的 v-slot 来定义我们提供的插槽 prop 的名字:

<current-user>
  <template v-slot:default="slotProps">{{ slotProps.user.firstName }}</template>
</current-user>

# 解构插槽

<current-user v-slot="{ user }">{{ user.firstName }}</current-user>

# 具名插槽的缩写

比如v-slot:header可以被重写为#header

# 动态组件&异步组件

# 在动态组件上使用keep-alive

你会注意到,如果你选择了一篇文章,切换到 Archive 标签,然后再切换回 Posts,是不会继续展示你之前选择的文章的。这是因为你每次切换新标签的时候,Vue 都创建了一个新的 currentTabComponent 实例。

重新创建动态组件的行为通常是非常有用的,但是在这个案例中,我们更希望那些标签的组件实例能够被在它们第一次被创建的时候缓存下来。为了解决这个问题,我们可以用一个 <keep-alive> 元素将其动态组件包裹起来。

<!-- 失活的组件将会被缓存!-->
<keep-alive>
  <component v-bind:is="currentTabComponent"></component>
</keep-alive>

# 异步组件

在大型应用中我们可能需要将应用分割成小一些的代码块,并且只在需要的时候才从服务器加载一个模块,为了简化,Vue 允许你以一个工厂函数的方式定义你的组件,这个工厂函数会异步解析你的组件定义。Vue 只有在这个组件需要被渲染的时候才会触发工厂函数,且会把结果缓存起来供未来重渲染。

new Vue({
  // ...
  components: {
    "my-component": () => import("./my-async-component")
  }
});

# 访问元素

  1. 访问根实例

    在每个 new Vue 实例的子组件中,其根实例可以通过$root property 进行访问

  2. 访问父级组件实例

    $root类似,$parentproperty 可以 用来从一个子组件访问父组件的实例。

  3. 访问子组件实例

通过ref这个 attribute 为子组件赋予一个 ID 引用,使用时通过$refs来获取引用,这个引用是对包含了所有子组件的一个对象

<base-input ref="usernameInput"></base-input>
this.$refs.usernameInput;
  1. 依赖注入(祖先、后代传值)

provide选项允许我们指定想要提供给后代组件的数据和方法,然后在任何后代组件中,我们可以使用inject来接收,我们可以把依赖注入看作一个大范围有效的prop

provide: function () {
  return {
    getMap: this.getMap
  }
}
inject: ["getMap"];

# 组件递归

组件是可以在它们自己的模板中调用自身的。不过它们只能通过 name 选项来做这件事

此外注意请确保递归调用是条件性的,例如使用一个最终会得到 false 的 v-if

如果递归的组件不是一个全局组件,那么我们应该在父组件的生命周期钩子beforeCreate时去注册它

beforeCreate: function () {
  this.$options.components.TreeFolderContents = require('./tree-folder-contents.vue').default
}

或者,在本地注册组件的时候,你可以使用 webpack 的异步 import:

components: {
  TreeFolderContents: () => import("./tree-folder-contents.vue");
}