文章目录
react特点
优势
声明式设计 −React采用声明范式,可以轻松描述应用。
//命令式编程:命令“机器”如何去做事情(how),这样不管你想要的是什么(what),它都会按照你的命令实现。类似jquery 操作DOM,创建一个页面,一点点的告诉DOM 怎么去挂载,你要怎么去做;JQ\原生 都是命令式编程,都是在做DOM 操作。 //声明式编程:告诉“机器”你想要的是什么(what),让机器想出如何去做(how)。高效 −React通过对DOM的模拟,最大限度地减少与DOM的交互。
//1.虚拟DOM(Virtual DOM) 用js 对象来模拟页面上的DOM 和DOM嵌套**实现DOM 元素的高效更新 //2.Diff 算法 tree diff component diff element diff
灵活 −React可以与已知的库或框架很好地配合。
JSX − JSX 是 JavaScript 语法的扩展。React 开发不一定使用 JSX ,但我们建议使用它。
组件 − 通过 React 构建组件,使得代码更加容易得到复用,能够很好的应用在大项目的开发中。模块化
vue的组件是通过.vue 文件(template/script/style)实现的
react中没有像vue中那样的模板文件,在react里一切都是以js来实现的
单向响应的数据流 − React 实现了单向响应的数据流,从而减少了重复代码,这也是它为什么比传统数据绑定更简单。
劣势
- 学习成本很高,学习曲线很陡
- react本身能做的事并不多,想做大东西必须得用react插件(全家桶)
jsx
介绍
全称: JavaScript XML, React 使用 JSX 来替代常规的 JavaScript。
JSX 是一个看起来很像 XML 的 JavaScript 语法扩展。
我们不需要一定使用 JSX,但它有以下优点:
JSX 执行更快,因为它在编译为 JavaScript 代码后进行了优化。
它是类型安全的,在编译过程中就能发现错误。
使用 JSX 编写模板更加简单快速。
作用: 用来创建react虚拟DOM(元素)对象
- var ele = <h1>Hello JSX!</h1>;
- 注意1: 它不是字符串, 也不是HTML/XML标签
- 注意2: 它最终产生的就是一个JS对象
标签名任意: HTML标签或其它标签
标签属性任意: HTML标签属性或其它
基本语法规则
- 遇到 <开头的代码, 以标签的语法解析: html同名标签转换为html同名元素, 其它标签需要特别解析
- 遇到以 { 开头的代码,以JS的语法解析: 标签中的js代码必须用{ }包含
babel.js的作用
- 浏览器的js引擎是不能直接解析JSX语法代码的, 需要babel转译为纯JS的代码才能运行
- 只要用了JSX,都要加上type=“text/babel”, 声明需要babel来处理
入口文件index.js的理解
//在index.js文件中App变量中保存的数据渲染到root关联的根元素中了
//但是js文件中应该写js语法,js语法变量中只能保存:6种基本数据 和2种引用数据
//let App=<div>666</div> 保存的是一个标签 这种语法就是JSX语法
//JSX语法就是一种插值语法 它经过打包工具编译后本质上还是JS语法
//JSX:
// 1.遇到了<>就会解析为标签:
// 1.1如果是html语法标准的标签就会直接解析为同名元素 <nav/> <div/> <article/>
// 1.2如果是其它标签需要特别解析--组件<App>
// 1.3 <App/> 这个代码的意义决定了能不能真正入门react框架: 可以理解为:<App titel="200"/> ===> new App(200) (这里只是方便理解,不是等价的)
使用样式
React 推荐使用内联样式。React 会在指定元素数字后自动添加 px 。
render() {
let mystyle={color:'red',fontSize:33}
return (
<div style={mystyle}>我的样式</div>
)
}

注释
注释需要写在花括号中,需要注意的是:
- 在标签内部的注释需要花括号
- 在标签外的的注释不能使用花括号
条件渲染
在 JSX 内部不能使用 if else 语句,但可以使用 三元运算 表达式来替代。
render() {
let i=0
return (
<div>
{i==1? <h1>正确</h1>:<h1>错误</h1>}
</div>
)
}

可以在JSX外部使用if语句
render() {
let islogin=true
let Box=''
if(islogin){
Box=<h1>已登录</h1>
}else{
Box=<h1>未登录</h1>
}
return (
<div>
{Box}
</div>
)
}

数组
JSX 允许在模板中插入数组,数组会自动展开所有成员
render() {
let arr=[1111,2222,3333,4444,5555]
return (
<div>
{arr}
</div>
)
}

列表渲染
通过数组的map函数
map函数的返回值是一个数组,数组里面的值为处理后的值
render() {
let arr=[{title:'ljy',age:22},{title:'jack',age:30},{title:'marry',age:25}]
return (
<div>
{arr.map(el=><p>{el.title}--{el.age}</p>)}
</div>
)
}

组件
React.js 中一切皆组件,用 React.js 写的其实就是 React.js 组件。
我们在编写 React.js 组件的时候,一般都需要继承 React.js 的 Component(类定义)。一个组件类必须要实现一个 render 方法,这个 render 方法必须要返回一个 JSX 元素。但这里要注意的是,必须要用一个外层的 JSX 元素把所有内容包裹起来。返回并列多个 JSX 元素是不合法的。
定义单个组件
方式1:通过React自定义组件(DOM元素):类定义
import React, { Component } from 'react'
export default class Box4 extends Component {
render() {
return (
<div>Box4</div>
)
}
}
方式2:通过React自定义组件(DOM元素):函数定义
import React from 'react'
export default function Box4(props) {
return (
<div>Box4</div>
)
}
方式3:不建议使用,React.createClass创建组件的方式在react 16版本中去除。
var MyApp = React.createClass({
render: function() {
return <h1>Hello World!</h1>;
}
});
使用组件
import React from 'react';
import ReactDOM from 'react-dom/client';
import Box4 from './Box4.jsx'; 导入自定义组件
const root = ReactDOM.createRoot(document.getElementById('root')); //关联页面根元素
root.render(<Box4 />); //给根元素内部渲染组件模板
定义复合组件
我们可以通过创建多个组件来合成一个组件,即把组件的不同功能点进行分离
import Box5 from './Box5'
import React, { Component } from 'react'
export default class Box4 extends Component {
render() {
return (
<div>
<h1>Box4组件中</h1>
<Box5></Box5>
</div>
)
}
}

传值Props
定义和使用props 传值
通过React类定义组件时:
- 在父组件render 方法中调用组件时使用key/value 的形式来指定属性。
- 在自定义子组件中通过this.props.key 来获得组件属性的值,需要使用{}括起来。
//父组件Box4中
render() {
let a='父组件Box4传递的数据'
return (
<div>
<h1>Box4组件中</h1>
<Box5 mypro={a}></Box5> //定义属性名绑定数据传递给子组件
</div>
)
}
//子组件Box5中
render() {
return (
<div>
<h2>Box5中-{this.props.mypro}</h2> //通过this.props.父组件传递的属性名 来接收
</div>
)
}

通过React函数定义 组件时:
- 在父组件render 方法中调用组件时使用key/value 的形式来指定属性。
- 在自定义子组件中通过函数接收参数props,props.key来获得组件属性的值,需要使用{}括起来
// 函数定义组件时,在组件内部使用属性值:props.属性名称
function Welcome(props) { // 函数需要传递一个参数props
return(<h2>{props.title}</h2>)
}
ReactDOM.render(
<Welcome title="张三" />,
document.getElementById('root')
);
定义默认props:
export default class Box5 extends Component {
render() {
return (
<div>
<h2>Box5中-{this.props.name}</h2>
</div>
)
}
}
//由于是用ES6 class语法创建组件,其内部只允许定义方法,而不能定义属性,class的属性只能定义在class之外。所以defaultProps要写在组件外部。
Box5.defaultProps = {
name: 'Box5'
};
多属性传值
(1)定义一个this.props对象,在对象中声明多个键值对,用于表示组件的属性
(2)在组件中使用**{…this.props}**的方式传递属性。“…”表示JSX的延展操作符,这种方式可以很方便的为组件指定多个属性,并且为属性的值指定数据类型。
//父组件ox4中
render() {
let a="父组件传递的值"
let b=1111111111
let c=[111,222,333]
let props={
a,b,c
}
return (
<div>
<h1>Box4组件中</h1>
<Box5 {...props}></Box5>
</div>
)
}
//子组件Box5中
render() {
return (
<div>
<h2>Box5中-{this.props.a}--{this.props.b}--{this.props.c}</h2>
</div>
)
}
State
React 的核心思想是组件化的思想,应用由组件搭建而成,而组件中最重要的概念是State(状态),State是一个组件的UI数据模型,是组件渲染时的数据依据。
如何定义State
组件中用到的一个变量是不是应该作为组件State,可以通过下面的4条依据进行判断:
- 这个变量是否是通过Props从父组件中获取?如果是,那么它不是一个状态。
- 这个变量是否在组件的整个生命周期中都保持不变?如果是,那么它不是一个状态。
- 这个变量是否可以通过其他状态(State)或者属性(Props)计算得到?如果是,那么它不是一个状态。
- 这个变量是否在组件的render方法中使用?如果不是,那么它不是一个状态。这种情况下,这个变量更适合定义为组件的一个普通属性。
注意:并不是组件中用到的所有变量都是组件的状态!
import Box5 from './Box5'
import React, { Component } from 'react'
export default class Box4 extends Component {
constructor(arg){
super(arg)
this.state={
msg:0
}
//写法一
this.change1=()=>{
this.setState({msg:this.state.msg+1}) //使用setState函数更改状态
}
//写法二
this.change2=()=>{
this.state.msg++
this.setState(this.state) //修改变量之后,调用setState刷新render 从而页面重新取到已经修改后的值
}
}
render() {
return (
<div>
<h1>Box4组件中</h1>
<h2>{this.state.msg}</h2>
<button onClick={this.change1}>change1</button><button onClick={this.change2}>change2</button>
</div>
)
}
}
注意:React在ES6的实现中,规定state在constructor中实现
正确定义State的方式如下:
(1)在constructor中实现state
(2)在constructor中通过bind绑定事件函数(事件函数是用来改变状态)
(3)在事件函数内部使用setState函数更改状态
(4)在组件中的render函数中使用该状态
(5)在组件上需要设置监听事件,去触发事件函数的执行
setState设置状态
(1)语法:
setState(object nextState[, function callback])
(2)说明:
- setState是React事件处理函数中和回调函数中触发UI更新的主要方法。
- 不能在组件内部通过this.state修改状态,因为该状态会在调用setState()后被替换。
- setState()不一定是同步的,为了性能提升,React会批量执行state和DOM渲染。
- setState()总是会触发一次组件重绘,但可在shouldComponentUpdate()中实现一些条件渲染逻辑来解决。
同步设置多个状态时(不常用),可以在setState函数的第二个参数可以传递一个function回调函数,如下:
this.change1=()=>{
this.setState({msg:this.state.msg+1},()=>{
this.state.num+= this.state.msg
})
}
**状态上移:**当存在多个组件共同依赖一个状态,或是当子组件的props 数据需要被修改时,将这个状态放到对应组件的父组件中:
State 与 Props 区别
除了State, 组件的Props也是和组件的UI有关的。他们之间的主要区别是:
- props 中的数据都是外界传递过来的
- state 中的数据都是组件私有的;(通过Ajax 获取回来的数据,一般都是私有数据)
- props 中的数据都是只读的,不能重新赋值
- state 中的数据都是可读可写的
- State定义在constructor内部,在super(props)代码后面;Props默认值定义在类(组件)的外部
当子组件的属性值是可变值时,采用状态上移:
状态上移通过属性将父组件的状态传递到子组件,那么父组件的状态发生变化时,子组件的属性也会改变
state/props 实现父子组件通信
- 子组件获取父组件整个组件进行传参
- 父组件在调用子组件时,传入一整个组件给子组件**<Children parent={ this } />**
- 父组件中定义一个方法getChildrenMsg(resulet, msg),用来获取子组件传来的值以及执行其他操作
- 子组件在通过this.props来获取到一整个组件this.props.parent 或者this.props[parent]
- 子组件调用父组件步骤2里定义的方法进行传值this.props.parent.getChildrenMsg(this,val)
Parent:
import Box5 from './Box5'
import React, { Component } from 'react'
export default class Box4 extends Component {
constructor(arg){
super(arg)
this.state={
msg:0,
num:0
}
//写法一
this.change1=()=>{
this.setState({msg:this.state.msg+1},()=>{
this.state.num+= this.state.msg
})
}
//写法二
this.change2=()=>{
this.state.msg++
this.setState(this.state)
}
this.change3=(arg,arg2)=>{
this.state.msg=arg
this.state.num=arg2
this.setState(this.state)
}
}
render() {
let props={
msg:this.state.msg,
num:this.state.num,
change3:this.change3
}
return (
<div>
<h1>Box4组件中</h1>
<h2>msg--{this.state.msg}</h2>
<h2>num--{this.state.num}</h2>
<Box5 {...props}></Box5>
<button onClick={this.change1}>change1</button><button onClick={this.change2}>change2</button>
</div>
)
}
}
Children:
import React, { Component } from 'react'
export default class Box5 extends Component {
render() {
return (
<div>
<h2>Box5中---msg:{this.props.msg}----num:{this.props.num}</h2>
<button onClick={()=>{this.props.change3('子组件修改了msg','子组件修改了num')}}>点击更改父组件传递的数据</button>
</div>
)
}
}

Redux
我们为什么需要状态管理器Redux?为了方便的组织和管理组件之间的数据。
Redux是什么?
Redux是一个流行的JavaScript框架,为应用程序提供一个可预测的状态容器。Redux基于简化版本的Flux框架,Flux是Facebook开发的一个框架。在标准的MVC框架中,数据可以在UI组件和存储之间双向流动,而Redux严格限制了数据只能在一个方向上流动。

Store:存储数据的公告区域;eg–图书管理员;
React Component:页面组件;eg–借书的用户;
Reducer:数据存放位置;eg–图书记录本;
Action Creators:动作;eg–结束的时候,跟图书管理员说的话;
Redux基本原则
- 单一数据源;
- state是只读的;
- 使用纯函数来执行修改;
Redux核心API
- createStore(reducer):创建数据仓库: import {createStore} from “redux”;
- store.getState():用于获取store里面的数据;
- store.dispatch(action):用于派发action,触发reducer,执行修改动作;
- store.subscribe(componentMethods):订阅store的修改,只要store发生改变,组件中的回调函数就会被执行;
store的创建和使用
使用Redux需要安装该模块
npm i redux –save
创建reducer(store/reducer.js),提供数据源,是一个纯函数
// src/store/reducer.js
const defaultState = {
username: "ljy",
stulist: [1, 2, 3]
}
//1、state用于提供为仓库提供数据源,需要给定一个初始值
//2、action用于对state 进行更新处理,表示要执行的动作,通过dispatch(action)触发;action被传递到reducer内部来处理,action中必须有一个type字段
const reducer = (state=defaultState) => {
//action……
return state;//必须返回处理后的state
}
export default reducer;
- 创建数据仓库(store/index.js):createStore()
```react
// src/store/index.js
import {createStore} from "redux";
import reducer from "./reducer";
const store = createStore(reducer);
export default store;
组件内部使用数据:getState()
//组件内部引入仓库
import store from "./store/index";
//获取数据
<h2>{store.getState().username}</h2>
数据的修改和订阅
在组件内部定义要执行的动作action,并触发该动作dispatch。action中type字段是必须的,值通常大写:
addValue() {
const action = {
type: "ADD",
data: "测试数据"
}
store.dispatch(action);
}
接收动作并修改数据:通过dispatch触发动作后,会将action传递到reducer函数的第二个参数中
const reducer = (state=defaultState, action) => {
switch(action.type) {
case "ADD":
state.stulist.push(action.data);
break;
default:
break;
}
return state;
}
组件中及时响应更新的数据:订阅
componentDidMount() {
// 订阅store改变事件
store.subscribe(this.changeValue.bind(this));
}
changeValue() {
this.setState(store.getState())
}
subscribe函数可以监听Redux中state的变化,一旦Redux中的state发生了变化,render函数就会被调用,页面就会被重新渲染