【React系列】Hook(二)高级使用

本文来自#React系列教程:https://mp.weixin.qq.com/mp/appmsgalbum?__biz=Mzg5MDAzNzkwNA==&action=getalbum&album_id=1566025152667107329)

一. Hook高级使用

1.1. useReducer

很多人看到useReducer的第一反应应该是redux的某个替代品,其实并不是。

useReducer仅仅是useState的一种替代方案:

  • 在某些场景下,如果state的处理逻辑比较复杂,我们可以通过useReducer来对其进行拆分;
  • 或者这次修改的state需要依赖之前的state时,也可以使用;

单独创建一个reducer/counter.js文件:

export function counterReducer(state, action) {
  switch(action.type) {
    case "increment":
      return {...state, counter: state.counter + 1}
    case "decrement":
      return {...state, counter: state.counter - 1}
    default:
      return state;
  }
}

home.js:

import React, { useReducer } from 'react'
import { counterReducer } from '../reducer/counter'

export default function Home() {
  const [state, dispatch] = useReducer(counterReducer, {counter: 100});

  return (
    <div>
      <h2>当前计数: {state.counter}</h2>
      <button onClick={e => dispatch({type: "increment"})}>+1</button>
      <button onClick={e => dispatch({type: "decrement"})}>-1</button>
    </div>
  )
}

我们来看一下,如果我们创建另外一个profile.js也使用这个reducer函数,是否会进行数据的共享:

import React, { useReducer } from 'react'
import { counterReducer } from '../reducer/counter'

export default function Profile() {
  const [state, dispatch] = useReducer(counterReducer, {counter: 0});

  return (
    <div>
      <h2>当前计数: {state.counter}</h2>
      <button onClick={e => dispatch({type: "increment"})}>+1</button>
      <button onClick={e => dispatch({type: "decrement"})}>-1</button>
    </div>
  )
}

数据是不会共享的,它们只是使用了相同的counterReducer的函数而已。

所以,useReducer只是useState的一种替代品,并不能替代Redux

1.2. useCallback

useCallback实际的目的是为了进行性能的优化。

如何进行性能的优化呢?

  • useCallback会返回一个函数的 memoized(记忆的) 值;
  • 在依赖不变的情况下,多次定义的时候,返回的值是相同的;
const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b]
);

我们来看下面一段很有趣的代码:

import React, { memo, useState, useCallback } from 'react'

export default function CallbackHookDemo() {
  const [count, setCount] = useState(0);

  const increment1 = useCallback(function increment() {
    setCount(count + 1);
  }, []);

  const increment2 = function() {
    setCount(count + 1);
  }

  return (
    <div>
      <h2>当前计数: {count}</h2>
      <button onClick={increment1}>+1</button>
      <button onClick={increment2}>+1</button>
    </div>
  )
}
  • increment1在每次函数组件重新渲染时,会返回相同的值;
  • increment2每次定义的都是不同的值;

问题:是否increment1会比increment2更加节省性能呢?

  • 事实上,经过一些测试,并没有更加节省内存,因为useCallback中还是会传入一个函数作为参数;
  • 所以并不存在increment2每次创建新的函数,而increment1不需要创建新的函数这种性能优化;

那么,为什么说useCallback是为了进行性能优化呢?

我们来对上面的代码进行改进:

import React, { memo, useState, useCallback } from 'react';

const CounterIncrement = memo((props) => {
  console.log("CounterIncrment被渲染:", props.name);
  return <button onClick={props.increment}>+1</button>
})

export default function CallbackHookDemo() {
  const [count, setCount] = useState(0);

  const increment1 = useCallback(function increment() {
    setCount(count + 1);
  }, []);

  const increment2 = function() {
    setCount(count + 1);
  }

  return (
    <div>
      <h2>当前计数: {count}</h2>
      {/* <button onClick={increment1}>+1</button>
      <button onClick={increment2}>+1</button> */}
      <CounterIncrement increment={increment1} name="increment1"/>
      <CounterIncrement increment={increment2} name="increment2"/>
    </div>
  )
}
  • 在上面的代码中,我们将回调函数传递给了子组件,在子组件中会进行调用;
  • 在发生点击时,我们会发现接受increment1的子组件不会重新渲染,但是接受increment2的子组件会重新渲染;
  • 所以useCallback最主要用于性能渲染的地方应该是和memo结合起来,决定子组件是否需要重新渲染;

