React Hooks —— useState异步更新队列、闭包、浅比较深入理解

2023-11-15

文章目录

useState

作用:在函数组件中使用状态,修改状态值可让函数组件更新,类似于类组件中的setState

语法:

const [state, setState] = useState(initialState);

返回一个 state,以及更新 state 的函数

seXXX(value)修改状态值为value,并通知视图更新。注意,不同于类组件setState的部分更新语法,而是直接修改成value

import React, { useState } from "react";
export default function Demo(props) {
    let [num, setNum] = useState(10);
    const handler = () => {
        setNum(num + 1);
    };
    return <div>
        <span>{num}</span>
        <button onClick={handler}>新增</button>
    </div>;
};

函数组件【Hooks组件】不是类组件,所以没有实例的概念,调用组件不再是创建类的实例,而是执行函数并产生一个私有上下文而已,所以在函数组件中不涉及this的处理

设计原理

类组件只在初次渲染时创建一个实例,之后的更新都是按照生命流程走,并不是重新创造实例。

函数组件的每一次渲染或更新是让函数重新执行,也就是useState会被重新执行,产生一个全新的私有上下文,内部的代码也重新执行

// 函数组件每一次渲染/更新,都具备独立的闭包
import React, { useState } from "react";
export default function Demo(props) {
    let [num, setNum] = useState(10);
    const handler = () => {
        setNum(100);
        setTimeout(() => {
            console.log(num); //10
        }, 1000);
    };
    return <div>
        <span>{num}</span>
        <button onClick={handler}>新增</button>
    </div>;
};

实现原理

  1. 执行handle方法时,由于所用到的setNum num都不是当前作用域的私有变量,所以里面会沿着作用域链找到上级上下文里面的num和setNum(闭包)

  2. 每次更新都重新执行一次内部的代码、都创建一个新的私有上下文如EC(DEMO2),涉及的函数需要进行重新构建。这些函数的作用域会沿着函数的作用域链向上查找,找到每一次执行DEMO产生的新的闭包

  3. 每一次执行DEMO函数,也会把useState重新执行。但是:

    • 返回的状态:只有第一次设置的初始值会生效,其余以后再执行,获取的状态都是最新的状态,而不是初始值。

    • 返回的修改状态的方法:每一次都是新的方法函数

    • 每次运行useState返回的结果都用新的num和setNum变量保存,因此状态和修改状态的方法的地址和之前的都不同,是全新的

在这里插入图片描述

那么它是如何确保每一次获取的是最新状态值,而不是传递的初始值呢?

// 核心原理:闭包
var _state; // 创建全局state。
function useState(initialState) {
  _state = _state | initialState;
  function setState(state) {
  	if(Object.is(_state, value)) return;
  	if(typeof value === 'function'){
  		_state = value(_state) // 相当于传入prevalue后,return经过处理得到的新value
  	}else{
  		_state = value
  	}
    // 通知视图更新 
    //...重新渲染组件
  }
  return [_state, setState]; // 数组是新的变量,里面的每项自然也是新的,栈地址也不相同 
}

let [num1, setNum] = useState(0); //初始时num1=0  setNum=setState 0x001
setNum(100); //=>_state=100 通知视图更新
// ---
再次执行整个函数组件,在执行到useState的时候:
let [num2, setNum] = useState(0); //由于初次渲染时,全局state被赋值了,不再为undefined,所以不再赋值为initialState
在内部又产生了一个新的setState,地址和之前不同,使用这次新的闭包作为父级上下文
最后返回新的state和新的setState并被声明为新的变量
num2=100  setNum=setState 0x002

setXXX沿着作用域查找闭包的理解——与同步异步无关

在这里插入图片描述

第一个setTimeout沿着作用域链找到的闭包里的num是初始渲染的num,和setNum后产生的新的闭包(作用域)无关,因此输出0

更新多状态

方案一:类似于类组件中一样,让状态值是一个对象(包含需要的全部状态),每一次只修改其中的一个状态值——setXXX不支持类组件setState的partial state change

import React, { useState } from "react";
export default function Demo(props) {
    let [state, setState] = useState({
        x: 10,
        y: 20
    });
    const handler = () => {
        // setState({ x: 100 }); //state={x:100}
        setState({
            ...state,
            x: 100
        });
    };
    return <div>
        <span>{state.x}</span>
        <span>{state.y}</span>
        <button onClick={handler}>处理</button>
    </div>;
};

问题:不能像类组件的setState函数一样,支持部分状态更新!

