React相关总结

1.react中几个核心概念

提示:react中的一个重要理念是 all in js
react关注的是数据的操作,是一个数据驱动的框架,stateprops 中的数据发生变化的时候,render 函数就会被重新执行;同样,父组件传递的数据发生变化时,子组件也将重新执行 render 函数。而不是DOM操作,数据发生变化,对应的DOM就会改变

虚拟DOM

  • DOM的本质是: 浏览器中的概念,用js对象表示页面上的元素,并提供了操作DOM元素的API。
  • React中的虚拟DOM是: 是框架中的概念,是程序员用js对象来模拟页面上的DOM和DOM嵌套。
  • 使用虚拟DOM的目的: 为了实现页面中的DOM元素的高效更新。
  • 虚拟DOM和ODM二者间的区别:
    • 浏览器提供的概念,用js对象来表示页面中的元素,并未元素提供了相应的API。
    • 虚拟DOM:框架中的概念,是开发框架的程序员,手动用js对象来模拟DOM元素及其嵌套关系的。
      1. 本质:用js对象来模拟虚拟DOM元素和嵌套关系
      2. 目的:为了实现元素的高效更新
//虚拟DOM底层原理的实现
1.state数据
2.JSX模板

3.数据+模板 生成虚拟DOM(虚拟DOM就是一个js对象,用它来描述真实的DOM) ==>(损耗了性能)
	['div',{节点属性},[子节点]]
	['div',{id:'abc'},['span',{},'hello world']]
	
4.用虚拟DOM结构生成真实的DOM,来显示
	<div id='abc'><span>hello world</span></div>
	
	
5.state发生变化

6.数据+模板 生成新的虚拟DOM ==>(极大的提升了性能)
	['div',{id:'abc'},['span',{},'hello you']]
	
7.新旧虚拟DOM作对比,找到不同就更新 ==>(极大提升了性能)

8.直接操作DOM,改变span中的内容

优点:性能提升了
		   使得跨端应用得以实现。React Native

温馨提示
	   React中生成DOM或者 对比DOM是非常耗性能的。
	   而通过JSX生成的js对象并去对比js对象只会消耗极小的性能,这样整体下来提升了性能。

2.Diff算法(different)

  • tree diff: 新旧两颗DOM数,逐层对比的过程,就是Tree Diff;当整个DOM逐层对比完成之后,则需要按需更新的元素必然能够找到。

  • component diff: 在进行 tree diff 的时候,每一层,组件级别的对比,就是 组件树。

  • 注意:

    • 如果对比前后,组件类型相同,则暂时认为组件不需要被更新。
    • 如果对比前后,组件类型不同,则需要移除就组件,创建新组件,并追加到页面中去。
  • element diff: 在对比组件的时候,如果两个组件类型不同,则需要进行元素级别的对比,这叫做 element diff.
    在这里插入图片描述

3.registerServiceWorker

我们使用create-react-app脚手架时,会发现入口文件index.js中有这样一个引入使用。
在这里插入图片描述
Http 协议上的服务,PWA:progressive web application

这个文件可以视情况用或者不用,它是用来做离线缓存等任务的,实际上就是为react项目注册了一个service worker。这样的话,如果在线上,只要访问过一次该网站,以后即使没有网络也可以访问(此时使用的是之前缓存的资源)。只在生产环境中有效(process.env.NODE_ENV === ‘production’)

在项目的public目录下,会有一个manifest.json文件,可以在这里做一些配置(图标、名字等等)。当用户把网页生成一个快捷方式时。会感觉用起来像原生APP一样。

4.JSX语法

  • 注意:react项目中使用到 jsx 语法时,必须引入 react
import React,{Component,Fragment} from 'react';
//JSX -> React.createElement() -> 虚拟DOM(JS对象) -> 真实的DOM

class App extents Component{
	constructor(){    //最优先执行,构造方法
		super(props);
		//数据定义在状态中state中
		this.state = {
			test:'test',
		}
		/* 
			使用 jsx 语法时必须导入react包,并且使用js代码时,需要放在 {} 中
			改变state数据时,需要使用 this.setState({})   不能直接改变state下的值
			组件上绑定方法的时候,注意  通过  .bind(this)
			事件不同原生的事件   应该是  onClick
			或者在此处声明:  this.function = this.function.bind(this);
		 */
	}
	render(){
		return (
			//可以用Fragment占位符来处理根标签
			<Fragment>     
				<div>Test</div>
				<ul>
					<li>react</li>
					<li>redux</li>
				</ul>
			</Fragment>
		)
	}
}

