1. 如何实现一个自定义指令,控制元素的权限(如按钮权限)?
问题解析
- 核心需求:根据用户权限动态显示/隐藏元素(如按钮)。
- 难点:权限逻辑复用性、响应式更新权限状态。
解决方案
javascript
复制代码
// 权限指令 v-permission
Vue.directive('permission', {
inserted(el, binding, vnode) {
const { value: requiredPermission } = binding;
const userPermissions = vnode.context.$store.getters.userPermissions;
if (!requiredPermission || !userPermissions.includes(requiredPermission)) {
el.parentNode?.removeChild(el); // 直接移除元素
}
}
});
// 使用
<button v-permission="'edit'">编辑</button>
优化点
- 响应式更新 :在
update
钩子中处理权限变化,重新渲染。
- 服务端权限验证 :可通过
binding.value
传递异步权限码。
2. 如何用指令实现全局防抖(v-debounce)?
问题解析
- 核心需求:防止按钮重复点击或输入框频繁触发事件。
- 难点:通用性(支持多种事件)、参数传递(防抖时间)。
解决方案
ini
复制代码
Vue.directive('debounce', {
inserted(el, binding) {
const { value: handler, arg: event = 'click', modifiers } = binding;
const delay = modifiers.delay || 300;
let timer = null;
el.addEventListener(event, (...args) => {
clearTimeout(timer);
timer = setTimeout(() => handler.apply(this, args), delay);
});
}
});
// 使用:防抖点击事件,延迟500ms
<button v-debounce:click.delay="submitForm">提交</button>
优化点
- 支持修饰符 :通过
modifiers
配置不同防抖时间。
- 内存泄漏处理 :在
unbind
钩子中移除事件监听。
3. 如何实现一个拖拽指令(v-draggable)?
问题解析
- 核心需求:让元素可拖拽,支持边界限制。
- 难点:DOM 操作、事件解绑、性能优化。
解决方案
ini
复制代码
Vue.directive('draggable', {
inserted(el) {
let isDragging = false;
let initialX = 0, initialY = 0;
const onMouseDown = (e) => {
isDragging = true;
initialX = e.clientX - el.offsetLeft;
initialY = e.clientY - el.offsetTop;
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
};
const onMouseMove = (e) => {
if (!isDragging) return;
const x = e.clientX - initialX;
const y = e.clientY - initialY;
el.style.left = `${x}px`;
el.style.top = `${y}px`;
};
const onMouseUp = () => {
isDragging = false;
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
};
el.addEventListener('mousedown', onMouseDown);
},
unbind(el) {
// 清理事件防止内存泄漏
el.removeEventListener('mousedown', onMouseDown);
}
});
// 使用
<div v-draggable style="position: absolute;">拖拽我</div>
优化点
- 边界限制 :在
onMouseMove
中计算元素位置时添加边界判断。
- 性能优化 :使用
transform
代替 left/top
减少回流。
4. 如何通过指令实现图片懒加载(v-lazy)?
问题解析
- 核心需求:图片进入视口时再加载资源。
- 难点:交叉观察器(IntersectionObserver)的使用、占位符设计。
解决方案
ini
复制代码
javascriptCopy Code
Vue.directive('lazy', {
inserted(el, binding) {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = new Image();
img.src = binding.value;
img.onload = () => el.src = binding.value;
observer.unobserve(el); // 加载后停止观察
}
});
});
observer.observe(el);
}
});
// 使用
<img v-lazy="'https://example.com/large-image.jpg'" src="placeholder.jpg">
优化点
- 兼容性 :降级方案(如
scroll
事件监听)。
- 错误处理 :添加
onerror
回调显示默认图片。
问题解析
- 核心需求:鼠标悬停时显示动态内容提示。
- 难点:动态内容渲染、位置计算、组件化与指令的协作。
解决方案
ini
复制代码
Vue.directive('tooltip', {
bind(el, binding) {
const tooltip = document.createElement('div');
tooltip.className = 'custom-tooltip';
document.body.appendChild(tooltip);
el.addEventListener('mouseenter', () => {
tooltip.textContent = binding.value;
const rect = el.getBoundingClientRect();
tooltip.style.left = `${rect.left + rect.width / 2}px`;
tooltip.style.top = `${rect.top - 30}px`;
tooltip.style.display = 'block';
});
el.addEventListener('mouseleave', () => {
tooltip.style.display = 'none';
});
},
unbind(el) {
// 清理 Tooltip 元素
const tooltip = document.querySelector('.custom-tooltip');
tooltip?.remove();
}
});
// 使用
<button v-tooltip="'这是提示内容'">悬停查看提示</button>
优化点
- 内容动态更新 :在
update
钩子中更新 tooltip.textContent
。
- 动画效果:通过 CSS 过渡或第三方动画库增强交互。
复杂场景设计原则
- 解耦与复用:将指令逻辑拆分为独立函数,方便复用。
- 性能优化 :避免在指令中频繁操作 DOM,优先使用 CSS 或
requestAnimationFrame
。
- 响应式处理 :通过
binding.value
监听数据变化,更新指令行为。
- 内存管理 :在
unbind
或 beforeUnmount
中清理事件和对象。