前言
Vite相信大家都用过,它是一种新型前端开发与构建工具,能够显著提升前端开发体验。我们在搭建Vite项目,选择Vue模板之后,默认会下载Vue3模板。如果你的公司现在还没有准备使用Vue3,而在使用Vue2,那么这篇文章值得你继续看下去。下面,我将带大家如何搭建一个 Vite+Vue2+Composition-api+<script setup>
+TypeScript 搭配使用的项目。这篇文章很干,请大家点点赞哦!
安装所需依赖
又到了实战环节,下面可以一步步跟着我哦! 我这里使用的是yarn
依赖管理工具。
初始化项目
这里使用快捷初始化命令:
yarn init -y
创建完package.json
文件之后,我们可以手动修改下项目名称字段name
:
vitevue2p。
初始化Vite
安装Vite。
yarn add vite -D
初始化Vue2
我们需要安装Vue2,所以直接这样安装。
yarn add vue
目前,我安装的版本是^2.6.14
。
另外,我们还需要安装vue-template-compiler
这个依赖,此包可用于将Vue 2.0模板预编译为渲染函数,以避免运行时编译开销和CSP限制。在编写具有非常特定需求的构建工具时,才需要单独使用它。所以,我们这里单独安装。
yarn add vue-template-compiler -D
最后,如果想让Vite支持Vue2,就必须安装这个依赖vite-plugin-vue2
。
yarn add vite-plugin-vue2 -D
支持Composition-api
Composition-api
字面意思是组合API,它是为了实现基于函数的逻辑复用机制而产生的。这也是Vue3亮点之一,那么我们如何才能够在Vue2项目中使用呢?这需要安装@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 结合使用以获得最佳体验(如果您拥有 Vetur,您可能希望禁用它)。 使用 Volar 时,您需要安装 @vue/runtime-dom 作为 devDependencies 以使其在 Vue 2 上工作。
yarn add @vue/runtime-dom -D
支持TypeScript语法
随着应用的增长,静态类型系统可以帮助防止许多潜在的运行时错误,所以我们推荐使用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" }
搭建项目架构
首先,我先列出我自己搭建的项目文件目录,我是参照Vite默认模板而创建的文件目录。
- 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图标文件,这个不再说明。src
文件夹中文件有点多,我们放在最后讨论。
index.html
谈到index.html
这个文件,我们需要引入Vite官网一段话:
你可能已经注意到,在一个 Vite 项目中,
index.html
在项目最外层而不是在public
文件夹内。这是有意而为之的:在开发期间 Vite 是一个服务器,而index.html
是该 Vite 项目的入口文件。Vite 将
index.html
视为源码和模块图的一部分。Vite 解析<script type="module" src="...">
,这个标签指向你的 JavaScript 源码。甚至内联引入 JavaScript 的<script type="module">
和引用 CSS 的<link href>
也能利用 Vite 特有的功能被解析。另外,index.html
中的 URL 将被自动转换,因此不再需要%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
这个文件定义了这个项目所需要的各种模块,以及项目的配置信息(比如名称、版本、许可证等元数据)。这里,需要注意的是我们自定义了 "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
后缀结尾的是TypeScript中的类型定义文件。我们知道自从引入 Composition API 以来,一个主要未解决的问题是 refs
与reactive
的使用,到处使用 .value
可能很麻烦,如果不使用类型系统,很容易错过。 一些用户特别倾向于只使用reactive
,这样他们就不必处理 refs
。
为了优化,官方提出了一个RFC,大家可以打开下面这个网址 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
文件中的内容。
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 优先支持 Vue 3。Vue 3 和 Vue 2 模板有些不同。您需要设置 ExperimentCompatMode 选项以支持 Vue 2 模板。
{ "compilerOptions": { ... }, "vueCompilerOptions": { "experimentalCompatMode": 2 }, }
最后,文件内容如下:
{ "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的配置文件。当以命令行方式运行 vite
时,Vite 会自动解析项目根目录下名为 vite.config.js
(或vite.config.ts
) 的文件。
这里需要注意 refTransform
现在是插件根级选项,需要手动定义为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
文件中只有logo.png
一个图片,你可以把静态文件放在当中,这里不多过介绍。
main.ts
这是Vue2的入口文件,我们可以看到这里VueCompositionAPI
被当做插件引入。另外,我们引入的App.vue
以及其他*.vue
为后缀的文件,需要有专门的类型定义文件进行声明,在下面的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()
方法,使代码更简洁。还有我们可以直接引入组件,直接在模板中使用。
更多关于<script setup>
语法的内容可以看看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 + Composition-api" @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
然后,我们再看下这个文件中什么内容。这里需要注意的是$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>
结语
最后,我们启动下项目。
yarn dev
如上图所示,启动成功。
相信这样可以在一定程度上提升你 Vue 2 的开发体验,赶快来!
以下是本篇文章的源码地址:
https://github.com/maomincoding/viteVue2p
如果觉得这篇文章对你有帮助,感谢点赞哦~