React技术栈系列—基础01
Posted tdd-qdkfgcs
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了React技术栈系列—基础01相关的知识,希望对你有一定的参考价值。
React简介
于2013年来自Facebook开源项目。
和Angular不一样的是,React并不是一个完整的MVC/MVVM框架,它只专注于提供清晰、直接的View视图层解决方案。它的功能全部以构建组件视图为核心,并提供类似控制器的函数接口和生命周期函数。所以在React中么有控制器、没有服务、没有指令、没有过滤器等等这些。
Virtual DOM
是React中的一个非常重要的概念,在日常开发中,前端需要将后端的数据呈现到界面中,同事还要能对用户的操作提供反馈,并且作用到UI上,这些操作都离不开操作DOM,但是频繁的DOM操作会造成极大的资源浪费和引起性能瓶颈。于是React引入了Virtural DOM。核心就是在内存上构建虚拟DOM,然后计算改变前后的DOM区别,最后用最少的DOM操作对DOM进行操作再用虚拟DOM替换真是DOM。
DIFF算法
Virtual DOM技术使用了DIFF算法,DIFF算法是一个比较计算层次结构区别的算法,主要是用来计算DOM之间的差异。(说实话,我并不懂DIFF算法,只是知道Virtual DOM使用DIFF算法实现的。)也许上图会更直观一点:
JSX语法糖
React中使用JSX语法糖,JSX = javascript + XML。可以让开发者在JS文件中写html模板,代码语境不需要来回切换。比如:
jsx不能直接运行,是需要babel-loader中的react这个preset编译。此处不赘述,在React技术栈从0搭建中介绍。
需要注意的是:
import React, Component from 'react';
import connect from "react-redux";
import getUserDataAction from "../../Store/actionCreators";
import md5 from "md5";
const P_KEY = 'dmx.top';
class Login extends Component
constructor(props)
super(props);
this.state =
"user_name": "",
"user_pwd": ""
render()
return (
<div>
<div className="login">
<div className="login-wrap">
<div className="avatar">
<img src="./uploads/logo.jpg" className="img-circle" alt="" />
</div>
<div className="col-md-offset-1 col-md-10">
<div className="input-group input-group-lg">
<span className="input-group-addon">
<i className="fa fa-id-card-o"></i>
</span>
<input
name="user_name"
type="text"
className="form-control"
placeholder="请输入用户名"
onChange=(e) => this._onInputChange(e)
onKeyUp=(e) => this._onInputKey(e)
/>
</div>
<div className="input-group input-group-lg">
<span className="input-group-addon">
<i className="fa fa-key"></i>
</span>
<input
name="user_pwd"
type="password"
className="form-control"
placeholder="请输入密码"
onChange=(e) => this._onInputChange(e)
onKeyUp=(e) => this._onInputKey(e)
/>
</div>
<button
type="submit"
className="btn btn-lg btn-danger btn-block"
onClick=(e) => this._onSubmit(e)
>登 录
</button>
</div>
</div>
</div>
</div>
);
//输入框改变
1) 必须被一个单独的大标签包裹,比如div。
2)单标签必须封闭,比如img , input 等
3)class要写成className, for要写成htmlFor
4)HTML注释不能使用,只能使用js的注释方法
5)原生标签比如p,li,div如果需要自定义属性,必须加data-前缀。自定义组件不需要。
6)js表达式用胆大括号包裹,在jsx中不能使用if else语句,但是可以使用conditinal(三元运算符)来替代。
7)可以运行函数:比如
//输入框改变
_onInputChange (e)
let inputValue = e.target.value,
inputName = e.target.name;
this.setState([inputName] : inputValue);
8)如果要在React组件中写行内样式,需要还用双大括号包裹。比如:
<img src=icon_url.includes(undefined) ? icon : icon_url style=border: "1px solid #e0e0e0" />
9)可以使用数组,如果是jsx语法糖数组会被自动展开,但是,React会提示需要给展开的每一项加一个独特的key值,因为底层DIFF比对是根据key值来比对的。比如:
let arr = [1,2,3,4,5,6,7,8,9,10];
return (
<div>
<ul>
arr
</ul>
</div>
);
React中数据的传递
React中数据传递三兄弟:state/props/context
三基友之一:state
React把组件看成一个状态机(State Machines)。通过与用户交互,实现不同状态,然后渲染UI,让用户界面和数据保持一致。
在React中,只有更新三兄弟中的任何一个,才会引发Virtual DOM的改变,才能重新渲染用户界面。
import React from "react";
class App extends React.Component
constructor()
super();
this.state =
a : 100,
b : 200,
c : 300
add()
this.setState(a : this.state.a + 1);
render()
return (
<div>
<h1>我是APP组件</h1>
<p>我有状态state</p>
<p>a : this.state.a</p>
<p>b : this.state.b</p>
<p>c : this.state.c</p>
<p>
<input type="button" value="按我" onClick=(this.add).bind(this)/>
</p>
</div>
)
//向外暴露
export default App;
所以,
定义state: 在构造函数(即constructor)中使用this.state属性即可。
使用state: 在JSX中this.state.a
改变state: this.setState(a: this.state.a + 1); 不能写成a: this.state.a++,因为state的属性只读,必须通过setState来改变,才能确保state在页面中安全。
state的是内部的,也被称为local state, 只有组件自己才能改变自己的state,别人想改变自己的state都不可能。
三基友之二:props
就是定义在自定义组件标签上面的值,就是props。当props改变的时候,会引发Virtual DOM的改变,从而引发视图的重绘。react崇尚数据的单向流动,所以设计的时候就是让数据从父组件流向子组件。props在子组件中是只读的,不能修改的。
如果父组件想和子组件通信,就需要使用自身属性往下传递需要通信的数据。
父组件:
import React from "react";
import MyCompo from "./MyCompo.js";
class App extends React.Component
constructor()
super();
render()
return (
<div>
<MyCompo a="66" b="77" c="88"></MyCompo>
</div>
)
//向外暴露
export default App;
子组件:
import React from "react";
class MyCompo extends React.Component
constructor()
super();
render()
return (
<div>
我是MyCompo组件
/*子组件中就可以使用this.props来枚举父组件传下来的值*/
<p>this.props.a</p>
<p>this.props.b</p>
<p>this.props.c</p>
</div>
);
//向外暴露
export default MyCompo;
如果是要在构造函数中使用父组件传下来的props,此时React内部会将props作为构造函数的第一个参数传进来
class MyCompo extends React.Component
constructor(props)
super();
this.state =
//接收系统注入的父组件的属性值为自己的local状态
c : props.c
注意:props在子组件中,依然是只读的。不能修改。如果想要修改,只能用state来接受,然后修改state。
有时候难以避免,子组件非要和父组件通信,那只能通过父组件向下传递一个函数,子组件通过传参的形式,调用父组件的函数,然后将数据返回给父组件的函数,父组件的函数接收到实参之后,改变父组件自己内部的state。
import React from "react";
import MyCompo from "./MyCompo.js";
class App extends React.Component
constructor()
super();
this.state =
d : 16
setD(number)
this.setState("d" : number);
render()
return (
<div>
<p>我是App组件,我有一个d状态:this.state.d</p>
<MyCompo setD=(this.setD).bind(this) d=this.state.d></MyCompo>
</div>
)
//向外暴露
export default App;
子组件就要接受父组件传来的d参数和设置D的函数:
import React from "react";
import PropTypes from "prop-types";
class MyCompo extends React.Component
constructor(props)
super();
this.state =
d : props.d
this.add = () =>
this.setState("d" : this.state.d + 1);
props.setD(this.state.d + 1);
render()
return (
<div>
<hr/>
我是MyCompo组件
<p>d : this.state.d</p>
<p>
<input type="button" value="按我更改d的值" onClick=this.add/>
</p>
</div>
);
//定义组件需要传入的参数
//props属性是可以被验证有效性的,也就是说规定你要传给我什么样的类型,就必须是什么样的类型。否则无效。
//需要yarn add prop-types -D
MyCompo.propTypes =
a : PropTypes.string.isRequired,
b : PropTypes.string.isRequired,
c : PropTypes.number.isRequired
;
//向外暴露
export default MyCompo;
目前看起来还可以接收,但是如果要和孙子,重孙子组件通信,怎么办呢,虽然可以一层一层的通过props往下传递,但是组件嵌套太深,既难以维护,编写有麻烦。所以将会在后续的文章中介绍redux,状态管理。
Tips:如果通过props传递引用类型数据,此时也是不会颠覆“数据单向传递”的限制。子组件中对数组、JSON对象的改变,不会引起父组件中那个数组、JSON对象的改变,可以认为传入了副本。
三基友之末:context
context又称上下文, 其精髓是可以跨级传递数据,爷爷组件可以直接传递数据到孙子组件。
爷爷组件(哈哈哈哈)
import React from "react";
import Baba from "./Baba.js";
import PropTypes from "prop-types";
//为了方便理解,就用拼音啦。
class Yeye extends React.Component
constructor()
super();
this.state =
a : 100
render()
return (
<div>
<h1>爷爷</h1>
<Baba></Baba>
</div>
);
//得到孩子上下文,实际上这里表示一种设置,返回一个对象,这个对象就是现在这个家族体系共享的上下文。将上下文中的a值变为自己的状态中的a值
getChildContext()
return
a : this.state.a
//设置child的上下文类型
Yeye.childContextTypes =
a : PropTypes.number.isRequired
export default Yeye;
孙子组件(哈哈哈哈)
import React from "react";
import PropTypes from "prop-types";
class Sunzi extends React.Component
//React会将上下文当做构造函数的第二个参数传入:
constructor(props,context)
super();
console.log(context); //得到上下文
render()
return (
<div>
<h1>孙子</h1>
</div>
);
//设置上下文的类型
Sunzi.contextTypes =
a : PropTypes.number
export default Sunzi;
结论:
1) 当祖先元素中更改了上下文的数据,此时所有的子孙元素中的数据都会更改,视图也会更新;
2) 反之不成立,可以认为上下文的数据在子孙元素中是只读的。此时又要需要使用奇淫技巧,就是在context中共享一个操作祖先元素的函数,子孙元素通过上下文获得这个函数,从而操作祖先元素的值。也就是说,state是自治的不涉及传值的事儿;props是单向的,父亲→儿子;context也是单向的,祖先→后代。如果要反向,就要传入一个函数。
基本很少使用这个api,但是Redux底层使用context实现.
以上是关于React技术栈系列—基础01的主要内容,如果未能解决你的问题,请参考以下文章