Skip to content

React 基础语法

个人感觉,就是同 jsp形式上类似

    1. 声明式编程是目前整个大前端开发的模式:Vue、React、Flutter、SwiftUI;
    2. 它允许我们只需要维护自己的状态,当状态改变时,React可以根据最新的状态去渲染我们的UI界面;
    3. 组件化、声明式、跨平台、响应式、虚拟DOM、通过jsx技术有更纯粹的 javaScripts 语法
  • 组件化开发页面目前前端的流行趋势,我们会将复杂的界面拆分成一个个小的组件;

    1. 2013年,React发布之初主要是开发Web页面;

    2. 2015年,Facebook推出了ReactNative,用于开发移动端跨平台;(虽然目前Flutter非常火爆,但是还是有很多公司在使用ReactNative-);

      用开发跨平台移动端的适配方案——native 是原生的意思

  1. 2017年,Facebook推出ReactVR,用于开发虚拟现实Web应用程序;(VR也会是一个火爆的应用场景);
  • React 开发依赖3个库

    1. react:包含 react 所必须的核心代码

    2. react-domreact 渲染在不同平台所需要的核心代码,主要用于页面渲染

    3. babel:将 jsx 转换成React,创建虚拟节点方法代码的工具

      如果使用 React.createElement 来编写源代码,它编写的代码非常的繁琐和可读性差。让 babel 帮助我们将 jsx 转换成 React.createElement

      <script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
      <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
      <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
  • 是因为 react 包中包含了react webreact-native 所共同拥有的核心代码

    • react-dom针对 webnative 所完成的事情不同
    • web端:react-dom会将jsx最终渲染成真实的 DOM,显示在浏览器中
    • native端:react-dom会将 jsx 最终渲染成原生的控件(比如 Android 中的 ButtoniOS 中的UIButton)。
    1. 引入依赖库
    2. 重点:一定要设置 script 标签的类型 <script type="text/babel">
      • 就像 vue 中使用 jsx 一样,要添加 lang=‘jsx'

react 中是借助于 babel 解析的,相对与 vue 非常的简单,就是对应 jsx 生成 React.createElement('div',{title: "zhangsan"}, "这里是内容")

vue 中的解析,解析的过程中还需要一些指令进行处理

  • (1)、所以 jsx babel 转化之后 的代码就是,React.createElement(),相似于 vue 中的 h() 函数

    因此,可以直接使用 React.createElement()react 代码,但是可读性非常差

  • (2)、React.createElement 执行最终创建出来一个 ReactElement对象,组成了一个JavaScript的对象树

JavaScript的对象树就是虚拟DOM(Virtual DOM)

  • (3)、最后将虚拟dom,同步渲染成真实的dom,展示到页面上。
  1. 可以快速的进行 diff 算法,来决定哪些节点需要更新,哪些不需要更新

  2. 跨平台,react-navtive

  3. 通过虚拟 DOM 将命令式编程转到了声明式编程的模式

    它是一个相对简单的 JavaScript 对象 通过声明 ReactDOM.render 让虚拟 DOM 和 真实DOM同步起来,这个过程中称为更新 (patch),又被称为**“比对”(diffing)** 或**“协调”(reconciliation)**。

    • 只需要告诉 React 希望让UI是什么状态
    • React 来确保 DOM 和这些状态是匹配的
    • 不需要直接进行 DOM 操作,就可以从手动更改 DOM、属性操作、事件处理中解放出来

    一个运行时渲染器将会遍历整个虚拟 DOM 树,并据此构建真实的 DOM 树。这个过程被称为挂载 (mount)。

  • react没有双向绑定的概念,每次更新页面和值都需要手动调用指定的api来触发

  • 尽管每次修改都会新建一个描述整个domVNode,但只会更新实际改变了的内容的dom元素。

    • 进行优化,子组件没发生变化的话,就只会重新生成当前组件的VNode
  • React 框架在接收到用户状态改变通知后,会根据当前渲染树,结合最新的状态改变,通过Diff算法,计算出树中变化的部分、自动且高效的同步到虚拟 DOM,最后再批量同步到真实DOM只更新变化的部分DOM操作)。

    • 浏览器的重绘和回流都是比较昂贵的操作,如果每一次改变都直接对DOM进行操作,这会带来性能问题,而批量操作只会触发一次DOM更新
    • 而不是每次改变都去操作一下DOM。
    • 从而避免整棵树重构,提高性能。
  • 如果DOM只是外观风格发生变化,如颜色变化,会导致浏览器重绘界面。

  • 如果DOM树的结构发生变化,如尺寸、布局、节点隐藏等导致,浏览器就需要回流(及重新排版布局)。

