一、简介
1)简介
React主要用于构建UI。你可以在React里传递多种类型的参数,如声明代码,帮助你渲染出UI、也可以是静态的HTML DOM元素、也可以传递动态变量、甚至是可交互的应用组件。
React 拥有较高的性能,代码逻辑非常简单,越来越多的人已开始关注和使用它。
2)特点
优势
-
声明式设计 ?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 构建组件,使得代码更加容易得到复用,能够很好的应用在大项目的开发中。 - 单向响应的数据流 ? React 实现了单向响应的数据流,从而减少了重复代码,这也是它为什么比传统数据绑定更简单。
劣势
二、JSX
(一) 介绍
?全称: JavaScript XML,是一个看起来很像 XML 的 JavaScript 语法扩展。
?优点:
?作用:
? ? ?用来创建react虚拟DOM(元素)对象:
-
标签名任意: HTML标签或其它标签 -
标签属性任意: HTML标签属性或其它
? babel.js的作用:
-
浏览器的js引擎是不能直接解析JSX语法代码的, 需要babel转译为纯JS的代码才能运行 -
只要用了JSX,都要加上type="text/babel", 声明需要babel来处理
(二) JSX语法
? JavaScript + XML语法(HTML)
? 解读jsx语法:
? 1.遇到<>就会按照HTML语法解析为标签
- 如果是html语法标准的标签就会解析为同名元素 (注意nav article等h5新标签名)
- 如果是其他标签(组件)需要特别解析
- <App/> 这个代码的意义是 new App() ? <App num=200/> 相当于 new App(200)
? 2.遇到以{开头的代码会按照JavaScript语法解析
(三) 使用
可以将React JSX 代码可以放在一个独立文件上,并在不同的js文件中引入即可
App.js文件中:
//定义导出的标签
let App =(<div>
? ? ? ? ? ? <h1>标题</h1>
? ? ? ? ? ? <p>hahaha</p>
? ? ? ? ? ?</div>);
export default App; //将自定义的DOM元素暴露出来,供给其他的页面使用
index.js文件中:
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App.js';
?
ReactDOM.render(
App,
document.getElementById('root')
);
给根元素内部渲染组件模板的三种写法:?
//1. let App=<div>666</div>
//2.函数组件 function App(){
// let a="hello"
// return <div>666{a+"world"}</div>
// }
//3.导入对象组件: import App from "./App"
root.render(<App/>); //给根元素内部渲染组件模板
?
?(四) 样式
? ? ? App.css文件:
.adiv{
color: pink;
}
? ? ?App.jsx文件:
import React from "react";
import "./App.css" //全局css样式
class App extends React.Component {
constructor(arg) {
super(arg)
}
render() {
let box = "adiv"
let bdiv = {
color: "lightblue"
}
return (
<div>
<div className={box}>123</div>
<div style={bdiv}>456</div>
</div>
)
}
}
export default App;
?显示结果:
?(五) 注释
注释需要写在花括号中,需要注意的是:
-
在标签内部的注释需要花括号 -
在标签外的的注释不能使用花括号
ReactDOM.render(
? ?/*标签外部的注释 */
? ?<h1>我是标题 {/*标签内部的注释*/}</h1>,
? ?document.getElementById('root')
);
?(六)?JavaScript 表达式
- 我们可以在 JSX 中使用 JavaScript 表达式。表达式写在花括号 {} 中。
let a=20
function fn(){return "hello"}
ReactDOM.render(
? ?<div>
? ? ?<h1>{a+1}</h1>
? ? ?<h3>{fn()}</h3>
? ?</div>,
? ?document.getElementById('root')
);
?(七)条件渲染
? 1. 三目运算符
- 在 JSX 内部不能使用 if else 语句,可以使用 三元运算 表达式来替代。
import React from "react";
class App extends React.Component {
constructor(arg) {
super(arg)
}
render() {
let flag = false
let a = "hi"
let b = <h1>hi</h1>
return (
<div>
{/* 三目运算符代替if-else */}
<div>{flag ? a : b}</div>
</div>
)
}
}
export default App;
? ?显示结果:
??
?2. 可以在JSX外部使用if语句
var i = 10;
if(i > 0){ //使用if语句的好处是:如果条件不满足且不需要渲染组件时不需要写else
box = <div className="box"></div>;
}
ReactDOM.render(
? ?<div>
? ? { box }
? ?</div>,
? ?document.getElementById('root')
);
?(八)数组&列表渲染
- JSX 允许在模板中插入数组,数组会自动展开所有成员
- 通过数组的map函数
import React from "react";
class App extends React.Component {
constructor(arg) {
super(arg)
}
render() {
let arr = ["a", "b", "c", <div>这是div标签</div>, "换行了"]
let arr2 = ["hello", "react"]
let arr3 = [{ id: 1, name: "haha" }, { id: 2, name: "xixi" }]
return (
<div>
{/* 把元素从数组arr中取出来显示在div中 不需要for循环了 */}
<div>{arr}</div>
{/* 可以在渲染标签的时候写逻辑 */}
<div>{arr2.map(el => <h3>{el}</h3>)}</div>
<div>{arr3.map(el => <div><h2>{el.id}</h2><p>{el.name}</p></div>)}</div>
</div>
)
}
}
export default App;
? ?显示结果:
面试题:为什么render函数返回的模板用()括起来?
答:因为js语法中return换行的话 认为是没有返回值的
三、组件
React.js 中一切皆组件,用 React.js 写的其实就是 React.js 组件。
我们在编写 React.js 组件的时候,一般都需要继承 React.js 的 Component(类定义)。一个组件类必须要实现一个 render 方法,这个 render 方法必须要返回一个 JSX 元素。但这里要注意的是,必须要用一个外层的 JSX 元素把所有内容包裹起来。返回并列多个 JSX 元素是不合法的。
1、定义单个组件
(1)定义组件
import React, { Component } from 'react'
export class Box extends Component {
render() {
return (
<div>
<h2>类组件--Box</h2>
</div>
)
}
}
export default Box
//普通函数
export default function Box2() {
return (
<div>
<div>
<h2>静态组件Box2--纯粹的静态模板</h2>
函数组件,无状态组件 pure
</div>
</div>
)
}
//箭头函数
import React from 'react'
let Box3=()=>{return <div>Box3</div>}
export default Box3
(2) 使用组件
import React from "react";
import Box from "./Box";
import Box2 from './Box2'
import Box3 from './Box3'
class App extends React.Component {
constructor(arg) {
super(arg)
}
render() {
return (
<div>
<Box></Box>
<Box2></Box2>
<Box3></Box3>
</div>
)
}
}
export default App;
2、定义复合组件
可以通过创建多个组件来合成一个组件,即把组件的不同功能点进行分离--组件嵌套
import React from 'react';
import ReactDOM from 'react-dom';
?
class WebSite extends React.Component {
render() {
return (
? ? <div>
? ? ? <Name name={this.props.name} />
? ? ? <Link site={this.props.site} />
? ? </div>
? );
}
}
//局部组件Name
class Name extends React.Component {
render() {
return (
? ? <h1>{this.props.name}</h1>
? );
}
}
//局部组件Link
class Link extends React.Component {
render() {
return (
? ? <h1>{this.props.site}</h1>
? );
}
}
?
ReactDOM.render(
<WebSite name="百度一下,你就知道" site='http://www.baidu.com' />,
document.getElementById('root')
)
四、传值 React Props
React 的一大特点是单向数据流。
React 中的每一个组件,都包含有一个属性(props),属性主要是从父组件传递给子组件的,在组件内部,可以通过this.props获取属性对象。
1、定义和使用props 传值
? ?1)通过React类定义组件时:
ReactDOM.render(
<MyApp name="哈哈哈" />,
document.getElementById('root')
);
- 在自定义子组件中通过this.props.key 来获得组件属性的值,需要使用{}括起来。
// 类定义组件时,使用属性 this.props.属性名称
class MyApp extends React.Component {
render() {
return (<p>{this.props.name}</p>);
}
}
? 2)通过React函数定义 组件时:
ReactDOM.render(
<Myfun title="哈哈哈" />,
document.getElementById('root')
);
// 函数定义组件时,在组件内部使用属性值:props.属性名称
function Myfun(props) { // 函数需要传递一个参数props
? ?return(<h2>{props.title}</h2>)
}
?
2、默认Props
父组件:
//由于是用ES6 class语法创建组件,其内部只允许定义方法,而不能定义属性,class的属性只能定义在class之外。所以defaultProps要写在组件外部。
App.defaultProps = {
name: '哈哈哈'
};
ReactDOM.render(
<App />,
document.getElementById('root')
);
子组件:
class App extends React.Component {
render() {
return <p>my name is {this.props.name}</p>
}
}
3、多属性传值
(1)定义一个this.props对象,在对象中声明多个键值对,用于表示组件的属性
(2)在组件中使用{...this.props}的方式传递属性。“...”表示JSX的延展操作符,这种方式可以很方便的为组件指定多个属性,并且为属性的值指定数据类型。
父组件:
let p1 = {
name: '哈哈哈',
age: 21,
sex: '女'
};
?
ReactDOM.render(
? ?//<MyApp name='哈哈哈' age='21' sex='女' />
<MyApp {...p1}/>,
document.getElementById('root')
);
子组件:
class MyApp extends React.Component {
render() {
return(
<h1>{this.props.name} : {this.props.age} : {this.props.sex}</h1>
);
}
}
五、状态 React State
1、定义State
React在ES6的实现中,规定state在constructor中实现
正确定义State的方式如下:
(1)在constructor中实现state
(2)在constructor中通过bind绑定事件函数(事件函数是用来改变状态)
(3)在事件函数内部使用setState函数更改状态
(4)在组件中的render函数中使用该状态
(5)在组件上需要设置监听事件,去触发事件函数的执行
//定义组件
class LikeButton extends React.Component {
//constructor表示构造器,在constructor需要声明状态state,在声明state之前需要使用super(props);
constructor(props) {
super(props);//使用父类的属性
//声明状态
this.state = {
liked: false
}
//Currently, you are calling bind.
//But bind returns a bound function.
//You need to set the function to its bound value.
//目前,你正在调用绑定。但是绑定返回绑定函数。您需要将函数设置为其绑定值。
this.handleClick = this.handleClick.bind(this);
}
handleClick(event) {
this.setState({liked: !this.state.liked});
}
render() {
return (
<button onClick={this.handleClick}>
你<strong>{this.state.liked ? '喜欢' : '不喜欢'}</strong>我,点我切换
</button>
)
}
}
ReactDOM.render(
<LikeButton />,
document.getElementById('root')
)
2、setState设置状态
(1)语法:
? ? ? setState(object nextState[, function callback])
(2)说明:
-
setState是React事件处理函数中和回调函数中触发UI更新的主要方法。 -
不能在组件内部通过this.state修改状态,因为该状态会在调用setState()后被替换。 -
setState()不一定是同步的,为了性能提升,React会批量执行state和DOM渲染。 -
setState()总是会触发一次组件重绘,但可在shouldComponentUpdate()中实现一些条件渲染逻辑来解决。
class MyApp extends React.Component {
constructor(props) {
super(props);
this.state = {
clickCount: 0
};
?
this.handleClick = this.handleClick.bind(this);
}
?
handleClick(event) {
this.setState({clickCount: this.state.clickCount + 1});
}
?
render() {
return (
<h2 onClick={this.handleClick}>
点击后次数变更: {this.state.clickCount}
</h2>
);
}
}
?
ReactDOM.render(
<MyApp />,
document.getElementById('root')
);
同步设置多个状态时(不常用),可以在setState函数的第二个参数可以传递一个function回调函数,如下:
class MyApp extends React.Component {
constructor(props) {
super(props);
this.state = {
clickCount: 0,
isRed: false,
smallFont: true
};
?
this.handleClick = this.handleClick.bind(this);
}
?
handleClick(event) {
this.setState(
{clickCount: this.state.clickCount + 1},
function() {
this.setState(
{isRed: !this.state.isRed},
function() {
this.setState({smallFont: !this.state.smallFont});
}
);
}
);
console.log(this.state.isred);
}
?
render() {
var redStyle = {color: 'red', fontSize: 50};
var blueStyle = {color: 'blue', fontSize: 14};
return (
<h2 onClick={this.handleClick} style={this.state.isRed ? redStyle : blueStyle}>
点击后次数变更: {this.state.clickCount}
</h2>
);
}
}
?
ReactDOM.render(
<MyApp />,
document.getElementById('root')
);
状态上移:当存在多个组件共同依赖一个状态,或是当子组件的props 数据需要被修改时,将这个状态放到对应组件的父组件中:
? 子组件:
class Site extends React.Component {
render() {
return (
<div>
<button onClick={this.props.updateState}>点击改变</button>
<h2>{this.props.myData}</h2>
</div>
);
}
}
? 父组件:?
class Content extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 'hello'
};
this.handleChange = this.handleChange.bind(this);
}
?
handleChange(event) {
this.setState({value: '你好'});
}
?
render() {
return(
<div>
<Site myData={this.state.value} updateState={this.handleChange}/>
</div>
);
}
}
3、State 与 Props 区别
除了State, 组件的Props也是和组件的UI有关的。他们之间的主要区别是:
当子组件的属性值是可变值时,采用状态上移:
状态上移通过属性将父组件的状态传递到子组件,那么父组件的状态发生变化时,子组件的属性也会改变
4、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 Children from './Children'
export default class Parent extends Component {
? ?constructor(props) {
? ? ? ?super(props)
? ? ? ?this.state = {
? ? ? ? ? ?msg: '父组件传值给子组件',
? ? ? ? ? ?childrenMsg: ''
? ? ? }
? }
?
? ?getChildrenMsg = (result, msg) => {
? ? ? ?// console.log(result, msg)
? ? ? ?this.setState({
? ? ? ? ? ?childrenMsg: msg
? ? ? })
? }
?
? ?render() {
? ? ? ?return (
? ? ? ? ? ?<div>
? ? ? ? ? ? ? ?<h2>我是父组件 </h2>
? ? ? ? ? ? ? ?<h3>子组件传来的值为:{ this.state.childrenMsg }</h3>
? ? ? ? ? ? ? ?<hr/>
? ? ? ? ? ? ? ?<Children parent={ this } />
? ? ? ? ? ? ? {/*<Children parent={ this.getChildrenMsg.bind(this) } />*/}
? ? ? ? ? ?</div>
? ? ? )
? }
}
? Children:
export default class Children extends Component {
? ?constructor(props) {
? ? ? ?super(props)
? ? ? ?this.state = {
? ? ? ? ? ?msg: '子组件传值给父组件'
? ? ? }
? }
?
? ?toParent = () => {
? ? ? ?// console.log(this.props.parent.getChildrenMsg.bind(this, this.state.msg))
? ? ? ?this.props.parent.getChildrenMsg(this, this.state.msg)
? }
?
? ?render() {
? ? ? ?return (
? ? ? ? ? ?<div>
? ? ? ? ? ? ? ?<h2>{ 我是子组件 }</h2>
? ? ? ? ? ? ? ?<button onClick={ this.toParent }>子组件传入给父组件</button>
? ? ? ? ? ?</div>
? ? ? )
? }
}
|