React全家桶
官网
React:用于构建用户界面的JavaScript库。
React是啊一个将数据渲染为HTML视图的开源JavaScript库。
Facebook开发且开源。
React核心就是不要自己操作DOM,让React帮忙,还有虚拟DOM,diff等。
以前的基本操作
1、发送请求获取数据
2、处理数据(过滤、整理格式等)
3、操作DOM呈现页面
为什么要学React
1、原生JS操作DOM繁琐、效率低(DOM-API操作UI)。
2、使用JS直接操作DOM,浏览器会进行大量的重绘重排。
3、原生JS,没有组件化编码方案(不是模块化,模块化就是写成a.js啥的,但是组件化其实就是结构样式都拆出来css等),代码复用率低。
React特点
1、采用组件化模式、声明式编码、提高开发效率以及组件复用率。
2、在React Native中可以使用React语法进行移动端开发。
3、使用虚拟DOM+优秀的Diffing算法,尽量减少与真实DOM的交互,最小化页面重绘。
不用组件的流程
1、创建虚拟DOM
2、渲染虚拟DOM到页面
Js和Jsx
Js和Jsx都能创建虚拟DOM,然后放到容器DOM节点。
但是Js通过React.createElement,需要嵌套创建,麻烦。
Jsx可以直接跟h5一样写标签,还能看清层级结构,且不需要加引号。
Jsx语法规则
JavaScript XML,类似于XML的JS扩展语法。
XML:早期用于存储和传输数据。后面被Json取代了。
parse:快速把Json字符串解析成js对应的数组和对象
stringfy:快速把Js对应的数组和对象解析成字符串
Jsx语法规则
在Jsx中,写的都是Jsx标签,会被转成html标签。
1、定义虚拟DOM时,不要写引号。
2、标签中混入JS表达式时要用{},引入css啥的不需要的。
3、样式的类名指定不要用class,要用className。
4、内联样式。正常的css中style,是style=”color: orange; width: 100px;”。然后在Jsx中的style,不应该写引号,而是两个{ {key: value} }花括号。外面花括号表示你要写js表达式,里面的花括号表示,你要写一个js对象。
5、虚拟DOM必须只有一个根标签,要写两个,用div标签包一下。
6、标签必须闭合,没有标签体么,自闭合。
7、标签首字母
(1)若小写字母开头,则将该标签转为html中同名元素。若html中无该标签对应的同名元素,则报错。
(2)若大写字母开头,react就会去渲染对应的组件(component),若组件没有定义,则报错。
8、表达式、语句(代码)。
在虚拟DOM中,只能写表达式。
(1)表达式:一个表达式,会产生一个值,可以放在任何一个需要值的地方。
1 | (1) a |
(2)语句(代码):
1 | (1) if () {} |
此处abc等等编号是视频中没有写的,自己写一下。
a、虚拟DOM,each child in a list should have a unique key。
1 | { |
b、给react数组,可以帮忙自动遍历,如果给他一个对象,是遍历不了的。那么我自己遍历呗,但是貌似不能用for循环。
虚拟DOM和真实DOM
虚拟DOM:
1、Js里面的一个普通的一般对象,Object
2、虚拟DOM比较轻,真实DOM比较“重”,因为虚拟DOM是React内部在用,无需真实DOM那么多的属性。
3、虚拟DOM最终会被React转化为真实DOM,呈现在页面上
断点debugger
真实DOM通过console.log打印出来,浏览器直接打出标签的,可以加断点,debugger。
组件与模块
模块
(1):向外提供特定功能的js程序,一般就是一个js文件。
(2):为什么要拆成膜快:随着业务逻辑增加,代码越来越多且复杂。
(3):作用:复用js,简化js的编写,提高js运行效率。
模块化:当应用的js都以模块来编写,这就是一个模块化的应用。
组件
(1)用来实现局部功能效果的的代码加资源的集合:html、css、js、img、video、font等等。
(2)为什么:一个界面的功能更复杂。
(3)复用编码,简化项目编码,提高运行效率。
组件化:当应用是以多组件的方式实现,这个应用就是一个组件化的应用。
React面向组件编程
函数式组件
类式组件
组件首字母大写。
函数式组件
用函数式组件的流程
1、创建函数式组件
2、渲染组件到页面
a、首字母必须大写。
b、函数必须有返回值。
c、ReactDOM.render里面必须有组件标签,不能直接写组件名字。
1 | // 1、创建函数式组件 |
类式组件
复习类相关知识
总结:
1、类中的构造器不是必须写的,要对实例进行一些初始化的操作,如添加指定属性才行
2、如果A类继承了B类,且A类中写了构造器,那么A类构造器中的super必须要调用。
3、类中所定义的方法,都是放在了类的原型对象上,供实例去使用。
1 | // 创建一个Person类 |
类式组件
原型、自身实例
非常注意,原型,与,自身实例,是两个东西。可以console.log实例打印出来看。
1 | // 1、创建类式组件,类名就是组件名,需要继承,需要写render,render一定要有返回值 |
简单组件和复杂组件
有状态的是复杂组件
组件实例的三大核心属性
类组件才有,因为函数式组件根本没有实例。最新版提出了hooks,函数式组件貌似也有了。
1、state
2、props
3、refs与事件处理
核心属性之一:state
(1)state是组件对象的最重要的属性,值是对象(可以包含多个key-value组合)
(2)组件被称为“状态机”,通过更新组件的state来更新对应的页面显示(重新渲染组件)
注意:
(1)组件中的render方法中的this为组件实例对象。
(2)组件自定义的方法中this为undefined,如何解决?
a. 强制绑定this,通过函数对象的bind()
b. 箭头函数
(3)状态数据,不能直接修改或更新
组件的数据存在状态里,数据的改变就会去懂页面的展示。
1 | class Weather extends React.Compomnent { |

setState
修改状态,不能直接把状态拿到然后赋值,需要setState。
类中的方法,this指向
1 | class Person { |
这是state的一开始的方式
1 | class Weather extends React.Component() { |
然后简写:
1、类中可以直接写赋值语句,这样的话是写死的,固定。所以可以简写。
所以,要用赋值语句的形式 + 箭头函数
1 | class Weather extends React.Component() { |
这是简写:
1 | class Weather extends React.Component() { |
核心属性二:props
state得自己初始化。
组件标签的所有属性都保存在props中。
1、通过标签属性从组件外向组件内传递变化的数据
2、注意:组件内部不要修改props数据
简单用法
1 | class Person extends React.Component { |
批量传递props
展开运算符 …
1 | // 1、展开运算符 ... |
批量传递props,批量传递标签属性
1 | // 批量传递props,批量传递标签属性 |
props限制属性类型,默认等
需要依赖包,prop-types.js,用于对组件标签属性进行限制
1 | // 对标签属性进行类型、必要性的限制 |
简写
对状态初始化,对标签限制、指定默认值,都放到类的里面去。前面是放在了外侧
1 | class Person extends React.Component { |
构造器
通常在React中,构造函数仅用于两种情况。
1、通过给this.state赋值对象来处理话内部
2、为事件处理函数绑定实例
1 | // 不过这两个都可以通过赋值语句不写在构造器里面 |
注意:
1、在React组件挂在之前,回调用它的构造函数,在为ReactComponent子类实现构造函数时,应在其他语句之前调用super(props)。否则,this.props在构造函数中可能会出现为定义的bug。
2、在costructor函数中不要调用setState方法。如果组件需要使用内部state,直接在构造函数中为this.state赋值初始state。
1 | class Person extends React.Component { |
函数式组件:props
函数没有state、refs,但是有props,因为它能接收参数。
函数式,最新属性,hooks可以
1 | function Person(props) { |
核心属性之三:refs
组件内的标签可以用ref来标识自己。感觉其实跟id差不多,但是更方便。
1、字符串形式ref
已经要被遗弃了。过时,因为string类型的ref会出现一些问题:会有一些效率问题,写多了效率不高。
1 | class Demo extends React.Component { |
注意,此处的ref,ref是当前所处的节点。
可以通过上面的打印,看到refs这一个属性,是一个keyvalue对,收集了这个组件内的所有ref。
其实可以不用上面的document.getElementById(‘input1’),
可以直接用this.refs.input1访问。访问到的事,虚拟DOM转成真实DOM后的一个真实的节点。
2、回调形式的ref
1 | // 1、回调。可以先这样看看这个a是什么,a是input这个节点本身 |
细节:回调函数,调用次数问题。
内联函数(应该就是闭包的意思),就是直接把函数丢在里面,而不是单独写出来有方法名啥的。
如果ref回调函数以内联函数的方式定义,在更新过程中(不是第一次上来的时候)会被执行两次,第一次传入参数null,然后第二次回传入参数DOM元素。这是因为在每次渲染的时候会创建一个新的函数实例(因为内联,所以重新创建函数实例),所以React清空旧的ref并且设置新的。通过将ref的回调函数定义成class的绑定函数的方式可以避免上述问题(如下),但是大多数情况下都是无关紧要的。
组件的更新次数是1+n次,第一次是初始化,n次是状态的更新次数。
1 | const saveInput = (currentNode) => { |
3、createRef
一个容器存储一个,可以创建多个容器。
注意这个,这些都是class组件,我外面没写。
1 | // React.createRef调用后可以返回一个容器,容器可以存储被ref所标识的节点 |
4、ref总结
条件允许下,避免字符串形式的ref。
回调形式稍微麻烦一点,更新被调用两次其实也没有太大问题。
createRef,创建容器,把容器放在实例自身,然后当前节点存进去,然后可以使用。
react应用(基于react脚手架)
前端:Angulat、React、Vue都有自己的脚手架
1、包含了所需要的配置(语法检查、jsx编译、devServer…),下载好了所有相关以来,可以直接运行一个简单效果。
2、react提供了一个用语创建react项目的脚手架库:create-react-app
3、项目的整体技术架构:react+webpack+es6+eslint
4、使用脚手架开发的项目特点:模块化、组件化、工程化
创建项目并启动
1、全局安装库:sudo npm install -g create-react-app
2、切换到想创建项目的目录,使用命令:create-react-app react-staging
3、进入项目文件夹:cd react-staging
4、启动项目:npm start
脚手架,各个文件介绍
public文件夹一般存储静态资源文件,页面样式公共图片
浏览器只认识html、css、js。写的其他的,都会被转成这些。
应用加壳技术,做应用夹壳,配置文件在这里manifest.json。应用加壳技术就是把网页包装成iOSapp或者Androidapp。这样就是一套代码,两类客户端复用了。
robots.txt爬虫规则文件,别人爬虫的时候,可以定一些规矩,什么能爬取,什么不能爬取。
html.index文件
整个项目里面只有一个index.html文件,没有其他html文件出现。其它需要用组件。
SPA,单页面应用。
1 |
|
setupTests.js,组件测试。第三方库:jest-dom
reportWebVitals.js,实现页面性能的监测,用了这个库:web-vitals
App.js
里面一个组件App.js(函数式组件),App.css。
以后只往 id=”root” 这个容器里面放一个组件App。然后其它的都放在App里面去。所以会给我们写好一个App。
index.js
是一个入口文件。
这里面会把App这个函数是组件挂载到 id=”root” 这个容器里面。
1 | ReactDOM.render( |
文件执行顺序
src下的index.js,引入React、ReactDOM、index.css、App、reportWebVitals,然后ReactDOM渲染,把App这个函数是组件挂载到 id=”root” 这个容器里面。
此处的getElementById会自动去public下的index.html找id=”root”的容器。(webpackpage里面写了让它去这里面找)
然后App组件展示到页面上去了。
一些文件注意:
webpack-dev-server,支撑整个3000端口服务运行。
可以新建一个文件夹components,所有的组件文件都放到这里面去。
components里面对于每一个组件都再创建一个文件夹,比如Hello组件,创建Hello文件夹,然后里面放Hello.js, Hello.css等都是这个组件信息的文件。
文件夹components应该和App.js还有index.jx平级。
如果组件文件夹里面有index.jsx,那么引入的时候,可以不写import …../index中的这个/index,但是感觉这样不太好。
由于有一些js文件是业务功能的,还有一些js文件是组件,怎么区分呢?
1、组件的js文件首字母大写。
2、把js改成jsx,jsx结尾的都是组件。App.js一般不改成jsx,但是改也可以。
React脚手架里面,引入js和jsx文件都不需要写后缀。
默认暴露和分别暴露
1 | // 以下是module.js |
样式模块化
1 | import './index.css' |
样式冲突。不同组件里面有个字的css,但是如果css中写的都是一样的className,因为最后都要汇总到App,所以可能会冲突,下面的会覆盖上面的。
可以直接在样式外面套一个组件名字,比如
1 | .hello { |
样式模块化:
在index.css中间加一个.module,然后引入hello这个“对象”
1 | import hello from '.index.module.css' |
现实中用的少,因为less文件,less是啥?
功能界面的组件化编码流程
1、拆分组件:拆分界面、抽取组件
2、实现静态组件:使用组件实现静态页面效果
3、实现动态组件
3.1、动态显示初始化数据
3.1.1、数据类型
3.1.2、数据名称
3.1.3、保存在哪个组件
3.2、交互(从绑定事件监听开始)
React ajax
4.1 前置说明
1、React本身只关注洁面,并不包含发送ajax请求的代码
2、前端应用需要通过ajax请求与后台进行交互(json数据)
3、React应用中需要继承第三方ajax库(或者自己封装)
4.2 常用的ajax请求库
1、jQuery比较重,如果需要另外引入不建议使用。(jQuery是专门操作DOM的,然而React专门不让操作DOM,而是React帮忙操作DOM。)
2、axios:轻量级,建议使用
(1)封装XmlHttpRequest对象的ajax
(2)promise风格
(3)可以在浏览器端和node服务器端,可以双端运行。
脚手架配置代理(跨域)
create-react-app react-axios-test
在终端:yarn add axios
错误:The engine “node” is incompatible with this module. Expected version “>=16.0.0”. Got “14.19.3”
本来应该升级node的,但我为了省事,直接
yarn config set ignore-engines true
了,直接忽略错误。
见部分代码,其他代码见工程react-axios-test。
1 | import logo from './logo.svg'; |
错误:No ‘Access-Control-Allow-Origin’ header is present on the requested resource.
跨域,ajax请求不允许从10007返回到3000。端口不一样。
端口的请求是成功的,但是返回不来。就是ajax引擎是可以让我发出去的,但是请求返回的时候,ajax引擎把返回拦在了外面。因为跨域了。
解决:搞一个中间服务器(本地的),端口也是3000(react默认端口3000),ajax引擎从3000发送请求给3000端口的中间服务器,中间服务器到10007请求并且拿到返回(这个可以返回,因为没有ajax引擎。),然后给ajax引擎3000端口
2种方法配置这个中间代理。
1、package.json中增加配置
1 | "proxy":"http://http://43.142.73.10:10007" |
这句话一加表示所有转发给localhost的3000的请求都转发给了这个服务器43.142.73.10的10007。
所以发送请求的时候,要请求这个localhost的3000端口。
1 | getWelcomeMessage = () => { |
2、配置多个代理setupProxy.js
1中的缺点很大,如果要请求两个服务器,proxy中可以写两个吗?应该不行。
2的优点是可以配置多个代理,可以灵活的控制请求是否走代理。缺点是配置繁琐,前端请求资源时必须加上前缀。
在src中添加文件,setupProxy.js。不能用ES6写,要用CJS写。
React会找到这个文件,加到webpack的page里面,webpack用的都是node里的语法,写的是CJS。
1 | // setupProxy.js |
1 | // App.js |
打包到生产
1 | npm run build |
之后,会生成一个build文件夹。
1 | npm i serve -g |
全局安装serve,用serve就可以将build的静态文件开一个服务器运行起来。
然后到build文件夹下面(有index.html)运行下面命令就行
1 | serve [文件夹名字] |
Author: Jcwang
Permalink: http://example.com/2023/08/19/React/