JSX是一种JavaScript的语法扩展(eXtension),也在很多地方称之为JavaScript XML,因为看起就是一段XML语法;

  • jsx 用于描述我们的UI界面,将标签 和 JavaScript 融合在一起使用
  • 不需要专门学习模块语法中的一些指令(比如v-forv-ifv-elsev-bind
  • 就是 jshtml 混在一起使用,就像是jspejs

React 认为像 vue template 模板语法那样,渲染逻辑本质上与其他UI逻辑存在内在耦合

  • 简单说,模板语法 和 js 之前存在很强的耦合性,实现业务逻辑上太依赖模板了,并没有 jsx 自由,js更好的实现思想,都要考虑模板
  1. jsx 标签不能是字符串,都需要babel进行转换

  2. react组件首字母必须大写

    因为 React 中使用 JSX 语法引入组件的时候,是根据首字母是否大写来区分react 组件还是 dom 元素。

  3. jsx 只能一个根vue2 也只能有一个根,vue3可以有多个根

  4. 通常使用分组运算符将换行的jsx当作一个整体

  5. 可以是双标签,单标签必须使用 /> 结尾,例:img 标签,后面要加 /

  6. jsx中使用 {} 包裹js代码进行执行,注释也是一样 {/* */}

  7. 在子元素(可以理解为被jsx标签包裹的 {} ) 的 {} 中如果存放的是数组的话,会将数组元素全部取出,直接显示。放的 jsx 标签的话也会显示

  8. NumberString 直接显示

  9. nullundefinedBoolean 不会显示内容,必须要转换成字符传显示 {ture.toString()}

    • 注意:但是元素会正常显示
    //布尔类型需要转换成 String 之后才可以显示,不转化的话这里是个空标签 <div></div>
    <div>{this.state.flag.toString()}</div>
  10. Object 不能作为 jsx 标签内容,会报错

~~~typescript
constructor() {
super();
this.state = {
argu3: {name: "zhangsan ", age: 98}
}
}
render() {
return (
<div>
{/* 会报错 <div>{this.state.argu3}</div> */}
</div>
)
}
~~~

11. {} 语法也可以嵌入表达式、运算表达式、三元运算符、执行一个函数。

  1. render() 函数当中返回的jsx内容( 注意:这里是jsx内容 ),就是之后React会帮助我们渲染的内容

  • 属性绑定统一 {},例:title={}, 再次提示jsx{}不是字符串,
  • class 绑定 {} + ${}

  • jsx 最后还是要转换为 js,由于 class 是关键字,所以定义的时候要使用className 替代 class尽量避免冲突

<div>
<button
className={`${this.state.flag ? 'box' : ''}`}
onClick={() => this.trigger()}
>
trigger
</button>
</div>
  • jsx不支持 style 字符串写法,style 的值本质上又是一个对象,可以 直接给 style 属性赋值 css 属性对象

  • 使用 Object 写法

    之前提到的 ( 只是被标签元素包裹的 jsx内容 不支持Object )

  • 注意:根据规范,这里的 {{}},并不是 mustache 语法

<button onClick={this.foo.bind(this)}>
foo
</button>
<div style={
{
width: "100px",
height: "100px",
backgroundColor: "green",
//绑定样式
border: `${this.state.str}`
}
}>
</div>

React中,所有的条件判断与列表渲染都和普通的JavaScript代码一致;简单来说就是js判断。

react 中的逻辑判断,就是普通的 if 语句

  • 实现 v-show 直接添加类即可
let showElement = null
if (isReady) {
showElement = <h2>准备开始比赛吧</h2>
} else {
showElement = <h1>请提前做好准备!</h1>
}
<div>{ isReady ? <button>开始战斗!</button>: <h3>赶紧准备</h3> }</div>

应用场景:在状态中取出属性值的时候,有可能这个状态在初始化或者某些情况null/undefined 的时候会报错,使用&&可以避免报错,等到真正有值的时候在去使用。

  • 用的很多
  • false 是不会显示的
<div>{ friend && <div>{friend.name + " " + friend.desc}</div> }</div>

其实就是,使用for循环组装jsx

  • 但更多的方式是使用,map组装 jsx 元素列表,直接通过 {} 语法进行展示

    以为,jsx 数组当作,元素内容的时候,会直接将jsx元素取出,直接展示

  • 例:

    books.map((item, index) => {
    return (
    //key值,要添加,方便进行diff算法
    <tr key={item.id}>
    {item.name}
    </tr>
    )
    })

开发中并不能直接通过修改state的值来让界面发生更新,

React并没有实现类似于Vue2中的Object.defineProperty或者Vue3中的Proxy的方式来监听数据的变化

  • 必须通过父组件 Component 中的 setState({}) 方法,来告知 React 数据已经发生了变化;
  • setState是一个合并的动作,没有修改的属性会保持

setState是异步的操作,并不能在执行完setState之后立马拿到最新的state的结果。

  • 如果每次调用 setState 都进行一次更新,那么意味着 render 函数会被频繁调用,界面重新渲染,这样效率是很低的;

    • 获取到多个更新之后的值,之后进行批量更新

      在同一个事件对列当中,会一次放置多个 setState ,当前对列中的异步 setState 执行之后,在执行render函数。

      render检查 stateprops 中的属性,将被修改的值,批量同步到真实dom上节省回流重绘的开销

    • 据说render执行的时机,是和屏幕的刷率有关系,不一定会等当前对列异步事件执行完毕之后执行

  • 保持 render 和 setState 修改的值保持一致性

    Section titled “保持 render 和 setState 修改的值保持一致性”
    1. 先明确 render 不是和 setState 绑定到一起的
    2. 如果 setState 为通过的话,获取到修改后的值,在计算一些逻辑的时候出现了异常这时已经修改了的数据会和页面展示的数据会存在差异
  • 一般不推荐,直接 this.state 上修改属性,这样操作是没有 SPU 优化的
    • 因此,下面第二次修改的 counter 的情况也不多见
//异步修改,state中的值。
this.setState({counter: this.state.counter+1})
/*
由于是异步的,
”且“ 函数执行“前”会将值赋值给形参(javaScript高级,函数的执行流程),
因此在将参数对象赋值的时候,
this.counter 还是上面 this.setSate 修改前的 this.counter
*/
this.setState({counter: this.state.counter+1})

传入回调函数写法

  • 让代码实现更好的内聚性。

    就是可以在回调函数中,写一些代码逻辑

  • 会将 “上一次” 修改的 stateprops回传到 “下一次” setState 的回调函数中

this.setState((state, props) => {
//可以在这里添加一些代码逻辑
return {counter: this.state.counter+1}
})
this.setState((state, props) => {
//这里的state 中的 counter 已经被修了
return {counter: this.state.counter+1}
})

setState在React的事件处理中是一个异步调用

  • state 修改完成之后,会回调第二个参数传入函数
this.setState({message: "zhangsan"}, () => {})
    1. setTimeout 和 “原生 dom 回调” 中的 setState 操作, 是同步操作
    2. 组件声明周期函数,或者 React 合成事件中( 就是 react 中的事件回调 ),是异步的
    • 默认所有的操作都放到了 “批处理” 中 都变成了异步操作(批处理)
  • 如果在某些场景一定要同步的话可以使用 react-dom包 中的 flushSync函数

    参数:callback

    import { flushSync } from 'react-dom'
    flushSync(() => {
    this.setState({ message: "你好啊, 李银河" })
    })
    console.log(this.state.message)
  • 结合 useEffect 第二个参数,依赖被修改之后自动回调
  • this.forceUpdate()
    • 不会经常用,不会有scu优化

全局的origin