今天开始使用 vue3 + ts 搭建一个电商项目平台,因为文章会将项目的每处代码的书写都会讲解到,所以本项目会分成好几篇文章进行讲解,我会在最后一篇文章中会将项目代码开源到我的github上,大家可以自行去进行下载运行,希望本文章对有帮助的朋友们能多多关注本专栏,学习更多前端vue知识,然后开篇先简单介绍一下本项目用到的技术栈都有哪几个方面(阅读本文章能够学习到的技术):
vite:快速轻量且功能丰富的前端构建工具,帮助开发人员更高效构建现代Web应用程序。
pnpm:高性能、轻量级npm替代品,帮助开发人员更加高效地处理应用程序的依赖关系。
vue3:vue最新版本的用于构建用户界面的渐进式JavaScript框架。
typescript:javascript的超集,提供了静态类型检查,使得代码更加健壮。
vue-router:Vue.js官方提供的路由管理器与Vue.js紧密耦合,非常方便与Vue.js一同使用。
pinia:vue3构建的Vuex替代品,具有响应式能力,提供非常简单的 API,进行状态管理。
element-plus:基于Vue.js 3.0的UI组件库,用于构建高品质的响应式Web应用程序。
axios:基于Promise的HTTP客户端,可以在浏览器和node.js中使用。
@vueuse/core:针对vue3功能性的API库,提供了许多实用的功能。
当然还有许多其他的需要安装的第三方库,这里就不再一一介绍了,在项目中用到的地方自行会进行讲解,大家自行学习即可,现在就让我们走进vue3+ts的实战项目吧。
目录
初始化项目
包管理工具
src别名配置
重置默认样式
配置elementPlus
自定义全局样式
axios基础配置
API接口统一管理
全局组件封装
路由配置管理
store仓库配置
git管理配置
初始化项目
找到自己想要创建项目的文件夹,在地址栏输入cmd,从而进入当前文件路径,在当前路径下执行如下命令,使用vite构建工具进行创建vue3项目,具体的操作这里就不再赘述,不了解 vite 构建工具的朋友可以先去看 vite脚手架的搭建与使用 ,因为我这里使用 vite3 版本,其安装命令如下:
我这里就使用 pnpm 进行安装项目了,没有安装过pnpm的朋友,cmd进行全局安装即可,命令为:npm i -g pnpm 。
pnpm create vite
安装过程与我上面分享的文章基本一致,cd到项目路径安装下依赖,最后的结果如下表明创建项目成功:
然后将项目文件拖到编辑器中,打开终端执行 pnpm run dev 打开项目,结果如下:
包管理工具
团队开发项目的时候,需要统一包管理器工具,因为不同包管理器工具下载同一个依赖,可能版本不一样,导致项目出现bug问题,因此包管理器工具需要统一管理! 只需在 package.json 中加入一行代码来限制,如下含义:只允许使用 pnpm 来进行安装
"scripts": {
// ... 其他命令
"preinstall": "npx only-allow pnpm"
}
直接将上面的代码复制如下,preinstall 是包安装工具的 钩子函数,在上例中作为 install 之前的拦截判断,也就是在 install 之前触发:
配置完成之后,当你使用的不是pnpm命令之后,控制台直接报错警告:
src别名配置
在开发项目的时候文件与文件关系可能很复杂,因此我们需要给src文件夹配置一个别名!在创建的项目中找到 vite.config.ts 配置文件,配置如下命令:
// vite.config.ts
import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
"@": path.resolve(__dirname, 'src') // 相对路径别名配置,使用 @ 代替 src
}
}
})
如果红色语法提示请安装 @type/node 其是TypeScript 的一个文件包,用于描述 Node.js 核心模块和常用的第三方库的类型信息,需要终端执行如下命令才能解决下图的警告:
pnpm i @types/node --save-dev
找到 tsconfig.json 配置文件,找到配置项 compilerOptions 添加配置,这一步的作用是让 IDE 可以对路径进行智能提示:
// tsconfig.json
{
"compilerOptions": {
"baseUrl": ".", // 解析非相对模块的基地址,默认是当前目录
"paths": { //路径映射,相对于baseUrl
"@/*": ["src/*"]
}
}
}
验证我们是否配置成功?我们可以在main.js入口文件处进行校验,如果有提示说明配置成功!
重置默认样式
在项目中我们都会用到一些标签,但是这些标签可能本身自带一些默认样式,这些默认样式可能会影响我们的排版布局,如果每次引用就去清除一遍默认样式有点太过繁琐,因此这里需要我们清除一下默认样式。执行如下命令安装第三方包:
pnpm install reset.css --save
安装完成之后,我们在入口文件处进行引入即可,如下:
配置elementPlus
因为本项目需要采用 element-plus 组件库进行创建项目,其官方地址为:element-plus ,所以接下来需要对组件库进行一个安装配置,具体的实现过程如下,终端执行如下安装命令:
pnpm install element-plus
这里我们采用自动按需导入的方式,如下需要安装两个插件:
pnpm install unplugin-auto-import unplugin-vue-components -D
因为我们可以还需要用到elementplus中的图标,所以这里也采用自动导入的方式:
pnpm install unplugin-icons -D
因为本次使用的是vite + ts的模板,所以我们需要对下面文件进行如下操作修改:
// vite.config.ts
import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
// 配置按需导入
import Icons from 'unplugin-icons/vite'
import IconsResolver from 'unplugin-icons/resolver'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
const pathSrc = path.resolve(__dirname, 'src')
export default defineConfig({
plugins: [vue(),
AutoImport({
// 自动导入 Element Plus 相关函数,如:ElMessage, ElMessageBox... (带样式)
resolvers: [
ElementPlusResolver(),
// 自动导入图标组件
IconsResolver({}),
],
}),
Components({
resolvers: [
// 自动注册图标组件
IconsResolver({
enabledCollections: ['ep'],
}),
// 自动导入 Element Plus 组件
ElementPlusResolver(),
],
}),
Icons({
autoInstall: true,
}),
],
resolve: {
alias: {
"@": pathSrc // 相对路径别名配置,使用 @ 代替 src
}
}
})
接下来我们直接在项目中引入一个button组件,尝试一下,如下说明我们自动按需引入成功!
接下来我们需要对elementPlus进行一个主题色的配置,这里我们采用sass样式系统,sass是一种预编译的css,作用类似于less,这里我们在vue项目中采用该预处理器进行处理样式,终端执行如下命令安装相应的插件:
pnpm install sass -D
接下来在src目录下新建styles文件夹用于存放全局样式组件,该文件夹下新建element.scss文件,设置定制化样式,如下:
@forward 'element-plus/theme-chalk/src/common/var.scss' with (
$colors: (
'primary': (
// 主色
'base': #f94417,
),
'success': (
// 成功色
'base': #f72f18,
),
'warning': (
// 警告色
'base': #fd5e1d,
),
'danger': (
// 危险色
'base': #e26237,
),
'error': (
// 错误色
'base': #cf4444,
),
)
)
然后我们需要在vite.config.ts文件夹下修改下面两处的内容:
回到页面可以看到我们的主题色已经生效了:
因为elementPlus默认是英文的,接下来我们还需要对其设置一下国际化配置,打开main.ys入口文件,进行如下配置:
import { createApp } from 'vue'
import 'reset.css'
import App from './App.vue'
// 配置国际化
import ElementPlus from 'element-plus'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
createApp(App)
.use(ElementPlus, { locale: zhCn })
.mount('#app')
接下来我们引入一个分页器,看看是否变成中文了,如下看来是没毛病了!
自定义全局样式
在项目里一些组件共享的色值会以scss变量的方式统一放到一个名为var.scss的文件中,正常组件中使用,需要先导入scss文件,再使用内部的变量,比较繁琐,自动导入可以免去手动导入的步骤,直接使用内部的变量,如下:
我们在公共样式文件夹styles中新增一个var.scss文件,里面存放着全局公共样式:
然后我们在vite.config.ts中进行引入该文件夹:
可以看到我们的样式已经生效了!
axios基础配置
axios是一个基于promise的http客户端,用于浏览器和node.js环境中发起http请求。它提供了简单易用的api,可以用于执行各种类型的http请求,包括get、post、put、delete等。接下来对其进行简单的配置讲解:
终端执行如下命令进行安装axios:
pnpm install axios
然后我们在src文件夹下新建utils工具文件夹,在该文件夹下新建http.ts文件对axios进行二次封装
目的:
使用请求拦截器,可以在请求拦截器中处理一些业务(开始进度条、请求头携带公共参数)
使用响应拦截器,可以在响应拦截器中处理一些业务(进度条结束、简化服务器返回的数据、处理http网络错误)
// axios基础封装
import axios from 'axios'
const http = axios.create({
baseURL: '服务器接口根地址',
timeout: 5000,
})
// 请求拦截器
http.interceptors.request.use(config => {
// 请求拦截器
return config
}, error => {
return Promise.reject(error)
})
// 响应拦截器
http.interceptors.response.use(res => {
return res.data
}, error => {
//处理网络错误
let msg = ''
const status = error.response.status
switch (status) {
case 401:
msg = 'token过期'
break
case 403:
msg = '无权访问'
break
case 404:
msg = '请求地址错误'
break
case 500:
msg = '服务器出现问题'
break
default:
msg = '网络出现问题'
break
}
ElMessage({
type: 'error',
message: msg,
})
return Promise.reject(error)
}
)
export default http
因为我们是自动按需导入,所以不需要像以前那样用到elementPlus组件还需要导入,这里直接使用相关api即可,不过可能ts文件会检索 ElMessage 不存在,会报错,这里我们需要在tsconfig.json中的include字段中添加auto-imports.d.ts就可以了:
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue", "auto-imports.d.ts",],
接下来开始对我们封装的axios进行相关测试,我们在入口文件main.ts进行接口测试:
打开控制台可以看到我们的接口数据已经请求成功了!
这里我们把接口地址故意写错试试,可以看到如下的效果,还不错!
API接口统一管理
在开发项目的时候,接口可能很多需要统一管理。在src目录下去创建api文件夹去统一管理项目的接口:
统一管理我们项目用户的相关接口
// 统一管理我们项目用户的相关接口
import http from '@/utils/http'
import type { loginForm, loginResponseData, userResponseData } from './type'
// 统一管理接口
enum API {
LOGIN_URL = '/user/login',
USERINFO_URL = '/user/info',
}
// 暴露请求函数
// 登录接口的方法
export const reqLogin = (data: loginForm) =>
http.post<any, loginResponseData>(API.LOGIN_URL, data)
// 获取用户信息接口方法
export const reqUserInfo = () =>
http.get<any, userResponseData>(API.USERINFO_URL)
登录接口需要携带参数ts类型
// 登录接口需要携带参数ts类型
export interface loginForm {
username: string
password: string
}
interface dataType {
token: string
}
// 登录接口返回数据类型
export interface loginResponseData {
code: number
data: dataType
}
interface userInfo {
userId: number
avatar: string
username: string
password: string
desc: string
roles: string[]
buttons: string[]
routes: string[]
token: string
}
// 定义服务器返回用户信息相关数据类型
interface user {
checkUser: userInfo
}
export interface userResponseData {
code: number
data: user
}
当然后期我们还需要其他的接口,这里仅仅作为一个展示,接下来进行接口测试:
全局组件封装
在vue3项目,有很多组件需要在多个地方进行使用,所以我们一般都将其封装成全局组件,然而单纯的封装成全局组件的话,每次使用还需要额外在每个项目中进行引入,就很烦,这里我们先将所有的全局进行全局注册,这样后面使用的话直接使用我们设置的组件名称即可,就不需要一次次再单独的进行引入了,如下:我们封装一个Loading加载组件为全局组件:
封装成全局组件之后,我们还需要在入口文件main.js进行一个引入注册:
上面index.js代码如下,方便大家复制直接使用即可:
import { defineAsyncComponent } from "vue";
// 获取要注册的全局组件
const components = import.meta.glob("./global/*.vue");
// 对外暴露插件
export default function install(app: any) {
for (const [key, value] of Object.entries(components)) {
const name = key.slice(key.lastIndexOf("/") + 1, key.lastIndexOf("."));
app.component(name, defineAsyncComponent(value as any));
}
}
然后Loading组件的加载的话,大家可以仿照 网站 里面的案例进行使用,还是有免费的找找就行:
然后这里我们将本次使用的Loading加载组件代码也分享出来,供大家使用:
<template>
<div class="load">
<div class="loading">
<div class="vsg">
<svg viewBox="0 0 50 50">
<circle class="ring" cx="25" cy="25" r="20"></circle>
<circle class="ball" cx="25" cy="5" r="3.5"></circle>
</svg>
</div>
<div class="text">
Loading
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.load {
width: 100%;
height: 100%;
position: absolute;
z-index: 3;
display: flex;
justify-content: center;
align-items: center;
background-color: rgb(0, 0, 0);
.loading {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.vsg {
display: flex;
justify-content: center;
align-items: center;
@keyframes spin {
to {
transform: rotate(360deg);
}
}
svg {
width: 3.75em;
animation: 1.5s spin ease infinite;
}
.ring {
fill: none;
stroke: hsla(106, 97%, 59%, 0.3);
stroke-width: 2;
}
.ball {
fill: #00ff85;
stroke: none;
}
}
.text {
text-align: center;
font-size: 16px;
background: linear-gradient(to right, #00ff85, #5090ff);
background-clip: text;
-o-background-clip: text;
-ms-background-clip: text;
-moz-background-clip: text;
-webkit-background-clip: text;
color: transparent;
}
}
}
</style>
接下来我们在App.vue组件进行引入loading组件,如下:
回到浏览器可以看到我们的loading组件已经加载出来了,后期可以根据情况使用异步加载的组件或者使用v-if来进行操作,博主这里不再赘述:
路由配置管理
我们在开始搭建后台项目之前,都要仔细考虑一下路由应该如何配置,以下是本项目配置路由的详细步骤,如果想更加深入了解vue3路由知识,推荐参考我之前的文章:Vue 3 路由进阶——从基础到高级的完整指南:
安装配置路由的第三方包:
pnpm install vue-router
安装完路由包之后,接下来我们要先创建路由组件,因为项目仅仅是一个后台所以我们只需要在src目录下新进views文件,再创建3个路由组件即可:
创建完路由组件之后,接下来开始进行相关router配置:
// 通过vue-router插件实现模板路由配置
import { createRouter, createWebHistory } from 'vue-router'
import { constantRoute } from './routes'
// 创建路由器
const router = createRouter({
// 路由模式
history: createWebHistory(),
routes: constantRoute,
// 滚动行为
scrollBehavior() {
return {
left: 0,
top: 0,
}
},
})
export default router
这里我单独将routes的属性对象抽离出一个js文件出来 ,便于后期的维护,代码如下:
// 对外暴露路由(常量路由)
export const constantRoute = [
{
path: '/login',
name: 'login', // 命名路由
component: () => import('@/views/login/index.vue'),
meta: {
title: '登录页面',
},
},
{
path: '/',
name: 'home',
component: () => import('@/views/home/index.vue'),
meta: {
title: '后台页面',
},
},
{
path: '/404',
name: '404',
component: () => import('@/views/404/index.vue'),
meta: {
title: '404界面',
},
},
{
path: '/:pathMatch(.*)*',
name: 'any',
redirect: '/404',
},
]
接下来在入口文件 main.ts 处进行路由router的挂载:
如果想把写好的路由进行展示的话,需要通过 router-view 作为路由出口,路由匹配到的组件将进行响应的渲染,这里在App根组件上进行展示:
最终呈现的效果如下所示:
store仓库配置
接下来我们需要对我们项目进行一个仓库管理,这里用到了pinia状态管理工具,不了解pinia的朋友,推荐看一下我之前讲解的文章:探索Pinia:Vue状态管理的未来 ,本项目就不再着重讲解其具体知识了。具体讲解如下:
终端执行如下命令安装pinia仓库:
pnpm install pinia
在src目录下新建store文件夹,文件夹下新建 index.ts 文件,暴露如下代码:
// 设置pinia仓库
import { createPinia } from 'pinia'
const pinia = createPinia()
// 对外暴露:入口文件需要安装仓库
export default pinia
在main.ts入口文件出进行pinia的挂载:
import { createApp } from 'vue'
import 'reset.css'
import App from './App.vue'
// 配置国际化
import ElementPlus from 'element-plus'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
// 引入自定义插件对象:注册整个项目的全局组件
import globalComponents from '@/components'
// 导入路由配置
import router from '@/router'
// 导入仓库
import pinia from '@/store'
createApp(App)
.use(ElementPlus, { locale: zhCn }) // 配置中文国际化
.use(globalComponents) // 安装全局路由组件
.use(router) // 安装路由
.use(pinia) // 安装仓库
.mount('#app')
接下来在store中新建文件夹modules用于存储仓库文件,在该文件夹创建一个login.js文件,用来存储登录相关数据,这里采用组合式API的写法,如下案例:
import { defineStore } from "pinia";
import { ref } from "vue"
export const useMemberStore = defineStore("login", () => {
const profile = ref();
const setProfile = (val: number) => {
profile.value = val;
}
const clearProfile = () => {
profile.value = undefined
}
return { profile, setProfile, clearProfile }
})
git管理配置
接下来我们需要对我们上面撰写的项目初始化模板进行一个git上传,进行版本管理操作,这里我们使用可视化工具 github desktop 工具进行操作,不了解这个工具的参考:文章 ,里面详细记录了这个工具的操作,然后我们先创建存储库:
点击新建存储库之后,这里我们需要给仓库中输入一些相应的信息,这里我将之前写的一个项目作为本地路径上传上去,然后点击新建存储库即可:
然后这里我们点击右上角的发布存储库按钮,将本地创建的存储库推送到远程仓库上:
点击之后会出现弹框,这里我们需要取消勾选,让仓库成为公共仓库,任何人都能访问到,然后点击发布即可:
打开github之后,就可以看到我们的创建的仓库已经出现了:
然后我们把之前写的一个电商项目里面的文件copy一下放到仓库当中去,如下github desktop出现了我们文件的差异提示,每个更改的文件以及里面具体的内容都能看到, 如下:
刷新浏览器之后,可以看到我们的项目已经全部推送到远程仓库上去了,很方便不需要再像git命令行那样一个一个的命令去敲:
后期我们在本地进行什么样的修改,github desktop工具都能识别到我们哪些地方进行了修改,如下我在本地代码文件中,main.ts文件下面输入两行空行,工具都识别到我的更改,是不是很方便!
注意:如果已经学习过我上一个项目的朋友可能已经了解过项目开始前的一些基础配置,觉得我再在新的项目再讲一遍有点啰嗦,但是我仍然坚持不厌其烦的去再讲一遍,一方面是培养自己的习惯另一方面是想不断加深读者对项目开始前基础配置的熟练程度,希望大家能够在小事中获得经验。