方案二:执行多次useState,把不同状态分开进行管理「推荐方案」——解耦

import React, { useState } from "react";
export default function Demo(props) {
    let [x, setX] = useState(10),
        [y, setY] = useState(20);
    const handler = () => {
        setX(100);
    };
    return <div>
        <span>{x}</span>
        <span>{y}</span>
        <button onClick={handler}>处理</button>
    </div>;
};

更新队列机制【updater,异步批处理】——异步和闭包是两码事

和类组件中的setState一样,每次更新状态值,也不是立即更新,而是利用了更新队列updater机制来处理

①遇到setState会立即将其放入到更新队列中,此时状态和视图还都未更新

当所有的代码操作结束,会刷新队列,也就是通知更新队列中的所有任务执行:把所有放入的setState合并在一起执行,只触发一次状态更新和视图更新

  • React 18 全部采用批更新
  • React 16中也和this.setState一样,只在合成事件/生命周期函数中异步,在定时器、手动DOM事件绑定等操作中同步

在这里插入图片描述

  • 可以基于flushSync刷新渲染队列

检验方式一:在handler里面修改state之后直接log ❌

不能在handler里面修改state之后直接log,因为这时log的变量仍然是上一次闭包中的,无论同步还是异步更新,都只能是上一个闭包中的值

因此,每次log的结果都是上一次的state

import React, { useState } from "react";
import { Button } from 'antd';
import './Demo.less';
import { flushSync } from 'react-dom';

const Demo = function Demo() {
    let [x, setX] = useState(10),
        [y, setY] = useState(20),
        [z, setZ] = useState(30);

    const handle = () => {
        setX(x + 1);
        console.log(x);
        // 1.异步批处理:所有的setXXX操作放到更新队列里面,执行完所有操作之后才会一次清空更新队列,因此console.log先执行
        // 2.闭包:由于handle始终拿到的是父级作用域的闭包,也就是更新前的闭包
        setY(y + 1);
        setZ(z + 1);
    };
    return <div className="demo">
        <span className="num">x:{x}</span>
        <span className="num">y:{y}</span>
        <span className="num">z:{z}</span>
        <Button type="primary"
            size="small"
            onClick={handle}>
            新增
        </Button>
    </div>;
};

export default Demo;

检验方式二:比较输出”验证值的次数“

若同步更新,那么会顺序输出“render”三次

若异步更新,则只在最后批处理更新一次,所以只输出一次

import React, { useState } from "react";
import { Button } from 'antd';
import './Demo.less';
import { flushSync } from 'react-dom';

const Demo = function Demo() {
    console.log('RENDER渲染');
    let [x, setX] = useState(10),
        [y, setY] = useState(20),
        [z, setZ] = useState(30);

    const handle = () => {
        setX(x + 1);
        setY(y + 1);
        setZ(z + 1);
    };
    return <div className="demo">
        <span className="num">x:{x}</span>
        <span className="num">y:{y}</span>
        <span className="num">z:{z}</span>
        <Button type="primary"
            size="small"
            onClick={handle}>
            新增
        </Button>
    </div>;
};

export default Demo;

只更新了一次,说明是异步执行的,与位置无关

在这里插入图片描述

更新队列flushSync设置同步操作

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      l

异步操作与闭包的深入理解

在这里插入图片描述

异步:handle里面的10次setX都会放在更新队列里面,然后在其他事情都做完之后,批处理一次更新完毕所有队列中的数据和视图,因此只’RENDER渲染’一次

闭包:x最后的状态值是11,因为handle里面的所有x都是在上一级闭包中拿到的,都是10,因此批处理中10个setX都是将x更新为11

setXXX的两种传参方式

1.直接传入新对象,不支持this.setState的部分更新

2.函数式更新——配合for循环、updater机制可以实现结果累计、只更新状态和视图一次 setXXX(prev => { return curV; })

如果新的 state 需要通过使用先前的 state 计算得出,那么可以将函数传递给 setState;该函数将接收先前的 state,并返回一个更新后的值!

惰性初始state——复杂的初始化逻辑只执行一次

如果初始 state 需要通过复杂计算获得,则可以传入一个函数,在函数中计算并返回初始的 state,此函数只在初始渲染时被调用,之后更新视图以后,状态值不再是undefined,所以不会再执行初始的惰性回调

