专栏首页Super 前端Vue基础:组件--slot、异步组件、递归组件及其他

Vue基础:组件--slot、异步组件、递归组件及其他

slot分发内容

为了让组件可以组合,我们需要一种方式来混合父组件的内容与子组件自己的模板。这个过程被称为内容分发。Vue中使用特殊的 <slot> 元素作为原始内容的插槽。

问题(编译作用域)

message 应该绑定到父组件的数据,还是绑定到子组件的数据?

<child-component>
  {{ message }}
</child-component>

答案是父组件。父组件模板的内容在父组件作用域内编译;子组件模板的内容在子组件作用域内编译。

单个slot

除非子组件模板包含至少一个 <slot> 插口,否则父组件的内容将会被丢弃。当子组件模板只有一个没有属性的 slot 时,父组件整个内容片段将插入到 slot 所在的 DOM 位置,并替换掉 slot 标签本身。在 <slot> 标签中的任何内容都被视为备用内容。备用内容在子组件的作用域内编译,并且只有在宿主元素为空,且没有要插入的内容时才显示备用内容。

具名slot

<slot> 元素可以用一个特殊的属性 name 来配置如何分发内容。多个 slot 可以有不同的名字。具名 slot 将匹配内容片段中有对应 slot 特性的元素。仍然可以有一个匿名 slot,它是默认 slot,作为找不到匹配的内容片段的备用插槽。如果没有默认的 slot,这些找不到匹配的内容片段将被抛弃。

<div id="app">
  <my-component>
    <template>
      <p>父组件分发内容</p>
    </template>
  </my-component>
  <my-component2>
    <b>内容a</b>
    <template slot="title">
      父组件分发的title
    </template>
    <b>内容b</b>
  </my-component2>
</div>

<script>
const app = new Vue({
  el: '#app',
  components: {
    'my-component': {
      template: `<div><slot><p>备选内容</p></slot></div>`
    },
    'my-component2': {
        template: `<div><slot name="title">备选title</slot><slot><b>备选内容</b></slot></div>`
    }
  }
});
</script>

渲染结果:

<div>
  <p>父组件分发内容</p>
</div> 
<div>
  父组件分发的title
  <b>内容a</b>  
  <b>内容b</b>
</div>

完整示例参考地址:https://jsfiddle.net/381510688/tugxd14s/

作用域插槽

在子组件插槽中可以通过slot插槽标签的属性将数据传递到父组件要分发的内容当中,父组件要通过<template scope=""></template>模板来接收子组件插槽传递上来的数据。

<my-component>
  <template scope="props">
    <p>父组件分发内容</p>
    <p>{{props.message}}</p>
  </template>
</my-component>

<script>
const app = new Vue({
    el: '#app',
    components: {
    'my-component': {
        template: `<div><slot message="哈哈"></slot></div>`
    }
  }
});
</script>

渲染结果:

<div>
  <p>父组件分发内容</p> 
  <p>哈哈</p>
</div>

具代表性的列表组:

<my-list :list="['a.com', 'b.com']">
  <template slot="item" scope="props">
    <li class="link"><a>{{props.link}}</a></li>
  </template>
</my-list>

<script>
// ...
'my-list': {
  props: ['list'],
  template: `<ul><slot name="item" v-for="item in list" :link="item"></slot></ul>`
}
</script>

渲染结果:

<ul>
  <li class="link"><a>a.com</a></li>
  <li class="link"><a>b.com</a></li>
</ul>

完整示例参考地址:https://jsfiddle.net/381510688/j8qkbwbo/

动态组件

动态地绑定到它的 is 特性,让多个组件可以使用同一个挂载点,并动态切换:

<component v-bind:is="currentView">
  <!-- 组件在 vm.currentview 变化时改变! -->
</component>
var vm = new Vue({
  el: '#example',
  data: {
    currentView: 'home'
  },
  components: {
    home: { /* ... */ },
    posts: { /* ... */ },
    archive: { /* ... */ }
  }
})

keep-alive可以把切换出去的组件保留在内存中,保留它的状态,避免重新渲染。

<keep-alive>
  <component :is="currentView">
    <!-- 非活动组件将被缓存! -->
  </component>
</keep-alive>

杂项

编写可复用组件

可复用组件应当定义一个清晰的公开接口,同时也不要对其使用的外层数据作出任何假设。

  • Prop 允许外部环境传递数据给组件;
  • 事件允许从组件内触发外部环境的副作用;
  • 插槽允许外部环境将额外的内容组合在组件中。
<my-component
  :foo="baz"
  :bar="qux"
  @event-a="doThis"
  @event-b="doThat"
>
  <img slot="icon" src="...">
  <p slot="main-text">Hello!</p>
</my-component>

子组件引用

尽管有 prop 和事件,但是有时仍然需要在 JavaScript 中直接访问子组件。为此可以使用 ref 为子组件指定一个引用 ID。

<div id="parent">
  <user-profile ref="profile"></user-profile>
