React 是Facebook 开发并开源的前端框架
当时他们的团队在市面上没找到合适的MVC 框架,就自己写一个 JS 框架,用来架设 instagram(图片分享社交网路),2013年开源
React 解决的是前端MVC 框架中的view 视图层的问题。
DOM(文档对象模型Document Object Model)
将网页内所有内容映射到一颗树形结构的层级对象模型上,浏览器提供对DOM的支持,用户可以是用脚本调用DOM API 来动态的修改DOM 结点,从而达到修改网页的目的,这种修改是浏览器中完成的,浏览器会根据DOM 的改变重绘 改变DOM 结点部分。
修改DOM 重新渲染代价太高,前端框架为了提高效率,尽量减少DOM 的重绘,提出了Virtual DOM,所有的修改都是在现在的Cirtual DOM 上完成的,通过比较算法,找出浏览器DOM 之间的差异,使用这个差操作DOM,浏览器只需要渲染这部分变化就行了
React 实现了DOM Diff 算法,可以高效的对比Virtual DOM 和DOM 之间的差异。
jsx 是 一种JavaScript 和XML 混写的语法,是JavaScript的扩展
XML 被设计为传输和存储数据,其焦点是数据的内容。 HTML 被设计用来显示数据,其焦点是数据的外观
修改 根目录下的 index.html:在html文件中,提供一个div标签,同时提供id ,使得react可以通过id找到
启动 npm start 后
程序解释:
import React from "react"; 导入 react 模块
import ReactDom from "react-dom";导入react 的DOM 模块
class Root extends React.Component :组件类定义,从React.Component 类上继承,这个类生成JSXElement对象即React元素。
render()渲染函数,返回组件中渲染的内容,注意,只能返回唯一一个顶级元素回去
ReactDom.render(<Root/>, document.getElementById("root")):第一个参数是JSXElement对象,第二个是DOM的Element元素,将React元素添加到DOM的Element 元素中并渲染。 也可以使用name,如果Element元素的属性定义了name,document.getElementsByName("newroot")
(不推荐使用)还可以使用React.createElement创建react元素,第一参数是react组件或者一个HTML的标签明后才能(如:div,span)
增加一个子元素: (这就是SPA网页,单页应用,普通的爬虫就只能爬基本页面了,因为此时,css和js分割开了) 图二是 DOM数,虚拟DOM 是react的事
注意:
JSX 规范:
每一个 React组件 都有一个状态属性 state,它是一个JavaScript对象,可以为他定义属性来保存值
如果状态变化了,会触发UI 的重新渲染,使用setState()方法可以修改stste值
注意:state是每个组件自己内部使用的,是组件自己的属性
依然修改/src/index.js
解决方式1:
可以使用延时函数,setTimeout(()=> this.setState({ p1: ' jerry' }), 3000) 是一个异步函数
但是 不要这样使用,把setState放在别的地方
setInterval() 每几秒,执行一次,而 setTimeout 多少秒后,执行一次
复杂状态的例子
浏览器结果:
========》点击后
div的id 是t1 ,鼠标按下事件捆绑了一个函数,只要鼠标按下就出触发调用 getEventTrigger 函数,浏览器会送给他一个参数 event, event是事件对象,当事件触发时,event 包含触发这个时间的对象
HTML DOM的JavaScript 事件
使用React 实现上面的传统的HTML
分析:
Toggle类
它有自己的state属性
当render完成后,网页上有一个div标签,div标签对象捆绑了一个click 时间的处理函数,div标签内有文本内容
如果通过点击左键,即触发了一个click方法关联的handleClick 函数,在这个函数里将状态改变
状态值state,的改变 将引发render重绘
如果组件自己的state变了,只会触发自己的render方法重绘。
注意:
{this.handleClick.bind(this)} 不能外加括号
this.handleClick.bind(this) 一定要绑定this,否则当触发捆绑的函数时,this是函数执行的上下文决定的,this已经不是触发事件的对象了。
console.log(event.target.id) 取回的产生的对象的id,但是这不是我们封装的组件对象,所以
console.log(event.target ===this) 是false,所以这里一定要使用this,而这个this是通过绑定来的
event.target 就是生成的一个块 <div>-----</div>
React中的事件:
使用小驼峰
使用JSX表达式,表达式中指定事件处理函数
不能使用return false 如果要组织事件默认行为,使用event。preventDefault()
props 就是组件的属性properties。
把React组件当作标签使用,可以为其增加属性,如下:
<Toggle name="school" parent={this} />
为上面的Toggle 元素增加属性:
尝试修改props 的属性值,会抛出 TypeError:cannot assign to read only property “name” of object # <Object>异常
应该说, state是私有 private 的属于组件自己的属性,组件外无法直接访问,可以修改state但是建议使用setState方法
props是公有public属性组件外也可以访问,但是只读。
使用ES 6 的构造器,要提供一个参数props, 并把这个参数使用super传递给父类
组件的生命周期可分为三个状态
组件的生命周期状态,说明在不同时机访问 组件,组件正在处于生命周期的不同转台上
在不同的生命周期状态访问,就产生不同的方法。
有图可知:
constructor 构造器是最早执行的函数
触发更新生命周期函数,需要更新 state 或 props
因此,重写编写 /scr/index.js
构造两个组件,在子组件SUb中,加入所有生命周期函数
测试:装载,卸载组件的生命周期函数。
1 import React from 'react';
2 import ReactDom from 'react-dom';
3
4
5 class Sub extends React.Component {
6 constructor (props){
7 console.log("sub constructor")
8 super(props);
9 this.state = {count:0};
10 }
11 handleClick(event) {
12 this.setState({count:this.state.count + 1});
13 }
14 render() {
15 console.log('sub render');
16 return (
17 <div id="sub" onClick={this.handleClick.bind(this)}>
18 Sub's count = {this.state.count}
19 </div>
20 );
21 }
22
23 componentWillMount() {
24 console.log('sub componentwillmont')
25 }
26
27 componentDidMount() {
28 console.log('sub componentdidmount')
29 }
30 componentWillUnmount(){
31 console.log('sub componentdidunmount')
32 }
33
34
35 }
36
37
38 class Root extends React.Component{
39 constructor (props) {
40 console.log("root constructor")
41 super(props);
42 this.state = {};
43
44 }
45 render (){
46 return(
47 <div>
48 <Sub />
49 </div>
50 )
51 }
52 }
53
54 ReactDom.render(<Root/>, document.getElementById("newroot"));
结果:
增加,更新组件函数:
演示 props的改变,为Root增加一个click事件处理函数
1 import React from 'react';
2 import ReactDom from 'react-dom';
3 import { runInAction } from 'mobx';
4
5
6 class Sub extends React.Component {
7 constructor (props){
8 console.log("sub constructor")
9 super(props);
10 this.state = {count:0};
11 }
12 handleClick(event) {
13 this.setState({count:this.state.count + 1});
14 }
15 render() {
16 console.log('sub render');
17 return (
18 <div style={{height:200 + "px", color:'red', backgroundColor:"#aaffff"}}>
19 <a id='sub' onClick={this.handleClick.bind(this)}>
20 sub'x count = {this.state.cont}
21 </a>
22 </div>
23 );
24 }
25
26 componentWillMount() {
27 //constructor 之后,第一次render之前
28 console.log('sub componentwillmont')
29 }
30
31 componentDidMount() {
32 // 第一次render之前
33 console.log('sub componentdidmount')
34 }
35 componentWillUnmount(){
36 //清理工作
37 console.log('sub componentdidunmount')
38 }
39
40 componentWillReceiveProps(nexProps) {
41 // props更新时,街道新的props,交给shouldComponentUpdate
42 // props组件内只读,只能从外部改变
43 console.log(this.props)
44 console.log(nexProps)
45 console.log('sub com---receiveProps', this.state.count)
46 }
47
48 shouldComponentUpdate(nexProps, nextState) {
49 // 是否组件更新,props 或state 方式改变,返回布尔值,true才会更新
50 console.log('sub shuold--------', this.state.count, nextState)
51 return true
52
53 }
54
55 componentWillUpdate(nexProps, nextState) {
56 //同意更新后,真正更新前,之后调用rener
57 console.log('will update', this.state.count, nextState)
58 }
59
60 componentDidUpdate(prevProps, prevState){
61 //同意更新后,真正更新后,在render在之后调用
62 console.log('did Update', this.state.count, prevState)
63 }
64
65 }
66
67
68 class Root extends React.Component{
69 constructor (props) {
70 console.log("root constructor")
71 super(props);
72 this.state = {flag:true, name:'root'};
73
74 }
75 handleClick(event){
76 this.setState({
77 flag:!this.state.flag,
78 name:this.state.flag ? this.state.name.toLowerCase() : this.state.name.toUpperCase()
79 })
80 }
81 render (){
82 return(
83 <div id='t1' onClick={this.handleClick.bind(this)}>
84 my name is {this.state.name}
85 <Sub /> {/* 父组件的render,会引起下一级组件的更新流程,导致props重新发送,即使子组件props没有改变*/}
86 </div>
87 )
88 }
89 }
90
91 ReactDom.render(<Root/>, document.getElementById("newroot"));
componentWillMount 第一次装载,在首次render之前,例如控制state,props
conpinentDidMount 第一次装载结束,在首次render之后,
注:++ 原位置自动加1 ,+= 是调到栈里,加1 再返回
React从15.0 开始支持无状态组件,定义如下:
无状态组件,也叫函数式组件
开发中,很多情况下,组件其实很简单,不需要state状态,也不需要使用生命周期函数,无状态组件很好的满足了需要
无状态组件函数应该提供一个参数props,返回一个React元素
无状态组件函数本身就是 render函数
改写上面的代码:箭头函数
如果要在上例的Root组件进行增强怎么办,例如将Root 组件的div 外部在加入其它的 div
简化Wrapper
1 import React from 'react';
2 import ReactDom from 'react-dom';
3 // let Wrapper = function (Component/*传入组件*/, props) {
4 // return (
5 // <div>
6 // {props.schoolName}
7 // <hr />
8 // <Component />
9 // </div>
10 // )
11 // }
12 // 柯里化
13 let Wrapper = function (Component) {
14 function _wrapper(props) {
15 return (
16 <div>
17 {props.schoolName}
18 <hr />
19 <Component />
20 </div>)
21 }
22 return _wrapper
23 }
24 // 第一次简化
25 let Wrapper = function(Component) {
26 return function _wrapper(props){
27 return (
28 <div>
29 {props.schoolName}
30 <hr />
31 <Component />
32 </div>)
33 }
34 }
35 //第二次简化
36 let Wrapper = function(Component) {
37 return (props) => {
38 return (
39 <div>
40 {props.schoolName}
41 <hr />
42 <Component />
43 </div>)
44 }
45 }
46 // 第三次简化
47 let Wrapper = (Component) =>(props) =>
48 (
49 <div>
50 {props.schoolName}
51 <hr />
52 <Component />
53 </div>
54 )
55
56 //整理
57 let Wrapper = (Component) =>(props) => <div> {props.schoolName} <hr /> <Component /> </div>
58
71 let Root = (props) => <div>Root</div>
72
73 // 返回新组件
74 let NewComp = Wrapper(Root)
75 ReactDom.render(<NewComp schoolName="jerry"/>, document.getElementById('newroot'))
测试:加入子组件(无状态组件)
新版的ES2016中增加了装饰器的支持,因此可以使用装饰器来改造上面的代码。
装饰器是装饰函数,类,不能对一个变量装饰(这样是不对的)
ES 2016 的装饰器只能装饰类,所以,将Root改写成类
让Root 也显示schoolName
想给装饰器函数增加一个id 参数
1 import React from 'react';
2 import ReactDom from 'react-dom';
3
4 let Wrapper = (id, Component) =>(props) =>
5 (
6 <div id={id}>
7 {props.schoolName}
8 <hr />
9 <Component />
10 </div>
11 )
12
13 // 柯里化
14 let Wrapper = id => Component => (props) =>
15 (
16 <div id={id}>
17 {props.schoolName}
18 <hr />
19 <Component {...props}/>
20 </div>
21 )
22
23
24 @Wrapper('123') // Root = wrapper("123")(Root) = (props) => .....
25
26 class Root extends React.Component {
27 render(){
28 return <div>Root ---- {this.props.address}</div>
29 }
30 }
31
32 ReactDom.render(<Root schoolName="jerry" address="somewhere"/>, document.getElementById('newroot'))