import React, { useState } from "react";
export default function Demo(props) {
    let [num, setNum] = useState(() => {
        let { x, y } = props;
        return x + y;
    });
    return <div>
        <span>{num}</span>
    </div>;
};
import React, { useState } from "react";
export default function Demo() {
    let [num, setNum] = useState(10);
    const handler = () => {
        for (let i = 0; i < 10; i++) {
            // 函数式更新
            setNum(num => {
                return num + 1;
            });
        }
    };
    return <div>
        <span>{num}</span>
        <button onClick={handler}>处理</button>
    </div>;
};
// 核心原理:闭包
var _state; // 创建全局state。
function useState(initialState) {
  _state = _state | initialState;
  if(typeof _state === 'undefined'){
      if(typeof initialState === 'function'){
          _state = initialState(); // 惰性初始化
      }else{
          _state = initialState;
      }
  }
  function setState(value) {
  	if(Object.is(_state, value)) return;
  	if(typeof value === 'function'){
  		_state = value(_state) // 相当于传入prevalue后,return经过处理得到的新value
  	}else{
  		_state = value
  	}
    // 通知视图更新 
    //...重新渲染组件
  }
  return [_state, setState]; // 数组是新的变量,里面的每项自然也是新的,栈地址也不相同 
}

let [num1, setNum] = useState(0); //初始时num1=0  setNum=setState 0x001
setNum(100); //=>_state=100 通知视图更新
// ---
再次执行整个函数组件,在执行到useState的时候:
let [num2, setNum] = useState(0); //由于初次渲染时,全局state被赋值了,不再为undefined,所以不再赋值为initialState
在内部又产生了一个新的setState,地址和之前不同,使用这次新的闭包作为父级上下文
最后返回新的state和新的setState并被声明为新的变量
num2=100  setNum=setState 0x002

优点:如果将回调里的逻辑写到外面,则一旦视图更新,不管是第一次还是后续更新的时候,这段逻辑都会执行。即使在更新阶段,num不再是undefined,初始值不再生效,这段逻辑依然会执行,浪费资源效率低下

在这里插入图片描述

useState性能优化机制——Object.is 类似PureComponent的浅比较

useState自带了性能优化的机制:

  • 每一次修改状态值的时候,会拿最新要修改的值和之前的状态值做浅比较「基于Object.is作浅比较,而不是更严格的===。如果前后状态都是NaN,Object.is返回true不更新状态和视图,===返回false更新状态和视图」
  • 如果发现两次的值是一样的,则不会修改状态,也不会让视图更新「可以理解为:类似于PureComponent,在shouldComponentUpdate中做了浅比较和优化,注意函数组件中不可能有PureComponent」

调用 State Hook 的更新函数,并传入当前的 state 时,React 将跳过组件的渲染(原因:React 使用 Object.is 比较算法,来比较新老 state;注意不是因为DOM-DIFF;)!

import React, { useState } from "react";
export default function Demo() {
    console.log('render');
    let [num, setNum] = useState(10);
    return <div>
        <span>{num}</span>
        <button onClick={() => {
            setNum(num);
        }}>处理</button>
    </div>;
};
// 核心原理:闭包
var _state; // 创建全局state。
function useState(initialState) {
  _state = _state | initialState;
  function setState(state) {
  	if(Object.is(_state, value)) return;
  	if(typeof value === 'function'){
  		_state = value(_state) // 相当于传入prevalue后,return经过处理得到的新value
  	}else{
  		_state = value
  	}
    // 通知视图更新 
    //...重新渲染组件
  }
  return [_state, setState]; // 数组是新的变量,里面的每项自然也是新的,栈地址也不相同 
}
let [num1, setNum] = useState(0); //初始时num1=0  setNum=setState 0x001
setNum(100); //=>_state=100 通知视图更新
// ---
再次执行整个函数组件,在执行到useState的时候:
let [num2, setNum] = useState(0); //由于初次渲染时,全局state被赋值了,不再为undefined,所以不再赋值为initialState
在内部又产生了一个新的setState,地址和之前不同,使用这次新的闭包作为父级上下文
最后返回新的state和新的setState并被声明为新的变量
num2=100  setNum=setState 0x002

例1 前后state浅比较true,不更新状态和视图

import React, { useState } from "react";
import { Button } from 'antd';
const UseStateDemo = function UseStateDemo() {
  console.log('RENDER');
  let [x, setX] = useState(10);
    const handle = () => {
      setX(10);
    };
    return <div className="UseStateDemo">
        <span className="num">x:{x}</span>
        <Button type="primary"
            size="small"
            onClick={handle}>
            新增
        </Button>
    </div>;
};

export default UseStateDemo;

