手摸手创建一个 Vue + Ts 项目(一) —— 初始化项目

2,360 阅读9分钟

手摸手创建一个 Vue + Ts 项目目录

介绍

本教程是为大家介绍如何一步步搭建一个后台的前端项目,只实现最最基本的功能,基于 Vue3 + TypeScript 和 NaiveUI 的技术方案。从零开始,手摸手一步步来搭建一个后台项目。它可能并不能帮助你快速搭建企业级中台产品原型,但可以让你更好地了解整个项目。

该项目中的源码地址:easii-admin-ui: 手摸手创建一个 Vue + Ts 项目源码 (gitee.com)

技术栈:

  • Vite
  • Vue3
  • VueRouter
  • Pinia
  • TypeScript
  • NaiveUI
  • Axios
  • Echarts
  • Unocss

环境准备

安装 Node

这里使用 nvm 来安装、管理 NodeJS,该框架主要是为了可以方便地安装和切换不同版本的 NodeJS。

nvm 中文网站:nvm.uihtm.com/

安装好 nvm 后,可以通过如下命令安装和使用 LTS 版本的 NodeJS:

-- 安装最新的 lts 版 NodeJS
nvm install --lts
-- 切换 lts 版 NodeJS
nvm use --lts

安装 pnpm

使用 pnpm 代替 npm 来作为软件包管理器。

可以使用 npm 来全局安装 pnpm:

npm install -g pnpm

之后在执行 npm 命令时,可以直接替换为 pnpm,例如安装一个依赖包,可以直接执行 pnpm install xxx.

使用 nrm 管理 npm 源

nrm 可以简单快速地切换不同的 npm 镜像源,可以用它来快速切换淘宝镜像源。

  1. 安装 nrm
    npm install -g nrm
    
  2. 使用 nrm 切换淘宝源
    nrm use cnpm
    

到此为止,前端所需要的环境基本完成,接下来就可以初始化一个前端项目了。

初始化项目

基于 Vite 初始化 Vue + Ts 项目

Vite 官网:cn.vitejs.dev/

使用 pnpm 来创建一个 Vue 项目:

pnpm create vite@latest easii-admin-ui -- --template vue

执行后,会让你选择 frameworkvariant,这里分别选择 VueTypeScript 就可以。

执行后,在控制台输入如下:

linpl@iMac Workspace % pnpm create vite@latest easii-admin-ui -- --template vue
.../Library/pnpm/store/v3/tmp/dlx-16232  | Progress: resolved 1, reused 0, downloaded 0, added.../Library/pnpm/store/v3/tmp/dlx-16232  |   +1 +
.../Library/pnpm/store/v3/tmp/dlx-16232  | Progress: resolved 1, reused 0, downloaded 0, added.../Library/pnpm/store/v3/tmp/dlx-16232  | Progress: resolved 1, reused 0, downloaded 1, addedPackages are hard linked from the content-addressable store to the virtual store.
  Content-addressable store is at: /Users/linpl/Library/pnpm/store/v3
  Virtual store is at:             ../../Library/pnpm/store/v3/tmp/dlx-16232/node_modules/.pnpm
.../Library/pnpm/store/v3/tmp/dlx-16232  | Progress: resolved 1, reused 0, downloaded 1, added 0
? Select a framework: › - Use arrow-keys. Return to submit.
✔ Select a framework: › Vue
✔ Select a variant: › TypeScript
 
Scaffolding project in /Users/linpl/Development/Workspace/easii-admin-ui...
 
Done. Now run:
 
  cd easii-admin-ui
  pnpm install
  pnpm run dev

按照控制台的提示,进入项目后,安装依赖,启动项目

cd easii-admin-ui
pnpm install
pnpm run dev

启动后,打开浏览器,输入控制台显示的 IP 和端口,效果如下:

image.png

OK,一个全新的 Vue3 + Ts 项目就初始化完成啦。接下来就按照我们需要的技术,一步步来操作。

安装 @types/node

@types/node 是一个 TypeScript 类型定义文件的包,主要用于提供 Node.js API 的类型定义,使得在 TypeScript 项目中使用 Node.js API 时,可以获得更好的代码提示、类型检查和代码补全等功能。这有助于提高代码的可读性、可维护性和安全性。

安装命令:

pnpm i -D @types/node

添加路径别名

在前端项目中,通常会用 @ 来作为 src 目录的别名,这里演示一下 Vite + TypeScript 下,如何设置该别名。

配置 vite.config.ts

vite.config.ts 中,增加如下配置:

import { resolve } from 'path'

/** 路径查找 */
const pathResolve = (dir: string): string => {
  return resolve(__dirname, '.', dir)
}

export default defineConfig({
  // ......
  resolve: {
    alias: {
      '@': pathResolve('src')
    },
  },
  // ......
})

配置 tsconfig.json

tsconfig.json 中的 compilerOptions 中添加 baseUrlpaths 配置:

