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 } = configreturn [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.signalpendingMap.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 // 禁用超时}// 判断是否需要转换为FormDataif (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 = formDataconfig.headers['Content-Type'] = 'multipart/form-data'}return config},error => {return Promise.reject(error)}
)// 响应拦截器
request.interceptors.response.use(response => {removePending(response.config)// 如果是流式响应,直接返回responseif (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('请求的资源不存在')breakcase 500:console.error('服务器错误')breakdefault: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//专门用于开发环境的工具