Vue 组件开发实践之 scopedSlot 的传递

导语

现今的前端开发都讲究模块化组件化,即把公共的交互和功能封装到一个个的组件之中,在开发整体界面的时候就能像搭积木一样快速清晰高效。在使用Vue开发我们的vhtml-ui的组件库的过程中遇到了组件嵌套组件时需要传递scopedSlot的情况,官方的文档和教程目前还没有比较明确的指引,所以摸着石头过河,调通了想要的效果。记录下来方便大家和自己。

在Vue中,为了让组件可以组合,我们使用Slot来混合父组件的内容与子组件自己的模板。这样就实现了Vue的内容分发。

Scoped Slot(作用域插槽)是在Vue 2.1引入的更进阶的功能,它是一种特殊类型的slot,用作使用一个(能够传递数据到)可重用模板替换已渲染元素。我的理解就是使用scoped slot能在插槽里自定义模板并且使用组件传递过来的context。这大大提高了组件开发的灵活性。

Select组件一期

在开发我们的select组件时很自然就用上了scoped slot这一特性。我们需要遍历数据中的选项数组,渲染成界面上的下拉选项列表。如果是比较复杂的允许自定义的list item,在组件里写死dom结构就行不通了,比如:

有了scoped slot实现很轻松:


    <v-select kind="popup" :options="options4">
        <template slot="listItem" scope="props">
            <div>
                <div><span>{{ props.item.text }}<span> | <span>{{ props.item.value }}<span></div><div>{{ props.item.area }}</div>
                <div>{{ props.item.url }}</div>
            </div>
        </template>
    </v-select>

很好,非常好,现在有一个新需求:这个列表有的时候想要脱离select使用,比如就直接展示在页面上,不需要通过下拉弹出。

select-list组件

这好办啊,作为组件开发的老司机们自然能想到把这个list独立做成一个组件,页面可以直接调用,select组件也可以在它之上再封装一层。

完美!

开干!

select-list template结构示意:

    <ul class="v-select-list">
        <li class="v-select-list__item" v-for="(item, index) in options">
            <slot name="listItem" :item="item">
                <span>{{ item.text }}</span>
            </slot>
        </li>
    </ul>

select template结构示意:

    <div class="v-select">
        <v-popper>
            <v-select-list>
                <slot name="listItem">
                </slot>
            </v-select-list>
        </v-popper>
    </div>

然后问题来了,最里层的select-list组件并没有接收到用户自定义的scoped slot。通过查找Vue官方文档以及谷歌,也没有找到使用template方式传递scoped slot的介绍和例子。

Render函数和JSX

人总不能让尿给憋死,一条路走不通我们就看看有没有其他办法。在Vue的官方文档上有这么一句话:

“ Vue 推荐在绝大多数情况下使用 template 来创建你的 HTML。然而在一些场景中,你真的需要 JavaScript 的完全编程的能力,这就是 render 函数,它比 template 更接近编译器。”

查看文档,通过render函数确实能够传递scoped slot,以下图的方式

把scoped slot作为createElement方法的第二参数(data object)的一个属性传递到子组件中。

但是render函数的缺点就是不灵活,特别是在定义你的组件的dom结构模板的时候,如果写很多 render 函数,可能会觉得痛苦。它比较适用于外层组件仅仅是对内层组件的一次逻辑封装,而渲染的模板结构变化扩展不多的情况。

还好我们还有最后一把杀手锏--JSX。它可以让我们回到于更接近模板的语法上。具体关于JSX的使用不是本文的主题,我们可以阅读使用文档 ,学习关于 JSX 映射到 JavaScript的用法。

JSX实现上文的嵌套例子

通过参阅文档及不断地摸索,最终实现了自己想要的功能。我们直接上关键代码(select的render函数),看看有什么奥秘:

render(h) {
        let directives = [{
            name: 'popper',
            arg: 'selector',
            modifiers: { click: true }
        }];

        return (
            <div
                class={{ 'v-select': true, 'is-open': this.isPopperShown, 'is-disabled': this.disabled }}>
                <v-popper
                    ref="selector"
                    placement="bottom-start"
                    onVisibleChange={this.togglePopper}>
                    <v-select-list
                        class="v-select__options"
                        onChange={this.handleChange}
                        multiple={this.multiple}
                        value={this.currentValue}
                        onInput={this.handleListInput}
                        scopedSlots={{listItem: this.$scopedSlots.listItem}} >
                    </v-select-list>
                </v-popper>
                <div
                    class="v-select__header"
                    { ...{directives} }>
                    {
                        this.currentIndex !== -1 && this.$scopedSlots.headItem ? this.$scopedSlots.headItem(this.current)
                            : (<span>{this.currentText}</span>)
                    }
                    <v-icon
                        class="v-select__header-arrow"
                        name="v-arrow_dropdown">
                    </v-icon>
                </div>
            </div>
        )
    }

