- 下载安装Node.js
- 然后安装npm
- 设置淘宝镜像npm install -g cnpm --registry=https://registry.npm.taobao.org
- npm config set registry https://registry.npm.taobao.org
- 可以远程拉去一个React初始化的项目cnpm install -g create-react-app
然后一个简单的React项目就完成了,
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<title>React App</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
-
这是前端展示给我们的页面(index.html),但是为什么这个React项目启动就会展示给我们这个页面呢?
-
接下来是manifest里面的代码,里面的start_url就是我们的内容
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
}
],
"start_url": "./index.html",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
- 页面里的内容是如何进行显示的呢?
看接下来的代码,这是index.js,和index.html相互对应,最主要的是它使用了ReactDom的render方法进行渲染,然后通过Id查找声明渲染的是root这个元素,而root我们在index.html里面已经定义过了,然后这个ReactDom的render方法就相当于在页面id为root的这个div里面添加东西,
import React from 'react';
import ReactDom from 'react-dom';
ReactDom.render(
document.getElementById('root')
);
- 我们可以在页面中显示这个html的标签元素的内容
ReactDom.render(
<h1>xx</h1>,
document.getElementById('root')
);
- 我们也可以在外部将一个html标签的元素定义为一个对象,并且渲染到我们的页面当中
const element = <h1>Hello, world!</h1>;
ReactDom.render(
element,
document.getElementById('root')
);
- 但是React中的元素是不可能动态的进行改变的,当元素被创建之后,是无法改变其内容或属性的,目前更新界面的唯一方法就是创建一个新的元素,然后重新传入DOM对象
function tick() {
const element = (
<div>
<h1>Hello, world!</h1>
<h2>现在是 {new Date().toLocaleTimeString()}.</h2>
</div>
);
ReactDom.render(
element,
document.getElementById('root')
);
}
setInterval(tick, 1000);
- 上面这个就是一个闹钟,虽然在上面已经显示了时间,但是正常情况下时间是不可能被修改的,我们只能每秒执行一次,然后把新的值赋给Dom里面的元素去显示,这样我们看着就好像是动态的了。我们实际上是每一秒都把element的值刷新一次
function Clock(props) {
return (
<div>
<h1>Hello, world!</h1>
<h2>现在是 {props.date.toLocaleTimeString()}.</h2>
</div>
);
}
function tick() {
ReactDom.render(
<Clock date={new Date()} />,
document.getElementById('root')
);
}
setInterval(tick, 1000);
- 通过上次的那个显示,其实不难发现一些问题,我每次需要更新的只有时间,但上面每一次都要把整个元素都要更新一遍,所以肯定是比较占用内存的,我们可以把它给优化一下,把不变的东西抽取出来,
定义一个Clock对象,里面需要使用到一个参数,这个参数就是时间,我们使用传递的参数给这个对象赋值时间,然后我们再定义一个方法,这个方法的作用就是给这个对象赋值新的日期,然后我们每秒钟就调用这个方法即可。
另外,我们也可以把我们需要展示的html标签转换为这个类,这个类里面有一些属性,可以展示出来,也可以接收一些参数用来进行展示,
- 这是定义一个类,然后把它暴露在外面,我们可以去使用它,相当于是一个组件吧
import React from 'react';
export default class Clock extends React.Component{
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>现在是 {this.props.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
- 使用一个外部组件的时候,需要根据路径去进行引用,然后就可以在ReactDom中进行渲染了
import React from 'react';
import ReactDom from 'react-dom';
import Clock from "./component/Clock";
function tick() {
ReactDom.render(
<Clock date={new Date()} />,
document.getElementById('root')
);
}
setInterval(tick, 1000);
需要注意的是,ReactDOM首先会比较元素内容的先后的不同,而在渲染过程中只会更新改变了的部分
React中的JSX
- JSX是JavaScript语法的扩展,执行更快,类型安全,在编译过程中就能发现错误
- JSX就是用来声明React当中的元素,
- 我们可以在render里面声明一个JSX语法的元素,但是不能同时写多个元素,如果需要使用多个元素,就需要使用一个大的div元素包裹起来,
- JSX语法可以存放到一个js文件中,当我们想要使用的时候,直接引入这个文件即可,
- 在JSX中也可以使用Javascript表达式,表达式写在{}当中
- 在JSX中还可以使用三元运算来计算,但是不能使用if和else
在React中还可以设置样式,
var myStyle = {
fontSize: 100,
color: '#FF0000'
};
ReactDom.render(
<h1 style = {myStyle}>菜鸟教程</h1>,
document.getElementById('root')
);
- 注释的话需要写在花括号中
{/注释…/}
JSX中可以插入数组,数组中可以放置很多元素,渲染的时候可以展开所有成员
组件
- 在React中,一个方法就可以当成一个组件,然后这个方法可以被当成组件来使用,具体的使用步骤如下
function HelloMessage(props) {
return <h1>Hello World!</h1>;
}
const element = <HelloMessage />;
ReactDom.render(
element,
document.getElementById('root')
);
并且可以声明这个方法需要一些参数,然后在方法内部的元素中使用这些参数,我们在使用这个方法的时候可以将一些元素传递进去
function HelloMessage(props) {
return <h1>Hello World! {props.name}</h1>;
}
ReactDom.render(
<HelloMessage name={"wq"}/>,
document.getElementById('root')
);
可以使用props来接收传递到方法中的参数进行使用
State
React把组件看成是一个状态机,通过与用户的交互,实现不同的状态,然后实时的修改页面当中的数据
import React from 'react';
export default 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>Hello, world!</h1>
<h2>现在是 {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
上面是每一秒都修改一下state,然后因为state被修改,所以就再显示state
Props
state和props有一些区别,state可以和用户进行交互,props只能使用父子组件来传递数据,
function HelloMessage(props) {
return <h1>Hello {props.name}!</h1>;
}
const element = <HelloMessage name="Runoob"/>;
ReactDOM.render(
element,
document.getElementById('root')
);
当然,我们也可以根据传入的一些参数设置他们的默认值
export default class HelloMessage extends React.Component {
render() {
return (
<h1>Hello ,{this.props.name}</h1>
);
}
}
HelloMessage.defaultProps={
name: "xx"
};
- 我们可以把state里面的数据传递给子组件来使用
import React from 'react';
import Name from './Name'
import Link from './Link'
export default class WebSite extends React.Component {
constructor() {
super();
this.state = {
name: "菜鸟教程",
site: "http://www.runoob.com"
}
}
render() {
return (
<div>
<Name name={this.state.name}/>
<Link site={this.state.site}/>
</div>
);
}
}
export default class Name extends React.Component {
render() {
return (
<h1>{this.props.name}</h1>
);
}
}
export default class Link extends React.Component {
render() {
return (
<h1>{this.props.site}</h1>
);
}
}
props验证
我们定义了一个组件的时候,这个组件可能会使用到一些父组件传递进来的元素,为了保持统一,我们可以设置自定义参数类型控制,如何不是预期的类型,是无法传递过来的
import React from 'react';
import PropTypes from 'prop-types';
export default class Title extends React.Component {
render() {
return(
<h1>Hello,{this.props.title}</h1>
);
}
}
Title.propsTypes = {
title: PropTypes.string,
};
这个参数类型定义,我建议是在自己的组件里面写,需要导入依赖 ‘prop-types’ ,
- 还有很多的校验器
MyComponent.propTypes = {
// 可以声明 prop 为指定的 JS 基本数据类型,默认情况,这些数据是可选的
optionalArray: React.PropTypes.array,
optionalBool: React.PropTypes.bool,
optionalFunc: React.PropTypes.func,
optionalNumber: React.PropTypes.number,
optionalObject: React.PropTypes.object,
optionalString: React.PropTypes.string,
// 可以被渲染的对象 numbers, strings, elements 或 array
optionalNode: React.PropTypes.node,
// React 元素
optionalElement: React.PropTypes.element,
// 用 JS 的 instanceof 操作符声明 prop 为类的实例。
optionalMessage: React.PropTypes.instanceOf(Message),
// 用 enum 来限制 prop 只接受指定的值。
optionalEnum: React.PropTypes.oneOf(['News', 'Photos']),
// 可以是多个对象类型中的一个
optionalUnion: React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.number,
React.PropTypes.instanceOf(Message)
]),
// 指定类型组成的数组
optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number),
// 指定类型的属性构成的对象
optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number),
// 特定 shape 参数的对象
optionalObjectWithShape: React.PropTypes.shape({
color: React.PropTypes.string,
fontSize: React.PropTypes.number
}),
// 任意类型加上 `isRequired` 来使 prop 不可空。
requiredFunc: React.PropTypes.func.isRequired,
// 不可空的任意类型
requiredAny: React.PropTypes.any.isRequired,
// 自定义验证器。如果验证失败需要返回一个 Error 对象。不要直接使用 `console.warn` 或抛异常,因为这样 `oneOfType` 会失效。
customProp: function(props, propName, componentName) {
if (!/matchme/.test(props[propName])) {
return new Error('Validation failed!');
}
}
}
}
事件处理
在React中可以给按钮附加一个点击事件,事件中进行一系列的操作,
- 基本上我们都是点击进行改变组件自身的一些状态或者发送一些请求什么的,
import React from 'react'
export default class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// 这边绑定是必要的,这样 `this` 才能在回调函数中使用
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(({
isToggleOn: !this.state.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
但是需要注意,我们需要在构造方法里面加上一行代码,要不然是无法成功执行的
如果不想绑定this,有一些方法可以解决,可以使用箭头函数
handleClick = () => {
this.setState(({
isToggleOn: !this.state.isToggleOn
}));
};
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
很多时候我们需要往事件里面添加参数,
条件渲染
react中也可以使用条件渲染
function UserGreeting(props) {
return <h1>欢迎回来</h1>;
}
function GuestGreeting(props) {
return <h1>请先注册!</h1>;
}
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting/>;
}
return <GuestGreeting/>;
}
ReactDOM.render(
<Greeting isLoggedIn={true} />,
document.getElementById('root')
);
现在我们把它改动一下,可以使用按钮动态的进行修改它的值,然后上面就可以进行动态的显示
export default 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() {
this.setState({isLoggedIn: true});
}
handleLogoutClick() {
this.setState({isLoggedIn: false});
}
render() {
const isLoggedIn = this.state.isLoggedIn;
let button;
if (isLoggedIn) {
button = <LogoutButton onClick={this.handleLogoutClick} />;
} else {
button = <LoginButton onClick={this.handleLoginClick} />;
}
return (
<div>
<Greeting isLoggedIn={isLoggedIn} />
{button}
</div>
);
}
}
function UserGreeting(props) {
return <h1>欢迎回来!</h1>;
}
function GuestGreeting(props) {
return <h1>请先注册。</h1>;
}
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}
function LoginButton(props) {
return (
<button onClick={props.onClick}>
登陆
</button>
);
}
function LogoutButton(props) {
return (
<button onClick={props.onClick}>
退出
</button>
);
}
动态显示的话也可以i使用一些高级的运算符
function Mailbox(props) {
const unreadMessages = props.unreadMessages;
return (
<div>
<h1>Hello!</h1>
{unreadMessages.length > 0 &&
<h2>
您有 {unreadMessages.length} 条未读信息。
</h2>
}
</div>
);
}
const messages = ['React', 'Re: React', 'Re:Re: React'];
ReactDOM.render(
<Mailbox unreadMessages={messages} />,
document.getElementById('root')
);
当我们想要让一个组件不显示的时候,可以把它返回为null,这个也可以进行动态的进行修改,让它消失和显示出来
function WarningBanner(props) {
if (!props.warn) {
return null;
}
return (
<div className="warning">
警告!
</div>
);
}
列表
React中也可以将列表渲染为一个元素,相当于使用map方法遍历了一下整个数组,这里比较重要的就是map方法进行遍历
const numbers = [1,2,3,4,5];
const listItems = numbers.map((numbers) =>
<li>{numbers}</li>
);
ReactDOM.render(
<ul>{listItems}</ul>,
document.getElementById('root')
);
上面这个只是最简单的将一个列表的信息包装成一个元素然后显示出来,我们还可以这样,定义一个组件,这个组件接收从外部传递的数组,然后这个组件可以显示东西,
export default class NumberList extends React.Component {
constructor(props){
super(props);
this.state = {
listItems: props.numbers?props.numbers:[]
};
}
render() {
return (
<ul>
{this.state.listItems.map((item)=>{
return <li>{item}</li>
})}
</ul>
);
}
}
我们最好再一个元素中放置一个独一无二的字符串,通常我们使用id来作为这个元素的key,但是不可能每一个元素都有一个key,所以遍历的时候,加上一个index,代表在这个遍历中的顺序也是比较常用的,不过还是比较推荐做一个id进行标识的
return (
<ul>
{this.state.listItems.map((item,index)=>{
return <li key={index}>{item}</li>
})}
</ul>
);
-
如果列表可以重新进行排序,我们不建议使用索引来进行排序,因为这会导致渲染变得很慢,
-
所以最后总结一下,当在map方法的内部调用元素时,最好随时记得为每一个元素加上一个独一无二的key,
-
并且元素的key在它的兄弟元素之间应该唯一
-
key会作为react的提示,但不会传递给你的组件,如果你的组件需要使用和key相同的值,请将其作为属性传递,
React组件API
setState
replaceState
setProps
replaceProps
forceUpdate
findDOMNode
isMounted
上面的这些都是React的组件API,但是常用的就只有第一个
export default class Counter extends React.Component {
constructor(props){
super(props);
this.state = {clickCount: 0};
}
handleClick = () => {
this.setState(
{clickCount: this.state.clickCount+1}
);
};
render() {
return(
<h2 onClick={this.handleClick}>点我,点击次数为{this.state.clickCount}</h2>
);
}
}
注意:不要直接更新状态,直接修改状态时不会重新渲染组件的,应该使用setState来进行修改
React可以将多个setState调用合并成一个调用来提高性能,因为this.props和this.state可能时异步更新的,不能依靠他们的值来计算下一个状态,
isMounted方法在ES6中已经被废除,因为可能不太行,尤其是在一些异步的情况下,所以我们现在是手动的定义,
componentDidMount() {
this.mounted = true;
}
componentWillUnmount() {
this.mounted = false;
}
React组件生命周期
组件的生命周期可分为三个状态
Mounting:已插入真实DOM
Updating:正在被重新渲染
Unmounting:已移出真实DOM
- componentWillMount:在渲染钱调用,在客户端也在服务端
- componentDidmount:在第一次渲染后调用,只在客户端,
- componentWillReceiveProps:在组件接收到一个新的props时调用,这个方法在初始化irender时不会调用
- shouComponentUpdate:返回一个布尔值,在组件收到新的props或者state时被调用,在初始化时或者forceUpdate时不被调用,我们可以选择到底要不要更新组件
- componentWillUpdate:在组件接收到新的props或者state但还没有render时调用,在初始化时不会被调用
- componentDidUpdate:在组件完成更新后立即调用,在初始化时不会被调用
- componentWillUnmount:在组件从DOM中移除之前立刻被调用
下面这个测试是在组件挂载到DOM中之后,给它附加一个定时器,变换它的透明度
export default 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 (
<div style={{opacity: this.state.opacity}}>
Hello {this.props.name}
</div>
);
}
}
Ajax请求
react中也可以引入依赖来发送ajax请求,
export default class UserGist extends React.Component {
constructor(props) {
super(props);
this.state = {username: '', lastGistUrl:''}
}
componentDidMount() {
this.serverRequest = $.get(this.props.source,function (result) {
var lastGist = result[0];
this.setState({
username: lastGist.owner.login,
lastGistUrl: lastGist.html_url
});
}.bind(this));
}
componentWillUnmount() {
this.serverRequest.abort();
}
render() {
return (
<div>
{this.state.username} 用户最新的Gist共享地址
<a href={this.state.lastGistUrl}>{this.state.lastGistUrl}</a>
</div>
);
}
}
有好几种方式的,这里只用这一种先,get方法中有一个参数,这个参数为url地址,还可以有一个回调函数,这个回调函数可以对数据进行处理,
表单与事件
我们可以监听到值的变化,一般都用来监听输入框的变化,然后给出一些反馈,
export default class HelloMessage extends React.Component {
constructor(props){
super(props);
this.state = {
value: 'Hello Runoob!'
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event){
this.setState({
value: event.target.value
});
}
render() {
var value = this.state.value;
return(
<div>
<input type="text" value={value} onChange={this.handleChange}/>
<h4>{value}</h4>
</div>
);
}
}
变化的时候将变化的值传递进去,然后可以对这个值进行操作
- 但是有的时候我们需要把那些消息跨越组件进行传递信息,先来说一下父组件向子组件传递方法,然后这些方法可以修改父组件的内容
export default class HelloMessage extends React.Component {
constructor(props){
super(props);
this.state = {
value: 'Hello Runoob!'
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event){
this.setState({
value: event.target.value
});
}
render() {
var value = this.state.value;
return(
<div>
<Content myDataProp={value}
updateStateProp={this.handleChange}/>
</div>
);
}
}
export default class Content extends React.Component {
render() {
return(
<div>
<input type="text" value={this.props.myDataProp} onChange={this.props.updateStateProp} />
<h4>{this.props.myDataProp}</h4>
</div>
);
}
}
这看着也比较容易理解,不过具体实现起来还是有点麻烦的
select 下拉菜单的使用,比较容易理解
export default class FlavorForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: 'coconut'};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({
value: event.target.value
})
}
handleSubmit(event) {
alert("Your favorite flavor is: "+this.state.value);
event.preventDefault();
}
render() {
return(
<form onSubmit={this.handleSubmit}>
<label>
选择你喜欢的网站
<select value={this.state.value} onChange={this.handleChange}>
<option value="gg">Google</option>
<option value="rn">Runoob</option>
<option value="tb">Taobao</option>
<option value="fb">Facebook</option>
</select>
</label>
<input type="submit" value="提交"/>
</form>
);
}
}
通过实例演示通过onClick事件修改数据
export default class HelloMessage extends React.Component {
constructor(props){
super(props);
this.state = {
value: 'Hello Runoob!'
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event){
this.setState({
value: '菜鸟教程'
});
}
render() {
var value = this.state.value;
return(
<div>
<button onClick={this.handleChange}>点我</button>
<h4>{value}</h4>
</div>
);
}
}
通过onClick事件来修改数据,