day-071-seventy-one-20230516-react基础-业务开发思路
react基础
-
MVVM(Vue) 与 MVC(React)
- MVVM: Model View ViewModel,双向数据驱动。
- 基本构成:
- Model:数据层
- View:视图层
- ViewModel:视图模型,Vue主要实现的。
- 基本构成:
- MVC: Model View Controller,单向数据驱动。
- 基本构成:
- Model:数据层。
- View:视图层。
- Controller:控制层。
- 学习这两类框架的基本思路:
- 要学会如何构建数据与状态,以及修改状态的方法。
- 例如:setState、useState…
- 当基于特定的方法修改状态后,要学会通知视图更新。
- Vue中会对对象的每一项进行get/set劫持
- Vue2:Object.defineProperty
- Vue3:Proxy
- this.age=17 => 修改了对象中的某个属性 => 触发set劫持函数 => 通知视图更新。
- 所以数据就有了响应式与非响应式数据的区别。
- 响应式:经过get与set劫持的对象及对象属性。
- 非响应式:没有经过get与set劫持的对象及对象属性。
- React 并没有对数据进行特殊的处理。
- this.state.age=17 => 只是单纯的修改值
- this.setState({age:18}) => 只有基于特定的方法,进行修改状态值,才会让视图更新。
- React的MVC框架中,实现了数据驱动视图渲染,也就是M->V。
- 也就是说基本上要放弃DOM的手动更改,DOM更新需要数据进行修改之后的由框架来完成。
- 实际操作中,就是返回一个jsx元素。
- jsx元素就是一个数据。
- 至于jsx元素如何转变成页面上的DOM结构,就交由React框架来做处理了。
- 实际操作中,就是返回一个jsx元素。
- 也就是说基本上要放弃DOM的手动更改,DOM更新需要数据进行修改之后的由框架来完成。
- Vue中会对对象的每一项进行get/set劫持
- 要学会如何构建视图。
- 主要构建视图的方式:
- Vue中是基于主要的
template标签语法
与 jsx语法构建视图的。 - React中构建视图的语法是:jsx。
- Vue中是基于主要的
- 不论是Vue还是React,构建的视图都有一套编译机制:
- VirtualDOM(虚拟DOM) -> DOM-diff -> 真实DOM。
- 主要构建视图的方式:
- 如果视图中的表单元素内容发生改变:
- Vue会基于v-model指令,自动监听表单内容的变化,然后把对应的状态值进行自动更改,所以Vue是双向驱动的框架。
- 具有视图驱动数据更新的机制,即V->M。
- 也就是说,表单元素数据的更新,是Vue内部实现的。
- 而vue的双向数据绑定,大多是需要使用表单元素才能驱动数据更新。
- React默认没有实现视图驱动数据,需要开发者手动对表单元素进行事件绑定和监听,手动去修改对应的状态,所以React被称为单向数据驱动的框架。
- 这个也就是说,主要通过对表单元素的处理监听,也大多可以双向绑定。只是这一步,需要让开发者自己去完成。
- Vue会基于v-model指令,自动监听表单内容的变化,然后把对应的状态值进行自动更改,所以Vue是双向驱动的框架。
- 要学会如何构建数据与状态,以及修改状态的方法。
- 基本构成:
- 具体到技术选型,主要看团队氛围与编程喜好,以及技术负责人。一般实际开发中的性能都差不多。
- vue更简单些,招人也更容易。
- React上限高些,更灵活。
- MVVM: Model View ViewModel,双向数据驱动。
-
关于版本问题。
- Vue框架,涉及现代开发,一般都依赖于脚手架@vue/cli或vite。
- Vue2 主流版本,目前2023年中国用得最多。
- 全家桶:
- @vue/cli – 脚手架。
- Vue2 – Vue核心。
- 做双向数据绑定。
- Vuex3 – 做全局状态管理。
- VueRouter3 – 做页面路由跳转。
- 根据路由显示与隐藏某些组件的。
- UI框架:ElementUI与Antdv1或vant2或iview。
- 全家桶:
- Vue3 技术与业务新的趋势。
- 全家桶:
- vite – 脚手架。
- Vue3 – Vue核心。
- 做双向数据绑定。
- Pinia 或 Vuex4 – 做全局状态管理。
- 项目中主要用Pinia。
- VueRouter4 – 做页面路由跳转。
- 根据路由显示与隐藏某些组件的。
- UI框架:ElementPlus或Antdv或vant3。
- TypeScript – 加强JavaScript语言功能。
- 全家桶:
- Vue2 主流版本,目前2023年中国用得最多。
- React框架
- 原生方案:
- create-react-app 或 vite — 脚手架。
- 主要用的create-react-app。
- React18与React16 — React核心。
- 15版本已经老了,React16才添加hook语法。
- React17基本上保持React16的特点。
- 主要学的是React18,因为React16上有的东西,它基本上都支持。
- redux 或 mobx 或 zustand – 做全局状态管理。
- 主要用的redux。
- react-router-dom5 或 react-router-dom6 — 页面渲染。
- 与React每个版本都有些兼容,两个都要学。
- Antd 或 AntdMobile — UI框架组件库。
- Antd – PC端。
- AntdMobile – 移动端。
- TypeScript – 加强JavaScript语言功能。
- create-react-app 或 vite — 脚手架。
- 淘系React方案:
- umi
- antdpro
- 核心:
- redux或redux-saga。
- react-router-dom5。
- dva – 数据流方案。
- 核心:
- 原生方案:
- 小程序开发
- 原生小程序开发。
- Vue:uni-app。
- React:taro。
- NativeApp开发
- 原生开发。
- Vue:uni-app。
- React:react-native。
- flutter:Dart语言。
- 项目应用级体系(主流):
- 全栈工程师体系2023年
- Node – 语言核心。
- Express(或Koa2、Egg) — web服务器。
- 主要用的Express。
- Mongodb(或MySQL、SQLServer…) — 数据库。
- 主要用的Mongodb。
- Linux(Nginx、Docker) — 服务器。
- Linux是操作系统,学命令行。
- Nginx是代理服务器,类似于前端的web服务器。
- Docker是容器管理,不太清楚。
- nuxt.js/next.js — SSR渲染。
- nuxt.js 是Vue框架。
- next.js 是React框架。
- …
- 微前端
- 低代码
- 可视化
- 2D:canvas(ECharts)
- 3D:webGL(three.js)
- …
- Web3(区块链)
- Electron框架 – PC桌面端
- …
- 全栈工程师体系2023年
- Vue框架,涉及现代开发,一般都依赖于脚手架@vue/cli或vite。
-
jsx语法
- jsx:
JavaScript and xml
(html) - 为了让vscode支持jsx语法,让其可以有提示、可以格式化等。
-
故而把需要构架视图的js文件,其后缀名改为
.jsx
。<div>//没有提示`</div>`;
<div></div>//会有`</div>`的提示;
-
可以把每一个
.jsx
理解为一个单文件组件。- 可以构建视图。
-
.jsx
的文件,在webpack打包的时候,也是按照js的方式处理。 -
把所有的视图(内容)编译后,都放在#root的容器中渲染。
-
React18
是这样写的。import React from 'react' import ReactDOM from 'react-dom/client'//想构造web页面,就引入'react-dom/client'。 // ReactDOM.createRoot()// 创建一个根节点。 const root = ReactDOM.createRoot(document.getElementById('root')) // 通过根节点中的render把jsx中的DOM对象放到根节点中。 root.render( <div> 珠峰培训 </div> )
-
在React16中,这部分是这样操作的:
import React from 'react' import ReactDOM from 'react-dom/client'//想构造web页面,就引入'react-dom/client'。 ReactDOM.render( <div> 珠峰培训 </div>, document.getElementById('root') )
-
-
-
在jsx语法中,我们基于
{}
大胡子语法绑定JavaScript表达式。{}
也简称胡子语法。- JavaScript表达式:执行有结果(返回值)的操作。
-
变量
{title}
-
值
{"字符串值"}
-
数学运算
{1+1}
-
判断操作
{1===1?"ok":"no"}
-
只能使用三元运算符。
- if/else、seitch/case不算表达式。
- 它们算操作语句,但不算JavaScript表达式。
import React from "react"; import ReactDOM from "react-dom/client"; // let isExist = true; let isExist = false; const root = ReactDOM.createRoot(document.getElementById("root")); root.render( <div> {isExist ? <button>按钮</button> : null} </div> )
- if/else、seitch/case不算表达式。
-
-
循环操作
{arr.map(item=>{return item*10})}
-
如:
{[1,2,3].map(item=>{return item*10})}
{ arr.map((item,index)=>{ return <li key={index}> //循环创建的元素,要设置唯一的key属性值 {item} </li> }) }
-
不能直接使用for循环等循环语句。
- for、while、for-in等循环,不算JavaScript表达式。
-
一般使用数组中的map语法或filter等方法。
import React from "react"; import ReactDOM from "react-dom/client"; let title = `fang-react-title`; let arr = ["选项1", "选项2", "选项3"]; const root = ReactDOM.createRoot(document.getElementById("root")); root.render( <div> <h2> {title} {100}{" "} </h2> <ul> { //arr:[<li key={0}>{选项1}</li>,...] arr.map((item, index) => { return <li key={index}>{item}</li>; //循环创建的元素,要设置唯一的key属性值。 }) } <li>选项2</li> </ul> </div> );
-
-
函数调用
-
包括自执行函数
<li>{(() => 15)()}</li>
-
-
…
-
- 在胡子语法中,嵌入不同数据类型的值,最后渲染的结果是不同的。
- 原始值类型中:除了
数字
与字符串
会直接渲染出来,其余的值,最后渲染的结果都是空-不显示。 - 对象数据类型:
- 数组对象:不会转成字符串再渲染,而是内部会把数组中的每一项都单独拿出来渲染。
- 函数对象:期望我们是基于
<Component>组件语法
这种方式调用函数组件。 - 剩下的大部分对象,是不允许直接在
胡子语法
中渲染的!-
排除两种特殊情况:
-
给元素设置style行内样式!
let styleObject = { color: "red" }; <p style={styleObject}>这是一个段落</p>
-
给元素标签设置style属性,只能使用对象语法。
<p style="color: red">这是一个段落</p>//会报错。
-
-
给元素设置style行内样式,需要使用对象的。
<p style={{ color: "red" }}>这是一个段落</p>
-
-
如果对象是jsx元素对象(
VirtualDOM
),是可以直接渲染的。<p >{<a>8888</a>}</p>
<p>{true ? <a>8888</a> : null}</p>
-
- 原始值类型中:除了
-
给元素/组件设置属性
-
如果属性值是一个字符串,直接正常设置即可
<div name="box"></div>
-
其余情况:非字符串,此时基于
{}
嵌套绑定即可,即直接用胡子语法包起来。let theName = "box" <Component name={theName}></Component>
- 例如:
-
把变量的值作为属性值。
let theName = "box" <Component name={theName}></Component>
-
传递的属性值是其它类型的。
<Component index={5}></Component>
-
- 例如:
-
特殊情况:
-
给元素设置的class要改为className。
<div className="the-class-1">the-class-1</div>
-
不必也没关系,依旧会正常渲染,但会报错。
<div class="the-class-2">the-class-2</div>//报错 Warning: Invalid DOM property `class`.不过依旧可以正常渲染。
-
-
给元素设置的style,要求其属性值必须是一个对象。
let styleObject = { color: "red" } <p style={styleObject}>这是一个段落</p>
<p style={{ color: "red" }}>这是一个段落</p>
<p style="color: red">这是一个段落</p>//会报错。
-
let num = 10 <Component name="box" x={num} y={0} className="box" style={样式对象}/>
-
-
每个视图-无论大视图还是小视图,都只能设置一个根节点。
-
小视图如果想要设置成多个根节点,则要用数组包起来。
//数组包起来。 <div> <div>1</div> <div>2</div> {[1,2,3].map(item=><p key={item}>{item}</p>)}//map返回的就是一个数组包起来一群子元素的根节点集合。 </div>
//只返回一个根节点。 let root = ReactDOM.createRoot(document.getElementById("root")); root.render( <div> <div>1</div> <div>2</div> {[1, 2, 3].map((item) => { return <p key={item}>{item}</p>; //正常 })} </div> );
-
基于map循环的时候,每一轮循环产生的上下文,都算是一个小的视图,也不允许出现多个根节点。
//map中返回多个根节点,报错。 let root = ReactDOM.createRoot(document.getElementById("root")); root.render( <div> <div>1</div> <div>2</div> {[1, 2, 3].map((item) => { return ( <p key={item}>{item}</p> <i>{item}</i> );//报错 })} </div> );
//map中返回用一个div包起来的原根节点,此时div才是根节点。 let root = ReactDOM.createRoot(document.getElementById("root")); root.render( <div> <div>1</div> <div>2</div> {[1, 2, 3].map((item) => { return ( <div> <p key={item}>{item}</p> <i>{item}</i> </div> ); })} </div> );
-
如果既不想让其只有一个根节点,也想让包外层盒子不占据层级结构,可以使用<></>–React.Fragment。
//使用<></> let root = ReactDOM.createRoot(document.getElementById("root")); root.render( <div> <div>1</div> <div>2</div> {[1, 2, 3].map((item) => { return ( <> <p key={item}>{item}</p> <i>{item}</i> </> ); })} </div> );
//使用<></>就相当于使用<React.Fragment></React.Fragment> let root = ReactDOM.createRoot(document.getElementById("root")); root.render( <div> <div>1</div> <div>2</div> {[1, 2, 3].map((item) => { return ( <React.Fragment> <p key={item}>{item}</p> <i>{item}</i> </React.Fragment> ); })} </div> );
//使用<></>设置不了key,使用<React.Fragment></React.Fragment>可以设置key。 let root = ReactDOM.createRoot(document.getElementById("root")); root.render( <div> <div>1</div> <div>2</div> {[1, 2, 3].map((item) => { return ( <React.Fragment key={item}> <p>{item}</p> <i>{item}</i> </React.Fragment> ); })} </div> );
-
-
基于胡子语法渲染的内容,都被当做普通字符串进行渲染,无法识别其内部的HTML标签。
-
类似于vue中的类似于v-text。
-
如果需要可以把字符串中的标签自动识别,就需要用到dangerouslySetInnerHTML标签,
dangerouslySetInnerHTML={{__html: html标签变量}}
;let str = `我的名字是:哈哈哈! <a href=''>呵呵呵</>` <div dangerouslySetInnerHTML={{ __html: str }}></div>
-
-
jsx中的事件绑定,是基于React内部的合成事件处理的,以onXxx的形式。
<div onClick={ev=>{ //..... }}></div>
let root = ReactDOM.createRoot(document.getElementById("root")); root.render( <div onClick={() => { console.log("打印"); }} > <div>1</div> <div>2</div> </div> );
- jsx:
-
jsx的底层渲染机制
- 基于
babel-preset-react-app
语法包,把jsx
编译为React.createElement()
这种格式。-
凡是HTML标签或组件,都会被编译为
React.createElement()
这种格式。React.createElement( 标签名/组件名, 属性对象/null, //->存储了给标签设置的各种各样的属性 后续参数都是其子节点 )
-
未转化之前:
<div onClick={() => { console.log("打印"); }} > <div></div> <div id="aa" class="theclassname">fang子元素</div> </div>
-
转化之后:
/*#__PURE__*/React.createElement("div", { onClick: function onClick() { console.log("打印"); } }, /*#__PURE__*/React.createElement("div", null), /*#__PURE__*/React.createElement("div", { id: "aa", "class": "theclassname" }, "fang\u5B50\u5143\u7D20"));
-
转化清简之后:
React.createElement( "div", { onClick: function onClick() { console.log("打印"); } }, React.createElement("div", null), React.createElement( "div", { id: "aa", "class": "theclassname" }, "fang\u5B50\u5143\u7D20" ) );
-
-
虚拟DOM对象:框架自己创建的一个普通对象。
-
- 把createElement方法执行,创建出对应的VirtualDOM(虚拟DOM对象),也被称为:jsx元素对象。
-
虚拟DOM:框架内部自己构建的一个对象,用来描述和记录元素标签的相关特征。
VirtualDOM = { $$typeof: Symbol(react.element), //标识 type: "div", //标签名或者组件 key: "10", ref: "AAA", props: { 除key/ref外其它设置的属性, children:子节点集合「可能没有、可能是一个值、可能是一个数组」 } }
-
真实DOM:交给浏览器渲染的、或者是渲染完毕后看到的元素标签、再或者是基于js获取到的原生DOM对象(有浏览器内置的属性和方法)。
-
- 基于render方法,把创建的VirtualDOM渲染为真实的DOM。
- 基于
-
jsx语法
与<template>模块语法
-
Vue2/Vue3中,既有
<template>模块语法
,也支持jsx语法。 -
React中只有jsx语法。
-
对于同一个需求:
-
vue思路-
<template>模块语法
state/data = { flag:true, arr:[...], text:'...', level:2 } <template> <div class="box"> <button v-if="flag">{{text}}</button> <span v-for="item in arr" :key="item.id"> {{item.title}} </span> <h1 v-if="level===1">我是标题</h1> <h2 v-else-if="level===2">我是标题</h2> <h3 v-else-if="level===3">我是标题</h3> </div> </template>
-
React思路-
jsx语法
state/data = { flag:true, arr:[...], text:'...', level:2 } <div className="box"> {flag?<button>{text}</button>:null} {arr.map(item=>{ return <span key={item.id}> {item.title} </span> })} {React.createElement(`h${level}`,null,'我是标题')} </div>
-
-
JSX语法比
<template>模版语法
具备更强的编程性(或者template是弱编程性的语法),构建视图更加的灵活方便!
-
React视图编译的机制
- 总结:React视图编译的机制。
- 把jsx语法,基于
babel-preset-react-app
得到React.createElement()
的层级结构,React.createElement()
创建出相应的VirtualDOM
。 - 如果是第一次渲染视图,直接基于
render()
方法,把VirtualDOM
变为真实DOM
,并且把本次创建的VirtualDOM
缓存起来。 - 当视图更新的时候,会重新的按照最新的数据,把 jsx 编译为一个全新的
VirtualDOM
,并且用新的VirtualDOM
和之前旧的VirtualDOM
进行对比(DOM-diff
),计算出差异的部分,最后只把差异的部分进行更新!
- 把jsx语法,基于
业务开发思路
- 业务开发涉及。
- 组件式开发 – 增加可复用性和可维护性。
- 组件间通信。
- 全局数据管理。
- 单页面应用。
- 基于路由更换组件。
- 组件式开发 – 增加可复用性和可维护性。