Redux 实践与中间件应用

news2025/6/7 7:40:24

Redux 异步处理的挑战

Redux 核心设计是同步的、单向数据流,但现代应用中异步操作无处不在。Redux 中间件填补了这一缺口,专门解决异步流程管理、副作用隔离等复杂场景。

中间件架构原理

中间件位于 action 被发起之后、到达 reducer 之前,提供了拦截和处理 action 的机会。

// 中间件基本结构
const middleware = store => next => action => {
  // 前置处理
  console.log('dispatching', action);
  
  // 调用下一个中间件或reducer
  let result = next(action);
  
  // 后置处理
  console.log('next state', store.getState());
  
  return result;
}

Redux-Thunk: 函数式异步处理

核心原理

Redux-Thunk 允许 action creator 返回函数而非普通对象,函数接收 dispatchgetState 参数。

// redux-thunk 核心实现(仅20行代码)
function createThunkMiddleware(extraArgument) {
  return ({ dispatch, getState }) => next => action => {
    if (typeof action === 'function') {
      return action(dispatch, getState, extraArgument);
    }
    return next(action);
  };
}

const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;

实战应用示例

// 用户列表加载功能完整实现
import { createSlice } from '@reduxjs/toolkit';
import axios from 'axios';

// Slice定义
const userSlice = createSlice({
  name: 'users',
  initialState: {
    data: [],
    loading: false,
    error: null
  },
  reducers: {
    fetchStart: (state) => {
      state.loading = true;
      state.error = null;
    },
    fetchSuccess: (state, action) => {
      state.data = action.payload;
      state.loading = false;
    },
    fetchFailure: (state, action) => {
      state.loading = false;
      state.error = action.payload;
    }
  }
});

// 导出actions
export const { fetchStart, fetchSuccess, fetchFailure } = userSlice.actions;

// Thunk action creator
export const fetchUsers = () => async (dispatch, getState) => {
  try {
    dispatch(fetchStart());
    
    // 可以访问当前state
    const { users } = getState();
    if (users.data.length > 0 && !users.loading) {
      return; // 避免重复加载
    }
    
    const response = await axios.get('https://api.example.com/users');
    
    dispatch(fetchSuccess(response.data));
  } catch (error) {
    dispatch(fetchFailure(error.message));
  }
};

// 带参数的thunk
export const fetchUserById = (userId) => async (dispatch) => {
  try {
    dispatch(fetchStart());
    const response = await axios.get(`https://api.example.com/users/${userId}`);
    dispatch(fetchSuccess([response.data]));
  } catch (error) {
    dispatch(fetchFailure(error.message));
  }
};

export default userSlice.reducer;

React组件集成

import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { fetchUsers } from './userSlice';

