4、配置 CSS 预处理器 Less
安装依赖
npm i less -D
配置 css 属性
更多请参考:postcss-modules
导出类名的样式,json 中的键。
Name | Type | Description |
camelCase |
String |
类名将被骆驼化,原始类名不会从本地中删除 |
camelCaseOnly |
String |
类名将被驼峰化,原来的类名将从本地中删除 |
dashes |
String |
只有类名中的破折号会被驼峰化 |
dashesOnly |
String |
类名中的破折号将被驼峰化,原始类名将从本地中删除 |
在 vite.config.js
里面添加配置:这里我们用 dashesOnly
import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import styleImport from 'vite-plugin-style-import' // https://vitejs.dev/config/ export default defineConfig({ css: { // css模块化,文件以.module.[css|less|scss]结尾,否则不生效的 modules: { /** * 配置 CSS modules 的行为。选项将被传递给 postcss-modules。 * 默认:'camelCaseOnly'。 * 'camelCase' | 'camelCaseOnly' | 'dashes' | 'dashesOnly' * */ localsConvention: 'dashesOnly' }, // 指定传递给 CSS 预处理器的选项。 preprocessorOptions: { // 预编译支持 less less: { // 支持内联 JavaScript javascriptEnabled: true, } } }, plugins: [ react(), styleImport({ libs: [ { libraryName: 'zarm', esModule: true, resolveStyle: (name) => { return `zarm/es/${name}/style/css`; } } ] }) ] })
新建 container/Index/style.module.less
文件,里面添加代码:
.kaimo-index { h3 { color: green; } } .kaimo_index { h3 { color: red; } }
然后在 container/Index/index.jsx
里面引入使用
import React from 'react' import { Button } from 'zarm'; import k from './style.module.less' console.log('style.module.less', k) export default function Index() { return <div className={k.kaimoIndex}> kaimo 的 index 页面 <h3> 按钮 Button 基本用法 </h3> <Button>default</Button> <Button theme="primary">primary</Button> </div> }
我们可以看到在 dashesOnly
下,类名中的破折号被驼峰化了。并且添加了 css modules 配置,自定义样式重名的风险也避免了。
我们可以修改一下在 camelCaseOnly
下,类名中都被驼峰化了。并且被覆盖了,样式变红了。
5、移动端项目适配 rem
安装依赖
postcss-pxtorem
:它的作用是在你编写完 css 后,将你的单位自动转化为 rem 单位。lib-flexible
:可伸缩布局方案。
npm i postcss-pxtorem lib-flexible -s
使用 flexible
在 main.jsx
中引入它:
import React from 'react' import ReactDOM from 'react-dom' import 'lib-flexible/flexible' import './index.css' import App from './App' ReactDOM.render( <React.StrictMode> <App /> </React.StrictMode>, document.getElementById('root') )
引入之后就会出现下面的 font-size
的设置,页面的大小变化就会让 html 的 font-size
随之变化,从而形成可伸缩布局。
配置 postcss-pxtorem
更多配置参考:postcss-pxtorem
一些默认配置:
{ rootValue: 16, // (Number | Function) 表示根元素字体大小或根据输入参数返回根元素字体大小 unitPrecision: 5, // (Number) 允许 REM 单位增长到的十进制数字。 propList: ['font', 'font-size', 'line-height', 'letter-spacing'], // (Array)可以从 px 更改为 rem 的属性。 selectorBlackList: [], // (Array)要忽略并保留为 px 的选择器。 ... }
根据文档,我们先在根目录新建一个 postcss.config.js
文件,里面添加配置信息:
/** * 具体配置可以去 postcss-pxtorem 仓库看看文档 * 默认配置 rootValue: 16, unitPrecision: 5, propList: ['font', 'font-size', 'line-height', 'letter-spacing'], selectorBlackList: [], replace: true, mediaQuery: false, minPixelValue: 0, exclude: /node_modules/i * */ module.exports = { "plugins": [ require("postcss-pxtorem")({ rootValue: 37.5, propList: ['*'], selectorBlackList: ['norem'] // 过滤掉 .norem 开头的 class,不进行 rem 转换,比如:['body'] will match .body-class }) ] }
我们来测试一下:
先修改 container/Index/style.module.less
文件,里面添加代码:
.kaimo-index { h3 { width: 100px; background-color: pink; color: green; } .norem-kaimo { width: 200px; } }
然后修改 container/Index/index.jsx
里面的代码:
import React from 'react' import { Button } from 'zarm'; import k from './style.module.less' console.log('style.module.less', k) export default function Index() { return <div className={k.kaimoIndex}> kaimo 的 index 页面 <h3> 按钮 Button 基本用法 </h3> <Button>default</Button> <Button theme="primary">primary</Button> <h3 className={k.noremKaimo}> 按钮 Button 基本用法 norem </h3> </div> }
重启项目 npm run dev
,我们可以发现加了 norem
前缀的类名的 px 不会转化为 rem。
6、resolve.alias 别名设置
打开 vite.config.js
,添加配置如下:
import path from "path"; export default defineConfig({ resolve: { alias: { '@': path.resolve(__dirname, 'src'), // src 路径 } }, }
配置好之后,我们就可以使用:
import Index from '@/container/Index' import About from '@/container/About' const routes = [ { path: "/", component: Index }, { path: "/about", component: About } ]; export default routes
7、进行 axios 封装配置
安装依赖
npm i axios -S
知识点
环境变量
Vite 在一个特殊的 import.meta.env
对象上暴露环境变量。这里有一些在所有情况下都可以使用的内建变量:
import.meta.env.MODE: {string} 应用运行的模式。
import.meta.env.BASE_URL: {string} 部署应用时的基本 URL。他由 base 配置项决定。
import.meta.env.PROD: {boolean} 应用是否运行在生产环境。
import.meta.env.DEV: {boolean} 应用是否运行在开发环境 (永远与 import.meta.env.PROD 相反)。
X-Requested-With
X-Requested-With 用于区分 ajax 请求还是传统请求。如果 requestedWith 为 null,则为同步请求。如果 requestedWith 为 XMLHttpRequest 则为 Ajax 请求。
XMLHttpRequest.withCredentials
XMLHttpRequest.withCredentials 属性是一个 Boolean 类型,它指示了是否该使用类似 cookies,authorization headers (头部授权)或者 TLS 客户端证书这一类资格证书来创建一个跨站点访问控制(cross-site Access-Control)请求。在同一个站点下使用 withCredentials 属性是无效的。
代理配置
我们启动之前的后端项目 kaimo-cost-server
,地址是:http://127.0.0.1:7001
。
然后我们打开 vite.config.js
,添加代理配置,代码如下:
export default defineConfig({ server: { proxy: { '/api': { // 当遇到 /api 路径时,将其转换成 target 的值 target: 'http://127.0.0.1:7001', changeOrigin: true, } } }, })
封装处理
下面我们新建 src/utils/axios.js
文件,里面添加代码:
import axios from "axios"; // 引入 zarm 的 Toast 组件用于提示 import { Toast } from "zarm"; // 设置接口响应时间 30s axios.defaults.timeout = 3000; /** * @description 添加请求拦截器 * 1. config中的一些信息不符合服务器的要求,这里可以做一些修改 * 2. 每次发送网络请求时,都希望在界面中显示一个请求的图标(然后再响应拦截中取消显示) * 3. 某些网络请求必须携带一些特殊的信息(如登录token),如果没有携带就可以拦截并作响应提示 */ axios.interceptors.request.use(config => { // 看知识点 XMLHttpRequest.withCredentials config.withCredentials = true; // 看知识点 X-Requested-With config.headers['X-Requested-With'] = 'XMLHttpRequest'; // Authorization 用于服务端鉴权,token 存于 localStorage config.headers['Authorization'] = `${localStorage.getItem('token') || null}`; console.log('进入请求拦截器-->config:', config); return config; }, err => { // 请求未成功发出,如:没有网络... console.log('进入请求拦截器-->err:', err); Toast.show("请求失败"); return Promise.reject(err); }) // 添加响应拦截器 axios.interceptors.response.use(response => { // 成功响应的拦截 console.log('进入响应拦截器-->response:', response); return Promise.resolve(response.data) }, err =>{ // 失败响应的拦截 console.log('进入响应拦截器-->err:', err); Toast.show("服务器响应失败"); if(err.response){ // 失败响应的status需要在response中获得 console.log(err.response) switch(err.response.status){ // 对得到的状态码的处理,具体的设置视自己的情况而定 case 401: console.log('未登录') window.location.href='/login'; break case 404: console.log('没有找到该方法'); break case 405: console.log('不支持的方法'); break default: console.log('其他错误'); break } } // 注意这里应该return promise.reject(), // 因为如果直接return err则在调用此实例时,响应失败了也会进入then(res=>{})而不是reject或catch方法 return Promise.reject(err) }); /** * @description 导出获取数据方法 * @param {String} url * @param {String} type * @param {Object} data * */ export function fetchData(url, type, data) { let ajaxType = type ? type.toLowerCase() : type; const config = {}; config.method = ajaxType || 'post'; config.url = url; config.headers = {}; if (ajaxType === 'get') { config.url = url + '?r=' + Math.random() * 1000; config.params = data; } else if (ajaxType === 'post') { config.data = data; config.headers['Content-Type'] = 'application/json;charset=UTF-8'; } console.log('进入fetchData', url, type, data, config); return axios(config); };
测试一下:我们在 Index/api
该路径添加 index.js
文件,该文件里面添加接口的配置代码:
import { fetchData } from "@/utils/axios.js"; export function login(data) { return fetchData('/api/user/login', 'post', data); }
然后在文件 Index/index.jsx
里添加登录按钮的代码
import React from 'react' import { Button, Toast } from 'zarm'; import k from './style.module.less' console.log('style.module.less', k) import { login } from "./api/index.js"; const onSubmit = async () => { try { const data = await login({ username: "kaimo313", password: "123456" }); console.log(data); Toast.show('登录成功'); } catch (error) { console.log(error) Toast.show('系统错误'); } }; export default function Index() { return <div className={k.kaimoIndex}> kaimo 的 index 页面 <h3> 按钮 Button 基本用法 </h3> <Button>default</Button> <Button theme="primary">primary</Button> <h3 className={k.noremKaimo}> 按钮 Button 基本用法 norem </h3> <Button onClick={onSubmit} block theme="primary">登录</Button> </div> }
点击登录,我们发现可以登录成功
这样,我们的基本环境就搭建好了。
package.json
{ "name": "kaimo-cost-h5", "private": true, "version": "0.0.0", "scripts": { "dev": "vite", "build": "vite build", "preview": "vite preview" }, "dependencies": { "axios": "^0.26.1", "lib-flexible": "^0.3.2", "postcss-pxtorem": "^6.0.0", "react": "^17.0.2", "react-dom": "^17.0.2", "react-router-dom": "^6.3.0", "zarm": "^2.9.13" }, "devDependencies": { "@vitejs/plugin-react": "^1.0.7", "less": "^4.1.2", "vite": "^2.9.0", "vite-plugin-style-import": "^1.0.1" } }
vite.config.js
import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import path from "path"; import styleImport from 'vite-plugin-style-import' // https://vitejs.dev/config/ export default defineConfig({ resolve: { alias: { '@': path.resolve(__dirname, 'src'), // src 路径 } }, css: { // css模块化,文件以.module.[css|less|scss]结尾,否则不生效的 modules: { /** * 配置 CSS modules 的行为。选项将被传递给 postcss-modules。 * 默认:'camelCaseOnly'。 * 'camelCase' | 'camelCaseOnly' | 'dashes' | 'dashesOnly' * */ localsConvention: 'dashesOnly' }, // 指定传递给 CSS 预处理器的选项。 preprocessorOptions: { // 预编译支持 less less: { // 支持内联 JavaScript javascriptEnabled: true, } } }, server: { proxy: { '/api': { // 当遇到 /api 路径时,将其转换成 target 的值 target: 'http://127.0.0.1:7001', changeOrigin: true, } } }, plugins: [ react(), styleImport({ libs: [ { libraryName: 'zarm', esModule: true, resolveStyle: (name) => { return `zarm/es/${name}/style/css`; } } ] }) ] })