前端小白学 React 框架(一)
React
最近打算在科研之余开始学习 React 框架,感觉大厂都用的 React 比较多,因此想搞一个专栏放学习 React 时的笔记,方便以后查找阅读吧 🥳,共勉。
React 的介绍
React 是什么
- React 是用于构建用户界面的 JavaScript 库
- 官方文档:www.ltonus.com/react/
React 的特点
- 声明式: React 使创建交互式 UI 变得轻而易举。为你应用的每一个状态设计简洁的视图,当数据变动时 React 能高效更新并渲染合适的组件。以声明式编写 UI,可以让你的代码更加可靠,且方便调试。
- 组件化: 构建管理自身状态的封装组件,然后对其组合以构成复杂的 UI。由于组件逻辑使用 JavaScript 编写而非模板,因此你可以轻松地在应用中传递数据,并保持状态与 DOM 分离。
- ### 多跨平台适配: 无论你现在使用什么技术栈,在无需重写现有代码的前提下,通过引入 React 来开发新功能。React 还可以使用 Node 进行服务器渲染,或使用 React Native 开发原生移动应用。
声明式编程:
声明式编程是目前整个大前端开发的模式: Vue、React、Flutter、SwiftUl;它允许开发者只需要维护自己的状态,当状态改变时,React可以根据最新的状态去渲染UI界面。
组件化开发:
组件化开发页面目前前端的流行趋势,会将复杂的界面拆分成一个个小的组件;如何合理的进行组件的划分和设计也是学习的一个重点。
### 多平台适配:
- 2013年,React发布之初主要是开发Web页面
- 2015年,Facebook推出了ReactNative,用于开发移动端跨平台;(虽然目前Flutter非常火爆,但是还是有很多公司在使用ReactNative)
- 2017年,Facebook推出ReactVR,用于开发虚拟现实Web应用程序;(VR也会是一个火爆的应用场景)
开发 React 前的热知识🔥
开发React必须依赖三个库:
- react: 包含react所必须的核心代码
- react-dom: react渲染在不同平台所需要的核心代码
- babel: 将jsx转换成React代码的工具
第一次接触React会被它繁琐的依赖搞蒙,居然依赖这么多东西:
- 对于Vue来说,我们只是依赖一个vuejs文件即可,但是react居然要依赖三个包
- 其实呢,这三个库是各司其职的,目的就是让每一个库只单纯做自己的事情
- 在React的0.14版本之前是没有react-dom这个概念的,所有功能都包含在react里
为什么要进行拆分呢?
- react包中包含了react web和react-native所共同拥有的核心代码
- react-dom针对web和native所完成的事情不同:
- web端: react-dom会将jsx最终渲染成真实的DOM,显示在浏览器中
- native端: react-dom会将isx最终渲染成原生的控件(比如Android中的Button,iOS中的UIButton)。
原因就是react-native
Babel 和 React 的关系
-
babel是什么呢? Babel,又名 Babel.js,是目前前端使用非常广泛的编译器、转移器。比如当下很多浏览器并不支持ES6的语法,但是确实ES6的语法非常的简洁和方便,我们开发时希望使用它,那么编写源码时我们就可以使用ES6来编写,之后通过Babel工具,将ES6转成大多数浏览器都支持的ES5的语法。
-
React和Babel的关系 默认情况下开发React其实可以不使用babel,但是前提是我们自己使用
React.createElement
来编写源代码,它编写的代码非常的繁琐和可读性差,那么我们就可以直接编写jsx (JavaScript XML) 的语法,并且让babel帮助我们转换成React.createElement。
Hello React 案例
为了演练React,我们可以提出一个小的需求:在界面显示一个文本:Hello World,点击下方的一个按钮,点击后文本改变为Hello React。
React 的依赖引入
根据上面所说的,我们在编写React代码时,需要引入三个依赖,且这三个依赖都是必不可少的。那么,添加这三个依赖有如下方式:
- 方式一: 直接CDN引入
- 方式二:下载后,添加本地依赖
- 方式三: 通过npm管理 (后续脚手架再使用)
暂时我们直接通过CDN引入,来演练下面的示例程序,这里有一个crossorigin的属性,这个属性的目的是为了拿到跨域脚本的错误信息。
<!-- 1.引入React核心库-得到一个React对象 -->
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<!-- 2.引入React扩展库-得到ReactDOM对象 -->
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<!-- 3.引入babel-作用是将jsx转化为浏览器能够识别的js语法 -->
<!-- 注:若是在开发过程中使用到了jsx语法(或使用es6等js高级语法)则引入babel,否则不需要引入 -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
❌ 注意:以上React库的CDN链接🔗来自React的官方文档中CDN连接和Babel的CDN在这里👉 【在网站中添加 React】,使用CDN链接进行开发只能在
开发环境
使用,且引入顺序:必须先引入核心库再引入扩展库。
❌ 注意:如果想要使用babel,需要在script标签中加上
type="text/babel"
,否则babel不会对script里的代码进行转换。
- 在页面上渲染一个页面结构
// React 18
const root = ReactDOM.createRoot(document.querySelector('#root'));
render();
function render() {
root.render((
<div>
<h2>{message}</h2>
<button onClick={btnClick}>修改文本</button>
</div>
));
}
- 首先将文本定义成变量
message
,把文本内容设置成变量来进行数据管理
let message = 'Hello World';
- 定义一个回调函数,监听按钮的点击事件
function btnClick() {
// ...
}
- 修改
message
变量后重新渲染页面即可
function btnClick() {
message = 'Hello React';
render();
}
Hello React 组件化开发
如果当整个逻辑其实可以看做一个整体,那么我们就可以将其封装成一个组件,因为root.render
的参数是一个HTML元素或者一个组件,所以我们可以先将之前的业务逻辑封装到一个组件中,然后传入到 ReactDOM.render 函数中的第一个参数。
这里我们暂时使用类的方式封装一个组件,首先定义一个类(类名大写,组件的名称是必须大写的,小写会被认为是HTML元素),继承自React.Component
,然后实现当前组件的render
函数,render
当中返回的JSX内容,就是之后React会帮助我们渲染的内容。
那么使用类组件的形式将静态页面渲染出来看看效果如何 😆
// 使用类组件的形式重写example1的案例
class App extends React.Component {
// 渲染方法的方法名必须为render
render() {
return (
<div>
<h2>Hello World</h2>
<button>修改文本</button>
</div>
)
}
}
const root = ReactDOM.createRoot(document.querySelector('#root'));
root.render(<App />);
有了静态页面后,下一个问题就是数据在哪里定义?,在组件中的数据,我们可以分成两类:
- 参与界面更新的数据: 当数据变量时,需要更新组件渲染的内容
- 不参与界面更新的数据: 当数据变量时,不需要更新将组件渲染的内容
其中参与数据流,这个数据是定义在当前对象的state中,我们可以通过在构造函数中this.state = {定义的数据}
,当我们的数据发生变化时,我们可以调用this.setState
来更新数据,并且通知React进行update
操作,在进行update
操作时,就会重新调用render
函数,并且使用最新的数据,来渲染界面。那么把上面的代码改动一下:
class App extends React.Component {
constructor() {
super();
this.state = {
message: 'Hello World'
}
}
render() {
return (
// ...
<h2>{this.state.message}</h2>
// ...
)
}
}
接下来就是绑定函数,这里会有个小问题,因为React更新state
是使用的this.setState
方法,而如果像如下代码绑定函数将会出现问题,大家可以在控制台中查看打印信息:
class App extends React.Component {
btnClick() {
console.log('btnClick', this);
this.setState({
message: 'Hello React'
})
}
render() {
return (
<div>
<h2>{this.state.message}</h2>
<button onClick={this.btnClick}>修改文本</button>
</div>
)
}
}
你会发现打印的是btnClick undefined
,然后一堆的报错信息:
出现以上错误的原因就是因为this指向出现了问题,在类中直接定义一个函数,并且将这个函数绑定到元素的onClick事件上,当前这个函数的this指向默认情况下是undefined
。
💡 因为在正常的DOM操作中,监听点击,监听函数中的this其实是节点对象(比如说是button对象),而React并不是直接渲染成真实的DOM,我们所编写的button只是一个语法糖,它的本质React的Element对象,那么在这里发生监听的时候,react在执行函数时并没有绑定this,默认情况下就是一个undefined。
那么需要给它纠正一下,具体修改方法我们参见官方文档中的事件处理:
class App extends React.Component {
constructor() {
// ...
// 为了在回调中使用 `this`,这个绑定是必不可少的
this.btnClick = this.btnClick.bind(this);
}
}
其实除了上面这种写法,还有下面一种写法:
<button onClick={this.btnClick.bind(this)>改变文本</button>
但是这种写法,如果在render函数中要多次绑定btnClick
方法,那么每绑定一次都写一遍bind
,这样感觉比麻烦,因此可能更加推荐官方的那种写法 🥳。
写在最后
如果大家喜欢的话可以收藏本专栏,之后会慢慢更新,然后大家觉得不错可以点个赞或收藏一下 🌟。
本节的源码放在作者的GitHub仓库里,分别是:
转载自:https://juejin.cn/post/7232549203006849080