字节青训前端笔记 | 响应式系统与 React

news2024/12/23 6:48:25

本节课为前端框架 React 的基础课程讲解

React的设计思路

  • UI编程的特点
  1. 状态更新的时候,UI不会自动更新,需要手动调用DOM接口进行更新
  2. 欠缺基本的代码层面的封装和隔离,代码层面没有组件化
  3. UI之间的数据依赖关系,需要手动维护,如果依赖链路长,则会遇到回调地狱

React的出现,就是为了解决这三大痛点,他做到了:

  1. 状态更新,UI也会进行更新

  2. 前端代码组件化,可复用,可封装

  3. 状态之间的互相依赖关系,只需声明即可

  • 响应式系统:

它使用了响应式编程的思想,通过监听事件,由消息驱动,需要有一个监控系统去关注事件,并对事件做出响应,更新UI界面:

请添加图片描述

  • 组件化

可以用树状结构表示组件之间的关系:

请添加图片描述

  1. 组件是组件的组合/原子组件

  2. 组件内拥有自己的状态,外部不可见

  3. 父组件可将状态传入组件内部

组件的设计:

  1. 组件有 props (外部传入的)和 state(内部定义的) 两种状态
  2. 组件的根据状态来返回一个 UI
  3. 组件可以由其他组件拼装而成
  • 状态归属和更新

React是单项数据流

如果想要两个组件的状态共享的话,他们的状态归属于最近的公共祖先,如上的例子中,当前价格属于根节点,因为所有的组件都需要可能影响到它。

当需要改变一个状态时,由于在js中,函数是一等公民,所以可以将函数也作为属性传递给子组件,那么就可以在Root组件中定义一个修改当前价格的函数,然后将这个函数传给子组件,当子组件需要修改当前价格时,就调用该函数即可。

React的生命周期

请添加图片描述

1.Mounting 挂载时 ,就是初始化的时候,把我们定义组件对应的UI 挂载到真实的 dom 上

2.Updating 更新时 ,当状态更新的时候,怎么样更新组件,重新渲染再挂载上去

3.Unmounting 销毁时

React (Hooks) 的写法

  • 类组件和函数组件

根据组件的定义方式,可以分为:函数组件(Functional Component )和类组件(Class Component);

类组件,顾名思义,也就是通过使用ES6类的编写形式去编写组件,该类必须继承React.Component,如果想要访问父组件传递过来的参数,可通过this.props的方式去访问,constructor 的存在是为了让我们获取 this,这是一个固定写法 :

class Welcome extends React.Component {
  constructor(props) {
    super(props)
  }
  render() {
    return <h1>Hello, {this.props.name}</h1>
  }
}

函数组件,顾名思义,就是通过函数编写的形式去实现一个React组件,是React中定义组件最简单的方式,函数第一个参数为props用于接收父组件传递过来的参数:

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

在 Hooks 出现之前,函数组件都是无状态组件,不能拥有自己的状态,hooks 的出现使得函数组件成为了主流,以下是几个常用的 hooks :

  • useState

useState可以用来定义一个状态。useState返回的是一个数组,第一个是当前状态的实际值,第二个用于更改该状态的函数,类似于setState。更新函数与setState相同的是都可以接受值和函数两种类型的参数,与useState不同的是,更新函数会将状态替换(replace)而不是合并(merge)。

import React, { useState } from 'react'

function Example() {
  const [count, setCount] = useState(0);
  return (
    <div>
      <span>{count}</span>
      <button onClick={()=> setCount(count + 1)}>+</button>
      <button onClick={() => setCount((count) => count - 1)}>-</button>
    </div>
  );
}

函数组件中如果存在多个状态,既可以通过一个useState声明对象类型的状态,也可以通过useState多次声明状态。

// 声明对象类型的状态
const [count, setCount] = useState({
    count1: 0,
    count2: 0
});

// 多次声明
const [count1, setCount1] = useState(0);
const [count2, setCount2] = useState(0);
  • useEffect

在函数式思想的React中,生命周期函数是沟通函数式和命令式的桥梁,你可以在生命周期中执行相关的副作用(Side Effects),例如: 请求数据、操作DOM等。React提供了useEffect来处理副作用。

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

function Example() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `You clicked ${count} times`
    return () => {
      console.log('clean up!')
    }
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

