考研是自己这学期的重点,但绝不是自己的全部,自己也不想把考研变得那么枯燥,于是有空还是会了解一些技术相关的知识的。但是可能是自己写文章总结惯了,很多东西往往看懂了但是不记录总结下来还是会觉得有些空虚。加上自己的学习时间也不是那么严格,于是自己累了就写,可能更新比较随意哈,多多谅解。
也许你听说过前端的三大框架React、Angular以及Vue。其中Vue里面有很多借鉴React的影子(当然也有Angular的渐进式等),Vue走的是数据驱动视图,也就是MVVM,可以说view层是Vue的重点目标。React相比于Vue发展社区更加庞大,Angular的编码规则可能相对特殊一些,近来热度不是很高。对我自己来说,我觉得web引用相比于原生的Android 或者ios引用有着很多缺陷,想了解使用前端的编码方式来实现一个Android或者ios App。
你可能说uniapp不香吗?如果是前两年,我不会反驳,因为当时微信小程序比较火,uniapp更像是从微信小程序里面扩展出来的,很多功能都与微信小程序的原生开发相似。但是现在,各大平台纷纷卷小程序,加上小程序实际使用率可能不是很客观,因此热度迅速降了下来。也从中可以看出app的强大与稳定。我这里说的是了解,但是如果让我上手Android开发,我想说这个不是很现实,我尝试过,觉得Android更像是一个api调用工程师,B站的教程也没有达到自己的预期,而且Android的热度降了好多,这种情况下不是很划算。React Native以及面试上对于前端的要求可能是我学这个的很大一部分原因。
当然,这也与自己对于学习的思考有关,可以说我了解的都是比较浅的知识,但是我大方向上还算的上是一个以前端为主的“全栈”摸鱼怪(需要说明的是全栈的定义里面需要懂一部分硬件知识)。很多时候我自己也在反思,自己总听到张三说,你得有个方向,学习得有选择性,什么东西该学什么东西不学得想清楚,其实默默回头发现自己依然浪费了很多宝贵的学习时间,在你纠结学什么的时候,别人已经沉进去了。少纠结,多搞点实在的,这也许就是我的学习心态吧。
React是一个构建用户界面的JavaScript库,起源于FaceBook,由FaceBook软件工程师Jardon Walke开发,2013年开源。React里面提出虚拟dom,相比于Jquery,Jquery提供的是用户角度的代码编写简化,提供大量的dom函数,方便用户操作dom,但是很多时候我们修改dom的时候可能并不需要涉及大量的dom的删除的新增。虽然React多了一个虚拟dom中间层,减少了很多dom操作函数并且能够很友好的处理兼容性问题,不像之前需要判断浏览器版本什么的,一切由react来帮助解决,虚拟dom的使用能够竟可能的减少对于dom的更改,里面有一个非常经典的修改检测的diff算法。React属于声明式框架,更加强调结果(传统编程属于命令式,更加强调过程,按照实际代码依次执行);拥有组件化的开发思想。你可能想说Vue也有,但是需要明白Vue毕竟诞生的更晚,这里可以稍稍忽略Vue,等之后对于React了解到一定程度的时候再来分析二者之间的差别。
首先咱不适用类似于vue-cli这样的脚手架,直接使用react提供的解析函数,从官网下载js文件(当前最新版本为18.*):
https://unpkg.com/react@18/umd/react.development.js
https://unpkg.com/react-dom@18/umd/react-dom.development.js
构建文件夹引入js文件,得到如下demo:
<body>
//注意引入顺序,react-dom在后面
<script src="./react.development.js"></script>
<script src="./react-dom.development.js"></script>
<div id="root"></div>
<script>
/**
* param1:需要创建的网页标签
* param2:标签属性初始化
* param3:子级元素或者里面的内容
*/
const myDiv = React.createElement("div", {
className: "mydiv"
}, "Hello React!");
//基于dom元素创建一个React根元素
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(myDiv);
</script>
</body>
得到效果如下:
从上面也可以看出来React看中结果的这一大特点,下面对React.createElement函数方法的第二个参数详细说明(初始化元素的属性参数):
const myDiv = React.createElement("div", {
id: 'mydiv',
type: 'id',
className: "hello",//可以使用class,但是为了和类区分开来,建议使用className,
onClick: () => {
alert('Hello React!');
}//注意函数需要使用小驼峰命名
}, "Hello React!");
JSX能够减少React.createElement的书写,能够使代码编写更加快捷舒服。JSX需要额外配置一个叫babel的JavaScript解析器,这个时候你可能会怀念脚手架的好了,没事其实配置也不难,这里推荐一个CDN平台可以方便的找到网上教程里面的cdn一致的源码,后期这里利用这个平台将资源本地化提高用户体验,减少网络延时,首先找到一个babel的js文件(在这个平台里面用的是babel-standalone
),下载之后放到代码的同级目录下,然后引入即可,babel官网也给出了测试转化的网页:
给出使用的代码样例(特别强调加上这个type="text/babel"
):
<body>
<script src="./react.development.js"></script>
<script src="./react-dom.development.js"></script>
<script src="babel.min.js"></script>
<div id="root"></div>
<script type="text/babel">//注意这个type="text/babel"必须加上,提示需要使用babel进行解析
const root = ReactDOM.createRoot(document.getElementById("root"));
const div = <div>我是一个按钮标签。</div>;
root.render(div);
</script>
</body>
接下来就是探究JSX还原React的reactElment()
函数基本功能了,首先明确以下几点:
(1)在babel中必须要用className
来代替class
;
(2)style
需要使用对象来代替,并且样式里面需要使用小驼峰
;
(3)undefined
的数据不会被展示;
(4){}
表示里面的值是变量;
给出示例程序如下(如果你能理解如下代码,JSX算是入门了):
const root = ReactDOM.createRoot(document.getElementById("root"));
const fruits = ['西瓜', '快乐水', '菠萝'];
const div = <div
id="box1"
className="box"
onClick={() => {
alter("点击事件被触发!");
}}
style={{ backgroundColor: 'pink', color: 'red', border: '1px blue solid' }}>
这是一个div标签
<ul>
{fruits.map((item, index) =><li style={{ color: 'blue' }}>{index}-{item}</li>)}
</ul>
</div>;
root.render(div);
理解React里面的虚拟dom可以说是了解这个框架的很重要的一部分。如果不能够很好的理解虚拟dom那么使用React开发总会觉得少了点什么。下面就来结合大佬们的文章分享一下自己的理解。
虚拟dom主要是针对于频繁大量的操作dom对象存在的性能问题,频繁而大量的dom操作需要浏览器不断的进行重绘,很可能影响用户体验。这种重绘在浏览器中进行,更加贴近而于用户层,虚拟dom是在内存中进行的,效率相对更高,基本原理是利用新dom结构里面的数据构建出一颗新的虚拟dom树,将新的得到的dom树与旧的dom树(这个旧的dom树会由React在内存中自动维护)进行比较,得到一个从旧的dom得到新的dom的最少或者说是最佳操作方法
,然后在旧的dom上需要更改的地方进行更改。站在这个角度上,有了虚拟dom能够一定程度上避免原生dom操作的暴力
,但是由于新旧两个dom树的比较一定程度上是增加了额外的时间和空间开销
。虚拟dom一定更佳吗?这个显然是不对的,比如当页面只需要进行少量修改,还是用新旧两颗dom树分析一遍就显得有些做无用功了。
由上面的分析可以发现,React的性能很大程度上依赖于新旧两颗dom树的比较算法了。是每一个属性子元素都递归比较吗?不,不是的,这样造成的时间复杂度极高,而且网页中的元素通常是修改、增加和删除,操作往往以修改为主,全部深入比较可能有些场合下还不如直接删了重新构建一个虚拟dom节点来的更快。在React通常有如下三种思路:
(1)逐层比较虚拟dom元素
,除了想流程图那种特殊需求,一般节点的跨层级移动还是比较少的,于是可以直接逐层来比较元素,如果同一层中的节点元素不同删除该节点元素重新建立新的节点元素,这样能够减低比较的深度,减少时间复杂度。
(2)组件级比较虚拟dom元素
,React是组件化构建应用的,组件级比较是拿新虚拟dom结构去比较旧的dom结构,如果两个组件结构不一样,直接暴力替换旧的组件以及其子组件。
(3)子节点比较
,对于上面的组件及比较可能会大面积更新局部树,可能这个树只是顺序不一样,这样显得有些得不偿失了。子节点比较弥补了这一不足,能够尽可能少的操作dom树实现更新,比如如果是同层次的子级元素交换了顺序,只需要稍微调整就行,而不必删除后重建,比如下图中将A移动到D之后就行,对于不同的子节点是通过key来标记的。
和之前Vue学习一样,利用npm手配能够加深理解,React虽然也有脚手架(Create-React-App),但是脚手架确实太舒服了。先不使用了解一下能够更好的理解原理,否则很可能成为那种写页面的或者组件的低阶码农了。配置方法比较简单:
(1)新建一个文件夹,执行npm init -y
(使用npm进行包管理);
(2)在根目录下创建一个public目录,然后创建一个index.html入口文件;
(3)在根目录下创建一个src目录并创建一个index.js文件;
(4)在终端执行yarn add react react-dom react-scripts -S
命令安装依赖,推荐使用yarn,速度更快(来源于Facebook)。
解释:之所以第二步和第三步这样创建项目目录,是因为react-scripts
里面内嵌了Webpack
来打包项目,而webpack默认配置情况下的操作为:将src目录下的index.js文件嵌入到public目录的下的index.html里面,对于其他资源直接都转向js文件,然后逐一引入这些文件,形成一个单页面模式
的应用。下面给出public/index.html
里面的内容:
DOCTYPE html>
<html lang="en">
<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>手动创建React项目title>
head>
<body>
<div id="root">div>
body>
html>
src/index.js
文件里面的内容如下:
//src/index.js入口文件
import reactDom from 'react-dom/client';
const app = (
<div>
<h1>这是一个React项目!</h1>
</div>
);
const root = reactDom.createRoot(document.getElementById('root'));
root.render(app);
接下面便是项目的打包了,有没有感觉和Vue中使用Webpack一模一样。执行npx react-scripts build
打包项目,你可能想问问什么不是npm,其实npx可以理解成一次性的npm,如果你没有预先装这个react-scripts
依赖,npx能够先不给报错,先去安装一下这个react-scripts
依赖,项目打包完成之后把这个依赖给你删了,如果你已经有了直接使用即可。这个权当了解即可,可能有些工具react-scripts还需要在使用的时候进一步获取,但是由于不同代码需要的依赖差别较大,所以为了节省内存没有全部下载。
接下来说几个优化点,可以在package.json
的scripts
里面配置脚本,省得每次都要使用npx执行那么长的指令,也使得项目的执行更加普遍化:
"start": "react-scripts start",
"build": "react-scripts build"
这样打包就只需要使用npm run build
和npm run start
。可以发现,这样每次都需要打包然后修改为客户端路径不是很优雅,于是就有了react-scripts
和webpack
联合提供的"react-scripts start"
执行(由于配置了脚本,执行只需要使用npm run start
即可)。这样就能完成类似于vue里面的热重载了。
(1)理解React的虚拟DOM
参考视频教程:尚硅谷李立超——React