Practical Guide to Vue 3.2 Setup Sugar, <script setup> with TypeScript, and New Vue APIs

This article walks through preparing a Vue 3 + TypeScript project, explains the concept of setup sugar and its benefits, demonstrates the new compiler‑macro APIs such as defineProps, defineEmits and defineExpose, introduces useful hook APIs, covers Vue 3.2 additions like v‑memo and style‑variable binding, and provides tips for i18n, debugging and common pitfalls.

Sohu Tech Products
Sohu Tech Products
Sohu Tech Products
Practical Guide to Vue 3.2 Setup Sugar, <script setup> with TypeScript, and New Vue APIs

Vue 3.2 introduces several new features, most notably the <script setup> syntax combined with TypeScript and the Volar extension, which the author calls "truly fragrant". The article shares a practical workflow and common pitfalls discovered while using these features.

Preparation

Create a Vue 3 + TypeScript project with vue-cli.

Disable Vetur in VS Code and install Volar from the marketplace.

Note that Vue 3 does not support IE (including IE 11) because it relies on the Proxy API.

What is Setup Sugar?

Setup sugar adds a special <script> block with the setup attribute, automatically exposing all top‑level bindings to the template.

<code style="padding: 16px; color: #abb2bf; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menus, monospace; font-size: 12px"><span style="line-height: 26px"><template>
  <Foo :count="count" @click="inc" />
</template>
<script>
import Foo from './Foo.vue'
import { ref } from 'vue'
export default {
  setup() {
    const count = ref(1)
    const inc = () => { count.value++ }
    return { Foo, count, inc }
  }
}
</script></span></code>

The same component can be written more concisely with <script setup>:

<code style="padding: 16px; color: #abb2bf; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menus, monospace; font-size: 12px"><template>
  <Foo :count="count" @click="inc" />
</template>
<script setup>
import Foo from './Foo.vue'
import { ref } from 'vue'
const count = ref(0)
const inc = () => { count.value++ }
</script></code>

Composition API vs Options API

The composition API reduces logical coupling by allowing related code (data, methods, watchers, etc.) to be placed together, and enables reusable hooks. However, without disciplined code partitioning, it can become harder to read.

Self‑partition code into sections: imports, reactive state, lifecycle/watch, methods, and exports.

Separate UI into views and reusable components folders.

Extract logic into custom hooks even if they are not reused.

New Compiler‑Macro APIs (define…)

APIs that start with define are compiler macros usable only inside <script setup>. They are not imported and are stripped during compilation. defineProps: declares component props. Example:

<code style="padding: 16px; color: #abb2bf; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menus, monospace; font-size: 12px">defineProps({
  name: { type: String, required: false, default: 'Petter' },
  userInfo: Object,
  tags: Array,
})</code>

With TypeScript:

<code style="padding: 16px; color: #abb2bf; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menus, monospace; font-size: 12px">const props = defineProps<{ foo: string; bar?: number }>()</code>
withDefaults

: provides default values for props when using TypeScript declarations.

<code style="padding: 16px; color: #abb2bf; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menus, monospace; font-size: 12px">withDefaults(defineProps<{ size?: number; labels?: string[] }>(), {
  size: 3,
  labels: () => ['default label'],
})</code>
defineEmits

: declares emitted events.

<code style="padding: 16px; color: #abb2bf; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menus, monospace; font-size: 12px">const emit = defineEmits(['change', 'delete'])
emit('change')
// With TS:
const emit = defineEmits<{ (e: 'change', id: number): void; (e: 'update', value: string): void }>()</code>
defineExpose

: manually expose variables or methods from the setup context.

<code style="padding: 16px; color: #abb2bf; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menus, monospace; font-size: 12px">const a = 1
const b = ref(2)
defineExpose({ a, b })</code>

Hook APIs

useAttrs

: returns all component attributes, including class, style, and listeners.

<code style="padding: 16px; color: #abb2bf; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menus, monospace; font-size: 12px">const attrs = useAttrs()
<component v-bind="attrs" /></code>
useCSSModule

, useSlots, useCssVars, useTransitionState, useSSRContext: various utilities for CSS modules, slot access, CSS variable binding, transition state, and server‑side rendering.

Vue 3.2 New Features

v-memo

: skips rendering of a subtree when the supplied array values do not change.

<code style="padding: 16px; color: #abb2bf; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menus, monospace; font-size: 12px"><div v-memo="[valueA, valueB]"> ... </div></code>

Experimental v-bind inside <style> to bind component state to CSS variables.

<code style="padding: 16px; color: #abb2bf; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menus, monospace; font-size: 12px">.text { color: v-bind(color); font-size: v-bind('font.size'); }</code>

Internationalization (i18n) in Vue 3

Install vue-i18n (e.g., ^9.1.7).

Create a locales folder with JSON files for each language.

Initialize i18n in a separate module:

<code style="padding: 16px; color: #abb2bf; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menus, monospace; font-size: 12px">import en from "./en.json"
import zhHans from "./zh-cn.json"
import zhHant from "./zh-hk.json"
import { createI18n } from "vue-i18n"
import { judgeLang } from "@/utils/url"

const messages = { en, "zh-hans": zhHans, "zh-hant": zhHant }
const i18n = createI18n({ locale: judgeLang(), messages })
export default i18n</code>

Use the i18n instance in main.ts and provide a custom hook for injection.

<code style="padding: 16px; color: #abb2bf; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menus, monospace; font-size: 12px">export function useProvideI18n(): void {
  const { t } = useI18n()
  provide("t", t)
}
export function useInjectI18n(): (text: string) => string {
  const t = inject("t") as (text: string) => string
  return t
}</code>

Limitations and Debugging

Component options (e.g., name, inheritAttrs) cannot be set inside <script setup>; a normal <script> block is required.

TypeScript and ESLint rules such as @typescript-eslint/no-unused-vars may need to be disabled for setup sugar.

For mobile debugging, vConsole may cause stack overflow; eruda is an alternative, loaded dynamically:

<code style="padding: 16px; color: #abb2bf; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menus, monospace; font-size: 12px">export default function debugInit(): void {
  const script = document.createElement("script")
  script.type = "text/javascript"
  script.src = "//cdn.bootcdn.net/ajax/libs/eruda/2.3.3/eruda.js"
  document.getElementsByTagName("head")[0].appendChild(script)
  script.onload = function () { window.eruda.init() }
}</code>

Using components without a root element is now allowed, but some edge cases (e.g., transition) may still cause issues.

References

Links to Vue CLI guide, Volar marketplace page, Vue RFC discussions, vue‑i18n documentation, and other RFCs are provided for further reading.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Composition APIi18nVue3VolarScript Setupsetup sugar
Sohu Tech Products
Written by

Sohu Tech Products

A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.