vue2和 Vue3 中的slot、scopedSlot v-slot v-slots ,在template和jsx中 的写法汇总梳理

4,015 阅读3分钟

2.6之前的用法 (template)

1静态插槽

子组件声明
<div class="container">  
<header>  
<slot name="header"></slot>  
</header>  
<main>  
<slot></slot>  
</main>  
<footer>  
<slot name="footer"></slot>  
</footer>  
</div>
父组件声明

<base-layout>  
<template 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 slot="footer">  
<p>Here's some contact info</p>  
</template>  
</base-layout>

或者 

<h1 slot="header">Here might be a page title</h1>

2作用域插槽

子组件声明
//customname 任意明明
<span>  
<slot name ="header" :customname="mydata">  
{{ user.lastName }}  
</slot>  
</span>

父组件声明

// usename 为任意命名,其中 ‘customname’ 为前面子组件声明的key

 <mycomponent>

        <template  slot="header" slot-scope="usename">

            <div>{{usename.customname}}</div>

        </template>

    </mycomponent>

2.6之后的用法 (template)

1静态插槽

子组件声明

同上 不变化

父组件声明

<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>

企业微信截图_16822156074764.png

2作用域插槽

子组件声明

同上 不变化

父组件声明

v-slot 也是需要写在template上


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


只有一种情况  是独占插槽的时候可以写在组件上


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




ps:额外补充下slot 和children区别

企业微信截图_1682225360903.png

jsx写法

3.0之前

1静态插槽

子组件声明
export default defineComponent({
    name: "Test",
    render() {
        return (
            <>
                <span>I'm Child</span>
                { this.$slots.default }
                { this.$slots.header }
            </>
        )
    }
})

父组件声明

上面是父组件的 作用域传值 会传递到子组件的 scopedSlots,下面是静态传值会传递到子组件的scopedSlots, 下面是静态传值 会传递到子组件的 slot和$scopedSlots

企业微信截图_1682225689608.png

1682228831601.png

2动态插槽

子组件声明

export default defineComponent({
    name: "Test",
    setup() {
        return {
            value: {
                name: 'xzw'
            }
        }
    },
    render() {
        return (
            <>
                <span>I'm Child</span>
                { this.$scopedSlots.content?.(this.value) }
            </>
        )
    }
})


父组件声明

<child
    scopedSlots={{
        default: props => {
            return (
                <div style="line-height: 30px;">
                    {props.info.title}
                </div>
            );
        },
        other: props => {
            return (
                <div style="line-height: 30px;">
                    {props.info.title}
                </div>
            );
        }
    }}
/>

3.0

1静态插槽

子组件声明
export default defineComponent({
    name: "Test",
    render() {
        return (
            <>
                <span>I'm Child</span>
                { this.$slots.default?.() }
                { this.$slots.header?.() }
            </>
        )
    }
})

或者  ,当 `enableObjectSlots` 不是 `false` 时,您可以使用对象插槽



const App = {
  setup() {
    return () => (
      <>
        <A>
          {{
            default: () => <div>A</div>,
            bar: () => <span>B</span>,
          }}
        </A>
        <B>{() => "foo"}</B>
      </>
    );
  },
};


父组件声明

企业微信截图_16822259394037.png

github.com/vuejs/babel…

import { defineComponent } from 'vue'
import TestComponent from './TestComponent'

export default defineComponent({
    name: "Test",
    components: {
        TestComponent
    },
    render() {
        return (
            <TestComponent v-slots={{
                default: () => (
                    <div>这是默认插槽</div>
                ),
                header: () => (
                    <div>这是header插槽</div>
                )
            }}>
            </TestComponent>
        )
    }
})

2动态插槽

子组件声明

同上

父组件声明

同上

pps 关于 slotsslots 和 scopedSlots 在3.0中统一

v3-migration.vuejs.org/zh/breaking…

案例分析

这个组件是对el-form的二次封装,

我们先约定三个名称

eltable 为 el-table组件(孙组件)

TargetTable 为 对eltable封装,也就是图中组件 (子组件)

homeTable 为 使用TargetTable的组件 (父组件)

1682229162859.png

