不晓得啥情况,markdown在csdn识别错误?排版后面的代码被破坏了,正确的排版:https://ryan-miao.github.io/2017/08/03/react-tutorial-1/

笨人学习法

10000个小时策略来学习,因为笨。先照着官方文档敲一遍,写一遍。

准备

先要准备环境。搭建一个基于webpack的react环境:Hello ReactJS.

一些要点

我在想是否应该完整的记录照抄的过程呢。毕竟已经开始一段,前面的要不要补上?回头看以前写过的angularJS的博客,现在完全不会了,太久没用了。所以,还是记录基础以及关注的问题就好。

1.1 基本格式

react的模板文件后缀结尾为.jsx

react可以采用html标签拼接的方式定义一个元素。比如:

const element = <h1>Hello, world</h1>;

假设页面有个div:

<div id="root"></div>

那么,reactJS可以这样渲染页面:

const element = <h1>Hello, world</h1>;
ReactDOM.render(
element,
document.getElementById('root')
);
  • 需要引入react-dom.
  • element变量就是一个react的元素,一个组件,一个component.
  • 通过ReactDOM.render(reactElement, domElement)来渲染页面

1.1 变量

react可以使用一对大括号来包裹变量,与html拼接:

function tick() {
const element = (
<div>
<h1>Hello, world!</h1>
<h2>It is {new Date().toLocaleTimeString()}.</h2>
</div>
);
ReactDOM.render(
element,
document.getElementById('clock')
);
} setInterval(tick, 1000);
  • 大括号里的代码是js代码
  • element是一个react组件:component。可以看成由divh1,h2拼接的匿名组件。

下面实践以上的代码。首先,由于采用单个元素测试,需要修改上次搭建好的环境。

修改webpack.config.js

 module.exports = {
- entry: './app/index.js',
+ entry: {
+ app: './app/index.js',
+ clock: './app/components/step1-element.jsx'
+ },
output: {
path: path.resolve(__dirname, 'dist'),
- filename: 'index_bundle.js'
+ filename: '[name].bundle.js',
},

意思是可以渲染多个打包后的js文件。分别定义entry就是需要单独打包的js。在filename就会根据entry的key来生成打包后的文件名。

1.1.1 构建第一个react component

创建app/components/step1-element.jsx

import React from 'react';
import ReactDOM from 'react-dom'; function Clock(props) {
return (
<div>
<h1>Step1, learn element and variable.</h1>
<h2>It is {props.date.toLocaleTimeString()}.</h2>
</div>
);
}
function tick() { ReactDOM.render(<Clock date={new Date()} />, document.getElementById('clock'));
}
setInterval(tick, 1000);
  • function Clock就是一个react component,和前面的element一样,都是react组件.
  • react component可以写成html标签的方式,但要求方法名必须大写,也即标签名必须大写。<Clock date={new Date()}/>就是组件的用法。
  • 组件Clock接收一个参数对象propsprops的属性可以通过标签上的变量来赋值。比如date就通过标签传入到function Clock里了。由此,像<div>这种拼接的标签肯定也是有function的,不过react库已经写好了。
  • react component必须有返回值,返回一段html代码,用圆括号包裹
  • html标签与js变量可以通过一对大括号的方式拼接起来

修改app/index.html.添加一个我们用来测试div节点。这里主要用于clock

<div id="root">           

</div>
+
+
+<div id="content">
+ <div id="clock"></div>
+</div>
</body>
</html>

然后,运行yarn build。编译后的dist目录如下:

|____dist
| |____app.bundle.js
| |____clock.bundle.js
| |____index.html
| |____index_bundle.js

可以看到定义的两个js都已经生成。而且index.html中也插入:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>React App</title>
</head>
<body>
<div id="root"> </div> <div id="content">
<div id="clock"></div>
</div>
<script type="text/javascript" src="app.bundle.js"></script><script type="text/javascript" src="clock.bundle.js"></script></body>
</html>

但发现还多了个index_bundle.js,这是我们上一步生成。在本次构建中并没有自动移除。想要自动移除怎么办?

添加webpack plugin: clean-webpack-plugin

yarn add clean-webpack-plugin

修改webpack.config.js

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
+ const CleanWebpackPlugin = require('clean-webpack-plugin'); const HtmlWebpackPluginConfig = new HtmlWebpackPlugin({
template: './app/index.html',
filename: 'index.html',
inject: 'body'
}); module.exports = {
entry: {
app: './app/index.js',
clock: './app/components/step1-element.jsx'
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].bundle.js',
},
module: {
loaders: [
{ test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ },
{ test: /\.jsx?$/, loader: 'babel-loader', exclude: /node_modules/ }
]
},
plugins: [
HtmlWebpackPluginConfig,
+ new CleanWebpackPlugin(['dist'])
]
};

