当前位置: 首页 > news >正文

Vue插槽(Slots)详解

文章目录

    • 1. 插槽简介
      • 1.1 什么是插槽?
      • 1.2 为什么需要插槽?
      • 1.3 插槽的基本语法
    • 2. 默认插槽
      • 2.1 什么是默认插槽?
      • 2.2 默认插槽语法
      • 2.3 插槽默认内容
      • 2.4 默认插槽实例:创建一个卡片组件
      • 2.5 Vue 3中的默认插槽
      • 2.6 默认插槽的应用场景
    • 3. 具名插槽
      • 3.1 什么是具名插槽?
      • 3.2 具名插槽语法
      • 3.3 具名插槽缩写
      • 3.4 默认插槽的显式名称
      • 3.5 具名插槽实例:页面布局组件
      • 3.6 Vue 3中的具名插槽
      • 3.7 具名插槽的应用场景
      • 3.8 动态插槽名
    • 4. 作用域插槽
      • 4.1 什么是作用域插槽?
      • 4.2 为什么需要作用域插槽?
      • 4.3 作用域插槽语法
      • 4.4 解构插槽Props
      • 4.5 作用域插槽实例:自定义列表渲染
      • 4.6 作用域插槽与具名插槽结合
      • 4.7 Vue 3中的作用域插槽
      • 4.8 作用域插槽的应用场景
    • 5. 插槽高级用法
      • 5.1 渲染函数中的插槽
      • 5.2 插槽包装器模式
      • 5.3 函数式组件中的插槽
      • 5.4 递归插槽
      • 5.5 透传插槽
      • 5.6 插槽与v-for结合
    • 6. 插槽最佳实践与常见问题
      • 6.1 插槽最佳实践
      • 6.2 常见问题与解决方案
      • 6.3 Vue 3中的插槽新特性
    • 7. 总结

1. 插槽简介

1.1 什么是插槽?

插槽(Slots)是Vue提供的一种内容分发机制,允许我们向组件内部传递内容。通俗地说,插槽就像是组件中的"占位符",你可以在使用组件时,在这个"占位符"中填充任何你想要显示的内容。

插槽使组件变得更加灵活和可复用,因为它允许使用者决定组件内部的部分内容。

1.2 为什么需要插槽?

想象一下,如果没有插槽,当我们需要创建一个按钮组件,可能会出现这样的情况:

// 没有插槽的按钮组件
Vue.component('my-button', {template: '<button class="btn">点击我</button>'
})

这个组件只能显示"点击我"这个文本。如果我们想要显示其他文本,就需要通过属性传递:

Vue.component('my-button', {props: ['text'],template: '<button class="btn">{{ text }}</button>'
})
<my-button text="保存"></my-button>
<my-button text="取消"></my-button>

但是,如果我们想要按钮内部显示复杂的HTML结构(如图标+文字),上面的方法就不够灵活了。这时插槽就派上用场了:

Vue.component('my-button', {template: '<button class="btn"><slot></slot></button>'
})
<my-button><i class="icon-save"></i> 保存
</my-button><my-button><i class="icon-cancel"></i> 取消
</my-button>

通过插槽,我们可以在不修改组件本身的情况下,灵活地决定组件内容。

1.3 插槽的基本语法

在Vue中,插槽使用<slot></slot>标签定义。一个简单的带插槽的组件示例如下:

Vue.component('alert-box', {template: `<div class="alert-box"><strong>重要提示!</strong><slot></slot></div>`
})

使用该组件时,我们可以在组件标签内添加内容,这些内容将替换组件模板中的<slot></slot>部分:

<alert-box>您的账号已被锁定,请联系管理员。
</alert-box>

渲染结果:

<div class="alert-box"><strong>重要提示!</strong>您的账号已被锁定,请联系管理员。
</div>

2. 默认插槽

2.1 什么是默认插槽?

默认插槽是最基本的插槽类型,就是我们上面看到的例子。它不需要名字,是组件中的默认内容分发位置。

2.2 默认插槽语法

使用<slot></slot>标签定义一个默认插槽:

Vue.component('my-component', {template: `<div><h2>组件标题</h2><slot></slot></div>`
})

使用该组件:

<my-component><p>这是一些默认插槽内容</p><p>可以有多个元素</p>
</my-component>

渲染结果:

<div><h2>组件标题</h2><p>这是一些默认插槽内容</p><p>可以有多个元素</p>
</div>

2.3 插槽默认内容

有时候,我们希望在使用者没有提供内容时,插槽能显示一些默认内容。这时,我们可以在<slot>标签内部添加内容作为默认值:

Vue.component('submit-button', {template: `<button type="submit"><slot>提交</slot></button>`
})

使用该组件:

<!-- 使用默认内容 -->
<submit-button></submit-button><!-- 替换默认内容 -->
<submit-button>保存</submit-button>

渲染结果:

<!-- 使用默认内容的渲染结果 -->
<button type="submit">提交</button><!-- 替换默认内容的渲染结果 -->
<button type="submit">保存</button>

2.4 默认插槽实例:创建一个卡片组件

让我们创建一个更实用的例子 - 一个卡片组件,它有标题和内容区域,其中内容区域使用默认插槽:

Vue.component('card', {props: ['title'],template: `<div class="card"><div class="card-header"><h3>{{ title }}</h3></div><div class="card-body"><slot>没有内容提供</slot></div></div>`
})

使用该组件:

<card title="欢迎"><p>感谢您访问我们的网站!</p><button>开始探索</button>
</card><card title="提示"></card>

渲染结果:

<div class="card"><div class="card-header"><h3>欢迎</h3></div><div class="card-body"><p>感谢您访问我们的网站!</p><button>开始探索</button></div>
</div><div class="card"><div class="card-header"><h3>提示</h3></div><div class="card-body">没有内容提供</div>
</div>

2.5 Vue 3中的默认插槽

在Vue 3中,默认插槽的使用方式与Vue 2基本相同,但是组件的定义方式可能不同:

// Vue 3使用setup语法
const Card = {props: ['title'],template: `<div class="card"><div class="card-header"><h3>{{ title }}</h3></div><div class="card-body"><slot>没有内容提供</slot></div></div>`
}

使用组合式API (Composition API):

