前端项目--尚医通学习分享

这段时间跟着线上课程完成了一个项目:商医通(基于Vue3+TypeScript的医院挂号平台)。具体我就不过多地介绍其具体功能以及详细的实现步骤了,感兴趣的小伙伴直接:传送门 。该文章我就分享一下在该项目中学习到的一些知识点,以及一些前端小伙伴比较常接触到的功能。

目录

    • 项目介绍
      • 项目实现效果如下:
      • 项目的技术选型如下:
    • 项目总结:
      • 一、Vue3中的一些基础语法?
      • 二、Vite构建工具如何使用?
      • 三、TypeScript的基础语法有哪些?
      • 四、如何使用Vue-router进行路由的配置?
      • 五、如何使用Pinia来进行数据状态的管理?
      • 六、element-plus组件库该如何使用?
      • 七、如何对axios进行二次封装来实现向服务器请求数据?

项目介绍

项目实现效果如下:

在这里插入图片描述
在这里插入图片描述

项目的技术选型如下:

在这里插入图片描述

项目总结:

一、Vue3中的一些基础语法?

官方文档
由于该项目是使用Vue3实现的,因此需要具备Vue3的一些基础知识,但是Vue3中的知识点不会介绍太多,主要介绍在这个项目中接触到的比较多的部分知识。

1.Vue3与Vue2创建实例方法的不同:

//Vue3中的写法:
import {createAPP} from 'vue'
import App from './App.vue'
createApp(App).mount("#app")//Vue2中的写法:
import Vue from 'vue'
import App from './App.vue'
new Vue({el:"#app",render:h=>h(APP)
})

2.Vue3中setup的作用:

理解:Vue3.0中一个新的配置项,值为一个函数,setup是所有Composition API(组合API)“ 表演的舞台 ”。组件中所用到的:数据、方法等等,均要配置在setup中。

setup函数的两种返回值:若返回一个对象,则对象中的属性、方法, 在模板中均可以直接使用。(重点关注!)若返回一个渲染函数:则可以自定义渲染内容。(了解)

注意点:尽量不要与Vue2.x配置混用。Vue2.x配置(data、methos、computed…)中可以访问到setup中的属性、方法。但在setup中不能访问到Vue2.x配置(data、methos、computed…)。如果有重名, setup优先。setup不能是一个async函数,因为返回值不再是return的对象, 而是promise, 模板看不到return对象中的属性。(后期也可以返回一个Promise实例,但需要Suspense和异步组件的配合)

3.Vu3中ref函数的作用:

作用: 定义一个响应式的数据。语法: const xxx = ref(initValue)。创建一个包含响应式数据的引用对象(reference对象,简称ref对象)。JS中操作数据: xxx.value。模板中读取数据: 不需要.value,直接:<div>{{xxx}}</div>

备注:接收的数据可以是:基本类型、也可以是对象类型。基本类型的数据:响应式依然是靠Object.defineProperty()getset完成的。对象类型的数据:内部 “ 求助 ” 了Vue3.0中的一个新函数—— reactive函数。

4.Vue3中的reactive函数:

作用: 定义一个对象类型的响应式数据(基本类型不要用它,要用ref函数)语法:const 代理对象= reactive(源对象)接收一个对象(或数组),返回一个代理对象(Proxy的实例对象,简称proxy对象)

reactive定义的响应式数据是“深层次的”。内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作。

5.reactive与ref的比较:

从定义数据角度对比:ref用来定义:基本类型数据。reactive用来定义:对象(或数组)类型数据

备注:ref也可以用来定义对象(或数组)类型数据, 它内部会自动通过reactive转为代理对象

从原理角度对比:ref通过Object.defineProperty()getset来实现响应式(数据劫持)。reactive通过使用Proxy来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据。

从使用角度对比:ref定义的数据:操作数据需要.value,读取数据时模板中直接读取不需要.value。reactive定义的数据:操作数据与读取数据:均不需要.value

6.setup的两个注意点:

