001. 从0到1搭建Vite项目工程

739 阅读6分钟

一、前言

需要的环境:

开发工具:VsCode、Visual Studio、sql server
前端:Vue3、Vite、TypeScript、SCSS、Element Plus、Router、axios、Vuex
后端:.NET6、Automapper、Autofac、Sql sugar、JWT、Log4Net

vite官方文档

cn.vitejs.dev/

二、创建vite项目

npm install -g pnpm
pnpm create vite@latest vvt

输入项目名称:vvt
选择:vue
选择:TypeScript
完成
生成的目录结构如下:

│  .gitignore
│  index.html
│  list.txt
│  package.json # 项目配置文件,标题、版本、模块版本等
│  README.md
│  tsconfig.json # TS配置文件
│  tsconfig.node.json
│  vite.config.ts # Vite配置文件
│  
├─.vscode
│      extensions.json
│      
├─public # 公共资源
│      vite.svg
│      
└─src # 项目目录
    │  App.vue # 根组件
    │  main.ts # 根函数入口、全局配置
    │  style.css
    │  vite-env.d.ts
    │  
    ├─assets # 静态资源
    │      vue.svg
    │      
    └─components # 组件
            HelloWorld.vue

三、安装依赖

pnpm i

四、安装SCSS

pnpm install sass --save

接下来可以这样使用:

<style lang="scss" ...

五、package.json配置

可以配置一下private、version等信息,可以加入license标签,进行协议生成。

六、Element Plus的安装与使用

pnpm install element-plus --save
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
const app = createApp(App)
app.use(ElementPlus)

七、路由的安装与使用

pnpm install vue-router@next --save

新建src/router目录,目录下新建index.ts,写入如下代码:

import { createRouter, createWebHistory } from 'vue-router'
import HomePage from "../pages/HomePage.vue"

const router = createRouter({
    history: createWebHistory(),
    routes: [
        { path: "/", component: HomePage }
    ]
})

export default router

main.ts中导入:

import router from './router'
app.use(router)

改造App.vue

<template>
  <router-view />
</template>

八、状态管理

pnpm install vuex@next --save

src下新建文件夹vuex,新建文件index.ts

main.ts

import store from './vuex/index'
app.use(store)

附录:如下是另一个流程分支,是之前做一些特殊功能的测试版本。

五、安装vue-router

pnpm i vue-router -D

新建src\modules文件夹来做一些项目的核心模块配置,比如router、pinia等等。
在文件夹下创建router.ts文件(这个文件会在后面进行更改,为了实现框架搭建,一步步来)。

import { createRouter, createWebHistory } from "vue-router"

const router = createRouter({
    routes: [],
    history: createWebHistory()
})

export default router

六、注册路由

main.ts:

import App from './App.vue'
import { createApp } from 'vue'
import './style.css'
import router from "./modules/router"

const app = createApp(App)

app.use(router)

app.mount('#app')

七、新建页面文件夹

新建src/pages文件夹来放项目的页面,注意文件夹名称需要为pages,很多项目中定义为views,由于框架后面引用了很多有用的插件,插件默认配置为pages,为了方便,采用pages作为文件夹名称。

八、新建src/pages/home.vue

建议的文件定义模式如下,注意大小写字母的区别:

pages文件夹\
|--home.vue\
|--about文件夹\
|--|--index.vue\
|--|--Component.vue

home.vue内容:

<template>
    我是首页
</template>

about/index.vue内容:

<template>
    我是about页
</template>

九、App.vue配置路由出口

<template>
  <router-view />
</template>

十、配置Home路由

import { createRouter, createWebHistory } from "vue-router"

const router = createRouter({
    routes: [
        {
            name: "首页",
            path: "/home",
            component: () => import("../pages/home.vue")
        },
        {
            name: "关于",
            path: "/about",
            component: () => import("../pages/about/index.vue")
        }
    ],
    history: createWebHistory()
})

export default router

十一、安装pinia

pnpm i pinia -D

十二、配置pinia

新建src/modules/pinia.ts

import { createPinia } from "pinia"

const pinia = createPinia()

export default pinia

十三、main.ts中引入

import pinia from './modules/pinia'

app.use(pinia)

十四、新建src/stores文件夹

新建src/stores文件夹,注意stores名称,同样于pages,这个文件夹名称在后面引入的组件中是作为默认名称存在的,所以不要改为其它名称。

在该文件夹下创建counterStore.ts,注意在该文件夹下创建的文件均需要以Store结尾,同样作为约定,这也是由后面引入的插件规则来决定的。

import { defineStore } from "pinia"

export const counterStore = defineStore("counter", {
    state() {
        return {
            num: 1
        }
    },
    actions: {
        inc() {
            this.num++
        }
    }
})

十五、使用pinia

<script lang="ts" setup>
import { counterStore } from "../stores/counterStore"

const counter = counterStore()
</script>

<template>
    <div @click="counter.inc()">
        我是首页{{ counter.num }}
    </div>
</template>

十六、小结

modules文件夹主要用来放项目核心模块。
pages文件夹主要用来放页面,注意命名。

十七、在components下创建组件

foo.vue

<template>
    我是foo
</template>

十八、传统方式引入组件

import foo from "../components/foo.vue"

<foo />

十九、按需引入组件

是否可以实现不用手写import,像element plus等组件那样实现按需引入,直接使用?这里需要使用一个组件unplugin-vue-components。