重新build.yarn build。此时,dist目录下当只有所需要的文件了。

启动html查看效果。这时可以采用webstom或者idea里的用浏览器打开功能,会自动创建的静态服务器。方便简单。也可以安装http-server。不过,既然用webpack,肯定采用webpack的热编译功能。

yarn start

浏览器访问localhost:8080就是我们的页面了。

1.2 React Developer Tools

一个值得二级标题的功能。在chrom扩展里搜索React Developer Tools,添加。然后重新打开我们的页面。看控制台的react节点:

1.3 推荐的react组件写法

除了上文使用function来创建一个react component。推荐采用es6 class的方式。更加清晰。

由于用到lambda语法糖,需要增加一个新的babel插件:

yarn add babel-plugin-transform-class-properties --dev

然后在.babelrc文件中新增:

"plugins": ["transform-class-properties"]

下面创建app/components/LoginButton.jsx

import React from 'react';

class LoginButton extends React.Component {

    handleClick = () => {
console.log("this is ", this);
}; render() {
return (
<button onClick={this.handleClick}>
Click me, auto bind this by lambda
</button>
);
}
} export default LoginButton;

这里有几个需要注意的地方。

  • 通过class来声明一个component,并在结尾处export default出去。
  • 创建的component需要继承React.Component
  • 必须创建render方法,并返回一个react component组件
  • 通过lambda语法可以指定方法为this的属性,相当于在构造器中绑定放大到this。因此可以在onClick中调用this。否则,普通的方法不会绑定到this上,需要在构造器上绑定。

以上创建了一个组件LoginButton,我们可以像开始一样直接render到一个dom元素里。也可以直接添加到另一个component组件中。比如搭建环境时给的App组件:

import React from 'react';
import Clock from './Clock.jsx';
import ActionLink from './ActionLink.jsx';
+ import LoginButton from './LoginButton.jsx' class App extends React.Component {
render() {
return (
<div style={{textAlign: 'center'}}>
<h1>Hello World! Hi ReactJS!</h1> <div>
<Clock />
<ActionLink />
+ <LoginButton/>
</div>
</div>
);
}
} export default App;
  • import引入刚才写的LoginButton,其中地址是相对地址。变量名可以自定义,因为export的时候采用了default。这里仍旧取名为LoginButton。
  • 将引入的变量的标签形式插入拼接即可。

yarn start可以观察到页面多了按钮。

1.4 使用state控制状态

最开始的demo Clock中,使用一个时间函数,定时render页面。这种需求可以转换为定时更新状态,由react自动根据状态来渲染页面。对于那个Clock组件来说,唯一变化的就是时间,那么这个时间就是动态的状态。react的component的有个state属性,专门用来传递状态,或者说数据的。当我们需要修改数据的时候,直接修改state就可以了。

新建app/components/Clock.jsx

import React from 'react';

function FormattedDate(props) {
return <h2> It is {props.date.toLocaleTimeString()}. </h2>
}
/**
* @Author Ryan Miao
* @Date 2017/08/02 20:58
*/
class Clock extends React.Component {
constructor(props){
super(props);
this.state = {date: new Date()};
} componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
} componentWillUnmount() {
clearInterval(this.timerID)
} tick() {
this.setState({date: new Date()});
} render() {
return (
<div>
<h1>This is a clock!</h1>
<FormattedDate date={this.state.date} />
</div>
);
}
} export default Clock;
  • constructor是一个构造函数,当new Clock()的时候会调用这个方法来创建对象,因此可以把对象的一些初始化操作放在这里。本例中,初始化state.
  • state是一个对象,内容自定义,本例只增加一个date属性.
  • componentDidMount()在component挂载的时候触发,这里设置一个定时器,定时调用tick().
  • this.setState({})是唯一能修改state的方式,通过this.state={}的做法无效。另外,setState是一个merge的异步操作。merge是说,每次set的时候,只会修改指定的变量,不会整体替换。异步是说不能直接this.state.xx来操作属性,因为有可能你调用this.state.xx来获取xx的值的时候,前一次的setState还没执行完。如果想要同步的修改state里的属性,可以采用第二种方式:
