React的基本认识
- Facebook开源的一个js库
- 一个用于构建用户界面的js库
- React的核心思想: 通过构建可复用组件来构建用户界面
- React的特点
- Declarative(声明式编码)
- Component-Based(组件化编码)
- Learn Once, Write Anywhere(支持客户端与服务器渲染)
- 高效
- 单向数据流
- React高效的原因
- 虚拟(virtual)DOM, 不总是直接操作DOM
- 高效的DOM Diff算法, 最小化页面重绘
- ReactJS官网
- Github地址
使用React
- 相关js库
- react.js: React的核心库
- react-dom.js: 提供操作DOM的扩展库
- babel.min.js: 解析JSX语法代码转为纯JS语法代码的库
在页面中导入js
123<script type="text/javascript" src="../js/react.js"></script><script type="text/javascript" src="../js/react-dom.js"></script><script type="text/javascript" src="../js/babel.min.js"></script>编码
1234<div id="example"></div><script type="text/babel"> //必须用babelReactDOM.render(<h1>Hello, React!</h1>, document.getElementById('example'));</script>
认识JSX
虚拟DOM
12// 这就是一个简单的虚拟DOM对象var element = React.createElement('h1', {id:'myTitle'}, 'hello');- React提供了一些API来创建一种 特别 的一般js对象
- 虚拟DOM对象最终都会被React转换为真实的DOM
- 我们编码时基本只需要操作react的虚拟DOM相关数据, react会转换为真实DOM变化而更新界面
- JSX
- 全称: JavaScript XML
- react定义的一种类似于XML的JS扩展语法: XML+JS
- 作用: 用来创建react虚拟DOM(元素)对象
- 注意1: 它不是字符串, 也不是HTML/XML标签
- 注意2: 它最终产生的就是一个JS对象
- 标签名任意: HTML标签或其它标签
- 标签属性任意: HTML标签属性或其它
- 基本语法规则
- 遇到 < 开头的代码, 以标签的语法解析: html同名标签转换为html同名元素, 其它标签需要特别解析
- 遇到以 { 开头的代码,以JS的语法解析: 标签中的js代码必须用{}包含
- babel.js的作用
- 浏览器的js引擎是不能直接解析JSX语法代码的, 需要babel转译为纯JS的代码才能运行
- 只要用了JSX,都要加上type=”text/babel”, 声明需要babel来处理
- 渲染虚拟DOM(元素)
- 语法: ReactDOM.render(virtualDOM, containerDOM) :
- 作用: 将虚拟DOM元素渲染到真实容器DOM中显示
- 参数说明
- 参数一: 纯js或jsx创建的虚拟dom对象
- 参数二: 用来包含虚拟DOM元素的真实dom元素对象(一般是一个div)
创建虚拟DOM的2种方式:
纯JS(一般不用):
1React.createElement('h1', {id:'myTitle'}, title)JSX:
1<h1 id='myTitle'>{title}</h1>
例:动态展示列表数据
1234567const names = ['Tom2', 'Jack2', 'Bob2'];ReactDOM.render(<ul>{ names.map((name, index) => <li key={index}>{name}</li>) }</ul>,document.getElementById('xxx'))JSX语法注意:
- 标签必须有结束
- 标签的class属性必须改为className属性
- 标签的style属性值必须为: 双大括号
Component基础
自定义组件(Component) :
- React的核心语法和概念
- 作用类似于angular中自定义标签指令
组件的2个基本概念
- 组件类(工厂函数/构造函数/类): MyCompoent
- 组件标签:
-定义组件类12345678910111213141516//方式1: 工厂(无状态)函数(最简洁, 推荐使用)function MyComponent1() {return <h1>自定义组件标题11111</h1>;}//方式2: ES6类语法(复杂组件, 推荐使用)class MyComponent3 extends React.Component {render () {return <h1>自定义组件标题33333</h1>;}}//方式3: ES5老语法(不推荐使用了)var MyComponent2 = React.createClass({render () {return <h1>自定义组件标题22222</h1>;}});
渲染组件标签
1ReactDOM.render(<MyComponent/>, document.getElementById('example'));
注意:
- 返回的组件类必须首字母大写
- 虚拟DOM元素必须只有一个根元素
- 虚拟DOM元素必须有结束标签
- ReactDOM.render()渲染组件标签的基本流程
- React内部会创建组件实例对象
- 得到包含的虚拟DOM并解析为真实DOM
- 插入到指定的页面元素内部
组件3大属性之一: props属性
- 每个组件对象都会有props(properties的简写)属性
- 组件标签的所有属性都保存在props中
- 内部读取某个属性值: this.props.propertyName
- 作用: 通过标签属性从组件外向组件内传递数据(只读)
对props中的属性值进行类型限制和必要性限制
1234Person.propTypes = {name: React.PropTypes.string.isRequired,age: React.PropTypes.number.isRequired}扩展属性: 将对象的所有属性通过props传递
1<Person {...person}/>默认属性值
123Person.defaultProps = {name: 'Mary'};组件类的构造函数
1234constructor (props) {super(props);console.log(props); // 查看所有属性}
组件的3大属性之二: refs属性
refs属性
- 组件内的标签都可以定义ref属性来标识自己
- 在组件中可以通过this.refs.refName来得到对应的真实DOM对象
- 作用: 用于操作指定的ref属性的dom元素对象(表单标签居多) 12<input ref='username'>this.refs.username //返回input对象
事件处理
- 通过onXxx属性指定组件的事件处理函数(注意大小写)
- React使用的是自定义(合成)事件, 而不是使用的DOM事件
- React中的事件是通过委托方式处理的(委托给组件最外层的元素)
- 通过event.target得到发生事件的DOM元素对象 1234<input onFocus={this.handleClick}/>handleFocus(event) {event.target //返回input对象}
- 通过onXxx属性指定组件的事件处理函数(注意大小写)
强烈注意
- 组件内置的方法中的this为组件对象
- 在组件中自定义的方法中的this为null
- 强制绑定this
- 箭头函数(ES6模块化编码时才能使用)
组件3大属性之: state属性
- 组件被称为”状态机”, 通过更新组件的状态值来更新对应的页面显示(重新渲染)
初始化状态:
1234567constructor (props) {super(props);this.state = {stateProp1 : value1,stateProp2 : value2};}读取某个状态值
1this.state.statePropertyName更新状态—->组件界面更新
1234this.setState({stateProp1 : value1,stateProp2 : value2})
实现一个双向绑定的组件
- React是单向数据流
- 需要通过onChange监听手动实现 12345678910111213141516171819202122class App extends React.Component {constructor() {super()this.state = {value: ''}}handleChange(e) {const value = e.target.value;this.setState({ value })}render() {const { value } = this.state.value;return (<div><input type="text" value={value} onChange={this.handleChange.bind(this)}/><p>{this.state.value}</p></div>)}}ReactDOM.render(<App/>, document.getElementById('example'));
组件的生命周期
- 组件的三个生命周期状态:
- Mount:插入真实 DOM
- Update:被重新渲染
- Unmount:被移出真实 DOM
- React 为每个状态都提供了两种勾子(hook)函数,will 函数在进入状态之前调用,did 函数在进入状态之后调用
- componentWillMount()
- componentDidMount() : 已插入真实DOM, 在render之后才会执行
- componentWillUpdate(object nextProps, object nextState)
- componentDidUpdate(object prevProps, object prevState)
- componentWillUnmount()
- 生命周期流程:
- 第一次初始化渲染显示: render()
- constructor(): 创建对象初始化state
- componentWillMount() : 将要插入回调
- render() : 用于插入虚拟DOM回调
- componentDidMount() : 已经插入回调
- 每次更新props
- componentWillReceiveProps() : 将要接收父组件传来的props
- shouldComponentUpdate() : 子组件是不是应该更新 返回true更新,返回false不更新
- componentWillUpdate() : 将要更新回调
- render() : 更新(重新渲染)
- componentDidUpdate() : 已经更新回调
- 每次更新state: this.setSate()
- componentWillUpdate() : 将要更新回调
- render() : 更新(重新渲染)
- componentDidUpdate() : 已经更新回调
- 删除组件
- ReactDOM.unmountComponentAtNode(document.getElementById(‘example’)) : 移除组件
- componentWillUnmount() : 组件将要被移除回调
- 第一次初始化渲染显示: render()
- 注意:
- 一般会在componentDidMount()中: 开启监听, 发送ajax请求
- 可以在componentWillUnmount()做一些收尾工作: 停止监听
- 生命周期例子: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899import React from 'react';import ReactDOM from 'react-dom';class Component extends React.Component{// 构造函数constructor(props){super(props)this.state = {data: 'Old State'}console.log('初始化数据','constructor');}// 组件将要加载componentWillMount(){console.log('componentWillMount');}// 组件加载完成componentDidMount(){console.log('componentDidMount');}// 将要接收父组件传来的propscomponentWillReceiveProps(){console.log('componentWillReceiveProps');}// 子组件是不是应该更新shouldComponentUpdate(){console.log('shouldComponentUpdate');return true;}// 组件将要更新componentWillUpdate(){console.log('componentWillUpdate');}// 组件更新完成componentDidUpdate(){console.log('componentDidUpdate');}// 组件即将销毁componentWillUnmount(){console.log('componentWillUnmount');}// 处理点击事件handleClick(){console.log('更新数据:');this.setState({data: 'New State'});}// 渲染render(){console.log('render')return (<div><div>Props: {this.props.data}</div><button onClick={()=>{this.handleClick()}}>更新组件</button></div>);}}class App extends React.Component{// 构造函数constructor(props){super(props)this.state = {data: 'Old Props',hasChild: true}console.log('初始化数据','constructor');}onPropsChange(){console.log('更新props:');this.setState({data: 'New Props'});}onDestoryChild(){console.log('干掉子组件:');this.setState({hasChild: false});}render(){return (<div>{this.state.hasChild ? <Component data={this.state.data}/> : null}<button onClick={()=>{this.onPropsChange()}}>改变Props</button><button onClick={()=>{this.onDestoryChild()}}>干掉子组件</button></div>);}}ReactDOM.render(<App/>,document.getElementById('app'));
应用编码:
- 拆分组件
- 数据结构的设计
- props设计
- state设计
- 自定义事件处理(消息发布/订阅): pubsub.js
- PubSub.subscribe(“事件名”, function(eventName, data){})
- PubSub.publish(“事件名”, data) : 发布事件
组件总结
- 组件的组成
- html
- css
- js
- 图片
- 为什么要整组件化开发
- 应用开发越来越复杂(结构/行为)–> 简化开发—>拆分界面—>多个组件
- 多个界面有相同的功能(可能有一些变化部分)–>提高复用
–>抽取成一个组件(使用props技术解决变化部分)–>各个界面只使用组件标签就可以轻松实现效果