安装:

pnpm i unplugin-vue-components -D

配置,在vite.config.ts中引入:

import Components from "unplugin-vue-components/vite"

export default defineConfig({
  plugins: [..., Components()]
})

将十八小节的import注释

// import foo from "../components/foo.vue"

二十、Element Plus、Native UI的按需引入

安装

pnpm i -D naive-ui
pnpm i -D vant

vite.config.ts:

import {
  AntDesignVueResolver,
  ElementPlusResolver,
  VantResolver,
  NaiveUiResolver
} from "unplugin-vue-components/resolvers"

export default defineConfig({
  plugins: [..., Components({
    resolvers: [NaiveUiResolver(), VantResolver()]
  })]
})

直接使用:

<n-button>abc</n-button>

二十一、api的自动引入

这里主要用来解决import { ref } from 'vue'的问题。这里用到了unplugin-auto-import插件。

安装:

pnpm i -D unplugin-auto-import

在vite.config.ts中配置:

import AutoImport from "unplugin-auto-import/vite"

export default defineConfig({
  plugins: [
    ...
    AutoImport({
      imports: ["vue", "vue-router", "pinia"]
    })
    ...
  ]
})

使用:

const val = ref(100)

{{ val }}

二十二、小节

截至当前,应用程序可以正常运行,原工程目录下多出了auto-imports.d.ts、components.d.ts两个文件。打开auto-imports.d.ts、components.d.ts,发现这两个文件的内容是自动构建的。
需要注意的是项目本身不在上述两个范畴内的自定义组件还是需要import,比如stores下的组件,可能未来还有一些工具函数组件,这个随后再说。
存在的问题:
components.d.ts存在报错:找不到模块“./src/components/foo.vue”或其相应的类型声明。ts(2307)
Home.vue下存在报错:找不到名称“ref”。ts(2304)
但是程序运行是没有问题的,这些报错主要是类型声明的问题,这个随后再说。 附:解决上述的问题

vite.config.ts

import AutoImport from 'unplugin-auto-import/vite';
  plugins:
    AutoImport({
      imports: ['vue', 'vue-router', 'pinia'],
      dts: './auto-imports.d.ts',
      eslintrc: {
        enabled: true,
        globalsPropValue: true,
        filepath: './.eslintrc-auto-import.json',
      },
    }),

.eslintrc.json

  "extends": [
    ...
    ".eslintrc-auto-import.json"
  ],

tsconfig.json

 "include": [... "auto-imports.d.ts"],

二十三、解决store的自动引入

api自动导入插件支持这样的属性:

AutoImport({
  ...
  dirs: ['src/stores/**/*.ts']
})

这样的话只要src/stores下有export default,可以直接按需导入。

只要stores下的文件符合Store结尾即可自动引入,对十二节的store文件进行修改:

src/stores/counterStore.ts

import { defineStore } from "pinia"

export default defineStore("counter", {
    state() {
        return {
            num: 1
        }
    },
    actions: {
        inc() {
            this.num++
        }
    }
})

在文件中直接使用:

const counter = counterStore()

{{ counter.num }}

二十三、解决自定义组件的自动引入

创建src/composables文件夹,创建有导出的ts文件。同时在vite.config.ts中配置自动导入插件:

import AutoImports from "unplugin-auto-import/vite"

AutoImports({
  ...
  dirs: ['src/composables/**/*.ts']
}), 

注意:unplugin-auto-import更新还是比较快的,为了实现stores和composables的引入支持,后来形成了默认规则,而自定义的模块这里只要配置dirs即可实现响应目录下的文件自定引入。

需要注意的是:由于composables和stores的意义,composables中的文件已use开头,stores已Store结尾,这主要形成一个项目约定。

直接使用:

console.log(useFoo)

二十四、文件路由

安装vite-plugin-pages。

pnpm install -D vite-plugin-pages

vite.config.ts引入:

import Pages from "vite-plugin-pages"

export default defineConfig({
  plugins: [
    ...
    Pages(),
    ...
  ]
})

修改src/modules/router.ts文件:

import routes from "~pages"

import { createRouter, createWebHistory } from "vue-router"

const router = createRouter({
    routes,
    history: createWebHistory()
})

export default router

解决类型问题:

在某个类型声明文件中引入/// <reference types='vite-plugin-pages/client' />

二十五、布局

为了在一些子组件中避免引入header、footer等公共组件,这里采用layout来做这一块的功能。

安装:

pnpm i vite-plugin-vue-layouts -D

vite.config.ts注册:

import Layouts from "vite-plugin-vue-layouts"

export default defineConfig({
  plugins: [
    ...
    Layouts(),
    ...
  ]
})

再次对router.ts进行更改:

import { createRouter, createWebHistory } from "vue-router"
import { setupLayouts } from "virtual:generated-layouts"
import generatedRoutes from "virtual:generated-pages"

const routes = setupLayouts(generatedRoutes)

const router = createRouter({
    routes,
    history: createWebHistory()
})

export default router

新建src/layouts文件夹,新建一个布局文件default.vue:

<template>
    asdfasdfsad
    <router-view />
</template>

可以看到这个布局已经默认启用了。

怎么在vue文件中使用,加入一个这样的节点即可:

<route>
    {
        meta: {
            layout: 'custom'
        }
    }
</route>