目录
3.1 使用create-react-app创建react应用
1. React入门
1.1 React简介
1.1.1 官网
https://reactjs.org/
https://react.docschina.org
用于动态构建用户界面的JS库,只关注于视图。由facebook开源。
1.1.2 特点
声明式编码
组件化编码
React Native 编写原生应用
1.1.3 高效的原因
(1)使用虚拟DOM,不总是直接操作页面真实DOM。
(2)优秀的Diffing算法,最小化页面重绘。
1.2 React的基本应用
1.2.1 例子
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title> hello react</title>
</head>
<body>
<!--准备好一个容器-->
<div id="test"></div>
<!--引入react核心库-->
<script type="text/javascript" src="../js/react.development.js"></script>
<!--引入react-dom, 用于支持react操作DOM-->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!--引入babel, 用于将jsx转为js-->
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel"> /*此处一定要写babel*/
//1. 创建虚拟DOM
const VDOM=<h1> /*此处一定不要写引号,因为不是字符串*/ </h1>
//2.渲染虚拟DOM到页面
ReactDOM.render(VDOM,document.getElementById("test"))
</script>
</body>
</html>
解析:!!!相关js库
(1)react.js: React核心库, ./js/react.development.js
(2)react-dom.js: 提供操作DOM的react扩展库,./js/react-dom.development.js
(3)babel.min.js: 解析JSX语法代码转为JS代码的库,./js/babel.min.js
创建虚拟DOM的两种方式:
(1)纯JS方式(一般不用)
<script type="text/javascript">
//1. 创建虚拟DOM
const VDOM=React.createElement('h1',{id:'title'}, React.createElement('span',{},'Hello, React'))
//2.渲染虚拟DOM到页面
ReactDOM.render(VDOM, document.getElementById("test"))
</script>
(2)JSX方式
<script type="text/babel"> /*此处一定要写babel*/
//1. 创建虚拟DOM
const VDOM=(
<h1 id="title">
<span>Hello, React</span>
</h1>
)
//2.渲染虚拟DOM到页面
ReactDOM.render(VDOM, document.getElementById("test"))
</script>
1.2.2 虚拟DOM和真实DOM
(1)React通过API来创建虚拟DOM
React提供了一些API来创建一种特别的一般js对象:
const VDOM=React.createElement('xx', {id:'xx'}, 'xx')
创建一个简单的虚拟DOM对象, 括号里:类似标签名,{属性名:属性值},标签内容
说明:
虚拟DOM对象最终都会被React转换为真实DOM
编码时只需要操作react的虚拟DOM相关数据,react会转换为真实DOM变化,进而更新页面。
(2)例子:
<script type="text/babel">/*此处一定要写babel*/
//1. 创建虚拟DOM
const VDOM=(
<h1 id="title">
<span>Hello, React</span>
</h1>
)
//2.渲染虚拟DOM到页面
ReactDOM.render(VDOM, document.getElementById("test"))
//真实DOM
const TDOM=document.getElementById('demo')
console.log('虚拟DOM', VDOM);
console.log('真实DOM', TDOM);
debugger;
console.log(typeof VDOM);
console.log(typeof TDOM);
</script>
1.2.3 关于虚拟DOM
(1)本质是Object类型的对象(一般对象)
(2)虚拟DOM比较轻,因为虚拟DOM是React内部在用,无需真实DOM上那么多属性。
(3)虚拟DOM最终会被React转化为真实DOM, 再呈现在页面上.
1.3 React JSX
1.3.1 JSX
(1)定义:
全称:JavaScript XML
是react定义的一种类似于XML的JS扩展语法: JS+XML。
本质是React.createElement(component, props, ...children)方法的语法糖。
(2)作用:
用来简化创建虚拟DOM
(3)语法:
var 对象名 = <标签名>xxxxxx</标签名>
例如:var ele = <h1>Hello JSX</h1>
注意:
它不是字符串,也不是HTML/XML标签
它最终产生的就是一个JS对象
标签名任意:可以是HTML标签,或者其他自己创建的标签 (首字母大写)
标签属性任意: HTML标签属性或者其他
(3)基本语法规则:
遇到<开头的代码,以标签的语法解析: 如果是html同名标签转换为html同名元素。如果是其他标签需要特别解析(自定义标签)。
遇到{ 开头的代码,以JS语法解析: 标签中的js表达式必须用 { }包含。
(4)babel.js 的作用:
将虚拟DOM中的JSX代码,转化为纯JS代码。
浏览器不能直接解析JSX代码,需要babel转译为纯JS的代码才能运行。
只要用了JSX, 都要加上type="text/babel", 声明需要babel来处理。
1.3.2 渲染虚拟DOM (元素)
(1)作用:
将虚拟DOM元素渲染到页面中的真实容器DOM中显示
(2)语法:
ReactDOM.render(virtualDOM, containerDOM)
参数说明:
参数1: 纯JS或者JSX创建的虚拟dom对象
参数2:用来包含虚拟DOM元素的真实DOM元素对象(一般是个div标签,或其他标签容器)
(3)举例:
<!--准备好一个容器-->
<div id="test"></div>
ReactDOM.render(VDOM, document.getElementById("test"))
1.4 模块与组件,模块化与组件化的理解
1.4.1 模块
向外提供特定功能的JS程序,一般就是一个JS文件。
为什么要拆成模块?因为随着业务逻辑增加,代码越来越多且复杂。
作用:复用JS,简化JS的编写,提高JS的运行效率。
1.4.2 组件
用来实现局部功能效果的代码和资源的集合(html/css/image)等等。
为什么要用组件?一个界面的功能很复杂。
作用:复用编码,简化项目编码,提高运行效率。
1.4.3 模块化
当应用的JS都以模块来编写的,这个应用就是一个模块化的应用。
1.4.4 组件化
当应用是以多组件的方式实现,这个应用就是一个组件化的应用。
2. React面向组件编程
2.1 基本理解和使用
函数式组件
类式组件
2.1.1 函数式组件
<script type="text/babel">
//创建函数式组件
function MyComponent(){
console.log(this);
return <h2>我是函数定义的组件(适用于简单组件的定义)</h2>;
}
//渲染组件到页面
ReactDOM.render(<MyComponent />, document.getElementById('test'))
</script>
解析:
(1)这里的this是undefined,因为babel编译后开启了严格模式。
(2)执行渲染语句后发生了什么?
React解析组件标签,找到了MyComponent组件,发现组件是使用函数定义的,随后调用了该函数,将返回的虚拟DOM转为真实DOM, 最后呈现在页面上。
2.1.2 类式组件
<script>
//创建类式组件
class MyComponent2 extends React.Component{
render(){
console.log('render中的this:', this);
return <h2>我是用类定义的组件(适用于复杂组件的定义)</h2>
}
}
//渲染DOM
ReactDOM.render(<MyComponent2 />, document.getElementById('test'))
</script>
解析:
(1)render方法是放在哪儿的?放在MyComponent2的原型对象上,供实例使用。
(2)这里的this是指谁?MyComponent2的实例对象 《=》MyComponent2组件的实例对象
(3)执行渲染语句后发生了什么?
(4)注意:
1)组件名必须首字母大写。
2)虚拟DOM元素只能有一个根元素。
3)虚拟DOM元素必须有结束标签。
(5)渲染类组件标签的基本流程:
1)React内部会创建组件实例对象。
2)调用reander()得到虚拟DOM, 并解析为真实DOM。
3)插入到指定的页面元素内部。
2.2 组件三大核心属性
2.2.1 组件三大属性I:state
(1)什么是state
state是组件对象最重要的属性,值是对象(可以包含多个key-value的组合)。
state组件被称为状态机,通过更新组件的state来更新对应的页面显示(重新渲染组件)。
(2)注意
1)组件中render方法中的this为组件实例对象。
2)组件自定义的方法中this为undefined, 如何解决?强制绑定this, 通过函数对象的bind()。
3)状态数据,不能直接修改或更新,通过set。
!!!复习: 箭头函数: 前端基础(六):ES6
(3)state的简写模式
<script type="text/babel">
class Demo extends React.Component{
constructor(props){ //JS 类中的构造方法
super(props); //super 调用父级构造方法
this.state = {flag:false};
}
render(){
return (
<div>
<h1>今天天气真{this.state.flag?"好":“不好”}</h1>
</div>
)
}
}
</script>
如何理解?
1)render里要用一些参数值,这些值哪儿来?
答:可以考虑往类的实例上加,怎么加?类的构造器就是在创建实例时创建/配置了一些属性。
实例上原本就有一个state属性,那么我们就看怎么往这个state属性上加我们要的属性值。
state是实例的,即this.state。
2)然后在render方法中要用这个值,render里的this指向实例对象,即:this.state.变量...
constructor里的this, 和render里的this, 都指向实例对象。
在constructor里赋值的属性,在render里可以使用。
(4)React 的 setState()方法
1)React中状态state数据不能直接修改或更新,比如这样是错的:this.state.isHot=!isHot
所以,更改state的属性值时,要用一个原型的原型上的方法 setState()。
setState会到state中找相同的key,有则更新。其余原state中其他的key不变,不会被覆盖掉,也不会被清除掉。可以理解为,setState()中的key:value会和 state()里的key:value会进行并集。
只要调用setState, React就会重新调用render方法,从而更新虚拟DOM达到页面内容更新的效果。
2)思考两个问题:
render()方法(固定的)被调用几次? 答:1+n次
changeFlag方法(类中的一般方法)被调用几次? n次。 少了初始化那次,初始化时只是bind了一下,没有被调用执行。
(5)简化代码
1)写构造函数的原因,一是给属性初始化,一个是更改类中方法的this指向。
constructor里的this, 和render里的this, 都指向实例对象。
如果这两个是在构造函数外且在类里,已经有别的实现方式了,那就不需要构造函数了。
2)在类组件中,不一定要在构造器中定义属性变量,也可以直接在类下定义state属性。
class Demo extends React.Component {
state = {flag: true} //可以直接在类中定义state属性
//箭头函数简写: 用箭头函数简写类中的一般方法,箭头函数没有this
//该方法会自动往外找this, 外面就是类/实例。
changeFlag = () => { //类中的一般方法,用箭头函数
let {flag} = this.state;
this.setState({flag: !flag})
}
render() {
const {flag} = this.state
return (
<div>
<h1 onClick={this.changeFlag}>今天天气真{flag ? "好" : "不好"}</h1>
</div>
)
}
}
(6)注意事项
1)React标签中,属性一般用驼峰命名法,因为React更接近JS。
2)React标签中,属性={JS表达式}, 这里JS表达式要用花括号括起来,不能用引号,用了引号的话,就把这个表达式当成字符串。
3)调用函数 (表达式),不带括号的话,返回的是函数。带括号,返回的是函数结果,如果没有return的话,返回的结果是Undefined。
4)<script>的type=“text/babel”时,执行严格模式。此时script标签下直接定义的方法的this, 是undefined。
5)类中写方法(相当于函数),不需要加function,直接方法名。
6)在类的某个方法中,想要调用该类中的其他方法(或属性?)时,要用this.方法名 (区别于JAVA,不能直接使用方法名)。
7)回顾call(), apply() 和bind()的用法,区别。
bind() 被调用时,不立即执行,而是生成/返回一个新函数。新函数的this, 就是bind()方法的第一个参数。
8)类中定义的方法,是挂在原型上的。
2.2.2 组件三大属性II: props
(1)定义
每个组件对象都会有props属性。组件标签的所有属性都保存在props中,将属性值通过props传递,比如传参。即,作用是:通过标签属性从组件外向组件内传递变化的数据。注意:组件内部不要修改props数据。
state和props的区别:
state是类组件内部的事
props是类组件外面的事,传参
(2)举例
//定义类式组件
class Person extends React.Component{
render(){
const {name, age, sex}=this.props; //接收props带的参数,ES6的解构赋值
return(
<ul>
<li>姓名:{name}</li>...
</ul>
)
}
}
//如何渲染:这些组件中的参数打包为props, 进行传参
ReactDOM.render(<Person name="jerry" age={18} sex="male"/>, documemt.getElememtById("test"));
//另一种方式
const p={name:'Kate', age"18, sex:'女'};//JS对象
ReactDOM.render(<Person {...p} />, document.getElementById("test"))
(3)对props中的属性值进行类型限制和必要性限制
Person.propTypes={
name: PropTypes.string.isRequired,
age: PropTypes.number
}
(4)扩展属性:将对象的所有属性通过props传递
<Person {...person} />
(5)默认属性值
Person.defaultProps={
age: 18,
sex: '男'
}
(6)类式组件中的props
1)回顾扩展运算符
2)Let p1 = {name: “zhangsan”, age: 18}
注意:
{…p1}是 React中的语法糖,跟扩展运算符不一样 (看上面一项的例子)。
props传进来的key名是什么,接收端的key名也要一样。
(7)函数式组件中的props
函数式组件无this,因为它没有实例的概念嘛,直接传props。
props属性就是:它收集调用的标签中的所有属性,作为一个对象,传到函数中。
function Person {
const {name, age, sex}=props; //函数式组件的props, 没有this
return(
<ul>
<li>姓名:{name}</li>...
</ul>
)
}
Person.propTypes={
//限制name必传,且为字符串
name: PropTypes.string.isRequired,
sex: PropTypes.string,
age: PropTypes.number,
}
Person.defaultProps={
age: 18,,
sex: '男', //sex默认为男
}
//渲染
ReactDOM.render(<Person name="jerry" />, document.getElementById("test"))
(8)小结
1)React DOM 会将元素和它的子元素与它们之前的状态进行比较,并只会进行必要的更新来使 DOM 达到预期的状态。
2)函数组件:
function Welcome(props) { return <h1>Hello, {props.name}</h1>; }
该函数是一个有效的 React 组件,因为它接收唯一带有数据的 “props”(代表属性)对象与返回一个 React 元素。这类组件被称为“函数组件”,因为它本质上就是 JavaScript 函数。
3)类式组件:
可以使用 ES6 的 class 来定义组件:class Welcome extends React.Component {}
4)什么是props?
它会将 JSX 所接收的属性(attributes)以及子组件(children)转换为单个对象传递给组件,这个对象被称之为 “props”。类似实参。
5)组件名称必须以大写字母开头。
React 会将以小写字母开头的组件视为原生 DOM 标签。
例如,<div /> 代表HTML的div标签,而<Welcome />则代表一个组件,并且需在作用域内使用 Welcome。
6)组件可以在其输出中引用其他组件(组件间的相互调用)。
这就可以让我们用同一组件来抽象出任意层次的细节:按钮,表单,对话框,甚至整个屏幕的内容......在 React 应用程序中,这些通常都会以组件的形式表示
7)可接收 author(对象),text (字符串)以及 date(日期)作为 props。
用花括号括起对象,字符串...
const ele = <Comment auther={{name: 'Harper', avatarUrl: 'www.baidu.com'}} text={"testtest"} date={new Date().toLocaleDateString()} />; //组件中的属性,作为props传参
ReactDOM.render(ele, document.getElementById(‘testing'));
2.2.3 组件三大属性III:refs与事件处理
字符串形式的ref
回调函数形式的ref !!!
createRef 创建ref容器
(1)定义
组件内的标签可以定义ref属性来标识自己。
React里通过ref拿到的节点属性,是真实DOM的节点属性。
在对应的方法中,通过this.refs拿到的是跟虚拟DOM对应的真实DOM里的节点/标签信息。
(2)字符串形式的ref
//类式组件
class Demo extends React.Component {
showData = () => { //箭头函数,this指向当前方法所在作用域下的this
console.log("showData", this)
const {input1} = this.refs //ES6解构函数,获得input1节点/标签
alert(input1.value) //获取input节点信息后,操作
}
showData2 = () => {
const {input2} = this.refs //ES6解构函数,获得input2节点/标签
alert(input2.value); //获取input节点信息后,操作
}
render() {
console.log("render", this)
return (
<div>
<input ref="input1" type="text" placeholder="点击显示左侧数据" />
<button ref="button1" onClick={this.showData}>click me</button>
<br />
<input ref="input2" onBlur={this.showData2} type="text" placeholder="失去焦点显示数据" />
</div>
)
}
}
ReactDOM.render(<Demo />, document.getElementById('test'));
解析:
1)ref属性在实例对象下。而render和showData的this都指向实例对象,所以通过this可以访问到ref属性。
2)ref属性的值,是对象,包含多个键值对,如input1: input
this -> refs -> input1: input, input2:input...
(3)回调函数形式的ref
<script type="text/babel">
class Demo extends React.Component {
showData = () => {
const {input1} = this
alert(input1.value);
}
showData2 = () => {
const {input2} = this
alert(input2.value);
}
render() {
return (
<div>
<input ref={c => this.input1 = c} type="text" placeholder="点击显示左侧数据" />
<button onClick={this.showData}>click me</button>
<br />
<input ref={c => this.input2 = c} onBlur={this.showData2} type="text" placeholder="失去焦点显示数据" />
</div>
)
}
}
ReactDOM.render(<Demo />, document.getElementById('test'));
</script>
解析:
1)参数c代表当前的input节点
2)将这个节点赋给this.input1(名字自己起),然后在showData中的this, 从this里找到input1 (this这里指实例对象,实例对象下就有input1)
this -> input1: input, input2: input...
ref属性在实例对象下。
ref属性的值,是对象,包含多个键值对,如input1: input
工作中用的最多!!!
(4)createRef 创建ref容器
class Demo extends React.Component {
myRef1 = React.createRef()
myRef2 = React.createRef()
showData = () => {
console.log(this)
const input = this.myRef1.current
alert(input.value);
}
showData2 = () => {
const input = this.myRef2.current
alert(input.value);
}
render() {
return (
<div>
<input ref={this.myRef1} type="text" placeholder="点击显示左侧数据" />
<button onClick={this.showData}>click me</button>
<br />
<input ref={this.myRef2} onBlur={this.showData2} type="text" placeholder="失去焦点显示数据" />
</div>
)
}
}
解析
1)this -> myRef1 -> {current: input}
2)说明:React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是“专人专用”的。
(5)事件处理
1)通过onXxx属性指定事件处理函数(注意大小写)。
2)React使用的是自定义(合成)事件,而不是使用的原生DOM事件,为了更好的兼容。
3)React中的事件,是通过事件委托方式处理的(委托给组件最外层的元素),为了高效。
4)通过event.target得到发生事件的DOM元素对象,不要过度使用ref。
2.3 组件的生命周期
2.3.1 定义
组件从创建到死亡,它会经历一些特定的阶段
React组件中包含一系列勾子函数(生命周期回调函数),会在特定的时刻调用
我们在定义组件时,会在特定的生命周期回调函数中,做特定的工作
2.3.2 生命周期流程图(旧)
2.3.3 生命周期流程图(新)
2.3.4 重要的勾子
render:初始化渲染或更新渲染调用
componentDidMount: 开启监听,发送ajax请求
componentWillUnmount: 做一些收尾工作,如清理定时器
2.3.5 即将废弃的勾子
componentWillMount
compoenentWillReceiveProps
compoenentWillUpdate
现在使用会出现警告,下一个大版本需要加上UNSAFE_前缀才能使用,以后可能会被彻底废弃
2.3.6 例子
<script type="text/babel">
class Demo extends React.Component {
state = {opacity: 1}
// 卸载组件
delete = () => {
ReactDOM.unmountComponentAtNode(document.getElementById('test'))
}
// 组件挂载完成,自带,不用调用
componentDidMount(){
console.log("componentDidMount");
this.timer = setInterval(()=>{
let {opacity} = this.state
opacity -= 0.1
if(opacity <= 0){
opacity = 1
}
this.setState({opacity})
}, 200)
}
// 组件将要卸载,自带,不用调用
componentWillUnmount() {
console.log("componentWillUnmount");
clearInterval(this.timer)
}
render() {
console.log("render")
return (
<div>
<h2 style={{opacity: this.state.opacity}}>透明度变化</h2>
<button onClick={this.delete}>卸载组件</button>
</div>
)
}
}
ReactDOM.render(<Demo />, document.getElementById('test'));
</script>
执行顺序:
render初始化一次
-> componentDidMount
-> render多次(每200毫秒setState一次,进而render更新渲染一次,这个动作在componentDidMount定义好)
-> componentWillUnmount (点击卸载组件)
3. React应用(基于React脚手架)
3.1 使用create-react-app创建react应用
3.1.1 react脚手架
(1)什么是xxx脚手架?
用来帮助程序员快速创建一个基于xxx库的模版项目。
包含了所有需要的配置(语法检查,jsx编译,devServer...)。
下载好了所有相关的依赖。
可以直接运行一个简单效果。
(2)react提供了一个用于创建react项目的脚手架库: create-react-app
(3)项目的整体技术架构为: react+webpack+es6+eslint
(3)使用脚手架开发的项目的特点: 模块化,组件化,工程化
3.1.2 创建项目并启动
(1) 安装nodejs
在https://nodejs/org/en/download/ 下载并安装对应系统的安装文件。
检查是否安装成功?cmd中, node -v 和 npm -v 测试。
用cnpm?设置阿里npm镜像源,可以访问https://developer.aliyun.com/mirror/NPM, 按照最新方法安装,也可以直接运行命令: npm install -g cnpm --registry=https://registry.npm.taobao.org
(2)全局安装react脚手架:cnpm i -g create-react-app
(3)切换到想创建项目的目录,创建react项目: create-react-app hello-react (名字随便起)
(4)进入项目文件夹: cd hello-react
(5)启动项目: cnpm start
(6)在浏览器中访问 :http://localohost:3000
3.1.3 脚手架项目结构
3.1.4 功能界面的组件化编码流程
(1)拆分组件:拆分界面,抽取组件
(2)实现静态组件:使用组件实现静态页面效果
(3)实现动态组件
精简后的项目目录