不更新视图和状态

例2 更新多次,最终值11

import React, { useState } from "react";
import { Button } from 'antd';
import { flushSync } from 'react-dom'; 

const UseStateDemo = function UseStateDemo() {
  console.log('RENDER');
  let [x, setX] = useState(10);
    const handle = () => {
      for (let i = 0; i < 10; i++) {
        flushSync(() => {
          setX(x + 1);
        })          
      }
    };
    return <div className="UseStateDemo">
        <span className="num">x:{x}</span>
        <Button type="primary"
            size="small"
            onClick={handle}>
            新增
        </Button>
    </div>;
};

export default UseStateDemo;

render多次(理论上是一次,这里是因为这些操作作用的都是一个闭包中的同一个状态值x,在第一次改状态还没改成功之前,其他次操作访问的状态值仍旧是还没改的10,直到第一次改状态成功,剩余次操作不会再通过Object.is的测试。这里render次数和浏览器的效率有关,可能是3次也可能是5次,不过绝对不是10次 ),最后状态值是11

在这里插入图片描述

在第一次渲染时创造出来顶级的函数作用域,_state私有属性就是在这个顶级作用域里面的

点击handle之后会执行10次同步清空更新队列的操作

在第一次flushSync,更新队列里只有一个setX,立即同步执行,使用的是第一次Demo创建出来的EC的闭包中的state,也就是10。进入setX,通过了Object.is的比较,更新状态和视图,此时最外部的_state也被更新为11

第二次flushSync,更新队列里只有一个setX,立即同步执行,使用的也是第一次Demo创建出来的EC的闭包中的state(因为这些flushSync方法都存在于第一个上下文中),也就是10。进入setX,第一次flushSync还没来得及彻底修改完成,所以也通过了Object.is的比较,更新状态和视图

直到某一次flushSync的时候,闭包中的X被彻底修改完了,此时不通过Object.is的比较,不再更新状态和视图,之后的若干轮轮同样不更新

例3 更新1次,最终值20 ——函数式更新

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

