一、碎碎念念

(一)React 定义

官方定义:用于构建 Web 和原生交互界面的库。其核心概念组件的思想和Vue存在很大的相似之处
 

(二)来龙去脉

我们知道对于前端来说,主要的任务就是构建用于界面,而构建用于界面离不开三个技术:
  • HTML:构建页面的结构
  • CSS:构建页面的样式
  • JavaScript:页面动态内容和交互
使用最原生的HTML、CSS、JavaScript可以构建完整的用户界面,但是会存在很多问题
  • 比如操作DOM兼容性的问题;
  • 比如过多兼容性代码的冗余问题;
  • 比如代码组织和规范的问题;
所以,一直以来前端开发人员都在需求可以让自己开发更方便的JavaScript库,在过去的很长时间内,jQuery是被使用最多的JavaScript库;在过去的一份调查中显示,全球前10,000个访问最高的网站中,有65%使用了jQuery,是当时最受欢迎的JavaScript库;但是越来越多的公司开始慢慢不再使用jQuery,包括程序员使用最多的GitHub;
如今前端最为流行的三大框架是:Vue、React、Augular
而Angular在国内并不是特别受欢迎,尤其是Angular目前的版本对TypeScript还有要求的情况下,Vue和React是国内最为流行的两个框架,而他们都是帮助我们来构建用户界面的JavaScript库。
 

(三)React起源

React是2013年,Facebook开源的JavaScript框架,那么当时为什么Facebook要推出这样一款框架呢?
这个源于一个需求,所产生的bug:
notion image
该功能上线之后,总是出现bug;三个消息的数字在发生变化时,过多的操作很容易产生bug。bug是否可以修复呢?当然可以修复,但是Facebook的工程师并不满足于此。他们开始思考为什么会产生这样的问题,在传统的开发模式中,我们过多的去操作界面的细节(前端、iOS、Android),并且需要掌握和使用大量DOM的API,当然我们可以通过jQuery来简化和适配一些API的使用,另外关于数据(状态),往往会分散到各个地方,不方便管理和维护;
他们就去思考,是否有一种新的模式来解决上面的问题:
  1. 以组件的方式去划分一个个功能模块;
  1. 组件内以jsx来描述UI的样子,以 state 来存储组件内的状态;
  1. 当应用的状态发生改变时,通过 setState 来修改状态,状态发生变化时,UI会自动发生更新。
 