我们会发现每次组件更新时,useEffect中的回调函数都会被调用,因此我们可以认为useEffect是componentDidMount和componentDidUpdate结合体,

useEffect为我们提供了第二个参数,如果第二个参数传入一个数组,仅当重新渲染时数组中的值发生改变时,useEffect中的回调函数才会执行。因此如果我们向其传入一个空数组,则可以模拟生命周期componentDidMount

//仅执行一次
useEffect(() => {
  document.title = `You clicked ${count} times`
  return () => {
    console.log('clean up!')
  }
},[]);

//只在count变化时调用
useEffect(() => {
  document.title = `You clicked ${count} times`
  return () => {
    console.log('clean up!')
  }
},[count]);

你可以在useEffect中定义 return 方法,它只在组件被销毁之前才会执行,常用于清理以下遗留垃圾,比如订阅或计时器 ID 等占用资源的东西。相当于生命周期的 componentWillUnMount:

 useEffect(()=>{
    console.log('我执行了')
    return ()=>{
      console.log('我销毁了')
    }
  },[])

要注意的是 useEffect 的函数会在组件渲染到屏幕之后执行
而 useLayoutEffect 则是在DOM结构更新后、渲染前执行,相当于有一个防抖效果,他们的用法是一样的,不一样是只是执行的时机

  • 父子组件交互

react 中,父组件可以把状态或者函数方法传递给子组件:

//父组件传参
<Hearders name={name}  />

//子组件获得参数
function Hearders(props) {
    const {name} =props
}

父组件也可以传递一个方法给子组件,子组件可以通过这个方法来修改子组件的值:

//父组件
const Parent = () => {
    const onClick = (value) => {
        console.log(value,'点击了')
    }
    return(
        <div>
            <Child
                click={onClick}
            />
        </div>
    )
}

//子组件
const Child = (props) => {
    const handleClick = (value) => {
        props.click(value)
    }
    return(
        <div onClick={()=>{handleClick(1)}}>
            子组件
        </div>
    )
}

借助Hook useContext可以帮助我们跨越组件层级直接传递变量,实现数据共享。

import React,{useContext, useState, createContext} from 'react';
import {Button} from 'antd';
import '../../App.css';
 
const CountContext = createContext();
 
const TestContext = () =>{
    const [count, setCount] = useState(0);
    return(
      <div>
          <p>父组件点击次数:{count}</p>
          <Button type={"primary"} onClick={()=>setCount(count+1)}>点击+1</Button>
          <CountContext.Provider value={count}>
            <Counter/>
          </CountContext.Provider>
      </div>
  )
};

不止在子组件,在子组件的下一级孙子组件,再下一级中,都可以获取到响应的值,只要是被 Context.Provider 包裹的内容中,都可以如下方法使用 Context 里的值:

const CountContext = createContext();
const Counter = () => {
    const count = useContext(CountContext);
    return (
        <div>
            <p>子组件获得的点击数量:{count}</p>
        </div>
    );
};
  • useRef

useRef 可以用来拿到 dom 节点的引用,拿到引用后可以进行一系列操作

function Example() {
    const inputEl = useRef();
    const onButtonClick = () => {
        inputEl.current.focus();
    };
    return (
        <>
            <input ref={inputEl} type="text" />
            <button onClick={onButtonClick}>Focus the input</button>
        </>
    );
}

useRef 也可以接受一个默认值,并返回一个含有current属性的可变对象,该可变对象会将持续整个组件的生命周期。它有什么用处呢,例子如下:

在like为6的时候, 点击 alert , 再继续增加like到10, 弹出的值为 6, 而非 10.当我们更改状态的时候,React会重新渲染组件,每次的渲染都会拿到独立的like值,并重新定义个handleAlertClick函数,每个handleAlertClick函数体里的like值也是它自己的,所以当like为6时,点击alert,触发了handleAlertClick,此时的like是6,哪怕后面继续更改like到10,但alert时的like已经定下来了。可见不同渲染之间无法共享state状态值

import React, { useState } from "react";
const LikeButton: React.FC = () => {
    const [like, setLike] = useState(0)
    function handleAlertClick() {
        setTimeout(() => {
            alert(`you clicked on ${like}`) 
            //形成闭包,所以弹出来的是当时触发函数时的like值
        }, 3000)
    }
    return (
        <>
            <button onClick={() => setLike(like + 1)}>{like}赞</button>
            <button onClick={handleAlertClick}>Alert</button>
        </>
    )
}
export default LikeButton