// Correct
this.setState((prevState, props) => ({
counter: prevState.counter + props.increment
}));

接受两个参数,第一个是state,第二个是props。这两个变量会在最后一次修改结束后自动注入。所以就可以放心的设置state的属性了。

  • componentWillUnmount()和componentDidMount()都是react的lifecycle hooks。是react组件声明周期前后会调用的方法。componentWillUnmount()会在component移除的时候触发。this.timerID可以直接将属性timerID绑定到this上,这个不需要绑定到state,因为这个和渲染(render)页面无关。
  • FormattedDate是我们抽出来的专门显示时间的组件,date是它的一个props.

组件创建完毕,下面开始使用。使用方式就是转换成标签的方式调用它。

import React from 'react';
+ import Clock from './Clock.jsx';
import ActionLink from './ActionLink.jsx';
import LoginButton from './LoginButton.jsx'
import LoginControl from './LoginControl.jsx' class App extends React.Component {
render() {
return (
<div style={{textAlign: 'center'}}>
<h1>Hello World! Hi ReactJS!</h1> <div>
+ <Clock />
<ActionLink />
<LoginButton/>
</div> <div>
<LoginControl />
</div>
</div>
);
}
} export default App;

页面这时候就会自动刷新时间了。

1.5 阻止事件

React里的属性采用驼峰命名规则,在原来的html中,定义onclick属性:

<button onclick="activateLasers()">
Activate Lasers
</button>

但在react里,必须将onclick改成onClick

<button onClick={activateLasers}>
Activate Lasers
</button>

在原来的html中,可以通过return false的方式阻止默认事件。比如,a标签有href和onClick属性。在html中,我们想要阻止点击的时候跳转到href,那么可以在onClick中返回false

<a href="#" onclick="console.log('The link was clicked.'); return false">
Click me
</a>

这样,你点击a标签后,浏览器地址栏不会有#,如果你不return false,浏览器地址栏就会发生跳转。这是a标签的默认行为。在html中可以通过return false来阻止。但在react中这样做无效。必须使用preventDefault

创建app/components/ActionLink.jsx

import React from 'react';

function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log("The link was clicked. PreventDefault event.");
} return (
<a href="#" onClick={handleClick}>
Click me
</a>
);
} export default ActionLink;

然后在App.jsx中引入。刷新页面,点击a标签。观察浏览器地址栏可以发现没有任何变化,证明默认行为被阻止了。如果注释掉e.preventDefault();,刷新页面,点击a标签,观察地址栏就会发现发生了改变。

1.8 方法绑定到this

接着理解react组件的写法。写一个Toggle按钮,每次点击都切换状态。

创建app/components/Toggle.jsx

import React from 'react';

class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {
isToggleOn: true,
color: 'red'
}; //This bind is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
} handleClick() {
console.log("this=", this);
this.setState(
prevStat => ({
isToggleOn: !prevStat.isToggleOn,
color: prevStat.color==='red'? 'green':'red'
})
);
} render() {
return (
<button onClick={this.handleClick} style={{background: this.state.color}}>
{this.state.isToggleOn ? 'ON':'OFF'}
</button>
);
}
} export default Toggle;
  • 首先,构造函数定义了state有两个属性,并初始化
  • 构造函数绑定了handleClick的作用域为Toggle. 关于如何理解这个绑定,参阅如何理解js中的this绑定. 如果注释掉这一行,触发handleClick的时候,里面的this是null。那么setState当然也就不存在。我们这里setState是希望调用Toggle的方法,希望这个this指向Toggle. 因此需要在构造器中绑定this。
  • setState的时候,如果和前一个状态相关的话,一定要采用方法传参的方式。这里是一个lambda语法糖。
  • 将Toggle插入到App.jsx中,页面会有个按钮,每次点击都会改变颜色。这是因为,点击的时候触发onClick,调用handleClick,然后setState修改了state,react就会根据state来重新render组件。

