# react_learn
**Repository Path**: shafish/react_learn
## Basic Information
- **Project Name**: react_learn
- **Description**: facebook 开源前端框架学习记录
- **Primary Language**: Unknown
- **License**: CC-BY-SA-4.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2019-09-20
- **Last Updated**: 2020-12-19
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# react_learn
## Description
facebook 开源前端框架学习记录
## 例子
看react-demos目录
## 学前个人看法
因为之前一直都是在侧重学java方面后端技术,虽早听闻世上存有三大前端开发框架,但一直没打算学(懒+拖)。后来还看到说可以做后端??什么鬼,感觉是时候学习咧。
以上。
## react历史
不多说了,自己百度吧
## html模板
大致结构:
```js
```
模板需要注意两个事情:
1. 如果需要使用JSX语法,必须由type值为`text/babbel`的script的标签包围;
2. 代码用到的react js库,比如react、readct-dom、browser等等都必须首先加载(放在开头)。
## ReactDOM.render()
函数用于将模板转为html代码,并插入指定的dom节点内容。相当于render的两个参数。
详细使用:
```js
```
上面的代码就是将h1标签,整个插入到id为example节点内。运行结果:

## JSX语法
是不是对上面例子里,h1内容可以直接写在js代码里感到奇怪??
可以不加引号写在js代码中,这就是JSX语法,它允许Html于JavaScript代码混写。
jsx语法有几个个特点:
1. html标签用`<>` -- html规则解析
2. 代码块用`{}` -- js解析
3. js变量中可以直接插入模板中,用`{变量}`获取
例子:
```js
var names = ['alice','emily','kate'];
ReactDOM.render(
{
names.map(function(name){
return
Hello! {name}!
})
}
,
document.getElementById('example')
)
```
```js
var arr = [
Hello world!
,
React is awesome
,
];
ReactDOM.render(
{arr}
,
document.getElementById('example')
);
```
## 组件
react允许将代码封装成一个组件,也就是我们html中用到的标签,然后跟普通html标签一样使用。React.createClass方法就用于生成一个组件。
```js
class HelloMessage extends React.Component {
render(){
return Hello,{this.props.name}
;
}
}
ReactDOM.render(
,
document.getElementById("example")
);
```
我们可以发现组件的几大特点:
1. 组件需要用class定义,(组件名/类名??)组件类名必须要大写开头
2. 组件类下必须有reander()
3. 使用时,标签参数`xxxx`,需要用`this.props.xxxx`获取
4. reander函数内,只能包含一个顶层标签,比如上面的`h1`标签,顶层标签不能并列。
以上。
遵守上面几点就可以灵活使用组件类,来自定义我们的标签咧。组件可以添加多个属性,比如上面的`name`,根据上面第三点操作就行;组件的属性也可以接受任意值,字符串、对象、函数等等都可以。
注意:
`class`属性强制为`className`
`for`属性强制为`htmlFor`
因为class和for是js的保留字。
ps:保留字是语言中定义具有特殊含义的标识符,保留字不能作为标识符使用
### 高级用法
当组件实例被创建并插入 DOM 中时,其生命周期调用顺序如下:
```js
constructor() //构造函数。(构造函数都出来了)
static getDerivedStateFromProps()
render()
componentDidMount()
```
看看倒数第二节,组件的生命周期
## this.props.children
上面说了`this.props.xxx`是拿来去属性值的,但是有一个例外,就是`this.props.children`。
它表示了组件的所有子节点:
```js
class NoteList extends React.Component{
render() {
return (
{ /* 之前用过map,大概用法就是:集合.map(单个元素,处理当个元素函数),函数里用到的一个无序列表li*/
React.Children.map(this.props.children, function (child) {
return - {child}
;
})
}
);
}
}
ReactDOM.render(
hello
world
shafish
,
document.getElementById("example")
);
```
建议:
vscode的语言模式选javascript react,获取百度查查vscode关于react的语法查错,打错好几次了。
上面的代码NoteList组件标签在用的时候,包含了三个子节点,都可以通过`this.props.children`获取:

