【React】狀態管理 Context API \ MobX \ Redux

  • Context API
  • MobX
  • Redux

React有自己狀態管理,周邊生態也有很多狀態管理

Context API

直接從react中引入并調用即可,context包含兩個東西:

Provider:提供商(翻譯),提供數據;屬性:value={提供商需要提供的數據}<Provider value={}>組件內容</Provider>Consumer:消費者(翻譯),使用數據;內部是函數<Consumer>{(value)=>{ return 組件內容 }}</Consumer>

測試Context:

import {Component} from 'react'
import {createContext} from 'react' //引入react自帶的狀態管理,得先創建它之后才可以去用
const {Provider,Consumer}=createContext(); //用于創建一個上下文class Child extends Component {render () {return (<div><h1>兒子</h1><Child2/></div>)}
}class Child2 extends Component {render () {return (//使用時包裹起來,標明身份,Consumer內部是個函數<Consumer>{(value)=>{return <h1>孫子,使用Provider提供的數據:{value.a}</h1>}}</Consumer>/*<div><h1>子組件2</h1></div>*/)}
}class Parent extends Component {render () {return (//使用時包裹起來,標明身份,Provider不要忘記value屬性<Provider value={{a:1}}><h1>父組件</h1><Child/></Provider>/*<div><h1>父組件</h1><Child/></div>*/)}
}export  default Parent;

src\App.js

import './App.css';
import Routers from './router'
// import Parent from "./pages/home/context/myclass";
import ContextAPI from './store/index';function App() {return (<div className="App"><ContextAPI><Routers/></ContextAPI>{/*<Parent/>  /!*狀態管理 - 理解Context*!/*/}</div>);
}export default App;

創建公共數據組件:src\store\index.js

import {Component,createContext} from 'react'const {Provider,Consumer}=createContext();class ContextAPI extends Component {constructor() {super();this.state = {a:1,b:2}}setA=(v)=>{this.setState({a:v,});}setB=(v)=>{this.setState({b:10,});}render() {return <Provider value={{ /*value通過這個屬性能夠對外提供公共的數據*/state:this.state,setA:this.setA,setB:this.setB}}>{this.props.children} {/*展示ContextAPI組件中間的子組件內容的*/}</Provider>}
}export default ContextAPI;
export {Consumer}; //導出使用者:Consumer

使用公共狀態:src\pages\home\notice\index.js

import {Consumer} from '../../../store/index';const Notice = () => {return <Consumer>{(value)=>{console.log(value)return <div><h1>Notice</h1><h2>a:{value.state.a}</h2>  {/*使用提供者(公共組件)的數據*/}<h2>b:{value.state.b}</h2><button onClick={()=>{value.setA(25);  /*調用提供者共享出來的方法*/value.setB();}}>修改數據</button></div>}}</Consumer>/*return <div><h1>Notice</h1></div>*/
}export default Notice;

總結:

1)創建公共數據的組件,設置數據和修改數據的方法并導出2)將該組件包裹住最大的組件,所有的組件都可以獲取到公共組件的數據了2.1)公共組件需要設置 {this.props.children} 才能展示包裹住的組件的內容2.2)不能在使用公共數據的組件里面自己重新創建 const {Consumer}=createContext()因為重新創建的Consumer和我們之前在公共組件中的 Consumer 不是同一個對象3)哪個組件要使用公共數據就引入import {Consumer} from "../../../store";參考:notice -> index_context.js

MobX

mobx分兩部分:

mobx:mobx語法
mobx-react:更新react的虛擬dom,把mobx和react連接起來

安裝:安裝 mobx 和 mobx-react 或 mobx-react-lite;mobx-react 和 mobx-react-lite 都是連接 react 和mobx的中間件,區別是:mobx-react支持類和函數組件,體積相對而言較大;而mobx-react-lite只支持函數組件,體積較小,可以根據具體情況進行下載

npm i mobx mobx-react --save語法參考:<!-- https://mobx.js.org/README.html --><!-- https://github.com/mobxjs/mobx -->

引入:

import { makeAutoObservable } from "mobx"  //makeAutoObservable:使自動可觀察
import { observer } from "mobx-react-lite"  //observer:觀察者

