jsx变为js的过程http://babeljs.io/repl/

youtube: https://www.youtube.com/channel/UCA-Jkgr40A9kl5vsIqg-BIg/playlists

raisl365: https://www.rails365.net/movies/you-ren-de-react-shi-pin-jiao-cheng-ji-chu-pian-1-jie-shao

上一节讲了:通过代码的分离来展示client.js文件的进化过程。

关于基础,还需要继续夯实。


先考虑下组件的生命周期

Ref: 基础篇 #14 组件生命周期(完结), 对应代码:https://github.com/hfpp2012/hello-react

Ref: [React] 02 - Intro: why reac and its design pattern - React 组件生命周期

  • Mounting     :已插入真实 DOM 【挂载】
  • Updating      :正在被重新渲染  【更新】
  • Unmounting:已移出真实 DOM 【卸载】

生命周期的方法:

  • componentWillMount 【挂载前的操作】在渲染前调用,在客户端也在服务端。

  • componentDidMount 【挂载后调用的操作】在第一次渲染后调用,只在客户端。之后组件已经生成了对应的DOM结构,可以通过this.getDOMNode()来进行访问。 如果你想和其他JavaScript框架一起使用,可以在这个方法中调用setTimeout, setInterval或者发送AJAX请求等操作(防止异部操作阻塞UI)。

  • componentWillReceiveProps 【接收prop后调用】在组件接收到一个新的 prop (更新后)时被调用。这个方法在初始化render时不会被调用。

  • shouldComponentUpdate 【帮助解决一些性能上的问题】返回一个布尔值。在组件接收到新的props或者state时被调用。在初始化时或者使用forceUpdate时不被调用。 可以在你确认不需要更新组件时使用。

  • componentWillUpdate 在组件接收到新的props或者state但还没有render时被调用。在初始化时不会被调用。

  • componentDidUpdate 在组件完成更新后立即调用。在初始化时不会被调用。

  • componentWillUnmount 【做一些清除的动作】在组件从 DOM 中移除的时候立刻被调用。

官方api文档:https://reactjs.org/docs/react-component.html

Goto: 生命周期流程图

Mounting 第四个componentDidMount常用。

Updating 第四个componentDidUpdate常用。

其他不太常用。

附加题:React Native 中 component 生命周期

(1) 这里的 app 代表挂载点:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="app"></div> <script type="text/babel">
ReactDOM.render(
<h1>hello world</h1>,
document.getElementById("app")
)
</script>
<script src="https://cdn.bootcss.com/react/16.2.0/umd/react.development.js"></script>
<script src="https://cdn.bootcss.com/react-dom/16.2.0/umd/react-dom.development.js"></script>
<script src="https://cdn.bootcss.com/babel-standalone/7.0.0-beta.3/babel.js"></script>
</body>
</html>

(2) react 应用最好用的脚手架 create-react-app

  • 创建项目
  1. 先安装nodejs
  2. 再安装create-react-app这条命令
  3. 之后通过该命令生成项目hello-react

结果:生成了如下默认的文件,也就是默认空项目。

  • 下一步导航提示

  • 开启开发环境的服务器

yarn start ----> 开启3000端口,开始默认的react页面。

  • 绑定到静态文件

yarn build ----> 得到如下两条命令提示

yarn global add serve
serve -s build

相继执行两命令,得到如下图:

默认监听在5000端口,貌似跟上述的端口3000显示的是一样的内容。

以上,便是生成一个新项目到运行的过程。

  • 运行现成项目

$ npm start

unsw@unsw-UX303UB$ npm start

> hello-react@0.1.0 start /media/unsw/CloudStorage/Linux-pan/ExtendedTmpSpace/Android-Workplace/android-and-ml/React-Native/demo-react/rails365/hello-react-master
> react-scripts start Starting the development server... Compiled successfully! You can now view hello-react in the browser. Local: http://localhost:3000/
On Your Network: http://10.248.169.134:3000/ Note that the development build is not optimized.
To create a production build, use yarn build.