const UserList = () => {
  const dispatch = useDispatch();
  const { data, loading, error } = useSelector((state) => state.users);

  useEffect(() => {
    dispatch(fetchUsers());
  }, [dispatch]);

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error}</div>;

  return (
    <div>
      <h2>用户列表</h2>
      <ul>
        {data.map(user => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </div>
  );
};

export default UserList;

Redux-Saga: 声明式异步处理

核心原理

Redux-Saga 使用生成器函数管理副作用,提供丰富的组合操作符,适合复杂异步流程。
Ran tool

实战应用示例

// users模块完整实现
import { createSlice } from '@reduxjs/toolkit';
import { call, put, takeLatest, select } from 'redux-saga/effects';
import axios from 'axios';

// Slice定义
const userSlice = createSlice({
  name: 'users',
  initialState: {
    data: [],
    loading: false,
    error: null
  },
  reducers: {
    fetchUsersRequest: (state) => {
      state.loading = true;
      state.error = null;
    },
    fetchUsersSuccess: (state, action) => {
      state.data = action.payload;
      state.loading = false;
    },
    fetchUsersFailure: (state, action) => {
      state.loading = false;
      state.error = action.payload;
    }
  }
});

export const { 
  fetchUsersRequest, 
  fetchUsersSuccess, 
  fetchUsersFailure 
} = userSlice.actions;

// Saga函数
export function* fetchUsersSaga() {
  try {
    // 可以通过select Effect获取Redux状态
    const { users } = yield select();
    if (users.data.length > 0 && !users.loading) {
      return; // 避免重复加载
    }
    
    // call Effect执行异步调用
    const response = yield call(axios.get, 'https://api.example.com/users');
    
    // put Effect分发新action
    yield put(fetchUsersSuccess(response.data));
  } catch (error) {
    yield put(fetchUsersFailure(error.message));
  }
}

// 带参数的Saga
export function* fetchUserByIdSaga(action) {
  try {
    const { userId } = action.payload;
    const response = yield call(axios.get, `https://api.example.com/users/${userId}`);
    yield put(fetchUsersSuccess([response.data]));
  } catch (error) {
    yield put(fetchUsersFailure(error.message));
  }
}

// Root Saga
export function* usersSaga() {
  // 监听对应action类型,触发saga处理函数
  yield takeLatest('users/fetchUsersRequest', fetchUsersSaga);
  yield takeLatest('users/fetchUserById', fetchUserByIdSaga);
}

export default userSlice.reducer;

复杂流程处理

import { call, put, takeLatest, all, race, delay } from 'redux-saga/effects';

// 并发请求
function* fetchDashboardData() {
  try {
    yield put(dashboardLoadingStart());
    
    // 并行执行多个请求
    const [users, posts, comments] = yield all([
      call(axios.get, '/api/users'),
      call(axios.get, '/api/posts'),
      call(axios.get, '/api/comments')
    ]);
    
    yield put(dashboardLoadSuccess({ users: users.data, posts: posts.data, comments: comments.data }));
  } catch (error) {
    yield put(dashboardLoadFailure(error.message));
  }
}

// 请求竞态处理(请求超时处理)
function* fetchUserWithTimeout(action) {
  try {
    const { userId } = action.payload;
    
    // 竞争条件:请求成功 vs 超时
    const { response, timeout } = yield race({
      response: call(axios.get, `https://api.example.com/users/${userId}`),
      timeout: delay(5000) // 5秒超时
    });
    
    if (response) {
      yield put(fetchUserSuccess(response.data));
    } else if (timeout) {
      yield put(fetchUserFailure('请求超时'));
    }
  } catch (error) {
    yield put(fetchUserFailure(error.message));
  }
}

Redux-Observable: 响应式异步处理

核心原理

Redux-Observable 基于 RxJS,以 Epic 形式处理 action 流,擅长复杂事件流处理和响应式编程。

// Epic基本结构
const pingEpic = (action$, state$) => 
  action$.pipe(
    ofType('PING'),
    delay(1000),
    map(() => ({ type: 'PONG' }))
  );

实战应用示例

// 用户模块完整实现
import { createSlice } from '@reduxjs/toolkit';
import { ofType } from 'redux-observable';
import { ajax } from 'rxjs/ajax';
import { mergeMap, map, catchError, filter, withLatestFrom } from 'rxjs/operators';
import { of } from 'rxjs';

// Slice定义
const userSlice = createSlice({
  name: 'users',
  initialState: {
    data: [],
    loading: false,
    error: null
  },
  reducers: {
    fetchUsersRequest: (state) => {
      state.loading = true;
      state.error = null;
    },
    fetchUsersSuccess: (state, action) => {
      state.data = action.payload;
      state.loading = false;
    },
    fetchUsersFailure: (state, action) => {
      state.loading = false;
      state.error = action.payload;
    },
    fetchUserById: (state, action) => {
      state.loading = true;
      state.error = null;
    }
  }
});

export const { 
  fetchUsersRequest, 
  fetchUsersSuccess, 
  fetchUsersFailure,
  fetchUserById
} = userSlice.actions;

// Epic定义
export const fetchUsersEpic = (action$, state$) => 
  action$.pipe(
    ofType('users/fetchUsersRequest'),
    withLatestFrom(state$),
    filter(([action, state]) => {
      // 避免重复加载
      return state.users.data.length === 0 || state.users.loading === false;
    }),
    mergeMap(() => 
      ajax.getJSON('https://api.example.com/users').pipe(
        map(response => fetchUsersSuccess(response)),
        catchError(error => of(fetchUsersFailure(error.message)))
      )
    )
  );

export const fetchUserByIdEpic = (action$) => 
  action$.pipe(
    ofType('users/fetchUserById'),
    mergeMap(action => 
      ajax.getJSON(`https://api.example.com/users/${action.payload}`).pipe(
        map(response => fetchUsersSuccess([response])),
        catchError(error => of(fetchUsersFailure(error.message)))
      )
    )
  );

export default userSlice.reducer;

复杂操作符应用

import { combineEpics } from 'redux-observable';
import { interval, of, EMPTY } from 'rxjs';
import { 
  switchMap, debounceTime, distinctUntilChanged, 
  takeUntil, retry, timeout
} from 'rxjs/operators';

// 自动补全搜索Epic
const autocompleteEpic = (action$) => action$.pipe(
  ofType('SEARCH_INPUT_CHANGE'),
  debounceTime(300), // 防抖
  distinctUntilChanged(), // 防止重复请求相同搜索词
  switchMap(action => {
    // switchMap取消前一次未完成的请求
    const searchTerm = action.payload;
    return ajax.getJSON(`https://api.example.com/search?q=${searchTerm}`).pipe(
      map(results => ({ type: 'SEARCH_RESULTS', payload: results })),
      takeUntil(action$.pipe(ofType('CANCEL_SEARCH'))),
      timeout(5000),
      retry(2),
      catchError(err => of({ type: 'SEARCH_ERROR', payload: err.message }))
    );
  })
);

// 长轮询Epic
const pollingEpic = (action$) => action$.pipe(
  ofType('START_POLLING'),
  switchMap(action => {
    return interval(10000).pipe(
      switchMap(() => 
        ajax.getJSON('https://api.example.com/updates').pipe(
          map(data => ({ type: 'RECEIVE_UPDATES', payload: data })),
          catchError(err => of({ type: 'POLLING_ERROR', payload: err.message }))
        )
      ),
      takeUntil(action$.pipe(ofType('STOP_POLLING')))
    );
  })
);

中间件方案对比分析

特性Redux-ThunkRedux-SagaRedux-Observable
学习曲线低,函数式编程中高,Generator语法高,需RxJS基础
代码复杂度低(简单场景)
高(复杂场景)
中等初始高,后期可降低
测试难度中等,需模拟异步低,纯函数易测试中等,需RxJS测试工具
异步流程控制基础,手动控制丰富,声明式极其强大,响应式流
取消操作困难,需手动实现简单,内置支持简单,内置支持
竞态处理困难,需手动实现简单,内置race简单,多种操作符
并发控制需手动实现内置all/fork内置多种操作符
调试便利性一般优秀,支持时间旅行一般,需RxJS工具
适用场景简单异步操作复杂业务流程事件流处理、响应式UI

中间件选型决策指南

项目规模考量

  • 小型项目: Redux-Thunk 足够应付,学习成本最低
  • 中型项目: Redux-Saga 平衡了复杂性和功能性
  • 大型项目: Redux-Observable 提供长期可扩展性,尤其处理事件流、实时应用

团队因素

  • 团队熟悉度:已有RxJS经验优先考虑Redux-Observable
  • 学习资源:Redux-Saga社区资源更丰富
  • 新手友好度:Redux-Thunk > Redux-Saga > Redux-Observable

业务复杂度

  • 简单CRUD:Redux-Thunk
  • 多步骤流程、状态机:Redux-Saga
  • 复杂事件流、高响应UI:Redux-Observable

中间件整合

Redux Toolkit整合

import { configureStore } from '@reduxjs/toolkit';
import createSagaMiddleware from 'redux-saga';
import { createEpicMiddleware, combineEpics } from 'redux-observable';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
import { rootSaga } from './sagas';
import { rootEpic } from './epics';

// 创建中间件实例
const sagaMiddleware = createSagaMiddleware();
const epicMiddleware = createEpicMiddleware();

const store = configureStore({
  reducer: rootReducer,
  middleware: (getDefaultMiddleware) => 
    getDefaultMiddleware()
      .prepend(thunk)  // 简单异步处理
      .concat(sagaMiddleware)  // 复杂业务逻辑
      .concat(epicMiddleware)  // 特殊事件流处理
});

// 运行根saga和根epic
sagaMiddleware.run(rootSaga);
epicMiddleware.run(rootEpic);

export default store;

模块化组织

src/
├── features/
│   ├── users/
│   │   ├── usersSlice.js    # 定义state和reducers
│   │   ├── usersThunks.js   # 简单异步操作
│   │   ├── usersSagas.js    # 复杂业务流程
│   │   └── usersEpics.js    # 响应式事件流
│   └── ...
├── store/
│   ├── rootReducer.js       # 组合所有reducers  
│   ├── rootSaga.js          # 组合所有sagas
│   ├── rootEpic.js          # 组合所有epics
│   └── index.js             # store配置

案例:购物车

需求场景

实现购物车功能,包括添加商品、调整数量、获取实时库存和优惠信息、下单流程。

混合中间件实现

// cartSlice.js
import { createSlice } from '@reduxjs/toolkit';
import axios from 'axios';

const cartSlice = createSlice({
  name: 'cart',
  initialState: {
    items: [],
    loading: false,
    error: null,
    checkoutStatus: 'idle' // 'idle' | 'processing' | 'success' | 'failed'
  },
  reducers: {
    // 基础state变更
    addToCart: (state, action) => {
      const { product, quantity = 1 } = action.payload;
      const existingItem = state.items.find(item => item.id === product.id);
      
      if (existingItem) {
        existingItem.quantity += quantity;
      } else {
        state.items.push({...product, quantity});
      }
    },
    
    updateQuantity: (state, action) => {
      const { productId, quantity } = action.payload;
      const item = state.items.find(item => item.id === productId);
      if (item) {
        item.quantity = quantity;
      }
    },
    
    removeFromCart: (state, action) => {
      const productId = action.payload;
      state.items = state.items.filter(item => item.id !== productId);
    },
    
    // 异步action状态管理
    checkoutStart: (state) => {
      state.checkoutStatus = 'processing';
      state.error = null;
    },
    
    checkoutSuccess: (state) => {
      state.checkoutStatus = 'success';
      state.items = [];
    },
    
    checkoutFailure: (state, action) => {
      state.checkoutStatus = 'failed';
      state.error = action.payload;
    },
    
    // 库存检查
    inventoryCheckStart: (state) => {
      state.loading = true;
    },
    
    inventoryCheckSuccess: (state, action) => {
      state.loading = false;
      // 更新商品库存信息
      action.payload.forEach(stockInfo => {
        const item = state.items.find(item => item.id === stockInfo.productId);
        if (item) {
          item.inStock = stockInfo.inStock;
          item.maxAvailable = stockInfo.quantity;
        }
      });
    },
    
    inventoryCheckFailure: (state, action) => {
      state.loading = false;
      state.error = action.payload;
    }
  }
});

export const {
  addToCart,
  updateQuantity,
  removeFromCart,
  checkoutStart,
  checkoutSuccess,
  checkoutFailure,
  inventoryCheckStart,
  inventoryCheckSuccess,
  inventoryCheckFailure
} = cartSlice.actions;

export default cartSlice.reducer;

使用Thunk处理简单操作

// cartThunks.js
import axios from 'axios';
import {
  inventoryCheckStart,
  inventoryCheckSuccess,
  inventoryCheckFailure
} from './cartSlice';

// 简单库存检查 - Thunk适合
export const checkInventory = () => async (dispatch, getState) => {
  const { cart } = getState();
  if (cart.items.length === 0) return;
  
  try {
    dispatch(inventoryCheckStart());
    
    // 获取购物车内所有商品ID
    const productIds = cart.items.map(item => item.id);
    
    // 检查库存
    const response = await axios.post('/api/inventory/check', { productIds });
    
    dispatch(inventoryCheckSuccess(response.data));
  } catch (error) {
    dispatch(inventoryCheckFailure(error.message));
  }
};

使用Saga处理复杂结账流程

// cartSagas.js
import { takeLatest, put, call, select, all } from 'redux-saga/effects';
import axios from 'axios';
import {
  checkoutStart,
  checkoutSuccess,
  checkoutFailure
} from './cartSlice';

// 复杂结账流程 - Saga适合多步骤流程
function* checkoutSaga() {
  try {
    // 1. 获取当前购物车
    const { cart } = yield select();
    if (cart.items.length === 0) {
      yield put(checkoutFailure('购物车为空'));
      return;
    }
    
    // 2. 最终库存确认
    const inventoryResponse = yield call(axios.post, '/api/inventory/check', {
      productIds: cart.items.map(item => item.id)
    });
    
    // 3. 检查库存不足情况
    const outOfStockItems = [];
    inventoryResponse.data.forEach(stockInfo => {
      const cartItem = cart.items.find(item => item.id === stockInfo.productId);
      if (cartItem && cartItem.quantity > stockInfo.quantity) {
        outOfStockItems.push({
          ...cartItem,
          availableQuantity: stockInfo.quantity
        });
      }
    });
    
    if (outOfStockItems.length > 0) {
      yield put(checkoutFailure({
        message: '部分商品库存不足',
        outOfStockItems
      }));
      return;
    }
    
    // 4. 创建订单
    const orderData = {
      items: cart.items,
      totalAmount: cart.items.reduce((total, item) => total + item.price * item.quantity, 0)
    };
    
    const orderResponse = yield call(axios.post, '/api/orders', orderData);
    
    // 5. 处理支付
    const paymentResponse = yield call(axios.post, '/api/payments', {
      orderId: orderResponse.data.id,
      amount: orderData.totalAmount
    });
    
    // 6. 完成结账
    yield put(checkoutSuccess());
    
    // 7. 可选:发送确认邮件等后续操作
    yield call(axios.post, '/api/notifications/order-confirmation', {
      orderId: orderResponse.data.id
    });
    
  } catch (error) {
    yield put(checkoutFailure(error.message));
  }
}

export function* cartSagas() {
  yield all([
    takeLatest('cart/checkoutStart', checkoutSaga)
  ]);
}

使用Observable处理实时价格

// cartEpics.js
import { ofType } from 'redux-observable';
import { ajax } from 'rxjs/ajax';
import { 
  mergeMap, map, catchError, switchMap, 
  debounceTime, takeUntil, withLatestFrom
} from 'rxjs/operators';
import { of, timer } from 'rxjs';

// 处理实时价格更新 - Observable适合事件流
export const priceUpdateEpic = (action$, state$) => action$.pipe(
  ofType('cart/addToCart', 'cart/updateQuantity', 'cart/removeFromCart'),
  debounceTime(500), // 防抖,避免频繁请求
  withLatestFrom(state$),
  switchMap(([action, state]) => {
    const { cart } = state;
    
    // 如果购物车为空,不请求
    if (cart.items.length === 0) {
      return of({ type: 'cart/priceUpdateSkipped' });
    }
    
    // 准备请求数据
    const requestData = {
      items: cart.items.map(item => ({
        productId: item.id,
        quantity: item.quantity
      }))
    };
    
    // 发起价格计算请求
    return ajax.post('/api/cart/calculate', requestData).pipe(
      map(response => ({
        type: 'cart/priceUpdateSuccess',
        payload: response.response
      })),
      takeUntil(action$.pipe(ofType('cart/addToCart', 'cart/updateQuantity', 'cart/removeFromCart'))),
      catchError(error => of({
        type: 'cart/priceUpdateFailure',
        payload: error.message
      }))
    );
  })
);

// 实时库存轮询 - Observable适合循环事件
export const inventoryPollingEpic = (action$, state$) => action$.pipe(
  ofType('INVENTORY_POLLING_START'),
  switchMap(() => {
    // 每30秒查询一次库存
    return timer(0, 30000).pipe(
      withLatestFrom(state$),
      mergeMap(([_, state]) => {
        const { cart } = state;
        
        if (cart.items.length === 0) {
          return of({ type: 'INVENTORY_POLLING_SKIP' });
        }
        
        const productIds = cart.items.map(item => item.id);
        
        return ajax.post('/api/inventory/check', { productIds }).pipe(
          map(response => ({
            type: 'cart/inventoryCheckSuccess',
            payload: response.response
          })),
          catchError(error => of({
            type: 'cart/inventoryCheckFailure',
            payload: error.message
          }))
        );
      }),
      takeUntil(action$.pipe(ofType('INVENTORY_POLLING_STOP')))
    );
  })
);

总结与思考

  1. 混合策略:根据需求复杂度选择合适的中间件

    • 简单异步操作:Redux-Thunk
    • 复杂业务流程:Redux-Saga
    • 事件流处理:Redux-Observable
  2. 测试优先:中间件测试方法各不相同

    • Thunk:模拟store和API调用
    • Saga:单独测试每个generator函数
    • Observable:使用RxJS专用测试工具如TestScheduler
  3. 状态设计:保持状态扁平化,规范异步状态表示

    • 包含loading/error/data三要素
    • 使用请求状态枚举(idle/loading/success/failed)
    • 规范化复杂数据结构
  4. 代码组织:按功能模块化

    • 每个模块包含reducer、action、不同中间件实现
    • 按业务领域而非技术层次拆分文件
    • 使用barrel文件导出公共API
  5. 性能保障

    • 避免冗余请求:使用缓存状态和条件判断
    • 处理竞态条件:取消过时操作
    • 调试体验:使用Redux DevTools监控action和状态变化

Redux中间件生态是前端异步状态管理的强大解决方案,选择合适的中间件组合才能提升开发效率和代码可维护性。

参考资源

官方文档

  • Redux 官方文档 - 包含中间件概念和API详解
  • Redux Toolkit 官方文档 - 现代Redux最佳实践工具集
  • Redux-Thunk GitHub - 官方仓库和文档
  • Redux-Saga 官方文档 - 完整API参考和教程
  • Redux-Observable 文档 - 详细介绍Epic和操作符

教程与深度解析

  • Dan Abramov: Redux中间件详解 - Redux创建者对中间件的深度解释
  • LogRocket: Redux中间件对比 - 不同中间件优缺点分析
  • RxJS官方文档 - Redux-Observable的核心依赖

工具与插件

  • Redux DevTools Extension - 调试Redux应用的必备工具
  • redux-logger - 日志中间件,展示action流
  • redux-persist - Redux状态持久化方案

视频教程

  • Redux Middleware深入浅出 - Dan Abramov的系列课程
  • Redux-Saga入门到精通 - 完整视频教程
  • RxJS + Redux实战 - Redux-Observable使用指南

社区讨论

  • Redux中间件选择指南 - Stack Overflow上的详细对比
  • Redux FAQ - 中间件相关问题 - 官方常见问题解答

高级模式与最佳实践

  • 可取消的异步操作模式 - Redux-Saga中的任务取消
  • Redux性能优化指南 - 官方性能提升建议
  • React Query与Redux协作 - 现代数据获取方案与Redux集成

趋势与未来

  • Redux Toolkit Query - Redux生态的最新数据获取解决方案
  • Reselect - Redux选择器库,提升性能

如果你觉得这篇文章有帮助,欢迎点赞收藏,也期待在评论区看到你的想法和建议!👇

终身学习,共同成长。

咱们下一期见

💻

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

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

相关文章

ModBus总线协议

一、知识点 1. 什么是Modbus协议&#xff1f; Modbus 是一种工业通信协议&#xff0c;最早由 Modicon 公司在1979年提出&#xff0c;目的是用于 PLC&#xff08;可编程逻辑控制器&#xff09;之间的数据通信。它是主从式通信&#xff0c;即一个主机&#xff08;主设备&#xf…

【计算机网络】非阻塞IO——poll实现多路转接

&#x1f525;个人主页&#x1f525;&#xff1a;孤寂大仙V &#x1f308;收录专栏&#x1f308;&#xff1a;计算机网络 &#x1f339;往期回顾&#x1f339;&#xff1a;【计算机网络】非阻塞IO——select实现多路转接 &#x1f516;流水不争&#xff0c;争的是滔滔不息 一、…

DAX权威指南8:DAX引擎与存储优化

文章目录 十七、DAX引擎17.1 DAX 引擎的体系结构17.1.1 表格模型的双引擎架构17.1.2 存储引擎的三种模式17.1.2.1 VertiPaq引擎17.1.2.2 DirectQuery 引擎17.1.2.3 对比与最佳实践 17.1.3 数据刷新 17.2 理解 VertiPaq 存储引擎17.2.1 列式数据库17.2.2 VertiPaq 压缩17.2.2.1 …

智慧货运飞船多维度可视化管控系统

图扑搭建智慧货运飞船可视化系统&#xff0c;借数字孪生技术&#xff0c;高精度复刻货运飞船外观、结构与运行场景。整合多维度数据&#xff0c;实时呈现飞行状态、设备参数等信息&#xff0c;助力直观洞察货运飞船运行逻辑&#xff0c;为航天运维、任务推演及决策提供数字化支…

电脑开不了机,主板显示67码解决过程

文章目录 现象分析内存条问题BIOS设置问题其它问题 解决清理内存条金手指所需工具操作步骤注意事项 电脑在运行过程中&#xff0c;显示内存不足&#xff0c;重启电脑却无法启动。 现象 System Initialization 主板风扇是转的&#xff0c;也有灯光显示&#xff0c;插上屏幕&am…

自托管图书搜索引擎Bookologia

简介 什么是 Bookologia &#xff1f; Bookologia 是一个专门的书籍搜索引擎&#xff0c;可以在几秒钟内找到任何书籍。它是开源的&#xff0c;可以轻松自托管在 Docker 上&#xff0c;为用户提供一个简单而高效的书籍查找体验。 主要特点 简洁的用户界面&#xff1a;界面设计…

前端flex、grid布局

flex布局 弹性布局是指通过调整其内元素的宽高&#xff0c;从而在任何的显示设备上实现对可用显示空间最佳填充的能力。弹性容器扩展其内元素来填充可用空间&#xff0c;或将其收缩来避免溢出 简单来说&#xff0c;弹性盒子模型&#xff0c;是为了你的网页可以在不同分辨率设…

Maven相关问题:jna版本与ES冲突 + aop失效

文章目录 1、背景2、解决3、一点思考4、环境升级导致AOP失效5、okhttp Bean找不到6、总结 记录一些Maven依赖相关的思考 1、背景 做一个监控指标收集&#xff0c;用一下jna依赖&#xff1a; <dependency><groupId>net.java.dev.jna</groupId><artifact…

Tomcat全方位监控实施方案指南

#作者&#xff1a;程宏斌 文章目录 一&#xff0e;二进制部署1、安装包信息2、新建配置文件2.1 配置config.yaml文件2.2 上传jar包 3、修改配置3.1 备份3.2 修改bin目录下的startup.sh文件 4、重启tomcat5、访问测试 二&#xff0e;docker部署1、临时方案1.1、重新启动容器1.2…

To be or Not to be, That‘s a Token——论文阅读笔记——Beyond the 80/20 Rule和R2R

本周又在同一方向上刷到两篇文章&#xff0c;可以说&#xff0c;……同学们确实卷啊&#xff0c;要不卷卷开放场域的推理呢&#xff1f; 这两篇都在讲&#xff1a;如何巧妙的利用带有分支能力的token来提高推理性能或效率的。 第一篇叫 Beyond the 80/20 Rule: High-Entropy Mi…

《UE5_C++多人TPS完整教程》学习笔记37 ——《P38 变量复制(Variable Replication)》

本文为B站系列教学视频 《UE5_C多人TPS完整教程》 —— 《P38 变量复制&#xff08;Variable Replication&#xff09;》 的学习笔记&#xff0c;该系列教学视频为计算机工程师、程序员、游戏开发者、作家&#xff08;Engineer, Programmer, Game Developer, Author&#xff09…

AWS API Gateway配置日志

问题 访问API Gateway接口出现了403问题&#xff0c;具体报错如下&#xff1a; {"message":"Missing Authentication Token"}需要配置AWS API Gateway日志&#xff0c;看请求过程是什么样子的。 API Gateway 先找到API Gateway的的日志角色&#xff0c…

Towards Open World Object Detection概述(论文)

论文&#xff1a;https://arxiv.org/abs/2103.02603 代码&#xff1a;https://github.com/JosephKJ/OWOD Towards Open World Object Detection 迈向开放世界目标检测 Abstract 摘要 Humans have a natural instinct to identify unknown object instances in their environ…

轻松备份和恢复 Android 系统 | 4 种解决方案

我们通常会在 Android 手机上存储大量重要的个人数据&#xff0c;包括照片、视频、联系人、信息等等。如果您不想丢失宝贵的数据&#xff0c;可以备份 Android 数据。当您需要访问和使用这些数据时&#xff0c;可以将其恢复到 Android 设备。如果您想了解 Android 备份和恢复&a…

具备强大的数据处理和分析能力的智慧地产开源了

智慧地产视觉监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒&#xff0c;省去繁琐重复的适配流程&#xff0c;实现芯片、算法、应用的全流程组合&#xff0c;从而大大减少企业级应用约95%的开发成本。 AI是新形势下数…

【iSAQB软件架构】软件架构中构建块的视图:黑箱、灰箱和白箱及其交互机制

在软件架构描述中&#xff0c;黑箱视图&#xff08;Black-box&#xff09;、灰箱视图&#xff08;Gray-box&#xff09;和白箱视图&#xff08;White-box&#xff09; 是不同抽象层级的构建模块表示方式&#xff0c;用于满足不同受众和设计阶段的需求。以下是基于ISAQB标准的清…

如何在电脑上轻松访问 iPhone 文件

我需要将 iPhone 下载文件夹中的文件传输到 Windows 11 电脑上。我该怎么做&#xff1f;我可以在 Windows 11 上访问 iPhone 下载吗&#xff1f; 由于 iOS 和 Windows 系统之间的差异&#xff0c;在 PC 上访问 iPhone 文件似乎颇具挑战性。然而&#xff0c;只要使用正确的工具…

C语言字符数组输入输出方法大全(附带实例)

在 C语言中&#xff0c;字符数组是一种特殊的数组&#xff0c;用于存储和处理字符串。理解字符数组的输入和输出操作对于初学者来说至关重要&#xff0c;因为这是处理文本数据的基础。 字符数组的定义与初始化 在讨论输入输出之前&#xff0c;我们先来回顾一下字符数组的定义…

短视频矩阵SaaS系统:开源部署与核心功能架构指南

一、系统架构概述 短视频矩阵系统是基于SaaS&#xff08;软件即服务&#xff09;模式的多平台内容管理解决方案&#xff0c;通过开源技术实现账号聚合、智能创作、跨平台分发及数据闭环。系统采用微服务架构&#xff0c;支持高并发场景下的弹性扩展。 二、核心功能模块开发逻辑…

oss:上传图片到阿里云403 Forbidden

访问图片出现403Forbidden问题&#xff0c;我们可以直接登录oss账号&#xff0c;查看对应权限是否开通&#xff0c;是否存在跨域问题