Vue3 鲜为人知的实用技巧
我将为你展示一些大多数开发者不知道的Vue3高级技巧,这些技巧可以显著提升开发效率和代码质量。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue3 鲜为人知的实用技巧</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background: linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d);
color: #333;
line-height: 1.6;
padding: 20px;
min-height: 100vh;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: rgba(255, 255, 255, 0.95);
border-radius: 15px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
overflow: hidden;
}
header {
text-align: center;
padding: 40px;
background: linear-gradient(90deg, #3494e6, #ec6ead);
color: white;
}
h1 {
font-size: 2.8rem;
margin-bottom: 15px;
}
.subtitle {
font-size: 1.2rem;
max-width: 800px;
margin: 0 auto;
}
.intro {
padding: 30px;
background: #f8f9fa;
border-bottom: 1px solid #e9ecef;
}
.intro p {
margin-bottom: 15px;
font-size: 1.1rem;
}
.tips-container {
padding: 30px;
}
.section-title {
font-size: 1.8rem;
margin-bottom: 25px;
color: #2c3e50;
border-left: 5px solid #3498db;
padding-left: 15px;
}
.tip-card {
background: white;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.08);
margin-bottom: 30px;
border-left: 5px solid;
transition: transform 0.3s;
}
.tip-card:hover {
transform: translateY(-5px);
}
.tip-header {
padding: 20px;
color: white;
display: flex;
align-items: center;
justify-content: space-between;
cursor: pointer;
}
.tip-icon {
font-size: 2rem;
margin-right: 15px;
}
.tip-title {
font-size: 1.5rem;
font-weight: 600;
}
.tip-content {
padding: 25px;
display: none;
}
.tip-content.active {
display: block;
}
.tip-description {
margin-bottom: 20px;
padding: 15px;
background: #f8f9fa;
border-radius: 8px;
border-left: 4px solid #6c757d;
}
.demo-area {
border: 2px dashed #3498db;
background: #e8f4fd;
border-radius: 8px;
margin-bottom: 15px;
padding: 20px;
min-height: 150px;
}
.code-example {
background: #2d2d2d;
color: #f8f8f2;
padding: 15px;
border-radius: 8px;
font-family: 'Courier New', monospace;
overflow-x: auto;
margin-top: 15px;
}
.property {
color: #f92672;
}
.value {
color: #a6e22e;
}
.comment {
color: #75715e;
}
.tip-1 {
border-left-color: #FF416C;
}
.tip-1 .tip-header {
background: linear-gradient(90deg, #FF416C, #FF4B2B);
}
.tip-2 {
border-left-color: #3494E6;
}
.tip-2 .tip-header {
background: linear-gradient(90deg, #3494E6, #EC6EAD);
}
.tip-3 {
border-left-color: #2193b0;
}
.tip-3 .tip-header {
background: linear-gradient(90deg, #2193b0, #6dd5ed);
}
.tip-4 {
border-left-color: #f39c12;
}
.tip-4 .tip-header {
background: linear-gradient(90deg, #f39c12, #e74c3c);
}
.tip-5 {
border-left-color: #27ae60;
}
.tip-5 .tip-header {
background: linear-gradient(90deg, #27ae60, #2ecc71);
}
.tip-6 {
border-left-color: #9b59b6;
}
.tip-6 .tip-header {
background: linear-gradient(90deg, #9b59b6, #8e44ad);
}
.demo-controls {
display: flex;
gap: 10px;
margin-top: 15px;
}
.demo-btn {
padding: 10px 15px;
background: #3498db;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 0.9rem;
transition: background 0.3s;
}
.demo-btn:hover {
background: #2980b9;
}
.demo-element {
padding: 10px;
margin: 5px;
background: #3498db;
color: white;
border-radius: 5px;
display: inline-block;
}
footer {
text-align: center;
padding: 20px;
color: #7f8c8d;
background: #f8f9fa;
border-top: 1px solid #e9ecef;
}
@media (max-width: 768px) {
h1 {
font-size: 2.2rem;
}
}
.badge {
display: inline-block;
padding: 3px 8px;
background: #e74c3c;
color: white;
border-radius: 10px;
font-size: 0.8rem;
margin-left: 10px;
}
</style>
</head>
<body>
<div id="app"></div>
<script type="module">
const { createApp, ref, reactive, computed, watch, watchEffect, onMounted, nextTick, provide, inject, defineAsyncComponent, h } = Vue;
// 异步组件示例
const AsyncDemo = defineAsyncComponent(() => {
return new Promise((resolve) => {
setTimeout(() => {
resolve({
template: '<div class="demo-element">异步加载的组件!</div>'
});
}, 1000);
});
});
// 渲染函数组件示例
const RenderFunctionDemo = {
props: ['message'],
render() {
return h('div', {
class: 'demo-element',
style: { background: '#27ae60' }
}, this.message || '来自渲染函数的组件');
}
};
const App = {
components: {
AsyncDemo,
RenderFunctionDemo
},
setup() {
// 1. 响应式技巧 - 使用shallowRef优化性能
const heavyObject = ref({
data: Array.from({ length: 1000 }, (_, i) => ({ id: i, value: `Item ${i}` }))
});
// 使用shallowRef避免深度响应式转换
const shallowHeavyObject = Vue.shallowRef({
data: Array.from({ length: 1000 }, (_, i) => ({ id: i, value: `Item ${i}` }))
});
// 2. 自定义指令示例
const vHighlight = {
mounted(el, binding) {
el.style.background = binding.value || 'yellow';
el.style.padding = '5px';
},
updated(el, binding) {
el.style.background = binding.value || 'yellow';
}
};
// 3. 使用watchEffect的自动依赖追踪
const count = ref(0);
const doubleCount = ref(0);
watchEffect(() => {
doubleCount.value = count.value * 2;
});
// 4. 使用provide/inject进行深层组件通信
const appTheme = ref('light');
provide('appTheme', appTheme);
// 5. 使用nextTick确保DOM更新后执行代码
const showMessage = ref(false);
const messageElement = ref(null);
const toggleMessage = async () => {
showMessage.value = !showMessage.value;
await nextTick();
if (showMessage.value && messageElement.value) {
messageElement.value.scrollIntoView({ behavior: 'smooth' });
}
};
// 6. 使用Teleport的动态目标
const teleportTarget = ref('body');
// 7. 使用CSS模块作用域
const styleModule = {
button: 'demo-btn',
container: 'demo-area'
};
// 8. 使用Suspense处理异步组件
const asyncComponentLoaded = ref(false);
onMounted(() => {
setTimeout(() => {
asyncComponentLoaded.value = true;
}, 1500);
});
// 9. 使用v-model的自定义修饰符
const customModelValue = ref('');
// 10. 使用渲染函数和h()创建动态组件
const dynamicComponent = ref('div');
const dynamicProps = ref({ class: 'demo-element', style: { background: '#9b59b6' } });
const dynamicChildren = ref(['动态内容']);
const changeDynamicComponent = () => {
const components = ['div', 'span', 'p', 'h1', 'h2'];
const randomComponent = components[Math.floor(Math.random() * components.length)];
dynamicComponent.value = randomComponent;
dynamicProps.value.style.background = `#${Math.floor(Math.random()*16777215).toString(16)}`;
dynamicChildren.value = [`这是${randomComponent}元素`];
};
return {
heavyObject,
shallowHeavyObject,
vHighlight,
count,
doubleCount,
appTheme,
showMessage,
messageElement,
toggleMessage,
teleportTarget,
styleModule,
asyncComponentLoaded,
customModelValue,
dynamicComponent,
dynamicProps,
dynamicChildren,
changeDynamicComponent
};
},
template: `
<div class="container">
<header>
Vue3 鲜为人知的实用技巧
<p class="subtitle">提升开发效率的高级技巧和最佳实践</p>
</header>
<div class="intro">
<p>Vue3 引入了许多强大的新特性和改进,但很多实用技巧并不为大多数开发者所知。</p>
<p>掌握这些技巧可以显著提升代码质量、性能和开发体验。</p>
</div>
<div class="tips-container">
<h2 class="section-title">高级技巧展示</h2>
<div class="tip-card tip-1">
<div class="tip-header" @click="toggleTip(1)">
<div>
<div class="tip-icon">🚀</div>
<div class="tip-title">性能优化:shallowRef 和 shallowReactive</div>
</div>
<span class="badge">性能</span>
</div>
<div class="tip-content" :class="{ active: activeTip === 1 }">
<div class="tip-description">
<p>对于大型对象或数组,使用 <code>shallowRef</code> 和 <code>shallowReactive</code> 可以避免不必要的深度响应式转换,提升性能。</p>
</div>
<div class="demo-area">
<p>普通 ref 深度响应式对象属性数量: {{ Object.keys(heavyObject).length }}</p>
<p>shallowRef 浅层响应式对象属性数量: {{ Object.keys(shallowHeavyObject).length }}</p>
<button class="demo-btn" @click="heavyObject.data.push({ id: heavyObject.data.length, value: 'New Item' })">
添加项目(深度响应式)
</button>
<button class="demo-btn" @click="shallowHeavyObject.data.push({ id: shallowHeavyObject.data.length, value: 'New Item' })">
添加项目(浅层响应式)
</button>
</div>
<div class="code-example">
<pre><code><span class="comment">// 深度响应式 - 性能开销大</span>
const heavyObject = <span class="property">ref</span>({
data: <span class="property">Array</span>.from({ length: 1000 }, (_, i) => ({ id: i, value: <span class="value">'Item'</span> }))
});
<span class="comment">// 浅层响应式 - 性能更优</span>
const shallowHeavyObject = <span class="property">shallowRef</span>({
data: <span class="property">Array</span>.from({ length: 1000 }, (_, i) => ({ id: i, value: <span class="value">'Item'</span> }))
});</code></pre>
</div>
</div>
</div>
<div class="tip-card tip-2">
<div class="tip-header" @click="toggleTip(2)">
<div>
<div class="tip-icon">🎯</div>
<div class="tip-title">自定义指令的高级用法</div>
</div>
<span class="badge">功能扩展</span>
</div>
<div class="tip-content" :class="{ active: activeTip === 2 }">
<div class="tip-description">
<p>自定义指令不仅可以修改DOM,还可以实现复杂的交互逻辑和动画效果。</p>
</div>
<div class="demo-area">
<p v-highlight="'#ffeb3b'">这个段落使用了自定义高亮指令</p>
<p v-highlight="appTheme === 'light' ? '#e3f2fd' : '#90caf9'">根据主题变化的高亮</p>
<button class="demo-btn" @click="appTheme = appTheme === 'light' ? 'dark' : 'light'">
切换主题: {{ appTheme }}
</button>
</div>
<div class="code-example">
<pre><code><span class="comment">// 自定义指令定义</span>
const vHighlight = {
<span class="property">mounted</span>(el, binding) {
el.style.background = binding.value || <span class="value">'yellow'</span>;
},
<span class="property">updated</span>(el, binding) {
el.style.background = binding.value;
}
};
<span class="comment">// 在模板中使用</span>
<p v-highlight=<span class="value">"'#ffeb3b'"</span>>高亮文本</p>
<p v-highlight=<span class="value">"themeColor"</span>>动态高亮</p></code></pre>
</div>
</div>
</div>
<div class="tip-card tip-3">
<div class="tip-header" @click="toggleTip(3)">
<div>
<div class="tip-icon">🔍</div>
<div class="tip-title">watchEffect 的自动依赖追踪</div>
</div>
<span class="badge">响应式</span>
</div>
<div class="tip-content" :class="{ active: activeTip === 3 }">
<div class="tip-description">
<p><code>watchEffect</code> 会自动追踪其内部依赖,当任何依赖发生变化时都会重新执行。</p>
</div>
<div class="demo-area">
<p>计数器: {{ count }}</p>
<p>自动计算的双倍值: {{ doubleCount }}</p>
<button class="demo-btn" @click="count++">增加计数</button>
<button class="demo-btn" @click="count = 0">重置计数</button>
</div>
<div class="code-example">
<pre><code>const count = <span class="property">ref</span>(0);
const doubleCount = <span class="property">ref</span>(0);
<span class="comment">// watchEffect 自动追踪 count 依赖</span>
<span class="property">watchEffect</span>(() => {
doubleCount.value = count.value * 2;
});
<span class="comment">// 相比 watch,不需要明确指定依赖</span>
<span class="property">watch</span>(count, (newVal) => {
doubleCount.value = newVal * 2;
});</code></pre>
</div>
</div>
</div>
<div class="tip-card tip-4">
<div class="tip-header" @click="toggleTip(4)">
<div>
<div class="tip-icon">🔄</div>
<div class="tip-title">nextTick 的使用技巧</div>
</div>
<span class="badge">DOM操作</span>
</div>
<div class="tip-content" :class="{ active: activeTip === 4 }">
<div class="tip-description">
<p><code>nextTick</code> 确保在DOM更新后执行代码,非常适合需要操作更新后DOM的场景。</p>
</div>
<div class="demo-area">
<button class="demo-btn" @click="toggleMessage">
{{ showMessage ? '隐藏' : '显示' }}消息
</button>
<div v-if="showMessage" ref="messageElement" style="margin-top: 15px; padding: 10px; background: #e8f4fd; border-radius: 5px;">
这条消息在显示后会自动滚动到视图内!
</div>
</div>
<div class="code-example">
<pre><code>const showMessage = <span class="property">ref</span>(false);
const messageElement = <span class="property">ref</span>(null);
const toggleMessage = async () => {
showMessage.value = !showMessage.value;
<span class="comment">// 等待DOM更新完成</span>
await <span class="property">nextTick</span>();
if (showMessage.value && messageElement.value) {
messageElement.value.scrollIntoView({ behavior: <span class="value">'smooth'</span> });
}
};</code></pre>
</div>
</div>
</div>
<div class="tip-card tip-5">
<div class="tip-header" @click="toggleTip(5)">
<div>
<div class="tip-icon">🎨</div>
<div class="tip-title">动态组件和渲染函数</div>
</div>
<span class="badge">高级功能</span>
</div>
<div class="tip-content" :class="{ active: activeTip === 5 }">
<div class="tip-description">
<p>使用渲染函数和动态组件可以实现极其灵活的UI渲染逻辑。</p>
</div>
<div class="demo-area">
<button class="demo-btn" @click="changeDynamicComponent">
更改动态组件
</button>
<component :is="dynamicComponent" v-bind="dynamicProps">
{{ dynamicChildren[0] }}
</component>
<RenderFunctionDemo message="通过渲染函数创建的组件" />
</div>
<div class="code-example">
<pre><code><span class="comment">// 使用渲染函数创建组件</span>
const RenderFunctionDemo = {
<span class="property">props</span>: [<span class="value">'message'</span>],
<span class="property">render</span>() {
return <span class="property">h</span>(<span class="value">'div'</span>, {
<span class="property">class</span>: <span class="value">'demo-element'</span>,
<span class="property">style</span>: { <span class="property">background</span>: <span class="value">'#27ae60'</span> }
}, <span class="property">this</span>.message || <span class="value">'默认内容'</span>);
}
};
<span class="comment">// 动态组件使用</span>
<component :is=<span class="value">"dynamicComponent"</span> v-bind=<span class="value">"dynamicProps"</span>>
{{ dynamicChildren[0] }}
</component></code></pre>
</div>
</div>
</div>
<div class="tip-card tip-6">
<div class="tip-header" @click="toggleTip(6)">
<div>
<div class="tip-icon">⚡</div>
<div class="tip-title">异步组件和Suspense</div>
</div>
<span class="badge">性能优化</span>
</div>
<div class="tip-content" :class="{ active: activeTip === 6 }">
<div class="tip-description">
<p>使用异步组件和Suspense可以优化应用加载性能,实现按需加载。</p>
</div>
<div class="demo-area">
<Suspense>
<template #default>
<AsyncDemo v-if="asyncComponentLoaded" />
</template>
<template #fallback>
<div>加载中...</div>
</template>
</Suspense>
<p>异步组件状态: {{ asyncComponentLoaded ? '已加载' : '加载中...' }}</p>
</div>
<div class="code-example">
<pre><code><span class="comment">// 定义异步组件</span>
const AsyncDemo = <span class="property">defineAsyncComponent</span>(() => {
return <span class="property">new</span> <span class="property">Promise</span>((resolve) => {
<span class="property">setTimeout</span>(() => {
resolve({
<span class="property">template</span>: <span class="value">'<div>异步加载的组件!</div>'</span>
});
}, 1000);
});
});
<span class="comment">// 使用Suspense包装</span>
<Suspense>
<template #default>
<AsyncDemo />
</template>
<template #fallback>
<div>加载中...</div>
</template>
</Suspense></code></pre>
</div>
</div>
</div>
</div>
<footer>
<p>© 2023 Vue3 鲜为人知的实用技巧 | 提升你的Vue开发技能</p>
</footer>
</div>
`,
data() {
return {
activeTip: 1
};
},
methods: {
toggleTip(tipNumber) {
this.activeTip = this.activeTip === tipNumber ? null : tipNumber;
}
}
};
// 创建Vue应用
const app = Vue.createApp(App);
app.mount('#app');
</script>
</body>
</html>
Vue3 鲜为人知的实用技巧
1. 性能优化:shallowRef 和 shallowReactive
- 对于大型对象或数组,使用
shallowRef
和shallowReactive
避免不必要的深度响应式转换 - 显著提升性能,特别是处理大量数据时
2. 自定义指令的高级用法
- 自定义指令不仅可以修改DOM,还可以实现复杂交互逻辑
- 支持动态参数和生命周期钩子
3. watchEffect 的自动依赖追踪
- 自动追踪其内部依赖,无需显式指定监听对象
- 代码更简洁,响应式逻辑更清晰
4. nextTick 的使用技巧
- 确保在DOM更新后执行代码
- 适合需要操作更新后DOM的场景,如滚动到元素
5. 动态组件和渲染函数
- 使用渲染函数创建高度灵活的组件
- 动态组件可以实现条件渲染和组件切换
6. 异步组件和Suspense
- 使用
defineAsyncComponent
实现按需加载 Suspense
组件提供优雅的加载状态处理
7. 其他实用技巧
- Teleport 的动态目标:可以动态改变传送目标
- CSS 模块作用域:更好地管理组件样式
- v-model 的自定义修饰符:扩展双向绑定的功能
- provide/inject 的响应式:实现深层组件通信
使用建议
- 性能敏感场景:优先使用
shallowRef
和shallowReactive
- 复杂交互:考虑使用自定义指令封装可复用逻辑
- 异步操作:充分利用
Suspense
和异步组件优化加载体验 - DOM 操作:总是使用
nextTick
确保操作时机正确
这些技巧可以帮助你编写更高效、更易维护的Vue3应用,提升开发体验和应用性能。