采用useRef,在like为6的时候, 点击 alert , 再继续增加like到10, 弹出的值为10。useRef 在更新的时候不会使得组件重新渲染, useRef 每次都会返回相同的引用

import React, { useRef } from "react";
const LikeButton: React.FC = () => {
  // 定义一个实例变量
  let like = useRef(0);
  function handleAlertClick() {
    setTimeout(() => {
      alert(`you clicked on ${like.current}`);
    }, 3000);
  }
  return (
    <>
      <button
        onClick={() => {
          like.current = like.current + 1;
        }}
      >
        {like.current}赞
      </button>
      <button onClick={handleAlertClick}>Alert</button>
    </>
  );
};
export default LikeButton;

useImperativeHandle用于自定义暴露给父组件的ref属性。需要配合forwardRef一起使用。

function Example(props, ref) {
    const inputRef = useRef();
    useImperativeHandle(ref, () => ({
        focus: () => {
            inputRef.current.focus();
        }
    }));
    return <input ref={inputRef} />;
}

export default forwardRef(Example);

class App extends Component {
  constructor(props){
      super(props);
      this.inputRef = createRef()
  }
  
  render() {
    return (
        <>
            <Example ref={this.inputRef}/>
            <button onClick={() => {this.inputRef.current.focus()}}>Click</button>
        </>
    );
  }
}

  • useCallback 和 useMemo

都是react可用于性能优化的内置hooks。两者的区别在于:useCallback缓存的是一个函数,而useMemo缓存的是计算结果。

// useCallback
// 第一个参数是一个回调函数,useCallback会缓存这个函数,返回缓存的回调函数
// 第二个参数是依赖项,只有当依赖项改变时,才会重新创建这个函数
const memorizedCallback = useCallback(()=>{
    doSomething(a,b);
},[a,b])
 
// useMemo
// 第一个参数是一个函数,useMemo会缓存函数运行返回的值,返回缓存的值
// 第二个参数是依赖项,只有当依赖改变时,才会重新计算这个值
const memorizedValue = useMemo(()=>computeValue(a,b),[a,b])

他们的用处是:在函数式组件中,每次UI的变化,都是通过重新执行整个函数来完成的,这和传统的类组件有很大区别:函数组件中并没有一个直接的方式在多次渲染之间维持一个状态。在重新执行整个函数组件的过程中,其中的函数和引用类型的变量会创建新的(指向新的引用),函数组件在重新渲染前后,其中函数和引用类型变量是不相等的,这又会导致其他非必要的重新渲染。

如下:当Counter组件因为其他数据(非count)发生变化而导致重新渲染的时候,重新执行整个Counter函数,会创建新的 handleIncrement 函数,而子组件 Button 会由于 props-handleClick 传入的 handleIncrement 函数改变而重新渲染,但其实这个渲染是不必要的,因为只有在count发生变化时,才应该导致Button组件的渲染。

// 需要做到:只有当count发生变化时,才需要重新定一个回调函数-useCallback
function Counter() {
  const [count, setCount] = useState(0);
  const handleIncrement = useCallback(
      () => setCount(count + 1),
      [count]  
  )  
// 只有当依赖项count改变时,才会重新生成函数,不然都是返回的缓存的回调函数,不会触发Button子组件的重绘
  return <Button handleClick={handleIncrement}/>
}
  • 自定义 hooks

React 允许我们创建自定义Hook来封装共享状态逻辑。所谓的自定义Hook是指以函数名以use开头并调用其他Hook的函数。

// 自定义一个hook  功能判断当前的网络情况
// 函数名要以use开头
// 函数中必须要用到内置hook函数
const useOnline = () => {
  const [online, setOnline] = useState(navigator.onLine)

  // 让它在第1次挂载时执行
  useEffect(() => {
    const onlineFn = () => setOnline(true)
    const offlineFn = () => setOnline(false)

    // js提供的监听事件
    window.addEventListener('online', onlineFn, false)
    window.addEventListener('offline', offlineFn, false)

    return () => {
      window.removeEventListener('online', onlineFn, false)
      window.removeEventListener('offline', offlineFn, false)
    }
  }, [])
  return online
}