另一种方式自动绑定方法成为一个实例,是采用babel-plugin-transform-class-properties。这个目前还不是es的标准,因为将方法定义为属性这种做法还很有争议。在java8中lambda也是如此,但java8将lambda设定为一等公民,是另一个东西,和成员变量类似。这里,如果使用这个plugin的话,lambda语法糖可以升级为属性,那么就不用绑定this了。

class LoggingButton extends React.Component {
// This syntax ensures `this` is bound within handleClick.
// Warning: this is *experimental* syntax.
handleClick = () => {
console.log('this is:', this);
} render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
);
}
}
  • 这里,handleClick就会升级为属性,就可以直接用this调用,里面的this就是外面的LogginButton.

还有一种方式是lambda语法,但官方不推荐:

class LoggingButton extends React.Component {
handleClick() {
console.log('this is:', this);
} render() {
// This syntax ensures `this` is bound within handleClick
return (
<button onClick={(e) => this.handleClick(e)}>
Click me
</button>
);
}
}

The problem with this syntax is that a different callback is created each time the LoggingButton renders. In most cases, this is fine. However, if this callback is passed as a prop to lower components, those components might do an extra re-rendering. We generally recommend binding in the constructor or using the property initializer syntax, to avoid this sort of performance problem.

建议采用前两种方式。

1.7 一个稍微复杂的例子:登录按钮的动态切换

综合以上的demo。编写新需求。当用户没有登录的时候,显示"Please login",并显示login按钮,当用户登录的时候显示"welcome"和logout按钮。

创建app/components/Greeting.jsx

import React from 'react';

function UserGreeting(props){
return <h1>Welcome back!</h1>
}
function GuestGreeting(props){
return <h1>Please sign up.</h1>
}
function Greeting(props){
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn){
return <UserGreeting/>
} return <GuestGreeting/>
} export default Greeting;

创建app/components/LoginControl.jsx

import React from 'react';
import Greeting from "./Greeting.jsx"; function LoginButton(props) {
return (
<button onClick={props.onClick} style={{color: 'white', background:"green"}} >
Login
</button>
);
} function LogoutButton(props) {
return (
<button onClick={props.onClick} style={{color: 'white', background:'red'}}>
Logout
</button>
);
} /**
* @Author Ryan Miao
* @Date 2017/08/02 20:23
*/
class LoginControl extends React.Component {
constructor(props) {
super(props);
this.handleLoginClick = this.handleLoginClick.bind(this);
this.handleLogoutClick = this.handleLogoutClick.bind(this);
this.state = {isLoggedIn: false};
} handleLoginClick() {
console.log("Click login");
this.setState({isLoggedIn: true});
}
handleLogoutClick() {
console.log("Click logout");
this.setState({isLoggedIn: false});
} render() {
const isLoggedIn = this.state.isLoggedIn; let button = null;
if (isLoggedIn) {
button = <LogoutButton onClick={this.handleLogoutClick} />;
} else {
button = <LoginButton onClick={this.handleLoginClick} />;
} return (
<div style={{border: '1px solid #000'}}>
<Greeting isLoggedIn={this.state.isLoggedIn}/>
{button}
</div>
);
}
}
export default LoginControl;

在App.jsx中引入

import React from 'react';
import Clock from './Clock.jsx';
import ActionLink from './ActionLink.jsx';
import LoginButton from './LoginButton.jsx'
+ import LoginControl from './LoginControl.jsx'
import Toggle from './Toggle.jsx' class App extends React.Component {
render() {
return (
<div style={{textAlign: 'center'}}>
<h1>Hello World! Hi ReactJS!</h1> <div>
<Clock />
<ActionLink />
<LoginButton/>
<Toggle />
</div> + <div>
+ <LoginControl />
+ </div>
</div>
);
}
} export default App;

参考

