入门概念
1.什么是React?
-
React 起源于 Facebook 的内部项目, 因为Facebook对市场上所有 JavaScript MVC 框架,都不满意, 就决定自己写一个框架,用来架设 Instagram 的网站
2.什么是框架?
- 框架是一个'半成品',已经对基础的代码进行了封装并提供相应的API, 开发者在使用框架时,可以复用框架中封装好的代码,从而提高工作效率
- 框架就是毛坯房, 已经帮助我们搭建好了基本的架子, 我们只需要拿过来根据我们自己的需求装修即可
3.为什么要学习框架?
- 提升开发效率:
- 对于企业来说,时间就是金钱
- 对于个人来说, 时间≈女朋友
4.为什么要学习React?
- 真香定律(国内技术发展潜规则)
- 国外流行 -> 国内大厂尝试 -> 大厂觉得很香 -> 其它公司觉得也很香
- 国外流行
- 2020 Stackoverflow 全球开发者调研报告中, 最受欢迎框架排第二
- HackerRank 2020 全球开发者调研报告, 程序员最想学习框架排第一
- 国内大厂尝试
- 阿里: www.aliyun.com/
- 斗鱼: www.douyu.com/
- 优酷: youku.com/
- 飞猪: www.fliggy.com/
- 知乎: www.zhihu.com/
- 滴滴: www.didiglobal.com/
- 网易: yuedu.163.com/
- ... ...
- 其它公开觉得也很香
- 猎聘: wow.liepin.com/
- 36氪: 36kr.com/
- 墨刀: www.modao.cc/
- 丁香医生: dxy.com/
- 石墨文档: shimo.im/
- ... ...(江哥怎么知道的?)
5.为什么要学习React?
- 安全可靠
- React是由Facebook来更新和维护, 所以一般不会出现跑路情况
- 思想升华
- React是一个开源项目, 融合了全世界诸多优秀成员的编程思想
- 值得借鉴
- Vue.js设计之初,有很多的灵感来自Angular和React Vue3的很多新特性, 在React中你也能看到它们的身影 () 诸如: Composition API / Fragment / Teleport(Protal)/ Suspense
6.为什么要学习React?
- 面向未来, 迎接5G
- 2013年,React发布之初主要是开发Web页面
- 2015年,Facebook推出了ReactNative,用于开发移动端跨平台(Android/iOS APP) (虽然目前Flutter非常火爆,但是还是有很多公司在使用ReactNative)
- 2017年,Facebook推出ReactVR,用于开发虚拟现实Web应用程序 (随着5G的普及,VR也会是一个火爆的应用场景)
基本使用
react.js和react-dom.js
- react.js包含了React和React-Native所共同拥有的核心代码, 主要用于'生成虚拟DOM'
- react-dom.js包含了针对不同平台渲染不同内容的核心代码, 主要用于'将虚拟DOM转换为真实DOM'
- 简而言之:
- 利用react.js编写界面(创建虚拟DOM)
- 利用react-dom.js渲染界面(创建真实DOM)
<head>
<script src="../react17/react.development.v17.js"></script>
<script src="../react17/react-dom.development.v17.js"></script>
</head>
<body>
<div id="app"></div>
<script>
// 1.创建虚拟DOM
let message = '学习react'
let oDiv = React.createElement('div', null, message)
// 2.将虚拟DOM转换成真实DOM
ReactDOM.render(oDiv, document.getElementById('app'), () => {
console.log('已经将虚拟DOM转换成真实DOM, 已经渲染到界面')
})
</script>
</body>
JSX写法
<head>
<script src="../react17/react.development.v17.js"></script>
<script src="../react17/react-dom.development.v17.js"></script>
<script src="../react17/babel.min.js"></script>
</head>
<body>
<div id="app"></div>
<script type="text/babel">
let message = 'light'
// let oDiv = React.createElement('div', null, message)
// let oBtn = React.createElement('button', { onClick: myFn }, '按钮')
// let oRoot = React.createElement('div', null, oDiv, oBtn)
let oRoot = (
<div>
<div>{message}</div>
<button onClick={myFn}>按钮</button>
</div>
)
ReactDOM.render(oRoot, document.getElementById('app'), () => {
console.log('已经将虚拟DOM转换成了真实DOM, 已经渲染到界面上了')
})
function myFn() {
console.log('a')
}
</script>
</body>
函数组件
<head>
<script src="./react17/react.development.v17.js"></script>
<script src="./react17/react-dom.development.v17.js"></script>
<script src="./react17/babel.min.js"></script>
</head>
<body>
<div id="app"></div>
<script type="text/babel">
let message = '信息'
function Home() {
return (
<div>
<div>{message}</div>
<button onClick={myFn}>按钮</button>
</div>
)
}
ReactDOM.render(<Home />, document.getElementById('app'), () => {
console.log('渲染完成')
})
function myFn() {
message = '学习'
ReactDOM.render(<Home />, document.getElementById('app'), () => {
console.log('修改完成')
})
}
</script>
</body>
类组件
<head>
<script src="./react17/react.development.v17.js"></script>
<script src="./react17/react-dom.development.v17.js"></script>
<script src="./react17/babel.min.js"></script>
</head>
<body>
<div id="app"></div>
<script type="text/babel">
let message = '拿铁'
class Home extends React.Component {
render() {
return (
<div>
<div>{message}</div>
<button onClick={myFn}>按钮</button>
</div>
)
}
}
ReactDOM.render(<Home />, document.getElementById('app'))
function myFn() {
message = '因子'
ReactDOM.render(<Home />, document.getElementById('app'))
}
</script>
</body>
this
1.this指向问题
- 在ES6之前, 方法中的this谁调用就是谁,
并且还可以通过
call/apply/bind方法修改this - 从ES6开始, 新增了箭头函数, 箭头函数没有自己的this, 箭头函数中的this是函数外最近的那个this 并且由于箭头函数没有自己的this, 所以不能通过call/apply/bind方法修改this
2.监听事件中的this
- React内部在调用监听方法的时候, 默认会通过
apply方法将监听方法的this修改为了undefined所以在监听方法中无法通过this拿到当前组件的state. (undefined.state) - 如果想在监听方法中拿到当前组件的state, 那么就必须保证监听方法中的this就是当前实例 所以我们可以借助箭头函数的特性, 让React无法修改监听方法中的this, 让监听方法中的this就是当前实例
有状态组件和无状态组件
概念
组件中的状态(state)指的是
数据
- 有状态组件指的是
有自己数据的组件(逻辑组件) - 无状态组件指的是
没有自己数据的组件(展示组件)
如何定义
- 凡是继承于React.Component的组件,默认都会从父类继承过来一个state属性,这个state属性就是专门用来爆粗当前数据的
- 继承于React.Component的组件,都是有状态组件
- 不是继承于React.Component的组件,都是无状态组件
- 类组件就是有状态组件
- 函数组件就是无状态组件
state
注意点
- 不要直接修改state
- 直接修改state并不会触发界面更新
- 只有使用setState才会触发界面更新
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./react17/react.development.v17.js"></script>
<script src="./react17/react-dom.development.v17.js"></script>
<script src="./react17/babel.min.js"></script>
</head>
<body>
<div id="app"></div>
<script type="text/babel">
class Home extends React.Component {
constructor () {
super()
this.state = {
message: 'study'
}
}
render() {
return (
<div>
<div>{this.state.message}</div>
<button onClick={this.myFn}>按钮</button>
</div>
)
}
myFn = () => {
this.setState({
message: '好的'
})
}
}
ReactDOM.render(<Home />, document.getElementById('app'))
</script>
</body>
JSX
注释
- 不能直接在JSX中写 HTML的注释、JS的单|多行注释,JSX会把这些当做元素来处理
- 我们需要把(单行|多行)注释写在{}中,JSX会把{}中的注释当做JS来处理
错误写法
<head>
<script src="../react17/react.development.v17.js"></script>
<script src="../react17/react-dom.development.v17.js"></script>
<script src="../react17/babel.min.js"></script>
</head>
<body>
<div id="app"></div>
<script type="text/babel">
class Home extends React.Component {
constructor () {
super()
this.state = {
message: 'JSX注释'
}
}
render() {
return (
<div>
<!--html注释-->
// 单行注释
/**多行注释*/
<div>{this.state.message}</div>
<button onClick={this.myFn}>按钮</button>
</div>
)
}
myFn = () => {
this.setState({
message: 'JSX注释在{}进行单|多行注释'
})
}
}
ReactDOM.render(<Home />, document.getElementById('app'))
</script>
</body>
html注释直接写会报错,因为会被识别为元素
正确写法
绑定属性
JSX本质是转换成JS代码, 而在JS代码中class是一个关键字。所以不能直接使用class来绑定类名
- 对于普通属性,与之前一样绑定
- 类名通过
className绑定 - 绑定样式:
对象形式编写,通过-连接的需要转换为驼峰命名
<head>
<title>Document</title>
<script src="../react17/react.development.v17.js"></script>
<script src="../react17/react-dom.development.v17.js"></script>
<script src="../react17/babel.min.js"></script>
</head>
<body>
<!--
1.JSX绑定内容
- 在JSX中只要看到{}就会当做JS解析(执行里面的JS代码)
所以无论是绑定属性,还是绑定类名,还是绑定样式, 只需要将字符串改为{}
然后再通过JS动态获取, 动态绑定即可
- 绑定普通属性
+ <p title="我是标题">我是段落</p>
+ <p title={message}>我是段落</p>
- 绑定类名(class)
+ 由于JSX本质是转换成JS代码, 而在JS中class有特殊含义, 所以不能使用
同理可证, 但凡是属性名称是JS关键字的都不能直接使用
- 绑定样式(style)
+ 由于样式是键值对形式的, 所以在JSX中如果想要动态绑定样式
必须将样式放到一个对象中, 并且所有以-连接的样式名称都要转换成驼峰命名
+ <p style={{color:'red', fontSize:'50px'}}>绑定样式</p>
-->
<div id="app"></div>
<!--<p id="box" class="active" style="color: red; font-size: 100px">我是段落</p>-->
<script type="text/babel">
class Home extends React.Component{
constructor(){
super();
this.state = {
message:'知播渔'
}
}
render(){
return (
<div>
{/*1.对于普通属性而言, 过去怎么绑定, 现在就怎么绑定*/}
<p id="box">{this.state.message}</p>
<p title={this.state.message}>{this.state.message}</p>
{/*2.如果想通过JSX绑定类名, 那么不能直接通过class来绑定
JSX本质是转换成JS代码, 而在JS代码中class是一个关键字
所以不能直接使用class来绑定类名*/}
{/*3.所以以后但凡名称是JS关键字的属性, 都不能直接绑定*/}
<p className="active">{this.state.message}</p>
{/*4.如果想通过JSX绑定样式, 那么不能像过去一样编写
必须通过对象的形式来绑定*/}
{/*5.在绑定样式的时候, 如果过去在原生中是通过-连接的, 那么就必须转换成驼峰命名*/}
<p style={{color:'red', fontSize:'100px'}}>{this.state.message}</p>
</div>
)
}
}
ReactDOM.render(<Home/>, document.getElementById('app'));
</script>
</body>
插入内容
- 任何合法的JS表达式都可以嵌入{}中
- 不显示:[]、true、false、null、undefined
- true、false、null、undefined转换为字符串可以显示,空数组转成字符串也不能展示
<head>
<script src="../react17/react.development.v17.js"></script>
<script src="../react17/react-dom.development.v17.js"></script>
<script src="../react17/babel.min.js"></script>
</head>
<body>
<div id="app"></div>
<script type="text/babel">
class Home extends React.Component {
constructor () {
super()
this.state = {
message: 'JSX',
arrCon: ['1', '2', '3'],
flag: true
}
}
render() {
return (
<div>
{/*1.任何合法的JS表达式都可以嵌入到{}中*/}
<p>{this.state.flag ? '为真' : '为假'}</p>
{/*2.以下嵌入的内容不会被显示出来 [] true false null undefined*/}
<p>{[]}</p>
<p>{true}</p>
<p>{false}</p>
<p>{null}</p>
<p>{undefined}</p>
{/*3.如果想显示上面的这些内容, 那么就必须先转换成字符串
但是对于空数组来说, 哪怕转换成了字符串, 也不能显示*/}
<p>{[].toString()}</p>
<p>{true + ''}</p>
<p>{false + ''}</p>
<p>{null + ''}</p>
<p>{undefined + ''}</p>
{/*4.除了上述内容以外, 其它的内容都可以正常显示*/}
<p>我是段落</p>
<p>{this.state.message}</p>
</div>
)
}
}
ReactDOM.render(<Home />, document.getElementById('app'))
</script>
</body>
灵活度
- JSX使我们在JS中拥有了直接编写XML代码的能力
- 所以在JS中能干的事, 在JSX中都能干
<head>
<script src="../react17/react.development.v17.js"></script>
<script src="../react17/react-dom.development.v17.js"></script>
<script src="../react17/babel.min.js"></script>
</head>
<body>
<div id="app"></div>
<script type="text/babel">
class Home extends React.Component {
constructor () {
super()
this.state = {
flag: true
}
}
render() {
return (
<div>
{
// 类似vue的v-show
/*
<p style={{ display: this.state.flag ? 'block' : 'none' }}></p>
*/
}
{
// 类似vue的v-if
this.state.flag && <p>哈哈哈</p>
}
<button onClick={this.myFn}>按钮</button>
</div>
)
}
myFn = () => {
this.setState({
flag: !this.state.flag
})
}
}
ReactDOM.render(<Home />, document.getElementById('app'))
</script>
</body>
<head>
<script src="../react17/react.development.v17.js"></script>
<script src="../react17/react-dom.development.v17.js"></script>
<script src="../react17/babel.min.js"></script>
</head>
<body>
<div id="app"></div>
<script type="text/babel">
class Home extends React.Component {
constructor () {
super()
this.state = {
names: ['小红', '小张', '小兰']
}
}
render() {
const { names } = this.state
console.log('names', names)
return (
<div>
{
// <ul>{this.state.names}</ul>
}
<ul>
{
names.map((item, index) => { return <li key={index}>{item}</li> })
}
</ul>
</div>
)
}
}
ReactDOM.render(<Home />, document.getElementById('app'))
</script>
</body>
书写规范
- 1.在编写JSX代码的时候, 建议使用
()将JSX代码包裹起来 - 2.在JSX中只能有
一个根元素 - 3.在JSX中可以编写
单标签, 也可以编写双标签- 但是如果编写的是
单便签, 那么就必须加上闭合符号
- 但是如果编写的是
- 4.在使用组件的时候, 可以使用单便签, 也可以使用双标签
- 但是如果是使用的
单标签, 那么就必须加上闭合符号
- 但是如果是使用的
- 5.在使用组件的时候, 如果组件中没有内容, 那么建议使用
单标签
JSX绑定事件
- 1.事件监听方法中的this
- 默认情况下React在调用事件监听方法的时候, 是通过apply来调用的 并且在调用的时候将监听方法中的this修改为了undefined 所以默认情况下我们是无法在监听方法中使用this的
- 2.如何解决监听方法中this默认是undefined的问题
- 2.1 通过
箭头函数 - 2.2 通过添加监听方法的时候, 手动通过
bind修改监听方法中的this - 2.3 通过在
构造函数中, 手动通过bind修改监听方法中的this - 2.4 手动绑定一个
箭头函数, 然后在箭头函数的函数体中手动调用监听方法- [1] 因为箭头函数中的
this, 就是当前的实例对象 - [2] 因为监听方法
并不是React调用的, 而是我们在箭头函数中手动调用的 - [3] 因为普通的方法, 默认情况下谁调用就是谁
- [1] 因为箭头函数中的
- 2.1 通过
- 注意点: 在企业开发中, 最为推荐的一种方式就是第四种
<head>
<script src="../react17/react.development.v17.js"></script>
<script src="../react17/react-dom.development.v17.js"></script>
<script src="../react17/babel.min.js"></script>
</head>
<body>
<div id="app"></div>
<script type="text/babel">
class Home extends React.Component {
constructor () {
super()
this.state = {
message: 'JSX绑定事件'
}
this.myClick = this.myBtn.bind(this)
}
render() {
return (
<div>
<div>{this.state.message}</div>
<button onClick={this.changeBtn}>按钮</button>
<button onClick={this.myClick}>myclick</button>
<button onClick={this.myBtn.bind(this)}>bind</button>
<button onClick={() => { this.myBtn() }}>按钮</button>
</div>
)
}
myBtn() {
this.setState({
message: '普通函数'
})
}
changeBtn = () => {
this.setState({
message: '箭头函数'
})
}
}
ReactDOM.render(<Home />, document.getElementById('app'))
</script>
</body>
JSX事件对象
-
1.JSX事件参数
- 和原生JS一样, React在执行监听方法会传递一个事件对象给我们 但是React传递给我们的并不是原生的事件对象, 而是一个React自己合成的事件对象
-
2.什么是合成事件?
-
合成事件是 React 在浏览器事件基础上做的一层包装, 基本上有着和浏览器的原生事件有相同的接口, 也能够进行 stopPropagation() 和 preventDefault(), 并且合成事件在所有浏览器中的工作方式相同
-
如果由于某种原因需要浏览器的原生事件, 则能够简单的通过 nativeEvent 属性就能够获取
-
注意点:
- 从 ReactV0.14 起,从事件处理程序返回 false 将不再停止事件的传递。 应当手动调用 e.stopPropagation() 或 e.preventDefault() 去阻止传递。
- 合成事件 是合并而来。这意味着 合成事件 对象可能会被重用, 而且在事件回调函数被调用后,所有的属性都会无效。出于性能考虑,你不能通过异步访问事件。
-
-
3.React事件处理性能优化
-
React并不会把事件处理函数直接绑定到真实的节点上, 而是使用一个统一的事件监听器 ReactEventListener, 把所有事件绑定到结构的最外层 document 节点上,依赖冒泡机制完成事件委派
-
ReactEventListener:React事件监听器维持了一个映射来保存所有组件内部的事件监听和处理函数, 负责事件注册和事件分发。当组件在挂载或卸载时,只是在这个统一的事件监听器上插入或删除一些对象; 当事件发生时,首先被这个统一的事件监听器处理,然后在映射里找到真正的事件处理函数并调用。 这样简化了事件处理和回收机制,提升了效率
-
<head>
<script src="../react17/react.development.v17.js"></script>
<script src="../react17/react-dom.development.v17.js"></script>
<script src="../react17/babel.min.js"></script>
</head>
<body>
<div id="app"></div>
<script type="text/babel">
class Home extends React.Component {
constructor() {
super();
this.state = {
message: '知播渔'
}
}
render() {
return (
<div>
<div>{this.state.message}</div>
{/*
在React中当监听方法被触发的时候, React也会传递一个事件对象给我们
但是React传递给我们的这个事件对象并不是原生的事件对象,
而是React根据原生的事件对象自己合成的一个事件对象
注意点: 虽然传递给我们的是React自己合成的事件对象, 但是提供的API和元素的几乎一致
如果你用到了一个没有提供的API, 那么你也可以根据合成的事件对象拿到原生的事件对象
*/}
<button onClick={(e)=>{console.log(e)}}>按钮</button>
<button onClick={(e)=>{console.log(e.nativeEvent)}}>按钮</button>
</div>
)
}
}
ReactDOM.render(<Home/>, document.getElementById('app'));
</script>
</body>
学习笔记,版权归李南江所有