[Vue Note] - Vue 佐 JSX/TSX,在 JS 中切版吧

Introduction & 前言

CASL 權限管理系統

你是否接觸過 React 呢?在 JavaScript 裡面寫 HTML 的格式已經很習慣了吧!

如果你正在接觸 Vue 但是苦惱怎麼在 Script 裡寫 JSX,可以參考一下這篇文章。

如果你正初學 Vue.js 或者還沒接觸框架也可以參考一下,後續會介紹 JSX 是什麼。

1
const element = <h1>你好,世界!</h1>;

『這個有趣的標籤語法不是一個字串也不是 HTML。』- React Document

React 文件裡是這麼介紹的,讓我偷懶引用一下

這個語法叫做 JSX,是一個 JavaScript 的語法擴充。我們推薦你在寫 React 的時候透過這個語法來描述使用者介面的外觀。 JSX 可能為讓你想到一些樣板語言,但不一樣的地方是 JSX 允許你使用 JavaScript 所有的功能。


Summary & 摘要

本篇文章預設學習前的基本條件需求:

  • 需要會創建及使用 Vue (了解基本的用法)
  • 需要會 Npm 用法
  • 需要了解 JSX 是什麼

本篇文章將會學到:

  1. 基礎認識 JSX/TSX
  2. 在 Vue 裡使用 JSX/TSX

備註:如果你真的不了解 JSX 是什麼,下面會簡單介紹,但如果你已經了解了,可以跳過基礎認識


為何要寫這篇

筆者約莫在幾年前斷斷續續的利用閒暇時間做自己的 Side Project,在這期間經歷了換工作的時期,因為第一份工作筆者最早接觸的框架是 Vue.js,所以在筆者想好好了解前後端怎麼運作的時候,決定前端框架使用 Vue.js

筆者在結束第一份工作後,當時大部分的工程師普遍都認為 Vue.js 易學好入門,但是大部分大公司都還是採用 React.js,所以業界大部分其實還是後者框架居多。

在筆者進入第二份工作時,也被告知公司是會採用 React.js,所以筆者其實後來就一直處在使用 React.js 的時間居多,剛開始著實不太習慣,但寫久了也能慢慢抓到兩個框架各自的優點。

這邊不討論兩者框架的優缺點,普遍使用 React.js 的人應該(絕對吧?)都不能避免接觸 JSX

就在筆者學習了一陣子 React.js 後回頭來繼續完成 Side Project 時,想要客製化自己的 Components,並且傳入 HTML,這時候筆者發現以前寫了兩年多的 Vue.js,我竟然從來沒有這樣傳遞過,霎那間覺得 Vue.js 可以在 Script 裡面寫 HTML 該有多好啊!

顧這篇文章就用來記錄我怎麼在 Vue.js 使用 JSX 的吧,老樣子希望能幫到你。

備註:JSX 及 TSX 是一樣的東西,差別在於後者加入了 TypeScript,如果不了解 TypeScript 是什麼可以參考 TypeScript 新手指南,簡單說 TypeScript 就是 JavaScript 的超集,JavaScript 屬於弱型別,當今天我們賦予一個變數字串,我們可以隨時把這個變數改為數字型別,但如果今天使用了 TypeScript 就會報錯。


基礎認識 JSX/TSX

JSX 編譯過遲

這邊我們借用 React.js 的渲染過程來解釋,大部分的人接觸到 JSX 應該都是從接觸 React.js 開始,但為什麼我們要使用 JSX 呢?

  1. 關注點分離

React.js介紹文章有提到,以往我們前端開發都是 HTML、JS、CSS 去做開發,但 React.js 是以 components 做為開發核心,每一個 components 包含這些東西,內容可以是純字串,也可以是包含邏輯的。

剛開始接觸或者還沒接觸 React.js 的人可能不能接受將標籤與法寫在 JavaScript 裡面,但確實這種包再一起的方式可以讓你把關注點鎖在商業邏輯內。

舉例來說下面一段 React.js 程式碼就是在寫一個 component,裡面包涵畫面的呈現,這個 component 主要用於顯示使用者的資訊。

不了解下面程式碼的用法沒關係,只要瀏覽過知道為什麼要用 TSX 就行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
interface IUser {
name: string;
age: number;
}

const UserComponent = ({
userInfo
}: IUser) => {
return (
<div>
<h2>User Info:</h2>
<ul>
<li>name: {userInfo.name}</li>
<li>age: {userInfo.age}</li>
</ul>
</div>
)
};

上面的這串 TSX 會被編譯為下面程式碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { jsx as _jsx } from "react/jsx-runtime";
import { jsxs as _jsxs } from "react/jsx-runtime";

const UserComponent = ({
userInfo
}) => {
return /*#__PURE__*/_jsxs("div", {
children: [/*#__PURE__*/_jsx("h2", {
children: "User Info:"
}), /*#__PURE__*/_jsxs("ul", {
children: [/*#__PURE__*/_jsxs("li", {
children: ["name: ", userInfo.name]
}), /*#__PURE__*/_jsxs("li", {
children: ["age: ", userInfo.age]
})]
})]
});
};

編譯網站可參考 Babeljs.io