使用mobx,創建store類,并實例化導出以供使用:src\store\index.js(class寫法)

import { makeAutoObservable } from "mobx"  //使自動可觀察(可觀測的)
// import { observer } from "mobx-react-lite"  //觀察者class Store {constructor() {makeAutoObservable(this); //統一所有的this指向的固定寫法}//定義數據a=1;b=2;//定義方法setA(v){this.a=v;}setB(){this.b=22;}//計算屬性get c(){    //添加計算屬性通過get關鍵詞實現return this.a+this.b;}}//實例化并導出
const store=new Store();
export default store;

mobx的函數組件的寫法:

import { makeAutoObservable } from "mobx"
import moduleMobx from './moduleMobx'  //引入子模塊const store=()=>{return makeAutoObservable({//...moduleMobx   //在組件上使用數據的時候寫法就是:store.dmoduleMobx,      //store.moduleMobx.d//定義數據a:1,b:2,//定義方法setA(v){this.a=v;},setB(){this.b=22;},//計算屬性get c(){    //添加計算屬性通過get關鍵詞實現return this.a+this.b;}})}export default store();

模塊化:src\store\moduleMobx.js

 const moduleMobx={ //直接導出notice.js需要使用的數據d:6,e:5,get dxe(){return this.d*this.e;},setD(v){this.d=v;}}export default moduleMobx;/*
如果直接:export default {}會出現黃色警告:Assign object to a variable before exporting as module default  import/no-anonymous-default-export
*/

使用mobx-react,observer包裹組件:src\pages\notice\index.js

import store from '../../../store';  //哪個組件需要用到store的數據就在哪個組件引入
import {observer} from 'mobx-react';  //觀察者,被它包裹的組件就是響應式的,改了數據頁面就會更新const Notice = () => {return <div><h1>Notice</h1>{/*使用子模塊*/}<div>d:{store.moduleMobx.d}</div><div>e:{store.moduleMobx.e}</div><div>計算屬性d*e:{store.moduleMobx.dxe}</div><button onClick={()=>{store.moduleMobx.setD(20)}}>修改</button><hr/>{/*沒有使用子模塊*/}<div>a:{store.a}</div><div>b:{store.b}</div><div>計算屬性c:{store.c}</div><button onClick={()=>{store.setA(18);}}>修改</button></div>}export default observer(Notice);

總結:

1)store文件中引入:import { makeAutoObservable } from "mobx" //可觀測的2)constructor() {makeAutoObservable(this); //統一所有的this指向的固定寫法}3)實例化store,并導出store4)需要使用的組件引入store5)引入observer:import {observer} from 'mobx-react' //被它包裹的組件就是響應式的,改了數據頁面就會更新6)將observer包裹當前組件:export default observer(Notice)7)頁面使用:<div>{store.a}</div>

計算屬性:

類似 Vuex 的 getter(c值:c=a+b,當a或b發生變化的時候,我希望c同步變化,就需要用到計算屬性)在Mobx中添加計算屬性通過get關鍵詞實現:get 方法名(){return 干啥;}

模塊化:

export default {  //直接導出component.js需要使用的數據數據}import moduleMobx from './moduleMobx'  //引入子模塊moduleMobx,    //組件使用時:store.子模塊名.d

Redux

參考網址:

Redux :https://cn.redux.js.org/introduction/getting-started/Redux Toolkit :https://toolkit.redux.js.cn/api/createSliceReact Redux :https://react-redux.js.org/tutorials/quick-start

特點:

1) 單一數據源:整個應用的狀態存儲在一個單一的對象樹,且該對象樹只存儲在一個store中2) State是只讀的:狀態是不可直接修改的,改變內部狀態的唯一方法是dispatch一個action3) 使用純函數來完成狀態變更:通過reducer將舊state和actions聯系在一起,并返回一個新的state,不產生任何副作用

在這里插入圖片描述

安裝依賴:

npm i @reduxjs/toolkit react-redux //@reduxjs/toolkit 是 Redux Toolkit 的核心庫,包含了創建 Redux 狀態管理邏輯的工具
//react-redux 是連接 React 和 Redux 的橋梁

