# Vue 3 + TypeScript + TSX 上手體驗

# 建立專案

vue-cli (opens new window) 建立一個專案,接著加入 vue-cli-plugin-vue-next (opens new window) 這 plugin

vite (opens new window) 這尤大大的新玩具,並搭配 create-vite-app (opens new window) 快速建立 Vue 3 的專案,基本上一直下一步就可以完成了

yarn create vite-app test-vite
cd test-vite

預設 vite 是沒有 typescript 的,所以需要手動加上

yarn add --dev typescript

# 改寫 js 進入點

/src/main.js 重命名成 /src/main.ts/index.htmlmain.js 改成 main.ts

<script type="module" src="/src/main.ts"></script>

# 改寫 App.vue

/src/App.vue 重命名成 /src/App.tsx 並改寫內容

import { defineComponent } from 'vue'
import HelloWorld from '../HelloWorld';
import imgLogo from './assets/logo.png';

export default defineComponent({
  name: 'Root',

  setup() {
    return () => (
      <>
        <img alt="Vue logo" src={imgLogo} />
        <HelloWorld msg="Hello Vue 3.0 + Vite" />
      </>
    )
  },
})

這邊有幾個重點改寫:

  • 使用 Composition API setup 並使用 TSX 寫 VNode
  • 支援使用 Fragment TSX (可以不用每個 Component 都一定要有 root DOM,但整個 Vue App 一一定要至少一個 DOM)
  • Component HelloWorld 有型別提示 (ex: msg)
  • Component HelloWorld 有型別提示!!
  • Component HelloWorld 有型別提示!!!
  • <img src="./assets/logo.png"> 改成用 import resource

# 改寫 HelloWorld.vue

components/HelloWorld.vue 重命名成 components/HelloWorld.tsx 並改寫內容

import { defineComponent, ref } from 'vue';

export default defineComponent({
  props: {
    msg: String
  },

  setup(props) {
 ****   const count = ref(0)
    const handleCountButtonClick = () => count.value ++

 ****   return () => (
      <>
        <h1>{props.msg}</h1>
        <button onClick={handleCountButtonClick}>count is: { count.value }</button>
        <p>Edit <code>components/HelloWorld.tsx</code> to test hot module replacement.</p>
      </>
    )
  }
})

改寫重點:

  • count 使用 ref 變成 reactive var
  • handleCountButtonClick 作為接受按鈕按下事件

# 改寫 HelloWorld Counter

看到他有一個 counter 的按鈕,想說可以把 counter 的邏輯拆出來做成 useCounter,順便使用 Composition API 實作!

新增 components/Counter.tsx

import { defineComponent, ref } from 'vue';

export const useCounter = (step: () => number) => {
  const count = ref(0)

	const add = () => count.value += step()
	const current = () => count.value

	return {
		add, current,
	}
}

export default defineComponent({
	name: 'Counter',

  setup(props) {
		const { add, current } = useCounter(() => 1)
    const handleButtonClick = () => add()

 ****   return () => (
      <button onClick={handleButtonClick}>count is: { current() }</button>
    )
  }
})

更新 components/HelloWorld.tsx 使用 components/Counter.tsx

import { defineComponent } from 'vue';
import Counter from './Counter';

export default defineComponent({
  props: {
    msg: String
  },

  setup(props) {
    return () => (
      <>
        <h1>{props.msg}</h1>
        <Counter></Counter>
        <p>Edit <code>components/HelloWorld.tsx</code> to test hot module replacement.</p>
      </>
    )
  }
})