你能發現我們只要專注在處理 顯示使用者資訊 這件事情上即可,雖然剛開始你會納悶為什麼東西都塞成一大包,但透過上面的轉譯我們能知道 TSX 幫我們處理了 JavaScript 的這一段,如果你曾經寫過原生 JavaScript,一定不能忘記頻繁的操作 DOM 吧 (淚!

1
2
3
4
5
6
7
8
9
10
11
12
const userBox = document.getElementById("userBox");
const user = {
name: 'Rex',
age: 18
};

userBox.innerHTML = `
<ul>
<li>name: ${user.name}</li>
<li>age: ${user.age}</li>
</ul>
`;

別忘了寫完 Javascript 邏輯你還要去處理 HTML 畫面呢…

  1. 提供更加語意化且易懂的標籤

一般我們處理一些顯示邏輯,會像下方使用 命令式(Imperative) 去做判斷:

假如我們需要判斷使用者成年沒去相對應顯示一些適合的東西

1
2
3
4
5
if (userIsAdult(user)) {
canSeeSomeExcitingThing();
} else {
showYouNotAToughGuy();
}

比遊戲還刺激

但如果我們透過 聲明式(Declarative) 則是:

1
2
3
4
5
if (this.state.userIsAdult) {
return (<ExcitingComponent />);
} else {
return (<ChildrenComponent />);
}

是不是更佳清晰易懂了呢?

  1. 與 JavaScript 語法碰出新滋味

JSX 非一種全新的語言,而是一種語法糖,有點類似 ES6/7/8 那種語法糖,如果你寫過 Vue.js,可以想像今天你在使用 v-for、v-if 的概念,你能處理畫面的同時,又能處理商業邏輯。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
const users = [
{
id: 0,
name: 'Rex',
age: 18
},
{
id: 1,
name: 'Mike',
age: 32
},
{
id: 2,
name: 'Joanne',
age: 24
}
];

interface IUser {
id: number;
name: string;
age: number;
}

const UserComponent = ({
users
}: IUser[]) => {
return (
<div>
<h2>Users Info:</h2>
<ul>
{
users.map((user) => (
<Fragment key={user.id}>
<li>name: {user.name}</li>
<li>age: {user.age}</li>
</>
))
}
</ul>
</div>
)
};

說了這麼多也許你還不能體驗到優點,沒關係我們就開始安裝使用吧!


在 Vue 裡使用 JSX/TSX

想在 Vue.js 裡面使用 JSX/TSX 其實很簡單,甚至在一般的 HTML 你也能使用,不過我們先來講講今天的重點 Vue.js 如何使用吧。

一般的 .vue 檔案內容大概會長這樣:

這邊容許我使用 Vue3 作為示範,但不會影響我們使用的過程及結果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<template>
<div>
<span>我是內容</span>
</div>
</template>

<script>
import { defineComponent } from 'vue';

export default defineComponent({
name: 'line-message-template-preview',
setup() {
return {
user: {
name: 'Rex',
age: 18
}
};
},
});
</script>

<style scoped></style>

普通的邏輯處理都還能順利的書寫,但今天我們想要從 script 處取得 API 資訊後,把拿到的資訊包含畫面邏輯再放回 template 渲染呢?

舉例來說,我們今天透過 age 取得使用者的資訊後,我們想要依照使用者的年齡去渲染相對應的畫面顯示,這時候就會有點棘手(其實應該是說要多寫一些程式碼去處理渲染的邏輯部分)。

我們先來看看下面這張圖,這張圖使用的是 Vue + Element-plus

渲染排序邏輯

這段程式碼的範例可以參考 Element-plus 的 Table Document

你可以觀察到 el-table-column 提供了一個 formatter 的方法,裡面可以傳入客製化的內容,但一般的 vue 沒有安裝 JSX 套件下我們只能傳入字串。

像是範例中透過 function formatter() 去做的事情。

1
2
3
const formatter = (row: User, column: TableColumnCtx<User>) => {
return row.address
};

但假如今天我的邏輯想要傳入一些 UI Component 呢?例如我想要加上 Tag UI

這時候如果你把上面的 formatter() 改為這樣可是會出錯的:

1
2
3
4
5
const formatter = (row: User, column: TableColumnCtx<User>) => {
return (
<el-tag>{row.address}</el-tag>
)
};

別急,讓我們來安裝 JSX/TSX 吧!

安裝及設定

  1. 安裝套件

使用 Vue2 請在專案輸入:

1
npm install @vue/babel-preset-jsx @vue/babel-helper-vue-jsx-merge-props

使用 Vue3 請在專案輸入:

1
npm install @vue/babel-plugin-jsx -D
  1. 設置套件

打開專案根目錄的 .babelrc(babel.config.js)(沒有請自行創建),並且加入配置:

使用 Vue2 請配置:

1
2
3
4
5
6
7
8
9
10
11
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset',
[
'@vue/babel-preset-jsx',
{
'injectH': false
}
]
]
};

使用 Vue3 請配置:

1
2
3
module.exports = {
plugins: ['@vue/babel-plugin-jsx'],
};
  1. 使用套件

Vue<script></script> 改為 <script lang="jsx"></script>,如果使用 typescript 請改為 <script lang="tsx"></script>

就是這麼簡單,之後你便可以正常地在 Script 裡面寫一些 UI 邏輯了。

關於 Vue2 的套件可以參考 babel-preset-vue

關於 Vue3 的套件可以參考 babel-plugin-jsx


Conclusion & 結論

雖然文章漏漏長,但大多是想解釋一下 JSX/TSX 是什麼,其實安裝過程簡單到不需要幾分鐘,使用也是很簡單,主要都是透過 Babel 去編譯的,和 ESLint 差不多,其實只要專案有吃到設定,基本上都能正常編譯使用。

上面有提到一些範例,可能不是那麼好懂也不是那麼適合,希望各路前輩包含,也希望這篇文章能幫助到需要的人。

最後想提一下如果今天想在 HTML 裡面使用 JSX/TSX 的話,可以參考 一看就懂的 JSX 簡明入門教學指南 一文的 二、用法摘要,裡面介紹的滿詳細的。


參考網站