創建 Redux Store: store\index.js
configureStore 是 @reduxjs/toolkit 提供的函數,自動集成了 Redux DevTools 和一些默認的中間件(如 thunk)。reducer 是狀態管理的核心

//Redux狀態管理(使用的是:Redux Toolkit+ react-redux)
import {configureStore} from '@reduxjs/toolkit';
import counterReducer from './counterSlice';   //可以創建多個slice切片(類似于模塊化)
import noticeReducer from './noticeSlice'//配置 store
const store=configureStore({reducer:{//注冊子切片(將reducer添加到store中)counter:counterReducer,notice:noticeReducer,}
})export default store;

創建 Slice: store\counterSlice.js
Slice 是 Redux Toolkit 中的概念,集成了 action creators 和 reducer 的定義,便于模塊化管理。每個 slice 通常代表應用中的一部分狀態
createSlice 自動生成 action types 和 action creators。狀態更新使用 Immer 實現,可以直接“修改” state,而無需手動展開對象

import {createSlice} from "@reduxjs/toolkit";//定義一個counter slice (slice:切片)
const counterSlice=createSlice({name: "counter", //slice的名稱(模塊名稱獨一無二)//初始化stateinitialState: {value:25,  //初始狀態},//修改數據的同步方法reducers:{addNumber:(state)=>{state.value += 1; //直接修改state(內置了Immer,無需手動返回新狀態)},reduceNumber:(state)=>{state.value -= 1;},numberByAmount:(state,action)=>{state.value += action.payload; //action.payload是傳遞的參數}}
})//導出 action creators(解構出動作創作者函數)
export const {addNumber,reduceNumber,numberByAmount} = counterSlice.actions;//導出 reducer
export default counterSlice.reducer;

將 Store 接入 React: src\index.js
在項目的入口文件(比如 src/index.js 或 src/main.js)中,使用 react-redux 的 Provider 將 Store 提供給整個應用

import React from 'react';
import ReactDOM from 'react-dom/client';
// 導入store
import store from './store';
// 導入store提供組件Provider
import { Provider } from 'react-redux';
import './index.css';
import App from './App';
import {HashRouter} from "react-router-dom";const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Provider store={store}> {/*包裹起來,提供store數據*/}<HashRouter><App /></HashRouter></Provider>);

在組件中使用 Redux: src\pages\home\notice\index.js
在 React 組件中,可以通過 react-redux 提供的 Hooks(如 useSelector 和 useDispatch)來訪問狀態和分發 action。useSelector 用于從 Store 中讀取狀態,useDispatch 用于觸發 action。性能優化:如果你的組件因狀態變化頻繁重渲染,可以結合 useSelector 的第二個參數(比較函數)或 Redux Toolkit 的 createSelector 來優化選擇器

