Vue 3 <script setup> 语法详解

<script setup> 是 Vue 3 引入的一种编译时语法糖,它极大地改善了开发者在单文件组件(SFC)中的开发体验。下面我将详细解释它的特性和用法。

基本概念

<script setup> 是在单文件组件(SFC)中使用组合式 API 的更简洁语法。它让代码更简洁,减少了样板代码,同时提供了更好的类型推断支持。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Vue 3 script setup 示例</title>
  <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
  <style>
    body {
      font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
      max-width: 800px;
      margin: 0 auto;
      padding: 20px;
      background-color: #f5f7fa;
      color: #333;
    }
    .container {
      background: white;
      border-radius: 10px;
      padding: 20px;
      box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
    }
    h1 {
      color: #42b883;
      border-bottom: 2px solid #42b883;
      padding-bottom: 10px;
    }
    h2 {
      color: #35495e;
    }
    .feature-block {
      background: #f9f9f9;
      border-left: 4px solid #42b883;
      padding: 15px;
      margin: 15px 0;
      border-radius: 0 5px 5px 0;
    }
    code {
      background: #272822;
      color: #f8f8f2;
      padding: 2px 6px;
      border-radius: 4px;
      font-family: 'Fira Code', monospace;
    }
    pre {
      background: #272822;
      color: #f8f8f2;
      padding: 15px;
      border-radius: 5px;
      overflow-x: auto;
    }
    .comparison {
      display: flex;
      gap: 20px;
      margin: 20px 0;
    }
    .comparison > div {
      flex: 1;
      background: #f9f9f9;
      padding: 15px;
      border-radius: 5px;
    }
    .btn {
      background: #42b883;
      color: white;
      border: none;
      padding: 10px 15px;
      border-radius: 4px;
      cursor: pointer;
      font-size: 16px;
      margin: 5px;
      transition: background 0.3s;
    }
    .btn:hover {
      background: #368a6a;
    }
  </style>
</head>
<body>
  <div id="app"></div>

  <script type="module">
    const { createApp } = Vue;
    
    // 子组件
    const ChildComponent = {
      template: `
        <div class="feature-block">
          <h3>子组件</h3>
          <p>从父组件接收的消息: {{ message }}</p>
          <button class="btn" @click="sendMessageToParent">向父组件发送消息</button>
        </div>
      `,
      props: {
        message: String
      },
      emits: ['message-to-parent'],
      setup(props, { emit }) {
        function sendMessageToParent() {
          emit('message-to-parent', '来自子组件的问候!');
        }
        
        return {
          sendMessageToParent
        };
      }
    };

    const app = createApp({
      setup() {
        const message = Vue.ref('Hello Vue 3!');
        const count = Vue.ref(0);
        const childMessage = Vue.ref('');
        
        function increment() {
          count.value++;
        }
        
        function receiveMessage(msg) {
          childMessage.value = msg;
        }
        
        return {
          message,
          count,
          increment,
          childMessage,
          receiveMessage
        };
      },
      template: `
        <div class="container">
          <h1>Vue 3 &lt;script setup&gt; 语法详解</h1>
          
          <div class="feature-block">
            <h2>基本响应式状态</h2>
            <p>{{ message }}</p>
            <button class="btn" @click="increment">计数: {{ count }}</button>
          </div>
          
          <div class="feature-block">
            <h2>组件通信</h2>
            <p>来自子组件的消息: {{ childMessage }}</p>
            <child-component 
              :message="'来自父组件的消息 '" 
              @message-to-parent="receiveMessage">
            </child-component>
          </div>
          
          <h2>&lt;script setup&gt; 的优势</h2>
          <div class="comparison">
            <div>
              <h3>传统写法</h3>
              <pre><code>
&lt;script&gt;
export default {
  setup() {
    const count = ref(0)
    const increment = () => count.value++
    
    return {
      count,
      increment
    }
  }
}
&lt;/script&gt;
              </code></pre>
            </div>
            <div>
              <h3>&lt;script setup&gt; 写法</h3>
              <pre><code>
&lt;script setup&gt;
import { ref } from 'vue'

const count = ref(0)
const increment = () => count.value++
&lt;/script&gt;
              </code></pre>
            </div>
          </div>
          
          <div class="feature-block">
            <h2>主要特性</h2>
            <ul>
              <li>更简洁的代码 - 不需要从 setup() 函数返回变量和方法</li>
              <li>更好的类型推断 - 对 TypeScript 支持更友好</li>
              <li>更好的运行时性能 - 模板编译为没有代理的渲染函数</li>
              <li>可以使用顶层 await - 使得异步代码更简洁</li>
            </ul>
          </div>
        </div>
      `
    });
    
    app.component('ChildComponent', ChildComponent);
    app.mount('#app');
  </script>
</body>
</html>

核心特性解释

1. 自动暴露顶层绑定

<script setup> 中,所有顶层绑定(变量、函数、import导入)自动可用于模板,无需return语句。

2. 组件使用

直接import组件后即可在模板中使用,无需在components选项中注册。

3. 定义props和emits

使用defineProps和defineEmits编译器宏来声明props和emits:

<script setup>
const props = defineProps({
  title: String,
  count: Number
})

const emit = defineEmits(['update', 'delete'])
</script>

4. 使用Slots和Attrs

可以通过useSlots和useAttrs来访问slots和attrs:

<script setup>
import { useSlots, useAttrs } from 'vue'

const slots = useSlots()
const attrs = useAttrs()
</script>

5. 顶层await

<script setup>中可以直接使用顶层await:

<script setup>
const data = await fetch('/api/data').then(res => res.json())
</script>

与传统写法的对比

传统写法需要将所有的变量和方法从setup函数中返回,而<script setup>自动完成这一过程,大大简化了代码结构。


总结

<script setup>是Vue 3组合式API的重要补充,它提供了更简洁的语法、更好的类型推断支持,并且减少了样板代码。对于新项目,强烈推荐使用<script setup>语法。

以上示例展示了<script setup>的主要特性,可以直接运行这段代码来查看效果。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