React是什么?
- 这里我们来看一下官方对它的解释:用于构建用户界面的 JavaScript 库。
-
我们知道对于前端来说,主要的任务就是构建用于界面,而构建用于界面离不开三个技术:
- HTML:构建页面的结构
- CSS:构建页面的样式
- JavaScript:页面动态内容和交互
-
那么使用最原生的HTML、CSS、JavaScript可以构建完整的用户界面吗?当然可以,但是会存在很多问题:
- 比如操作DOM兼容性的问题;
- 比如过多兼容性代码的冗余问题;
- 比如代码组织和规范的问题;
所以,React是国内最为流行的两个框架,而他的目的是帮助我们来构建用户界面的JavaScript库。(当然在国内目前Vue也应用广泛)
React的起源
学习一类技术当然也要了解一下起源和开发者的思维,加深自己的兴趣,便于以后的学习。下面我把自己看到的给大家搬运一下。
- React是2013年,Facebook开源的JavaScript框架,这个源于一个需求,所产生的bug,如下图三个消息的数字在发生变化时,过多的DOM操作很容易产生bug。
当然对于Facebook的工程师来说,这是小菜一碟,但是Facebook的工程师并不满足于此,他们开始思考为什么会产生这样的问题?
- 在传统的开发模式中,我们过多的去操作界面的细节;(前端、iOS、Android)并且需要掌握和使用大量DOM的API,当然我们可以通过jQuery来简化和适配一些API的使用另外关于数据(状态),往往会分散到各个地方,不方便管理和维护。
这就导致他们去想能否有一种新的模式来解决上面的问题?
- 以组件的方式去划分一个个功能模块
- 组件内以jsx来描述UI的样子,以state来存储组件内的状态
- 当应用的状态发生改变时,通过setState来修改状态,状态发生变化时,UI会自动发生更新
(这想法推动了React的诞生)
React的特点
声明式编程
- 声明式编程是目前整个大前端开发的模式:Vue、React、Flutter、SwiftUI 都是声明式编程,和原生的命令式编程相比舒服太多了。
- React允许我们只需要维护自己的状态,当状态改变时,React可以根据最新的状态去渲染我们的UI界面;
组件化开发
-
组件化开发页面目前前端的流行趋势,我们会将复杂的界面拆分成一个个小的组件进行拼合;(比如说一栋楼房比做一个复杂的界面,那么每一层可以作为一个组件,电力系统也作为一个公共组件,水系统也作为一个公共组件,每个房间作为一个组件等等这样可以重复利用,也就是复用性,当你需要整体改动的时候你只要把组件修改一下就可以不必一个个界面去修改)
-
那么如何合理的进行组件的划分和设计就成为了一艺术性的问题。
多平台适配
-
2013年,React发布之初主要是开发Web页面。
-
2015年,Facebook推出了ReactNative,用于开发移动端跨平台;(虽然目前Flutter非常火爆,但是还是有很多公司在使用 ReactNative)。
-
2017年,Facebook推出ReactVR,用于开发虚拟现实Web应用程序;(随着5G的普及,VR也将会是一个火爆的应用场景)。在这里说一下华为的5G NB 。相信芯片技术也能很快实现突破的。
不得不说他们的科技公司还是相当先进和强大的,他们掌握最先进的思想和技术,在科技方面还是需要借鉴。
-
React是大量优秀程序员的思想结晶它的流行不仅仅局限于普通开发工程师对它的认可,大量流行的其他框架借鉴React的思想,如:
-
Vue.js框架设计之初,有很多的灵感来自Angular和React。特别是Vue 3.0 很多新的特性,比如React Hooks是开创性的新功能,Vue Function Based API学习了React Hooks的思想
-
Flutter的很多灵感都来自React,来自官网的一段话:(SwiftUI呢)事实上Flutter中的Widget – Element – RenderObject,对应的就是JSX – 虚拟DOM – 真实DOM
-
React开发依赖
开发React必须依赖三个库:
- react:包含react所必须的核心代码
- react-dom:react渲染在不同平台所需要的核心代码
- babel:将jsx转换成React代码的工具
(第一次接触React会被它繁琐的依赖搞蒙,对于Vue来说,我们只是依赖一个vue.js文件即可,但是react居然要依赖三个库。其实呢,这三个库是各司其职的,目的就是让每一个库只单纯做自己的事情。
在React的0.14版本之前是没有react-dom这个概念的,所有功能都包含在react里为什么要进行拆分呢?原因就是react-native。react包中包含了react和react-native所共同拥有的核心代码。)
react-dom针对web和native所完成的事情不同:
- web端:react-dom会讲jsx最终渲染成真实的DOM,显示在浏览器中
- native端:react-dom会讲jsx最终渲染成原生的控件(比如Android中的Button,iOS中的UIButton)。
babel是什么呢?
- 是目前前端使用非常广泛的编辑器、转移器。
- 比如当下很多浏览器并不支持ES6的语法,但是确实ES6的语法非常的简洁和方便,我们开发时希望使用它那么编写源码时我们就可以使用ES6来编写,之后通过Babel工具,将ES6转成大多数浏览器都支持的ES5的语法。
React和Babel的关系:
- 默认情况下开发React其实可以不使用babel,但是前提是我们自己使用React.createElement 来编写源代码,它编写的代码非常的繁琐和可读性差,那么我们就可以直接编写jsx(JavaScript XML)的语法,并且让babel帮助我们转换成React.createElement。
引入React依赖
我们在编写React代码时,这三个依赖库都是必不可少的。
-
添加依赖库的方式:
直接CDN引入
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
这里有一个crossorigin的属性,这个属性的目的是为了拿到跨域脚本的错误信息
2. 下载后,添加本地依赖
3. 通过npm管理(通过脚手架使用)
下面是一个例子
例一:在界面上通过React显示一个Hello World
注意:这里我们编写React的script代码中,必须添加 type="text/babel",作用是可以让babel解析jsx的语法
- ReactDOM.render函数:
- 参数一:传递要渲染的内容,这个内容可以是HTML元素,也可以是React的组件(这里我们传入了一个h2元素)
- 参数二:将渲染的内容,挂载到哪一个HTML元素上,我们可以通过**{ }**语法来引入外部的变量或者表达式
例二:点击按钮改变message
将上面的例子运用组件化思想:
<script type="text/babel">
// 封装App组件
class App extends React.Component {
constructor() {
super();
// this.message = "Hello World";
this.state = {
message: "Hello World"
};
}
render() {
return (
<div>
<h2>{this.state.message}</h2>
<button onClick={this.btnClick.bind(this)}>改变文本</button>
</div>
)
}
btnClick() {
this.setState({
message: "Hello React"
})
}
}
// 渲染组件
ReactDOM.render(<App/>, document.getElementById("app"));
</script>
-
整个逻辑其实可以看做一个整体,那么我们就可以将其封装成一个组件,之前说过 ReactDOM.render 第一参数是一个HTML原生或者一个组件,所以我们可以先将之前的业务逻辑封装到一个组件中,然后传入到 ReactDOM.render 函数中的第一个参数
-
使用类的方式封装组件:
- 定义一个类(类名大写,组件的名称是必须大写的,小写会被认为是HTML元素),继承自React.Component
- 实现当前组件的render函数,render当中返回的jsx内容,就是之后React会帮助我们渲染的内容
组件化问题一:数据在哪里定义
-
在组件中的数据,我们可以分成两类:
- 参与界面更新的数据:当数据变量时,需要更新组件渲染的内容 2. 不参与界面更新的数据:当数据变量时,不需要更新将组建渲染的内容
-
参与界面更新的数据我们也可以称之为是参与数据流,这个数据是定义在当前对象的state中。
-
我们可以通过在构造函数中 this.state = {定义的数据}
-
当我们的数据发生变化时,我们可以调用 this.setState 来更新数据,并且通知React进行update操作,在进行update操作时,就会重新调用render函数,并且使用最新的数据,来渲染界面
-
组件化问题二:事件绑定中的this
-
在类中直接定义一个函数,并且将这个函数绑定到html原生的onClick事件上,当前这个函数的this指向的是谁呢?
- 默认情况下是undefined
- 原因是在正常的DOM操作中,监听点击,监听函数中的this其实是节点对象(比如说是button对象)
- 这次因为React并不是直接渲染成真实的DOM,我们所编写的button只是一个语法糖,它的本质React的Element对象
- 那么在这里发生监听的时候,react给我们的函数绑定的this,默认情况下就是一个undefined
-
我们在绑定的函数中,可能想要使用当前对象,比如执行 this.setState 函数,就必须拿到当前对象的this
- 我们就需要在传入函数时,给这个函数直接绑定this
- 类似于下面的写法:<button onClick={this.changeText.bind(this)}>改变文本</button>
刚刚接触练习代码段1
<script type="text/babel">
// 封装App组件
class App extends React.Component {
constructor(props) {
super(props);
// this.message = "Hello World";
this.state = {
movies: ["奔涌吧猿浪", "大话西游", "让子弹飞", "邪不压正"]
}
}
render() {
return (
<div>
<h2>电影列表</h2>
<ul>
{
this.state.movies.map((item, index) =>{
return (<li>{item}</li>)
})
}
</ul>
</div>
)
}
}
// 渲染组件
ReactDOM.render(<App/>, document.getElementById("app"));
</script>
刚刚接触练习代码段2
<script type="text/babel">
// 封装App组件
class App extends React.Component {
constructor(props) {
super(props);
// this.message = "Hello World";
this.state = {
counter: 0
}
}
render() {
return (
<div>
<h2>当前计数:{this.state.counter}</h2>
<button onClick={this.increment.bind(this)}>+1</button>
<button onClick={this.decrement.bind(this)}>-1</button>
</div>
)
}
increment() {
this.setState({
counter: this.state.counter + 1
})
}
decrement() {
this.setState({
counter: this.state.counter - 1
})
}
}
// 渲染组件
ReactDOM.render(<App/>, document.getElementById("app"));
</script>