import {useSelector,useDispatch} from "react-redux";
import {addNumber,reduceNumber,numberByAmount} from "../../../store/counterSlice";
import {fetchData,twoAsync} from "../../../store/noticeSlice";const Notice = () => {//獲取狀態const count=useSelector(state => state.counter.value);const adminData=useSelector(state=>state.notice.adminDate)const status=useSelector(state=>state.notice.status);//獲取dispatch函數const dispatch=useDispatch();return <div><h1>Notice</h1><h1>計數器:{count}</h1><button onClick={()=>dispatch(addNumber())}>1</button><button onClick={()=>dispatch(reduceNumber())}>1</button><button onClick={()=>dispatch(numberByAmount(3))}>3</button><h1>異步處理</h1><buttononClick={()=>dispatch(fetchData())}disabled={status==="loading"}style={{color: status==="failed"?'red':'green'}}>異步fetchData</button>{JSON.stringify(adminData)}<button onClick={()=>dispatch(twoAsync({age:25,name:"lmy"}))}>異步2</button></div>}export default Notice;

異步操作: store\noticeSlice.js
如果需要處理異步邏輯,可以使用 @reduxjs/toolkit 內置的 createAsyncThunk,然后在 createSlice 中通過 extraReducers 處理異步狀態

createAsyncThunk: 該函數有兩個參數,一個字符串,用作生成的action types的前綴;一個payload creator回調函數,應該返回一個Promise。這通常使用async/await語法編寫,因為async函數會自動返回一個Promise。createAsyncThunk函數生成一個 pending/fulfilled/rejected 基于該Promise分派動作類型的thunk。默認redux不能在reducers中處理異步,而在外部處理或者使用自帶的一個方法createAsyncThunk;createAsyncThunk可以被認為是一個action,只不過它是異步的。進而正常通過dispatch派發即可;但是提到異步,就免不了有狀態產生(pending/fulfilled/rejected),所以結果并不能被reducers正常歸納處理

import http from "../utils/http"; //使用自己封裝的fetch
import {createSlice,createAsyncThunk} from "@reduxjs/toolkit";//1.定義異步action
export const fetchData=createAsyncThunk("notice/fetchData",async ()=>{const response=await http({url:"/admin/getadmin"}).then(res => {console.log(res);return res;})await new Promise(resolve => setTimeout(resolve, 2000)); //等待 2 秒return response.data;})//3.多個異步
export const twoAsync=createAsyncThunk("notice/twoAsync",async (obj)=>{await new Promise(resolve => setTimeout(resolve,2000));return obj;})const noticeSlice=createSlice({name: "notice",initialState: {adminDate: undefined,status:"idle",error: null,},reducers: {// ... 同步 reducers},//2.然后在createSlice中處理異步的reducer,通過extraReducers處理異步狀態extraReducers:(builder)=>{builder.addCase(fetchData.pending, (state)=>{ //action被發出,但是還沒有最終的結果state.status="loading";console.log("待定pending")}).addCase(fetchData.fulfilled, (state, action)=>{ //獲取到最終的結果(有返回值的結果)state.adminDate=action.payload;console.log(state.adminDate)state.status="succeeded";console.log("已完成fulfilled")}).addCase(fetchData.rejected, (state, action)=>{ //執行過程中有錯誤或者拋出了異常state.status='failed';state.error = action.error.message; console.log("已拒絕rejected:",state.error) })//4.多個異步.addCase(twoAsync.fulfilled, (state, {payload})=>{console.log(payload)})}
})export default noticeSlice.reducer;

參考:https://blog.csdn.net/qq_34645412/article/details/144982492
React 18 與 Redux 的結合主要依賴 @reduxjs/toolkit 和 react-redux。通過 configureStore 創建 store,用 Provider 提供給組件樹,然后在組件中使用 hooks(如 useSelector 和 useDispatch)來操作狀態。通過以上步驟,就可以在 React 項目中成功使用 @reduxjs/toolkit 來管理狀態了 (注意:Redux Toolkit 本身與 React 版本無關,React-Redux 對 React 通常會有自己的版本要求)

在這里插入圖片描述

在Redux Toolkit出現之前,開發Redux應用通常涉及以下幾個步驟:
狀態管理目錄:
在這里插入圖片描述

步驟:(建議使用 Redux Toolkit 來編寫代碼,因為它能簡化你的代碼的同時消除許多常見的 Redux 問題和 Bug)

1.定義 Action Types:在 Redux 中,所有的 action 類型需要事先定義,以便在創建 action 和 reducer 時引用//constants.js2.創建 Actions:使用定義好的 action types 創建 action 對象//actionCreators.js 3.編寫 Reducers:根據 action 的類型更新 state//reducer.js4.引入并統一導出reducer 和 action//index.js	5.創建 Store:創建 Redux store,將 counter 和 home 的 reducer 進行合并成一個 reducer//store\index.js //參考:https://blog.csdn.net/kouzuhuai2956/article/details/145001802

redux-devtools 瀏覽器工具

給瀏覽器安裝拓展:
在這里插入圖片描述
按F12使用:可以看到存儲在 redux 中的數據

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/78249.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/78249.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/78249.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

RK3588上編譯opencv 及基于c++實現圖像的讀入

參考博文&#xff1a; https://blog.csdn.net/qq_47432746/article/details/147203889 一、安裝依賴包 sudo apt install build-essential cmake git pkg-config libgtk-3-dev libavcodec-dev libavformat-dev libswscale-dev libv4l-dev libxvidcore-dev libx264-dev libjpe…

MATLAB 訓練CNN模型 yolo v4

學生對小車控制提出了更好的要求&#xff0c;能否加入深度學習模型。 考慮到小車用matlab來做&#xff0c;yolo v5及以上版本都需要在pytorch下訓練&#xff0c;還是用早期版本來演示。 1 yolov4 調用 參考 trainYOLOv4ObjectDetector (mathworks.com) name "tiny-yo…

Windows下使用 VS Code + g++ 開發 Qt GUI 項目的完整指南

&#x1f680; 使用 VS Code g 開發 Qt GUI 項目的完整指南&#xff08;Windows MSYS2&#xff09; 本指南幫助你在 Windows 下使用 VS Code g CMake Qt6 快速搭建 Qt GUI 項目&#xff0c;適合熟悉 Visual Studio 的開發者向跨平臺 VS Code 工具鏈遷移。 &#x1f6e0;?…

開源漏洞掃描器:OpenVAS

一、OpenVAS介紹 OpenVAS (Open Vulnerability Assessment System) 是一款功能強大的開源漏洞掃描器。它由 Greenbone Networks 開發和維護&#xff0c;是 Greenbone 安全管理器 (GSM) 產品的基礎&#xff0c;同時也有免費的社區版本&#xff08;Greenbone Community Edition&…

Redis Pipeline 詳解

Redis Pipeline 詳解 Redis 無 Pipeline 耗時情況 : #mermaid-svg-8RIiJyeBO0uIrWjr {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-8RIiJyeBO0uIrWjr .error-icon{fill:#552222;}#mermaid-svg-8RIiJyeBO0uIrWjr .…

2025.04.24【3D】3D繪圖入門指南

Nifty graph A contribution by Matt Asher. 3D animation A 3D animated scatterplot made with R and rgl. 文章目錄 Nifty graph3D animation 2025.04.24【3D】| 3D繪圖入門指南什么是3D繪圖&#xff1f;為什么使用3D繪圖&#xff1f;如何在R中進行3D繪圖&#xff1f;安裝…

[特殊字符] 分布式事務中,@GlobalTransactional 與 @Transactional 到底怎么配合用?

在微服務架構中&#xff0c;隨著系統模塊的拆分&#xff0c;單體應用中的本地事務已經無法滿足跨服務的數據一致性需求。此時&#xff0c;我們就需要引入分布式事務解決方案&#xff0c;比如 Seata。在使用 Seata 的過程中&#xff0c;很多人會遇到一個常見的疑問&#xff1a; …

TDengine 集群高可用方案設計(二)

四、TDengine 集群高可用方案設計 4.1 硬件與網絡架構設計 服務器選型&#xff1a;選擇配置高、穩定性強的服務器&#xff0c;如戴爾 PowerEdge R740xd、華為 RH2288H V5 等。以戴爾 PowerEdge R740xd 為例&#xff0c;它配備英特爾至強可擴展處理器&#xff0c;具備高性能計…

從對數變換到深度框架:邏輯回歸與交叉熵的數學原理及PyTorch實戰

目錄 前言 一、連乘變連加二、最小化損失函數2.1交叉熵2.2 二分類交叉熵2.3 多分類交叉熵三、邏輯回歸與二分類3.1 邏輯回歸與二分類算法理論講解3.1.1 散點輸入3.1.2 前向計算3.1.3 Sigmoid函數引入3.1.4 參數初始化3.1.5 損失函數3.1.6 開始迭代3.1.7 梯度下降顯示四、基于框…

高企復審獎補!2025年合肥市高新技術企業重新認定獎勵補貼政策及申報條件

一、合肥市高新技術企業重新認定獎勵補貼政策 &#xff08;一&#xff09;高新區高新技術企業重新認定復審補貼獎勵 重新認定為國家高新技術企業的給予5萬元一次性獎勵。 &#xff08;二&#xff09;經開區高新技術企業重新認定復審補貼獎勵 對重新認定的企業&#xff0c;給…

Spring Boot 中配置線程池時優化 `ThreadPoolTaskExecutor` 的配置總結

在 Spring Boot 中配置線程池時&#xff0c;可以通過以下方式進一步優化 ThreadPoolTaskExecutor 的配置&#xff0c;提升性能、靈活性和可靠性&#xff1a; 優化點 1&#xff1a;合理設置線程池參數 關鍵參數調整 Bean(name "taskExecutor") public Executor tas…

opencv 圖像的旋轉

圖像的旋轉 1 單點旋轉2. 圖片旋轉&#xff08;cv2.getRotationMatrix2D&#xff09;3. 插值方法3.1 最近鄰插值(cv2.INTER_NEAREST)3.2 雙線性插值(cv2.INTER_LINEAR)3.3 像素區域插值&#xff08;cv2.INTER_AREA&#xff09;3.4 雙三次插值&#xff08;cv2.INTER_CUBIC&#…

如何在 Odoo 18 中配置自動化動作

如何在 Odoo 18 中配置自動化動作 Odoo是一款多功能的業務管理平臺&#xff0c;旨在幫助各種規模的企業更高效地處理日常運營。憑借其涵蓋銷售、庫存、客戶關系管理&#xff08;CRM&#xff09;、會計和人力資源等領域的多樣化模塊&#xff0c;Odoo 簡化了業務流程&#xff0c…

每日兩道leetcode

345. 反轉字符串中的元音字母 - 力扣&#xff08;LeetCode&#xff09; 題目 給你一個字符串 s &#xff0c;僅反轉字符串中的所有元音字母&#xff0c;并返回結果字符串。 元音字母包括 a、e、i、o、u&#xff0c;且可能以大小寫兩種形式出現不止一次。 示例 1&#xff1a;…

【SQL 基礎入門 1. -- SQL 基本語法詳解及舉例】

文章目錄 SQL 數據庫創建及使用刪除數據庫SQL 查看數據空中有哪些表格SQL 創建表格SQL 修改表格列數據格式SQL 表格插入數據SQL 查看表格類型組成SQL 查看表格中的內容 SQL 查詢語句SQL 查看指定列SQL 選擇指定列SQL 按指定列進行升序排序SQL 平均值/求和/最大值/最小值 SQL 數…

PostgreSQL 分區表——范圍分區SQL實踐

PostgreSQL 分區表——范圍分區SQL實踐 1、環境準備1-1、新增原始表1-2、執行腳本新增2400w行1-3、創建pg分區表-分區鍵為創建時間1-4、創建24年所有分區1-5、設置默認分區&#xff08;兜底用&#xff09;1-6、遷移數據1-7、創建分區表索引 2、SQL增刪改查測試2-1、查詢速度對比…

Apache Flink 深度解析:流處理引擎的核心原理與生產實踐指南

Apache Flink 深度解析&#xff1a;流處理引擎的核心原理與生產實踐指南 引言&#xff1a;實時計算的范式革命 2023年雙十一期間&#xff0c;某頭部電商平臺基于Flink構建的實時風控系統成功攔截了每秒超過120萬次的異常交易請求。這背后是Apache Flink作為第四代計算引擎的強…

【Java學習筆記】選擇結構

選擇結構 內容結構 一、順序結構 二、分支控制 &#xff08;1&#xff09;單分支 &#xff08;2&#xff09;雙分支 &#xff08;3&#xff09;多分支 &#xff08;4&#xff09;嵌套分支 &#xff08;5&#xff09;switch 分支結構 三、switch和if的比較 一、順序結構…

03_JavaScript

文章目錄 一、概述1.1、JavaScript簡介1.2、JavaScript組成部分1.3、為什么要學習JavaScript1.4、學習的目的1.5、JavaScript與Java的關系 二、使用位置及運行說明2.1、使用位置2.2、如何運行 三、JavaScript基礎語法3.1、變量3.2、運算符3.3、控制流程3.3.1、分支結構3.3.2、循…

PySide6 GUI 學習筆記——常用類及控件使用方法(常用類矩陣QRect)

文章目錄 一、構造與初始化方法二、坐標與尺寸獲取三、坐標與尺寸設置四、幾何運算方法五、移動與調整方法六、狀態判斷方法七、類型轉換方法八、操作符重載九、靜態方法十、特殊方法附錄方法速查表注意的問題交集和并集圖解 &#x1f4d8; PySide6.QtCore.QRect 使用整數精度定…