{
	"compilerOptions": {
		// ......
		"baseUrl": "./",
		"paths": {
			"@/*": ["src/*"]
		}
	}
}

修改这两部分后,可以在 App.vue 文件中测试下效果:

之前引入 HelloWord 组件的方式:

import HelloWord from './components/HelloWorld.vue'

可以修改为:

import HelloWorld from '@/components/HelloWorld.vue'

修改后,启动正常,则表示已经生效啦。

安装 NaiveUI

NaiveUI 官网:www.naiveui.com/

// 安装 naive-ui
pnpm i -D naive-ui

// 安装 vfonts
pnpm i -D vfonts

安装 xicons

xicons 是一个第三方图标库,风格比较统计且细腻,是 NaiveUI 中推荐使用的。

image.png

安装 xicons 中所有的图标库(这里也可以部分安装):

pnpm i -D @vicons/fluent
pnpm i -D @vicons/ionicons4
pnpm i -D @vicons/ionicons5
pnpm i -D @vicons/antd
pnpm i -D @vicons/material
pnpm i -D @vicons/fa # font awesome
pnpm i -D @vicons/tabler
pnpm i -D @vicons/carbon

使用教程,可以参考 图标 Icon - Naive UI

自动引入组件功能

由于组件库和图标库等提供的组件较多,且不想全局引用时,这样子话,在每个文件中使用时,都会需要手动编写 import { xxx } from 'xxx'

这里介绍一款由 Vue 官方人员开发的自动引入插件 —— unplugin-vue-components,可以省去比如 UI 库的大量 import 语句。

安装 unplugin-vue-components

pnpm i -D unplugin-vue-components

Vite 启用 unplugin-vue-components

编辑 vite.config.ts,添加内容如下:

/**  
* unplugin-vue-components  
*/  
import Components from 'unplugin-vue-components/vite'

export default defineConfig({  
	plugins: [  
		// ......
		Components({  
			dts: true, // ts 环境下要启用  
		})  
	],
	// ......
})

添加 NaiveUI 解析器

unplugin-vue-components 为多个流行的 UI 库提供了内置解析器,这里以 NaiveUI 为例,启用该解析器:

基于上面添加的内容:

/**  
* unplugin-vue-components  
*/  
import Components from 'unplugin-vue-components/vite'
import {NaiveUiResolver} from 'unplugin-vue-components/resolvers'	// ##### new #####

export default defineConfig({  
	plugins: [  
		// ......
		Components({  
			dts: true, // ts 环境下要启用
			resolvers: [NaiveUiResolver()]  	// ###### new ######
		})  
	],
	// ......
})

启用后,在 Vue 文件中,就可以不必再添加 import { xxx } from 'naive-ui' 相关代码,同时,会在根目录生成 components.d.ts 文件,来全局引入需要的组件。

引入自定义组件

unplugin-vue-components 还可以自动引入自定义的组件,默认情况下,会引入 src/components 目录下的组件,如果需要引入其他目录的组件,则需要配置 dirs 属性:

/**  
* unplugin-vue-components  
*/  
import Components from 'unplugin-vue-components/vite'

export default defineConfig({  
	plugins: [  
		// ......
		Components({  
			dts: true, // ts 环境下要启用
			dirs: ['src/components', 'src/layouts'],	// ###### new ######
		})  
	],
	// ......
})

自定义 Xicons 组件解析器

在使用 Xicons 组件库时,每次使用一个组件,都需要提前引入,冗余繁琐。所以这里基于 unplugin-vue-components,来实现一个自动引入 Xicons 组件的解析器。

安装 fs、local-pkg

这两个依赖主要用于解析依赖包中的文件,在自定义的组件解析器中使用。

安装命令:

pnpm i local-pkg
pnpm i -D fs

增加 Xicons 组件解析器

在根目录新增文件:ViconsResolver.ts,在其中实现组件解析的功能。

import { readdirSync } from 'fs'
import { dirname } from 'path'
// @ts-ignore
import { resolveModule } from 'local-pkg'
import type { ComponentResolver } from 'unplugin-vue-components'

/**
 * key: 图标组件名称
 * value: 模块
 */
let iconPkgMap: Map<string, string>

const iconPkgs: Array<string> = [
  '@vicons/fluent',
  '@vicons/ionicons4',
  '@vicons/ionicons5',
  '@vicons/antd',
  '@vicons/fa',
  '@vicons/material',
  '@vicons/tabler',
  '@vicons/carbon',
]

export function ViconsResolver(): ComponentResolver {
  if (!iconPkgMap) {
    try {
      iconPkgMap = new Map();
      iconPkgs.forEach(pkg => {
        let icons = readdirSync(dirname(resolveModule(pkg as string) as string), { withFileTypes: true })
        .filter((item) => !item.isDirectory() && item.name.match(/^[A-Z][A-Za-z0-9]+\.js$/))
        .map((item) => item.name.replace(/\.js$/, ''))
        icons.forEach(icon => iconPkgMap.set(icon, pkg))
      }) 
    } catch (error) {
      console.error(error)
      throw new Error(`[unplugin-vue-components] failed to load vicons, have you installed it?`)
    }
  }

  return {
    type: 'component',
    resolve: (name: string) => {
      if (iconPkgMap.has(name)) {
        return {
          name: name,
          from: iconPkgMap.get(name) as string
        }
      }
    },
  }
}