照着官方文档学习react的更多相关文章

  1. Spring 4 官方文档学习(十二)View技术

    关键词:view technology.template.template engine.markup.内容较多,按需查用即可. 介绍 Thymeleaf Groovy Markup Template ...

  2. Spring 4 官方文档学习(十一)Web MVC 框架之配置Spring MVC

    内容列表: 启用MVC Java config 或 MVC XML namespace 修改已提供的配置 类型转换和格式化 校验 拦截器 内容协商 View Controllers View Reso ...

  3. Spring Data Commons 官方文档学习

    Spring Data Commons 官方文档学习   -by LarryZeal Version 1.12.6.Release, 2017-07-27 为知笔记版本在这里,带格式. Table o ...

  4. Spring 4 官方文档学习(十一)Web MVC 框架之resolving views 解析视图

    接前面的Spring 4 官方文档学习(十一)Web MVC 框架,那篇太长,故另起一篇. 针对web应用的所有的MVC框架,都会提供一种呈现views的方式.Spring提供了view resolv ...

  5. Spring 4 官方文档学习(十一)Web MVC 框架

    介绍Spring Web MVC 框架 Spring Web MVC的特性 其他MVC实现的可插拔性 DispatcherServlet 在WebApplicationContext中的特殊的bean ...

  6. Spring Boot 官方文档学习(一)入门及使用

    个人说明:本文内容都是从为知笔记上复制过来的,样式难免走样,以后再修改吧.另外,本文可以看作官方文档的选择性的翻译(大部分),以及个人使用经验及问题. 其他说明:如果对Spring Boot没有概念, ...

  7. Spring Framework 官方文档学习(四)之Validation、Data Binding、Type Conversion(一)

    题外话:本篇是对之前那篇的重排版.并拆分成两篇,免得没了看的兴趣. 前言 在Spring Framework官方文档中,这三者是放到一起讲的,但没有解释为什么放到一起.大概是默认了读者都是有相关经验的 ...

  8. Spring Framework 官方文档学习(四)之Validation、Data Binding、Type Conversion(二)

    接前一篇 Spring Framework 官方文档学习(四)之Validation.Data Binding.Type Conversion(一) 本篇主要内容:Spring Type Conver ...

  9. Spring boot官方文档学习(一)

    个人说明:本文内容都是从为知笔记上复制过来的,样式难免走样,以后再修改吧.另外,本文可以看作官方文档的选择性的翻译(大部分),以及个人使用经验及问题. 其他说明:如果对Spring Boot没有概念, ...

随机推荐

  1. Testlink安装步骤Checking if C:\inetpub\wwwroot\testlink-1.9.3\gui\templates_c directory is writable Failed !

    Testlink安装过程中问题现象: Checking if C:\inetpub\wwwroot\testlink-1.9.3\gui\templates_c directory is writab ...

  2. java架构师负载均衡、高并发、nginx优化、tomcat集群、异步性能优化、Dubbo分布式、Redis持久化、ActiveMQ中间件、Netty互联网、spring大型分布式项目实战视频教程百度网盘

    15套Java架构师详情 * { font-family: "Microsoft YaHei" !important } h1 { background-color: #006; ...

  3. PHP按值合并数组

    /** * PHP按值合并数组 * */ function my_array_merge(&$array1, &$array2) { $result = Array(); foreac ...

  4. Lambda(Linq)

    在谈到lambda表达式之前,首先要说一下委托,在下一章会详细介绍委托,在这里就是简单说明一下. 委托的关键字段delegate,声明委托 public delegate void NoReturnN ...

  5. [leetcode-526-Beautiful Arrangement]

    Suppose you have N integers from 1 to N. We define a beautiful arrangement as an array that is const ...

  6. RandomAccessFile乱码问题

    转自:http://www.cnblogs.com/xudong-bupt/archive/2013/04/20/3028980.html     Thanks Java对文件的读.写随机访问,Ran ...

  7. 本地服务器 windows server 2008 datacenter conn /as sysdba 提示 ora-01031 insufficient privileges

    原因是需要把当前用户administrator(为例)添加到ora_dba组里. 服务器管理器--配置--本地用户和组--组

  8. DOM0级事件处理、DOM2级事件处理

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  9. CJOJ 2171 火车站开饭店(树型动态规划)

    CJOJ 2171 火车站开饭店(树型动态规划) Description 政府邀请了你在火车站开饭店,但不允许同时在两个相连的火车站开.任意两个火车站有且只有一条路径,每个火车站最多有 50 个和它相 ...

  10. windows下安装DB2数据库以及使用Aqua Data Studio链接数据库

    本文只是作为自己的心得体会,不具有一般性! 1.其实安装DB2数据库还是比较简单的,一般都是直接下一步下一步就可以了,只是有些地方需要注意.我安装的DB2数据库版本如下图所示: 2.拿到数据库的版本之 ...