[React] 09 - Tutorial: components
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
- 创建项目
- 先安装nodejs
- 再安装create-react-app这条命令
- 之后通过该命令生成项目hello-react
结果:生成了如下默认的文件,也就是默认空项目。
- 下一步导航提示
- 开启开发环境的服务器
yarn start ----> 开启3000端口,开始默认的react页面。
- 绑定到静态文件
yarn build ----> 得到如下两条命令提示
yarn global add serveserve -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.
View Code
$ 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.
View Code
(3) bootstrap好看的组件
首先
Bootstrap是美国Twitter公司的设计师Mark Otto和Jacob Thornton合作基于HTML、CSS、JavaScript 开发的简洁、直观、强悍的前端开发框架,使得 Web 开发更加快捷。
Bootstrap提供了优雅的HTML和CSS规范,它即是由动态CSS语言Less写成,就是个“界面工具集”。
关于bootstrap文件包简单介绍
文件名称:
1. bootstrap.css2. 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的内容。
<inputtype ="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 的全部内容, 来源链接: utcz.com/z/381889.html