React Hooks —— useState异步更新队列、闭包、浅比较深入理解 的相关文章

  • 用隐藏单元格补充 colspanned 表格有什么不好吗?

    我一直在表格上开发一些排序和选择功能 我发现在具有跨单元格的表格中定位非常困难 我只是添加了跨区单元格并将其隐藏 它看起来不错 它与我的 js 一起工作 非常适合索引 但我想知道这是否是合法的方法 stuffing display none
  • 非 DOM 对象上的 jQuery 自定义事件

    我最近阅读了一些代码 其功能如下 bob name Bob Smith rank 7 bob bind nameChanged function bob trigger nameChanged 这似乎有效 但我在 jQuery 文档或源代码
  • 在Javascript中按降序对字符串进行排序(最有效)?

    W3Schools 有这个例子 var fruits Banana Orange Apple Mango fruits sort fruits reverse 这是在 Javascript 中按降序对字符串进行排序的最有效方法吗 Updat
  • 消息“在 jest.setTimeout 指定的 5000 毫秒超时内未调用异步回调”

    我正在使用 Puppeteer 和 Jest 来运行一些前端测试 我的测试如下 describe Profile Tab Exists and Clickable settings user gt test Assert that you
  • jQuery 选择 # id 以单词为前缀,计数器为后缀

    有没有办法用 jQuery 选择所有带有前缀 my 和后缀 0 9 的 id 像这样的 my 1 4 还是可以用循环来实现 div div div div div div div div div div 第一个想法 似乎效果很好 div i
  • 动画进度元素值

    我有一个progress元素 该元素如下所示 div class container div div div
  • Leaflet js虚构地图

    我是 Leaflet 的新手 我想了解如何创建完全交互式的虚构地图 我有一张图像想要转换为传单地图 该图像基本上像图表一样具有许多连接和点 我想首先将该图像转换为地图 能够将鼠标悬停在这些点上 突出显示它们并显示有关它们的信息 并且还可以在
  • 使用 CryptoJS 更改密钥 [重复]

    这个问题在这里已经有答案了 我正在使用 CryptoJS 来加密和解密文本 在这里 我只是获取消息并显示加密和解密消息 我使用DES算法进行加密和解密 这是我的 HTML 文件
  • jquery window.open 在 ajax 成功中被阻止

    尝试在我的 ajax 成功调用中打开一个新的浏览器窗口 但是 它被阻止为弹出窗口 我做了一些搜索 发现用户事件需要绑定到 window open 才能避免这种情况发生 我还找到了这个解决方案 您可以在 ajax 之前打开一个空白窗口 然后在
  • 如何在React中的Material-UI选择框中设置默认值?

    我在用选择框 https material ui com demos selects 来自材料用户界面 我想显示默认选择的 选择值 选项 但之后用户无法选择此选项
  • 仅一页 JavaScript 应用程序

    您是否尝试过单页 Web 应用程序 即浏览器仅从服务器 获取 一页 其余部分由客户端 JavaScript 代码处理 此类 应用程序页面 的一个很好的例子是 Gmail 对于更简单的应用程序 例如博客和 CMS 使用这种方法有哪些优点和缺点
  • HTML2canvas 和 Canvas2image,下载的屏幕截图不显示我的 HTML 图像

    我一直在开发一个 HTML 页面 我想将其转换为图像 我一直在使用 html2canvas 和 canvas2image 脚本并采用此代码http jsfiddle net 8ypxW 3 http jsfiddle net 8ypxW 3
  • 通过 node-http-proxy 保留基于 cookie 的会话

    我有一个简单的基于 Express 的 Node js Web 服务器 用于开发 JavaScript 应用程序 我将服务器设置为使用 node http proxy 来代理应用程序向在不同域和端口上运行的 Jetty 服务器发出的 API
  • 如何流式传输 OpenAI 的完成 API?

    我想流式传输结果通过 OpenAI 的 API 完成 https beta openai com docs api reference completions 该文档提到使用服务器发送的事件 https developer mozilla
  • 如何使JavaScript函数在Eclipse“大纲视图”中可见?

    我有这样的代码 但如果它在匿名函数中定义 则无法打开函数大纲 类没有问题 我该如何概述something2 请分享一些提示 我可以将所有函数标记为构造函数 但这是无效的方法 start of track event required deb
  • Nodejs mysql 获取正确的时间戳格式

    我在用着mysqljs https github com mysqljs mysql得到结果后sql我变得不同TimeStamp格式如下 created at Sat Jul 16 2016 23 52 54 GMT 0430 IRDT 但
  • JavaScript onresize 事件多次触发

    我在尝试仅在触发 onresize 事件时运行一次函数时遇到一些麻烦 我已经看过这个问题DOM onresize 事件 https stackoverflow com questions 1500312 javascript onresiz
  • 如何在打字稿文件中导入没有定义文件的js库

    随着我们的项目变得越来越大 我想从 JavaScript 切换到 TypeScript 以帮助进行代码管理 然而 我们使用许多库作为 amd 模块 我们不想将其转换为 TypeScript 我们仍然想将它们导入 TypeScript 文件
  • 在 React Native 中将 Props 传递到屏幕

    我已经开始学习 React Native 并且一如既往地从创建可重用组件开始 我了解了如何在创建自定义组件时传递和访问 props 我想在 React Native 中创建一个基本屏幕 它具有通用属性 并且我的应用程序中的所有屏幕都可以设置
  • 单击列表时使用 bootstrap Dropdown 防止下拉菜单消失

    我正在使用使用引导下拉菜单 http twitter github com bootstrap javascript html dropdowns生成下拉菜单 我想防止点击菜单时菜单消失 我已经实现了以下代码 但它不起作用 知道如何修复它吗