(四)React特点

  • 声明式编程 声明式编程是目前整个大前端开发的模式:Vue、React、Flutter、SwiftUI,它允许我们只需要维护自己的状态,当状态改变时,React可以根据最新的状态去渲染我们的UI界面(f 函数就是 render 函数)
    • notion image
  • 组件化开发 组件化开发页面目前前端的流行趋势,我们会讲复杂的界面拆分成一个个小的组件;如何合理的进行组件的划分和设计是一个重点;
    • Vue组件化配图
      Vue组件化配图
  • 多平台适配
    • 2013年,React发布之初主要是开发Web页面;
    • 2015年,Facebook推出了ReactNative,用于开发移动端跨平台;(虽然目前Flutter非常火爆,但是还是有很多公司在使用(ReactNative);
    • 2017年,Facebook推出ReactVR,用于开发虚拟现实Web应用程序;(随着5G的普及,VR也会是一个火爆的应用场景);
 
掌握最先进的思想和技术
  • React由Facebook来更新和维护,它是大量优秀程序员的思想结晶。React的流行不仅仅局限于普通开发工程师对它的认可,大量流行的其他框架借鉴React的思想。
  • Vue.js框架设计之初,有很多的灵感来自Angular和React
  • 包括Vue3很多新的特性,也是借鉴和学习了React。比如React Hooks是开创性的新功能,Vue Function Based API学习了React Hooks的思想
  • Flutter的很多灵感都来自React,来自官网的一段话:(SwiftUI呢) 事实上Flutter中的Widget – Element – RenderObject,对应的就是JSX – 虚拟DOM – 真实DOM
    • notion image
  • 所以React可以说是前端的先驱者,它总是会引领整个前端的潮流。
notion image
 

二、牛刀小试

(一)Hello React案例——原生实现

我们用不同的方法实现在界面显示一个文本:Hello World,点击下方的一个按钮,点击后文本改变为Hello React。
⏺ 原生开发(命令式编程)
💡
命令式编程:每做一个操作,都是给计算机(浏览器)一步步命令
 

(二)Hello React案例——引入React

⏺ 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 ,又名Babel.js,是目前前端使用非常广泛的编辑器、转移器。比如当下很多浏览器并不支持ES6的语法,但是确实ES6的语法非常的简洁和方便,我们开发时希望使用它。那么编写源码时我们就可以使用ES6来编写,之后通过Babel工具,将ES6转成大多数浏览器都支持的ES5的语法。
React和Babel的关系:
  • 默认情况下开发React其实可以不使用babel。
  • 但是前提是我们自己使用 React.createElement 来编写源代码,它编写的代码非常的繁琐和可读性差。
  • 那么我们就可以直接编写jsx(JavaScript XML)的语法,并且让babel帮助我们转换成React.createElement
 
所以,我们在编写React代码时,这三个依赖都是必不可少的。添加这三个依赖的方式:
  • 方式一:直接CDN引入
  • 方式二:下载后,添加本地依赖
  • 方式三:通过npm管理(后续脚手架再使用)
 
以上代码点击按钮message发生了修改,但是并没有渲染到页面上,所以我们需要再调用render函数进行重新渲染,具体代码如下:

(三)Hello React案例——组件化实现

有两个重点,第一个是按钮绑定事件需要加上 .bind(this),第二个是需要实现响应式开发的变量必须要放在 state 中,并通过 setState() 进行赋值
 

(四)Js基础补充

1. 类的定义

以下分别从ES5和ES6的两个标准说明对类的定义
 

2. 类的继承

三、JSX核心语法

(一)电影列表案例

💡
 

(二)计数器案例

 

(三)认识JSX

上述element变量的声明右侧赋值的标签语法,即是JSX语法,其需要依赖babel插件和 type="text/babel",二者缺一不可。
 
JSX是什么?
  • JSX是一种JavaScript的语法扩展(eXtension),也在很多地方称之为JavaScript XML,因为看起就是一段XML语法;
  • 它用于描述我们的UI界面,并且其完成可以和JavaScript融合在一起使用;
  • 它不同于Vue中的模块语法,你不需要专门学习模块语法中的一些指令(比如v-for、v-if、v-else、v-bind);
 
React选择了JSX的原因(All in js)
React认为渲染逻辑本质上与其他UI逻辑存在内在耦合,比如UI需要绑定事件(button、a原生等等);比如UI中需要展示数据状态,在某些状态发生改变时,又需要改变UI。
他们之间是密不可分,所以React没有讲标记分离到不同的文件中,而是将它们组合到了一起,这个地方就是组件(Component)
 
书写规范
  • JSX的顶层只能有一个根元素,所以我们很多时候会在外层包裹一个div原生(或者使用后面我们学习的Fragment);
  • 为了方便阅读,我们通常在jsx的外层包裹一个小括号(),这样可以方便阅读,并且jsx可以进行换行书写,⚠️ 如果不用小括号,需要全部放在一行;
  • JSX中的标签可以是单标签(必须以 /> 结尾),也可以是双标签;
 

(四)JSX使用

1. JSX注释

JSX的注释是 {/* 我是一段注释 */}
 

2. JSX嵌入数据

  • 情况一:当变量是Number、String、Array类型时,可以直接显示
  • 情况二:当变量是null、undefined、Boolean类型时,内容为空;
    • 如果希望可以显示null、undefined、Boolean,那么需要转成字符串;
    • 转换的方式有很多,比如toString方法、和空字符串拼接,String(变量)等方式;
  • 情况三:对象类型不能作为子元素(not valid as a React child)
 

3. JSX嵌入表达式

主要分为三种:
  • 运算表达式
  • 三元运算符
  • 执行一个函数
 

4. JSX绑定属性

例如以下属性:
  • 比如元素都会有title属性
  • 比如img元素会有src属性
  • 比如a元素会有href属性
  • 比如元素可能需要绑定class
  • 比如原生使用内联样式style(属性名使用驼峰命名
 

5. JSX事件绑定

原生DOM原生有一个监听事件,我们可以有两种办法:其一是获取DOM原生,添加监听事件;其二是在HTML原生中,直接绑定 onclick
在React中操作:React 事件的命名采用小驼峰式(camelCase),而不是纯小写。我们需要通过 {} 传入一个事件处理函数,这个函数会在事件发生时被执行;
💡
关于以下代码中 this 的问题
我们这里相关函数中打印 this,也会发现它是一个 undefined。原因是 btnClick() 函数并不是我们主动调用的,而且当button发生改变时,React内部调用了 btnClick() 函数;
解决 this 的问题
方案一:bind给btnClick显示绑定this 方案二:使用ES6 class fields 语法 方案三:事件监听时传入箭头函数(推荐)
 

6. JSX传递参数

在执行事件函数时,有可能我们需要获取一些参数信息:比如event对象、其他参数。
情况一:获取event对象,很多时候我们需要拿到event对象来做一些事情(比如阻止默认行为),假如我们用不到this,那么直接传入函数就可以获取到event对象(即函数默认的参数即为 event
情况二:获取更多参数。有更多参数时,我们最好的方式就是传入一个箭头函数,主动执行的事件函数,并且传入相关的其他参数;
浏览器原生的点击事件操作如下:
 
在React中也有类似的事件,但是他们并不是同一种类型的对象,它是React内部合成的对象
 

7. 条件渲染

某些情况下,界面的内容会根据不同的情况显示不同的内容,或者决定是否渲染某部分内容。
在Vue中,我们会通过指令来控制:比如 v-ifv-show;在React中,所有的条件判断都和普通的JavaScript代码一致;
  • 方式一:条件判断语句(适合逻辑较多的情况)
  • 方式二:三元运算符(适合逻辑比较简单)
  • 与运算符&&(适合如果条件成立,渲染某一个组件;如果条件不成立,什么内容也不渲染)
  • v-show的效果(主要是控制 display 属性是否为 none
 
以下是仿照Vue,v-show 效果的实现,通过控制css的 display 属性,控制元素的显示和隐藏
 

8. 列表渲染

真实开发中我们会从服务器请求到大量的数据,数据会以列表的形式存储:比如歌曲、歌手、排行榜列表的数据;比如商品、购物车、评论列表的数据;比如好友消息、动态、联系人列表的数据;
以下有一些函数较为常用:map() 用于展示列表、filter() 用于过滤列表数据、slice() 用于数据的截取。

9. JSX的本质

实际上,jsx 仅仅只是 React.createElement(component, props, ...children) 函数的语法糖,所有的jsx最终都会被转换成 React.createElement 的函数调用。
 
如果采用 React.createElement() 创建元素,则可以省略babel插件和script标签的类型限制,具体代码如下
 
接下来我们从源码的角度对 createElement() 函数进行解读
 
createElement() 需要传递三个参数:
  • 参数一:type当前ReactElement的类型; 如果是标签元素,那么就使用字符串表示“div”; 如果是组件元素,那么就直接使用组件的名称;
  • 参数二:config 所有jsx中的属性都在config中以对象的属性和值的形式存储
  • 参数三:children 存放在标签中的内容,以children数组的方式进行存储; 当然,如果是多个元素呢?React内部有对它们进行处理,处理的源码在下方
 

10. Babel转换

babel的官网中提供快速查看转换的过程,👉 官网
notion image
看到Babel转化后的代码我们可以看到,React.createElement() 函数的参数不止三个,但是函数定义只有三个,实际上背后的逻辑如下:
 
然后我们需要验证结果,我们将转化后的代码进行运行
运行JSX验证成功
运行JSX验证成功
 

11. JSX与虚拟DOM

我们通过 React.createElement() 最终创建出来一个 ReactElement 对象(可以见上源码)。
ReactElement对象是用于组成一个JavaScript的对象树,对象树就是大名鼎鼎的虚拟DOM(Virtual DOM),然后通过 render() 函数将虚拟DOM树转化成真实DOM树。
💡 jsx → createElement函数 → ReactElement(对象树) → ReactDOM.render→ 真实DOM
 
查看ReactElement的树结构的方法如下:
 
💡 真实DOM和虚拟DOM对比
  • 很难跟踪状态发生的改变:原有的开发模式,我们很难跟踪到状态发生的改变,不方便针对我们应用程序进行调试;
  • 操作真实DOM性能较低:传统的开发模式会进行频繁的DOM操作,而这一的做法性能非常的低;
 
E.g. 我们有一组数组需要渲染:[0, 1, 2, 3, 4],我们可以用 ulli 将他们展示出来
后来我们增加5条数据,即 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],我们要么通过重新遍历整个数组(不推荐),要么在后面追加另外5个元素的 li
以上代码性能非常低效,因为我们通过 document.createElement 创建元素,再通 过 ul.appendChild(li) 渲染到DOM上,进行了多次DOM操作;
 
对于批量操作的,最好的办法不是一次次修改DOM,而是对批量的操作进行合并;(比如可以通过DocumentFragment进行合并);我们可以通过Virtual DOM来帮助我们解决上面的问题;
所以虚拟DOM帮助我们从命令式编程转到了声明式编程的模式。根据React官方的说法:Virtual DOM 是一种编程理念。在这个理念中,UI以一种理想化或者说虚拟化的方式保存在内存中,并且它是一个相对简单的JavaScript对象。我们可以通过 ReactDOM.render 让虚拟DOM 和真实DOM同步起来,这个过程中叫做协调(Reconciliation);
这种编程的方式赋予了React声明式的API,只需要告诉React希望让UI是什么状态,React来确保DOM和这些状态是匹配的,不需要直接进行DOM操作,可以从手动更改DOM、属性操作、事件处理中解放出来。
 

(五)购物车案例

1. 练习要求

实现的效果图
实现的效果图
  • 在界面上以表格的形式,显示一些书籍的数据;
  • 在底部显示书籍的总价格;
  • 点击+或者-可以增加或减少书籍数量(如果为1,那么不能继续-);
  • 点击移除按钮,可以将书籍移除(当所有的书籍移除完毕时,显示:购物车为空~);

2. 页面结构

首先通过render函数编写大概的界面布局结构
 

3. 总价格显示

 

4. 移除书籍和购物车为空

 

5. 书籍数量修改

 
 
 
notion image

 
使用Notion笔记快速搭建个人博客脚手架 × 组件化开发(上)
Loading...
😈Zabanya
😈Zabanya
一名喜欢瞎折腾选手
公告
 
部分教程类文章篇幅过大,可能会导致加载时间稍微偏长,非常感谢您的耐心等待 ~ 🎉