本篇文章主要介绍一个后端开发(我),是如何一步一个坑的使用前端技术Vue3
搭建前端项目的。
首先提一下技术选型,现在前端技术框架真的是如雨后春笋啊!对于我这种半吊子来说,了解的越多越迷茫,最后还是选则了以前有用过的vue
,使用了比较新的vue3
,用起来才发现,和以前的vue2
差别好大呀!完全有点无从下手,还专门找了个B站教学视频简单了看了一下(【vue3+Typescript项目实战】ps:感觉老师单词老是打错😂)。
sh# 初始化vue项目。没有vue环境会提示安装,选择`y`,自动安装
npm init vue@latest
shNeed to install the following packages:
[email protected]
Ok to proceed? (y) y
# 会让你先输入项目名,此处输入books
# 下列为选择环境安装,根据自身所需选择 yes or no 即可
√ Add TypeScript? ... No / Yes
√ Add JSX Support? ... No / Yes
√ Add Pinia for state management? ... No / Yes
√ Add Vitest for Unit Testing? ... No / Yes
√ Add Cypress for both Unit and End-to-End testing? ... No / Yes
√ Add ESLint for code quality? ... No / Yes
√ Add Prettier for code formatting? ... No / Yes
Scaffolding project in E:\code\DingDangDog_sapce\ddd-cashbook\books...
Done. Now run:
cd books
npm install
npm run dev
books
文件夹(上一步创建时,根据输入的项目名自动创建的源码文件夹),执行以下命令sh# 1、安装依赖
npm install
# 输出
added 258 packages in 16s
# 2、运行
npm run dev
# 输出
> [email protected] dev
> vite
VITE v3.2.2 ready in 433 ms
➜ Local: http://localhost:5173/
➜ Network: use --host to expose
/src/components/FlowTable.vue
<script setup lang="ts">
这个叫做语法糖
新东西?着实难为了我好久!!!html<template>
<div>{{ flowPage }}
</div>
</template>
<script setup lang="ts">
import type { FlowPage } from '../types/flow';
// 初始化假数据,用于展示验证
const flowPage: FlowPage = {
pageNum: 1,
pageSize: 1,
dataList: [{
id: 1,
day: new Date(),
name: "测试",
description: "测试流水",
money: 1,
type: "测试类型",
}],
totalNum: 1
};
// 将需要对外暴露的方法和对象添加到这里
defineExpose({
flowPage
});
</script>
<style scoped>
</style>
/src/App.vue
文件中添加刚才新建的文件,并使用,如下代码中的import FlowTable from './components/FlowTable.vue'
和<FlowTable/>
html<script setup lang="ts">
import HelloWorld from './components/HelloWorld.vue'
import TheWelcome from './components/TheWelcome.vue'
import FlowTable from './components/FlowTable.vue'
</script>
<template>
<header>
<img alt="Vue logo" class="logo" src="./assets/logo.svg" width="125" height="125" />
<div class="wrapper">
<FlowTable/>
<HelloWorld msg="You did it!" />
</div>
</header>
<main>
<TheWelcome />
</main>
</template>
<style scoped>
header {
line-height: 1.5;
}
.logo {
display: block;
margin: 0 auto 2rem;
}
@media (min-width: 1024px) {
header {
display: flex;
place-items: center;
padding-right: calc(var(--section-gap) / 2);
}
.logo {
margin: 0 2rem 0 0;
}
header .wrapper {
display: flex;
place-items: flex-start;
flex-wrap: wrap;
}
}
</style>
npm run dev
即可建议先具备一个正常访问的后端服务,后续会有跨域调用测试。
经过以上步骤,基本架构路线只差最后一步,就是调用真实的数据接口,将上一步展示的数据替换成接口获取到的数据,如果这一步走通了,剩下的就是填充代码的问题了。接下来就来做这一步吧!
/src/api/index.ts
axios-http调用工具封装
应该能找到很多类似的源码,以下代码是我看了个教程自己手打:typescript/**
* 封装http请求工具
*/
import axios from 'axios'
import { ElMessage } from 'element-plus'
// 创建http调用者
const $http = axios.create({
// 服务地址
baseURL: 'http://localhost:3000/api',
timeout: 2000,
headers: {
"Content-Type": "application/json;chartset=utf-8"
}
})
// 拦截请求,为请求header中增加token【暂时无用】
$http.interceptors.request.use(config => {
// baseURL/headers 属性可能不存在,若不存在设置默认值
config.baseURL = config.baseURL || '';
config.headers = config.headers || {};
// 如果访问登录接口,则清除当前缓存token
if (config.baseURL?.indexOf('/login') >= 0) {
localStorage.removeItem('token');
config.headers.token = null;
return config;
}
if (localStorage.getItem('token')) {
config.headers.token = localStorage.getItem('token') || '';
}
return config;
})
// 响应拦截:解析响应结果,返回数据或捕获异常
$http.interceptors.response.use(res => {
if (res.data.code != 200) {
ElMessage.error(res.data.message);
return Promise.reject(res.data);
}
return res.data
}, err => {
console.log(err);
})
export default $http;
/src/api/api.ts
typescriptimport $http from './index'
import type { FlowPage } from '../types/flow';
/**
* 查询流水
* @returns FlowPage
*/
export async function getFlows(): Promise<FlowPage> {
return $http({ url: "/flow/getAll", method: "get" })
}
FlowTable.vue
文件html<template>
<div>{{ flowPage }}
</div>
</template>
<script setup lang="ts">
import { getFlows } from '../api/api'
import type { FlowPage } from '../types/flow';
// 初始化假数据,用于展示验证
// const flowPage: FlowPage = {
// pageNum: 1,
// pageSize: 1,
// dataList: [{
// id: 1,
// day: new Date(),
// name: "测试",
// description: "测试流水",
// money: 1,
// type: "测试类型",
// }],
// totalNum: 1
// };
const flowPage: FlowPage = await getFlows();
// 将需要对外暴露的方法和对象添加到这里
defineExpose({
flowPage
});
</script>
<style scoped>
</style>
typescript
文件不能写src/api
,启动会报找不到文件的错,所以我都改成了../api
,报错如下:sh21:48:26 [vite] Internal server error: Failed to resolve import "src/api/api" from "src\components\FlowTable.vue". Does the file exist?
Plugin: vite:import-analysis
File: E:/code/DingDangDog_sapce/ddd-cashbook/books/src/components/FlowTable.vue:7:25
1 | import { withAsyncContext as _withAsyncContext, defineComponent as _defineComponent } from "vue";
2 | import { getFlows } from "src/api/api";
| ^
3 | const _sfc_main = /* @__PURE__ */ _defineComponent({
4 | __name: "FlowTable",
at formatError (file:///E:/code/DingDangDog_sapce/ddd-cashbook/books/node_modules/vite/dist/node/chunks/dep-c842e491.js:41168:46)
at TransformContext.error (file:///E:/code/DingDangDog_sapce/ddd-cashbook/books/node_modules/vite/dist/node/chunks/dep-c842e491.js:41164:19)
at normalizeUrl (file:///E:/code/DingDangDog_sapce/ddd-cashbook/books/node_modules/vite/dist/node/chunks/dep-c842e491.js:38032:33)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async TransformContext.transform (file:///E:/code/DingDangDog_sapce/ddd-cashbook/books/node_modules/vite/dist/node/chunks/dep-c842e491.js:38165:47)
at async Object.transform (file:///E:/code/DingDangDog_sapce/ddd-cashbook/books/node_modules/vite/dist/node/chunks/dep-c842e491.js:41421:30)
at async loadAndTransform (file:///E:/code/DingDangDog_sapce/ddd-cashbook/books/node_modules/vite/dist/node/chunks/dep-c842e491.js:37808:29)
npm run dev
F12
,可以看到接口报错了,但是你又会发现,错误码是200
,什么鬼,200
不是成功吗?此时就是遇到了前后端分离必然的、闻名不如见面的跨域问题
!跨域问题
,Postman
截图为证:【vue技巧(九)跨域及跨域配置不生效原因探究】感谢这篇博客最后的总结!!!
推荐再axios请求发送时不要添加ip port,而是再代理转发时通过target参数配置
修改index.ts文件
,把baseURL
中的http://localhost:3000
删掉即可,修改后如下
typescript/**
* 封装http请求工具
*/
import axios from 'axios'
import { ElMessage } from 'element-plus'
// 创建http调用者
const $http = axios.create({
baseURL: '/api',
timeout: 2000,
headers: {
"Content-Type": "application/json;chartset=utf-8"
}
})
// 拦截请求,为请求header中增加token
$http.interceptors.request.use(config => {
// baseURL/headers 属性可能不存在,若不存在设置默认值
config.baseURL = config.baseURL || '';
config.headers = config.headers || {};
// 如果访问登录接口,则清除当前缓存token
if (config.baseURL?.indexOf('/login') >= 0) {
localStorage.removeItem('token');
config.headers.token = null;
return config;
}
if (localStorage.getItem('token')) {
config.headers.token = localStorage.getItem('token') || '';
}
return config;
})
// 响应拦截:解析响应结果,返回数据或捕获异常
$http.interceptors.response.use(res => {
if (res.data.code != 200) {
ElMessage.error(res.data.message);
return Promise.reject(res.data);
}
return res.data
}, err => {
console.log(err);
})
export default $http;
修改vite.config.ts
文件
typescriptimport { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
server: {
// 本服务暴露的端口
host: '127.0.0.1',
port: 8080,
// 跨域配置
proxy: {
// /api相关接口跨域配置
'/api': {
//实际请求地址
target: 'http://127.0.0.1:3000/api',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, "")
},
}
}
})
App.vue
(即引用异步组件的文件),FlowTable.vue
的引用和使用方式均有改变。。html<script setup lang="ts">
import HelloWorld from './components/HelloWorld.vue'
import TheWelcome from './components/TheWelcome.vue'
import { defineAsyncComponent } from 'vue'
const FlowTable = defineAsyncComponent(() => import("./components/FlowTable.vue"));
</script>
<template>
<header>
<img alt="Vue logo" class="logo" src="./assets/logo.svg" width="125" height="125" />
<div class="wrapper">
<Suspense>
<template #default>
<FlowTable/>
</template>
<template #fallback>
<div>加载中...</div>
</template>
</Suspense>
<HelloWorld msg="You did it!" />
</div>
</header>
<main>
<TheWelcome />
</main>
</template>
<style scoped>
header {
line-height: 1.5;
}
.logo {
display: block;
margin: 0 auto 2rem;
}
@media (min-width: 1024px) {
header {
display: flex;
place-items: center;
padding-right: calc(var(--section-gap) / 2);
}
.logo {
margin: 0 2rem 0 0;
}
header .wrapper {
display: flex;
place-items: flex-start;
flex-wrap: wrap;
}
}
</style>
本文作者:DingDangDog
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!