隨著Vue3和TypeScript的大浪潮不斷襲來,越來越多的Vue項目採用了TypeScript的語法來編寫代碼,而Vue3的JS中的Setup語法糖也越來越廣泛的使用,給我們這些以前用弱類型的JS語法編寫Vue代碼的人不少衝擊,不過隨著大量的學習和代碼編寫,經歷過一段難熬的時間後,逐步適應了這種... ...
隨著Vue3和TypeScript的大浪潮不斷襲來,越來越多的Vue項目採用了TypeScript的語法來編寫代碼,而Vue3的JS中的Setup語法糖也越來越廣泛的使用,給我們這些以前用弱類型的JS語法編寫Vue代碼的人不少衝擊,不過隨著大量的學習和代碼編寫,經歷過一段難熬的時間後,逐步適應了這種和之前差別不小的寫法和衝擊。本篇隨筆介紹總結了Vue3中一些常見的基於TypeScript的Setup語法與組合式 API的處理代碼案例。
TypeScript(簡稱ts)是微軟推出的靜態類型的語言,相比於js,TypeScript擁有強類型、編譯器嚴謹的語法檢查、更加嚴苛的語法,TypeScript 是 JS類型的超集,並支持了泛型、類型、命名空間、枚舉等特性,彌補了 JS 在大型應用開發中的不足。TypeScript 是 JavaScript 的強類型版本,最終在瀏覽器中運行的仍然是 JavaScript,所以 TypeScript 並不依賴於瀏覽器的支持,也並不會帶來相容性問題。
基於TypeScript的Setup語法糖寫法越來越多,熟練使用的話,需要一個學習過程,另外ElementPlus控制項也有了一些不同的變化,而且它的官方案例代碼基本上採用了Setup語法糖的寫法來提供例子代碼。
<script setup lang="ts"> 是在單文件組件 (SFC) 中使用組合式 API 的編譯時語法糖。script-setup
弱化了vue模板式編程體驗,也使得代碼更簡潔。
1、定義組件或者頁面名稱
由於組合式API的特殊性,組件裡面的各項內容可以分開進行定義,同時藉助一些輔助函數進行處理。如這裡定義組件或者頁面名稱,通過使用defineOptions進行聲明。
<script setup lang="ts"> import { reactive, ref, onMounted, watch, computed } from "vue"; defineOptions({ name: "MyDictdata" }); //定義組件或頁面名稱
如果是組件,通過這樣定義後,我們在頁面引入它的時候,就可以import這個名稱就可以了,如下代碼所示。
// 自定義字典控制項 import MyDictdata from "./src/my-dictdata.vue";
這樣我們在頁面中就可以和其他HTML標簽一樣使用這個組件了。
<my-dictdata v-model="editForm.nationality" type-name="民族" />
2、data屬性定義
不管是Vue 頁面還是組件,我們都需要設置一些屬性信息,並提供一些初始化值,以前這些在選項式代碼中的時候,是在data塊中定義的,採用了<script setup lang="ts">語法後,任何在裡面定義的信息,在當前頁面或者組件的模板裡面都是公開,可以訪問的。
我們可以使用ref或者 reactive 來定義不同類型的,ref針對的是簡單類型,reactive 針對的是對象類型,它們底層的實現是一樣的,ref的參數增加了一個value的屬性。
let expandMore = ref(false); //是否展開更多條件 let list = ref([]); // 頁面列表數據 let listSelection = ref([]); // 選中記錄 let loading = ref(true); // 載入狀態 let sorting = ref(""); // 排序條件 // 分頁條件 let pageInfo = reactive({ pageIndex: 1, pageSize: 20, totalCount: 0 });
這些信息可以在HTML頁面中直接引用使用即可。
<!--分頁部分 --> <div class="block" style="height: 70px"> <el-pagination background :current-page="pageInfo.pageIndex" :page-size="pageInfo.pageSize" :total="pageInfo.totalCount" :page-sizes="[10, 20, 30, 40]" layout="total, sizes, prev, pager, next,jumper" @size-change="sizeChange" @current-change="currentChange" /> </div>
不過記得,如果是在JS裡面引用對象,那麼記得加上.value的屬性,才能設置或者訪問它。
3、表單或者組件的ref引用
有時候,需要通過在頁面的ref=“form” 來引用一些表單或者組件的名稱,那麼就需要初始化相關的類型的,如下代碼所示。
const searchRef = ref<FormInstance>(); //表單引用
而這個需要引入對應的類型的。
import { FormInstance, FormRules } from "element-plus";
這樣我們在HTML模板中就可以使用它的名稱了。
而對於自定義組件的話,如果需要嚴謹類型的處理,一般也需要約束對應的類型,我們如果需要反射某個特定組件的類型,那麼也可以使用InstanceType的關鍵字來處理,如下代碼所示。
<script lang="ts" setup> import { ref } from 'vue' import { ElTree } from 'element-plus' const treeRef = ref<InstanceType<typeof ElTree>>()
這樣在調用相關介面方法的時候,就有Typescript的只能提示,代碼更加健壯了。
通過InstanceType這樣方式獲得的ref引用,會顯示組件很多公開的屬性和介面方法,如下圖所示。
我們也可以單獨定義一個類型,用來約束自定義組件的方法或者屬性,如下我們定義一個視圖類型組件,只有一個show方法。
我們在<script setup lang="ts">的頂部export一個介面定義,然後再在下麵使用 defineExpose 暴露組件屬性和方法,這樣就可以在組件的引用的地方調用這些方法了。
<script setup lang="ts"> //組件的介面類型 export interface ExposeViewType { show(id?: string | number): Function; } //顯示視窗 const show = (id: string | number) => { if (!isNullOrUnDef(id)) { testuser.Get(id).then(data => { Object.assign(viewForm, data); isVisible.value = true; //顯示對話框 }); } }; //暴露組件屬性和方法 defineExpose({ show });
這樣我們在頁面中定義這個自定義組件的引用的時候,除了使用InstanceType之外,還可以使用自定義的類型聲明瞭。
<!--查看詳細組件界面--> <view-data ref="viewRef" />
在<script setup lang="ts">裡面定義對應引用的類型。
const viewRef = ref<ExposeViewType | null>(); //查看表單引用
這樣我們就可以在代碼中查看它的對外公佈的方法信息了。
4、組件prop屬性定義
在我們開發自定義組件的時候,我們往往需要定義很多父傳子的屬性,也叫作prop屬性定義。
prop屬性定義,是通過defineProps函數進行處理的,這個defineProps()
巨集函數支持從它的參數中推導類型,定義的代碼如下所示。
<script setup lang="ts"> const props = defineProps<{ foo: string bar?: number }>() </script>
我們也可以將 prop 的類型移入一個單獨的介面中:
<script setup lang="ts"> interface Props { foo: string bar?: number } const props = defineProps<Props>() </script>
有時候,我們還需要給指定的prop屬性給定預設值,那麼也可以通過函數withDefaults一起進行處理即可。
如下麵是我們指定模塊定義的prop介面信息和defineProps的處理代碼。
<script setup lang="ts"> import { reactive, ref, onMounted, watch, computed} from vue"; //定義組件名稱 defineOptions({ name: "MyDictdata" }); //聲明Props的介面類型 interface Props { placeholder?: string; // 空白提示 typeName?: string; // 字典類型方式,從後端字典介面獲取數據 options?: Array<TreeNodeItem>; // 固定列表方式,直接綁定,項目包括id,label屬性 modelvalue?: string | number; // 接受外部v-model傳入的值 clearable?: boolean; // 是否可以清空 disabled?: boolean; // 是否禁用 multiple?: boolean; // 是否多選 } //使用預設值定義Props const props = withDefaults(defineProps<Props>(), { placeholder: "請選擇", typeName: "", options: () => { return []; }, clearable: true, disabled: false, multiple: false, modelValue: "" //對應自定義控制項的v-model的值 });
這樣我們在使用的時候,就可以傳入給組件對應的prop名稱了。
<el-form-item label="民族" prop="nationality"> <my-dictdata v-model="editForm.nationality" type-name="民族" /> </el-form-item>
5、Emits事件聲明
在組件裡面,我們拋出事件,通過在Emits中進行聲明,再行使用。
聲明事件在setup語法裡面也是和其他巨集函數一樣,如下代碼所示。
// 聲明事件 const emit = defineEmits(['updateName'])
如果為了更強的指定事件的參數和返回值等信息,我們也可以通過定義介面然後在聲明Emits的方式,如下代碼所示。
//聲明控制項事件 interface Emits { (e: "update:modelValue", value: string): void; (e: "change", value: string): void; } //定義控制項事件 const emit = defineEmits<Emits>();
或者直接整合一起聲明。
// 基於類型的聲明 const emit = defineEmits<{ (e: 'change', id: number): void (e: 'update', value: string): void }>()
然後在組件的函數中觸發事件,通知父頁面即可。
function change(data) {
const obj = dictItems.value.find(item => {
return item.id + "" === data;
});
emit("change", obj);
}
這樣我們在頁面使用組件的時候,HTML模板中使用的組件代碼裡面,可以獲得獲得對應的事件處理。
<el-form-item label="狀態" prop="state"> <my-dictdata v-model="searchForm.state" :options="Status" @change="change" /> </el-form-item>
6、Computed計算函數的使用
「computed」 是Vue中提供的一個計算屬性。它被混入到Vue實例中,所有的getter和setter的this上下文自動的綁定為Vue實例。
<script setup lang="ts"> import { computed, ref } from 'vue' const count = ref(1) // 通過computed獲得doubleCount const doubleCount = computed(() => { return count.value * 2 }) // 獲取 console.log(doubleCount.value) </script>
7、Watch函數的使用
有時候,子組件需要監控自身某個值的變化,然後進行相關的處理,那麼對值進行監控就需要用到了watch函數。
// 監聽外部對props屬性變更,如通過ResetFields()方法重置值的時候 watch( () => props.modelValue, newValue => { console.log(newValue); emit("update:modelValue", newValue + ""); } ); watch( () => props.options, newValue => { newValue.forEach(item => { dictItems.value.push(item); }); } );
8、onMounted函數的使用
我們一般在 onMounted 的邏輯裡面準備好組件或者頁面顯示的內容,這裡面在頁面組件準備妥當後進行更新顯示。
//頁面初始化載入 onMounted(() => { getlist(); });
或者組件裡面
//掛載的時候初始化數據 onMounted(async () => { var typeName = props.typeName; var options = props.options; if (typeName && typeName !== "") { // 使用字典類型,從伺服器請求數據 await dictdata.GetTreeItemByDictType(typeName).then(list => { if (list) { list.forEach(item => { dictItems.value.push({ id: item.id, label: item.label }); }); } }); } else if (options && options.length > 0) { // 使用固定字典列表 options.map(item => { dictItems.value.push({ id: item.id, label: item.label }); }); } // 設置預設值 keyword.value = props.modelValue; });
9、自定義組件的ModelValue
一般組件在綁定值的時候,一般使用v-Model的屬性來設置它的值。
<el-form-item label="姓名" prop="name"> <el-input v-model="editForm.name" /> </el-form-item>
或者日期組件
<el-form-item label="出生日期" prop="birthDate"> <el-date-picker v-model="editForm.birthDate" align="right" type="date" placeholder="選擇日期" format="YYYY-MM-DD" /> </el-form-item>
因此我們自定義開發的組件,也應該採用這樣約定的屬性。這裡面的v-Model對應的prop屬性就是modelValue的,因此我們需要定義這個屬性,並處理Emits事件就可以了。
//聲明Props的介面類型 interface Props { modelvalue?: string | number; // 接受外部v-model傳入的值 } //使用預設值定義Props const props = withDefaults(defineProps<Props>(), { modelValue: "" //對應自定義控制項的v-model的值 });
然後聲明組件的事件,在組件內部合適的地方觸發即可。
//聲明組件事件 interface Emits { (e: "update:modelValue", value: string): void; (e: "change", value: string): void; } //定義組件事件 const emit = defineEmits<Emits>();
併在Watch監控它的變化,觸發組件的自定義事件
watch( () => props.modelValue, newValue => { console.log(newValue); emit("update:modelValue", newValue + ""); } );
10、自定義引入Vue的API和組件
上面所有的setup語法糖代碼裡面,我們在開始的時候,往往都需要引入ref,reactive等API,如下代碼所示。
<script setup lang="ts"> import { reactive, ref, onMounted, watch, computed } from "vue";
那麼每次引入局的麻煩的話,可以通過使用https://github.com/antfu/unplugin-auto-import 這個插件來實現自動引入這些配置信息,這樣每次就可以省卻一些定義代碼了。
這樣在使用ref,reactive的時候,不用引入就直接使用,如下代碼所示。
const count = ref(0)
const doubled = computed(() => count.value * 2)
安裝組件,直接通過下麵npm 或者pnmp進行安裝即可。
npm i -D unplugin-auto-import
它提供了Vite、WebPack等編譯器的集成,可以參考官網進行修改。
如Vite的配置處理如下所示。
// vite.config.ts import AutoImport from 'unplugin-auto-import/vite' export default defineConfig({ plugins: [ AutoImport({ /* options */ }), ], })
然後對Typescript和ESLint進行修改配置一下就可以一勞永逸了(具體參考官網的說明),希望下個版本的vue能自動不用引入這些API就好了。
以上就是我們在<script setup lang="ts">語法中經常涉及到的一些常用的知識和代碼案例了。
專註於代碼生成工具、.Net/.NetCore 框架架構及軟體開發,以及各種Vue.js的前端技術應用。著有Winform開發框架/混合式開發框架、微信開發框架、Bootstrap開發框架、ABP開發框架、SqlSugar開發框架等框架產品。轉載請註明出處:撰寫人:伍華聰 http://www.iqidi.com