$ npm restart

unsw@unsw-UX303UB$ npm restart

> hello-react@0.1.0 start /media/unsw/CloudStorage/Linux-pan/ExtendedTmpSpace/Android-Workplace/android-and-ml/React-Native/demo-react/rails365/hello-react-master
> react-scripts start Starting the development server... Compiled successfully! You can now view hello-react in the browser. Local: http://localhost:3000/
On Your Network: http://10.248.169.134:3000/ Note that the development build is not optimized.
To create a production build, use yarn build.

(3) bootstrap好看的组件

首先

Bootstrap是美国Twitter公司的设计师Mark Otto和Jacob Thornton合作基于HTML、CSS、JavaScript 开发的简洁、直观、强悍的前端开发框架,使得 Web 开发更加快捷。

Bootstrap提供了优雅的HTML和CSS规范,它即是由动态CSS语言Less写成,就是个“界面工具集”。

关于bootstrap文件包简单介绍

文件名称:

1. bootstrap.css
2. bootstrap.min.css
3. bootstrap-responsive.css
4. bootstrap-responsive.min.css
5. bootstrap.js
6. bootstrap.min.js

解释:

1. bootstrap.css 是完整的bootstrap样式表,未经压缩过的,可供开发的时候进行调试用
2. bootstrap.min.css 是经过压缩后的bootstrap样式表,内容和bootstrap.css完全一样,但是把中间不必要的空格之类的东西都删掉了,所以文件大小会比bootstrap.css小,可以在部署网站的时候引用,如果引用了这个文件,就没必要引用bootstrap.css了
3. bootstrap-responsive.css 这个是在对bootstrap框架应用了响应式布局之后所需要的CSS样式表,如果你的网站项目不准备做响应式设计,就不需要引用这个CSS。
4. bootstrap-responsive.min.css 和bootstrap.min.css的作用是一样的,是bootstrap-responsive.css的压缩版
5. bootstrap.js 这个是bootstrap的灵魂所在,是bootstrap的所有js指令的集合,你看到bootstrap里面所有的js效果,都是由这个文件控制的,这个文件也是一个未经压缩的版本,供开发的时候进行调试用
6. bootstrap.min.js 是bootstrap.js的压缩版,内容和bootstrap.js一样的,但是文件大小会小很多,在部署网站的时候就可以不引用bootstrap.js,而换成引用这个文件了~~

对于Bootstrap的认识,看来需要将菜鸟教程概览一遍:Bootstrap 教程

其次

代码:https://github.com/hfpp2012/hello-react

  • 安装react开发插件:React Developer Tools

查看组件的层次、各个组件的Props、States等信息,演示如下:

app.js的内容,也就是标签<app>实际挂在的地方。

render() {
const user = {
name: "Anna",
hobbies: ["Sports", "Reading"]
}
let homeCmp = "";
if (this.state.homeMounted) {
homeCmp = (
<Home
name={"Max"}
initialAge={12}
user={user}
greet={this.onGreet}
changeLink={this.onChangeLinkName.bind(this)}
initialName={this.state.homeLink}
/>
);
}
return ( /**
* 一段普通的html内容
*/
<div className="container">
<div className="row">
<div className="col-xs-1 col-xs-offset-11">
<Header homeLink={this.state.homeLink} />
</div>
</div>
<div className="row">
<div className="col-xs-1 col-xs-offset-11">
<h1>Hello !!</h1>
</div>
</div>
<div className="row">
<div className="col-xs-1 col-xs-offset-11">
{homeCmp}
</div>
</div>
<hr />
<div className="row">
<div className="col-xs-1 col-xs-offset-11">
<button onClick={this.onChangeHomeMounted.bind(this)} className="btn btn-primary">(Un)mount Home Component</button>
</div>
</div>
</div> );
}
  • 利用BootCDN写组件 - CDN 加速服务

稳定、快速、免费的前端开源项目 CDN 加速服务。

