0
点赞
收藏
分享

微信扫一扫

Vue3 鲜为人知的实用技巧

7dcac6528821 20小时前 阅读 1

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>
&lt;p v-highlight=<span class="value">"'#ffeb3b'"</span>&gt;高亮文本&lt;/p&gt;
&lt;p v-highlight=<span class="value">"themeColor"</span>&gt;动态高亮&lt;/p&gt;</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>
&lt;component :is=<span class="value">"dynamicComponent"</span> v-bind=<span class="value">"dynamicProps"</span>&gt;
  {{ dynamicChildren[0] }}
&lt;/component&gt;</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">'&lt;div&gt;异步加载的组件!&lt;/div&gt;'</span>
      });
    }, 1000);
  });
});

<span class="comment">// 使用Suspense包装</span>
&lt;Suspense&gt;
  &lt;template #default&gt;
    &lt;AsyncDemo /&gt;
  &lt;/template&gt;
  &lt;template #fallback&gt;
    &lt;div&gt;加载中...&lt;/div&gt;
  &lt;/template&gt;
&lt;/Suspense&gt;</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

  • 对于大型对象或数组,使用 shallowRefshallowReactive 避免不必要的深度响应式转换
  • 显著提升性能,特别是处理大量数据时

2. 自定义指令的高级用法

  • 自定义指令不仅可以修改DOM,还可以实现复杂交互逻辑
  • 支持动态参数和生命周期钩子

3. watchEffect 的自动依赖追踪

  • 自动追踪其内部依赖,无需显式指定监听对象
  • 代码更简洁,响应式逻辑更清晰

4. nextTick 的使用技巧

  • 确保在DOM更新后执行代码
  • 适合需要操作更新后DOM的场景,如滚动到元素

5. 动态组件和渲染函数

  • 使用渲染函数创建高度灵活的组件
  • 动态组件可以实现条件渲染和组件切换

6. 异步组件和Suspense

  • 使用 defineAsyncComponent 实现按需加载
  • Suspense 组件提供优雅的加载状态处理

7. 其他实用技巧

  • Teleport 的动态目标:可以动态改变传送目标
  • CSS 模块作用域:更好地管理组件样式
  • v-model 的自定义修饰符:扩展双向绑定的功能
  • provide/inject 的响应式:实现深层组件通信

使用建议

  1. 性能敏感场景:优先使用 shallowRefshallowReactive
  2. 复杂交互:考虑使用自定义指令封装可复用逻辑
  3. 异步操作:充分利用 Suspense 和异步组件优化加载体验
  4. DOM 操作:总是使用 nextTick 确保操作时机正确

这些技巧可以帮助你编写更高效、更易维护的Vue3应用,提升开发体验和应用性能。

举报

相关推荐

0 条评论