一、初始化项目(create-react-app)
create-react-app 项目名
二、配置别名(alias)
1、安装依赖
yarn add customize-cra react-app-rewired --save-dev
2、根目录创建config-overrides.js文件,注意名字不要写错,内容如下
const { override, addDecoratorsLegacy, addWebpackAlias } = require("customize-cra"); const path = require("path"); module.exports = override( addDecoratorsLegacy(), addWebpackAlias({ "@": path.resolve(__dirname, './src') }) );
3、修改package.json的scripts部分,如下
"scripts": { "start": "react-app-rewired start", "build": "react-app-rewired build", "test": "react-app-rewired test", "eject": "react-app-rewired eject" }
三、配置反向代理(http-proxy-middleware)
yarn add http-proxy-middleware
在src目录下创建 setupProxy.js 文件
// setupProxy.js const { createProxyMiddleware } = require('http-proxy-middleware') module.exports = function (app) { app.use(createProxyMiddleware('/api', { target: 'http://152.136.185.210:4000', secure: false, changeOrigin: true, pathRewrite: { "^/api": "" } })) }
四、引入样式( scss / less / style-component )
scss
npm install sass-loader node-sass --save-dev
npm install --save-dev sass-resources-loader 在用scss预处理器的时候,会常用变量/函数/混合等功能。目前如果想要使用变量/函数的话需要在每个文件中单独引入,这样明显不合理,我们需要的是引入一次,所以我们就需要定义全局主题的样式变量,也就是利用sass-resources-loader这个loader将scss变量打包到每个文件中。首先我们要安装sass-resources-loader。
//在项目根目录,也就是和package.json同级创建config-overrides.js文件,该文件配置代码如下: const { override, adjustStyleLoaders } = require("customize-cra"); module.exports = override( // ...其他配置... adjustStyleLoaders(rule => { if (rule.test.toString().includes("scss")) { rule.use.push({ loader: require.resolve("sass-resources-loader"), options: { resources: "./src/assets/scss/output.scss" //这里是你自己放公共scss变量的路径 } }); } }) );
styled-components
npm install styled-components
新建一个专门负责样式的 style.js 文件 import styled , { ThemeProvider } from 'styled-components' ThemeProvider 是最外层的样式组件 <ThemeProvider theme={{themeColor : "red"}}> <Children/> <ThemeProvider/> 可以用来提供主题。在子组件中通过 ${props => props.theme.themeColor} 访问
五、引入请求模块(axios)
yarn add axios
六、引入路由模块(react-router-dom)
yarn add react-router-dom
import { Link , NavLink , withRouter , Route, Switch , BrowserRouter ,withRouter } from 'react-router-dom'
1. <BrowserRouter> history路由 2. <HashRouter> hash路由 3. <Route> 跳转路由 path="路径",component={组件} 4. <Redirect> 重定向 to="路径" 5. <Link> 跳转标签 6. <NavLink> 高亮跳转标签 添加 active 类名 7. <Switch> 选中一个后停止匹配,用于优化 8. withRouter函数 将非路由组件转换为路由组件,使其能够编程式跳转路由 withRouter(组件) 入口文件必须制定路由模式,并包裹组件或其祖宗组件才能使用 内置组件
1.编程式跳转
1. history对象 函数式组件需在函数参数声明形参props后,通过props可以拿到这些对象参数 2. match对象 获取query 3. location对象 locations.pathname 可以拿到当前路径 其它对象 注:若不是路由组件,需使用withRouter()函数进行包装成路由组件 import React from 'react' import { Switch, Route, Redirect,withRouter } from 'react-router-dom' export default function Discover(props) { function changeRouter(path) { if (props.location.pathname !== path) { props.history.push(path) } } return <div> <button style={{ color: pathname === '/home/recommend' ? 'red' : null, }} onClick={(e) => changeRouter('/home/recommend')} > 点击我跳转推荐页 </button> </div> }
2.动态路由参数
params传的参数是暴露在url中的 // 且后代路由都会接收到该参数 // 主 需要 先声明 后注册传递参数 注册声明 <Route path=' /sort/:id ' component={Sort}></Route> 传递参数 this.props.history.push( '/sort/'+'2' )、 取值 props.match.params.id(函数式组件)
state传的参数是加密的 // 且后代路由不会接收到该参数, 重要的一点 : 刷新也可以保持住参数 // 主 无需声明,直接注册使用即可 传递参数 props.history.push({ pathname: path, state: { name: '孙悟空' } }) 取值 props.location.state.name(函数式组件)
search(query) 需要解析,所以略过 // 且后代路由都会接收到该参数 无需声明,直接注册使用即可 传递参数 props.history.push({ pathname, query: { name: ' sunny' } }) 取值 this.props.location.query.name
七、路由集中管理(react-router-config)
yarn add react-router-config
import { renderRoutes } from 'react-router-config';
//renderRoutes 可以配置 路由管理映射 //子路由通过组件内的 const { route } = props 拿到当前路由对象, 然后通过api renderRoutes(route.routes)
import React from 'react'; import { Redirect } from "react-router-dom"; import Discover from "../../src/components/centerMain/discover" import Recommend from "../components/centerMain/discover/recommend" const routes = [ { path: "/", exact: true, render: () => ( <Redirect to="/discover"/> ) }, { path: "/discover", component: Discover, routes: [ { path: "/discover", exact: true, render: () => ( <Redirect to="/discover/recommend"/> ), }, { path: "/discover/recommend", component: Recommend }] } ] export default routes;
在根组件中使用
import React, { memo , Suspense } from 'react' import { renderRoutes } from 'react-router-config'; import { Provider } from "react-redux" import TopBar from "./components/topBar/index.js" import Player from "./components/player/index.js" import HYAppFooter from '../src/components/bottomBar'; import routes from './router'; import store from '../src/store' import './assets/css/base.css' import 'antd/dist/antd.css'; // or 'antd/dist/antd.less' import { HashRouter } from 'react-router-dom'; export default memo(function App() { return ( <Provider store={store}> <HashRouter> <TopBar></TopBar> <Suspense fallback={<div>page loading</div>}> {renderRoutes(routes)} </Suspense> <HYAppFooter/> <Player/> </HashRouter> </Provider> ) })
八、引入全局状态管理工具(redux)
yarn add redux react-redux redux-thunk // redux 全局状态管理工具 // react-redux 将react和redux连接起来 // redux-thunk 用于异步操作
九、引入antd组件库
1.安装依赖
npm install antd --save
yarn add antd
2.引入样式
import 'antd/dist/antd.css'; // or 'antd/dist/antd.less'
3.在需要使用的页面中引入antd组件
import { DatePicker } from 'antd';
Form
const [form] = Form.useForm() //可以拿到表单实例 form.setFieldsValue({ name: 'Hi there!' })// 可以设置表单对应字段的值 可以输入多个字段 form.resetFields() //重置表单
安装脚手架(react-cli)
cmd : 1.更换npm源 npm config set registry https://registry.npm.taobao.org 2.检查是否更换成功 npm config get registry //如果显示 https://registry.npm.taobao.org 该地址就表示配置成功了 3.全局安装 react 脚手架 cnpm install -g create-react-app 4.创建react脚手架项目 create-react-app + 项目文件夹名称 5.启动项目 npm start 6.打包项目 npm build
引入eslint
一、
安装vscode插件
EditorConfig for VS Code
根目录添加文件
// .editorconfig # http://editorconfig.org root = true [*] # 表示所有文件适用 charset = utf-8 # 设置文件字符集为 utf-8 indent_style = space # 缩进风格(tab | space) indent_size = 2 # 缩进大小 end_of_line = lf # 控制换行类型(lf | cr | crlf) trim_trailing_whitespace = true # 去除行首的任意空白字符 insert_final_newline = true # 始终在文件末尾插入一个新行 [*.md] # 表示仅 md 文件适用以下规则 max_line_length = off trim_trailing_whitespace = false
二、
VSCode需要安装Prettier - Code formatter的插件
npm install prettier -D
配置.prettierrc文件:
{ "useTabs": false, //使用tab缩进还是空格缩进,选择false; "tabWidth": 2, // tab是空格的情况下,是几个空格,选择2个; "printWidth": 80, //当行字符的长度,推荐80,也有人喜欢100或者120; "singleQuote": true, //使用单引号还是双引号,选择true,使用单引号; "trailingComma": "none", //在多行输入的尾逗号是否添加,设置为 `none`; "semi": false //语句末尾是否要加分号,默认值true,选择false表示不加; }
配置.prettierignore忽略文件
/dist/* .local .output.js /node_modules/** **/*.svg **/*.sh /public/*
在package.json中配置一个scripts:
"prettier": "prettier --write ."
三、ESLint
VSCode需要安装ESLint插件
npm i eslint-plugin-prettier eslint-config-prettier -D
在package.json中配置extend:
extends: [ "eslint:recommended", "plugin:prettier/recommended" ],
dva.js
安装脚手架
npm install dva-cli -g
创建项目
dva new dva-quickstart
全局状态管理dva
import {routerRedux} from 'dva/router' // routerRedux 可以实现在状态管理下进行路由跳转 // 类似vuex模式 export default { //命名空间 namespace: 'example', // 状态 state: {}, // 初始化会执行 subscriptions: { setup({ dispatch, history }) { // eslint-disable-line }, }, // 相当于 vuex中action // payload 执行dispatch时传过来的参数 // call是执行函数的方法 // put 相当于 dispatch effects: { *fetch({ payload }, { call, put }) { // eslint-disable-line yield put({ type: 'save' }); }, }, // 相当于 vuex中mutation reducers: { save(state, action) { return { ...state, ...action.payload }; }, }, };
新的状态子模块需要在index.js入口文件中引入
app.model(require('./models/example').default);
umi.js
yarn create @umijs/umi-app
dva状态管理
根模块 1.在src目录下新建 models 文件夹,新建一个js文件。文件名就是根模块状态名 子模块 1.在src/pages/Home目录下新建 models 文件夹,新建一个js文件。文件名就是分模块状态名
模块文件js,书写如下
export default { state: { name: '王洪元', }, effects: { *getUserInfoAction({ payload }: any, { call, put }: any) { yield put({ type: 'changeUserInfo', payload }); console.log(payload); }, }, reducers: { changeUserInfo(state: any, action: any) { return { ...state, name: action.payload, }; }, }, };
组件中使用
import React from 'react'; import { connect } from 'dva'; const Home = ({ dispatch, rootUserName, homeUserName }: any) => { const handleChangeUserInfo = () => { dispatch({ type: 'global/getUserInfoAction', payload: '上古鬼' }); }; const handleChangeHomeUserInfo = () => { dispatch({ type: 'home/getUserInfoAction', payload: 'masjdge' }); }; return ( <div> <div> <span>根姓名:{rootUserName}</span> </div> <div> <span>home姓名:{homeUserName}</span> </div> <div> <button onClick={handleChangeUserInfo}>根模块点击更改名字</button> <button onClick={handleChangeHomeUserInfo}>子模块点击更改名字</button> </div> </div> ); }; const mapStateToProps = (state: any) => { return { rootUserName: state.global.name, homeUserName: state.home.name, }; }; export default connect(mapStateToProps)(Home);
运行时配置
//执行顺序 render > patchRoutes // 往路由表中向前增加一条路由映射 export function patchRoutes({ routes }: any) { console.log(routes); routes[0].routes.unshift({ path: '/foo', exact: true, title: '蕉叶', component: require('@/pages/Home/index').default, }); routes[0].routes.unshift({ path: '/', exact: true, redirect: '/home', }); } // 比如用于渲染之前做权限校验, // 覆写 render。 export function render(oldRender: any) { // console.log('中间件。路由在刷新前可以做一些事哦'); console.log(123); oldRender(); } // 在初始加载和路由切换时做一些事情。 export function onRouteChange({ location, routes, action, matchedRoutes }) { // 动态修改网页标题 if (matchedRoutes.length) { document.title = matchedRoutes[matchedRoutes.length - 1].route.title || ''; } } // 修改交给 react-dom 渲染时的根组件。 // 给根标签包裹一层组件 // export function rootContainer(container) { // return React.createElement(ThemeProvider, null, container); // }
页面跳转
声明式
import { Link } from 'umi'; export default () => ( <Link to="/list">Go to list page</Link> );
命令式
import { history } from 'umi'; function goToListPage() { history.push('/list'); }
属性获取
export default (props) => ( <Button onClick={()=>props.history.push('/list');}>Go to list page</Button> );
引入图片
JS 里使用图片
通过 require 引用相对路径的图片。
比如:
export default () => <img src={require('./foo.png')} />
支持别名,比如通过 @
指向 src 目录:
export default () => <img src={require('@/foo.png')} />
JS 里使用svg
组件式引入
import { ReactComponent as Logo } from './logo.svg' function Analysis() { return <Logo width={90} height={120} />}
url式引入
import logoSrc from './logo.svg' function Analysis() { return <img src={logoSrc} alt="logo" />}
CSS 里使用图片
通过相对路径引用。
比如,
.logo { background: url(./foo.png); }
CSS 里也支持别名,但需要在前面加 ~
前缀,
.logo { background: url(~@/foo.png); }
注意:
-
这是 webpack 的规则,如果切到其他打包工具,可能会有变化
-
less 中同样适用
图片路径问题
项目中使用图片有两种方式,
-
先把图片传到 cdn,然后在 JS 和 CSS 中使用图片的绝对路径
-
把图片放在项目里,然后在 JS 和 CSS 中通过相对路径的方式使用
前者不会有任何问题;后者,如果在 JS 中引用相对路径的图片时,在发布时会根据 publicPath 引入绝对路径,所以就算没有开启 dynamicImport 时,也需要注意 publicPath 的正确性。
Base64 编译
通过相对路径引入图片的时候,如果图片小于 10K,会被编译为 Base64,否则会被构建为独立的图片文件。
10K 这个阈值可以通过 inlineLimit 配置修改。