前言
该笔记是自己做项目时的一些随笔总结,以及如何从RC-->RF的一个过度心得,现在更多是作为一个字典以及备忘录的形式存在。可能有很多不足或者有误的地方,望指正。
项目启动与技术栈选型
- 项目技术栈
- React
- React-Router-Dom
- Redux + React-Redux + Redux-Thunk
- Axios 数据请求
- LeanCloud云服务 在线数据存取操作
- AntDesign UI组件库
一、搭建React开发环境
- 切换镜像
切换安装镜像源 npm config set registry https://registry.npm.taobao.org
配置后可通过下面方式来验证是否成功npm config get registry
- 初始化react项目
npx create-react-app [项目名称]
npm run start(yarn start) 启动项目
import React from 'react'; //视图交互
import ReactDOM from 'react-dom'; //真实dom操作
ReactDOM.render(
<App />,
document.getElementById('root')
)
- Es7 React插件常用
imp import moduleName from 'module'
imr import React from 'react'
imd import { } from 'module'
imc
rcc 生成类组件结构
rfc 生成函数组件结构
rfcredux 生成状态机
二、JSX语法
语法糖,本质是为了更加方便的实现效果 虚拟DOM 与 diff算法 渲染JSX结构 我们需要一种数据格式,表达一段DOM结构
- 通过虚拟DOM来表达
{
tag:'div',
type:'tag',
attr:{
'className':'App'
},
}
- JSX表达DOM结构(React)
let nodes = <div className="App">
asdfasdfa
<span>我是span</span>
</div>
-
JSX渲染组件的流程 JSX结构--虚拟DOM--Diff算法比对--新的虚拟DOM--真实DOM
-
JSX中数据的动态绑定
- 字符串渲染
- 基本运算(加减乘除模)
- 模板字符串
- 三目运算
- 函数调用
- 逻辑运算(与或非)
- JSX嵌套
- 属性、样式动态绑定 className的动态绑定
<div className={isActive?'box active':'box'}></div>
其他属性
- input value\checked\disabled
- img src 注意表单元素的部分属性,例如value value需要用defaultValue checkbox需要用defaultChecked
<input defaultValue={str}/>
<input type="checkbox" defaultChecked={isCheck}/>
三、组件分类
组件名称首字母必须大写(调用组件时不大写会报错)
- 函数式组件
在React较低版本中,我们把函数式组件,又称为无状态组件 新版本中,可以使用Hooks来让函数式组件也拥有状态
export default function FnComp(){
return <div>组件结构</div>
}
- 类组件
通过class类的方式,定义组件 需要引入import React,{Component} from 'react'
import React,{Component} from 'react'
class ClassDemo extends Component{
render(){
return <h1>这是类组件</h1>
}
}
export default ClassDemo
四、事件函数的定义
- 普通函数定义方式(定义生命周期)
必须要在constructor中修正this指向
constructor(){ //自有属性
super() //为ClassDemo提供this,调用了父类的constructor
this.state = {
isActive:false
}
this.handleBg = this.handleBg.bind(this) //修正this指向
}
handleBg(){ //【1】通过普通语法定义事件函数,注意this指向
let {isActive} = this.state;
this.setState({
isActive:!isActive //通过事件触发state变化
})
}
- 箭头函数定义方式
handleBg = ()=>{
let {isActive} = this.state;
this.setState({
isActive:!isActive //【3】通过事件触发state变化
})
}
五、组件通信
总结:(类)父元素变量传参,如果子组件是函数组件,用实参porps接住使用,如果子组件是类组件,用this.props接住直接使用!如果需要回传参数,父组件需要传一个function,子组件用嵌套匿名函数回传参数
(一)、父子通信 props
- 函数式组件
通过组件的第一个参数接受props
//父组件直接传参
<Job jobname={jobname}/>
//子组件通过props接受并使用
import React from 'react'
export default function Job(props) {
return (
<h3>岗位名称:{props.jobname}</h3>
)
}
- 类组件
通过this.props获取props参数
//父组件直接传参
<ClassJob
jobname={jobname}
salary={salary}
handlesalary={this.handleSalary}
/>
//子组件定义时继承原型上有props, 通过this.props参数
render() {
let {jobname,salary,handleSalary} = this.props
return (
<div>
<h3>岗位名称:{jobname}</h3>
<p>薪资:{salary}K</p>
<button onClick={()=>{
handlesalary(idx,5)
}}>涨薪</button>
</div>
)
}
(二)、子父通信
父组件传递事件函数给子组件,以改变父属性的父子通信
- 在父组件定义state
- 在父组件中定义用以修改state的事件函数 handleSalary
- 调用子组件的时候,将父组件的事件函数传递给子组件
constructor(){
super()
this.state = {
joblist:[
{
jobname:"前端工程师",
salary:8
}
]
}
}
handleSalary=(idx,m)=>{
let {joblist} = this.state
joblist[idx].salary += m
this.setState({
joblist
})
}
<ClassJob
jobname={jobname}
salary={salary}
handlesalary={this.handleSalary}
/>
- 在子组件中,按需触发父组件传递的事件函数,引发父组件state的变化
如果需要传参,子组件调用时需要嵌套匿名箭头函数
render() {
let {jobname,salary,idx} = this.props
let {handlesalary} = this.props
return (
<div>
<h3>岗位名称:{jobname}</h3>
<p>薪资:{salary}K</p>
{salary>=20
?''
:<button onClick={()=>{
handlesalary(idx,5)
}}>涨薪</button>
}
</div>
)
}
(三)、父组件调用子组件方法
1、如果是在方法组件中调用子组件(>= react@16.8),可以使用 forwardRef 和 useImperativeHandle:
const { forwardRef, useRef, useImperativeHandle } = React;
const Child = forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({
getAlert() {
alert("getAlert from Child");
}
}));
return <h1>Hi</h1>;
});
const Parent = () => {
const childRef = useRef();
return (
<div>
<Child ref={childRef} />
<button onClick={() => childRef.current.getAlert()}>Click</button>
</div>
);
};
2、如果是在类组件中调用子组件(>= react@16.4),可以使用 createRef:
const { Component } = React;
class Parent extends Component {
constructor(props) {
super(props);
this.child = React.createRef();
}
onClick = () => {
this.child.current.getAlert();
};
render() {
return (
<div>
<Child ref={this.child} />
<button onClick={this.onClick}>Click</button>
</div>
);
}
}
class Child extends Component {
getAlert() {
alert('getAlert from Child');
}
render() {
return <h1>Hello</h1>;
}
}
六、数据渲染
(一)、列表渲染
{
this.state.joblist.map((job,index)=>{
return <ClassJob />
})
}
(二)、条件渲染
{salary>=20
? ''
: <button>涨薪</button>
}
七、获取真实DOM
不建议大规模使用
管理焦点,文本选择或媒体播放。 触发强制动画。 集成第三方 DOM 库。 文档
- 使用createRef生成一个用以获取DOM的属性 myInput
import React, { Component,createRef } from 'react'
this.myInput = React.createRef()
- 将该属性通过ref绑定给指定jsx中的某个节点元素
<input type="text" ref={this.myInput} onKeyUp={this.handleInput}/>
- 在组件的其他逻辑代码中,通过this.myInput获取真实DOM并操作
注意使用了createRef就不要用onchange事件,受控跟不受控两种方式,不要混合使用
handleInput=(ev)=>{
let v = this.myInput.current.value
if(ev.keyCode===13){
let v = this.myInput.current.value
this.setState({
msglist:[
...this.state.msglist,
v
]
},()=>{ //setState执行完毕后才会触发回调
this.myInput.current.value = ''
})
}
}
八、解决setState异步方法的回调
解决setstate异步问题:使用回调(注意回调地狱问题)
this.setState({},()=>{})
this.setState({
msglist:[
...this.state.msglist,
v
]
},()=>{ //setState执行完毕后才会触发回调
this.myInput.current.value = ''
})
九、生命周期函数
**### **小知识PureComponent(选择性更新)
能够自动识别新旧state数据的不同,从而选择性更新
常用生命周期函数 生命周期函数图
初始化阶段
- constructor
- render
- componentDidMount 发起在线请求,获取数据包
更新阶段
- render
- componentDidUpdate
销毁阶段
- componentWillUnmount
十、leancloud
十一、webpack介绍
项目开发打包工具
文档
- 模块化、组件化开发 ---webpack---代码整合
- 高级语法(ES6语法、React语法)---webpack---浏览器可以识别的语法
- 提供比较方便的开发环境
plugin插件与loader的区别
-
Loader的作用是让webpack拥有了加载和解析非JavaScript文件的能力。用于解析不同类型语法的组件(如:React)
- 使用babel-loader协助webpack编译react语法
- css-loader style-loader 解析样式文件及其语法
-
Plugin可以扩展webpack的功能,让webpack具有更多的灵活性。 在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。
-
Babel 是一个 JavaScript 编译器。
- babel的预设:react预设、ES语法预设等
devserver的配置使用
启动一个本地服务,监听代码变化,重新打包代码并自动刷新浏览器进行预览
十二、HOC (Hight-Order-Component) 高阶组件
本质是一个函数 作用就是处理一个已有组件,为这个组件追加新结构或方法后,返回新的组件 HOC文档
- 使用场景 react-router withRouter() 向组件注入路由数据 react-redux connect() 向组件注入store数据
十三、8个Hooks新特性(函数式编程)
常用Hooks特性
(一)、useState
让函数式组件拥有state及操作方法
import React,{useState} from 'react'
let [num,setNum] = useState(100) //定义了一个state名为num,默认值为100,可以通过setNum方法来重新设置num的值
(二)、useEffect
1.第一个参数,接收一个函数作为参数
2.第二个参数,接收【依赖列表】,只有依赖更新时,才会执行函数
3.返回一个函数,先执行返回函数,再执行参数函数
useEffect(()=>{
setList(['赵敏','灭绝师太111'])
(async()=>{
let res = await getTodoList()
setList(res.data.results)
})()
},[]) //依赖可以适当添加
(三)、useLayoutEffect
useLayoutEffect在DOM更新之后执行;useEffect在render渲染结束后执行。执行示例代码会发现useLayoutEffect永远比useEffect先执行,这是因为DOM更新之后,渲染才结束或者渲染还会结束
(四)、useMemo
搭配React.memo做性能优化:存储一个变量,可以传递一个创建函数和依赖项,创建函数会需要返回一个值,只有在依赖项发生改变的时候,才会重新调用此函数,返回一个新的值。简单来说,作用是让组件中的函数跟随状态更新(即优化函数组件中的功能函数)
1.接收一个函数作为参数
2.同样接收第二个参数作为依赖列表(与useEffect、useLayoutEffect进行对比学习)
3.返回的是一个值。返回值可以是任何,函数、对象等都可以
//子组件初始值换成useMemo包起来;子组件只会在初始化状态时渲染一次
const info = {
name: 'Even',
age: 22
}
const info = useMemo( () => { //包裹子组件初始化数据
return {
name: 'Even',
age: 22
}
},[]) //依赖可以适当添加
或者
import React, { useContext, useMemo } from 'react';
export default (props= {}) => {
const { state, dispatch } = useContext(MyContext);
return useMemo(() => {
return (
<div></div>
}, [state.count, state.number. state.step, dispatch]);
}
(五)、useCallback
类同useMome,useCallback是对传过来的回调函数优化,返回的是一个函数;useMemo返回值可以是任何,函数,对象等都可以
function Parent () {
const [num, setNum] = useState(1)
const [age, setAge] = useState(18)
const getDoubleNum = useCallback( () => {
console.log(`获取双倍Num${num}`)
return 2 * num
},[num] )
return (
<div onClick={ () => {setNum( num => num+1 )} }>
这是一个函数式组件————num:{ getDoubleNum() }
<br></br>
age的值为————age:{ age }
<br></br>
set.size:{set.size}
<Child callback={ getDoubleNum() }></Child>
</div>
)
}
function Child(props) {
useEffect( () => {
console.log('callback更新了') //这里代表的是需要跟随传入内容的改变而同步进行的操作
},[props.callback])
return (
<div>
子组件的getDoubleNum{props.callback}
</div>
)
}
子组件更新优化简单总结使用场景判断:
+ 在子组件不需要父组件的值和函数的情况下,只需要使用memo函数包裹子组件即可
+ 如果有函数传递给子组件,使用useCallback
+ 其他使用useMemo
(六)、useRef
useRef 不仅仅是用来管理 DOM ref 的,它还相当于 this , 可以存放任何变量
父组件获取子组件方法、参数并操作子组件
//父组件中:
const detailInfoRef = useRef<DrawerInstance>(null);
然后在detailInfoRef.current中解构出需要使用的方法、值
//子组件中:
//需要使用forwardRef包裹子组件、再useImperativeHandle传递参数,如:
import { forwardRef, memo, useImperativeHandle } from 'react';
const DrawerBillGoods = forwardRef<propsType,refType>(({percent}, ref) => {
const { openDrawer, closeDrawer } = useDrawer();
useImperativeHandle(ref, () => ({
openDrawer,
closeDrawer,
}));
return (
<div>I am update every {percent} seconds</div>
)
})
// memo()可接受2个参数,第一个参数为纯函数的组件,
第二个参数用于对比props控制是否刷新
export default memo(CSlider, (prevProps, nextProps) => {
return prevProps.percent === nextProps.percent;
})
//如:
export default memo(SellerDrawer, (next, prev) =>
_.isEqual(next.data, prev.data),
)
(七)、useContext
需要引入useContetx,createContext两个内容
通过createContext创建一个context句柄
Context.Provider来确定数据共享范围
通过value来分发内容 在子组件中,通过useContext(Context句柄)来获取数据
const Context = createContext(null) //创建/可以分发值
function StateFunction () {
const [num, setNum] = useState(1)
return (
<div>
<button onClick={ ()=> setNum(num => num+1) }>增加num的值+1</button>
这是一个函数式组件——num:{ num }
<Context.Provider value={num}> //确定范围且分发值
<Item3></Item3>
<Item4></Item4>
</Context.Provider>
</div>
)
}
function Item3 () {
const num = useContext(Context) //直接使用
return (
<div>子组件3: { num }</div>
)
}
(八)、useReducer
> 1、对于单个组件复杂的state操作逻辑,嵌套的state的对象,推荐使用useReducer。
> 2、可以通过useReducer在函数式组件中使用Redux。作用是可以从状态管理的工具中获取到想要的状态
使用方法1
function ReducerDemo(){
const [ count , dispatch ] =useReducer((state,action)=>{
switch(action){
case 'add':
return state+1
case 'sub':
return state-1
default:
return state
}
},0)
return (
<div>
<h2>现在的分数是{count}</h2>
<button onClick={()=>dispatch('add')}>Increment</button>
<button onClick={()=>dispatch('sub')}>Decrement</button>
</div>
)
}
使用方法2
const store = {
age:18,
num:1
} //数据仓库(含多个state)
const reducer = (state=store, action) => {
switch(action.type){
case 'add':
return {
...state,
num: action.num+1
}
default:
return {
...state
}
}
} //管理者
function StateFunction () {
const [state,dispacth] = useReducer(reducer,store) //获取状态和改变状态的动作
return (
<div>
<button onClick={ () => {
dispacth({
type: 'add',
num: state.num
})
} }>
增加num的值+1
</button>
这是一个函数式组件——num:{ state.num }
</div>
)
}
十四、Fragment代码片段
渲染一组元素,Fragment本身不会渲染为标签
效果等同 < ></>
import React,{Fragment} from 'react'
export default function Frag1() {
return (
<Fragment>
<p>列表元素</p>
<p>列表元素</p>
</Fragment>
)
}
十五、props.children插槽
return ( //组件中
<div>
<h1>演示props.children</h1>
{this.props.children}
</div>
)
<SlotDemo> //调用时
<p>传入的新标签</p>
<p>传入的新标签</p>
</SlotDemo>
十六、PropTypes类型验证
//组件中,安装引入使用
import PropTypes from 'prop-types'
PropTypeDemo.propTypes = { //为类组件添加类型验证
jobname:PropTypes.string,
salary:PropTypes.number
}
//使用时
<PropTypeDemo jobname="前端开发工程师" salary={20}/>
十七、Context组件通信
可用状态机替换
- 使用流程
- 定义Context对象
- 在组件中使用Provider提供数据
- 在其他组件CompC中提取Context数据
十八、react路由相关模块区分
(一)、基本使用
- 安装
yarn add react-router-dom
- 在App组件外包裹BrowserRouter或HashRouter
import {BrowserRouter} from 'react-router-dom'
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById('root')
);
- 在需要动态展示组件的位置(如:Home组件中),使用Route做路由映射
<Route path="/frag" component={FragDemo}/>
<Route path="/frag" exact component={FragDemo}/>
//exact 表示完全匹配path
- 使用Link实现路由切换
<Link to="/frag">Fragment演示</Link>
// NavLink具有动态激活效果
<NavLink to="/frag/2333" activeClassName="active"></NavLink>
(二)、路由传参
- 设置路由形参
<Route path="/frag/:id" component={FragDemo}/>
- 传递路由实参
<Link to="/frag/2333"></Link>
- 提取路由参数并使用
<h2>路由参数:{props.match.params.id}</h2>
函数式组件路由参数在 props
类组件路由参数在 this.props
(三)、编程式导航
- 绑定事件
- 触发路由切换
==编程式导航传参==
handleProd(target){
//不传参
// this.props.history.push(target)
//传参方法1 接参:props.location.state
// this.props.history.push(target,66666)
//传参方法2 接参:props.location.state
this.props.history.push({
pathname:target,
state:{
id:88888
}
})
}
(四)、路由嵌套
跟主路由配置'基本使用'流程一样 参考 Product/index.js
(五)、路由守卫
render属性的方法函数返回的组件,会被渲染到对应的Route位置
<Route path="/product" render={()=>{ //回调灵活使用
let {isLogin} = this.state; //如登录态
if(isLogin){
return <Product />
}else{
return <Login handleLogin={this.handleLogin}/>
}
}}/>
(六)、switch和重定向
在所有的Route外包裹Switch组件 路由发生变化的时候,Switch会依次去匹配Route的路径,如果有一个路径成功了,后续的Route就不再进行匹配
<Switch>
<Route path="/effect" component={EffectOnline}/>
<Route path="/404" component={NotFound}/>
<Redirect from="*" to="/404"/>
</Switch>
(七)、withRouter 按需使用
路由守卫返回的组件也没有this.props,需要手动使用withRouter
不通过路由引导进行展示的组件内部,默认this.props中是没有路由对象的
withRouter可以让这类组件内部的this.props也能拥有路由对象;比如:默认展示组件home使用this.props
class Home extends Component {}
export default withRouter(Home)
(八)、路由原理分析
手写react路由
- Link组件
- 通过a标签改变hash值
<Link to="/pro">菜单1</Link>
- Route组件
- 监听浏览器地址栏hash变化 onhashchange
- 拿Route组件的path属性 与 hash的路径 进行判断
<Route path="/pro1" component={组件}/>
十九、Redux介绍
- Redux
状态机数据的动态变化,需要手动订阅subscribe
- Redux + React-Redux
不需要手动订阅
- Redux + React-Redux + @rematch/core
语法非常接近vuex 设计模式 (观察者模式)
(一)、核心概念
- Store 生成状态机数据
import {createStore} from 'redux'
import reducer from '../reducer'
let store = createStore(reducer)
export default store
- reducer 根据用户不同的需求,生产不同的数据包
- Action 描述用户需求的对象
{
type:'increment', //action的动作描述
payload:10 //action携带的参数
}
(二)、Redux
- reducer
必须是纯函数 ,结果可预测的数据包
function reducer(state,action对象){
if(action.type){
return 数据包 //根据dispatch传递的type类型返回对应数据
}
}
- store状态机
- getState() 用以获取状态机数据的方法(数据不是动态的)
- subscribe(()=>{}) 在view视图层中,订阅状态机数据的变化,从而做出响应
- dispatch() view视图层向store状态机派发action描述的方法,reducer通过不同的type返回不同的值
componentDidMount(){
this.setState({
num:store.getState()
})
store.subscribe(()=>{
console.log('Num组件察觉到了store数据的变化',store.getState());
this.setState({
num:store.getState()
})
})
}
<button onClick={()=>{
store.dispatch({
type:'increment'
})
}}>+</button>
(三)、redux + react-redux
删除商品:方法放在reducer中
- 引入connect并注入状态机
- 引入准备好的actioncreator
- 将actioncreator传入connect使之变成一个对action对应的dispatch
- 使用事件调用reducer中的方法 <DEL_GOOD>
- action
一个用以描述数据变动方式的对象
涉及到本地存储之类的操作不要在action中执行,去reducer中相应位置操作
{
type:'',
payload:''
}
- ActionCreator
一些能够生成action的方法函数
function increment(){
return {
type:'',
payload:''
}
}
- 安装react-redux
yarn add react-redux
- 在项目入口文件index.js中,从根组件注入store状态机
import {Provider} from 'react-redux'
import store from './store'
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
- 在Num.js中提取store状态机中的==数据==
数据在reducer中已被注入,所以不用引入
<!--
connect方法接受两个参数:mapStateToProps和mapDispatchToProps。
它们定义了 UI 组件的业务逻辑。前者负责输入逻辑,即将state映射到 UI
组件的参数(props),后者负责输出逻辑,即将用户对 UI 组件的操作映射成 Action。
-->
import {connect} from 'react-redux' //【1】使用connect方法关联状态机
class Num extends Component { }
const mapStateToProps = (state)=>{ //【2】此处的state就是connect注入的状态机数据包
return { //【3】此处return的值,将作为props出现在Num组件中
count:state.?? //count是自定义的props名称
}
}
export default connect(mapStateToProps)(Num)
// mapStateToProps 的作用,是为了把状态机中的数据引用到当前组件的props中去
- 在Btn.js组件中,提取状态机中的dispatch==方法==
方法需要importy引入
import React, { Component } from 'react'
import {connect} from 'react-redux' //【1】引入connect方法来关联状态机
import {
decrement,
increment
} from '../../action/index' //【2】准备好的actionCreator
class Btn extends Component {
render() {
let {children,increment,decrement} = this.props
//【4】此处的increment,decrement已经变为了一个跟action对应的dispatch
let disp = children=='+'? increment : decrement
return (
<button onClick={disp}>{children}</button>
)
}
}
export default connect(null,{
increment, //【3】将actionCreator传入connect中
decrement
})(Btn)
(四)、redux + react-redux + redux-thunk [常见方案]
redux-thunk使得状态管理器具有异步action的能力
异步请求发起位置
- 组件内部发起? 异步请求用于渲染组件
- 异步action? 异步请求用于操作状态机
哪些场景数据包适合放在状态机中?
- 跨组件共享
- 购物车、个人信息、地址管理 等
- 安装
yarn add redux-thunk
- 在store/index.js中引入并配置
import {createStore,combineReducers, applyMiddleware} from 'redux'
import thunk from 'redux-thunk'
let reducer = combineReducers({cartReducer,countReducer})
// applyMiddleware配置
let store = createStore(reducer,applyMiddleware(thunk))
- 在action/index.js中定义实现异步action
export const incrementSync = (payload)=>{ //异步action
return dispatch =>{
//当用户触发异步action的时候,将dispatch进行拦截
//等到异步操作结束后,再手动dispatch一个action
setTimeout(()=>{
dispatch(increment()) //用户在合适的条件下,自己手动dispatch
},2000)
}
}
- 在组件中调用异步action
跟调用同步action的流程一样 参考BtnSync.js
(五)、@rematch/core 的使用流程
简化了store状态机的定义流程 参考 React(理论周)/day05/learn-redux 项目中的 learn-rematch分支
(六)、redux的HOOKS写法(useSelector, useDispatch )
import { useSelector, useDispatch } from 'react-redux';
import { addAction, reduceAction } from './store/actions/action';
const App = (props: any) => {
const { num } = useSelector((state: any) => state.numReducer);
const dispatch = useDispatch();
return (
<>
<h2>全局数据store:{num}</h2>
<button onClick={() => {
dispatch(addAction())
}}>+</button>
<button onClick={() => {
dispatch(reduceAction())
}}>-</button>
</>
)
}
二十、react项目的自定义配置
实现的目标:
如何关闭EsLint 如何在项目中使用less语法 antd的自定义主题
- 自定义配置需要的包 react-app-rewired customize-cra
customize-cra提供具体某个配置项 react-app-rewired 可以让我们以新的方式启动项目,并让上面提供的配置项生效
(一)、资源引用路径的别名配置
@ 指向 src @action 指向 src/action
跳转config-overrides.js配置文件
addWebpackAlias({
'@':path.resolve(__dirname,'src'),
'@action':path.resolve(__dirname,'src/action'),
'@api':path.resolve(__dirname,'src/api'),
'@assets':path.resolve(__dirname,'src/assets'),
'@components':path.resolve(__dirname,'src/components'),
'@utils':path.resolve(__dirname,'src/utils')
})
(二)、自定义配置的流程
- 安装依赖
yarn add react-app-rewired customize-cra --dev
- 在项目根目录下新建config-overrides.js文件并配置
const {
override,
disableEsLint
} = require('customize-cra')
module.exports = override(
disableEsLint() //关闭eslint代码检测
)
- 在package.json中调整scripts命令
"scripts": {
- "start": "react-scripts start", 原有命令
+ "start": "react-app-rewired start", 新命令
}
(三)、addLessLoader配置
- 安装样式预编译包
yarn add --dev less less-loader
- 调整config-overrides.js配置文件
addLessLoader({ //less文件编译配置
lessOptions: {
javascriptEnabled: true,
modifyVars: { '@primary-color': '#A80000' },
}
})
(四)、装饰器的配置流程
语法糖
- 使用装饰器语法前
class Login extends Component{
}
export default connect(mapState)(Login)
- 使用装饰器
@connect(mapState)
class Login extends Component{
}
- 装饰器语法文档 关于装饰器语法
@testable
class MyTestableClass {
// ...
}
function testable(target) {
target.isTestable = true;
}
MyTestableClass.isTestable // true
- 安装bebel库
yarn add @babel/plugin-proposal-decorators
- 配置config-overrides.js
为了让脚手架环境支持装饰器语法 文档
- vscode对装饰器语法的识别配置
如果代码能运行,但是vscode提示语法错误,则需要配置
"settings": {
"problems.decorations.enabled": false,
"javascript.implicitProjectConfig.experimentalDecorators": true
},
实战 · 富文本编辑器
- 什么是富文本?
- 富文本中会携带标签跟样式
- vue 中可以使用 v-html
- 小程序中 rich-text
- uniapp rich-text
如何在React中集成第三方 wangEditor Ueditor tinymce
- 在React中集成富文本编辑器流程
- 安装、引入
yarn add wangeditor
import E from 'wangeditor'
-
获取一个富文本编辑器的容器DOM节点 createRef
-
在需要展示富文本编辑器的组件中的componentDidMount中初始化编辑器
this.e = new E(this.editor.current) //富文本编辑器实例
this.e.create()
this.e.config.zIndex = 1
- 通过指定方法提取富文本编辑器内容
let intro = this.e.txt.html()
实战 · 高德地图
在React中集成地图API 高德地图api
- 注册登录
- 新建应用并创建key
- 在项目public/index.html中引入高德地图方法包(js包)
- 在需要展示地图的组件中调用AMap方法初始化地图
var map = new AMap.Map(this.container.current, {
resizeEnable: true
});
实战 · 用户角色控制
不同权限控制手段
- 登录权限 得到roles角色
- 通过roles筛选路由数据包,动态渲染符合权限要求的导航菜单 【用户看不到菜单】
- 通过roles筛选路由数据包,动态渲染符合权限要求的Route
- 后端在执行数据库操作前,需要对当前用户权限做判断 【后端开发完成】
- 按钮级别的权限控制 【根据实际情况决定】
- 路由映射关系数据包,放在服务器端动态下发 【可选】
路由添加rolus[]键值对[含能访问的角色],后续优化按钮级别权限时,[{user:root,C:true,R:false]},{user:other,R:true}]
- 根据登录后状态机中存放的roles判断菜单和路由渲染情况
- includes(a)数组api用于判断数组中是否存在a
let bool = roles.includes(role) && title
return bool
?
<Menu.Item key={itm.path}>{itm.title}</Menu.Item>
: ''
- 路由控制需要用404兜底,渲染时基本步骤同菜单,需要用 取消正常路由渲染时多出来的404组件
实战 · Echarts的基本使用
- 安装
yarn add echarts
- 在需要展示图表的组件中,引入echarts提供的方法
方法1:
import {init} from 'echarts'
方法2:
// import * as echarts from 'echarts'
-
为图表展示准备一个DOM容器
-
调用echarts的init方法初始化图表并做自定义配置
var myChart = init(this.chart1.current);
// 指定图表的配置项和数据
var option = {};
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option);
react 打包时
"build": "react-scripts build",
改成
"build": "react-app-rewired build",