Vue 进阶,自定义指令 + 插槽 + 路由入门

Vue 的自定义指令、插槽是组件扩展的核心能力,路由则是单页应用的基础。

一、自定义指令:扩展 Vue 的 DOM 操作能力

Vue 内置指令(如v-if/v-bind)满足基础需求,自定义指令可封装重复的 DOM 操作逻辑(如权限控制、加载状态)。

1. 基本语法:全局 / 局部注册

自定义指令有 5 个钩子函数(常用bind/inserted/update):

  • bind:指令绑定到元素时执行(仅一次);
  • inserted:元素插入 DOM 时执行;
  • update:元素更新时执行。

(1)全局注册(全项目可用)

main.js中通过Vue.directive()注册:

javascript 复制代码
// main.js
Vue.directive('focus', {
  // 元素插入DOM时自动聚焦
  inserted(el) {
    el.focus();
  }
});

使用:

vue 复制代码
<template>
  <input v-focus placeholder="自动聚焦">
</template>

(2)局部注册(仅当前组件可用)

在组件的directives选项中注册:

vue 复制代码
<template>
  <div v-color="color">自定义颜色</div>
</template>

<script>
export default {
  data() {
    return { color: 'red' };
  },
  directives: {
    color: {
      // 绑定指令时设置颜色
      bind(el, binding) {
        // binding.value是指令的值(此处为'red')
        el.style.color = binding.value;
      },
      // 元素更新时更新颜色
      update(el, binding) {
        el.style.color = binding.value;
      }
    }
  }
};
</script>

2. 实战:封装v-loading指令

实现 "加载中遮罩层" 的指令:

javascript 复制代码
// main.js 全局注册v-loading
Vue.directive('loading', {
  bind(el) {
    // 创建遮罩层元素
    const mask = document.createElement('div');
    mask.className = 'loading-mask';
    mask.innerHTML = '<span>加载中...</span>';
    // 存入el的自定义属性,方便后续操作
    el._loadingMask = mask;
    // 给元素添加定位,确保遮罩层覆盖
    el.style.position = 'relative';
  },
  update(el, binding) {
    // binding.value为true时显示遮罩,否则隐藏
    if (binding.value) {
      el.appendChild(el._loadingMask);
    } else {
      el.removeChild(el._loadingMask);
    }
  }
});

添加样式:

css 复制代码
.loading-mask {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(255,255,255,0.8);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 999;
}

使用:

vue 复制代码
<template>
  <div v-loading="isLoading" style="width: 300px; height: 200px; border: 1px solid #eee;">
    内容区域
  </div>
  <button @click="isLoading = !isLoading">切换加载状态</button>
</template>

<script>
export default {
  data() {
    return { isLoading: false };
  }
};
</script>

二、插槽:让组件更灵活的 "占位符"

插槽(Slot)是组件的 "内容分发出口",允许父组件向子组件传递任意内容,解决组件内容固定的问题。

1. 默认插槽:基础内容分发

子组件用<slot>定义默认插槽,父组件在子组件标签内写内容:

vue 复制代码
<!-- 子组件 MyCard.vue -->
<template>
  <div class="card">
    <!-- 默认插槽:父组件内容会替换这里 -->
    <slot>默认内容(父组件没传内容时显示)</slot>
  </div>
</template>