setup执行的时机,在beforeCreate之前执行一次,this是undefined。

setup的参数:props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性。context:上下文对象。attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于 this.$attrs。slots: 收到的插槽内容, 相当于 this.$slots。emit: 分发自定义事件的函数, 相当于 this.$emit

二、Vite构建工具如何使用?

1.Vite官网

2.为什么使用Vite:

一句话总结:使用vite构建项目,启动的速度要比使用webpack构建更快。

之前浏览器是不支持ES Modules的,为了在让浏览器能够运行我们写的代码(es6语法、.jsx/.vue文件),我们需要使用打包工具,例如webpack,来实现代码的转换和优化的过程;

在浏览器支持ES Modules后,import、export、<script type='modules'>等成为vite出现的条件;

vite 主要对应的场景是开发模式,它只启动一台静态页面的服务器,对文件代码不打包,服务器会根据客户端的请求加载不同的模块处理;

底层实现上,Vite 是基于 esbuild 预构建依赖的。当声明一个script标签类型为module时,浏览器将对其内部的import引用发起HTTP请求获取模块内容;

Vite 劫持了这些请求,并在后端进行相应的处理(如处理.ts文件为.js文件),然后再返回给浏览器;

由于浏览器只会对用到的模块发起 HTTP 请求,所以 Vite 没必要对项目里所有的文件先打包后返回,而是只编译浏览器发起 HTTP 请求的模块即可。也就实现了所谓的按需加载。

三、TypeScript的基础语法有哪些?

一般在做项目都会使用到TypeScript,因为其有更加严格的类型限制,易于后期维护,同时也很适合于Vue3一同开发。具体基础知识可以看我之前发布的文章:就是这里

四、如何使用Vue-router进行路由的配置?

在项目中比较常见的场景为:通过点击左侧的导航栏来实现切换不同的页面。主要是在一级路由中配置多个二级的路由来进行实现。以本次项目中的医院页面为例:其具体的文件存放位置如下图:一级路由一般存放在src文件夹下建立的pages文件夹下,一级路由命名为hospital,其下的二级路由为close,detail,notice等。

在这里插入图片描述

首先在各自的二级路由中编写各自页面的内容,以search页面为例子:

<template><div><div class="info"><h1 >{{ hospitalStore.hospitalInfo.hospital?.hosname }}停诊信息</h1><el-empty description="暂无信息"/></div></div>
</template><script setup lang="ts">
import useDetailStore from '@/store/modules/hospitalDetail';
let hospitalStore = useDetailStore();
</script><style scoped lang="scss">
.info{h1{text-align: center;font-size: 30px;}
}
</style>

在一级路由组件的文件中,需要使用来存放其二级路由相应的内容,而左侧的导航栏,为每一个导航绑定一个点击事件,该事件能够实现路由的跳转,通过传入相应的路径参数来实现通过点击不同的内容而显示相应的内容。

