Vite+Vue2+Composition-api+<script setup>+TypeScript搭配如何開發(fā)項目?
前言
Vite相信大家都用過,它是一種新型前端開發(fā)與構(gòu)建工具,能夠顯著提升前端開發(fā)體驗。我們在搭建Vite項目,選擇Vue模板之后,默認(rèn)會下載Vue3模板。如果你的公司現(xiàn)在還沒有準(zhǔn)備使用Vue3,而在使用Vue2,那么這篇文章值得你繼續(xù)看下去。下面,我將帶大家如何搭建一個 Vite+Vue2+Composition-api+<script setup>+TypeScript 搭配使用的項目。這篇文章很干,請大家點點贊哦!
安裝所需依賴
又到了實戰(zhàn)環(huán)節(jié),下面可以一步步跟著我哦!我這里使用的是yarn 依賴管理工具。
初始化項目
這里使用快捷初始化命令:
- yarn init -y
創(chuàng)建完package.json文件之后,我們可以手動修改下項目名稱字段name:vitevue2p。
初始化Vite
安裝Vite。
- yarn add vite -D
初始化Vue2
我們需要安裝Vue2,所以直接這樣安裝。
- yarn add vue
目前,我安裝的版本是^2.6.14。
另外,我們還需要安裝vue-template-compiler這個依賴,此包可用于將Vue 2.0模板預(yù)編譯為渲染函數(shù),以避免運行時編譯開銷和CSP限制。在編寫具有非常特定需求的構(gòu)建工具時,才需要單獨使用它。所以,我們這里單獨安裝。
- yarn add vue-template-compiler -D
最后,如果想讓Vite支持Vue2,就必須安裝這個依賴vite-plugin-vue2。
- yarn add vite-plugin-vue2 -D
支持Composition-api
Composition-api字面意思是組合API,它是為了實現(xiàn)基于函數(shù)的邏輯復(fù)用機制而產(chǎn)生的。這也是Vue3亮點之一,那么我們?nèi)绾尾拍軌蛟赩ue2項目中使用呢?這需要安裝@vue/composition-api依賴。
- yarn add @vue/composition-api
支持<script setup>語法
<script setup>是在單文件組件 (SFC) 中使用組合式 API 的編譯時語法糖,是Vue3.2新加入的語法。那么,我們也可以在Vue2項目中使用它。
你需要安裝unplugin-vue2-script-setup依賴。
- yarn add unplugin-vue2-script-setup -D
了解更多,可以查看https://github.com/antfu/unplugin-vue2-script-setup。
在Vue2項目中使用Volar
以下是官方的解釋:
我們建議將 VS Code 與 Volar 結(jié)合使用以獲得最佳體驗(如果您擁有 Vetur,您可能希望禁用它)。使用 Volar 時,您需要安裝 @vue/runtime-dom 作為 devDependencies 以使其在 Vue 2 上工作。
- yarn add @vue/runtime-dom -D
支持TypeScript語法
隨著應(yīng)用的增長,靜態(tài)類型系統(tǒng)可以幫助防止許多潛在的運行時錯誤,所以我們推薦使用TypeScript。
- yarn add typescript -D
最后,我把安裝的所有依賴列出來,可以參照有沒有漏的。
- "dependencies": {
- "@vue/composition-api": "^1.1.5",
- "vue": "^2.6.14"
- },
- "devDependencies": {
- "@vue/runtime-dom": "^3.2.11",
- "typescript": "^4.4.3",
- "unplugin-vue2-script-setup": "^0.6.4",
- "vite": "^2.5.7",
- "vite-plugin-vue2": "^1.8.1",
- "vue-template-compiler": "^2.6.14"
- }
搭建項目架構(gòu)
首先,我先列出我自己搭建的項目文件目錄,我是參照Vite默認(rèn)模板而創(chuàng)建的文件目錄。
- - public
- -- favicon.ico
- - src
- -- assets
- --- logo.png
- -- components
- --- Async.vue
- --- Bar.vue
- --- Foo.vue
- --- HelloWorld.vue
- -- App.vue
- -- main.ts
- -- shims-vue.d.ts
- - index.html
- - package.json
- - ref-macros.d.ts
- - tsconfig.json
- - vite.config.ts
下面,我們按排列順序分別看下文件中都放了什么東西?
public文件夾中放著一個ico圖標(biāo)文件,這個不再說明。src文件夾中文件有點多,我們放在最后討論。
index.html
談到index.html這個文件,我們需要引入Vite官網(wǎng)一段話:
你可能已經(jīng)注意到,在一個 Vite 項目中,index.html 在項目最外層而不是在 public 文件夾內(nèi)。這是有意而為之的:在開發(fā)期間 Vite 是一個服務(wù)器,而 index.html 是該 Vite 項目的入口文件。
Vite 將 index.html 視為源碼和模塊圖的一部分。Vite 解析 <script type="module" src="..."> ,這個標(biāo)簽指向你的 JavaScript 源碼。甚至內(nèi)聯(lián)引入 JavaScript 的 <script type="module"> 和引用 CSS 的 <link href> 也能利用 Vite 特有的功能被解析。另外,index.html 中的 URL 將被自動轉(zhuǎn)換,因此不再需要 %PUBLIC_URL% 占位符了。
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <link rel="icon" href="/favicon.ico" />
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Vite App</title>
- </head>
- <body>
- <div id="app"></div>
- <script type="module" src="/src/main.ts"></script>
- </body>
- </html>
package.json
這個文件定義了這個項目所需要的各種模塊,以及項目的配置信息(比如名稱、版本、許可證等元數(shù)據(jù))。這里,需要注意的是我們自定義了"scripts"字段,有三個命令:"vite --open"、"vite preview"、"vite build"。
- {
- "name": "vitevue2p",
- "version": "0.1.1",
- "description": "",
- "keywords": [],
- "license": "MIT",
- "main": "dist/index.js",
- "module": "dist/index.mjs",
- "scripts": {
- "dev": "vite --open",
- "serve": "vite preview",
- "build": "vite build"
- },
- "dependencies": {
- "@vue/composition-api": "^1.1.5",
- "vue": "^2.6.14"
- },
- "devDependencies": {
- "@vue/runtime-dom": "3.2.11",
- "typescript": "^4.4.3",
- "unplugin-vue2-script-setup": "^0.6.4",
- "vite": "^2.5.7",
- "vite-plugin-vue2": "^1.8.1",
- "vue-template-compiler": "^2.6.14"
- }
- }
ref-macros.d.ts
以d.ts后綴結(jié)尾的是TypeScript中的類型定義文件。我們知道自從引入 Composition API 以來,一個主要未解決的問題是 refs 與reactive的使用,到處使用 .value可能很麻煩,如果不使用類型系統(tǒng),很容易錯過。一些用戶特別傾向于只使用reactive,這樣他們就不必處理refs。
為了優(yōu)化,官方提出了一個RFC,大家可以打開下面這個網(wǎng)址 https://github.com/vuejs/rfcs/discussions/369 了解一下。
下面,可以看下一個簡單的例子。
- // declaring a reactive variable backed by an underlying ref
- let count = $ref(1)
- // no need for .value anymore!
- console.log(count) // 1
- function inc() {
- // assignments are reactive
- count++
- }
另外,這是一項實驗性功能。實驗性功能可能會改變補丁版本之間的行為。建議將您的 vue 依賴項固定到確切的版本以避免損壞。
言歸正傳,我們來看下ref-macros.d.ts文件中的內(nèi)容。
- import type {
- Ref,
- UnwrapRef,
- ComputedRef,
- WritableComputedOptions,
- WritableComputedRef,
- ShallowUnwrapRef,
- } from '@vue/composition-api'
- declare const RefMarker: unique symbol
- type RefValue<T> = T & { [RefMarker]?: any }
- declare const ComputedRefMarker: unique symbol
- type ComputedRefValue<T> = T & { [ComputedRefMarker]?: any }
- declare const WritableComputedRefMarker: unique symbol
- type WritableComputedRefValue<T> = T & { [WritableComputedRefMarker]?: any }
- type ToRawRefs<T extends object> = {
- [K in keyof T]: T[K] extends ComputedRefValue<infer V>
- ? ComputedRefValue<V>
- : T[K] extends WritableComputedRefValue<infer V>
- ? WritableComputedRef<V>
- : T[K] extends RefValue<infer V>
- ? Ref<V>
- : T[K] extends object
- ? T[K] extends
- | Function
- | Map<any, any>
- | Set<any>
- | WeakMap<any, any>
- | WeakSet<any>
- ? T[K]
- : ToRawRefs<T[K]>
- : T[K];
- }
- /**
- * Vue ref transform macro for binding refs as reactive variables.
- */
- declare function _$<T>(arg: ComputedRef<T>): ComputedRefValue<T>
- declare function _$<T>(
- arg: WritableComputedRef<T>
- ): WritableComputedRefValue<T>
- declare function _$<T>(arg: Ref<T>): RefValue<T>
- declare function _$<T extends object>(arg?: T): ShallowUnwrapRef<T>
- /**
- * Vue ref transform macro for accessing underlying refs of reactive varaibles.
- */
- declare function _$$<T>(value: T): ComputedRef<T>
- declare function _$$<T>(
- value: WritableComputedRefValue<T>
- ): WritableComputedRef<T>
- declare function _$$<T>(value: RefValue<T>): Ref<T>
- declare function _$$<T extends object>(arg: T): ToRawRefs<T>
- declare function _$ref<T>(arg?: T | Ref<T>): RefValue<UnwrapRef<T>>
- declare function _$shallowRef<T>(arg?: T): RefValue<T>
- declare function _$computed<T>(
- getter: () => T,
- // debuggerOptions?: DebuggerOptions
- ): ComputedRefValue<T>
- declare function _$computed<T>(
- options: WritableComputedOptions<T>,
- // debuggerOptions?: DebuggerOptions
- ): WritableComputedRefValue<T>
- declare global {
- const $: typeof _$
- const $$: typeof _$$
- const $ref: typeof _$ref
- const $shallowRef: typeof _$shallowRef
- const $computed: typeof _$computed
- }
tsconfig.json
tsconfig.json文件中指定了用來編譯這個項目的根文件和編譯選項。
我們這里需要注意如果您的 IDE 缺少全局類型。
- {
- "compilerOptions": {
- "types": [
- "unplugin-vue2-script-setup/types"
- ]
- }
- }
Volar 優(yōu)先支持 Vue 3。Vue 3 和 Vue 2 模板有些不同。您需要設(shè)置 ExperimentCompatMode 選項以支持 Vue 2 模板。
- {
- "compilerOptions": {
- ...
- },
- "vueCompilerOptions": {
- "experimentalCompatMode": 2
- },
- }
最后,文件內(nèi)容如下:
- {
- "compilerOptions": {
- "target": "es2017",
- "module": "esnext",
- "moduleResolution": "node",
- "esModuleInterop": true,
- "strict": true,
- "strictNullChecks": true,
- "resolveJsonModule": true,
- "types": [
- "unplugin-vue2-script-setup/types"
- ]
- },
- "vueCompilerOptions": {
- "experimentalCompatMode": 2
- }
- }
vite.config.ts
這個文件是Vite的配置文件。當(dāng)以命令行方式運行 vite 時,Vite 會自動解析項目根目錄下名為 vite.config.js(或vite.config.ts) 的文件。
這里需要注意 refTransform 現(xiàn)在是插件根級選項,需要手動定義為true。(為什么配置refTransform,可以看上面ref-macros.d.ts文件中對refs處理,不使用.value的介紹)。
另外,如果想支持<script setup>語法,必須在這里以插件的形式配置。
- import { defineConfig } from 'vite'
- import { createVuePlugin as Vue2 } from 'vite-plugin-vue2'
- import ScriptSetup from 'unplugin-vue2-script-setup/vite'
- export default defineConfig({
- plugins: [
- Vue2(),
- ScriptSetup({
- refTransform: true,
- }),
- ],
- })
介紹完這些文件,剩下的就是src文件夾中的文件了,因為文件過多,我們把它單獨放在Src文件夾欄目中。
Src文件夾
assets文件中只有l(wèi)ogo.png一個圖片,你可以把靜態(tài)文件放在當(dāng)中,這里不多過介紹。
main.ts
這是Vue2的入口文件,我們可以看到這里VueCompositionAPI被當(dāng)做插件引入。另外,我們引入的App.vue以及其他*.vue為后綴的文件,需要有專門的類型定義文件進(jìn)行聲明,在下面的shims-vue.d.ts文件中我們會講到。
- import Vue from 'vue'
- import VueCompositionAPI from '@vue/composition-api'
- import App from './App.vue'
- Vue.use(VueCompositionAPI)
- const app = new Vue({ render: h => h(App) })
- app.$mount('#app')
shims-vue.d.ts
- declare module '*.vue' {
- import Vue from 'vue'
- export default Vue
- }
App.vue
這個文件是頁面入口文件。我們來看下它是如何寫的,這是Vue2項目,但是寫法與Vue3項目無異,只不過在Vue2項目中需要'@vue/composition-api'使用Composition-api,而Vue3項目直接引入vue。
另外,這里看到我們直接使用<script setup>語法,替換了之前setup()方法,使代碼更簡潔。還有我們可以直接引入組件,直接在模板中使用。
更多關(guān)于<script setup>語法的內(nèi)容可以看看https://v3.cn.vuejs.org/api/sfc-script-setup.html,了解更多使用方法。
- <template>
- <div id="app">
- <img alt="Vue logo" src="./assets/logo.png">
- <hello-world name="Vue 2 + TypeScript + Vite" @update="onUpdate" />
- <async-component />
- </div>
- </template>
- <script setup lang="ts">
- import { defineAsyncComponent } from '@vue/composition-api'
- import HelloWorld from './components/HelloWorld.vue'
- const AsyncComponent = defineAsyncComponent(() => import('./components/Async.vue'))
- function onUpdate(e: any) {
- console.log(e)
- }
- </script>
- <script lang="ts">
- export default {
- name: 'App',
- }
- </script>
- <style>
- #app {
- font-family: Avenir, Helvetica, Arial, sans-serif;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
- text-align: center;
- color: #2c3e50;
- margin-top: 60px;
- }
- </style>
HelloWorld.vue
然后,我們再看下這個文件中什么內(nèi)容。這里需要注意的是$ref()、$computed()方法,這就是之前提到的refTransform語法,不得不說,這比以前使用.value處理方便多了。
- <template>
- <div>
- <h1>{{ msg }}, {{ name }}</h1>
- <button @click="inc">
- Inc
- </button>
- <div>{{ count }} x 2 = {{ doubled }}</div>
- <button @click="dec()" v-html="decText" />
- <component :is="count > 2 ? Foo : Bar" />
- </div>
- </template>
- <script setup lang="ts">
- import { watch } from '@vue/composition-api'
- import Foo from './Foo.vue'
- import Bar from './Bar.vue'
- const props = withDefaults(defineProps<{ msg: string; name: string | number }>(), { msg: 'Hello' })
- const emit = defineEmits(['update'])
- let count = $ref(1)
- // eslint-disable-next-line prefer-const
- let doubled = $computed(() => count * 2)
- function inc() {
- count += 1
- }
- function dec() {
- count -= 1
- }
- const decText = '<b>Dec</b>'
- watch(()=>count, value => emit('update', value))
- </script>
- <style scoped>
- button{
- margin: 20px 0;
- }
- </style>
其他文件就不過多介紹了,就只是簡單的模板文件。
Foo.vue
- <template>
- <div>Foo</div>
- </template>
Bar.vue
- <template>
- <div>Bar</div>
- </template>
Async.vue
- <template>
- <div>Async Component</div>
- </template>
結(jié)語
最后,我們啟動下項目。
- yarn dev
如上圖所示,啟動成功。
相信這樣可以在一定程度上提升你 Vue 2 的開發(fā)體驗,趕快來!
以下是本篇文章的源碼地址:
- https://github.com/maomincoding/viteVue2p
如果覺得這篇文章對你有幫助,感謝點贊哦~
本文轉(zhuǎn)載自微信公眾號「前端歷劫之路」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系前端歷劫之路公眾號。