添加该文件后,需要在 tsconfig.node.json 中引入该文件,在其 include 属性中,添加该文件:

{
	// .......
  "include": ["vite.config.ts", "ViconsResolver.ts"]
}

启用

unplugin-vue-componentsresolvers 中添加该自定义的解析器,如下:

import {ViconsResolver} from './ViconsResolver'

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

安装 UnoCSS

UnoCSS 介绍

UnoCSS 官网:alfred-skyblue.github.io/unocss-docs…

这里引用一段作者(Antfu)对于 UnoCSS 的概括:“UnoCSS 是一个具有高性能且极具灵活性的即时原子化 CSS 引擎。它是一个引擎,而非一款框架,因为它并未提供核心工具类,所有功能可以通过预设和内联配置提供”。

这里又涉及到一个点 —— “原子化 CSS”。

什么是原子化 CSS 呢?

其实就是一种 CSS 的架构方式,倾向于小巧且用途单一的 class,并且会以视觉效果进行命名。举个例子:

.m-0 {
    margin: 0;
}

.text-red {
    color: red;
}

看完实例,就能简单理解什么是原子化 CSS 了。如果用熟悉的后端语言来说,就是一种符合“单一职责(一个类应该只有一项职责,这样可以保证类的内聚性,并且降低类之间的耦合性)”设计原则的一种 CSS class 定义方式,一个 class 只会实现一个效果。

关于这个框架的介绍,就到这里,想要了解更多,可以阅读下作者的文章:重新构想原子化 CSS (antfu.me)

安装 UnoCSS

  • 安装 UnoCSS
pnpm i -D unocss
  • 安装 UnoCSS 预设

    有两个依赖包,第一个是 UnoCSS 的默认预设,第二个是启用属性化模式的规则

pnpm i -D @unocss/preset-attributify

pnpm i -D @unocss/preset-uno

启用 UnoCSS

  1. 首先在 vite.config.ts 中启用该插件
import UnoCSS from 'unocss/vite'

export default defineConfig({
  plugins: [
	// ......
    UnoCSS()
  ],
}
  1. 添加 UnoCSS 预设

    基于上面的内容:

import UnoCSS from 'unocss/vite'

import presetUno from '@unocss/preset-uno'	// ******** new *********
import presetAttributify from '@unocss/preset-attributify'	// ******** new *********

export default defineConfig({
  plugins: [
	// ......
    UnoCSS({
        presets: [presetUno(), presetAttributify()]		// ******** new *********
    })
  ],
}

UnoCSS 的预设与 Tailwind CSSWindi CSS 兼容,可以参考它们的文档以获取详细的使用方法。

  1. main.ts 中引入 uno.css
import 'uno.css'

示例

比如,我想要将一个标题颜色设置为深灰色,可以直接在其标签上面添加:text-gray-500

<template>
  <h1 text-gray-500>{{ msg }}</h1>
</tempalte>

效果如下:

image.png

安装 Pinia

Pinia 官网:pinia.web3doc.top/

Pinia 是 Vue 的存储库,它允许跨组件/页面共享状态。

安装

pnpm i pinia

定义一个 store

在 src 目录下,创建一个 store 目录,用作存放 store 相关的文件。

目录定义如下:

image.png

这里以定义一个计数器的 Store 为例:

在 modules 目录中,新建一个 counter.ts:

import { defineStore } from 'pinia'
import { ref } from 'vue'

export const useCounterStore = defineStore('counter', () => {
  const count = ref(0)
  function increment() {
    count.value++
  }
  return { count, increment }
})

使用 store

在 store 目录中的 index.ts 中,添加如下内容:

import type {App} from 'vue'
import {createPinia} from 'pinia'

const store = createPinia()

export function useStore(app: App<Element>): void {
  app.use(store)
}

export {store}

使用

在 HelloWorld.vue 中,添加一个计数器功能:

<script setup lang="ts">
import {useCounterStore} from "@/store/modules/counter"

const counterStore = useCounterStore()
</script>

<template>
  <div class="card">
    <button type="button" @click="counterStore.increment">count is {{ counterStore.count }}</button>
  </div>
</template>

效果如下:

image.png

结语

这篇文章中,手把手地讲述了如何创建一个后台前端项目。从环境搭建、初始化项目,以及安装各种最基本的依赖。这只是项目的第一步,下一篇将讲述如何实现一个左侧菜单栏,并搭配 vue-router 使用,为项目增添更多功能。

最后,欢迎关注我的公众号:「代码笔耕」和我的开源项目:「easii (easii) - Gitee.com