1:安装
cnpm install -g create-react-app
2:初始化项目
create-react-app demoNmae
3:了解react脚手架中的webpack配置文件
释放配置文件(一般不释放)
npm run eject(要删除目录下的.git文件) 释放所有的webpack配置文件
4:理解react设计的核心思想,一切皆为组件,可以细化一个元素
5:React开发特性:除了react本身这个库,其它的所有解决方案以及依赖库都是社区维护
名称释义:
JavaScript XmL 就是使用JS来描述一个DOM结构的对象
1:类组件(状态组件,有this)
快捷键创建方式:rcc
必须有return方法,return我们的Dom内容,同时,我们需要继承react.component,使我们的类变成一个组件。在return内部需要一个render函数,通过render函数来渲染创建我们的Dom内容。
类组件默认有属性,this.props,里面包含了关于路由(router)的一些参数,具体参见下面的react路由部分
事件this问题,参见下面react事件机制部分
2:函数组件(无状态组件,无this)
快捷创建方式:rfc
定义一个方法,通过return导出我们的jsx对象
3:组件应该包含的内容
react 库
react-dom 组件内容
react-dom.return 导出组件内容 类组件中应当包含render方法,渲染我们的DOM节点
4:关于组件的引用
在react中,引入的组件的第一个字母,必须大写(特别注意)
1:事件this指向问题
需要使用箭头函数或bind(this)的方法改变this的指向
(因为this是谁调用就是指向谁,在jsx事件中,this为jsx对象,无this,所以需要将this指向外层)
2:事件传值方式:默认为最后一个参数为事件对象,如果用户传值,则按顺序接收传值
基本传值方式:在组件上直接使用元素属性来传值,指定传值的属性
传值的接收:
1:类组件:使用this.props来接收值,接收到的是一个对象,里面包含有传过来的属性值
2:函数组件:使用入参的形式接收参数,接收到的是一个对象(props入参)
1:安装属性类型检查插件:cnpm i -S prop-types
2:使用方法
引入我们的prop-types库
import propCheck from 'prop-types'
一:类组件中
通过定义静态变量的方式来进行属性校验
static propTypes = {
//propsName : propCheck.type 例子如下
userid: propCheck.stying,
//添加isRequired属性来标识当前属性为必传属性
userName : propCheck.stying.isRequired
}
//属性默认值
static defaultProps = {
userAge: '12'
}
注:以上两个静态变量的名称为固定写法,不可更改 (propTypes, defaultProps)
二:函数组件中(类组件也可以使用此方法)
直接在组件对象上添加一个属性XXX.propTypes = {}对象 进行属性校验
在组件对象上添加一个属性XXX.defaultProps = {}对象 进行属性默认初始化
const Home = props =>{
return (
.....
)
}
Home.propTypes = {
userid: propCheck.stying,
userName : propCheck.stying.isRequired
}
Home.defaultProps = {
userid: '志伟'
}
一:在jsx对象中使用style属性来进行样式书写
style属性使用绑定语法接收一个对象,这个对象里面的各种样式属性都是以小驼峰命名的方式存在着
example:
return(
)
二:使用classNames插件来给定样式名
cnpm i -S classnames
let sytle = classnames({
active:active,
disable:!active
"title-cls":true,这里因为有- 所以需要使用引号
})
三:使用less来进行样式处理加载
在react的webPack中,已经集成了sass样式预计sass样式的预处理器,如果使用sass来书写样式,可以不用安装sass已经sass-loader
使用less
由于react中没有集成less已经less-loader,所以如果我们需要使用less,需要重构我们的开发依赖
1:安装我们的 react-app-rewired
cnpm i -D react-app-rewired
2:安装我们的less一级less-loader
cnpm i -D less less-loader
3:安装一个自定义库 customize-cra
cnpm i -D customize-cra
4:在我们的package.json同级目录下,新建一个文件: config-overriders.js
在config-overriders.js文件中,引入自定义库,使用overrider方法,导出对象,再把对应的库加载到这个方法里面(2.x版本)
const {
override,
addLessLoader
addDecoratorsLegacy //Es6装饰器
} = require('customize-cra')
module.exports = override(
addLessLoader()
addDecoratorsLegacy()
)
1.x版本(现在基本不用了)
module.exports = config => {
console.log(config.module.rules[1].oneOf)
config.module.rules[1].oneOf.push({
test: /\.less$/,
use: [
{
loader: "style-loader",
},
{
loader: "css-loader",
},
{
loader: "less-loader",
options: {
lessOptions: {
strictMath: true,
},
},
},
],
})
return config
}
四:修改项目的启动命令
在package.json文件中,将我们的启动命令改为如下
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build"
}
在react中,虚拟Dom节点,使用createElement(type, props,children)方法 三个参数必传,不能不传
我们的render方法最终都会转换为createElement方法
createElement(
'div',//标签
{},//属性
“这里是div标签内容”,
createElement( //从第三个参数开始,同级排列的参数都为div标签的子元素
'p',//标签
{},//属性
“这里是p标签内容”, //从第三个参数开始,同级排列的参数都为p标签的子元素
)
“这里是div标签的第二个内容”
)
当我们需要使用ajax进行跨域请求的时候,需要我们使用react中配置请求代理
一:安装依赖库 cnpm i -D http-proxy-middleware
二:配置setupProxy文件,在src路径下进行一个setupProxy.js文件,默认导出一个函数对象,这个函数有一个入参,为我们的express实例对象,通过这个对象,使用插件,引入我们的http-proxy-middleware库,然后使用createProxyMiddleware方法。
这里注意:app.use(),可以传入一个插件,或者定义一个插件,(定义一个插件第一个参数为,变量名,第二个参数为,处理内容)
代码如下:
const { createProxyMiddleware } = require('http-proxy-middleware')
module.exports = app =>{
app.use( //这里app.use使用一个插件库,createProxyMiddleware这个方法就返回一个插件
createProxyMiddler('/apis',{
//目标地主:
target:'xxxxxxxx',
//重写/apis字符
pathRewrite:{'/apis':''},
//是否改变请求源,域名
changeOrigin:true
})
)
}
//这里导入导出的规范是遵循的common.js的模块化规范
到这里一个项目的基本框架就搭好了,这里放德明老师给我们项目框架,cnpm install
documentary-web.zip (报两天工时,开始摸鱼)
下面继续,react知识
一:类组件中(两种方式)
1:直接写state,将类中的state覆盖掉
state = {
}
2:在constructor里面写
constructor() {
// 为了能够继承Component 的构造函数,所以必须使用super()
super()
this.state = {
date: Date.now(),
__date: Date.now()
}
}
二:在函数组件中
使用useState方法
let [state, setState] = useState(initialState);
需要使用值得使用直接state,设置值得时候setState方法
setState(newState)
一:在我们的jsx对象上给定ref属性
二:通过我们的this.refs.属性可以获取到页面节点(不推荐)
三:通过createRef方法来获取,获取到的是一个reactDom对象,current属性为当前的页面节点(推荐),一般在componentDidMount生命周期,指定echarts图对象
代码如下:
state = {
node:createRef()
}
render(){
return (
)
}
componentDidMount(){
this.state.node.current //当前div的页面节点
}
四:在函数组件中
使用useRef()方法,类似于类组件的createRef方法
类组件的生命周期
一张图,说明
这里列出常用的生命周期(按用途分,需要牢记)
一:挂载生命周期
componentDidMount()
可以获取页面节点,修改state初始数据,echarts对象实例化
二:更新生命周期
shouldComponentUpate(nextprops,nextstate)(更新之前)
用于性能优化,当props或state发送变化时,shouldComponentenUpdate()会在渲染执行之前被调用。返回值默认为true。首次渲染或使用forceUpadate()时不会被调用
PS:在做性能优化的时候,最后判断一下新旧两个props/state是否相同,相同则返回false不要触发更新,这样就不用生成新的dom树和旧的进行diff算法对比,从而进行性能优化
shouldComponentUpdate(nextProps, nextState){
if(nextProps.content !== this.props.content){
return true
}
else{
return false
}
}
componentDidUpdate(prevProps, prevState)(更新之后)
更新之后会被立即调用。首次渲染不会执行此方法。当组件更新后,可以在此处对DOM进行操作,如果使用this.setState()方法,需要加上限制条件,不然会造成死循环
三:卸载生命周期
componentWillUnmount()
一般移出我们的监听事件和我们的echarts对象,和大数据对象,已经清除定时器
函数组件的钩子函数
一:useState()方法,相当于我们类组件的state属性
useState方法返回一个对象,第一个参数为state的值,第二个参数为设值state的方法,useState方法中可以传递一个初始值
let [token, setToken] = useState('diengkdngaignelgndslgjieng')
二:useEffect()方法
它与类组件的componentDidMount、shouldComponentUpdate这个生命周期类似
如果传递了第二个参数,在数据进行改变的时候,只有当被监听的数据发生变化才会执行这个方法,否则不会执行
useEffect( () =>{
.........
},[props])
三:useMemo()方法
使用useMemo方法的时候,一定要加上我们的依赖项,避免性能消耗,需要有一个返回值,return
let total = useMemo(() =>{
return amount * rate + amount
})
四:useRef()方法
类似于类组件的createRef方法
为了能够解决数据逐层传递的问题,提出使用cocntext来进行局部/全局简单的数据和方法传递
//引入创建我们的createContext方法
import Reatc, { createContext } from 'react'
const context = createContext()
引入createContext方法创建一个新的Context对象,使用Context.Provider来储存数据(数据,方法)
把组件的子组件加载到页面上,指定位置
const Store = ({children}) => {
const [title, setTitle] = useState('新标题')
const changeTitle = num => {
setTitle('新标题+' + num)
}
return (
//provider,必须有value属性
Store Title
{ children }
)
}
导出当前组件,并且还要导出与Provider成对的Consumer组件
const Comsumer = Context.Consumer
export {
Store,
Comsumer
}
在下发数据的地方进行组件注册(scr/index.js 全局数据挂载,也可以在对应的需要传递数据的地方进行注册)
在需要消费的地方使用store.js导出Consumer对象进行数据消费
Consumer组件必须接收一个方法或函数
在Provider进行注册的数据/方法以入参的形式进行注入
{
args => {
return {
args.changeTitle(Math.random())
}
}>{args.title}
}
}
把一个复杂的业务分为了一步一步的操作,简单理解柯里化
高阶函数
function x(a){
return function(b){
return a+b
}
}
高阶组件,类似于一个方法里面返回组件,这个方法里面需要传入一个组件作为入参,然后再重新返回一个组件
const MainLayout = (PageLayout) =>{
return class Layout extends Component{
render(){
return (
)
}
}
}
redux相对于context来说,redux更加的规范,体系完整,有完整的数据操作流程
一:redux原理
简单来说就是,数据初始化—>数据修改动作发起—>数据扭转—>数据导出
深层原理:
二:在项目中使用react-redux(将redux和context进行结合,进行全局数据传递)
1:安装redux cnpm i -S redux react-redux
2:新建一个store文件夹,暴露一个使用Redux.createStore方法创建一个store对象出来,store对象接收一个合并后的reducers
import { createStore } from 'redux'
import RootReducer from './reducers'
export default createStore(RootReducer)
3:需要新建一个reducers文件夹,暴露一个使用Redux.combineReducers合并后的reducer出来
combineReducers这个方法,接收一个对象,里面里面传入我们的不同的reducer
import { combineReducers } from 'redux'
import count from './count'
export default combineReducers({
count
})
4:在reducers文件夹下边新建reducer文件进行单独的业务reducer开发。一个reducer为一个方法,这个方法需要传入两个参数,一个为初始值,一个为action。action为一个对象,这个对象必须包含一个type,以及其他需要传过来的参数。在reducer进行数据扭转
import {REDUCE_ACT, ADD_ACT} from '../action/types'
const initData = {value: 50}
const reducer = (state = initData, action) => {
switch(action.type) {
case REDUCE_ACT:
return {
...state,
value: state.value - action.step
}
case ADD_ACT:
return {
...state,
value: state.value + action.step
}
default:
return {...state}
}
}
export default reducer
5:因为action需要一个type,type一般为一个动作的类型/名字,所以需要使用静态变量来声明动作名字或类型
export const REDUCE_ACT = '减少'
export const ADD_ACT = '增加'
6:因为页面只是调用action,每一个action有单独的业务处理,所以需要单独定义每一个action
import {REDUCE_ACT, ADD_ACT} from './types'
export const getReduceAct = () => {
return {type: REDUCE_ACT, step: 3}
}
export const getAddAct = () => {
return {type: ADD_ACT, step: 6}import { connect } from 'react-redux'
7:在主页上全局注册
自己封装一个Context,使用Provider进行数据管理,再使用Consumer进行数据获取
import { Provider } from 'react-redux'
// 在使用react-redux的Provider的时候,不能使用value进行挂载store对象,需要使用store属性进行redux挂载
,
8:在其他页面上进行数据引用和动作发起
import { connect } from 'react-redux'
connect方法
第一个参数为映射state中的数据到组件的属性上
第二个参数为dispatch,如果不写的话会自动在组件的属性上加上dispatch方法,否则需要自己实现dispatch方法映射
三:异步action的实现
很多时候,在定义action的时候,需要我们请求数据,以便进行传递,这个时候,我们就需要实现我们的异步action了
问:为什么使用了中间件就能实现异步action了呢
1: 安装库:cnpm i -S redux-thunk
2: 在store的createStore方法中使用applyMiddleware方法进行thunk中间件使用
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import RootReducer from './reducer'
export default createStore(RootReducer, applyMiddleware(thunk))
3:在action中的return 会是一个回调函数,回调函数会入参一个dispatch对象,在完成所有业务以后,使用dispatch方法返回一个action
const addAjaxAction = () =>{
return async dispatch => {
let result = await ajax()
dispatch({type:actionType, step: 10})
}
}
一:基本使用
1:Router模式
Router(HashRouter:在路由中有一个#,BrowserRouter:这中模式成为H5 History模式,在react中开发可以测试,当正式发布的时候,需要后端支持)
Switch(相当于switch方法,匹配一个即结束路由匹配);Route(当条路由配置对象/组件:path、component/render)组件使用;Redirect组件,重定向
2:通用路由的使用方法
1、默认路由一般方法路由定义的最后,如果有404页面,需要放在默认路由的后边;
2、权限判断,可以使用简单的三目运算,也可以使用render方法进行权限判断
1:react-router-dom:是页面开发的路由,react-router-native:是react-native(RN)开发的路由管理器
安装我们的路由管理器
cnpm i -S react-router-dom
2:在我们的pages下面的index页面,进行路由管理
render() {
return (
{/* 使用switch匹配到一个路由就停止查找路由 */}
{/* 相当于页面的switch方法,在写默认路由的时候,需要把默认路由写在最后 */}
{/* 普通模式下,匹配路由以什么开头,或则与当前要跳转的路由一定匹配 */}
{/* 如果路由出现二级这样的路由的时候,需要使用exact来精确匹配 */}
{/* */}
{
return
}
} exact/>
{/* */}
{/* {
return (
this.getUserRole()
?
:
)
}
}/> */}
{/* 默认路由,页面进来初始化路由 */}
{/* 如果整个路由都匹配完以后,还是没有匹配到具体的路由,应该跳转到默认页面 */}
)
}
二:路由传参
1、动态路由,在定义路由的时候在路由字符串上添加(/:paramsName)属性,在页面上使用props.match.params.paramsName来接收参数;
2、在页面上的history.push()方法中传递一个对象,{pathname: 路由名字, state: {key-value形式来传递参数}},在页面上使用props.location.state对象来接收数据;
3、在Route中使用render方法,routeParams => RouteComponent,对应的数据在页面上使用props直接接收;
a、得到一个空项目,编写全局请求地址文件
b、新建src/index.js作为项目的入口文件
c、新建src/pages/index.js作为项目组件分发/入口的文件
d、项目中需要使用ajax请求:安装axios来实现数据交互,开发的时候需要使用http-proxy-middleware做请求代理,cnpm i -S axios http-proxy-middleware,新建src/setupProxy.js 文件来做开发请求代理:导出一个方法,方法有一个Express()实例对象,使用Express().use方法注册代理方法
e、项目中需要使用样式预处理器less:安装样式加载器less/less-loader,还需要使用react-app-rewired、脚手架资定加载库customize-cra配置到全局webpack rules中:cnpm i -D less less-loader react-app-rewired customize-cra;在package.json同级目录下新建config-overrides.js,在这个文件中需要导出一个重载方法(override()),再在这个重载方法中添加需要使用到的customize-cra库;需要修改package.json中的启动、打包命令:react-scripts -> react-app-rewired
f、因为cra是单页面开发模式,需要使用路由这个库来实现页面切换:使用react-router-dom库来实现功能,cnpm i -S react-router-dom,在项目的页面入口src/pages/index.js 文件中进行路由部署
g、项目中需要缓存一些重要的数据,减少后端接口的调用次数减轻后端服务器压力,使用redux、react-redux,为了能够在redux的action中发起异步操作需要使用redux-thunk库:cnpm i -S redux react-redux redux-thunk;为了解决react-redux的connect方法的调用使用难度,需要使用注解/装饰器这个库,修改config-overrides添加装饰器库;
h、在项目中新建一个文件夹(src/store),需要使用store/index.js暴露一个store数据对象(reducer是合并后的reducers,支持异步操作的中间件redux-thunk库的安装);新建src/store/reducer文件夹,使用reducer/index.js 合并并暴露一个reducer;因为reducer需要传递一个action,action在redux中定义为一个对象,并且必须有type属性,所以需要新建src/store/action文件夹,再在action/types.js文件中暴露所有的type;
类似于vue中的数据后,重新渲染你的dom节点的原理,减少因为拷贝数据对象造成的性能损耗
用法
定义一个Map对象
import { Map } from 'immutable'
const obj = Map({
x:10,
y:20,
z:30,
k:{
x:40,
y:{
h:60
}
}
})
//获取值
obj.get('x')
//获取有层级下的值
obj.getIn(['x','y','h'])
//设置值
obj.set('x')
//设置有层级下的值
obj.setIn(['x','y','h'])
在项目中的使用
在我们的store中,reducer进行数据扭转的时候,可以使用map来减少因为拷贝数据对象造成的性能损耗
import { Map } from 'immutable'
let initData = Map({
count:10
})
const common = {state = initDate, action} =>{
switch(action.type){
cast:'add'
return state.set('count', state.count + action.step)
}
}
在页面上,使用我们的数据,则为Map.get()
import { combineReducers } from 'redux-immutable'
const reducer = combineReducers({
common
})
//页面的使用
const mapStateToProps = state =>{
count: state.get('common').get('count')
}
//get('count')为reducer中的state的MaP
//state.get('common')为我们reducer的Map
安装我们的Mobx
数据的提供者 Provider
数据的消费者 inject
在我们的store中直接定义我们的一个类,保护我们方法和数据
import { action, observable } from 'mobx'
class Counter {
@observable
count = 10,
@action
add(step){
this.count += step
}
}
export default new Counter()
在我们的主页面中
import { Provider } from 'mobx-react'
import counter from './store/counter'
render(
document.getElementById('root')
)
在我们用的页面上,我们直接使用
import { inject, observer } from 'mobx-react'
@inject('counter')
@observer //函数组件用useobserver
class Home extends Component{
//获取我们传下拉的值
console.log(this.props.counter.count)
render(){
return(
{this.props.counter.count}
)
}
}