vue3之写一个aichat ----vite.config.js
vite.config.js的CSS配置
postcss-pxtorem
开发响应式网页的时候需要用到postcss-pxtorem
amfe-flexible
amfe-flexible是由阿里团队开发的一个库,它可以根据设备的屏幕宽度去动态调整HTML根元素()的字体大小,这意味着无论用户使用什么尺寸的设备访问你的网站,页面都能根据设备的实际宽度进行缩放,从而达到响应式设计效果
工作原理:amfe-flexible会检测设备的屏幕宽度,并基于这个宽度设置一个基准字体大小,默认情况下,如果设备宽度为750px,则根字体被设置为100px,如果是375px宽,则根字体大小为50px,以此类推。
引入方式:在main.js中引入amfe-flexible
什么是postcss-pxtorem
postcss-pxtorem是PostCSS的一个插件,它的主要作用是将CSS中的px(像素)单位自动转换为rem(根em)单位,Rem单位基于HTML文档的根元素的字体大小来计算尺寸,这使得它可以很好的适应不同的屏幕尺寸和分辨率,从而帮助创建响应式设计
为什么选择Rem而不是px
灵活性:使用rem可以让你更容易地调整整个页面或者其他部分的缩放比列,只需要改变根元素的字体大小即可
响应式设计: rem单位非常适合用于响应式设计,因为它允许你根据用户的设备设置基础字体大小,并以此为基础进行相对缩放
postcss-pxtorem的使用
上面的 postcssPxToRem只能将标签内的css像素单位转换成rem,但是实际的项目开发中,我们需要用到行内样式,所以我们还需要写一个工具函数来转换行内样式的单位,在项目的src目录下创建 >utils文件夹 >创建pxToRem.js
pxToRem.js
const getBaseFontSize = () => {
// 使用 document.documentElement 获取 HTML 文档的根元素(即 <html> 标签)
// getComputedStyle 是一个用于获取所有计算样式的接口,它返回一个包含所有计算样式的对象
// .fontSize 属性从这个对象中获取根元素的字体大小属性值,该值是一个字符串,如 "16px"
const rootFontSize = parseFloat(getComputedStyle(document.documentElement).fontSize);
// parseFloat 函数将字符串转换为浮点数,这里是为了去掉 'px' 单位,只保留数字部分
// 例如,如果 fontSize 返回的是 '16px',那么 parseFloat('16px') 将返回 16
// 返回计算后的根字体大小,这是一个纯数字,表示当前页面根元素的字体大小(以像素为单位)
return rootFontSize;
}
const pxToRem = px => {
// 如果传入的是带单位的字符串(比如 '100px')
if (typeof px === 'string') {
// 如果包含 '%',直接返回
if (/%/gi.test(px)) {
return px;
}
// 去掉 'px' 单位
px = parseFloat(px.replace('px', ''));
}
// 如果传入的不是数字,直接返回
if (isNaN(px)) {
return px;
}
// 使用 amfe-flexible 设置的根字体大小进行计算
const baseFontSize = getBaseFontSize();
const remValue = (px / baseFontSize).toFixed(6) + 'rem';
return remValue;
}
// 导出函数
export default pxToRem;
使用pxToRem.js
在需要使用的组件中引入文件再使用
preprocessorOptions预处理器选项,配置了预处理器是less
javascriptEnabled: true允许在less文件中使用JavaScript表达式,动态计算颜色值或根据条件生成样式有用
additionalData: '@import “@/assets/styles/variables.less”;'在每个less文件的开头自动导入@/assets/styles/variables.less文件这样,就可以在任何less文件中使用variables.less中定义的变量
vite.config.js中的server配置
基础服务配置:
host: 指定服务器监听的IP地址。0.0.0.0表示允许所有网络接口(包括本地和外部)访问服务
port:指定服务器运行的端口号,项目启动后可以通过http://localhost:8888/chatai/Chat或者外部IP访问
代理配置 (proxy):
'/api': {
target: 'https://thecatapi.com/',
changeOrigin: true,
ws: true,
timeout: 300000
},
作用:将所有请求代理到https://thecatapi.com/
配置项:
target:代理的目标服务器URL
changeOrigin:修改请求头中的Host为目标URL的主机名(绕过某些服务器的反爬机制)
ws: 启用webSocket代理(用于实时通信)
timeout:设置代理请求超时时间
路径匹配规则
所有以/api开头的请求都会被代理到https://thecatapi.com
例如:/api/cats → https://thecatapi.com/cats(自动去除/api前缀)
允许访问的主机 (allowedHosts):
allowedHosts: [
'8eec-113-106-75-166.ngrok-free.app', // 添加你的ngrok URL
// 如果需要,可以添加其他允许的主机
'localhost', // 允许本地访问
'127.0.0.1' // 允许本地访问
],
作用:定义合法的主机名列表,防止未经授权的主机访问开发服务器
常用于ngrok暴露本地服务到公网时,需要添加生成的域名,一般开发H5移动端,想要手机调试可以使用这个方法,关于ngrok的安装使用可以参考这篇文章
前端请求发起到代理转发的全流程
前端axios实例配置的baseURL为/api,发送请求到vite开发服务器的8888端口,vite的server.proxy配置检测到/api开头的请求,转发到目标服务器,修改请求头,处理路径重写,然后返回响应
封装的axios,其中baseURL: ‘/api’,表示每个请求都会自动加上"/api"前缀,如:request.get(‘/cats’)实际的请求地址是http://localhost:8888/api/cats
import axios from 'axios'
import { useUserStore } from '@/stores/user'
// 创建 axios 实例
const request = axios.create({
baseURL:'/api',
timeout: 100000,
withCredentials: true,
headers: {
'Content-Type': 'application/json;charset=utf-8',
Accept: 'text/event-stream'
}
})
// 创建请求取消控制器Map
const pendingMap = new Map()
// 生成取消请求的key
const getPendingKey = config => {
const { url, method, params, data } = config
return [url, method, JSON.stringify(params), JSON.stringify(data)].join('&')
}
// 添加请求到pendingMap
const addPending = config => {
const pendingKey = getPendingKey(config)
if (!pendingMap.has(pendingKey)) {
const controller = new AbortController()
config.signal = controller.signal
pendingMap.set(pendingKey, controller)
}
}
// 移除请求从pendingMap
const removePending = config => {
const pendingKey = getPendingKey(config)
if (pendingMap.has(pendingKey)) {
const controller = pendingMap.get(pendingKey)
controller.abort()
pendingMap.delete(pendingKey)
}
}
// 请求拦截器
request.interceptors.request.use(
config => {
// removePending(config)
addPending(config)
// 如果是流式请求
if (config.isStream) {
config.responseType = 'blob'
config.timeout = 0 // 禁用超时
}
// 判断是否需要转换为FormData
if (config.formData === true && config.data) {
let formData = new FormData()
for (let key in config.data) {
if (Array.isArray(config.data[key])) {
config.data[key].forEach(value => {
formData.append(`${key}[]`, value)
})
} else {
formData.append(key, config.data[key])
}
}
config.data = formData
config.headers['Content-Type'] = 'multipart/form-data'
}
return config
},
error => {
return Promise.reject(error)
}
)
// 响应拦截器
request.interceptors.response.use(
response => {
removePending(response.config)
// 如果是流式响应,直接返回response
if (response.config.isStream) {
return response
}
if (response.data.code == 215) {
const userStore = useUserStore()
userStore.clearUserState() //清除用户状态
}
return response.data
},
error => {
if (error.name === 'AbortError') {
console.log('请求被取消')
return Promise.reject(error)
}
handleHttpError(error)
return Promise.reject(error)
}
)
// 处理 HTTP 错误
const handleHttpError = error => {
if (error.response) {
switch (error.response.status) {
case 404:
console.error('请求的资源不存在')
break
case 500:
console.error('服务器错误')
break
default:
console.error('网络错误')
}
} else if (error.request) {
console.error('请求超时')
} else {
console.error('请求错误')
}
}
export default request
在项目中启动的VConsole,用于调试移动端网页
viteVConsole函数用于初始化和配置VConsole,接收一个配置对象作为参数
entry,配置VConsole的入口文件,path.resolve用于将相对路径转换为绝对路径,指定了入口文件为src/main.js
enabled,设置VConsole的启用状态
config,用于配置VConsole的各种参数,maxLogNumber设置日志的最大数量,当日志超过这个数量,旧的日志就会被自动清除;theme设置VConsole的主题为黑色
当然在这之前的先安装相应的依赖,vconsole不必说
vite-plugin-vconsole自动集成vconsole,通过插件就可以实现vconsole的自动注入,无需修改代码;可以根据不同的构建环境动态的启动或者禁用vconsole,使用vite插件系统更攘夷进行扩展和维护
npm install vconsole --save
npm install vite-plugin-vconsole --save-dev//专门用于开发环境的工具