export default App;

<!--传统方式-->
 <script type="text/babel">
     //1.创建变量
     let myClass = "span";
     let myContent = "你好,世界!";

     //2.创建虚拟DOM
     const vDom = React.createElement('span',{className:myClass},{myContent});

     //3.将虚拟DOM渲染到页面上去
     ReactDOM.render(vDom,document.getElementById("app1"));
 </script>

5.子父组件间的传值

  • 父组件向子组件
    父组件通过属性来传值,子组件通过 this.props.属性值 来获取数据
    在子组件中一般会通过变量来声明父组件传递过来的属性名称,并指定类型。
type Props = {
	name: String,
	func: Function,
}

class App extends Component<Props> {
	render() {
		//使用 this.props.name 来获取值属性值
	}
}
  • 子组件向父组件
    父组件通过属性的传递方式将方法传递给子组件,子组件通过 this.props.方法名 来调用方法,从而实现将数据传递给父组件。
    注意:父组件向子组件传递方法,并且子组件需要执行该方法的一定时候,一定要留意 this 的指向问题。

6.ES6语法(可以用来优化代码)

  • 可以通过解构赋值的方式来获取 state 下的值
    const { test } = { this.state }

  • 可以将 this.setState({}) 写成es6高级语法

    this.setState({},callback) 属于异步函数,可以通过回调函数来处理响应的操作。

this.setState(() => {
	return {***};
});

this.setState(() => ({***}));

this.setState((prevState) => ({***}));    //prevState 相当于是 this.state

7.React生命周期函数

https://blog.csdn.net/zlq_CSDN/article/details/94971594

8.React中组件概念

  • UI组件: 负责的是页面的渲染问题,也称为“傻瓜组件”;
// UI组件
class TodoListUI extends Component{
    render() {
        return (
            <div style={{marginTop:20,marginLeft:20}}>
				<Input 
                    style={{width:300}}
                    value={this.props.inputValue}
                    onChange={this.props.handleInputChange}
                />
                <Button onClick={this.props.handleClick} type="primary">提交</Button>
                <List
                    style={{width:300,marginTop:10}}
                    bordered
                    dataSource={this.props.list}
                    renderItem={(item,index) => (
                        <List.Item onClick={(index) => {this.props.handleClickDelete(index)}}>
                            {item}
                        </List.Item>
                    )}
                />
            </div>
        )
    }
}
  • 容器组件: 来使用UI组件,并且负责组件中的逻辑问题,也称为“聪明组件”;
  • 无状态组件: 实际上是一个函数,一个组件中,只有 render 时,就可以改变为一个无状态组件,性能比UI组件高。
// 无状态组件(作为一个函数,将模板渲染出去,参数props来接收父组件传递的值)
const TodoListUI = (props) =>{
    return (
        <div style={{marginTop:20,marginLeft:20}}>
            <Input 
                style={{width:300}}
                value={props.inputValue}
                onChange={props.handleInputChange}
            />
            <Button onClick={props.handleClick} type="primary">提交</Button>
            <List
                style={{width:300,marginTop:10}}
                bordered
                dataSource={props.list}
                renderItem={(item,index) => (
                    <List.Item onClick={(index) => {props.handleClickDelete(index)}}>
                        {item}
                    </List.Item>
                )}
            />
        </div>
    )
}

9.redux-thunk中间件进行ajax请求发送

注意:中间件是属于 redux 的,而不是react 。中间可以理解为是在某两个部分之间建立的连接(redux-thunk中间件在actionstore之间建立了连接)。使用了 redux-thunk 中间件后,在action中使用时,可以返回一个函数。
实质上是对 dispatch 方法的一个升级。具体体现:

  1. 当action中返回一个对象的时候,会直接传递给store。
  2. 当action返回一个函数的时候,会先执行函数。

在这里插入图片描述

import {createStore, applyMiddleware, compose} from 'redux';
import reducer from './reducer';
import thunk from 'redux-thunk';

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;
const enhancer = composeEnhancers(applyMiddleware(thunk));
//获取到数据
const store = createStore(reducer, enhancer );

export default store;
  • actionCreatoer.js 中编写方法