<template><div class="hospital"><!-- 左侧导航区域 --><div class="menu"><div class="top"><el-icon><HomeFilled/></el-icon><span>/ 医院信息</span></div><el-menu :default-active="$route.path" class="el-menu-vertical-demo"><el-menu-item index="/hospital/register" @click="changeActive('/hospital/register')"><el-icon><icon-menu /></el-icon><span>预约挂号</span></el-menu-item><el-menu-item index="/hospital/detail" @click="changeActive('/hospital/detail')"><el-icon><document /></el-icon><span>医院详情</span></el-menu-item><el-menu-item index="/hospital/notice" @click="changeActive('/hospital/notice')"><el-icon><setting /></el-icon><span>预约通知</span></el-menu-item><el-menu-item index="/hospital/close" @click="changeActive('/hospital/close')"><el-icon><InfoFilled /></el-icon><span>停诊信息</span></el-menu-item><el-menu-item index="/hospital/search" @click="changeActive('/hospital/search')"><el-icon><Search /></el-icon><span>查询/取消</span></el-menu-item></el-menu></div><!-- 右侧内容展示区域:路由组件展示位置 --><div class="content"><router-view></router-view></div></div>
</template><script setup lang="ts">
import {onMounted} from 'vue';
import useDetailStore from '@/store/modules/hospitalDetail';
// 获取仓库对象
let detailStore =useDetailStore();
import {Document,Menu as IconMenu,InfoFilled,Setting,Search,
HomeFilled,
} from '@element-plus/icons-vue';
import { useRouter,useRoute } from 'vue-router';
let $router=useRouter();
let $route=useRoute();
const changeActive=(path:string)=>{$router.push({path,query:{hoscode:$route.query.hoscode}})
};
onMounted(()=>{detailStore.getHospital($route.query.hoscode as string);// 获取某一个医院科室的数据detailStore.getDeparment($route.query.hoscode as string);})
</script>

最后还需要创建一个router文件来专门对路由进行配置,以上展现的部分相应的路由配置如下:

import { createRouter, createWebHistory } from "vue-router";
export default createRouter({// 路由模式history: createWebHistory(),routes: [{path: '/hospital',component: () => import('@/pages/hospital/index.vue'),children: [{path: 'register',component: () => import('@/pages/hospital/register/index.vue')},{path: 'detail',component: () => import('@/pages/hospital/detail/index.vue')},{path: 'notice',component: () => import('@/pages/hospital/notice/index.vue')},{path: 'close',component: () => import('@/pages/hospital/close/index.vue')},{path: 'search',component: () => import('@/pages/hospital/search/index.vue')},{path: 'register_step1',component: () => import('@/pages/hospital/register/register_step1.vue')},{path: 'register_step2',component: () => import('@/pages/hospital/register/register_step2.vue')}]]
})

五、如何使用Pinia来进行数据状态的管理?

Pinia与Vuex的作用一样,它们都充当存储数据的作用,存储在Pinia中数据允许我们在各个组件中使用。

npm install pinia下载之后在main.js中进行引入:

import { createPinia } from "pinia";
const pinia = createPinia();
app.use(pinia);

然后再src下创建一个store文件夹用于存放各种store。以用户为例,在store文件夹下创建一个user.ts文件。

import {defineStore} from "pinia";
import {reqCode,reqUserLogin} from '@/api/hospital';
import  type {LoginData,UserLoginResponseData,UserInfo} from '@/api/hospital/type'
import type { UserState } from "./interface";
import {GET_TOKEN} from '../../utils/user'
const useUserStore =defineStore('User',{state:():UserState=>{return{visiable:false,code:'',userInfo:JSON.parse(localStorage.getItem('USERINFO') as string)||{}}},actions:{// 获取验证码的方法async getCode(phone:string){let result:any= await reqCode(phone);if(result.code==200){this.code=result.data;return 'ok';}else{return Promise.reject(new Error(result.message))}},//用户手机号码登录方法async userLogin(loginData:LoginData){let result:UserLoginResponseData= await reqUserLogin(loginData);if(result.code==200){this.userInfo=result.data;//本地存储持久化存储用户信息localStorage.setItem('USERINFO',JSON.stringify(this.userInfo));return 'ok';}else{return Promise.reject(new Error(result.message))}},//退出登录方法logout(){//清空仓库的数据this.userInfo={name:'',token:''};//清空本地存储的数据localStorage.removeItem('USERINFO');},queryState(){let timer=setInterval(()=>{if(GET_TOKEN()){this.visiable=false;this.userInfo=JSON.parse(GET_TOKEN() as string);clearInterval(timer);}},1000);}},getters:{}
});
export default useUserStore;

由于Pinia不是持久化的存储,因此为了让数据在页面刷新之后不会出现数据丢失的问题,使用了localStorage来对仓库中的数据进行持久化的存储。

在其他的组件中使用时,只需要使用以下的方式进行读取即可。

<template><p class="login" @click="login" v-if="!userStore.userInfo.name">登录/注册</p>
</template><script setup lang="ts">
import useUserStore from '@/store/modules/user';
let userStore = useUserStore();
</script>

state和getters属性都主要是数据层面的,并没有具体的业务逻辑代码,它们两个就和我们组件代码中的data数据和computed计算属性一样。那么,如果我们有业务代码的话,最好就是写在actions属性里面,该属性就和我们组件代码中的methods相似,用来放置一些处理业务逻辑的方法。actions属性值同样是一个对象,该对象里面也是存储的各种各样的方法,包括同步方法和异步方法。

六、element-plus组件库该如何使用?

相应官网:一个 Vue 3 UI 框架 | Element Plus (element-plus.org)

首先需要先导入其对应的包在main.js中导入如下内容:(Element Plus 组件 默认 使用英语,如果你希望使用中文,可以导入国际化文件。)

import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
// 国际化文件
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
app.use(ElementPlus,{locale: zhCn,
});

然后只需要搜素你所需要的内容,然后根据文档的提示将其引入到你自己的组件中即可。例如想要实现如下的搜素效果:

在这里插入图片描述

首先在官网中搜索search输入框,之后会出现各种各样的输入框,选择具有远程搜索功能的输入框,点击相应的代码查看,将相应的代码如下到自己的组件中,通过查看官方提供的代码以及API功能介绍进行补充以及修改:

 <el-autocompletev-model="state":fetch-suggestions="querySearchAsync"placeholder="Please input"@select="handleSelect"/>

v-model属性为选中项绑定值,fetch-suggestions获取输入建议的方法, 仅当你的输入建议数据 resolve 时,通过调用 callback(data:[]) 来返回它。也就是在这个方法中当输入框输入完数据之后就回调用它,此时就需要在这个方法中获取到输入框中需要的数据,在通过传给回调函数,让其展现在相应的搜索框下方。该方法具有两个参数:(queryString: string, cb: (arg: any) => void),第一个为数据的内容,第二个为回调函数。

const loadAll = () => {return [{ value: 'vue', link: 'https://github.com/vuejs/vue' },{ value: 'element', link: 'https://github.com/ElemeFE/element' },]
}

通过官网例子提供的代码我们知道在若需要在搜索框出现,则需要参数名为value,而我们通过发送请求返回来的数据参数名并不是value,因此后续我们还需要对数据进行处理之后才能够给回调函数。 @select="handleSelect"为选择其中的某一项时会触发的事件。

因此我们结合自己的项目编写的代码如下:

<template><div class="search"><el-autocomplete @select="goDetail" :trigger-on-focus="false" clearable placeholder="请输入医院名称" v-model="hosname":fetch-suggestions="fetchData" /><el-button type="primary" size="default" :icon="Search">搜索</el-button></div>
</template><script setup lang="ts">
import { Search } from '@element-plus/icons-vue';
import { useRouter } from 'vue-router';
import { ref } from 'vue';
import { reqHospitalInfo } from '@/api/home';
import type { HospitalInfo } from '@/api/home/type';
// 创建路由对象
let $router = useRouter();
// 收集搜素的关键字
let hosname = ref<string>('');
// 顶部组件的回调
const fetchData = async (keyword: string, cb: any) => {let result: HospitalInfo = await reqHospitalInfo(keyword);let showData = result.data.map(item => {return {value: item.hosname,hoscode: item.hoscode}})cb(showData);
}
// 点击某一个推荐项
const goDetail = (item: any) => {$router.push({ path: '/hospital/register' ,query:{hoscode:item.hoscode}})
}
</script>

因此其他的样式以及布局也可以通过在其中寻找相应的组件来完成,这样可以减少很多的开发时间。

七、如何对axios进行二次封装来实现向服务器请求数据?

对原生的axios进行二次封装

//对于axios函数库进行二次封装?
//你工作的时候是否axios进行二次封装?二次封装的目的是什么那?
//目的:1,利用axios请求、响应拦截器功能
//目的2:请求拦截器,一般可以在请求头中携带公共的参数:token
//目的3:响应拦截器,可以简化服务器返回的数据,处理http网络错误
import axios from 'axios';
//引入用户相关的仓库
import useUserStore from '@/store/modules/user';
//@ts-ignore
import { ElMessage } from 'element-plus';
//利用axios.create方法创建一个axios实例:可以设置基础路径、超时的时间的设置
const request = axios.create({baseURL: '/api',//请求的基础路径的设置timeout: 5000//超时的时间的设置,超出五秒请求就是失败的
});//请求拦截器
request.interceptors.request.use((config) => {//获取用户仓库let userStore = useUserStore();//token:公共参数,如果用户登录了需要携带if (userStore.userInfo.token) {config.headers.token = userStore.userInfo.token;}//config:请求拦截器回调注入的对象(配置对象),配置对象的身上最终要的一件事情headers属性//可以通过请求头携带公共参数-tokenreturn config;
});
//响应拦截器
request.interceptors.response.use((response) => {//响应拦截器成功的回调,一般会进行简化数据return response.data;
}, (error) => {//处理http网络错误let status = error.response.status;switch (status) {case 404://错误提示信息ElMessage({type: 'error',message: '请求失败路径出现问题'})break;case 500 | 501 | 502 | 503 | 504 | 505:ElMessage({type: 'error',message: '服务器挂了'})break;case 401:ElMessage({type: 'error',message: '参数有误'})break;}return Promise.reject(new Error(error.message))
});
//务必对外暴露axios
export default request;

对请求封装成相应的函数

import request from "@/utils/request";
import { get } from "http";
import type{HospitalResponseData,HospitalLevelAndRegionResponseData,HospitalInfo} from './type'enum API{// 获取已有医院数据接口HOSPITAL_URL='/hosp/hospital/',// 获取医院等级与地区接口HOSPITALLEVELANDREGION_URL='/cmn/dict/findByDictCode/',// 根据关键词搜素医院HOSPITALINFO_URL='/hosp/hospital/findByHosname/'}
export const reqHospital =  (page:number,limit:number,hostype='',districtCode='')=>request.get<any,HospitalResponseData>(API.HOSPITAL_URL+`${page}/${limit}?hostype=${hostype}&districtCode=${districtCode}`)
export const reqHospitalLevelAndRegion=(dictCode:string)=>request.get<any,HospitalLevelAndRegionResponseData>(API.HOSPITALLEVELANDREGION_URL+dictCode);
export const reqHospitalInfo=(hosname:string)=>request.get<any,HospitalInfo>(API.HOSPITALINFO_URL+hosname)

在对应的地方进行调用(可以在onMounted函数中编写方法,若需要多次的调用建议先封装成一个函数,在onMounted中进行调用)

为何使用async以及await与axios进行结合,因为axios异步请求会出现请求的数据来不及渲染到页面的情况,因此使用async以及await来处理异步请求。

onMounted(() => {getLevel();});
const getLevel = async () => {let result: HospitalLevelAndRegionResponseData = await reqHospitalLevelAndRegion('HosType')if (result.code == 200) {levelArr.value = result.data;}
};

好啦!本次文章就分享到这里了,希望对你有一定的帮助!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/131750.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Fisher辨别分析

问题要求 在UCI数据集上的Iris和Sonar数据上验证算法的有效性。训练和测试样本有三种方式&#xff08;三选一&#xff09;进行划分&#xff1a; &#xff08;一&#xff09; 将数据随机分训练和测试&#xff0c;多次平均求结果 &#xff08;二&#xff09;K折交叉验证 &…

Session共享问题

出现这个问题的原因&#xff0c;从根本上来说是因为Http协议是无状态的协议。客户端和服务端在某次会话中产生的数据不会被保留下来&#xff0c;所以第⼆次请求服务端无法认识到你曾经来过&#xff0c; Http为什么要设计为无状态协议&#xff1f;早期都是静态页面无所谓有无状态…

UE4和C++ 开发-C++绑定widget的方式和初始化UI

C绑定widget的方式有两种&#xff0c;一种是使用meta (BindWidget)&#xff0c;一种是使用GetWidgetFromName(TEXT("")),两种方式都可以。一、meta BindWidget方式 注意这种绑定的方式UMG里面的空间名称需要与C里面声明的变量名称相同 Btn_StartU 二、GetWidge…

Kopler.gl笔记:可视化功能总览

1 添加数据 2 添加图层 打开“数据层”菜单&#xff0c;开始可视化。 层&#xff08;Layers&#xff09;简单来说就是可以相互叠加的数据可视化。 3 添加过滤器 在地图上添加过滤器以限制显示的数据。过滤器必须基于数据集中的列。要创建新的过滤器&#xff0c;打开“过滤器…

保姆式教程:MAC安装Android studio(包括安装JDK,Android SDK),解决gradle下载慢的问题

文章目录 参考文章安装JDK并配置环境变量安装JDK配置JDK相关的环境变量 Android studio 安装下载Android studiogradle下载慢解决方法 安装Android SDK选择jdk版本安装SDK并配置环境变量 参考文章 原文链接 原文链接 安装JDK并配置环境变量 安装JDK 下载地址 下载后双击安装…

基于SpringBoot的在线试题库系统设计与实现

目录 前言 一、技术栈 二、系统功能介绍 学生管理 教师管理 专业管理 试卷管理 试题管理 考试管理 错题本 考试记录 三、核心代码 1、登录模块 2、文件上传模块 3、代码封装 前言 使用旧方法对作业管理信息进行系统化管理已经不再让人们信赖了&#xff0c;把现在的…

Docker系列--网络的配置

原文网址&#xff1a;Docker系列--网络的配置_IT利刃出鞘的博客-CSDN博客 简介 说明 本文介绍Docker的网络的配置。 官网网址 https://docs.docker.com/engine/reference/commandline/network/ 网络的默认设置 Docker启动之后&#xff0c;系统中会产生一个名为docker0的…

初识华为云数据库GaussDB for openGauss

01 前言 GaussDB是华为自主创新研发的分布式关系型数据库。该产品具备企业级复杂事务混合负载能力&#xff0c;同时支持分布式事务&#xff0c;同城跨AZ部署&#xff0c;数据0丢失&#xff0c;支持1000的扩展能力&#xff0c;PB级海量存储。同时拥有云上高可用&#xff0c;高可…

排序算法-冒泡排序法(BubbleSort)

排序算法-冒泡排序法&#xff08;BubbleSort&#xff09; 1、说明 冒泡排序法又称为交换排序法&#xff0c;是从观察水中的气泡变化构思而成的&#xff0c;原理是从第一个元素开始&#xff0c;比较相邻元素的大小&#xff0c;若大小顺序有误&#xff0c;则对调后再进行下一个…

排序算法-希尔排序法(ShellSort)

排序算法-希尔排序法&#xff08;ShellSort&#xff09; 1、说明 我们知道当原始记录的键值大部分已排好序的情况下插入排序法非常有效&#xff0c;因为它不需要执行太多的数据搬移操作。希尔排序法是D.L.Shell在1959年7月发明的一种排序法&#xff0c;可以减少插入排序法中数…

【高等数学】极限(上)(最全万字详解)

文章目录 1、数列的极限1.1、数列极限的定义1.2、为什么收敛数列极限是唯一的&#xff1f;1.3、为什么收敛数列是有界的&#xff1f;1.4、数列极限的保号性1.4.1、极限保数列值1.4.2、数列值保极限值 1.5、收敛数列与其子列之间的关系 2、函数极限概念2.1、函数极限的定义2.1.1…

Django实现音乐网站 ⒆

使用Python Django框架做一个音乐网站&#xff0c; 本篇主要为排行榜功能及音乐播放器部分功能实现。 目录 排行榜列表 设置路由 视图处理 模板渲染 设置跳转入口 播放器功能开发 设置路由 模板页面 脚本渲染 列表渲染和播放器实现 音乐播放器列表展示关闭 总结 排…