const App = () => {
  const online = useOnline()
  return (
    <div>
      {
        online
          ?
          <div style={{ color: 'green' }}>在线</div>
          :
          <div>离线</div>
      }
    </div>
  );
}

React的实现原理

  • 虚拟DOM

React 使用 JavaScript 对象表示 DOM 信息和结构,当状态变更的时候,重新渲染这个 JavaScript 的对象结构。这个 JavaScript 对象称为virtual dom;

使用它的原因是 DOM 操作很慢,轻微的操作都可能导致页面重新排版,非常耗性能。相对于DOM对象,js对象处理起来更快,而且更简单。通过diff算法对比新旧vdom之间的差异,可以批量的、最小化的执行 dom 操作,从而提高性能。

  • diff 算法

    diff算法的本质就是:找出两个对象之间的差异,目的是尽可能做到节点复用。传统的 diff 算法遍历整个结构逐一对比,效率很低,React用三大策略将 O(n3) 复杂度转化为 O(n) 复杂度

  1. tree diff

    React 通过 updateDepth 对 Virtual DOM 树进行层级控制。对树分层比较,两棵树只对同一层次节点进行比较。如果该节点不存在时,则该节点及其子节点会被完全删除,不会再进一步比较。只需遍历一次,就能完成整棵DOM树的比较。

  2. component diff

    React对不同的组件间的比较:同一类型的两个组件,按原策略(层级比较)继续比较Virtual DOM树即可,同一类型的两个组件,组件A变化为组件B时,可能Virtual DOM没有任何变化,如果知道这点(变换的过程中,Virtual DOM没有改变),可节省大量计算时间,所以用户可以通过 shouldComponentUpdate() 来判断是否需要判断计算。不同类型的组件,将一个(将被改变的)组件判断为dirtycomponent(脏组件),从而替换整个组件的所有节点。

  3. element diff

    当节点处于同一层级时,diff提供三种节点操作:删除、插入、移动:组件 C 不在集合(A,B)中,需要插入;组件 D 在集合(A,B,D)中,但 D的节点已经更改,不能复用和更新,所以需要删除 旧的D ,再创建新的。组件D之前在集合(A,B,D)中,但集合变成新的集合(A,B)了,D 就需要被删除。组件D已经在集合(A,B,C,D)里了,且集合更新时,D没有发生更新,只是位置改变,如新集合(A,D,B,C),D在第二个,无须像传统diff,让旧集合的第二个B和新集合的第二个D 比较,并且删除第二个位置的B,再在第二个位置插入D,而是 (对同一层级的同组子节点) 添加唯一key进行区分,移动即可。

React 状态管理库

状态管理库的就是将转换抽取到 UI 外部进行统一管理

  • redux

由于react的单向数据流问题,导致state状态传递和复用十分困难。比如一个组件向兄弟组件传递信息时,需要先传入父组件,再传到兄弟组件,十分的不方便。或者在不太相关的一个A组件中,使用B组件的状态,都是难以实现的。

redux想出一个办法:将所有需要复用的状态集中存放在一起,就可以在任意组件中调用需要的状态。 而存放这些state的一个集中的库,我们就叫它store

import { createStore } from 'redux'
const store = createStore(reducer)

action 指的是视图层发起的一个操作,告诉 Store 我们需要改变。比如用户点击了按钮,我们就要去请求列表,列表的数据就会变更。每个 action 必须有一个 type 属性,这表示 action 的名称,然后还可以有一个 payload 属性,这个属性可以带一些参数,用作 Store 变更:

const action = {
  type: 'ADD_ITEM',
  payload: 'new item', // 可选属性
}

Action不会自己主动发出变更操作到Store,所以这里我们需要一个叫dispatch的东西,它专门用来发出action,在redux里面,store.dispatch()是 View发出 Action 的唯一方法

store.dispatch({
  type: 'ADD_ITEM',
  payload: 'new item', // 可选属性
})

当 dispatch 发起了一个 action 之后,会到达 reducer,这个reducer就是用来计算新的store的,reducer接收两个参数:当前的state和接收到的action,然后它经过计算,会返回一个新的state:

const reducer = function(prevState, action) {
  ...
  return newState;
};

下面是一个完整的例子:

store.js文件

//该文件专门用于暴露一个store对象,整个应用只有一个store对象