// 使用 redux-thunk 中间件来进行ajax请求,可以返回一个函数
export const getTodoList = () => {
	return (dispatch) => {   //返回的这个函数会自动的接收dispatch方法
		axios.get('/list.json').then((res) => {
			const data = res.data;
			const action = getInitItemAction(data);
			console.log(action);
			dispatch(action);     //真正的将action提交到 index.js 中
        });
	}
}
  • 在生命周期函数中使用
// 生命周期函数来获取
componentDidMount(){
     //通过store.getState() 来获取store中的数据
     console.log(store.getState());   
     const action = getTodoList();   //此处的action是个方法
     store.dispatch(action)
 }

10.redux-saga 中间件来发送ajax请求

配置了该中间件后,当通过 store.diapatch() 提交 action 后,就可在 sagas.js 文件中监听到 action。

  1. store 下的 index.js 中进行配置
import {createStore, applyMiddleware, compose} from 'redux';
import reducer from './reducer';
import createSagaMiddleware  from 'redux-saga';

import todoListSage from './sagas';    //需要在store目录下创建文件 sagas.js

const sagaMiddleware = createSagaMiddleware();   //创建中间件
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;
const enhancer = composeEnhancers(applyMiddleware(sagaMiddleware));
//获取到数据
const store = createStore(reducer, enhancer );
sagaMiddleware.run(todoListSage);  //运行文件


export default store;
  1. 创建文件 sagas.js
// 通过 takeEvery 来捕获 dispatch 方法    put 来提交
import {takeEvery, put} from 'redux-saga/effects';
import {GET_INIT_TODO} from './actionTypes';    //规范action名称类型
import {getInitItemAction} from './actionCreator';
import axios from 'axios';

// 在该方法中进行 ajax 数据请求
function* getInitList(){
    try{
        const res = yield axios.get('./list.json');  //等待ajax数据请求完毕返回给res
        const action = getInitItemAction(res.data);
        yield put(action);
    }catch(e){
        console.log('list.json数据请求失败');
    }
}

// 当配置了 redux-saga 后,在 sagas.js 中也能够监听到 dispatch 方法
// generator函数(es6 内容)
function* mySaga() {
    yield takeEvery(GET_INIT_TODO, getInitList);  //当监听到dispatch方法后,就会执行  getInitList 方法
}
  
export default mySaga;  

详情请参考:https://github.com/redux-saga/redux-saga

性能优化

  • 将方法作用域绑定在 constructor中,而不在组件中绑定方法的时候来 .bind(this) 来绑定
constructor(props){
	this.handleclick = this.handleclick.bind(this);
}
  • this.setState({}) 中有内置的性能提升机制,属于异步函数(可以回调处理),将多次数据的改变集成到一次改变中,从而降低了虚拟DOM的匹配频率。
  • 使用生命周期函数 shouldComponentUpdate()
shouldComponentUpdate(nextProps,nextState){
	if(***){    //条件表示当数据发生变化的时候   比如:if(nextProps.content !== this.props.content)  前后两次的属性内容发生了变化
		return true;
	}else{
		return false;
	}
}

细节总结

  • 使用 className,而不是class
  • 使用 htmlFor,而不是for,jsx中默认class、for是保留字。 <label htmlFor=""></label>
  • dangerouslySetInnerHTML={{ __html=item }} //item表示渲染的数据,整体上表示解析html代码
  • propTypes、defaultTypes 属性校验 https://zh-hans.reactjs.org/docs/typechecking-with-proptypes.html#___gatsby
  • ref 来获取DOM 元素
//ref属性支持回调
<button ref={(btn) => {this.ul = ul}}></button>    //this.ul 指向的就是 当前DOM对象   可以在js中直接通过this.ul来访问对象
  • map遍历
    在使用map遍历的时候,需要添加 key 属性,否则会有警告。
    key需要添加在遍历渲染元素的最外层。

思考,react特点

  • 声明式的开发
  • 可以与其他框架并存
  • 组件化 (与标签的区别,组件首字母大写)
  • 单项数据流(子组件只能使用父组件的数据,而不能直接来修改数据,只能通过传递参数的形式来改变数据)
  • 视图层(当组件层级过多的时候,单靠react是不能够来实现项目的开发,需要借助类似于 redux 来管理)
  • 函数式的编程 (都是通过函数来进行对数据的处理,从而来更新DOM)
  • 版本上线,同Vue项目 npm run build
持续更新中…
发布了106 篇原创文章 · 获赞 46 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/zlq_CSDN/article/details/94842879