<!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">
<!--
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<link href="https://cdn.bootcss.com/bootstrap/4.0.0-beta.2/css/bootstrap.css" rel="stylesheet">  // insert this url.

(4) 多个组件的开发

这里有两个独立的组件,利于项目分工:

1. Header.js

2. Home.js

然后在App.js中引用了这两个组件。

(5) 输出动态数据

略,没啥可说的。

(6) 组件间消息传递

自定义控件,如何获得组件的“参数”和组件内部的“子组件”?

父组件

import React, { Component } from 'react';

import Header from './components/Header';
import Home from './components/Home'; class App extends Component {
render() {
const user = {              // <---- 这是一个对象
name : "Anna",
hobbies: ["Sports", "Reading"]   // <---- 遍历是需要map
}
return (
<div className="container">
<div className="row">
<div className="col-xs-1 col-xs-offset-11">
<Header />
</div>
</div>
<div className="row">
<div className="col-xs-1 col-xs-offset-11">
<h1>Hello !!</h1>
</div>
</div>
<div className="row">
<div className="col-xs-1 col-xs-offset-11">
<Homename={"Max"} age={12} user={user}>  // <---- 向组件传入三个参数,通过prop作为媒婆
<p>I am child</p>   // <---- 这算是Home的Children节点
</Home>
</div>
</div>
</div>
);
}
} export default App; 

为了防止参数的自动类型转化,可以执行参数强制检查的策略。

例如,希望是数字,但可能传入子组件后,自动变成了字符串,搞得我们没有办法拿来做算术运算了呢。

子组件

import React, { Component } from 'react';

import PropTypes from 'prop-types';        // <---- 类型转化

export default class Home extends Component {
render() {
return (
<div className="container">
<div className="row">
<div className="col-xs-1 col-xs-offset-11">
<div>your name is {this.props.name}, your age is {this.props.age}</div>
<div>
<h4>hobbies</h4>
<ul>
{this.props.user.hobbies.map((hobby, i) => <li key={i}>{hobby}</li>)}
</ul>
</div>
<div>{this.props.children}</div>  // <---- 此处获得了"I am child"
</div>
</div>
</div>
);
}
} /**
* 在此定义参数的类型
*/
Home.propTypes = {
name: PropTypes.string,
age: PropTypes.number,
user: PropTypes.object,
children: PropTypes.element.isRequired
};

(7) 事件定义、触发

注意:以下方案只是改变了this.age的值,但没有及时的使UI渲染。

export default class Home extends Component {
constructor(props) {
super(props);
this.age = this.props.age;
} onMakeOlder() {
this.age += 3;
console.log(this);
} render() {
return (
<div className="container">
<div className="row">
<div className="col-xs-1 col-xs-offset-11">
<div>your name is {this.props.name}, your age is {this.props.age}</div> <button onClick={() => {this.onMakeOlder()}} className="btn btn-primary">Make me older</button>
{this.onMakeOlder.bind(this)} // --> 这个方案需要写bind,费事儿
</div>
</div>
</div>
);
}  
}

(8) state属性及时渲染

因为:  
constructor(props) {
super(props);
this.state = {
age: props.initialAge
}
} 所以:
{this.props.age} 变为 {this.state.age}
----------------------------------------- 
  onMakeOlder() { 
   this.setState({
     age: this.state.age + 3
   })
}

(9) 虚拟DOM

使用的是DIFF Algorithm。

(10) 无状态组件

Ref: 基础篇 #10 无状态组件

简单的代码重构,如下。

import React from 'react';

