前言
React
技术栈的 UI
组件库相比 Vue
,会少一些。我们耳熟能详的便是 Antd
,但是它针对的是 PC
端的,我们的项目目前是一个 H5
的网页(不排除后期做一个 PC 端)。所以我选择了 Zarm。
这里再次强调,不是 Zarm
就比别的移动端组件库好,只是目前我开发的这款记账本项目,Zarm
比较适合。
知识点
- 构架工具
Vite
。
- 前端框架
React
和路由 react-router-dom
。
CSS
预加载器 Less
。
HTTP
请求库 axios
。
- 移动端分辨率适配
flexible
。
- 跨域代理。
初始化 Vite + React 项目
Vite
官方提供两种初始化项目的方式,一种是如下所示,可以自由选择需要的前端框架。
另一种则是直接用官方提供的模板,一键生成项目:
1 2 3 4 5
| npm init @vitejs/app react-vite-h5 --template react
npm init @vitejs/app react-vite-h5 -- --template react
|
我们使用第二种方式初始化项目,如下所示:
安装完 node_modules
之后,通过 npm run dev
启动项目,如下所示代表成功了:
引入路由插件 react-router-dom
没有路由的项目,那就不是一个完整项目,而是一个页面而已。真实项目都是存在各种模块之间的切换,各个模块的功能组合在一起才能叫做一个项目。
首选安装 react-router-dom
,指令如下:
1
| npm i react-router-dom -S
|
在项目 src
目录下新增 container
目录用于放置页面组件,再在 container
下新增两个目录分别是 Index
和 About
,添加如下内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import React from 'react'
export default function Index() { return <div> Index </div> }
import React from 'react'
export default function About() { return <div> About </div> }
|
再来新建 src/router/index.js
配置路由数组,添加如下内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import Index from "../container/Index"; import About from "../container/About";
const routes = [ { path: "/", component: Index, }, { path: "/about", component: About, }, ];
export default routes;
|
在 App.jsx
引入路由配置,实现切换浏览器路径,显示相应的组件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import React, { useState } from "react"; import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; import routes from "../src/router"; function App() { return ( <> <Routes> {routes.map((route) => ( <Route exact key={route.path} path={route.path} element={<route.component />} /> ))} </Routes> </> ); }
export default App;
|
启动项目 npm run dev
,如下图所示:
引入 Zarm UI 组件库
首先通过如下指令安装它:
修改 App.jsx
的代码,全局引入样式和中文包:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| import React, { useState } from "react"; import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import { ConfigProvider } from "zarm"; import zhCN from "zarm/lib/config-provider/locale/zh_CN"; import "zarm/dist/zarm.css";
import routes from "../src/router"; function App() { return ( <Router> <ConfigProvider primaryColor={"#007fff"} locale={zhCN}> <> <Routes> {routes.map((route) => ( <Route exact key={route.path} path={route.path} element={<route.component />} /> ))} </Routes> </> </ConfigProvider> </Router> ); }
export default App;
|
此时 zarm
的样式,已经全局引入了,我们先查看在 /container/Index/index.jsx
添加一个按钮是否生效:
1 2 3 4 5 6 7 8 9 10 11 12
| import React from "react"; import { Button } from "zarm";
export default function Index() { return ( <div> Index <Button theme="primary">按钮</Button> </div> ); }
|
重启项目,如下所示:
此时恭喜你 🎉,你已经成功将组件引入项目中。
小优化
组件虽然引入成功了,但是有一个问题,我不希望所有的组件样式都被一次性的引入,因为这样代码会比较冗余,我只需要引入我使用到的组件样式,实现「按需引入」。
我们先看看,就目前现在这个情况,打完包之后,静态资源有多大。运行指令 npm run build
,如下所示:
腚眼一看,全局引入样式的形式,直接打完包, css
静态资源就 168.22kb
了,我们尝试配置「按需引入」。
首先我们安装一个插件:
1
| npm i vite-plugin-style-import -D
|
然后在 vite.config.js
配置文件内添加如下内容:
打完包之后,肉眼可见,css
提及从 168.22kb
-> 35.22kb
。这种方式也是前端性能优化的其中一种。
配置 CSS 预处理器 Less
项目中采用的 Less
作为 CSS
预处理器,它能设置变量以及一些嵌套逻辑,便于项目的样式编写。
安装 less
插件包,npm i less -D
,因为上述配置我们使用的是 less
,并且我们需要配置 javascriptEnabled 为 true
,支持 less
内联 JS
。
修改 vite.config.js
,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| { plugins: [...] css: { modules: { localsConvention: 'dashesOnly' }, preprocessorOptions: { less: { javascriptEnabled: true, } } }, }
|
并且添加了 css modules
配置,这样我们就不用担心在项目中,自定义的样式重名的风险,我们尝试在 /container/Index
目录下添加样式文件 style.module.less
,并且在 /container/Index/index.jsx
中引入它,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| .index { span { color: red; } } // Index/index.jsx import React from 'react' import { Button } from 'zarm'
import s from './style.module.less'
export default function Index() { return <div className={s.index}> <span>样式</span> <Button theme='primary'>按钮</Button> </div> }
|
此时我只能再次恭喜你,Less
成功被引入。
移动端项目适配 rem
移动端项目,肯定是需要适配各种分辨率屏幕的,就比如你 10px 的宽度,在每个屏幕上的占比都是不一样的,我们这里不对分辨率做深入的探讨,我们目前的首要目的是完成项目移动端的分辨率适配。
首先我们需要安装 lib-flexible
:
并在 main.jsx
中引入它:
1 2 3 4 5 6 7 8 9 10 11 12
| 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") );
|
然后再安装一个 postcss-pxtorem
,它的作用是在你编写完 css
后,将你的单位自动转化为 rem
单位。
在项目根目录新建 postcss.config.js
:
1 2 3 4 5 6 7 8 9 10 11 12
|
module.exports = { plugins: [ require("postcss-pxtorem")({ rootValue: 37.5, propList: ["*"], selectorBlackList: [".norem"], }), ], };
|
修改 Index/style.module.less
:
1 2 3 4 5 6 7 8
| .index { width: 200px; height: 200px; background: green; span { color: red; } }
|
重启项目 npm run dev
,如下所示:
可以看到,200px
已经被转化为 5.3333rem
,我们设置的 rootValue
是 37.5
,你可以换算一下 5.33333 * 37.5 = 200
。
我们目前把浏览器调整成的是 iphone 6
,html
的 font-size
为 37.5px
,当我们手机变成其他尺寸的时候,这个 font-size
的值也会变化,这是 flexible
起到的作用,动态的变化 html
的 font-size
的值,从而让 1rem
所对应的 px
值一直都是动态适应变化的。
当我切换成 iphone 6 plus
时:
变成了 41.4px
,而相应的,我们 div
还是 5.33333rem
,所以此时 div
宽度就变大了,但是手机的屏幕宽度也变大了,这就不会影响视觉上的比例误差太大。
二次封装 axios
说到这里,那就要涉及到项目的服务端 API
接口,我们在前面的章节里,已经完成了服务端的代码编写,但是此时我们的服务端项目是跑在 http://127.0.0.1/7001
端口上的。
此时你是可以在后续的请求中,使用 http://127.0.0.1/7001
作为项目的 baseURL
。但是照顾到有些同学没有启动服务端项目,直奔前端项目来的。这里我已经将接口提前部署到了线上环境,供大家使用。接口地址是 http://api.chennick.wang
。
所以在后续的封装过程中,我会提醒大家两种使用。
首先我们安装 npm i axios -S
,在 src
目录下新建 utils
目录,并新建 axios.js
脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| import axios from "axios"; import { Toast } from "zarm";
const MODE = import.meta.env.MODE;
axios.defaults.baseURL = MODE == "development" ? "/api" : "http://api.chennick.wang"; axios.defaults.withCredentials = true; axios.defaults.headers["X-Requested-With"] = "XMLHttpRequest"; axios.defaults.headers["Authorization"] = `${ localStorage.getItem("token") || null }`; axios.defaults.headers.post["Content-Type"] = "application/json";
axios.interceptors.response.use((res) => { if (typeof res.data !== "object") { Toast.show("服务端异常!"); return Promise.reject(res); } if (res.data.code != 200) { if (res.data.msg) Toast.show(res.data.msg); if (res.data.code == 401) { window.location.href = "/login"; } return Promise.reject(res.data); }
return res.data; });
export default axios;
|
我逐行为大家分析上述代码的情况情况。
1
| const MODE = import.meta.env.MODE;
|
MODE
是一个环境变量,通过 Vite
构建的项目中,环境变量在项目中,可以通过 import.meta.env.MODE
获取,环境变量的作用就是判断当前代码运行在开发环境还是生产环境。
1
| axios.defaults.baseURL = "development" ? "/api" : "http://api.chennick.wang";
|
baseURL
是 axios
的配置项,它的作用就是设置请求的基础路径,后续我们会在项目实战中有所体现。配置基础路径的好处就是,当请求地址修改的时候,可以在此统一配置。
1 2 3 4 5
| axios.defaults.headers["X-Requested-With"] = "XMLHttpRequest"; axios.defaults.headers["Authorization"] = `${ localStorage.getItem("token") || null }`; axios.defaults.headers.post["Content-Type"] = "application/json";
|
上述三个配置是用于请求头的设置,Authorization
是我们在服务端鉴权的时候用到的,我们在前端设置好 token
,服务端通过获取请求头中的 token
去验证每一次请求是否合法。
最后一行是配置 post
请求是,使用的请求体,这里默认设置成 application/json
的形式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| axios.interceptors.response.use((res) => { if (typeof res.data !== "object") { Toast.show("服务端异常!"); return Promise.reject(res); } if (res.data.code != 200) { if (res.data.msg) Toast.show(res.data.msg); if (res.data.code == 401) { window.location.href = "/login"; } return Promise.reject(res.data); }
return res.data; });
|
interceptors
为拦截器,拦截器的作用是帮你拦截每一次请求,你可以在回调函数中做一些“手脚”,再将数据 return
回去。上述代码就是拦截了响应内容,统一判断请求内容,如果非 200,则提示错误信息,401
的话,就是没有登录的用户,默认跳到 /login
页面。如果是正常的响应,则 retrun res.data
。
最后我们将这个 axios
抛出,供页面组件请求使用。
在 utils
下新建一个 index.js
,内容如下:
1 2 3 4 5
| import axios from "./axios";
export const get = axios.get;
export const post = axios.post;
|
这样获取的时候,能少写几行代码,能少写点就少写点。
代理配置
baseURL
为什么在 development
环境下,用 /api
这样的请求地址。其实它就是为了代理请求而配置的。
这样配置完后,在请求接口的时候,请求地址大概长这样:
于是我们需要去配置代理,打开 vite.config.js
,添加如下代码:
1 2 3 4 5 6 7 8 9 10
| server: { proxy: { '/api': { target: 'http://api.chennick.wang/api/', changeOrigin: true, rewrite: path => path.replace(/^\/api/, '') } } }
|
这样配置完之后,开发环境下,/api/userInfo
-> http://api.chennick.wang/api/userInfo
。这样就解决了大家老大难的跨域问题。
但是其实服务端只要设置好白名单,就不会有这样那样的跨域问题。
resolve.alias 别名设置
这里我们必须得设置好别名,否则在页面中,你会写出很长一串类似这样的代码 ../../../
。
打开 vite.config.js
,添加配置如下:
1 2 3 4 5 6 7 8 9 10 11 12
| ... import path from 'path'
export default defineConfig({ ... resolve: { alias: { '@': path.resolve(__dirname, 'src'), 'utils': path.resolve(__dirname, 'src/utils') } }, })
|
此时我们便可以修改之前的代码如下:
router/index.js
1 2
| import Index from "@/container/Index"; import About from "@/container/About";
|
App.jsx
1
| import routes from "@/router";
|
总结
行文至此,我们的基础开发环境已经搭建完毕,涉及构建工具、前端框架、UI
组件库、HTTP
请求库、CSS
预加载器、跨域代理、移动端分辨率适配,这些知识都是一个合格的前端工程师应该具备的,所以请大家加油,将他们都通通拿下。