</div>
var parent = new Vue({ el: '#parent' })
// 访问子组件实例
var child = parent.$refs.profile

注意:$refs 只在组件渲染完成后才填充,并且它是非响应式的。它仅仅是一个直接操作子组件的应急方案——应当避免在模板或计算属性中使用 $refsref 被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例,可以通过$el,获取DOM元素。

异步组件

Vue.js 允许将组件定义为一个工厂函数,异步地解析组件的定义。

Vue.component('async-example', function (resolve, reject) {
  setTimeout(function () {
    // 将组件定义传入 resolve 回调函数
    resolve({
      template: '<div>I am async!</div>'
    })
  }, 1000)
})
Vue.component('async-webpack-example', function (resolve) {
  // 这个特殊的 require 语法告诉 webpack
  // 自动将编译后的代码分割成不同的块,
  // 这些块将通过 Ajax 请求自动下载。
  require(['./my-async-component'], resolve)
})

webpack 2 + ES2015 的语法时可以这样:

Vue.component(
  'async-webpack-example',
  () => import('./my-async-component')
)

组件命名约定

当注册组件 (或者 prop) 时,可以使用 kebab-case (短横线分隔命名)、camelCase (驼峰式命名) 或 PascalCase (单词首字母大写命名);在 HTML 模板中,请使用 kebab-case。

components: {
  myComponent: { /* ... */ }
}
<my-component/>

递归组件

一定要确保递归调用有终止条件,可以通过v-if进行控制。

递归组件示例:https://jsfiddle.net/381510688/nhhfL1bd/

组件间的循环引用

参考官网地址:https://cn.vuejs.org/v2/guide/components.html#组件间的循环引用

内联模板

如果子组件有 inline-template 特性,组件将把它的内容当作它的模板,而不是把它当作分发内容。这让模板编写起来更灵活。

<my-component inline-template>
  <div>
    <p>这些将作为组件自身的模板。</p>
    <p>而非父组件透传进来的内容。</p>
  </div>
</my-component>

但是 inline-template 让模板的作用域难以理解。使用 template 选项在组件内定义模板或者在 .vue 文件中使用 template 元素才是最佳实践。

X-Template

另一种定义模板的方式是在 JavaScript 标签里使用 text/x-template 类型,并且指定一个 id。例如:

<script type="text/x-template" id="hello-world-template">
  <p>Hello hello hello</p>
</script>
Vue.component('hello-world', {
  template: '#hello-world-template'
})

注意:在有很多大模板的演示应用或者特别小的应用中可能有用,其它场合应该避免使用,因为这将模板和组件的其它定义分离了。

对低开销的静态组件使用v-once

尽管在 Vue 中渲染 HTML 很快,不过当组件中包含大量静态内容时,可以考虑使用 v-once将渲染结果缓存起来,就像这样:

Vue.component('terms-of-service', {
  template: '\
    <div v-once>\
      <h1>Terms of Service</h1>\
      ...很多静态内容...\
    </div>\
  '
})

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Vue基础:组件--组件及组件通信

    组件可以扩展 HTML 元素,封装可重用的代码。在较高层面上,组件是自定义元素,Vue.js 的编译器为它添加特殊功能。在有些情况下,组件也可以是原生 HTML...

    奋飛
  • 现代前端技术解析:前端与协议

    完整的HTTP报文由头部、空行、正文三部分组成。目前最广泛使用的是HTTP1.1。

    奋飛
  • vue组件引用传值的最佳实践

    所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父...

    奋飛
  • Vue中的slot、slot-scope和v-slot

    插槽,也就是slot,是组件的一块HTML模板,这块模板显示不显示、以及怎样显示由父组件来决定。值得注意的是:内容要写在父组件中,然后分给需要的子组件,当slo...

    刘亦枫
  • 组件插槽

    星辰_大海
  • vue作用域插槽,你真的懂了吗?

    在网上搜了很多关于作用域插槽的解释,感觉没有写得很具体的吧,我认为应该对组件化有很深的理解才会触及到这个问题吧,这里也分享下我自己对于slot-scope的一点...

    Tusi
  • Vue插槽slot

    使用了那么久的vue,到现在才知道vue自带的组件插槽slot,说真的,插槽的作用在一些场景下非常有用。

    wade
  • 老雷PHP全栈开发教程之深入理解Vue组件

    Prop 是你可以在组件上注册的一些自定义特性。当一个值传递给一个 prop 特性的时候,它就变成了那个组件实例的一个属性。

    老雷PHP全栈开发
  • vue组件-template和slot插槽详细介绍

    vue项目,组件应用非常广泛,感觉只要能被多个地方复用的代码,都可以写成组件,这样不用做重复的工作。github上面也有很多大牛制造了n个轮子,方便大家使用。之...

    Javanx
  • 10.3【前端开发】背景图片:如何使用精灵图?

    https://www.toptal.com/developers/css/sprite-generator

    程序员LIYI

扫码关注云+社区

领取腾讯云代金券