(1)
export class Header extends Component {
render() { /**
* <好处>
* 不用再声明类
* 不要显式声明this关键字
* 更简洁,占用内存更小,可以写成无副作用的纯函数
*/ (2)
const Header = (props) => { ----------------------------------------------------
return (
<div className="container">
<div className="row">
<div className="col-xs-1 col-xs-offset-11">
<h1>Header</h1>
</div>
</div>
</div>
);
}; export default Header;

无状态组件

1. 无需state, 不处理用户的输入,组件所有的数据都是依赖props传入。

2. 不需要用到生命周期函数。

高阶组件HOC

会返回组件的组件,Redux就是一个实现例子,可处理状态。

(11) 子组件向父组件传值

import React, { Component } from 'react';

import Header from './components/Header';
import Home from './components/Home'; class App extends Component {
onGreet(age) {
alert(age);
} render() {
const user = {
name: "Anna",
hobbies: ["Sports", "Reading"]
}
return (
<div className="container">
<div className="row">
<div className="col-xs-1 col-xs-offset-11">
<Header />
</div>
</div>
<div className="row">
<div className="col-xs-1 col-xs-offset-11">
<h1>Hello !!</h1>
</div>
</div>
<div className="row">
<div className="col-xs-1 col-xs-offset-11">
<Homename={"Max"} initialAge={12} user={user} greet={this.onGreet} />  // onGreet作为参数传递给子组件
</div>
</div>
</div>
);
}
} export default App;

  

实验子组件调用父组件的“方法”

Home.js,其中的Greet button触发了App组件中的函数,是怎么做到的?

  /**
* 1. 点击 子组件的button, 点击后触发 该函数handleGreet
* 2. 该函数其实是个父附件的函数指针
* 如此,子组件通过父组件给他的一个接口从而改变了父组件自身
*/ handleGreet() {
this.props.greet(this.state.age)  // 其实就是父组件中的onGreet
}
  render() {
return (
<div className="container">
<div className="row">
<div className="col-xs-1 col-xs-offset-11">
<div>your name is {this.props.name}, your age is {this.state.age}</div>
<p>Status: {this.state.status}</p>
<button onClick={() => {this.onMakeOlder()}} className="btn btn-primary">Make me older</button>
<hr />
<button onClick={this.handleGreet.bind(this)} className="btn btn-primary">Greet</button>  
</div>
</div>
</div>
);
}
} Home.propTypes = {
name: PropTypes.string,
age: PropTypes.number,
user: PropTypes.object,
greet: PropTypes.func  // <---- 添加对应的参数检测
};
  • 为何要bind(this)? 

Ref: 为什么React事件处理函数必须使用Function.bind()绑定this?

经典考题

let obj = {
tmp: 'Yes!',
testLog: function(){
console.log(this.tmp);
}
};
obj.testLog();  # 正常显示‘yes!'
-------------------------------
let tmpLog = obj.testLog;
tmpLog(); # 过度了一下,然后testLog内部的this就变成了”全局this",在这里也就是window

思考:毕竟每一个对象都有自己的this,tmpLog虽然被设置成为了obj.testLog,但自己仍然保留了自己的this。

背后的原理

React跟原生JavaScript的事件绑定区别有两点,其中第二点就是:

在React(或者说JSX)中,传递的事件参数不是一个字符串,而是一个实实在在的函数:

这样说,React中的事件名(上图中的onClick)就是我所举例子中的中间变量,React在事件发生时调用onClick,由于onClick只是中间变量,所以处理函数中的this指向会丢失;

为了解决这个问题,我们需要在实例化对象的时候,需要在构造函数中绑定this,使得无论事件处理函数如何传递,它的this的指向都是固定的,固定指向我们所实例化的对象。

(12) 子组件之间的传值

子组件 Home 与 header 间的通信,该怎么搞? ---- 当然是通过父组件”搭桥“。

父组件App.js

import React, { Component } from 'react';

import Header from './components/Header';
import Home from './components/Home'; class App extends Component {
constructor() {
super();
this.state = {
homeLink: "Home"
}
} onGreet(age) {
alert(age);
} onChangeLinkName(newName) {  // (5) 实际干活的地方,也就是改变了state
this.setState({
homeLink: newName
})
} render() {
const user = {
name: "Anna",
hobbies: ["Sports", "Reading"]
}
return (
<div className="container">
<div className="row">
<div className="col-xs-1 col-xs-offset-11">
<HeaderhomeLink={this.state.homeLink} />    // (6) 在这里,homelink对于Header子组件就是一个参数;参数被另一个子组件改变,之后,state改变,自然会触发这个子组件UI更新
</div>
</div>
<div className="row">
<div className="col-xs-1 col-xs-offset-11">
<h1>Hello !!</h1>
</div>
</div>
<div className="row">
<div className="col-xs-1 col-xs-offset-11">
<Home
name ={"Max"}
initialAge={12}
user ={user}
greet ={this.onGreet}
changeLink={this.onChangeLinkName.bind(this)}  // (4) app 给子组件传递个“函数指针“,改变的state其实是属于父组件的东西(state),homelink是属于父组件
/>
</div>
</div>
</div>
);
}
} export default App;

子组件Header.js

Jeff: 作为父组件的一个子部分,看似return出html就好了,没用到render。

只需要显示,所以就搞成”无状态组件“就好啦,不需要render。

import React from 'react';

const Header = (props) => {
return (
<div className="container">
<div className="row">
<div className="col-xs-1 col-xs-offset-11">
<h1>{props.homeLink}</h1>
</div>
</div>
</div>
);
}; export default Header;

子组件Home.js

Jeff: 有交互,所以需要render。

import React, { Component } from 'react';

import PropTypes from 'prop-types';

export default class Home extends Component {
constructor(props) {
super(props);
this.state = {
age: props.initialAge,
status: 0,
homeLink: "Changed Link"    // (3) 兄弟子组件获得的改变值,也就是父组件为他俩搭的桥
}
setTimeout(() => {
this.setState({
status: 1
})
}, 3000)
} onMakeOlder() {
this.setState({
age: this.state.age + 3
})
} handleGreet() {
this.props.greet(this.state.age)
} onChangeLink() {
this.props.changeLink(this.state.homeLink);  // (2) 执行了“指针函数” from 父组件;注意参数,子组件会将input的值拿来作为这里的value。
} render() {
return (
<div className="container">
<div className="row">
<div className="col-xs-1 col-xs-offset-11">
<div>your name is {this.props.name}, your age is {this.state.age}</div>
<p>Status: {this.state.status}</p>
<button onClick={() => {this.onMakeOlder()}} className="btn btn-primary">Make me older</button>
<hr />
<button onClick={this.handleGreet.bind(this)} className="btn btn-primary">Greet</button>
<hr />
<button onClick={this.onChangeLink.bind(this)} class="btn btn-primary">Change Header Link</button>  // (1) 点击触发
</div>
</div>
</div>
);
}
} Home.propTypes = {
name: PropTypes.string,
age: PropTypes.number,
user: PropTypes.object,
greet: PropTypes.func
};