关键点:

  • 在子组件的标签上通过scopedSlots 属性可以向其传递自己的scoped slot;
  • 自身的scoped slot可以通过this.$scopedSlots 对象获取,默认就是default,具名slot就是它的名字。本例为“listItem”;
  • 如果不在标签上传递而是需要使用表达式传递,也可以通过 this.$scopedSlots对象。并且一个具体的scoped slot对象其实就是一个函数,其内部的scope可以在参数中传入。比如本例中的this.$scopedSlots.headItem(this.current)

JSX中对template常用点的转换

上面的介绍涵盖了基本的用法,但是我们在组件中往往会用一些不基本但常用的vue特性。我们接下来一起看看。

细心地小伙伴可能发现了上面的代码中已经出现了这些用法

directives

如果我们在组件中使用了directives,那么jsx里就不能想之前在template里那么自然的书写

v-popper:third.click

而是需要workaround:

  1. Pass everything as an object via value, e.g. v-name={{ value, modifier: true }}
  2. Use the raw vnode directive data format:

上面的例子中就是用了方法二。

v-model

render函数(JSX也是写在render函数中)中没有与v-model相应的api - 你必须自己来实现相应的逻辑。即通过value属性传递值,并通过绑定input事件来响应变化。

没有template 中的v-ifv-for:

这意味着我们需要在render函数或者JSX的表达式中手写if-else逻辑判断。或者如本例中使用三目表达式来实现。

这就是深入底层要付出的,尽管麻烦了一些,但你可以更灵活地控制。

希望这边文章能让我们在开发Vue组件的时候少走一些弯路,如果有大神有更好的办法或直接在template中实现传递scoped slot的功能,请多多指教!

原创声明,本文系作者授权云+社区-专栏发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

1 条评论
登录 后参与评论

相关文章

来自专栏腾讯社交用户体验设计

面向未来的 CSS Variable

622
来自专栏向治洪

React 介绍及实践教程

概述 React 是近期非常热门的一个前端开发框架,其本身作为 MVC 中的 View 层可以用来构建 UI,也可以以插件的形式应用到 Web 应用非 UI 部...

1819
来自专栏不止是前端

Vue:牛刀小试 Vue社区API

2706
来自专栏腾讯Bugly的专栏

微信小程序开发思考总结——腾讯“信用卡还款”项目实践

小程序概述 昨天晚上,微信团队对外宣布,微信小程序开放公测。开发者可登陆微信公众平台申请,开发完成后可以提交审核,公测期间咱不能发布。 我们前一段时间也进行了小...

3343
来自专栏知晓程序

开发 | 天天用小程序页面跳转接口,但你真的了解它们吗?

今天,知晓程序(微信号 zxcx0101)就来为你详细解析,小程序中有关页面跳转的 4 个接口,帮助你更有效地利用小程序的 5 层页面层级限制。

1403
来自专栏Python数据科学

手把手|50行Python代码教你爬取猫眼电影TOP100榜所有信息

对于Python初学者来说,爬虫技能是应该是最好入门,也是最能够有让自己有成就感的,今天,恋习Python的手把手系列,手把手教你入门Python爬虫,爬取猫眼...

662
来自专栏jeremy的技术点滴

前端ReactJS技术介绍

3143
来自专栏偏前端工程师的驿站

CSS魔法堂:一起玩透伪元素和Content属性

 继上篇《CSS魔法堂:稍稍深入伪类选择器》记录完伪类后,我自然而然要向伪元素伸出“魔掌”的啦^_^。本文讲讲述伪元素以及功能强大的Contet属性,让我们可以...

693
来自专栏HTML5学堂

CSS引入方式

HTML5学堂:CSS引入方式存在三种,分别为标签内联书写、页面头部书写、外部引入,那么这三者有何区别?本文给大家讲解了从不同的角度分析他们的优势与劣势。 1、...

2796
来自专栏影子

珍爱生命,远离JS=>JS避坑记

转载请注明源地址: http://www.cnblogs.com/funnyzpc/p/8407952.html

743

扫码关注云+社区