第1章:React入门
1.1.React简介
1.1.1.介绍描述
-
用于动态构建用户界面的 JavaScript 库(只关注于视图)
-
由Facebook开源
实现一个页面需求通常有三步:
1.发送请求获取数据
2.处理数据(过滤、整理格式等)
3.React就是操作DOM呈现页面
。
是一个将数据渲染为HTML视图的开源 JavaScript 库。
1.1.2.React的特点
- 采用
组件化模式
、声明式编码
,提高开发效率及组件复用率
命令式编码:获取节点再改样式;
声明式编码:用特殊语法表达更改的样式,react会帮我们操作dom - React Native 编写原生应用,进行
移动端开发
- 使用
虚拟DOM
+优秀的Diffing 算法
,尽量减少与真实DOM交互
1.1.3.React高效的原因
- 使用虚拟(virtual)DOM, 不总是直接操作页面真实DOM。
- DOM Diffing算法, 最小化页面重绘。
原生JavaScript操作DOM繁琐、效率低(DOM-API操作 UI)
使用JavaScript直接操作DOM,浏览器会进行大量的重绘重排
原生JavaScript没有组件化编码方案,代码复用率低
1.2.React的基本使用
1.2.1.效果
1.2.2.相关js库
- react.development.js:React核心库。
- react-dom.development.js:提供操作DOM的react扩展库。
- babel.min.js:解析JSX语法代码转为JS代码的库。
<!-- 引入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>
1.2.3.创建虚拟DOM的两种方式
-
纯JS方式(一般不用)
-
JSX方式
1.2.4.虚拟DOM与真实DOM
- 虚拟DOM本质是object类型的对象(一般对象)
- 虚拟DOM比较“轻”,真实DOM比较“重”,因为虚拟DOM是React内部在用,无需真实DOM上那么多的属性。
- 虚拟DOM最终会被React转化为真实DOM,呈现在页面上。
// js创建虚拟节点
React.createElement(标签名,标签属性,标签体内容)
1.3.React JSX
全称: JavaScript XML
react定义的一种类似于XML的JS扩展语法: JS + XML
本质是React.createElement(component, props, …children)方法的语法糖
1.3.1.作用:用来简化创建虚拟DOM
- 写法:
var ele = <h1>Hello JSX!</h1>
- 注意1:它不是字符串, 也不是HTML/XML标签
- 注意2:它最终产生的就是一个JS对象
1.3.2.基本语法规则
-
定义虚拟DOM时,不要写引号。
-
标签中混入Js表达式时要用{}。
-
样式的类名指定不要用class,要用className。
-
内联样式,要用style={{key:value}}的形式去写
-
只有一个根标签
-
标签必须闭合
-
标签首字母
若小写字母开头,则将改标签转为htm1中同名元素,若htm1中无该标签对应的同名元素,则报错。
若大写字母开头,react就去渲染对应的组件,若组件没有定义,则报错。
1.3.3.js中的表达式和语句区分
为什么这段代码会报红呢?
因为在react {}包裹的必须是js表达式
才可以,for循环是语句,不是表达式
- 表达式:一个表达式会产生一个值,
左侧用一个变量能接到值
就是表达式,可以放在任何一个需要值的地方- (1).a
- (2).a+b
- (3)demo(1) 如果函数没有返回值会自动返回underfined,所以也是能接到值的
- (4)arr.map()
- (5).function test(){}
- 语句(代码),控制代码走向,没有值
- (1).if(){}
- (2).for(){}
- (3).switch(){case:xxxx}
所以上面的代码改用map方法并返回就没问题了
注意:data数据是数组的形式react会帮我们遍历,如果是对象则不行,控制台报错
1.3.4.babel.js的作用
1)浏览器不能直接解析JSX代码, 需要babel转译为纯JS的代码才能运行
2)只要用了JSX,都要加上type=“text/babel”, 声明需要babel来处理
<body>
<!-- 准备容器 -->
<div id="test"></div>
<!-- 引入babel,用于将jsx转为js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel"> /* 此处一定要写babel */
// 1.创建虚拟DOM
const VDOM = <h1>Hello,React</h1>/* 此处一定不要写引号,因为不是字符串 */
// 2.渲染虚拟DOM到页面
// ReactDOM.render(VDOM,document.getElementById('test'))
</script>
</body>
1.3.5.渲染虚拟DOM(元素)
语法: ReactDOM.render(virtualDOM, containerDOM)
作用: 将虚拟DOM元素渲染到页面中的真实容器DOM中显示
参数说明
1)参数一: 纯js或jsx创建的虚拟dom对象
2)参数二: 用来包含虚拟DOM元素的真实dom元素对象(一般是一个div)
<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>Hello,React</h1>/* 此处一定不要写引号,因为不是字符串 */
// 2.渲染虚拟DOM到页面
ReactDOM.render(VDOM,document.getElementById('test'))
</script>
</body>
1.4.模块与组件、模块化与组件化的理解
1.4.1.模块
把庞大的js文件拆分成多份
- 理解:向外提供特定功能的js程序, 一般一个js文件就是一个模块
- 为什么要拆成模块:随着业务逻辑增加,代码越来越多且复杂。
- 作用:复用js, 简化js的编写, 提高js运行效率
1.4.2.组件
页面某个能复用的区域,将其封装成组件,就是该区域的所有资源代码功能全部打包到一个组件里
- 理解:用来实现局部功能效果的代码和资源的集合(html/css/js/image等等)
- 为什么要用组件: 一个界面的功能更复杂
- 作用:复用编码, 简化项目编码, 提高运行效率
1.4.3.模块化
当应用的js都以模块来编写的, 这个应用就是一个模块化的应用
1.4.4.组件化
当应用是以多组件的方式实现, 这个应用就是一个组件化的应用
第2章:React面向编程
2.1基本理解和使用
2.1.1使用React开发工具调试
在谷歌浏览器安装React调试工具
- 打开谷歌浏览器进入谷歌商店
- 搜索 React Developer Tools,添加到Chrome
- 在扩展程序中查看
- 将该工具固定在浏览器上方(绿色框的图标表示当前网页是否用React编写的,如果是图标则会变亮)
图标橙色,没有经过打包,开发者模式
正常图标,打包并且部署到服务器上线了
5. 开发者模式使用该工具
F12,点击展开箭头
Compontent:网页由多少个组件组成,每个组件拥有的属性
Profiler:记录网站的性能,渲染时间,组件加载时间
2.1.2定义组件
- 函数式组件
<script type="text/babel">
// 1.创建函数式组件(组件名首字母要大写)
function MyCompontent(){
console.log(this) //undefined
return <h2>我是函数式组件</h2>
}
// 2.渲染组件到id为test的标签内部
ReactDOM.render(<MyCompontent/>,document.getElementById('test'))
</script>
在函数组件中如果打印this,改this是undefined
,而不是像我们写js函数一样,this指向window,原因是jsx代码需要经过babel翻译,babel翻译
会开启严格模式
,禁止自定义函数里的this指向window。试一试
执行了ReactDOM.render(<MyCompontent…之后,发生了什么?
- React解析组件标签,标签为大写,找到对应的MyCompontent组件
- 发现组件是使用函数定义的,随后
调用该函数
(我们并没有调用函数,是react帮我们调的),将返回的虚拟DOM转为真实DOM,呈现在页面上
- 类式组件
<script type="text/babel">
// 1.创建类式组件
class MyComponent extends React.Component{
//render是放在MyComponent的原型对象上
render() {
console.log(this) //谁调用this指向谁,此处指向MyComponent的实例对象,也叫组件实例对象
return <h2>我是类式组件</h2>
}
}
// 2.渲染组件到指定容器中
ReactDOM.render(<MyComponent/>,document.getElementById('test'))
</script>
注意:必须要继承React.Component
父类,必须要写render
方法,而且render要有返回值
执行了ReactDOM.render(<MyCompontent…之后,发生了什么?
- React解析组件标签,标签为大写,找到对应的MyCompontent组件
- 发现组件是类定义的,随后
React
帮我们new出来该类的实例对象
,并通过实例对象调用原型对象中的render方法
- 将render返回的虚拟DOM转为真实DOM,随后呈现在页面上
原型链知识点复习:实例对象中都有_proto_(对象原型)属性,该属性指向prototype(原型对象),原型对象中存有类的共享的属性和方法其中的constructor属性指向该类的构造函数,如果在实例对象中的原型对象中找不到对应的方法,则会继续往上查找,直到找到原型对象是Object的为止,如果Object中也没有找到该方法,则会返回null
- 简单组件与复杂组件
有状态(state)的组件为复杂组件,反之为简单组件
2.2. 组件实例三大核心属性state
state是在组件的实例对象中的
2.2.1状态的基本使用
创建一个天气类 Weather,该类继承自 React.Component,则该类的实例身上会有 props、refs、state等的属性。
state 作为一个对象在我们的项目中使用,用于存储类组件内部的数据。
在类Weather 的构造器中,使用this.state 可以获取到 state。初始时state 为 null,我们在构造器中将 state 定义为对象类型,并为其指定若干属性。
<script type="text/babel">
// 1.创建组件
class Weather extends React.Component{
// 构造器
constructor(props) {
super(props)
// 借助构造器初始化状态
this.state = {isHot:true}
}
render(){
// 读取状态
const {isHost} = this.state
return <h1 onClick={demo}>今天天气很{ isHost?'炎热':'凉爽' }</h1>
}
}
// 2.渲染组件
ReactDOM.render(<Weather/>,document.getElementById('test'))
function demo(){
console.log('标题被点击了')
}
</script>
事件绑定: 如果demo函数写了括号,如onClick={demo()},此时demo()是一个表达式,表示调用该函数,函数没有返回值会返回undefined,相当于onClick=undefined,把undefined返回给onClick作为回调,这样的话对应的事件会没有任何效果,所以实际绑定的时候函数不需要写括号
2.2.2类中方法中this指向
- 自定义函数this指向
babel翻译开启了严格模式,禁止自定义函数指向window,所以this指向的是undefined
- 类中的方法this指向
解决这个问题需要将自定义函数,改写成类中的方法,将function去掉,代码写到类中,
但是此时控制台还是报同样的错误this还是undefined,原因是changeWeather是作为onclick的回调
,不是实例对象调用
,是直接调用
的就是window调用,类中的方法默认开启了局部的严格模式
,this不允许指向window,所以this是undefined
解析:通过this将找到weather实例对象原型上的方法,作为onclick的回调,实际上就是吧changeWeather方法作为属性赋值给了onclick,然后直接调用(window调用)的onclick,相当于点击h1标签和直接调用方法而不是通过实例对象调用,类中的方法默认会开启严格模式,不是实例对象调用,this会指向undefined,可以看下面js代码的示例来理解
- 解决类中this指向undefined问题
使用bind方法,将this指向更正为实例对象,并赋值到实例对象自身的方法
2.2.3更改状态setState
- 在构造器中声明setState
通过原型对象找到React.Component组件上的setState()方法
进行更改,React不能直接赋值给state更改
注意:更新状态的逻辑不是整个对象覆盖,而是同名的替换,没有同名的属性保持不变
class Weather extends React.Component{
constructor(props) {
super(props)
this.state = {isHot:true, wind:'微风'}
this.changeWeather = this.changeWeather.bind(this)
}
changeWeather(){
let isHot = this.state.isHot
// 状态必须通过setState就行修改是一种合并的操作,同名的替换掉,没有同名的则保留,setState是React.Component组件上的方法
this.setState ({isHot: !isHot})
// 严重注意:状态不可直接更改,下面这行就是直接更改
// this.state.isHot = !isHot// 错误写法!!
}
}
- 类中方法执行次数分析
- 构造器调用次数:1次
在ReactDOM.render渲染组件时,会帮我们new出Weather组件的示例对象,同时调用构造器,new了几次就调用了几次构造器 - render调用次数:1+n次,1是初始化次数,n是状态更新次数
执行过程先调构造器,构造器调完了实例对象才出来,实例对象出来了才能掉render - changeWeather调用次数:点击的次数
调用的是自身的changeWeather 而不是原型上的changeWeather
- 构造器调用次数:1次
class Weather extends React.Component{
constructor(props) { //调用1次
super(props)
this.state = {isHot:true, wind:'微风'}
this.changeWeather = this.changeWeather.bind(this)
}
changeWeather(){ //点击了几次就调用几次
let isHot = this.state.isHot
this.setState ({isHot: !isHot})
}
render(){ //调用1+n次
const {isHot,wind} = this.state
return <h1 onClick={this.changeWeather}>今天天气很{ isHot?'炎热':'凉爽' },{wind}</h1>
}
}
ReactDOM.render(<Weather/>,document.getElementById('test'))