 (13) 双向数据绑定

在以上的基础上,加了input框;

这里会读入“输入框”的内容,然后再改写Header的内容。

<input
  type ="text"
  defaultValue={this.props.initialName}
  value ={this.state.initialName}
  onChange ={(event) => this.onHandleChange(event)}  // (1) 只是改变了子组件home内部的一个state;奇怪,这里的event代表了什么?
/>
<button onClick={this.onChangeLink.bind(this)} className="btn btn-primary">Change Header Link</button> // (2) 点击后,读出这个home内部的state,然后改写父组件的state

event 代表了什么?event.target可获得事件涉及组件的属性。

  onHandleChange(event) {
this.setState({
homeLink: event.target.value
})
}

 (14) 组件生命周期

git clone https://github.com/hfpp2012/hello-react.git

组件的代码复用

class Show中没有state,所以课省略掉constructor,系统会自动采用默认的。

下图也展示了组件的可复制性,方便代码复用。

[React] 09 - Tutorial: components的更多相关文章

  1. [React] 10 - Tutorial: router

    Ref: REACT JS TUTORIAL #6 - React Router & Intro to Single Page Apps with React JS Ref: REACT JS ...

  2. 【转】Facebook React 和 Web Components(Polymer)对比优势和劣势

    原文转自:http://segmentfault.com/blog/nightire/1190000000753400 译者前言 这是一篇来自 StackOverflow 的问答,提问的人认为 Rea ...