//引入createStore,,专门用于创建redux中最核心的store对象
import {createStore, applyMiddleware} from 'redux';
//引入为Count组件服务的reducer
import countReducer from './count_reducer'
//引入redux-thunk,用于支持异步action
import thunk from 'redux-thunk'
const store = createStore(countReducer, applyMiddleware(thunk))
//暴露store
export default store

constant.js

/*
 该模块是用于定义action对象中type类型的常量值,目的只有一个:便于管理的同时防止在书写单词的时候,出现错误
*/

export const INCREMENT = 'increment'
export const DECREMENT = 'decrement';

count_reducer.js

/*
1、该文件是用于创建一个Count组件服务的reducer,reducer的本质就是一个函数
2、reducer函数会接到两个参数,分别为:之前的状态(preState),动作对象(action)
3、reducer被第一次调用时,是store自动触发的,传递的preState是undefined,传递的action是类似于:{
 type: '@@REDUX/INIT_a.2.b.4'
}
*/
const {INCREMENT, DECREMENT} from './constant'
const initState = 0;//初始化状态,推荐写法

export default function countReducer (preState = initState, action) {
//if(preState === undefined) preState = 0
//从action对象中获取:type、data
 const { type, data } = action
 //根据type决定如何加工数据
 switch (type) {
  case INCREMENT://如果是加
    return preState + data;
   case DECREMENT://如果是减
    return preState - data;
   default: 
    return preState
 }
}

count_action.js

/*
 该文件专门为Count组件生成action对象
*/

function createIncrementAction(data) {
 return {
  type:'increment',
  data
 }
}

function createDecrementAction(data) {
 return {
  type:'decrement',
  data
 }
}

//改造之后
const {INCREMENT, DECREMENT} from './constant'

//同步action,就是指action的值为Object类型的一般对象
export const createIncrementAction = data => ({type:INCREMENT,data})
export const createDecrementAction = data => ({type:DECREMENT,data})

//异步action,就是指action的值为函数,异步action中一般都会调用同步action,异步action不是必须要用的。
export const createIncrementAsyncAction = (data, time) => {
 return (dispatch) => {
  setTimeout(() => {
   //函数体
   dispatch(createDecrementAction(data));
  },time)
 }
}

引入:

//引入store,用于获取redux中保存的状态
import store from '../../redux/store'
//引入actionCreator专门用于创建action对象
import {createIncrementAction,createDecrementAction,createIncrementAsyncAction} from '../../redux/count_action'

//加法
increment = () => {
 const { value } = this.selectNumber;
 //store.dispatch({type:'increment', date : value * 1});
 store.dispatch(createIncrementAction(value * 1));
}
//减法
decrement = () => {
 const { value } = this.selectNumber;
 //store.dispatch({type:'decrement', date : value * 1});
 store.dispatch(createDecrementAction(value * 1));
}

incrementAsync = () => {
 const { value } = this.selectNumber;
 store.dispatch(createIncrementAsyncAction(value * 1 , 500))
}

render() {
 return (
   <div>
     <h1>当前和为: {store.getState()}</h1>
   </div>
 )
}

监听redux变化

import React from "react";
import ReactDOM from "react-dom";
import App from './App';
import store from './store/store';
ReactDOM.render(<App/>,document.getElementById('root'))
// 在这里需要明确的是:redux只是一个状态的管理机制,它不会自动的触发页面的更新,需要我们自己去写
store.subscribe(() => ReactDOM.render(<App/>,document.getElementById('root')))
  • useReducer

在React hooks 中,可以使用 useReducer 作为状态管理的工具,接收两个参数:第一个参数是reducer函数,没错就是我们上一篇文章介绍的。第二个参数是初始化的state。返回值为最新的state和dispatch函数(用来触发reducer函数,计算对应的state)。

    // 官方 useReducer Demo
    // 第一个参数:应用的初始化
    const initialState = {count: 0};

    // 第二个参数:state的reducer处理函数
    function reducer(state, action) {
        switch (action.type) {
            case 'increment':
              return {count: state.count + 1};
            case 'decrement':
               return {count: state.count - 1};
            default:
                throw new Error();
        }
    }

    function Counter() {
        // 返回值:最新的state和dispatch函数
        const [state, dispatch] = useReducer(reducer, initialState);
        return (
            <>
                // useReducer会根据dispatch的action,返回最终的state,并触发rerender
                Count: {state.count}
                // dispatch 用来接收一个 action参数「reducer中的action」,用来触发reducer函数,更新最新的状态
                <button onClick={() => dispatch({type: 'increment'})}>+</button>
                <button onClick={() => dispatch({type: 'decrement'})}>-</button>
            </>
        );
    }

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【程序人生 | 价值扳机】你的寒假自律第一步(建议收藏)

