Day12-slot插槽

元件的概念大概有了部分的了解,同一个元件可以重复利用,但这句话好像有个问题,不同页面使用相同架构的元件内容肯定需要做替换,这时候就是slot要出场啦!

元件编译作用域

编译作用域的意思就是「变数的作用范围」。

<h1>{{msg}}</h1>
<my-component>{{msg}}</my-component>
const app = Vue.createApp({
    data() {
        return {
            msg: 'Parent'
        }
    }
});
app.component('my-component', {
    template: `<h2>Hello!</h2>`,
    data() {
        return {
            msg: 'Child'
        }
    },
})

当内外层都有data时,画面会出现

Untitled

my-component内的msg不是出现Child而是template的内容,这表示元件在编译的时候会直接忽略内容物,直接放入template中的模板。

slot插槽

对子元件钻洞,让外层元件的资料可以放入。

app.component('my-component', {
    template: `
    <h2>Hello!
        <slot></slot>   
    </h2>`,
    data() {
        return {
            msg: 'Child'
        }
    },
})

将slot放入template中,可以得出这样的结果

Untitled

为什麽slot内容物不是Child???

slot特性是保留一个空间可以从外部传入内容,而子元件本身对slot并没有控制权。 ——重新认识 Vue.js

若想要给予子元件一个「预设内容」,可以直接在slot中放上预设内容。

<my-component></my-component>
app.component('my-component', {
    template: `
    <h2>Hello!
        <slot>预设内容</slot>   
    </h2>`,
    data() {
        return {
            msg: 'Child'
        }
    },
})

具名插槽

上面的范例是当只有一个内容物需要做变更的情况,当template内有很多需要换内容的时候就需要帮他们命名,让资料换到对应的位置。

  • v-slot:名称 : 会到指定的位置(相同slot名称)显示
  • 没有给名称 : 在模板上就会被放到没有slot名称的位置

Untitled

<light-box>
    <template v-slot:header>
        <h1>这是header</h1>
    </template>
    <div>
        没有给名字的slot
    </div>
</light-box>
const app = Vue.createApp({
    data() {
    }
});
app.component('light-box', {
    template: `
    <div class="lightbox">
    <div class="modal-mask" :style="modalStyle">
        <div class="modal-container" @click.self="toggleModal">
            <div class="modal-body">
                <header>
                <slot name="header">Default header</slot>
                </header>
                <hr>
                <main>
                    <slot>Default body</slot>
                </main>
                <hr>
                <footer>
                    <slot name="footer">Default footer</slot>
                </footer>
            </div>
        </div>
    </div>
    <button @click="isShow = true">Click</button>
</div>`,
    data: () => ({
        isShow: false
    }),
    computed: {
        modalStyle() {
            return {
                'display': this.isShow ? '' : 'none'
            }
        }
    },
    methods: {
        toggleModal() {
            console.log('click')
            this.isShow = !this.isShow
        }
    }
})

动态切换具名插槽

方法其实和昨天的is差不多,在这边的写法改为v-slot:[],可以自己选择位置放入更新内容。

Untitled

<label v-for="opt in options">
    <input type="radio" :value="opt" v-model="dynamic_slot_name">{{opt}}
</label>
<light-box>
    <template v-slot:[dynamic_slot_name]>
        <h1>更新後内容</h1>
    </template>
</light-box>
const app = Vue.createApp({
    data() {
        return {
            options: ['header', 'footer', 'default'],
            dynamic_slot_name: 'header'
        }
    }
});

Scoped Slots

slot资料都是由父层提供,若希望子元素的data也可以显示就可以用到以下方式

  1. :hello="helloString[lang]" 绑定子元素资料
  2. 插槽v-slot:default=""{hello}接收并吐值给下面的{{hello}}

Untitled

<p>
    请选择
    <select v-model="lang">
        <option v-for="n in langOptions" :value="n.val">
            {{n.name}}
        </option>
    </select>
</p>
<light-box :lang='lang'>
<!-- 写法一 -->
    <template v-slot:default="props">
        {{langOptions.find(d=>d.val===lang)['name']}}
        {{props.hello}}
    </template>
<!-- 写法二(解构) -->
	  <template v-slot:default="{hello}">
        {{langOptions.find(d=>d.val===lang)['name']}}
        {{hello}}
    </template>
</light-box>
const app = Vue.createApp({
    data: () => ({
        langOptions: [{
            name: '繁体中文',
            val: 'tw'
        }, {
            name: 'Deutsch',
            val: 'de'
        }, {
            name: 'English',
            val: 'en'
        }],
        lang: 'tw'
    })
});
app.component('light-box', {
    template: `
    <div class="lightbox">
    <div class="modal-mask" :style="modalStyle">
        <div class="modal-container" @click.self="toggleModal">
            <div class="modal-body">
                <slot name="default" :hello="helloString[lang]"></slot>
            </div>
        </div>
    </div>
    <button @click="isShow = true">Click</button>
</div>`,
    props: {
        lang: {
            type: String,
            default: 'tw'
        }
    },
    data: () => ({
        helloString: {
            'tw': '哈罗',
            'de': 'Hallo',
            'en': 'Hello'
        },
        isShow: false
    }),
    computed: {
        modalStyle() {
            return {
                'display': this.isShow ? '' : 'none'
            }
        }
    },
    methods: {
        toggleModal() {
            console.log('click')
            this.isShow = !this.isShow
        }
    }
})

这部分的内容有点复杂,所以再次改部分名称看看到底是怎麽传资料的。

  1. :hello="helloString[lang]"中的:hello传出值後只是个属性的名称,所以换成abc也可以
  2. 当要呼叫的时候{{prop.abc}}就可以看到值出现,或是用解构方式v-slot:default="{abc}"

Untitled

teleport

控制元件选染的位置,像是上面的范例,因为元件的父层不是body,所以灰底的遮罩盖不到整的页面,teleport to="body"就可以把她挂载到body

<div class="lightbox">
  <teleport to="body">
      <div class="modal-mask" :style="modalStyle">
          <div class="modal-container" @click.self="toggleModal">
              <div class="modal-body">
                  <slot name="default" :abc="helloString[lang]"></slot>
              </div>
          </div>
      </div>
  </teleport>
  <button @click="isShow = true">Click</button>
</div>

Untitled

参考资料

复用元件的好帮手:Vue Slots(v-slot、Scoped Slots)
https://medium.com/unalai/复用元件的好帮手-vue-slot-v-slot-scoped-slots-5364a0048ab7


<<:  部署 Kolla-Ansible 使用 External Ceph

>>:  day12: 模组化好的写法-为什麽要模组化

D22 - Grafana Monitor

前面用TiUP安装时,也已经将Grafana监控的部分一并安装完成。 在监控这部分大致上分为几个分类...

【Day 24】- 用方便的 Postman 储存或测试 API

前情提要 昨天带各位用 Selenium 写了自动发留言的 Discord 机器人,可以在指定的文字...

Day6-9. Palindrome Number

9. Palindrome Number(easy) Given an integer x, ret...

Flutter基础介绍与实作-Day23 旅游笔记的实作(4)

今天就接续来讲中部地区的制作吧! 资料夹建立 lib/scareens/food_Middle/fo...

Day 6: LeetCode 54. Spiral Matrix

Tag:随意刷-[50-100] LeetCode Problem Source: 54. Spir...