参考 B 站 --小霖同学-- 的教程入门一下quasar,然后我们给示例项目搞个页面。以下是前后端连接大概流程记录。
nvm alias default v24.12.0 ,nvm use default 把默认node版本切换到这个lts版本,因为版本太老可能不行。
在合适的位置 npm init quasar 来创建 quasar项目,需要回答项目根目录名称、和vue3有关的配置等等。我们基本用默认选项,只是组件选择把状态管理 pinia 、请求组件axios 、i18n 和代码 linting 都选上,我们用 JavaScript (不用 TypeScript)。建完项目,根据提示安装完依赖,用 VSCode 添加项目根目录到工作区,准备工作差不多完成了。(默认用的是 vite 而非 webpack,务必注意,两个打包工具对应需要的在配置文件中的格式不一样!可以从quasar官网文档查看各自不同)
cd PRJNAME, npx quasar dev 启动本地web服务器就可以看到默认页面了。
我们先观察一下项目目录结构:src/router/routes.js 是项目包含的路由 ,只有 / 和 404。/ 对应的组件是布局模板 layouts/MainLayout.vue,子组件包含页面pages/IndexPage.vue,子组件对应 path 为空,也就是布局模板里面的<router-view />默认渲染这个子组件(其实就是首页index)。这里的 layouts 和 pages 划分和通常的想法一样,也就是 pages的东西是嵌入layouts的一部分(主体部分)。
参考 https://blog.csdn.net/weixin_43815091/article/details/124951953 修改 api 的基础URL :quasar.config.js 修改 (axios基础url用相对路径,如果用协议号http开始的绝对路径,开发服务器就不会捕获并执行proxy代理。另外,我们路径额外加一个/api前缀并在proxy重写规则中去掉,如果用根目录/,配置会复杂一些。使用相对路径后,代理proxy会过滤请求,对于匹配上的请求,进行代理转发,即请求不再是浏览器到后端服务器,变成前端服务器到后端服务器,这样就避开了跨域问题)
javascript
build: {
.....................
env: {
apiBaseUrl: ctx.dev ? '/api' : 'http://localhost:8000',
},
.....................
devServer: {
// https: true,
open: true, // opens browser window automatically
proxy: {
'/api': {
target: 'http://localhost:8000',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
},
},
},
修改 boot/axios.js (api 是一个axios实例)
javascript
const api = axios.create({
baseURL: process.env.apiBaseUrl,
timeout: 30000,
})
参考 https://help.crudapi.cn/guide.html (源码网址 https://gitee.com/crudapi/crudapi-admin-web/blob/master/src/api/user.js),我们新建 src/api 目录,差不多就是把 GoFrame 的 *.http 文件中的各API请求包装一下方便使用。文件 src/api/post.js 如下:其中的Promise使用了 async/await 语法糖而非 then/catch
javascript
import { api } from 'boot/axios'
// axios 参考 https://axios-http.com/zh/docs/handling_errors
const post = {
index: async function (params) {
try {
const response = await api.get('/post/index', {
params: params,
})
console.log(response.data)
return response.data
} catch (error) {
if (error.response) {
// 请求成功发出且服务器也响应了状态码,但状态代码超出了 2xx 的范围
console.log(error.response.data)
console.log(error.response.status)
console.log(error.response.headers)
} else if (error.request) {
// 请求已经成功发起,但没有收到响应
// `error.request` 在浏览器中是 XMLHttpRequest 的实例,
// 而在node.js中是 http.ClientRequest 的实例
console.log(error.request)
} else {
// 发送请求时出了点问题
console.log('Error', error.message)
}
console.log(error.config)
}
},
create: async function (params) {
try {
const response = await api.post('/post/create', params)
console.log(response.data)
return response.data
} catch (error) {
console.log(error)
}
},
update: async function (id, params) {
try {
const response = await api.post('/post/update/' + id, params)
console.log(response.data)
return response.data
} catch (error) {
console.log(error)
}
},
view: async function (id) {
try {
const response = await api.get('/post/view/' + id)
console.log(response.data)
return response.data
} catch (error) {
console.log(error)
}
},
delete: async function (id) {
try {
const response = await api.post('/post/delete/' + id)
console.log(response.data)
return response.data
} catch (error) {
console.log(error)
}
},
}
export { post }
创建页面 src/pages/PostPage.vue (复习一下 <script setup>语法糖,ref响应对象创建,生命周期钩子 onMounted,异步函数使用,quasar表格基本使用)
html
<template>
<q-page class="flex flex-center">
<div class="q-pa-md">
<q-table title="博客列表" :columns="columns" :rows="rows" row-key="id" />
</div>
</q-page>
</template>
<script setup>
import { post } from 'src/api/post'
import { onMounted, ref } from 'vue'
const columns = [
{
name: 'id',
label: 'ID',
field: 'id',
},
{
name: 'title',
label: '标题',
field: 'title',
},
{
name: 'content',
label: '标题',
field: 'content',
format: (val) => {
return val.substr(0, 20)
},
},
{
name: 'tags',
label: '标签',
field: 'tags',
},
{
name: 'status',
label: '状态',
field: 'status',
},
{
name: 'createdAt',
label: '创建时间',
field: 'createdAt',
sortable: true,
},
{
name: 'updatedAt',
label: '更新时间',
field: 'updatedAt',
},
{
name: 'authorId',
label: '作者',
field: 'authorId',
},
]
const rows = ref([])
onMounted(async () => {
let res = await post.index()
rows.value = res.data.list
})
</script>
路由 src/router/routes.js 添加
javascript
const routes = [
{
path: '/',
component: () => import('layouts/MainLayout.vue'),
children: [
{ path: '', component: () => import('pages/IndexPage.vue') },
{ path: '/post', component: () => import('pages/PostPage.vue') },
{ path: '/comment', component: () => import('pages/CommentPage.vue') },
],
},
.........................
布局模板 src/layouts/MainLayout.vue中的链接改掉
javascript
const linksList = [
{
title: '博客',
caption: '我的各种博客',
icon: 'school',
link: '#/post',
},
{
title: '评论',
caption: '用户浏览器后评论',
icon: 'chat',
link: '#/comment',
},
...................
然后 npx quasar dev 启动本地服务器,浏览器自动打开,点击"博客"菜单导航到 #/post,主视图中就用表格显示了博客。不过表格右下角关于页的提示信息默认是英文的,因为quasar框架的组件默认用英文,我么改一下配置文件 quasar.config.js (如果要编写自己的支持i18n的组件,可以参考https://quasar.dev/options/app-internationalization)
javascript
framework: {
config: {},
lang: 'zh-CN',
作为GUI库,我们大部分时候关心的是布局、可视组件、样式风格,分别对应官网文档 Layout and Grid、Vue Components、Style and Identity。我们大概了解一下有哪些可视组件:
- Ajax Bar: 异步网络后台请求时,我们可以用这个让页面边缘显示进度
- Avatar:主要用于头像,也可用作展示性小图标
- Badge:徽章,主要是突出显示吸引注意的文字,比如待阅读的数量
- Banner:旗帜条,用于突出显示信息或者可选操作,如"登录成功"
- Bar:贴顶条,用来容纳菜单,最大化、最小化、关闭等按钮
- Breadcrumbs:面包屑,可以让人知道当前导航位置
- Button、Button Group、Button Dropdown:按钮是最常用的可视化组件
- Card:卡片,这是常见的信息展示容器
- Carousel:转盘,可以在相对有限的空间用幻灯方式展示更多信息
- Chat Message:聊天消息
- Chip:一个界面块,用来容纳头像、文本、图标等东西
- Circular Progress:彩色的圆形进度指示器
- Color Picker:颜色选择器
- Dialog:对话框
- Editor:编辑框
- Expansion Item:可扩展项,初始折叠,点击后可展示隐藏的内容
- Floating Action Button:悬浮的动作按钮,在悬浮在主体内容的上面,常常显示一个小圆圈,点击可以弹出更多操作按钮
- 表单组件 Input Textfield、Select、File picker、Form、Field(装饰器)、Radio、Checkbox、Toggle(开关指示)、Button Toggle(选中指示)、Option Group(用于分组radio/checkbox/toggle)、Slider(滑动条表示数量)、Ranger(滑动条指示范围)、Time Picker、Date Picker
- Icon:方便在其他组件中嵌入图标
- Img:方便嵌入图片,有加载进度指示
- Infinite Scroll:去穷滚动让用户滚动时加载新的内容
- Inner Loading:长加载耗时的时候显示动画,让用户可以看到应用在后台进行加载工作
- Intersection:和 Intersection quasar指令相关,主要是DOM对象进入和移出视口过程中,能节约内存
- Knob:圆形的数量展示,可以用鼠标或触控改变它的值,例如用于音量控制
- Linear Progress:彩色的加载条,用来提醒用户某个动作在背后发生
- List and List Items:用来进行列表展示,列表项可以是很复杂的
- Markup Table:是对table的包装,呈现为Material风格,只用于少量简单的表格数据展示,需要复杂分页、排序、过滤功能的表格,应该用 QTable组件
- Menu:菜单
- Resize Observer、Scroll Observer:尺寸改变、滚动时激发相应消息
- Pagination:分页组件
- Parallax:视差滚动,滚动时背景图片滚动略慢造成一定视觉效果
- Popup Edit:弹出式在地编辑,即点击要编辑目标,编辑器就在那个位置打开
- Popup Proxy:可以将弹出内容(如QMenu、QTooltip、QDialog、自定义浮层、上下文菜单等)与目标组件(按钮、输入框等)关联显示
- Pull to refresh:下拉刷新
- Rating:评级
- Responsive:强制让内容保持纵横比
- Scroll Area:可滚动区域,可以有自己的滚动条而不是浏览器提供的
- Separator:分隔条
- Skeleton:动态效果占位符,用于加载完之前显示
- Slide Item:是列表项 QItem的增强版,可以向左、向右、向下、向上拖拉以便触发特定动作
- Slide Transition:作用于单个DOM的滑动过渡效果
- Space:填充弹性盒子所有可用的空间,quasar组件都是弹性盒子
- Spinners:视觉特性,让用户感觉系统持续在工作
- Splitter:把容器分隔成可拖拉改变大小的部分
- Stepper:用于展示步骤,让人知道当前做了哪几步,还剩几步
- Table:表格
- Tabs:选项页
- Tab Panels:选项面板,和Tabs有点像,但它针对面板而非整页,主要是为了在有限空间显示更多信息
- Timeline:时间线
- Toolbar:工具条,最常用于页头和页脚
- Tooltip:工具提示
- Tree:树形结构,用来展示层次数据
- Uploader:对于简单上传一个不大的文件,QFile更简单,QUploader主要是复杂的控制,例如多个文件,添加头部
- Video:方便嵌入视频
- Virtual Scroll:只显示长列表中部分数据,当用户在容器内滚动时更新可视数据
这些组件可以慢慢学,路由细节也可以慢慢学,还有两个问题:1、如何用pinia实现状态管理,哪些东西需要进行状态管理;2、登录获取令牌、在请求中携带令牌和令牌超时前刷新问题