1.3. useMemo

useMemo实际的目的也是为了进行性能的优化。

如何进行性能的优化呢?

  • useMemo返回的也是一个 memoized(记忆的) 值;
  • 在依赖不变的情况下,多次定义的时候,返回的值是相同的;
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

我们来看一个案例:

import React, { useState, useMemo } from 'react';

function calcNum(count) {
  let total = 0;
  for (let i = 0; i < count; i++) {
    total += i;
  }
  console.log("计算一遍");
  return total
}

export default function MemoHookDemo() {
  const [count, setCount] = useState(10);
  const [isLogin, setIsLogin] = useState(true);

  const total = calcNum(count);

  return (
    <div>
      <h2>数字和: {total}</h2>
      <button onClick={e => setCount(count + 1)}>+1</button>
      {isLogin && <h2>Coderwhy</h2>}
      <button onClick={e => setIsLogin(!isLogin)}>切换</button>
    </div>
  )
}
  • 无论我们点击了是 +1还是 切换 案例都会重新计算一次;
  • 事实上,我们只是希望在count发生变化时重新计算;

这个时候,我们可以使用useMemo来进行性能的优化:

import React, { useState, useMemo } from 'react';

function calcNum(count) {
  let total = 0;
  for (let i = 0; i < count; i++) {
    total += i;
  }
  console.log("计算一遍");
  return total
}

export default function MemoHookDemo() {
  const [count, setCount] = useState(10);
  const [isLogin, setIsLogin] = useState(true);

  const total = useMemo(() => {
    return calcNum(count);
  }, [count]);

  return (
    <div>
      <h2>数字和: {total}</h2>
      <button onClick={e => setCount(count + 1)}>+1</button>
      {isLogin && <h2>Coderwhy</h2>}
      <button onClick={e => setIsLogin(!isLogin)}>切换</button>
    </div>
  )
}

当然,useMemo也可以用于子组件的性能优化:

import React, { useState, useMemo, memo } from 'react';

function calcNum(count) {
  let total = 0;
  for (let i = 0; i < count; i++) {
    total += i;
  }
  console.log("计算一遍");
  return total
}

const ShowCounter = memo((props) => {
  console.log("重新渲染");
  return <h1>Counter: {props.total}</h1>
})

const ShowInfo = memo((props) => {
  console.log("ShowInfo重新渲染");
  return <h1>信息: {props.info.name}</h1>
})

export default function MemoHookDemo() {
  const [count, setCount] = useState(10);
  const [isLogin, setIsLogin] = useState(true);

  const total = useMemo(() => {
    return calcNum(count);
  }, [count]);

  const info = useMemo(() => {
    return {name: "why"}
  }, [])

  return (
    <div>
      <h2>数字和: {total}</h2>
      <ShowCounter total={total} />
      <ShowInfo info={info}/>
      <button onClick={e => setCount(count + 1)}>+1</button>
      {isLogin && <h2>Coderwhy</h2>}
      <button onClick={e => setIsLogin(!isLogin)}>切换</button>
    </div>
  )
}
  • ShowCounter子组件依赖的是一个基本数据类型,所以在比较的时候只要值不变,那么就不会重新渲染;
  • ShowInfo接收的是一个对象,每次都会定义一个新的对象,所以我们需要通过useMemo来对其进行优化;

1.4. useRef

useRef返回一个ref对象,返回的ref对象在组件的整个生命周期保持不变。

最常用的ref是两种用法:

  • 用法一:引入DOM(或者组件,但是需要是class组件)元素;
  • 用法二:保存一个数据,这个对象在整个生命周期中可以保存不变;

用法一:引用DOM

import React, { useRef } from 'react';

export default function RefHookDemo() {
  const inputRef = useRef();
  const titleRef = useRef();

  const handleOperating = () => {
    titleRef.current.innerHTML = "我是coderwhy";
    inputRef.current.focus();
  }

  return (
    <div>
      <input type="text" ref={inputRef}/>
      <h2 ref={titleRef}>默认内容</h2>
      <button onClick={e => handleOperating()}>操作</button>
    </div>
  )
}

用法二:使用ref保存上一次的某一个值

  • useRef可以想象成在ref对象中保存了一个.current的可变盒子;
  • useRef在组件重新渲染时,返回的依然是之前的ref对象,但是current是可以修改的;