&#x1f482;作者简介&#xff1a; THUNDER王&#xff0c;一名热爱财税和SAP ABAP编程以及热爱分享的博主。目前于江西师范大学会计专业大二本科在读&#xff0c;阿里云社区专家博主&#xff0c;华为云社区云享专家&#xff0c;CSDN SAP应用技术领域新兴创作者。   在学习工…

小技巧:Excel顽固的名称、引用冲突的解决

共享编辑、跨文档引用的不便 Excel 的共享文档多人编辑、跨文档引用等功能似乎很美好&#xff0c;实际用下来很成问题。 首先共享文档虽然用约定按标注责任人的方式由不同的人编辑不同的行避免的多人同时编辑一行的冲突&#xff0c;但是这通常是个大文件&#xff0c;经常(大概…

函数 C语言】

函数的声明和定义 函数间调用关系是&#xff0c;由于函数调用其他函数&#xff0c;替他函数也可以互相调用&#xff0c;同一个函数可以被一个或多个函数调用任意次。 先声明&#xff0c;后调用。 #include <stdio.h> //去标准库下找文件 #include "stdio.h"…

分件操作和IO

1.文件的路径如图:当我们打开此电脑后(windows系统),上面会显示我们当前的位置&#xff0c;点击之后会出现如图片中的一段字符&#xff0c;这段字符代表着当前你所处位置的路径。最开头的D&#xff1a;/d&#xff1a;是盘符&#xff0c;后面会用斜杠‘/’或者反斜杠‘\’分开,该…

MFC的使用

1.初使用的简单代码该程序包含两个代码 头文件mfc.h和mfc.cpp文件头文件mfc.h#pragma once #include<afxwin.h>class MyApp:public CWinApp//CWinApp应用程序类 { public://程序入口virtual BOOL InitInstance(); }; class MyFrame : public CFrameWnd//继承窗口框架类a …

Java_Git:2. 使用git管理文件版本

目录 1 创建版本库 1.1 使用GitBash 1.2 使用TortoiseGit 2 添加文件 2.1 添加文件过程 2.2 工作区和暂存区 3 修改文件 3.1 提交修改 3.2 查看修改历史 3.3 差异比较 3.4 还原修改 4 删除文件 5 案例&#xff1a;将java工程提交到版本库 5.1 复制文件到工作目录 …

论文阅读:Boosting 3D Object Detection by Simulating Multimodality on Point Clouds

Boosting 3D Object Detection by Simulating Multimodality on Point CloudsResponse DistillationSparse-Voxel DistillationVoxel-to-Point DistillationInstance DistillationlossExperiments稠密&#xff08;多模态、多帧&#xff09;信息->稀疏&#xff08;单模态点云…

【华为上机真题 2023】数组去重和排序 (华为机考真题)

&#x1f388; 作者&#xff1a;Linux猿 &#x1f388; 简介&#xff1a;CSDN博客专家&#x1f3c6;&#xff0c;华为云享专家&#x1f3c6;&#xff0c;Linux、C/C、云计算、物联网、面试、刷题、算法尽管咨询我&#xff0c;关注我&#xff0c;有问题私聊&#xff01; &…

2023/1/20 ES6基本语法学习

1 let 命令的解读 1 let 声明变量&#xff0c;没有变量提升 <script>// 不存在变量提升console.log(variable)let variable zhaoshuai-lc </script>2 作用域 全局作用域函数作用域&#xff1a;function() {}块级作用域&#xff1a;{} let 是一个块作用域 <…

Spring中涉及的设计模式总结

Spring中涉及的设计模式总结 1.简单工厂(非23种设计模式中的一种) 实例化阶段主要是通过反射或者CGLIB对bean进行实例化&#xff0c;在这个阶段Spring又给我们暴露了很多的扩展点&#xff1a; 实现方式&#xff1a;BeanFactory。Spring中的BeanFactory就是简单工厂模式的体现…