<style scoped>
.card { border: 1px solid #eee; padding: 20px; }
</style>

使用:

vue 复制代码
<template>
  <MyCard>
    <!-- 父组件传递的内容 -->
    <h3>卡片标题</h3>
    <p>卡片内容</p>
  </MyCard>
</template>

2. 具名插槽:多区域内容分发

当组件需要多个插槽时,用name属性定义具名插槽:

vue 复制代码
<!-- 子组件 MyLayout.vue -->
<template>
  <div class="layout">
    <header>
      <!-- 具名插槽:header -->
      <slot name="header"></slot>
    </header>
    <main>
      <!-- 默认插槽:name为default -->
      <slot></slot>
    </main>
    <footer>
      <!-- 具名插槽:footer -->
      <slot name="footer"></slot>
    </footer>
  </div>
</template>

使用(Vue 2.6 + 用v-slot,简写#):

vue 复制代码
<template>
  <MyLayout>
    <!-- 具名插槽:header -->
    <template v-slot:header>
      <h1>页面标题</h1>
    </template>

    <!-- 默认插槽 -->
    <p>页面主体内容</p>

    <!-- 具名插槽简写:#footer -->
    <template #footer>
      <p>版权信息</p>
    </template>
  </MyLayout>
</template>

3. 作用域插槽:子组件向父组件传递数据

父组件使用插槽时,可接收子组件传递的数据(解决 "父组件内容依赖子组件数据" 的问题):

vue 复制代码
<!-- 子组件 MyList.vue -->
<template>
  <ul>
    <li v-for="item in list" :key="item.id">
      <!-- 插槽传递item数据 -->
      <slot :item="item" :index="$index"></slot>
    </li>
  </ul>
</template>

<script>
export default {
  props: { list: { type: Array, default: () => [] } }
};
</script>

使用:

vue 复制代码
<template>
  <MyList :list="goodsList">
    <!-- 接收子组件传递的item数据 -->
    <template v-slot="slotProps">
      <span>{{ slotProps.index + 1 }}. {{ slotProps.item.name }}</span>
    </template>
  </MyList>
</template>

<script>
export default {
  data() {
    return {
      goodsList: [{ id: 1, name: '苹果' }, { id: 2, name: '香蕉' }]
    };
  }
};
</script>

三、路由入门:Vue Router 实现单页应用

单页应用(SPA)通过路由切换页面,无需刷新,Vue Router 是 Vue 官方的路由工具。

1. 什么是单页应用?

  • 只有一个 HTML 页面,通过 JS 动态切换内容;
  • 优点:体验流畅、加载快;
  • 缺点:首屏加载慢(需优化)。

2. Vue Router 的基本使用

步骤 1:安装 Vue Router

bash 复制代码
npm install vue-router@3 # Vue 2对应vue-router@3

步骤 2:创建路由实例

src/router/index.js中配置路由:

javascript 复制代码
// src/router/index.js
import Vue from 'vue';
import VueRouter from 'vue-router';
// 导入页面组件
import Home from '../views/Home.vue';
import Goods from '../views/Goods.vue';

// 注册VueRouter
Vue.use(VueRouter);

// 路由规则
const routes = [
  { path: '/', component: Home }, // 首页
  { path: '/goods', component: Goods } // 商品页
];

// 创建路由实例
const router = new VueRouter({
  routes // 等价于routes: routes
});

export default router;

步骤 3:挂载路由到 Vue 实例

main.js中引入路由:

javascript 复制代码
// main.js
import Vue from 'vue';
import App from './App.vue';
import router from './router/index.js';

new Vue({
  el: '#app',
  router, // 挂载路由
  render: h => h(App)
});

步骤 4:使用路由

App.vue中添加路由出口和导航:

vue 复制代码
<!-- App.vue -->
<template>
  <div>
    <!-- 路由导航:相当于a标签 -->
    <router-link to="/">首页</router-link>
    <router-link to="/goods">商品页</router-link>

    <!-- 路由出口:匹配的组件会渲染到这里 -->
    <router-view></router-view>
  </div>
</template>
相关推荐
前端大卫1 小时前
Vue3 + Element-Plus 自定义虚拟表格滚动实现方案【附源码】
前端
却尘1 小时前
Next.js 请求最佳实践 - vercel 2026一月发布指南
前端·react.js·next.js
ccnocare1 小时前
浅浅看一下设计模式
前端
Lee川1 小时前
🎬 从标签到屏幕:揭秘现代网页构建与适配之道
前端·面试
Ticnix2 小时前
ECharts初始化、销毁、resize 适配组件封装(含完整封装代码)
前端·echarts
纯爱掌门人2 小时前
终焉轮回里,藏着 AI 与人类的答案
前端·人工智能·aigc
twl2 小时前
OpenClaw 深度技术解析
前端
崔庆才丨静觅2 小时前
比官方便宜一半以上!Grok API 申请及使用
前端
星光不问赶路人2 小时前
vue3使用jsx语法详解
前端·vue.js
天蓝色的鱼鱼2 小时前
shadcn/ui,给你一个真正可控的UI组件库
前端