30行的v-slot="scope" (相当于 scoped-slot="scope")表示动态作用域,给eltable传递模板,接受el-table返回的数据 "scope"

33行 表示接受外层组件 也就是 homeTable组件传递过来模板,并像外层组件 传递参数 scope

所以这个的作用 相当于 对原来接受slot的组件二次封装时,并能透传外层组件slot

其他文章: myblogbo.com/article/28.…

备份

指令

1. v-html ==>  `domPropsInnerHTML={xxxxx}`

2. v-on:click / @click ==> `on-click` = `{this.xxx()}` 无效则使用 `{() => this.xxx()}`

3. v-if ==>  `xxxx ? 为true的内容 : 为false的内容` (三目运算符)
   v-show ==> 在jsx中还是直接用 `v-show`

4. 指令修饰符,以`trim`为例: v-model_trim={this.userName}

5. v-for:
  <p v-for="(item, index) in content" :key="index" type="success">
    {{item.name}}
  </el-tag>

  {
    this.content.map((item, index) => {
      return (
        <p key={index} type="success">
          {item.name}
        </p>
      )
    })
  }

6. v-bind:xxx='value' ==> xxx={this.value}

动态绑定属性值

模板写法:
  <router-link :to="{path: '/user-center', query: { id: this.userInfo.id }}">
    个人中心
  </router-link>


JSX写法:
  <router-link
    to={{
      path: '/user-center',
      query: { id: this.userInfo.id }
    }}
  >
    个人中心
  </router-link>

绑定style与class

绑定动态class:
<div class={['left-info', item.createUserId === item.userId ? 'is-landlord' : '']}></div>

动态绑定style:
写法一:
<div class="thumbnail-wrapper" style={{ width: this.width + 'px' }}></div>

写法二:
<div class="zoom-wrapper" style={'width:' + this.width + 'px;height:' + this.width + 'px'}></div>

.sync 修饰符写法

模板写法:
<MyComponent :visible.sync="showDialog" />

JSX写法:
<MyComponent visible={ this.showDialog} {...{on: {'update:visible': (val) => { this.showDialog= val }}}} />

vue事件、事件修饰符与JSX原生事件

@submit.native.prevent ==> `nativeOnSubmit={event => event.preventDefault()}`

@change ==> `on-change={() => this.formatNumber()}`

@keyup.native ==> `nativeOnKeyup={() => {this.formatNumber()}}`

@mousewheel.native.prevent ==> `nativeOnMousewheel={event => event.preventDefault()}`

@keyup.native.enter="fetch()" ==> `nativeOn-keyup={e => e.keyCode === 13 && this.fetch()}`

绑定原生属性

使用 attrs 绑定原生属性
<input
  type="text"
  class="num-input"
  disabled
  value={props.row.quantity}
  attrs={{ onkeypress: 'return(/[\d]/.test(String.fromCharCode(event.keyCode)))' }}
  on-input={
    (event) => {
      this.customizeQuantity(props.$index, props.row.id, props.row.inventory, event);
    }
  }
/>

template 写法:
<input
  type="text"
  class="num-input"
  disabled
  :value="scope.row.quantity"
  onkeypress="return(/[\d]/.test(String.fromCharCode(event.keyCode)))"
  @input="this.customizeQuantity(props.$index, props.row.id, props.row.inventory, event)"
/>

绑定原生JavaScript事件

 

oninput="this.type = 'password'" ==> `nativeOnInput={(e) => {e.target.type = 'password'}}`

onkeypress ==> `nativeOnKeypress={event => (/[\d]/.test(String.fromCharCode(event.keyCode)))}`

作用域插槽,以el-table为例

模板写法:
<el-table-column>
  <template scope-slot="props">
    <el-button
      type="primary"
      size="mini"
      click="openDialog('edit',props.row)"
    >
      编辑
    </el-button>
  </template>
</el-table-column>


JSX写法:
<el-table-column
  key={index}
  label={item.name}
  width={item.width}
  {
  ...{
    scopedSlots: {
      default: props => {
        return (
          <el-button
            type="primary"
            size="mini"
            on-click={this.openDialog.bind(this,'edit',props.row)}
          >
            编辑
          </el-button>
        )
      }
    }
  }
  }
/>