react是一个将数据渲染成HTML的JavaScript框架。
JSX
- jsx是react定义的一种语法,用来简化创建虚拟DOM的语法,是React.createElement()方法的语法糖
- 语法:
- 可以像先HTML元素的写法直接书写结构,但不要用引号包裹,因为JSX不是字符串
- 书写多层结构时,可以使用‘()’包裹JSX语句,这样可以识别到是一个整体
- 在JSX中引用 JS 表达式,则使用‘{}’包裹,但 jsx 有很多 js 语句不支持,例如 if语句 for循环
- 在JSX的元素结构中想要添加 class 属性,要使用 className 来添加,在编译到真实DOM时会变成 class 属性
- 在JSX中元素结构中想要添加内联样式 style,只能通过 js 方式传入一个对象,对象的属性名使用小驼峰命名规则,属性值是字符串类型。
- 只能存在一个根标签:可以使用 <></> 这种空标签做根标签
- 标签需要闭合
- 在jsx中定义的小写开头的标签名,会将标签转换为HTML中同名的元素,如果HTML中不存在该标签对应的元素,则会报错
- 在jsx中定义的大写开头的标签名,react就会渲染对应的组件,如果组件未定义就报错
const myHi = 'Hello React'
const id = 'test'
const VDOM = (
<div className="title" > // 添加calss属性,要使用className添加
<span>{myHi}</span> // js 语句使用{}包裹
<span id={id} style={{color: 'f6c', fontSize: '25px'}}></span> // 内联样式 style,只能通过 js 方式传入一个对象
</div>
)
JSX 防止注入攻击
你可以安全地在 JSX 当中插入用户输入内容:
const title = response.potentiallyMaliciousInput;
// 直接使用是安全的:
const element = <h1>{title}</h1>;
React DOM 在渲染所有输入内容之前,默认会进行转义它可以确保在你的应用中,永远不会注入那些并非自己明确编写的内容。所有的内容在渲染之前都被转换成了字符串。这样可以有效地防止 XSS(cross-site-scripting, 跨站脚本)攻击。
函数式组件
- 定义一个函数,函数返回值为 JSX 语法的结果,且注意 函数名一定要以大驼峰命名
- 函数式组件中的this值为undefined
// 函数名也就是组件名
function MyCom() {
return
}
类式组件
- 创建一个类式组件一定要继承react定义的父类
- render 函数一定要属性,返回值为 JXS 语法
- 类式组件不需要自己去创建实例对象,在react使用时,react会帮我们 new 一个实例对象,再去调用 render方法,将虚拟DOM渲染到页面中的真实DOM
- 类式组件中的this值为组件实例对象
- 在JSX中绑定事件,事件名还是原生js的事件名,但要修改成小驼峰的命名规则
- 在JSX中绑定事件后,还需要绑定处理函数,绑定处理函数后不可以直接跟(),因为这样函数会被直接调用,而不是在事件触发时调用
- 而事件中调用处理函数时,处理函数的this指向并不是组件实例对象自己,这时候就需要使用箭头函数的方式将 this 的指向,指向组件实例对象自己
- 或者在 state 中定义一个变量储存自定义的函数,该函数通过 .bind(this) 传入组件实例对象
// 创建一个类式组件
calss MyComp extends React.Component {
// 构造函数 prpos 父组件传递的数据
// 构造函数一般不使用
constructor(prpos) { // 只会在初始化调用一次
super(prpos) // 继承
this.state = {} // 定义初始化状态,数据的储存和管理
}
// 想要创建 state 管理数据的状态,可以直接创建
state = {
myHi: 'hello react'
}
// 在类中可以直接自定义处理函数
// 这里用函数表达式的方式定义的函数,函数直接绑定在了组件的实例对象上
// 而使用箭头行数的好处是它没有自己的this,this是指向外部调用的对象,也就是组件实例对象自己
handlerClick = () => {
console.log('被点击了')
}
// render 返回虚拟DOM
// 触发 render 时机,初始化调用一次,每次更新调用一次
render() {
return (
<div className="title" >
<span onClick={this.handlerClick}>{this.state.myHi}</span>
</div>
)
}
}
组件的组成属性
state
- 值为对象
- 定义初始化状态,数据的储存和管理
- state 的属性的状态不可以直接修改。
- 修改 state 的属性的状态,要通过组件实例对象的方法:this.setState({state属性: 新值})
- 调用 setState 方法更新,是合并更新,就是state中的其他属性不变,只改变了修改的属性。
props
- 值是一个对象,props对象中的属性为使用组件时在组件上绑定的属性,属性值为绑定的属性传递的值
- 在 props 中还有一个特殊的属性为:children,可以获取到组件体(就是开始标签和结束标签直接)中的内容
- 所以组件体的内容也可以通过在使用组件时,在标签上通过 children 属性传递到组件内部
- 在组件中 render 函数中可以访问到 this.props
- 在组件内 props 的属性是只读的,不可修改
- propTypes:对传入的值进行类型限制,需要单独的插件 PropTypes
- 语法:propTypes = {属性: PropTypes.数据类型.isRequired}
- 数据类型:是js中数据的类型,但要写小写形式:string number 等,而限制为函数则是:func
- isRequired 是可选的,如果使用则该属性为必传
- defaultProps:传入一个对象,用于定义组件中使用的数据的默认值,如果没有传入值,则使用默认值,属性名为组件使用的变量
- 语法:defaultProps = {属性: '默认值'}
calss MyComp extends React.Component {
render() {
const {name, 属性2, ...} = this.props
return
}
// 对 props 的属性进行限制和默认值,通过 static 添加静态属性
static propTypes = {
name: PropTypes.string.isRequired
}
static defaultProps = {
name: 'jason'
}
}
<MyComp>组件体内容</MyComp>
// 第一种传递方式
ReactDOM.render(<MyComp name='张三' age={20} />, document.getElementById(''))
// 第二种传递方式:在react中有自己独特的解构对象的方式去传递,其他框架没有
const obj = {name: 'jason', age: 18}
ReactDOM.render(<MyComp {...obj} />, document.getElementById(''))
类组件中this的问题
你必须谨慎对待 JSX 回调函数中的 this,在 JavaScript 中,class 的方法默认不会绑定 this。如果你忘记绑定 this.handleClick 并把它传入了 onClick,当你调用这个函数的时候 this 的值为 undefined。
这并不是 React 特有的行为;这其实与 JavaScript 函数工作原理有关。通常情况下,如果你没有在方法后面添加 (),例如 onClick={this.handleClick},你应该为这个方法绑定 this。
如果觉得使用 bind 很麻烦,这里有两种方式可以解决。如果你正在使用实验性的 public class fields 语法,你可以使用 class fields 正确的绑定回调函数:(通常用以下这种方法)
class LoggingButton extends React.Component {
// 此语法确保 `handleClick` 内的 `this` 已被绑定。 // 注意: 这是 *实验性* 语法。
handleClick = () => { console.log('this is:', this); }
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
);
}
}
Create React App 默认启用此语法。
如果你没有使用 class fields 语法,你可以在回调中使用箭头函数。(这种方式不推荐)
class LoggingButton extends React.Component {
handleClick() {
console.log('this is:', this);
}
render() {
// 此语法确保 `handleClick` 内的 `this` 已被绑定。
return (
<button onClick={() => this.handleClick()}>
Click me
</button>
);
}
}
此语法问题在于每次渲染 LoggingButton 时都会创建不同的回调函数。在大多数情况下,这没什么问题,但如果该回调函数作为 prop 传入子组件时,这些组件可能会进行额外的重新渲染。我们通常建议在构造器中绑定或使用 class fields 语法来避免这类性能问题。
refs
字符串形式的ref
- 获取多个绑定 ref 属性的 真实DOM 元素对象
- 可以通过组件实例对象的 this.refs.'绑定ref时的赋值' 访问到每一个绑定 ref 的元素
回调函数形式的ref
- 语法:ref={currentNode => this.自定义属性 = currentNode }
- 然后在组件中就可以直接访问属性,获取到真实DOM
createRef() 创建
- 语法:定义一个容器 myref = react.createRef(),在节点中给ref绑定容器
- 访问绑定的ref真实DOM:this.自定义容器.current
calss MyComp extends React.Component {
myref = react.createRef() // 每一个容器只能绑定一个节点
myref2 = react.createRef()
myClick = () => {
this.myref.current
}
render() {
return (
// 字符串形式
<div ref='dom1'></div>
// 回调函数形式
<div ref={currentNode => this.div1 = currentNode}></div>
// createRef() 创建
<div ref={this.myref} onClick={this.myClick}></div>
)
}
}
setState 修改 state 状态
- setState(stateChange, [callback])------对象式的setState
- stateChange为状态改变对象(该对象可以体现出状态的更改)
- callback是可选的回调函数, 它在状态更新完毕、界面也更新后(render调用后)才被调用
- setState(updater, [callback])------函数式的setState
- updater为返回stateChange对象的函数。
- updater可以接收到state和props。
- callback是可选的回调函数, 它在状态更新、界面也更新后(render调用后)才被调用。
- 总结
- 对象式的setState是函数式的setState的简写方式(语法糖)
- 使用原则:
- 如果新状态不依赖于原状态 ===> 使用对象方式
- 如果新状态依赖于原状态 ===> 使用函数方式
- 如果需要在setState()执行后获取最新的状态数据, 要在第二个callback函数中读取
获取 DOm 的方式
- 可以通过事件绑定的函数 event.target 访问 真实DOM元素
- 通过元素绑定 ref 属性,获取真实DOM
JSX中事件绑定函数的注意事项
- 第一种绑定方式:函数绑定不需要传递参数时,可以直接绑定,但函数后面不能跟随()
onClick={this.myFun}// 这种形式会在事件触发时调用函数,函数后面如果加了(),则组件初始化时,函数就调用了- 在上述自定义的函数中,可以通过设置形参,来获取当前绑定事件的 DOM 元素:
myFun=(e)=>{e.target},e.target 就是当前绑定事件的DOM元素
- 第二种绑定方式(函数柯里化):函数绑定需要传递参数时,需要在函数中再返回一个函数,这样每次触发事件就会执行返回的函数
- 语法:
- 第三种方式:绑定一个箭头函数,函数体里面再调用自定义函数
calss MyComp extends React.Component {
// 第二种方式:函数中返回函数
myClick = (str) => { // str的值有调用函数传递的实参决定
return (event) => {
console.log(event.target) // 这里就可以获取到 DOM 元素
}
}
// 第三种方式绑定
myClick2 = (str, event) => {
}
render() {
return (
<div onClick={this.myClick('我被点击了')}></div>
// 第三种方式
<div onClick={event => this.myClick2('我被点击了', event)}></div>
)
}
}
calss 组件中常用生命周期
- componentDidMount() 挂载完成执行
- componentDidMount() 方法会在组件已经被渲染到 DOM 中后运行,所以,最好在这里设置计时器
- render() 初始化执行,数据更新时执行
- componentWillUnmount() 将要卸载前执行
- 调用 this.setState() 和父组件的render 会触发组件更新阶段
兄弟组件传值-消息订阅与发布
- 需要安装 'pubsub-js' 包
- 在要使用的文件中引入 import Pubsub from 'pubsub-js'
- 通过在组件中发布消息:Pubsub.publish('自定义名称', data)
- 在组件中订阅消息:const sub = Pubsub.subscribe('发布消息的自定义名称', (msg, data) => {})// data 为发布消息时传递过来的数据。
- 而在订阅的组件中,在卸载前的生命周期中去清除订阅。减少内存使用
render props
- 如何向组件内部动态传入带内容的结构(标签)
// 在 A 组件传入一个 C 组件,使用 render 属性
<A render={(data) => <C data={data}></C>}></A>
// 在A组中就可以通过 {this.props.render(内部state数据)} 渲染出 C 组件
// A组件: {this.props.render(内部state数据)}
// C组件: 读取A组件传入的数据显示 {this.props.data}