import React, { useState, useEffect, useRef } from 'react';

let preValue = 0;

export default function RefHookDemo02() {
  const [count, setCount] = useState(0);
  const countRef = useRef(count);

  useEffect(() => {
    countRef.current = count;
  }, [count]);

  return (
    <div>
      <h2>前一次的值: {countRef.current}</h2>
      <h2>这一次的值: {count}</h2>
      <button onClick={e => setCount(count + 1)}>+1</button>
    </div>
  )
}

1.5. useImperativeHandle

useImperativeHandle并不是特别好理解,我们一点点来学习。

我们先来回顾一下refforwardRef结合使用:

  • 通过forwardRef可以将ref转发到子组件;
  • 子组件拿到父组件中创建的ref,绑定到自己的某一个元素中;
import React, { useRef, forwardRef } from 'react';

const HYInput = forwardRef(function (props, ref) {
  return <input type="text" ref={ref}/>
})

export default function ForwardDemo() {
  const inputRef = useRef();

  return (
    <div>
      <HYInput ref={inputRef}/>
      <button onClick={e => inputRef.current.focus()}>聚焦</button>
    </div>
  )
}

上面的做法本身没有什么问题,但是我们是将子组件的DOM直接暴露给了父组件:

  • 直接暴露给父组件带来的问题是某些情况的不可控;
  • 父组件可以拿到DOM后进行任意的操作;

但是,事实上在上面的案例中,我们只是希望父组件可以操作的focus,其他并不希望它随意操作;

通过useImperativeHandle可以只暴露固定的操作:

  • 通过useImperativeHandle的Hook,将传入的refuseImperativeHandle第二个参数返回的对象绑定到了一起;
  • 所以在父组件中,使用 inputRef.current时,实际上使用的是返回的对象;
  • 比如我调用了 focus函数,甚至可以调用 printHello函数;
import React, { useRef, forwardRef, useImperativeHandle } from 'react';

const HYInput = forwardRef(function (props, ref) {
  // 创建组件内部的ref
  const inputRef = useRef();

  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    },
    printHello: () => {
      console.log("Hello World")
    }
  }))

  // 这里绑定的是组件内部的inputRef
  return <input type="text" ref={inputRef}/>
})

export default function ImperativeHandleHookForwardDemo() {
  const inputRef = useRef();

  return (
    <div>
      <HYInput ref={inputRef}/>
      <button onClick={e => inputRef.current.focus()}>聚焦</button>
      <button onClick={e => inputRef.current.printHello()}>Hello World</button>
    </div>
  )
}

1.6. useLayoutEffect

useLayoutEffect看起来和useEffect非常的相似,事实上他们也只有一点区别而已:

  • useEffect会在渲染的内容更新到DOM上之后执行,不会阻塞DOM的更新
  • useLayoutEffect会在渲染的内容更新到DOM上之前执行,会阻塞DOM的更新

如果我们希望在某些操作发生之后再更新DOM,那么应该将这个操作放到useLayoutEffect

我们来看下面的一段代码:

import React, { useEffect, useState, useLayoutEffect } from 'react';

export default function EffectHookDemo() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    if (count === 0) {
      setCount(Math.random()*200)
    }
  }, [count]);

  return (
    <div>
      <h2>当前数字: {count}</h2>
      <button onClick={e => setCount(0)}>随机数</button>
    </div>
  )
}
  • 这段代码在开发中会发生闪烁的现象;
  • 因为我们先将count设置为了0,那么DOM会被更新,并且会执行一次useEffect中的回调函数;
  • useEffect中我们发现count0,又执行一次setCount操作,那么DOM会再次被更新,并且useEffect又会被执行一次;

事实上,我们上面的操作的目的是在count被设置为0时,随机另外一个数字:

  • 如果我们使用useLayoutEffect,那么会等到useLayoutEffect代码执行完毕后,再进行DOM的更新;
import React, { useEffect, useState, useLayoutEffect } from 'react';

export default function EffectHookDemo() {
  const [count, setCount] = useState(0);

  useLayoutEffect(() => {
    if (count === 0) {
      setCount(Math.random()*200)
    }
  }, [count]);

  return (
    <div>
      <h2>当前数字: {count}</h2>
      <button onClick={e => setCount(0)}>随机数</button>
    </div>
  )
}

在这里插入图片描述

二. 自定义Hook

2.1. 认识自定义hook

