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>
相关推荐
浩星18 小时前
css实现类似element官网的磨砂屏幕效果
前端·javascript·css
一只小风华~19 小时前
Vue.js 核心知识点全面解析
前端·javascript·vue.js
2022.11.7始学前端19 小时前
n8n第七节 只提醒重要的待办
前端·javascript·ui·n8n
SakuraOnTheWay19 小时前
React Grab实践 | 记一次与Cursor的有趣对话
前端·cursor
阿星AI工作室19 小时前
gemini3手势互动圣诞树保姆级教程来了!附提示词
前端·人工智能
徐小夕19 小时前
知识库创业复盘:从闭源到开源,这3个教训价值百万
前端·javascript·github
xhxxx19 小时前
函数执行完就销毁?那闭包里的变量凭什么活下来!—— 深入 JS 内存模型
前端·javascript·ecmascript 6
StarkCoder19 小时前
求求你试试 DiffableDataSource!别再手算 indexPath 了(否则迟早崩)
前端
fxshy19 小时前
Cursor 前端Global Cursor Rules
前端·cursor
红彤彤19 小时前
前端接入sse(EventSource)(@fortaine/fetch-event-source)
前端