【设计模式】结构型模式·装饰者模式

学习汇总入口【23种设计模式】学习汇总(数万字讲解体系思维导图) 写作不易&#xff0c;如果您觉得写的不错&#xff0c;欢迎给博主来一波点赞、收藏~让博主更有动力吧&#xff01; 一.概述 在不改变现有对象结构的情况下&#xff0c;动态地给该对象增加一些职责&#xff08;即增…

2022年房地产投资退出方法和工具研究报告

第一章 房地产投资概况 房地产商品既是人们日常“衣食住行”中的一种必需品&#xff0c;又因保值增值的功能而具有很好的投资品属性。房地产投资是以房地产为对象&#xff0c;为获得预期效益而对土地和房地产开发、房地产经营&#xff0c;以及购置房地产等进行的投资。 房地产…

深度学习 GNN图神经网络(二)PyTorch Geometric(PyG)安装

一、前言 我们使用torch_geometric库来实现图神经网络的编码&#xff0c;因为它与PyTroch天然集成。本文介绍了PyTorch Geometric&#xff08;PyG&#xff09;的安装与测试。 二、安装 首先打开官方的安装说明文档&#xff1a;https://pytorch-geometric.readthedocs.io/en/…

蓝桥杯 stm32 LCD显示及 数据格式化

文章代码使用 HAL 库。 文章目录前言一、LCD 原理图&#xff1a;二、LCD 基本函数&#xff1a;1.LCD 清屏函数&#xff1a;LCD_Clear ( u16 Color )&#xff1b;2. 显示一行字符串&#xff1a;LCD_DisplayStringLine(u8 Line, u8 *ptr)&#xff1b;3.设置字符背景色&#xff1a…

switch自制软件开发环境搭建

参考: https://switch.homebrew.guide/ https://switchbrew.org/wiki/Main_Page https://www.bilibili.com/video/BV133411Q77X/?spm_id_from333.788&vd_sourcec5c272e9490d8bf475c8204462fc26e7 1.开发环境 开发机 -> 虚拟机 ubuntu22.04 设备 -> 破解switch 大…

Ubuntu20.04系统WineHQ7.0安装微信

提供3种Ubuntu系统安装微信的方法&#xff0c;在Ubuntu20.04上验证都ok。1.WineHQ7.0安装微信&#xff1a;ubuntu20.04安装最新版微信--可以支持微信最新版&#xff0c;但是适配的不是特别好&#xff1b;比如WeChartOCR.exe 报错。2. 原生微信安装&#xff1a;linux系统下的微信…

[电商实时数仓] 数据仓库建模过程分析

文章目录1.数据仓库概述1.1 数据仓库概念1.2 数据仓库核心架构2.数据仓库建模概述2.1 数据仓库建模的意义2.2 数据仓库建模方法论2.2.1 ER模型2.2.2 维度模型3.维度建模理论之事实表3.1 事实表概述3.2 事实表分类3.3 事务事实表4.维度建模理论之维度表5.数据仓库设计5.1 数据仓…

[前端笔记——HTML 表格] 8.HTML 表格

[前端笔记——HTML 表格] 8.HTML 表格1.HTML 表格基础1.1 什么是表格&#xff1f;1.2 创建表格&#xff1a;2.HTML 表格高级特性和无障碍2.1 使用<caption>为表格增加一个标题2.2 添加<thead>,<tfoot>和<tbody>结构2.3 嵌套表格2.4 对于视力受损的用户…

第五层:C++中的运算符重载

文章目录前情回顾运算符重载概念为什么会出现运算符重载运算符重载中函数名格式加减运算符重载作用实现左移运算符重载作用左移运算符是什么&#xff1f;实现递增递减运算符作用实现前置后置赋值运算符重载关系运算符重载作用实现函数调用运算符重载第二种重载掌握&#xff01;…

vueJs中toRaw与markRaw函数的使用比较

01toRaw()函数接收一个reactive响应式数据,将一个响应式的数据变为普通类型的数据,转化为非响应式数据,相当于还原对象,reactive相当于制作,但对于ref响应式数据不起作用将一个由reactive生成的响应式对象转为普通(原始)对象toRaw()可以返回由reactive(),readonly(),shallowRea…