自定义Hook本质上只是一种函数代码逻辑的抽取,严格意义上来说,它本身并不算React的特性。

需求:所有的组件在创建和销毁时都进行打印

  • 组件被创建:打印 组件被创建了
  • 组件被销毁:打印 组件被销毁了
export default function CustomHookDemo() {
  useEffect(() => {
    console.log("组件被创建了");
    return () => {
      console.log("组件被销毁了");
    }
  }, [])

  return (
    <div>
      <h2>CustomHookDemo</h2>
    </div>
  )
}

但是这样来做意味着所有的组件都需要有对应的逻辑:

function Home(props) {
  useEffect(() => {
    console.log("组件被创建了");
    return () => {
      console.log("组件被销毁了");
    }
  }, [])
  return <h2>Home</h2>
}

function Profile(props) {
  useEffect(() => {
    console.log("组件被创建了");
    return () => {
      console.log("组件被销毁了");
    }
  }, [])
  return <h2>Profile</h2>
}

如何可以对它们的逻辑进行抽取呢?

  • 我们可能希望抽取到一个函数中;
function loggingLife() {
  useEffect(() => {
    console.log("组件被创建了");
    return () => {
      console.log("组件被销毁了");
    }
  }, [])
}

但是,抽取到这里调用之后,代码是报错的:

在这里插入图片描述

  • 原因是普通的函数中不能使用hook

那么,我们应该如何操作呢?

  • 非常简单,函数以特殊的方式命名,以 use 开头即可;
function useLoggingLife() {
  useEffect(() => {
    console.log("组件被创建了");
    return () => {
      console.log("组件被销毁了");
    }
  }, [])
}

当然,自定义Hook可以有参数,也可以有返回值:

function useLoggingLife(name) {
  useEffect(() => {
    console.log(`${name}组件被创建了`);
    return () => {
      console.log(`${name}组件被销毁了`);
    }
  }, [])
}

2.2. 自定义Hook练习

我们通过一些案例来练习一下自定义Hook。

使用UserTokenContext

比如多个组件都需要使用UserTokenContext

import React, { useContext } from 'react'
import { UserContext, TokenContext } from '../App'

export default function CustomHookContextDemo() {
  const user = useContext(UserContext);
  const token = useContext(TokenContext);

  console.log(user, token);

  return (
    <div>
      <h2>CustomHookContextDemo</h2>
    </div>
  )
}
  • 这段代码我们在每次使用usertoken时都需要导入对应的Context,并且需要使用两次useContext

我们可以抽取到一个自定义Hook中:

function useUserToken() {
  const user = useContext(UserContext);
  const token = useContext(TokenContext);

  return [user, token];
}

获取窗口滚动的位置

  • 在开发中,某些场景我们可能总是希望获取创建滚动的位置:
import React, { useEffect, useState } from 'react'

export default function CustomScrollPositionHook() {

  const [scrollPosition, setScrollPosition] = useState(0);

  useEffect(() => {
    const handleScroll = () => {
      setScrollPosition(window.scrollY);
    }
    document.addEventListener("scroll", handleScroll);

    return () => {
      document.removeEventListener("scroll", handleScroll);
    }
  }, [])

  return (
    <div style={{padding: "1000px 0"}}>
      <h2 style={{position: "fixed", top: 0, left: 0}}>CustomScrollPositionHook: {scrollPosition}</h2>
    </div>
  )
}

但是如果每一个组件都有对应这样的一个逻辑,那么就会存在很多的冗余代码:

function useScrollPosition() {
  const [scrollPosition, setScrollPosition] = useState(0);

  useEffect(() => {
    const handleScroll = () => {
      setScrollPosition(window.scrollY);
    }
    document.addEventListener("scroll", handleScroll);

    return () => {
	      document.removeEventListener("scroll", handleScroll);
	    }
    }, [])
    
    return scrollPosition;
}

数据存储的localStorage

