前言
Vite相信大家都用过,它是一种新型前端开发与构建工具,能够显著提升前端开发体验。我们在搭建Vite项目,选择Vue模板之后,默认会下载Vue3模板。如果你的公司现在还没有准备使用Vue3,而在使用Vue2,那么这篇文章值得你继续看下去。下面,我将带大家如何搭建一个 Vite+Vue2+Composition-api+<script setup>+TypeScript 搭配使用的项目。这篇文章很干,请大家点点赞哦!
安装所需依赖
又到了实战环节,下面可以一步步跟着我哦!我这里使用的是yarn 依赖管理工具。
初始化项目
这里使用快捷初始化命令:
- yarninit-y
创建完package.json文件之后,我们可以手动修改下项目名称字段name:vitevue2p。
初始化Vite
安装Vite。
- yarnaddvite-D
初始化Vue2
我们需要安装Vue2,所以直接这样安装。
- yarnaddvue
目前,我安装的版本是^2.6.14。
另外,我们还需要安装vue-template-compiler这个依赖,此包可用于将Vue 2.0模板预编译为渲染函数,以避免运行时编译开销和CSP限制。在编写具有非常特定需求的构建工具时,才需要单独使用它。所以,我们这里单独安装。
- yarnaddvue-template-compiler-D
最后,如果想让Vite支持Vue2,就必须安装这个依赖vite-plugin-vue2。
- yarnaddvite-plugin-vue2-D
支持Composition-api
Composition-api字面意思是组合API,它是为了实现基于函数的逻辑复用机制而产生的。这也是Vue3亮点之一,那么我们如何才能够在Vue2项目中使用呢?这需要安装@vue/composition-api依赖。
- yarnadd@vue/composition-api
支持<script setup>语法
<script setup>是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖,是Vue3.2新加入的语法。那么,我们也可以在Vue2项目中使用它。
你需要安装unplugin-vue2-script-setup依赖。
- yarnaddunplugin-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 上工作。
- yarnadd@vue/runtime-dom-D
支持TypeScript语法
随着应用的增长,静态类型系统可以帮助防止许多潜在的运行时错误,所以我们推荐使用TypeScript。
- yarnaddtypescript-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% 占位符了。
- <!DOCTYPEhtml>
- <htmllang="en">
- <head>
- <metacharset="UTF-8">
- <linkrel="icon"href="/favicon.ico"/>
- <metaname="viewport"content="width=device-width,initial-scale=1.0">
- <title>ViteApp</title>
- </head>
- <body>
- <divid="app"></div>
- <scripttype="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":"vitepreview",
- "build":"vitebuild"
- },
- "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 了解一下。
下面,可以看下一个简单的例子。
- //declaringareactivevariablebackedbyanunderlyingref
- letcount=$ref(1)
- //noneedfor.valueanymore!
- console.log(count)//1
- functioninc(){
- //assignmentsarereactive
- count++
- }
另外,这是一项实验性功能。实验性功能可能会改变补丁版本之间的行为。建议将您的 vue 依赖项固定到确切的版本以避免损坏。
言归正传,我们来看下ref-macros.d.ts文件中的内容。
- importtype{
- Ref,
- UnwrapRef,
- ComputedRef,
- WritableComputedOptions,
- WritableComputedRef,
- ShallowUnwrapRef,
- }from'@vue/composition-api'
- declareconstRefMarker:uniquesymbol
- typeRefValue<T>=T&{[RefMarker]?:any}
- declareconstComputedRefMarker:uniquesymbol
- typeComputedRefValue<T>=T&{[ComputedRefMarker]?:any}
- declareconstWritableComputedRefMarker:uniquesymbol
- typeWritableComputedRefValue<T>=T&{[WritableComputedRefMarker]?:any}
- typeToRawRefs<Textendsobject>={
- [KinkeyofT]:T[K]extendsComputedRefValue<inferV>
- ?ComputedRefValue<V>
- :T[K]extendsWritableComputedRefValue<inferV>
- ?WritableComputedRef<V>
- :T[K]extendsRefValue<inferV>
- ?Ref<V>
- :T[K]extendsobject
- ?T[K]extends
- |Function
- |Map<any,any>
- |Set<any>
- |WeakMap<any,any>
- |WeakSet<any>
- ?T[K]
- :ToRawRefs<T[K]>
- :T[K];
- }
- /**
- *Vuereftransformmacroforbindingrefsasreactivevariables.
- */
- declarefunction_$<T>(arg:ComputedRef<T>):ComputedRefValue<T>
- declarefunction_$<T>(
- arg:WritableComputedRef<T>
- ):WritableComputedRefValue<T>
- declarefunction_$<T>(arg:Ref<T>):RefValue<T>
- declarefunction_$<Textendsobject>(arg?:T):ShallowUnwrapRef<T>
- /**
- *Vuereftransformmacroforaccessingunderlyingrefsofreactivevaraibles.
- */
- declarefunction_$$<T>(value:T):ComputedRef<T>
- declarefunction_$$<T>(
- value:WritableComputedRefValue<T>
- ):WritableComputedRef<T>
- declarefunction_$$<T>(value:RefValue<T>):Ref<T>
- declarefunction_$$<Textendsobject>(arg:T):ToRawRefs<T>
- declarefunction_$ref<T>(arg?:T|Ref<T>):RefValue<UnwrapRef<T>>
- declarefunction_$shallowRef<T>(arg?:T):RefValue<T>
- declarefunction_$computed<T>(
- getter:()=>T,
- //debuggerOptions?:DebuggerOptions
- ):ComputedRefValue<T>
- declarefunction_$computed<T>(
- options:WritableComputedOptions<T>,
- //debuggerOptions?:DebuggerOptions
- ):WritableComputedRefValue<T>
- declareglobal{
- 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{createVuePluginasVue2}from'vite-plugin-vue2'
- importScriptSetupfrom'unplugin-vue2-script-setup/vite'
- exportdefaultdefineConfig({
- plugins:[
- Vue2(),
- ScriptSetup({
- refTransform:true,
- }),
- ],
- })
介绍完这些文件,剩下的就是src文件夹中的文件了,因为文件过多,我们把它单独放在Src文件夹栏目中。
Src文件夹
assets文件中只有logo.png一个图片,你可以把静态文件放在当中,这里不多过介绍。
main.ts
这是Vue2的入口文件,我们可以看到这里VueCompositionAPI被当做插件引入。另外,我们引入的App.vue以及其他*.vue为后缀的文件,需要有专门的类型定义文件进行声明,在下面的shims-vue.d.ts文件中我们会讲到。
- importVuefrom'vue'
- importVueCompositionAPIfrom'@vue/composition-api'
- importAppfrom'./App.vue'
- Vue.use(VueCompositionAPI)
- constapp=newVue({render:h=>h(App)})
- app.$mount('#app')
shims-vue.d.ts
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>
- <divid="app">
- <imgalt="Vuelogo"src="./assets/logo.png">
- <hello-worldname="Vue2+TypeScript+Vite"@update="onUpdate"/>
- <async-component/>
- </div>
- </template>
- <scriptsetuplang="ts">
- import{defineAsyncComponent}from'@vue/composition-api'
- importHelloWorldfrom'./components/HelloWorld.vue'
- constAsyncComponent=defineAsyncComponent(()=>import('./components/Async.vue'))
- functiononUpdate(e:any){
- console.log(e)
- }
- </script>
- <scriptlang="ts">
- exportdefault{
- 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}}x2={{doubled}}</div>
- <button@click="dec()"v-html="decText"/>
- <component:is="count>2?Foo:Bar"/>
- </div>
- </template>
- <scriptsetuplang="ts">
- import{watch}from'@vue/composition-api'
- importFoofrom'./Foo.vue'
- importBarfrom'./Bar.vue'
- constprops=withDefaults(defineProps<{msg:string;name:string|number}>(),{msg:'Hello'})
- constemit=defineEmits(['update'])
- letcount=$ref(1)
- //eslint-disable-next-lineprefer-const
- letdoubled=$computed(()=>count*2)
- functioninc(){
- count+=1
- }
- functiondec(){
- count-=1
- }
- constdecText='<b>Dec</b>'
- watch(()=>count,value=>emit('update',value))
- </script>
- <stylescoped>
- button{
- margin:20px0;
- }
- </style>
其他文件就不过多介绍了,就只是简单的模板文件。
Foo.vue
- <template>
- <div>Foo</div>
- </template>
Bar.vue
- <template>
- <div>Bar</div>
- </template>
Async.vue
- <template>
- <div>AsyncComponent</div>
- </template>
结语
最后,我们启动下项目。
- yarndev
如上图所示,启动成功。
相信这样可以在一定程度上提升你 Vue 2 的开发体验,赶快来!
以下是本篇文章的源码地址:
- https://github.com/maomincoding/viteVue2p
如果觉得这篇文章对你有帮助,感谢点赞哦~
原文链接:https://mp.weixin.qq.com/s/qFZo0hE7iGDsVqtxgrE79g



