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>