yarn create vite
选择 react 项目yarn install
安装依赖yarn dev
启动项目yarn add eslint -D
npx eslint --init
按照提示选择,init 命令会自动生成 .eslintrc.js,关键选择如下:
选项 | 解释 |
---|---|
To check syntax and find problems | 只检查语法和错误 |
JavaScript modules (import/export) | 项目使用的模块化规范 |
Browser Node | 项目运行环境 |
eslint-plugin-react | 检查 react 的 eslint 插件 |
yarn add prettier eslint-config-prettier eslint-plugin-prettier -D
,其中 eslint-config-prettier 和 eslint-plugin-prettier 是为了解决 eslint 和 prettier 的冲突使用的插件"prettier"
解决 eslint 和 prettier 的冲突{
"arrowParens": "avoid",
"trailingComma": "none",
"singleQuote": true
}
yarn add lint-staged husky -D
,husky 是操作 git hooks 的工具,用来在 git commit 的时候,会使用 lint-stage 执行 eslint 和 prettier,对代码进行语法检查和格式化npm set-script prepare "husky install"
在 package.json 中添加脚本yarn prepare
初始化 husky,会在根目录创建 .husky 文件夹npx husky add .husky/pre-commit "npx lint-staged"
在 commit 时执行 npx lint-staged 指令"lint-staged": {
"*.{js,jsx}": [
"eslint --fix",
"prettier --write"
],
"*.{html,css,less,json,md}": [
"prettier --write"
]
}
yarn add vite-plugin-imp
用于解决 antd 组件样式的按需加载
分别新建 .env.development ,.env.staging ,.env.production ,环境变量文件,配置开发,预发布,生产环境的接口和 CDN 静态资源路径, 这里测试(BETA)环境静态资源路径(CDN)设置为了 vite preview
的默认端口 4173
VITE_PROXY_URL = http://localhost:5000/
VITE_CDN_URL = /
VITE_PROXY_URL = http://localhost:5000/
VITE_CDN_URL = http://localhost:4173/
VITE_PROXY_URL = http://localhost:5000/
VITE_CDN_URL = http://localhost:4173/
配置 vite.config.js :
import { defineConfig, loadEnv } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';
// https://vitejs.dev/config/
export default ({ mode }) => {
const env = loadEnv(mode, process.cwd());
return defineConfig({
base: env.VITE_CDN_URL, // 不同环境下静态资源的路径前缀
assetsInclude: ['**/*.glb'], // 指定额外的文件作为静态资源处理
plugins: [react()],
css: {
// 配置css modules
modules: {
generateScopedName: '[local]_[hash:base64:5]',
hashPrefix: 'prefix'
},
preprocessorOptions: {
less: {
// 全局变量 (官方建议只引入全局less变量, 其余样式会导致重复引入)
additionalData: '@import "@/assets/styles/global.less";',
// 支持内联 JavaScript
javascriptEnabled: true
}
}
},
resolve: {
alias: {
'~': path.resolve(__dirname, './'), // 根路径
'@': path.resolve(__dirname, 'src') // src 路径
}
},
server: {
open: true,
proxy: {
//遇见/cyberUnicorn-M前缀的请求,就会触发该代理配置
'/cyberUnicorn-M': {
target: env.VITE_PROXY_URL, //动态判断不同环境请求转发给谁
changeOrigin: true, //控制服务器收到的请求头中Host的值 true时为服务器端口号 false为客户端端口号 一般设置true为了避免服务器判定为跨域
rewrite: path => path.replace(/^\/cyberUnicorn-M/, '') //重写请求路径(必须) 去除服务器收到的请求url里带有/cyberUnicorn-M前缀找不到接口报错
}
}
}
});
};
配置package.json,在 script
中加入 "build:staging": "vite build --mode staging"
在开发环境下,执行 yarn dev
会默认加载 .env.development,执行 yarn build
会默认加载*.env.productioion*,所以 staging 模式通过传递 --mode
选项标志来覆盖命令使用的默认模式
在生产环境中,这些环境变量会在构建时被静态替换,因此,在引用它们时请使用完全静态的字符串。动态的 key 将无法生效。例如,动态 key 取值 import.meta.env[key] 是无效的。
所以这里在之前导致的在预发布环境和生产环境接口 500 的 bug,是因为 proxy 的 target 动态取值写了如下代码,导致生产环境 proxy 失效:
// 错误
proxy: {
'/cyberUnicorn-M': {
target: env[`VITE_${mode.toUpperCase()}_API`],
changeOrigin: true,
rewrite: path => path.replace(/^\/cyberUnicorn-M/, '')
}
}
yarn add react-router-dom@latest
安装最新版的 react-router-dom v6 版本
配置路由表,新建 router.jsx
import React, { lazy } from 'react';
import { Navigate } from 'react-router-dom';
const Exception = lazy(() => import('@/components/Exception'));
const Login = lazy(() => import('@/pages/Login'));
const Manage = lazy(() => import('@/pages/Manage'));
const Home = lazy(() => import('@/pages/Home'));
const Category = lazy(() => import('@/pages/Category'));
const Product = lazy(() => import('@/pages/Product'));
const UserManage = lazy(() => import('@/pages/UserManage'));
const AccountCenter = lazy(() => import('@/pages/AccountCenter'));
const AccountSettings = lazy(() => import('@/pages/AccountSettings'));
const Role = lazy(() => import('@/pages/Role'));
const Bar = lazy(() => import('@/pages/Bar'));
const Line = lazy(() => import('@/pages/Line'));
const Pie = lazy(() => import('@/pages/Pie'));
export default [
// * 为最低优先级 一般用于找不到匹配路由 展示404页面
{
path: '*',
element: <Exception />
},
// Navigate组件用于重定向 一定会引起页面的重新渲染 to属性不可以省略
{
path: '/',
element: <Navigate to="/manage/home" replace={true} />
},
{
path: '/login',
element: <Login />
},
{
path: '/manage',
element: <Manage />,
// 子级路由不需要再写 "/"
children: [
{
path: 'home',
element: <Home />
},
{
path: 'commodities',
children: [
{
path: 'category',
element: <Category />
},
{
path: 'product',
element: <Product />
}
]
},
{
path: 'user',
children: [
{
path: 'userManage',
element: <UserManage />
},
{
path: 'accountCenter',
element: <AccountCenter />
},
{
path: 'accountSettings',
element: <AccountSettings />
}
]
},
{
path: 'role',
element: <Role />
},
{
path: 'charts',
children: [
{
path: 'bar',
element: <Bar />
},
{
path: 'line',
element: <Line />
},
{
path: 'Pie',
element: <Pie />
}
]
}
]
}
];
在 main.jsx 中,用 BrowserRouter 包裹 App 组件
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import App from './App';
// StrictMode严格模式在开发环境下为了检测副作用影响会导致组件重复渲染 正式环境没有影响
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>
);
在 App 组件用 useRoutes hook 渲染路由
import React from 'react';
import { useRoutes } from 'react-router-dom';
import routesConfig from '~/config/router';
exprot default function App() {
const routes = useRoutes(routesConfig);
return routes
}
渲染多级嵌套路由, 使用 Outlet 组件,告知子路由应该渲染在什么位置
import React from 'react';
import { Outlet, NavLink } from 'react-router-dom';
export default function Manage() {
return (
<div>
<div className="nav">
<div className="nav-item">
<NavLink to="/manage/user/accountCenter">accountCenter</NavLink>
</div>
<div className="nav-item">
<NavLink to="/manage/user/accountSettings">
accountSettings
</NavLink>
</div>
<div className="content">
<Outlet />
</div>
</div>
</div>
);
}
yarn add react-intl
在 src 下新建 locales 文件夹,在文件夹下新建 en-US.js zh-CN.js 文件
// zh-CN.js
const zh_CN = {
'intl.SEARCH': '查询',
'intl.SELECT': '选择',
'intl.SAVE': '保存',
'intl.ALL': '全部',
'intl.YES': '是',
'intl.NO': '否',
'intl.OK': '确定',
'intl.CANCEL': '取消',
'intl.CLOSE': '关闭'
};
export default zh_CN;
// en-US.js
const en_US = {
'intl.SEARCH': 'Search',
'intl.SELECT': 'Select',
'intl.SAVE': 'Save',
'intl.ALL': 'All',
'intl.YES': 'Yes',
'intl.NO': 'No',
'intl.OK': 'Ok',
'intl.CANCEL': 'Cancel',
'intl.CLOSE': 'Close'
};
export default en_US;
在 App.jsx 中:
import { ConfigProvider } from 'antd';
import { IntlProvider } from 'react-intl';
import { useRoutes } from 'react-router-dom';
import routesConfig from '~/config/router';
import ant_en_US from 'antd/es/locale/en_US';
import ant_zh_CN from 'antd/es/locale/zh_CN';
import intl_en_US from '@/locales/en-US';
import intl_zh_CN from '@/locales/zh-CN';
export default function App() {
const routes = useRoutes(routesConfig);
// ConfigProvider 为antd的全局配置 我们可以利用state改变locale 传入它们的Provider更改全局语言设置
return (
<ConfigProvider locale={ant_zh_CN}>
<IntlProvider locale="zh-CN" messages={intl_zh_CN}>
{routes}
</IntlProvider>
</ConfigProvider>
);
}
在组件中使用:
使用 FormattedMessage 组件
import { FormattedMessage, FormattedDate } from 'react-intl';
export default function Home() {
return (
<>
<FormattedMessage id="intl.Search" />
// input文本框中placeholder文字的国际化
<FormattedMessage id="intl.ENTER_TML_NAME">
{text => <Input placeholder={text} />}
</FormattedMessage>
// 国际化日期
<FormattedDate value={Date.now()} year="numeric" />
</>
);
}
使用 useIntl hook
import { useIntl } from 'react-intl';
export default function Home() {
const intl = useIntl();
return (
<>
<span>{intl.formatMessage({ id: 'intl.Search' })}</span>
// input文本框中placeholder文字的国际化
<Input
placeholder={intl.formatMessage({ id: 'intl.ENTER_TML_NAME' })}
/>
</>
);
}
在非组件中使用
import { createIntl, createIntlCache } from 'react-intl';
import { message } from 'antd';
import intl_en_US from '@/locales/en-US';
import intl_zh_CN from '@/locales/zh-CN';
const locale = navigator.language;
const cache = createIntlCache();
// 创建intl实例
const intl = createIntl(
{
locale,
messages: locale.indexOf('zh') < 0 ? intl_en_US : intl_zh_CN
},
cache
);
message.error(intl.formatMessage({ id: 'Message.401' }));
注意事项: 如果使用了 antd 的时间类组件,需要引入 import 'moment/dist/locale/zh-cn'
否则会出现中英混合的情况,如果使用的时间类的组件多,可在 App.jsx 全局引入,如果只在一个组件里使用,可以在当前组件引入,如果没有使用则不引入
vite.config.js
文件中配置代理,解决跨域,发送请求时带上配置的前缀axios.get('/amap-weather/v3/weather/weatherInfo', params: { })
,配置代码如下:"/amap-weather": {
target: "https://restapi.amap.com/",
changeOrigin: true,
pathRewrite: { "^/amap-weather": "" }
}
yarn add jsonp
安装依赖,引入import jsonp from 'jsonp'
,接口代码如下:export const reqWeather = () => {
return new Promise(resolve => {
jsonp(
`https://restapi.amap.com/v3/weather/weatherInfo?key=${AMAP_KEY}&city=${CITY_ADCODE}&extensions=base&output=JSON`,
(err, data) => {
if (err) {
message.warn('请求天气接口失败,请联系管理员');
return new Promise(() => {});
} else {
if (data.status === '0') {
message.warn('天气数据返回错误,请联系管理员');
} else {
resolve(data.lives[0]);
}
}
}
);
});
};
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。