注意:
有没有想过如果NoteList标签里不设子节点会是什么情况??this.props.children 的值有三种可能:需要注意了
1. 如果当前组件没有子节点,它就是 undefined ;
2. 如果有一个子节点,数据类型是 object ;
3. 如果有多个子节点,数据类型就是 array;
你们注意到`React.Children`了吗,React提供了这个工具方法,不管什么类型,放进map中,迭代就行了。[更多Children用法](https://facebook.github.io/react/docs/top-level-api.html#react.children)
## PropTypes
上面在组件那小节提到-组件的属性可以接受任意值,字符串、对象、函数等等。有时,我们需要一种机制,验证别人使用组件时,提供的参数是否符合要求。
`组件类`的`PropTypes属性`,就是用来验证组件实例属性是否符合要求的!!
记得先导包
```js
```
```js
var data = 123;
class MyTitle extends React.Component {
static propTypes = {
title: PropTypes.string.isRequired, /*指定title必须为string*/
}
render() {
return {this.props.title}
;
}
}
ReactDOM.render(
, /*强制转类型,不过js的类型好像用的时候区分不大吧,先mark*/
document.getElementById('example')
);
```
此时显示是可以显示的,但是控制台就存在验证不通过报错了。

[更多PropTypes用法](http://facebook.github.io/react/docs/reusable-components.html)
## getDefaultProps
跟上面那个proptypes用法差不多,但是这个是用来设置组件属性默认值的。
```js
class MyTitle extends React.Component {
getDefaultProps : function () {
return {
title : 'Hello World'
};
render() {
return {this.props.title}
;
}
}
ReactDOM.render(
, /*用默认值就好*/
document.getElementById('example')
);
```
## 获取真实的DOM节点
组件并不是真实的 DOM 节点,而是存在于内存之中的一种数据结构,叫做虚拟 DOM。只有当它插入文档以后,才会变成真实的 DOM 。
根据 React 的设计,所有的 DOM 变动,都先在虚拟 DOM 上发生,然后再将实际发生变动的部分,反映在真实 DOM上,这种算法叫做 DOM diff ,它可以极大提高网页的性能表现。
但是,有时需要从组件获取真实 DOM 的节点,这时就要用到 ref 属性:
```js
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myTextInput = React.createRef();
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
this.myTextInput.current.focus();
}
render() {
return (
);
}
}
ReactDOM.render(
,
document.getElementById('example')
);
```
这个就有点复杂了,一步步来看。
- 先看布局,`render()`里包含一个文本输入和按钮。输入框需要获取用户输入,前面说了组件类是虚拟的dom,为了做到用户输入,文本输入框必须有一个 ref 属性,然后 `this.refs.[refName]` 就会返回这个真实的 DOM 节点。
- `this.refs.[refName]` 属性获取的是真实 DOM。所以必须等到虚拟 DOM 插入文档以后,才能使用这个属性,否则会报错。上面代码中,通过为组件指定 Click 事件的回调函数,确保了只有等到真实 DOM 发生 Click 事件之后,才会读取 this.refs.[refName] 属性。
- React 组件支持很多事件,除了 Click 事件以外,还有 KeyDown 、Copy、Scroll 等
[更多事件用法](http://facebook.github.io/react/docs/events.html#supported-events)
## this.state
组件免不了要与用户互动,React 的一大创新,就是将组件看成是一个状态机,一开始有一个初始状态,然后用户互动,导致状态变化,从而触发重新渲染 UI。
```js
class LikeButton extends React.Component {
constructor(props) {
super(props)
this.state = {
liked: false
}
this.handleClick = this.handleClick.bind(this)
}
handleClick(event) {
this.setState({ liked: !this.state.liked });
}
render() {
var text = this.state.liked ? 'like' : 'haven\'t liked';
return (
You {text} this. Click to toggle.
);
}
}
ReactDOM.render(
,
document.getElementById('example')
);
```
一步步来:
- 组件`LikeButton`的`getInitialState`方法用于定义初始状态,也就是一个对象,这个对象可以通过`this.state`属性读取。当用户点击组件,导致状态变化,`this.setState` 方法就修改状态值,每次修改以后,自动调用 `this.render` 方法,再次渲染组件。
- 由于` this.props `和 `this.state` 都用于描述组件的特性,可能会产生混淆。一个简单的区分方法是,`this.props` 表示那些一旦定义,就不再改变的特性,而 `this.state` 是会随着用户互动而产生变化的特性。
## 表单
用户在表单填入的内容,属于用户跟组件的互动,所以不能用 `this.props` 读取
```js
class Input extends React.Component {
constructor(props) {
super(props)
this.state = {
value: 'Hello!'
}
this.handleChange = this.handleChange.bind(this)
}
handleChange(event) {
this.setState({ value: event.target.value });
}
render() {
var value = this.state.value;
return (
);
}
}
ReactDOM.render(, document.getElementById('example'));
```
上面代码中,文本输入框的值,不能用 this.props.value 读取,而要定义一个 onChange 事件的回调函数,通过 event.target.value 读取用户输入的值。textarea 元素、select元素、radio元素都属于这种情况
[更多表达用法](http://facebook.github.io/react/docs/forms.html)
## 组件的生命周期
组件的生命周期:
- 1. Mounting:已插入真实 DOM
- 2. Updating:正在被重新渲染
- 3. Unmounting:已移出真实 DOM
React 为每个状态都提供了两种处理函数,will 函数在进入状态之前调用,did 函数在进入状态之后调用,三种状态共计五种处理函数:
```js
componentWillMount()
componentDidMount()
componentWillUpdate(object nextProps, object nextState)
componentDidUpdate(object prevProps, object prevState)
componentWillUnmount()
```
React 还提供两种特殊状态的处理函数:
```js
componentWillReceiveProps(object nextProps):已加载组件收到新的参数时调用
shouldComponentUpdate(object nextProps, object nextState):组件判断是否重新渲染时调用
```
例子:
```js
class Hello extends React.Component {
constructor(props) {
super(props)
this.state = {
opacity: 1.0
};
}
componentDidMount() {
this.timer = setInterval(function () {
var opacity = this.state.opacity;
opacity -= .05;
if (opacity < 0.1) {
opacity = 1.0;
}
this.setState({
opacity: opacity
});
}.bind(this), 100);
}
render() {
return (
Hello {this.props.name}
);
}
}
ReactDOM.render(
,
document.getElementById('example')
);
```
写的时候是懵的,这都是什么??
我们只看render函数,除了一个style参数其他都是之前熟悉的。继续
- setState函数,我是知道的,作用跟在flutter中用法是一样的,拿来刷新状态。
- setInterval设定了一个100毫秒的定时器,来处理opacity值,emmm? opacity是什么,opacity属性是css3中设置元素不透明级别的一个参数。而且React组件在构造函数里设置的一个初始值,`this.state.opacity`
- componentDidMount,组件插入dom之后。每隔0.1s,opacity以0.5递减,低于0.1又是一个循环。
所以最后显示的结果就是hello world在不停地渐变。
注意:
取opacity值的时候,不是`style="{opacity: this.state.opacity}"`,而是`style={{opacity: this.state.opacity}}`
这是因为 React 组件样式是一个对象,所以第一重大括号表示这是 JavaScript 语法,第二重大括号表示样式对象
## ajax
last one
组件的数据来源,通常是通过 Ajax 请求从服务器获取,可以使用 componentDidMount 方法设置 Ajax 请求,等到请求成功,再用 this.setState 方法重新渲染 UI :
上面代码使用 jQuery 完成 Ajax 请求,这是为了便于说明。React 本身没有任何依赖,完全可以不用jQuery,而使用其他库。
我们甚至可以把一个Promise对象传入组件:
```js
class UserGist extends React.Component {
constructor(props) {
super(props)
this.state = {
username: '',
lastGistUrl: ''
}
}
componentDidMount() {
$.get(this.props.source, function(result) {
var lastGist = result[0];
this.setState({
username: lastGist.owner.login,
lastGistUrl: lastGist.html_url
});
}.bind(this));
}
render() {
return (
{this.state.username}'s last gist is
here.
);
}
}
ReactDOM.render(
,
document.getElementById('example')
);
```
从Github的API抓取数据,然后将Promise对象作为属性,传给RepoList组件:
```js
ReactDOM.render(
,
document.body
);
```
如果Promise对象正在抓取数据(pending状态),组件显示"正在加载";如果Promise对象报错(rejected状态),组件显示报错信息;如果Promise对象抓取数据成功(fulfilled状态),组件显示获取的数据:
```js
class RepoList extends React.Component{
getInitialState: function() {
return { loading: true, error: null, data: null};
},
componentDidMount() {
this.props.promise.then(
value => this.setState({loading: false, data: value}),
error => this.setState({loading: false, error: error}));
},
render: function() {
if (this.state.loading) {
return Loading...;
}
else if (this.state.error !== null) {
return Error: {this.state.error.message};
}
else {
var repos = this.state.data.items;
var repoList = repos.map(function (repo) {
return (
{repo.name} ({repo.stargazers_count} stars)
{repo.description}
);
});
return (
Most Popular JavaScript Projects in Github
{repoList}
);
}
}
};
```
参考:
阮一峰的网络日志 15年出的 [React 入门实例教程](http://www.ruanyifeng.com/blog/2015/03/react.html)