随机推荐

  • 当MySQL表连接查询时,既想要左表的全部数据,又需要右表的条件

    当MySQL表连接查询时 既想要左表的全部数据 又需要右表的条件 示例 会议室使用情况查询 表 会议室表 会议室预定表 会议室表为主表 查询出所有会议室的预定信息 没有预定情况的也查询出来 以会议室预定表的del字段为查询条件 SELECT
  • Gradle与Wrapper

    Android开发中Gradle配置的相关概念 配置本地Gradle Gradle与Wrapper Gradle可以直接去官网下载安装 但一般没这个必要 因为Android Studio会自带一个Gradle 就在Android Studi
  • 使用ControlNet控制Stable-Diffusion出图人物的姿势

    概述 在Stable Diffusion 以下简称SD 出图中 我们往往需要对出图人物的姿势进行控制 这里我使用一个比较简单上手的方法 通过ControlNet可以很方便地对画面风格 人物姿势进行控制 从而生成更加符合自己期望的图片 安装C
  • Openwrt GCC 7.5编译sanitizer_internal_defs.h错误

    GCC 7 5 BINUTILS 2 31 1 报错信息 sanitizer internal defs h 72 error size of array assertion failed is negative 解决方法 修改下列文件 删
  • 邀请函

    区块链 元宇宙是近年来备受关注的新兴技术 也是推动数字经济发展的重要力量 高质量标准引领高质量发展 加快形成标准引领 充分释放区块链 元宇宙对实体经济牵引赋能效应 推进形成相关产业体系高质量发展新格局刻不容缓 为进一步探索标准化建设对区块链
  • Qt 操作数据库报错:QSqlDatabase: QSQLITE driver not loaded

    在解决了Clion Qt的数据库问题后 Clion Qt 添加SQLite数据库 又遇到了新的问题 在运行程序时 出现QSqlDatabase QSQLITE driver not loaded错误 使用下面语句输出支持的数据库 会输出空
  • 华为OD机试真题 Python,简单的解压缩算法,入栈出栈,队列

    def decoder s str gt str n len s A3B1 C 3 3 bracket pos stack 不是数字都入栈 def repeat stack pos num 弹栈次数 m len stack pos repe
  • element-ui中二次封装一个带select的form组件

    带select的form组件 样式 代码
  • java文档注释

    一 文档注释简介 需要编写注释的地方 包 公有类与接口 公有的和受保护的构造器及方法 公有的和受保护的域 注释应该放置在所描述特性的前面 注释以 开始 并以 结束 每个 文档注释在标记之后紧跟着自由格式文本 标记由 开始 如 author
  • OpenCV4.5.2 分水岭算法出现的 Error: Assertion failed (src_depth != CV_16F && src_depth != CV_32S) 解决方法

    在贾志刚老师关于opencv3 1 0的分水岭算法讲解中 链接为 添加链接描述 在merker和create markers代码块要是按照视频所给出代码在OpenCV 4 5 2上运行的话会出现下图这样的情况 这个错误出现在显示生成的 ma
  • Anaconda简介

    anaconda是python发行的包的管理工具 其中自带python的版本 还带很多python的包 安装它比安装python好 可以省掉再安装python包的时间 推荐使用Anaconda 用Anaconda安装python的包是非常便
  • 外设驱动库开发笔记53:MAX31856热偶变送器驱动

    在我们的产品中经常有需要温度检测的地方 而热电偶温度检测电路是我们常用的 热电偶温度检测的方法很多 有时出于简单方便的考虑我们会选择热偶温度变送器来实现 这一篇我们就来讨论使用MAX31856热电偶温度变送器实现温度的检测 1 功能概述 M
  • java模拟redisSentinel故障转移

    模拟redis出现宕机情况下redis sentinel的故障转移 1 java代码以下实现方式 倘若创建maven项目需要在pom xml中引入依赖 不是maven项目需要引入对应jar包
  • 大规模数据处理中拒绝连接错误分析处理

    1 处理的数据有几百个G 把数据处理成按照手机号计算1万多个特征 2 数据处理环境 spark 2 0 2 executor memory 40g total executor cores 120 driver memory 40g 3 报
  • spring boot 使用 com.mysql.jdbc.Driver 和 com.mysql.cj.jdbc.Driver的区别

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 今天集成spring boot 2 1 1构建web应用并且集成jdbc 发现默认用的8 0 13
  • 记录一下Win10启用WSL2的流程

    个人笔记 请勿转载 参考文章如下 Using WSL2 in a Docker Linux container on Windows to run a Minecraft Java Edition Windows 10开启Linux子系统
  • win10下如何装CH340驱动

    由于win10系统对PL2303兼容性不太好 而CH340兼容性相对较好 但是 win10系统安装CH340串口驱动后 一直有黄色叹号 并且显示 由于windows无法加载这个设备所需的驱动程序 导致这个设备工作异常 这是因为win10有个
  • 自然语言处理学习笔记(一)————概论

    目录 1 自然语言处理概念 2 自然语言与编程语言的比较 1 词汇量 2 结构化 3 歧义性 4 容错性 5 易变性 6 简略性 3 自然语言处理的层次 1 层次图 2 自然语言处理系统输入源 3 词法分析 4 信息抽取 5 文本分类与文本
  • 打印出1-100的奇数 (C语言)

    代码 include
  • React Hooks —— useState异步更新队列、闭包、浅比较深入理解

    文章目录 useState 设计原理 实现原理 setXXX沿着作用域查找闭包的理解 与同步异步无关 更新多状态 方案一 类似于类组件中一样 让状态值是一个对象 包含需要的全部状态 每一次只修改其中的一个状态值 setXXX不支持类组件se