import { defineComponent } from 'vue'export default defineComponent({props: ['title'],setup(props) {// 此处是组合式API的逻辑return () => (<div class="card"><div class="card-header"><h3>{props.title}</h3></div><div class="card-body">{/* JSX中的插槽表示 */}{this.$slots.default?.() || '没有内容提供'}</div></div>)}
})

2.6 默认插槽的应用场景

默认插槽非常适合以下场景:

  1. 包装组件 - 当你需要在某些内容周围添加一致的样式或结构
  2. 布局组件 - 例如容器、卡片或面板
  3. 功能性组件 - 如模态框、警告框,其中内容可变但行为一致

例如,一个简单的模态框组件:

Vue.component('modal-dialog', {props: ['isOpen'],template: `<div v-if="isOpen" class="modal-overlay"><div class="modal"><div class="modal-header"><button @click="$emit('close')" class="close-btn">&times;</button></div><div class="modal-body"><slot>这是模态框的默认内容</slot></div></div></div>`
})

使用该组件:

<modal-dialog :is-open="showModal" @close="showModal = false"><h2>重要通知</h2><p>您的订单已经成功提交!</p><button @click="showModal = false">确定</button>
</modal-dialog>

3. 具名插槽

3.1 什么是具名插槽?

当我们需要在组件中定义多个插槽时,就需要使用具名插槽。具名插槽允许我们将不同的内容分发到组件模板的不同位置。

例如,一个典型的页面布局组件可能需要头部、侧边栏、主内容区和底部等多个插槽。

3.2 具名插槽语法

在Vue 2.6之前,具名插槽使用slot属性:

Vue.component('layout', {template: `<div class="container"><header><slot name="header"></slot></header><main><slot></slot></main><footer><slot name="footer"></slot></footer></div>`
})
<layout><h1 slot="header">网站标题</h1><p>主内容区域</p><p slot="footer">版权信息 © 2023</p>
</layout>

在Vue 2.6及以后的版本中,引入了v-slot指令,替代了slot属性:

<layout><template v-slot:header><h1>网站标题</h1></template><p>主内容区域</p><template v-slot:footer><p>版权信息 © 2023</p></template>
</layout>

注意:在Vue 2.6以后,v-slot指令只能用在<template>标签上(除了一种特殊情况,后面会讲到)。

3.3 具名插槽缩写

v-slot指令可以缩写为#,这使得模板更加简洁:

<layout><template #header><h1>网站标题</h1></template><p>主内容区域</p><template #footer><p>版权信息 © 2023</p></template>
</layout>

3.4 默认插槽的显式名称

默认插槽其实有一个隐含的名称default。所以以下两种写法是等价的:

<!-- 隐式默认插槽 -->
<layout><p>主内容区域</p>
</layout><!-- 显式默认插槽 -->
<layout><template #default><p>主内容区域</p></template>
</layout>

3.5 具名插槽实例:页面布局组件

让我们创建一个更完整的页面布局组件:

Vue.component('page-layout', {template: `<div class="page"><header class="page-header"><slot name="header"><h1>默认标题</h1></slot></header><nav class="page-sidebar"><slot name="sidebar"><ul><li>默认导航项1</li><li>默认导航项2</li></ul></slot></nav><main class="page-content"><slot></slot></main><footer class="page-footer"><slot name="footer"><p>默认页脚内容</p></slot></footer></div>`
})

使用该组件:

<page-layout><template #header><div class="custom-header"><h1>我的应用</h1><p>欢迎访问!</p></div></template><template #sidebar><nav><a href="/home">首页</a><a href="/about">关于我们</a><a href="/contact">联系我们</a></nav></template><div><h2>主要内容</h2><p>这是页面的主要内容区域...</p></div><template #footer><div class="custom-footer"><p>© 2023 我的应用. 保留所有权利.</p><div class="social-links"><a href="#">Facebook</a><a href="#">Twitter</a></div></div></template>
</page-layout>

这个例子展示了如何创建一个灵活的页面布局组件,使用者可以自定义页面的各个部分。

3.6 Vue 3中的具名插槽

Vue 3中具名插槽的用法与Vue 2.6+基本一致,使用v-slot指令或其缩写#

<page-layout><template #header><h1>Vue 3 应用</h1></template><template #default><p>主内容区域</p></template><template #footer><p>Vue 3 页脚</p></template>
</page-layout>

在使用渲染函数或JSX时,Vue 3提供了更简洁的方式:

import { defineComponent } from 'vue'export default defineComponent({setup() {return () => (<div class="page"><header class="page-header">{this.$slots.header?.() || <h1>默认标题</h1>}</header><main class="page-content">{this.$slots.default?.()}</main><footer class="page-footer">{this.$slots.footer?.() || <p>默认页脚内容</p>}</footer></div>)}
})

3.7 具名插槽的应用场景

具名插槽特别适合以下场景:

  1. 复杂布局组件 - 网页布局、仪表盘等需要多个内容区域的组件
  2. 复合组件 - 如带有头部、内容和底部的卡片
  3. 对话框组件 - 需要自定义标题、内容和按钮区域
  4. 标签面板组件 - 自定义标签头和内容

例如,一个标签组件:

Vue.component('tabbed-panel', {template: `<div class="tabs"><div class="tab-headers"><slot name="tabs"></slot></div><div class="tab-content"><slot name="content"></slot></div></div>`
})

使用该组件:

<tabbed-panel><template #tabs><button class="tab" @click="activeTab = 'tab1'">用户信息</button><button class="tab" @click="activeTab = 'tab2'">订单历史</button></template><template #content><div v-if="activeTab === 'tab1'"><h3>用户信息</h3><p>用户详细信息显示在这里...</p></div><div v-else-if="activeTab === 'tab2'"><h3>订单历史</h3><p>用户订单历史显示在这里...</p></div></template>
</tabbed-panel>

3.8 动态插槽名

从Vue 2.6.0开始,我们可以使用动态插槽名,这在需要根据数据动态确定插槽位置时非常有用:

data() {return {dynamicSlot: 'header'}
}
<base-layout><template v-slot:[dynamicSlot]>动态插槽名称示例</template>
</base-layout>

使用缩写形式:

<base-layout><template #[dynamicSlot]>动态插槽名称示例</template>
</base-layout>

动态插槽名例子:标签切换

<div><div class="tab-buttons"><button v-for="tab in tabs" :key="tab.id"@click="currentTab = tab.id">{{ tab.name }}</button></div><tabbed-content><template #[currentTab]>当前显示的是:{{ currentTab }} 对应的内容</template></tabbed-content>
</div>
data() {return {currentTab: 'tab1',tabs: [{ id: 'tab1', name: '选项1' },{ id: 'tab2', name: '选项2' },{ id: 'tab3', name: '选项3' }]}
}

4. 作用域插槽

4.1 什么是作用域插槽?

作用域插槽(Scoped Slots)是Vue中最强大的插槽类型,它允许组件将自己内部的数据传递给插槽内容使用。这意味着插槽内容可以访问子组件中的数据,而不仅仅是父组件的数据。

通俗地说,普通插槽是把内容从父组件传给子组件,而作用域插槽则是让子组件决定要传什么数据给插槽内容使用。

4.2 为什么需要作用域插槽?

想象这样一个场景:你有一个展示用户列表的组件,但是你希望能够自定义每个用户的具体展示方式。

普通的做法可能是:

Vue.component('user-list', {props: ['users'],template: `<ul><li v-for="user in users" :key="user.id">{{ user.name }} - {{ user.email }}</li></ul>`
})

这个组件虽然展示了用户列表,但展示格式是固定的。如果我们想让使用者决定如何展示每个用户,就需要用到作用域插槽。

4.3 作用域插槽语法

在Vue 2.6之前,作用域插槽使用slot-scope属性:

Vue.component('user-list', {props: ['users'],template: `<ul><li v-for="user in users" :key="user.id"><slot :user="user"><!-- 默认内容 -->{{ user.name }}</slot></li></ul>`
})
<user-list :users="users"><template slot-scope="slotProps">{{ slotProps.user.name }} ({{ slotProps.user.email }})</template>
</user-list>

在Vue 2.6及以后版本中,使用v-slot指令替代了slot-scope属性:

<user-list :users="users"><template v-slot:default="slotProps">{{ slotProps.user.name }} ({{ slotProps.user.email }})</template>
</user-list>

缩写形式:

<user-list :users="users"><template #default="slotProps">{{ slotProps.user.name }} ({{ slotProps.user.email }})</template>
</user-list>

如果组件只有默认插槽,还可以直接在组件上使用v-slot

<user-list :users="users" v-slot="slotProps">{{ slotProps.user.name }} ({{ slotProps.user.email }})
</user-list>

4.4 解构插槽Props

作用域插槽的props可以使用解构赋值语法,使代码更简洁:

<user-list :users="users"><template #default="{ user }">{{ user.name }} ({{ user.email }})</template>
</user-list>

还可以指定默认值:

<user-list :users="users"><template #default="{ user = { name: '未知用户', email: '' } }">{{ user.name }} ({{ user.email }})</template>
</user-list>

4.5 作用域插槽实例:自定义列表渲染

让我们创建一个更完整的例子,一个可定制的列表组件:

Vue.component('fancy-list', {props: ['items', 'itemKey'],template: `<div class="fancy-list"><div v-if="items.length === 0" class="empty-message"><slot name="empty">没有数据可显示</slot></div><ul v-else><li v-for="(item, index) in items" :key="itemKey ? item[itemKey] : index" class="list-item"><slot name="item" :item="item" :index="index"><!-- 默认仅显示项目文本 -->{{ item.text || item }}</slot></li></ul></div>`
})

使用该组件:

<fancy-list :items="users" item-key="id"><!-- 自定义空状态 --><template #empty><div class="empty-state"><i class="icon icon-user"></i><p>暂无用户数据</p><button @click="loadUsers">加载用户</button></div></template><!-- 自定义每个项的渲染方式 --><template #item="{ item, index }"><div class="user-card"><img :src="item.avatar" :alt="item.name" class="avatar"><div class="user-details"><h3>{{ item.name }}</h3><p>{{ item.email }}</p><p class="user-index">用户 #{{ index + 1 }}</p></div><button @click="editUser(item)">编辑</button></div></template>
</fancy-list>

这个例子展示了如何使用作用域插槽创建一个高度可定制的列表组件。使用者可以自定义列表为空时的显示内容,以及每个列表项的显示方式。

4.6 作用域插槽与具名插槽结合

作用域插槽和具名插槽可以结合使用,创建更强大的组件:

Vue.component('data-table', {props: ['items', 'columns'],template: `<table><thead><tr><th v-for="column in columns" :key="column.id"><slot name="header" :column="column">{{ column.label }}</slot></th></tr></thead><tbody><tr v-for="item in items" :key="item.id"><td v-for="column in columns" :key="column.id"><slot :name="column.id" :item="item" :column="column">{{ item[column.id] }}</slot></td></tr></tbody></table>`
})

使用该组件:

<data-table :items="users" :columns="columns"><!-- 自定义表头 --><template #header="{ column }"><span class="column-title">{{ column.label }}</span><i v-if="column.sortable" class="sort-icon"></i></template><!-- 自定义名字列 --><template #name="{ item }"><a :href="`/users/${item.id}`">{{ item.name }}</a></template><!-- 自定义状态列 --><template #status="{ item }"><span :class="['status', item.status]">{{ formatStatus(item.status) }}</span></template><!-- 自定义操作列 --><template #actions="{ item }"><button @click="editUser(item)">编辑</button><button @click="deleteUser(item)">删除</button></template>
</data-table>
data() {return {users: [{ id: 1, name: '张三', email: 'zhangsan@example.com', status: 'active' },{ id: 2, name: '李四', email: 'lisi@example.com', status: 'inactive' }],columns: [{ id: 'name', label: '姓名', sortable: true },{ id: 'email', label: '邮箱' },{ id: 'status', label: '状态' },{ id: 'actions', label: '操作' }]}
},
methods: {formatStatus(status) {return status === 'active' ? '活跃' : '非活跃';},editUser(user) {// 编辑用户},deleteUser(user) {// 删除用户}
}

4.7 Vue 3中的作用域插槽

Vue 3中的作用域插槽语法与Vue 2.6+一致:

<fancy-list :items="users"><template #item="{ item }">{{ item.name }}</template>
</fancy-list>

在Vue 3的组合式API中,可以这样访问插槽:

import { defineComponent } from 'vue'export default defineComponent({props: ['items'],setup(props) {return () => (<div><ul>{props.items.map((item, index) => (<li key={item.id}>{this.$slots.item ? this.$slots.item({ item, index }) : item.text}</li>))}</ul></div>)}
})

4.8 作用域插槽的应用场景

作用域插槽特别适合以下场景:

  1. 列表组件 - 允许自定义每个列表项的渲染方式
  2. 表格组件 - 允许自定义表格单元格的显示
  3. 表单元素包装器 - 提供上下文信息(如验证状态)
  4. 无限滚动组件 - 允许自定义每个加载项
  5. 引导式教程组件 - 能够访问教程状态和控件

例如,一个表单字段包装器组件:

Vue.component('form-field', {props: ['value', 'label', 'errors'],template: `<div class="form-field" :class="{ 'has-error': hasError }"><label>{{ label }}</label><slot :value="value" :update="update" :hasError="hasError" :errors="errors"></slot><div v-if="hasError" class="error-message">{{ errors[0] }}</div></div>`,computed: {hasError() {return this.errors && this.errors.length > 0;}},methods: {update(newValue) {this.$emit('input', newValue);}}
})

使用该组件:

<form-field v-model="username" label="用户名" :errors="errors.username"><template #default="{ value, update, hasError }"><input type="text" :value="value" @input="update($event.target.value)":class="{ 'input-error': hasError }"></template>
</form-field><form-field v-model="gender" label="性别" :errors="errors.gender"><template #default="{ value, update }"><select :value="value" @change="update($event.target.value)"><option value="">请选择</option><option value="male"></option><option value="female"></option></select></template>
</form-field>

这个例子展示了如何使用作用域插槽创建灵活的表单字段包装器,它提供了标签、错误处理逻辑,同时允许使用者自定义实际的输入元素。

5. 插槽高级用法

5.1 渲染函数中的插槽

除了在模板中使用插槽,我们还可以在渲染函数(Render Functions)中使用插槽。这对于需要以编程方式处理插槽内容的场景非常有用。

在Vue 2中访问插槽:

Vue.component('example', {render(h) {// 访问默认插槽const defaultSlot = this.$slots.default;// 访问具名插槽const headerSlot = this.$slots.header;// 访问作用域插槽const contentSlot = this.$scopedSlots.content;return h('div', [// 具名插槽h('header', [headerSlot || h('h2', '默认标题')]),// 内容区域(作用域插槽)h('main', [contentSlot? contentSlot({ message: '来自子组件的数据' }): h('p', '默认内容')]),// 默认插槽h('footer', [defaultSlot || h('p', '默认页脚')])]);}
})

在Vue 3中,插槽访问方式有所变化:

import { h } from 'vue'export default {render() {// 在Vue 3中,所有插槽都在this.$slots中,并且都是函数const defaultSlotContent = this.$slots.default?.() || h('div', '默认内容');const headerSlotContent = this.$slots.header?.() || h('h2', '默认标题');// 作用域插槽也是函数,可以传入参数const contentSlotContent = this.$slots.content? this.$slots.content({ message: '来自子组件的数据' }): h('p', '默认内容');return h('div', [h('header', [headerSlotContent]),h('main', [contentSlotContent]),h('footer', [defaultSlotContent])]);}
}

5.2 插槽包装器模式

插槽包装器模式(Slot Wrapper Pattern)是一种设计模式,它允许组件通过插槽对其子组件进行增强或修改。

例如,创建一个为内容添加淡入动画的包装器组件:

Vue.component('fade-in', {props: {duration: {type: Number,default: 1000}},data() {return {visible: false};},mounted() {this.$nextTick(() => {this.visible = true;});},template: `<transition :duration="duration" name="fade"><div v-if="visible"><slot></slot></div></transition>`
})

使用该组件:

<fade-in :duration="2000"><div class="content"><h1>欢迎!</h1><p>这个内容会淡入显示</p></div>
</fade-in>

另一个例子是创建一个"悬停卡片"组件,当鼠标悬停在内容上时显示额外信息:

Vue.component('hover-card', {data() {return {isHovering: false};},template: `<div class="hover-container" @mouseenter="isHovering = true" @mouseleave="isHovering = false"><div class="hover-trigger"><slot name="trigger"></slot></div><div class="hover-content" v-if="isHovering"><slot name="content"><p>悬停内容</p></slot></div></div>`
})

使用该组件:

<hover-card><template #trigger><button>鼠标悬停在我上面</button></template><template #content><div class="tooltip"><h3>详细信息</h3><p>这是悬停时显示的额外信息</p></div></template>
</hover-card>

5.3 函数式组件中的插槽

函数式组件是一种特殊的组件,它没有状态和实例,因此性能更高。在Vue 2中,我们这样在函数式组件中使用插槽:

Vue.component('functional-component', {functional: true,render(h, context) {// 在Vue 2中,插槽通过context.slots()或context.scopedSlots访问const slots = context.slots();const scopedSlots = context.scopedSlots;return h('div', {class: 'wrapper'}, [slots.header || h('header', 'Default Header'),scopedSlots.content? scopedSlots.content({ message: 'Hello' }): h('p', 'Default Content'),slots.footer || h('footer', 'Default Footer')]);}
})

在Vue 3中,函数式组件是普通的函数,插槽的访问方式也有所不同:

import { h } from 'vue'const FunctionalComponent = (props, { slots }) => {return h('div', { class: 'wrapper' }, [slots.header?.() || h('header', 'Default Header'),slots.content? slots.content({ message: 'Hello' }): h('p', 'Default Content'),slots.footer?.() || h('footer', 'Default Footer')]);
}export default FunctionalComponent;

5.4 递归插槽

递归插槽是一种高级技术,它允许创建可以递归嵌套的组件,适用于表示树状结构(如目录树、评论嵌套等)。

例如,一个树形组件:

Vue.component('tree-item', {props: {item: Object},template: `<li><div class="tree-node"><slot name="node" :item="item">{{ item.name }}</slot></div><ul v-if="item.children && item.children.length"><tree-itemv-for="(child, index) in item.children":key="index":item="child"><template v-for="slotName in Object.keys($slots)" #[slotName]="slotProps"><slot :name="slotName" v-bind="slotProps"></slot></template></tree-item></ul></li>`
})

使用该组件:

<ul class="tree"><tree-item :item="treeData"><template #node="{ item }"><span class="node-content"><i :class="getIconClass(item)"></i>{{ item.name }}<span class="node-count" v-if="item.children">({{ item.children.length }})</span></span><button v-if="item.url" @click="openUrl(item.url)">打开</button></template></tree-item>
</ul>
data() {return {treeData: {name: '根目录',children: [{ name: '文档', children: [{ name: 'Vue教程.pdf', url: '/docs/vue.pdf' },{ name: 'React教程.pdf', url: '/docs/react.pdf' }]},{ name: '图片', children: [{ name: '头像.png', url: '/images/avatar.png' },{ name: '背景.jpg', url: '/images/background.jpg' }]},{ name: '视频', children: [{ name: '介绍.mp4', url: '/videos/intro.mp4' }]}]}};
},
methods: {getIconClass(item) {return item.children ? 'icon-folder' : 'icon-file';},openUrl(url) {window.open(url, '_blank');}
}

5.5 透传插槽

有时候我们需要将从父组件接收到的插槽内容传递给子组件,这称为"透传插槽"(Slot Passthrough):

Vue.component('layout-wrapper', {template: `<div class="layout"><site-header><!-- 将header插槽透传给site-header组件 --><template #logo><slot name="header-logo"></slot></template><template #nav><slot name="header-nav"></slot></template></site-header><main><slot></slot></main><site-footer><!-- 将footer插槽透传给site-footer组件 --><slot name="footer"></slot></site-footer></div>`
})

使用该组件:

<layout-wrapper><template #header-logo><img src="/logo.png" alt="Logo"></template><template #header-nav><nav><a href="/">首页</a><a href="/about">关于</a></nav></template><div class="content"><h1>页面内容</h1><p>这是主要内容...</p></div><template #footer><p>版权所有 © 2023</p></template>
</layout-wrapper>

5.6 插槽与v-for结合

可以在使用v-for的元素上使用插槽,动态创建多个插槽内容:

Vue.component('tabs', {props: ['tabs'],data() {return {activeTab: null};},created() {if (this.tabs.length > 0) {this.activeTab = this.tabs[0].id;}},template: `<div class="tabs-container"><div class="tabs-header"><button v-for="tab in tabs" :key="tab.id"@click="activeTab = tab.id":class="{ active: activeTab === tab.id }">{{ tab.name }}</button></div><div class="tab-content"><template v-for="tab in tabs"><div :key="tab.id" v-if="activeTab === tab.id"class="tab-pane"><slot :name="tab.id" :tab="tab"><!-- 默认内容 --><p>{{ tab.name }} 的默认内容</p></slot></div></template></div></div>`
})

使用该组件:

<tabs :tabs="tabItems"><template v-for="tab in tabItems" #[tab.id]="{ tab }"><div :key="tab.id" class="custom-tab-content"><h3>{{ tab.name }}</h3><div v-html="tab.content"></div><button v-if="tab.hasButton" @click="handleTabAction(tab)">{{ tab.buttonText }}</button></div></template>
</tabs>
data() {return {tabItems: [{ id: 'tab1', name: '介绍', content: '<p>这是介绍内容...</p>', hasButton: false },{ id: 'tab2', name: '功能', content: '<p>这是功能列表...</p>', hasButton: true, buttonText: '了解更多' },{ id: 'tab3', name: '定价', content: '<p>这是价格信息...</p>', hasButton: true, buttonText: '立即购买' }]};
},
methods: {handleTabAction(tab) {console.log(`Tab ${tab.id} button clicked`);// 根据不同的tab执行不同的操作}
}

6. 插槽最佳实践与常见问题

6.1 插槽最佳实践

  1. 提供默认内容

    始终为插槽提供合理的默认内容,这样即使用户没有提供插槽内容,组件也能正常工作:

    <slot name="header"><h2>默认标题</h2>
    </slot>
    
  2. 使用具名插槽而非多个默认插槽

    如果组件需要多个插槽,使用具名插槽而不是多个默认插槽嵌套:

    <!-- 好的做法 -->
    <div><slot name="header"></slot><slot name="content"></slot><slot name="footer"></slot>
    </div><!-- 避免这样做 -->
    <div><div><slot></slot></div><div><slot></slot></div>
    </div>
    
  3. 明确的插槽名称

    使用描述性强的插槽名称,避免模糊不清的名称:

    <!-- 好的命名 -->
    <slot name="item-header"></slot>
    <slot name="item-content"></slot><!-- 避免模糊的命名 -->
    <slot name="top"></slot>
    <slot name="middle"></slot>
    
  4. 为作用域插槽提供有意义的prop名

    当使用作用域插槽时,提供有意义的prop名称:

    <!-- 好的做法 -->
    <slot name="user" :user="userObj" :can-edit="hasEditPermission"></slot><!-- 避免这样做 -->
    <slot name="user" :a="userObj" :b="hasEditPermission"></slot>
    
  5. 使用解构简化作用域插槽

    在使用作用域插槽时,利用解构简化代码:

    <!-- 简化前 -->
    <template #item="slotProps">{{ slotProps.item.name }}
    </template><!-- 简化后 -->
    <template #item="{ item }">{{ item.name }}
    </template>
    
  6. 合理使用插槽默认值

    给作用域插槽的解构属性提供默认值:

    <template #user="{ user = { name: '游客' } }">{{ user.name }}
    </template>
    
  7. 使用插槽作为API的一部分

    将插槽视为组件API的一部分,在文档中明确记录每个插槽的用途和可用的作用域属性。

6.2 常见问题与解决方案

  1. 动态组件中的插槽

    使用<component>动态组件时,插槽内容会被传递给当前激活的组件:

    <component :is="currentComponent"><template #header><h1>动态组件标题</h1></template><p>内容会被传递给当前激活的组件</p>
    </component>
    
  2. 使用v-if/v-else与插槽

    在使用v-if/v-else与插槽时,确保在切换时正确处理插槽:

    <div v-if="condition"><slot name="one"></slot>
    </div>
    <div v-else><slot name="two"></slot>
    </div>
    

    使用该组件:

    <my-component :condition="value"><template #one>内容一</template><template #two>内容二</template>
    </my-component>
    
  3. 重复使用相同插槽内容

    如果需要在多个位置使用相同的插槽内容,可以使用变量保存插槽内容:

    const CustomBtn = {template: `<div><header><slot name="action"></slot></header><footer><slot name="action"></slot></footer></div>`
    }
    

    不过,更好的方式是使用两个不同的插槽名称,然后用户决定是否提供相同的内容:

    const BetterBtn = {template: `<div><header><slot name="header-action"></slot></header><footer><slot name="footer-action"></slot></footer></div>`
    }
    
  4. 插槽内容更新问题

    插槽内容由父组件提供,但在子组件中使用。这意味着插槽内容具有父组件的上下文,而不是子组件的上下文:

    Vue.component('parent-scope', {data() {return {message: '来自父组件的消息'};},template: `<div><child-component><!-- 这里使用的是父组件的message -->{{ message }}</child-component></div>`
    });Vue.component('child-component', {data() {return {message: '来自子组件的消息'};},template: `<div><slot></slot></div>`
    });
    

    渲染结果会显示"来自父组件的消息",而不是"来自子组件的消息"。

  5. 作用域插槽性能考虑

    作用域插槽比普通插槽稍微慢一些,因为它们需要在渲染时创建函数。不过在大多数应用中,这种差异是可以忽略的。只有在极端性能要求下,才需要考虑这一点。

  6. 处理未提供的插槽

    始终检查插槽是否存在,特别是在高阶组件中:

    // 在render函数中
    render() {return h('div', [this.$slots.header? this.$slots.header(): h('h2', '默认标题')]);
    }
    

6.3 Vue 3中的插槽新特性

Vue 3引入了一些插槽的新特性和变化:

  1. 统一的插槽语法

    Vue 3中,所有插槽(包括默认插槽、具名插槽和作用域插槽)都统一使用v-slot指令或其缩写#

  2. 所有插槽都是函数

    在Vue 3中,this.$slots中的所有插槽都是函数,需要调用才能获取VNode:

    // Vue 2
    render() {return h('div', this.$slots.default);
    }// Vue 3
    render() {return h('div', this.$slots.default());
    }
    
  3. 片段支持

    Vue 3支持片段(Fragments),这意味着组件可以有多个根节点,这也适用于插槽内容:

    <my-component><h1>标题</h1><p>段落一</p><p>段落二</p><!-- 不需要额外的包装元素 -->
    </my-component>
    

7. 总结

Vue的插槽系统是组件复用和自定义的强大工具。通过本教程,我们学习了:

  1. 默认插槽 - 用于单一内容分发
  2. 具名插槽 - 用于将内容分发到组件的多个位置
  3. 作用域插槽 - 让子组件向插槽内容提供数据
  4. 动态插槽名 - 允许根据数据动态确定插槽名
  5. 高级用法 - 包括渲染函数中的插槽、递归插槽等

插槽使Vue组件变得更加灵活和可复用,是构建高质量组件库的关键工具。熟练掌握插槽,能够帮助你创建出更加灵活、可定制的组件,大大提高代码的复用性和开发效率。

无论是简单的按钮组件,还是复杂的数据表格、布局系统,插槽都能让你的组件适应各种不同的使用场景,成为真正的"乐高积木",可以组合构建出复杂的用户界面。

相关文章:

Vue插槽(Slots)详解

文章目录 1. 插槽简介1.1 什么是插槽&#xff1f;1.2 为什么需要插槽&#xff1f;1.3 插槽的基本语法 2. 默认插槽2.1 什么是默认插槽&#xff1f;2.2 默认插槽语法2.3 插槽默认内容2.4 默认插槽实例&#xff1a;创建一个卡片组件2.5 Vue 3中的默认插槽2.6 默认插槽的应用场景 …...

[虚幻官方教程学习笔记]深入理解实时渲染(An In-Depth Look at Real-Time Rendering)

原英文教程地址深入理解实时渲染&#xff08;An In-Depth Look at Real-Time Rendering&#xff09; 文章目录 1.Intro to An In-Depth Look at Real-Time RenderingCPU VS GPUDeferred VS Forward 2. Before Rendering and OcclusionCulling计算的步骤使用console command:fre…...

【bibtex4word】在Word中高效转换bib参考文献,Texlive环境安装bibtex4word插件

前言 现已退出科研界&#xff0c;本人水货一个。希望帮到有缘人 本篇关于如何将latex环境中的参考文献bib文件转化为word&#xff0c;和一些踩坑记录。 可以看下面的资料进行配置&#xff0c;后面的文字是这些资料的补充说明。 参考文章&#xff1a;https://blog.csdn.net/g…...

torch.nn 下的常用深度学习函数

1. 层&#xff08;Layers&#xff09; 这些函数用于定义神经网络中的各种层&#xff0c;是构建模型的基础模块。 torch.nn.Linear 用途&#xff1a;全连接层&#xff08;也称为线性层&#xff09;。用于将输入数据从一个维度映射到另一个维度&#xff0c;常用于神经网络的隐藏…...

(2025)图文解锁RAG从原理到实操

什么是RAG RAG(检索增强生成)是一种将语言模型与可搜索知识库结合的方法&#xff0c;主要包含以下关键步骤&#xff1a; 数据预处理 加载&#xff1a;从不同格式(PDF、Markdown等)中提取文本分块&#xff1a;将长文本分割成短序列(通常100-500个标记)&#xff0c;作为检索单元…...

PXE_Kickstart_无人值守自动化安装系统

文章目录 1. PXE2. 配置服务参数2.1 tftp服务配置2.2 dhcp服务配置2.3 http服务配置 3. 配置PXE环境3.1 网络引导文件pxelinux.03.2 挂载镜像文件3.3 创建配置文件default3.4 复制镜像文件和驱动文件3.5 修改default文件3.6 配置ks.cfg文件 4. PXE客户端4.1 创建虚拟机&#xf…...

Redis经典面试题

本篇文章简单介绍一些 Redis 常见的面试题。 Redis 是什么&#xff1f; Redis&#xff0c;英文全称是Remote Dictionary Server&#xff08;远程字典服务&#xff09;&#xff0c;是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库&…...

Vite Proxy配置详解:从入门到实战应用

Vite Proxy配置详解&#xff1a;从入门到实战应用 一、什么是Proxy代理&#xff1f; Proxy&#xff08;代理&#xff09;是开发中常用的解决跨域问题的方案。Vite内置了基于http-proxy的代理功能&#xff0c;可以轻松配置API请求转发。 二、基础配置 在vite.config.js中配置…...

用AI写简历是否可行?

让AI批量写简历然后投简历是绝对不行的&#xff01;&#xff01;&#xff01; 为什么不行&#xff0c;按照 "招聘经理" 工作经历举例&#xff1a; ai提示词&#xff1a;请帮我写一份招聘经理的工作经历内容&#xff1a; 招聘经理 | XXX科技有限公司 | 2020年…...

投影显示技术全解析:主流方案对比与雷克赛恩 CyberPro1 的核心优势

目录 一、主流投影显示技术深度解析 &#xff08;一&#xff09;LCD 投影技术 &#xff08;二&#xff09;DP 投影技术 &#xff08;三&#xff09;3LCD 技术 &#xff08;四&#xff09;FSHD 技术 FSHD 技术优势 二、雷克赛恩 CyberPro1 核心优势对比分析 &#xff08…...

Skyvern:用 AI+视觉驱动浏览器自动化

Skyvern&#xff1a;用 AI视觉驱动浏览器自动化 一、前言二、项目概览2.1 Skyvern 项目简介2.2 代码结构与模块划分 三、环境搭建与快速上手3.1 环境准备3.1.1 系统与依赖3.1.2 克隆项目3.1.3 安装 Python 依赖3.1.4 配置环境变量3.1.5 启动服务 3.2 验证安装 四、核心功能与实…...

101alpha第九

alpha ((rank(ts_arg_max((vwap - close), 3)) * rank(ts_delta(volume, 3))) 今天我们来学下这个 这个是两个rank操作符相加&#xff0c;然后和另外一个操作符相乘&#xff0c;我们来看实现了什么 vwap - close&#xff1a;这部分先计算成交量加权平均价&#xff08;vwap&am…...

蓝牙身份证阅读器使用Uniapp调用二次开发demo

<template> <view class"content"> <view class"search" :loading"searchingstatus" click"searchbluetooth"> {{searchingstatus?"搜索中":"搜索蓝牙阅读器"}} </view> …...

好用的shell终端工具

FinalShell SSH工具,服务器管理 FinalShell SSH工具,服务器管理,远程桌面加速软件,支持Windows,macOS,Linux,版本4.5.12,更新日期2024.10.30 - FinalShell官网...

OSPF不规则区域划分

1、建立一条虚链路 vlink 可以被视为是⻣⼲区域的⼀段延伸。 这⼀条虚拟的链路&#xff0c;只能够跨域⼀个⾮⻣⼲区域。 [r2-ospf-1-area-0.0.0.1]vlink-peer 3.3.3.3 [r3-ospf-1-area-0.0.0.1]vlink-peer 2.2.2.2 在没有建立虚链路之前,r1是不能ping r4的。vlink建⽴的邻居关…...

复习javascript

1.修改元素内的内容 ​ <div>zsgh</div> <script> const box1document.querySelector("div") box1.innerText"ppp" box1.innerHtml<h1>修改</h1> </script>​ 2.随机点名练习 <!DOCTYPE html> <html lang…...

海盗王64位服务端+32位客户端3.0版本

经过多天的尝试&#xff0c;终于把海盗王3.0的服务端改成了64位的&#xff0c;包括AccountServer GroupServer GameServer GateServer。 客户端则保留了32位。 服务端改成64位的好处是GameServer可以只启动一个就开全部地图&#xff0c;大概6G内存左右&#xff0c;直接将跳…...

【从零实现JsonRpc框架#2】Muduo库介绍

1.基本概念 Muduo 由陈硕大佬开发&#xff0c;是一个基于非阻塞IO和事件驱动的C高并发TCP网络编程库。它是一款基于主从Reactor模型的网络库&#xff0c;其使用的线程模型是 one loop per thread。 1.1 主从 Reactor 模型 主 Reactor&#xff08;MainReactor&#xff0c;通常…...

如何创建伪服务器,伪接口

创建伪接口一般是用于模拟真实接口的行为&#xff0c;以便在开发和测试过程中进行使用&#xff0c;以下是一些常见的创建伪接口的方法&#xff1a; 使用 Web 框架搭建&#xff1a; Python 和 Flask&#xff1a;Flask 是一个轻量级的 Python Web 框架。示例代码如下&#xff1a;…...

NX949NX952美光科技闪存NX961NX964

NX949NX952美光科技闪存NX961NX964 在半导体存储领域&#xff0c;美光科技始终扮演着技术引领者的角色。其NX系列闪存产品线凭借卓越的性能与创新设计&#xff0c;成为数据中心、人工智能、高端消费电子等场景的核心组件。本文将围绕NX949、NX952、NX961及NX964四款代表性产品…...

vue配置代理解决前端跨域的问题

文章目录 一、概述二、报错现象三、通过配置代理来解决修改request.js中的baseURL为/api在vite.config.js中增加代理配置 四、参考资料 一、概述 跨域是指由于浏览器的同源策略限制&#xff0c;向不同源(不同协议、不同域名、不同端口)发送ajax请求会失败 二、报错现象 三、…...

深入解析Vue3中ref与reactive的区别及源码实现

深入解析Vue3中ref与reactive的区别及源码实现 前言 Vue3带来了全新的响应式系统&#xff0c;其中ref和reactive是最常用的两个API。本文将从基础使用、核心区别到源码实现&#xff0c;由浅入深地分析这两个API。 一、基础使用 1. ref import { ref } from vueconst count…...

Java Bean容器详解:核心功能与最佳使用实践

在Java企业级开发中&#xff0c;Bean容器是框架的核心组件之一&#xff0c;它通过管理对象&#xff08;Bean&#xff09;的生命周期、依赖关系等&#xff0c;显著提升了代码的可维护性和扩展性。主流的框架如Spring、Jakarta EE&#xff08;原Java EE&#xff09;均提供了成熟的…...

Xilinx Kintex-7 XC7K325T-2FFG676I 赛灵思 FPGA

XC7K325T-2FFG676I 属于 Kintex-7 FPGA &#xff0c;低功耗与合理成本的应用市场&#xff0c;可提供比前代产品两倍的性价比提升和卓越的系统集成能力。该器件于 28 nm 工艺节点制造&#xff0c;速度等级为 -2&#xff0c;适合对时序要求严格但预算有限的系统设计。 产品架构与…...

AI实战笔记(1)AI 的 6 大核心方向 + 学习阶段路径

一、机器学习&#xff08;ML&#xff09; 目标&#xff1a;用数据“训练”模型&#xff0c;完成分类、回归、聚类等任务。 学习阶段&#xff1a; &#xff08;1&#xff09;基础数学&#xff1a;线性代数、概率统计、微积分&#xff08;适度&#xff09; &#xff08;2&#xf…...

Ceph集群故障处理 - PG不一致修复

Ceph集群故障处理 - PG不一致修复 目录故障现象故障分析故障定位修复过程磁盘状态检查OSD存储结构检查修复分析故障总结问题原因修复方法后续建议经验教训最佳实践 参考资料 # ceph -v ceph version 14.2.22目录 故障现象故障分析故障定位修复过程磁盘状态检查OSD存储结构检查…...

【前端】每日一道面试题3:如何实现一个基于CSS Grid的12列自适应布局?

要实现一个基于CSS Grid的12列自适应布局&#xff0c;关键在于利用网格系统的灵活性和响应式设计能力。以下是具体实现步骤及核心代码示例&#xff1a; 一、基础网格容器定义 创建网格容器 使用display: grid将父元素定义为网格容器&#xff1a; .container {display: grid;gr…...

leetcode 349. Intersection of Two Arrays

题目描述 题目限制0 < nums1[i], nums2[i] < 1000&#xff0c;所以可以开辟一个1001个元素的数组来做哈希表。 class Solution { public:vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {vector<int> table(1001,0…...

机器学习 day01

文章目录 前言一、机器学习的基本概念二、数据集的加载1.玩具数据集2.联网数据集3.本地数据集 三、数据集的划分四、特征提取1.稀疏矩阵与稠密矩阵2.字典列表特征提取3.文本特征提取 前言 目前我开始学习机器学习部分的相关知识&#xff0c;通过今天的学习&#xff0c;我掌握了…...

C++STL——priority_queue

优先队列 前言优先队列仿函数头文件 前言 本篇主要讲解优先队列及其底层实现。 优先队列 优先队列的本质就是个堆&#xff0c;其与queue一样&#xff0c;都是容器适配器&#xff0c;不过优先队列是默认为vector实现的。priority_queue的接口优先队列默认为大根堆。 仿函数 …...

DS18B20温度传感器

1.基本信息 测温范围为一55&#xff5e; 125℃&#xff1b;3.3/5V的供电电压&#xff1b;-10~85内精度较高&#xff1b; 典型的温度转换时间为 750ms&#xff08;12 位分辨率&#xff09;&#xff1b; 输出最小分辨率&#xff1a;0.0625&#xff1b; 采用单总线数据格式&am…...

《Python星球日记》 第50天:深度学习概述与环境搭建

名人说&#xff1a;路漫漫其修远兮&#xff0c;吾将上下而求索。—— 屈原《离骚》 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录 一、什么是深度学习&#xff1f;它与传统机器学习的区别1. 深度学习的定义2. 深…...

BUUCTF——Cookie is so stable

BUUCTF——Cookie is so stable 进入靶场 页面有点熟悉 跟之前做过的靶场有点像 先简单看一看靶场信息 有几个功能点 flag.php 随便输了个admin 根据题目提示 应该与cookie有关 抓包看看 构造payload Cookie: PHPSESSIDef0623af2c1a6d2012d57f3529427d52; user{{7*7}}有…...

Java 基础面试题

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/literature?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;…...

bcm5482 phy 场景总结

1,BCM5482是一款双端口10/100/1000BASE-T以太网PHY芯片,支持多种速率和双工模式。其配置主要通过MDIO(Management Data Input/Output)接口进行,MDIO接口用于访问PHY芯片内部的寄存器,从而配置网络速率、双工模式以及其他相关参数。 a,具体以下面两种场景举例 2. 寄存器和…...

小程序多线程实战

在小程序开发中&#xff0c;由于微信小程序的运行环境限制&#xff0c;原生并不支持传统意义上的多线程编程&#xff0c;但可以通过以下两种核心方案实现类似多线程的并发处理效果&#xff0c;尤其在处理复杂计算、避免主线程阻塞时非常关键&#xff1a; 一、官方方案&#xff…...

PPT图表怎么制作?说5款自己使用过的PPT图表制作工具

PPT图表怎么制作&#xff1f;准备一份吸引人的PPT演示文稿时&#xff0c;图表往往能起到画龙点睛的作用。但是&#xff0c;对于很多人来说&#xff0c;制作既美观又专业的图表却不是一件容易的事情。今天&#xff0c;我们就来聊聊如何利用一些优秀的工具制作PPT图表。 1、亿图图…...

渠道销售简历模板范文

模板信息 简历范文名称&#xff1a;渠道销售简历模板范文&#xff0c;所属行业&#xff1a;其他 | 职位&#xff0c;模板编号&#xff1a;KRZ3J3 专业的个人简历模板&#xff0c;逻辑清晰&#xff0c;排版简洁美观&#xff0c;让你的个人简历显得更专业&#xff0c;找到好工作…...

数据库实验10

设计性实验 1&#xff0e;实验要求 1.编写函数FsumXXX&#xff0c;1~n&#xff08;参数&#xff09;求和&#xff1b; GO CREATE FUNCTION Fsum065 (n INT) RETURNS INT AS BEGIN DECLARE sum INT 0 WHILE n > 0 BEGIN SET sum sum n SET n n - 1 END RETURN sum END …...

C#异步Task,await,async和Unity同步协程

标题 TaskawaitasyncUnity协程 Task Task是声明异步任务的必要关键字&#xff0c;也可以使用Task<>泛型来定义Task的返回值。 await await是用于等待一个Task结束&#xff0c;否则让出该线程控制权&#xff0c;让步给其他线程&#xff0c;直到该Task结束才往下运行。 …...

【ML-Agents】ML-Agents示例项目导入unity报错解决

最近在跑ML-Agents的示例代码&#xff0c;无奈往unity中导入项目后&#xff0c;就出现报错。本文简要描述了各个报错的解决方法。 文章目录 一、error CS0234: The type or namespace name InputSystem does not exist in the namespace UnityEngine (are you missing an assem…...

【Web前端开发】HTML基础

Web前端开发是用来直接给用户呈现一个一个的网页&#xff0c;主要包含实现用户的结构&#xff08;HTML&#xff09;、样式&#xff08;CSS&#xff09;、交互&#xff08;JavaScript&#xff09;。然而一个软件通常是由后端和前端完成的。可以查阅文档&#xff1a;HTML 教程 (w…...

spark-哈希join介绍

目录 1. Shuffle Join 和 Hash Join 的复杂度1.1 Shuffle Join1.2 Hash Join 2. 哈希算法的原理2.1 什么是哈希算法&#xff1f;2.2 哈希算法的工作原理2.3 常见哈希函数 3. 哈希算法的弊端3.1 哈希碰撞3.2 哈希分布不均匀3.3 哈希值不可逆 4. 哈希碰撞的处理方法4.1 链地址法4…...

计算机网络与多线程同步机制详解

一、IP地址与子网划分 在互联网世界中&#xff0c;IP地址就像是每个设备的"门牌号"&#xff0c;它使得数据包能够准确送达目的地。IP地址的划分与管理就像城市的规划&#xff0c;通过合理的子网划分&#xff0c;能够高效地管理网络资源。 子网掩码的工作原理 子网…...

栈溢出攻击最基本原理

函数在调用的过程中&#xff0c;函数在调用之前呢&#xff0c;会将调用完这个函数之后的下一条命令的地址保存到LR中。 void func() {int a[4];a[6] 100; } 这个函数在用gcc编译的时候是不会报错的&#xff0c;所以我们可以在尝试之后&#xff0c;修改LR的值&#xff0c;让代…...

ChemDraw、InDraw、KingDraw有什么差别?

在化学相关的科研与教学领域&#xff0c;一款好用的结构式编辑器至关重要&#xff0c;ChemDraw因此闻名&#xff1b;但近年来&#xff0c;ChemDraw代理商频繁发送律师函&#xff0c;给学校和企业带来诸多困扰&#xff0c;促使大家纷纷寻找替代软件。InDraw和KingDraw这两款软件…...

NVMe控制器IP设计之接口模块

这是NVMe控制器IP设计系列博客之一&#xff0c;其他的见本博客或csdn搜用户名&#xff1a;tiantianuser。相关视频见B站用户名&#xff1a;专注与守望。 接口转换模块负责完成AXI4接口与控制器内部的自定义接口之间的转换工作。接口转换模块的框图如图1所示。 图1 接口转换示…...

从0开始学linux韦东山教程第三章问题小结(2)

本人从0开始学习linux&#xff0c;使用的是韦东山的教程&#xff0c;在跟着课程学习的情况下的所遇到的问题的总结,理论虽枯燥但是是基础。 摘要关键词&#xff1a;PC远程访问ubuntu配置&#xff0c;ubuntu配置uboot环境&#xff0c;串口控制开发板 本文详细介绍以下问题&…...

JS正则表达式介绍(JavaScript正则表达式)

文章目录 JavaScript正则表达式完全指南正则表达式基础元字符与特殊字符基本元字符. - 点号\d - 数字\D - 非数字\w - 单词字符\W - 非单词字符\s - 空白字符\S - 非空白字符 正则表达式标志常用标志详解g - 全局匹配i - 忽略大小写m - 多行匹配s - 点号匹配所有字符u - Unicod…...

(51单片机)LCD显示红外遥控相关数字(Delay延时函数)(LCD1602教程)(Int0和Timer0外部中断教程)(IR红外遥控模块教程)

前言&#xff1a; 本次Timer0模块改装了一下&#xff0c;注意&#xff01;&#xff01;&#xff01;今天只是简单的实现一下&#xff0c;明天用次功能显示遥控密码锁 演示视频&#xff1a; 在审核 源代码&#xff1a; 如上图将9个文放在Keli5 中即可&#xff0c;然后烧录在…...