  3. Facebook React 和 Web Components(Polymer)对比优势和劣势

    目录结构 译者前言 Native vs. Compiled 原生语言对决预编译语言 Internal vs. External DSLs 内部与外部 DSLs 的对决 Types of DSLs - ...

  4. [React Fundamentals] Composable Components

    To make more composable React components, you can define common APIs for similar component types. im ...

  5. [React] Higher Order Components (replaces Mixins)

    Higher order components will allow you to apply behaviors to multiple React components. So the idea ...

  6. [React] React Router: Named Components

    In this lesson we'll learn how to render multiple component children from a single route. Define a n ...

  7. [React] 08 - Tutorial: evolution of code-behind

    有了七篇基础学习,了解相关的知识体系,之后便是系统地再来一次. [React] 01 - Intro: javaScript library for building user interfaces ...

  8. [React] Write Compound Components

    Compound component gives more rendering control to the user. The functionality of the component stay ...

  9. [React] Break up components into smaller pieces using Functional Components

    We are going to ensure our app is structured in a clear way using functional components. Then, we ar ...

随机推荐

  1. python 安装pip setuptools

    注意操作前提一定要使用管理员方式运行 python目录要完全允许控制 windows7 下 0.先安装python2.7.13 32位:https://www.python.org/ftp/pytho ...

  2. SSH三大框架的原理和核心(转)

    一.Struts2的工作流程:1.用户在客户端发起请求,客户端会初始化一个servlet容器请求:2.servlet容器把请求会传递给context容器,context容器找到目标web工程.3.进行 ...

  3. 使用uploadify上传图片时返回“Cannot read property 'queueData' of undefined”

    在使用uploadify插件上传图片时,遇到一个比较坑的错误:上传时提示“Cannot read property 'queueData' of undefined”. 遇到这个问题有点无语,因为这个 ...

  4. python+requests接口自动化完整项目设计源码

    前言 有很多小伙伴吵着要完整的项目源码,完整的项目属于公司内部的代码,这个是没法分享的,违反职业道德了,就算别人分享了,也只适用于本公司内部的业务. 所以用例的代码还是得自己去一个个写,我只能分享项目 ...

  5. anaconda3/lib/libcrypto.so.1.0.0: no version information available (required by wget)

    Solution: sudo ldconfig /lib/x86_64-linux-gnu/ #you need to use the libcrypto.so from /lib/x86_64-li ...

  6. 【OpenCV学习】计算两幅图像的重叠区域

    问题描述:已知两幅图像Image1和Image2,计算出两幅图像的重叠区域,并在Image1和Image2标识出重叠区域. 算法思想: 若两幅图像存在重叠区域,则进行图像匹配后,会得到一张完整的全景图 ...

  7. 在SpringBoot中使用热部署(DevTools)

    一.简介 有时候我们开发完SpringBoot项目后,启动运行.但是经常发现代码需要反复修改,然后修改部分内容后需要再启动....这样太费时了,热部署就是用来解决这一问题.让你修改完代码后,能自动执行 ...

  8. Android studio3.1.3 打包jar,混淆

    最近公司需要将数据进行打包提供给用户,需要我们提供数据解析的jar给用户,为了防止数据格式的泄露,需要进行混淆.这里记录一下封装jar并混淆的过程. 1.创建module 之后创建了几个需要演示混淆的 ...

  9. Nginx 设置域名转向配置

    #运行用户 #user www-data; #启动进程,通常设置成和cpu的数量相等 worker_processes 2; #全局错误日志及PID文件 error_log logs/error.lo ...

  10. ab压力测试工具的简单使用

    ab是一种用于测试Apache超文本传输协议(HTTP)服务器的工具.apache自带ab工具,可以测试 apache.IIs.tomcat.nginx等服务器 但是ab没有Jmeter.Loadru ...