在开发中,我们会有一些数据希望通过localStorage进行存储(当然,你可以根据自己的情况选择sessionStorage

import React, { useState, useEffect } from 'react'

export default function CustomDataStoreHook() {
  const [name, setName] = useState(() => {
    return JSON.parse(window.localStorage.getItem("name"))
  });

  useEffect(() => {
    window.localStorage.setItem("name", JSON.stringify(name));
  }, [name])

  return (
    <div>
      <h2>CustomDataStoreHook: {name}</h2>
      <button onClick={e => setName("coderwhy")}>设置name</button>
    </div>
  )
}

如果每一个里面都有这样的逻辑,那么代码就会变得非常冗余:

function useLocalStorange(key) {
  const [data, setData] = useState(() => {
    return JSON.parse(window.localStorage.getItem(key))
  });

  useEffect(() => {
    window.localStorage.setItem(key, JSON.stringify(data));
  }, [data]);

  return [data, setData];
}

三. Hook原理分析

这里我主要分析一下useState的原理,因为本次教程是穿插讲解源码的,所以不会所有源码一一讲解。

3.1. useState代码位置

useState还是从React中导入的,所以我们可以先查看:

在这里插入图片描述

点到useState的源码中:

  • useState本质上,是使用的dispatcheruseState

在这里插入图片描述

  • dispatcher来自另外的一个函数resolveDispatcher

在这里插入图片描述

运行的过程中,会赋值一个current的值是一个Dispatcher类型:

  • Dispatcher来自于 react-reconciler/src/ReactFiberHooks

在这里插入图片描述

Dispatch类型的定义:

在这里插入图片描述

这里的Dispatch仅仅是一个类型而已,我们赋值具体的值在不同的阶段是不同的:

  • 在挂载阶段:HooksDispatcherOnMount
  • 在更新阶段:HooksDispatcherOnUpdate

在这里插入图片描述

挂载哪一个取决于renderWithHook函数:

在这里插入图片描述

3.2. HooksDispatcherOnMount

HooksDispatcherOnMount对应的useStatemountState

在这里插入图片描述

mountState的源码:

在这里插入图片描述

绑定的dispatchAction函数,事实上是将所有的action放到了queue的队列中:

在这里插入图片描述

3.3. HooksDispatcherOnUpdate

HooksDispatcherOnUpdate对应的useStateupdateState

在这里插入图片描述

updateState本质上会执行updateReducer

  • 所有其实就更新阶段而言,useState本质上用的是updateReducer

在这里插入图片描述

updateReducer的源码如下:

在这里插入图片描述
在这里插入图片描述

四. Redux Hooks

在之前的redux开发中,为了让组件和redux结合起来,我们使用了react-redux中的connect

  • 但是这种方式必须使用高阶函数结合返回的高阶组件;
  • 并且必须编写:mapStateToPropsmapDispatchToProps映射的函数;

在Redux7.1开始,提供了Hook的方式,我们再也不需要编写connect以及对应的映射函数了

4.1. useSelector使用

useSelector的作用是将state映射到组件中:

  • 参数一:将state映射到需要的数据中;
  • 参数二:可以进行比较来决定是否组件重新渲染;(后续讲解)
const result: any = useSelector(selector: Function, equalityFn?: Function)

现在,我可以改进一下之前的Profile中使用redux的代码:

function Profile(props) {
  const {banners, recommends, counter} = useSelector(state => ({
    banners: state.homeInfo.banners,
    recommends: state.homeInfo.recommends
  }));

  console.log("Profile重新渲染");

  return (
    <div>
      <h2>数字: {counter}</h2>
      <h1>Banners</h1>
      <ul>
        {
          banners.map((item, index) => {
            return <li key={item.acm}>{item.title}</li>
          })
        }
      </ul>
      <h1>Recommends</h1>
      <ul>
        {
          recommends.map((item, index) => {
            return <li key={item.acm}>{item.title}</li>
          })
        }
      </ul>
    </div>
  )
}

但是这段代码会有一个问题:

  • 当前我们的组件并不依赖counter,但是counter发生改变时,依然会引起Profile的重新渲染;

原因是什么呢?

  • useSelector默认会比较我们返回的两个对象是否相等;
  • 如何比较呢?const refEquality = (a, b) => a === b
  • 也就是我们必须返回两个完全相等的对象才可以不引起重新渲染;

这个时候,我们可以使用react-redux中给我们提供的 shallowEqual

  const {banners, recommends, counter} = useSelector(state => ({
    banners: state.homeInfo.banners,
    recommends: state.homeInfo.recommends
  }), shallowEqual);
  • 这段代码的作用是避免不必要的重新渲染;

当然,你也可以编写自己的比较函数,来决定是否重新渲染。

4.2. useDispatch

useDispatch非常简单,就是直接获取dispatch函数,之后在组件中直接使用即可:

const dispatch = useDispatch()

直接使用dispatch

<button onClick={e => dispatch(subAction(1))}>-1</button>
<button onClick={e => dispatch(subAction(5))}>-5</button>

我们还可以通过useStore来获取当前的store对象:

const store = useStore()

在组件中可以使用store

const store = useStore();
console.log(store.getState());

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/293961.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

通过消费者特征,设计更符合市场的商业模式,树立标杆用户拓展用户圈层

迅腾文化观察&#xff1a;通过消费者特征&#xff0c;设计更符合市场的商业模式&#xff0c;树立标杆用户拓展用户圈层 在当今快速变化的市场环境中&#xff0c;企业需要不断创新和调整自己的商业模式&#xff0c;以适应消费者的需求和市场的发展。迅腾文化观察到&#xff0c;…

经常耳鸣别大意,可能是这6种疾病的预警信号

你在日常生活中有没有遇到过这样的情况&#xff1a;耳朵里突然出现一阵响声&#xff0c;有时候像流水、有时候像蝉鸣、有时候像机器轰鸣……这些往往都是耳鸣的表现。 而耳鸣是一种很常见的症状&#xff0c;一项发表于《美医学会杂志&#xff1a;神经病学分册》的研究表明&…

机器学习分类

1. 监督学习 监督学习指的是人们给机器一大堆标记好的数据&#xff0c;比如&#xff1a; 一大堆照片&#xff0c;标记出哪些是猫的照片&#xff0c;哪些是狗的照片 让机器自己学习归纳出算法或模型 使用该算法或模型判断出其他没有标记的照片是否是猫或狗 上述流程如下图所…

ChatGPT大升级,文档图像识别领域迎来技术革新

​写在前面ChatGPT迎来重大升级冲击与机遇并存​大模型时代的思考与探索■ 像素级OCR统一模型- UPOCR■ OCR大一统模型- SPTS v3■ 文档识别分析LLM应用 写在最后问卷抽奖 ​写在前面 2023 年 12 月 31 日第十九届中国图象图形学学会青年科学家会议在广州召开&#xff0c;该会…

ZigBee协议栈 -- 协议栈版本与IAR版本适配说明(Zstack2.5.1a + IAR10.30.1)

文章目录 协议栈安装工程适配 在讲到ZigBee协议栈的文章中所用的协议栈版本是Zstack2.5.1a&#xff0c;对于Zstack2.5.1a运行在IAR8.10中是可以完全适配进行编译开发的&#xff0c;现在较新版本的IAR都是10的版本以上了&#xff0c;有部分开发者习惯使用最新版本来获得更好的开…

Python 可视化 web 神器 streamlit

官网&#xff1a;https://streamlit.io/ github&#xff1a;https://github.com/streamlit/streamlit API 参考&#xff1a;https://docs.streamlit.io/library/api-reference 1、streamlit 简介 streamlit 简介 Streamlit 是Python可视化 web 神器 &#xff0c;是一个开箱即…

Open3D聚类算法

按照官网的例子使用聚类&#xff0c;发现结果是全黑的。 经过多次测试发现 eps3.3, min_points1这里是关键 min_points必须等于1否则无效果 import time import open3d as o3d; import numpy as np; import matplotlib.pyplot as plt#坐标 mesh_coord_frame o3d.geometry.Tria…

自定义ChatGPT商店下周上线!大模型“App Store时刻”来啦

1月5日凌晨&#xff0c;OpenAI向所有自定义GPT开发者发布了一封邮件&#xff0c;下周将上线“自定义GPT商店”。 自定义GPTs是OpenAI在去年首届开发者大会上发布的重磅产品&#xff0c;用户无需任何代码&#xff0c;全程支持可视化点击操作。 用户只需要提交对话指令、额外的…

企业核心技术泄露可能给企业带来深远的负面影响以及补救措施

企业核心技术是企业竞争优势的重要来源&#xff0c;但同时也是企业面临的最大风险之一。随着信息技术的发展&#xff0c;企业核心技术泄露的风险也越来越大。一旦企业核心技术泄露&#xff0c;不仅会给企业带来直接的经济损失&#xff0c;还会对企业的品牌形象、市场竞争力、战…

led台灯哪些牌子性价比高?那些性价比高的LED护眼台灯推荐

台灯作为家居用品在日常生活中使用频繁。用户可以根据个人需求和喜好&#xff0c;在市场上找到合适的款式。然而&#xff0c;由于台灯种类繁多&#xff0c;甚至连相关标准都存在差异&#xff0c;这使得一些缺乏经验的购物小白感到困扰。那么&#xff0c;led台灯哪些牌子性价比高…

提升图像分割精度:学习UNet++算法

文章目录 一、UNet 算法简介1.1 什么是 UNet 算法1.2 UNet 的优缺点1.3 UNet 在图像分割领域的应用 二、准备工作2.1 Python 环境配置2.2 相关库的安装 三、数据处理3.1 数据的获取与预处理3.2 数据的可视化与分析 四、网络结构4.1 UNet 的网络结构4.2 UNet 各层的作用 五、训练…

SpringCloud之Eureka组件工作原理详解

Eureka是一种服务注册与发现组件&#xff0c;最初由Netflix开发并开源出来。它主要用于构建分布式系统中的微服务架构&#xff0c;并提供了服务注册、服务发现、负载均衡等功能。在本文中&#xff0c;我们将详细解释Eureka的工作原理。 一、Eureka概述 Eureka是Netflix开源的一…

MySQL数据库的CURD、常见函数及UNION和UNION ALL

一、概述 MySQL是一种流行的关系型数据库管理系统&#xff0c;广泛应用于各种应用场景。在MySQL中&#xff0c;CURD操作是指创建&#xff08;Create&#xff09;、读取&#xff08;Read&#xff09;、更新&#xff08;Update&#xff09;和删除&#xff08;Delete&#xff09;…

ROS学习笔记(9)进一步深入了解ROS第三步

0.前提 1. (C)Why did you include the header file of the message file instead of the message file itself?&#xff08;为包含消息的头文件而不是消息本身&#xff1f;&#xff09; 回答&#xff1a;msg文件是描述ROS消息字段的文本文件&#xff0c;用于生成不同语言消息…

【Mars3d】new mars3d.layer.GeoJsonLayer({不规则polygon加载label不在正中间的解决方案

问题&#xff1a; 1.new mars3d.layer.GeoJsonLayer({type: "polygon",在styleOptions里配置label的时候&#xff0c;发现这个 不规则polygon加载的时候&#xff0c;会出现label不在中心位置。 graphicLayer new mars3d.layer.GeoJsonLayer({ name: "全国省界…

游戏Lua调用01.lua的编译及测试

一、lua库下载与编译 进入lua官网 Lua: version history 找到lua5.1 选择lua5.1是因为大部分游戏使用的都是lua5.1的库&#xff0c;也可以选择高版本&#xff0c;影响不大 下载完了后使用vs建立一个静态库或者动态库的工程 这里以动态库为例子&#xff0c;静态库也是一样的…

6 网关和配置服务器

文章目录 网关模式Spring Cloud网关Spring Cloud网关微服务其他项目的变更运行和测试小结 运行状况Spring Boot Actuator在微服务中包含Actuator 服务发现和负载均衡ConsulSpring Cloud ConsulSpring Cloud负载均衡器网关中的服务发现和负载均衡使用服务发现和负载均衡 环境配置…

vins 实机测试 rs_d435 + imu

vins 实机测试 文章目录 1. imu标定2. camera内参标定3. imu-cam 外参标定4. vins 实际运行5. realsense 1. imu标定 git clone https://github.com/gaowenliang/code_utils.git git clone https://github.com/gaowenliang/imu_utils.git编译运行&#xff0c; roslaunch imu_…

CSS基本知识

文章目录 1. CSS 是什么2. 基本语法规范3. 引入方式3.1 内部样式表3.2 行内样式表3.3 外部样式 4. 选择器4.1 选择器的功能4.2 选择器的种类4.3 基础选择器4.3.1 标签选择器4.3.2 类选择器4.3.3 id 选择器4.3.4 通配符选择器 4.4 复合选择器4.4.1 后代选择器4.4.2 伪类选择器 5…

git在本地创建dev分支并和远程的dev分支关联起来

文章目录 git在本地创建dev分支并和远程的dev分支关联起来1. 使用git命令2. 使用idea2.1 先删除上面建的本地分支dev2.2 通过idea建dev分支并和远程dev分支关联 3. 查看本地分支和远程分支的关系 git在本地创建dev分支并和远程的dev分支关联起来 1. 使用git命令 git checkout…