在本指南中,我们将介绍:
- CRUD 操作
- 分页
- Redux 通过 RTK 查询持久化
- 多个基本 URL 使用
- 受保护和公共路线
- 缓存管理和失效
RTK 查询 是内置于 Redux Toolkit (RTK) 中的高级数据获取和缓存工具。它通过为获取、缓存和更新数据等常见任务生成 Redux 切片和挂钩来简化 API 交互。主要功能包括:
- 自动缓存:RTK Query 缓存数据,并在数据失效时自动重新获取,确保 UI 始终拥有最新数据。
- 缓存失效:RTK 查询使用标签,让您定义何时应重新获取某些数据。这有助于保持缓存最新,无需手动更新数据。
- 自动生成的钩子:RTK Query 为每个 API 端点创建钩子,允许您使用简单的 React 钩子(useGetPostsQuery、useCreatePostMutation 等)调用 API。
- 错误处理:包括通过中间件的自定义错误处理,可以轻松捕获和显示错误。
- 简化的 Redux 集成:RTK Query 直接与 Redux 集成,因此您不需要额外的库来进行全局状态管理或缓存。
RTK 查询与 React 查询
React Query 和 RTK Query 都提供了 React 应用程序中的数据获取和缓存解决方案,但它们具有不同的优势和用例:
Feature | RTK Query | React Query |
---|---|---|
Purpose | Integrated within Redux for managing server data in Redux state. Best for apps already using Redux or requiring centralized global state. | Dedicated to managing server state with no Redux dependency. Great for apps focused on server state without Redux. |
Caching | Automatic caching with fine-grained cache invalidation through tags. Caches data globally within the Redux store. | Automatic caching with flexible cache control policies. Maintains a separate cache independent of Redux. |
Generated Hooks | Auto-generates hooks for endpoints, allowing mutations and queries using useQuery and useMutation hooks. | Provides hooks (useQuery, useMutation) that work independently from Redux, but require manual configuration of queries and mutations. |
DevTools | Integrated into Redux DevTools, making debugging seamless for Redux users. | Provides its own React Query DevTools, with detailed insight into query states and cache. |
Error Handling | Centralized error handling using Redux middleware. | Error handling within individual queries, with some centralized error-handling options. |
Redux Integration | Built directly into Redux, simplifying usage for Redux-based apps. | Not integrated with Redux by default, although Redux and React Query can be combined if needed. |
在 RTK 查询和 React 查询之间进行选择:
-
使用 RTK 查询如果:
- 您已经在使用 Redux,并且想要一个集成的、简化的数据获取解决方案。
- 您需要在 Redux 中进行集中式错误处理和开发工具集成。
-
使用 React 查询 如果:
- 您想要一个更轻量级的设置,无需 Redux 依赖。
- 您更喜欢单独的服务器状态管理,不需要全局应用程序状态。
本质上,RTK Query 非常适合以 Redux 为中心的应用程序,而 React Query 为没有 Redux 的项目或那些注重本地化服务器状态管理的项目提供了灵活性和简单性。
1. 商店配置和设置
// src/store/store.js import AsyncStorage from '@react-native-async-storage/async-storage'; import { combineReducers, configureStore, isRejectedWithValue } from '@reduxjs/toolkit'; import { setupListeners } from '@reduxjs/toolkit/query'; import { FLUSH, PAUSE, PERSIST, persistReducer, PURGE, REGISTER, REHYDRATE } from 'redux-persist'; import { authApi } from '../api/authApi'; import { postsApi } from '../api/postsApi'; import { usersApi } from '../api/usersApi'; import authSlice from '../features/auth/authSlice'; const persistConfig = { key: 'root', version: 1, storage: AsyncStorage, blacklist: ['auth', postsApi.middleware, usersApi.middleware, authApi.middleware], // these reduce will not persist data (NOTE: blacklist rtk api slices so that to use tags) // whitelist: ['users'], //these reduce will persist data }; const getEnhancers = (getDefaultEnhancers) => { if (process.env.NODE_ENV === 'development') { const reactotron = require('../reactotronConfig/ReactotronConfig').default; return getDefaultEnhancers().concat(reactotron.createEnhancer()); } return getDefaultEnhancers(); }; /** * On api error this will be called */ export const rtkQueryErrorLogger = (api) => (next) => (action) => { // RTK Query uses `createAsyncThunk` from redux-toolkit under the hood, so we're able to utilize these matchers! if (isRejectedWithValue(action)) { console.log('isRejectedWithValue', action.error, action.payload); alert(JSON.stringify(action)); // This is just an example. You can replace it with your preferred method for displaying notifications. } return next(action); }; const reducer = combineReducers({ auth: authSlice, [postsApi.reducerPath]: postsApi.reducer, [usersApi.reducerPath]: usersApi.reducer, [authApi.reducerPath]: authApi.reducer, }); const persistedReducer = persistReducer(persistConfig, reducer); const store = configureStore({ reducer: persistedReducer, middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: { ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER], }, }).concat(postsApi.middleware, usersApi.middleware, authApi.middleware, rtkQueryErrorLogger), enhancers: getEnhancers, }); setupListeners(store.dispatch); export default store;
Redux Store (src/store/store.js):Redux store 是保存应用程序状态的主要结构。在您的设置中,它通过 redux-persist 进行了增强,可以在本地保存 Redux 状态的某些部分,因此即使应用程序重新启动,它们也会持续存在。
-
redux-persist:
- 用途:帮助保持部分 Redux 状态在应用程序会话中保持不变。
- 配置:persistConfig 对象指定 auth、postsApi 和 usersApi 不应被持久化(列入黑名单),这意味着它们的数据会在应用程序重新启动时重置。
- persistReducer 将减速器配置与持久化功能结合起来。
增强器:自定义增强器用于在开发模式下集成Reactotron,这是一个调试 Redux 操作、状态和网络请求的有用工具。这只在开发时激活,使调试更容易,而不影响生产。
-
中间件:
- RTK 查询中间件(postsApi.middleware、usersApi.middleware、authApi.middleware)添加自动缓存管理功能,提高数据获取效率。
- rtkQueryErrorLogger:自定义中间件在 API 调用失败时记录错误。它使用 RTK Query 的 isRejectedWithValue 函数来捕获和处理错误,允许您提醒用户有关问题或采取其他操作。
setupListeners:此功能可以在发生某些事件时自动重新获取数据,例如当应用程序重新获得焦点或从后台恢复时,为用户提供新鲜数据,而无需手动刷新。
2. RTK 查询的 API 定义
RTK Query 通过自动生成 Redux 切片、挂钩和缓存来简化 API 调用。以下是您定义的 API 的详细信息:
// src/store/store.js import AsyncStorage from '@react-native-async-storage/async-storage'; import { combineReducers, configureStore, isRejectedWithValue } from '@reduxjs/toolkit'; import { setupListeners } from '@reduxjs/toolkit/query'; import { FLUSH, PAUSE, PERSIST, persistReducer, PURGE, REGISTER, REHYDRATE } from 'redux-persist'; import { authApi } from '../api/authApi'; import { postsApi } from '../api/postsApi'; import { usersApi } from '../api/usersApi'; import authSlice from '../features/auth/authSlice'; const persistConfig = { key: 'root', version: 1, storage: AsyncStorage, blacklist: ['auth', postsApi.middleware, usersApi.middleware, authApi.middleware], // these reduce will not persist data (NOTE: blacklist rtk api slices so that to use tags) // whitelist: ['users'], //these reduce will persist data }; const getEnhancers = (getDefaultEnhancers) => { if (process.env.NODE_ENV === 'development') { const reactotron = require('../reactotronConfig/ReactotronConfig').default; return getDefaultEnhancers().concat(reactotron.createEnhancer()); } return getDefaultEnhancers(); }; /** * On api error this will be called */ export const rtkQueryErrorLogger = (api) => (next) => (action) => { // RTK Query uses `createAsyncThunk` from redux-toolkit under the hood, so we're able to utilize these matchers! if (isRejectedWithValue(action)) { console.log('isRejectedWithValue', action.error, action.payload); alert(JSON.stringify(action)); // This is just an example. You can replace it with your preferred method for displaying notifications. } return next(action); }; const reducer = combineReducers({ auth: authSlice, [postsApi.reducerPath]: postsApi.reducer, [usersApi.reducerPath]: usersApi.reducer, [authApi.reducerPath]: authApi.reducer, }); const persistedReducer = persistReducer(persistConfig, reducer); const store = configureStore({ reducer: persistedReducer, middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: { ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER], }, }).concat(postsApi.middleware, usersApi.middleware, authApi.middleware, rtkQueryErrorLogger), enhancers: getEnhancers, }); setupListeners(store.dispatch); export default store;
-
authApi (src/api/authApi.js):
- 定义登录突变,将用户凭据(例如用户名、密码)发送到服务器进行身份验证。
- onQueryStarted:登录成功后,它使用 setToken 操作将返回的令牌存储在 Redux 中。这可以实现对其他端点的安全、经过身份验证的请求。
// src/api/authApi.js import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'; import { setToken } from '../features/auth/authSlice'; export const authApi = createApi({ reducerPath: 'authApi', baseQuery: fetchBaseQuery({ baseUrl: 'https://dummyjson.com/auth/', }), endpoints: (builder) => ({ login: builder.mutation({ query: (credentials) => ({ url: 'login', method: 'POST', body: credentials, }), async onQueryStarted(arg, { dispatch, queryFulfilled }) { try { const { data } = await queryFulfilled; dispatch(setToken(data.accessToken)); // Store the token in Redux } catch (error) { console.error('Login error:', error); } }, }), }), }); export const { useLoginMutation } = authApi;
-
postsApi (src/api/postsApi.js):
-
CRUD 操作:帖子 API 包含多个与帖子交互的端点(获取、创建、更新、删除)。
- getPosts:获取分页帖子,这意味着它以较小的块(页面)检索数据,从而提高性能和加载时间。
- createPost、updatePost 和 deletePost:其中每一个都执行不同的操作(创建、更新或删除帖子)。
- 用于缓存的标签:每个端点都使用标签(例如,{ type: 'Posts', id })自动管理缓存失效和刷新。例如,创建或删除帖子会使缓存失效,从而提示 getPosts 无需手动干预即可获取新数据。
-
CRUD 操作:帖子 API 包含多个与帖子交互的端点(获取、创建、更新、删除)。
// src/store/store.js import AsyncStorage from '@react-native-async-storage/async-storage'; import { combineReducers, configureStore, isRejectedWithValue } from '@reduxjs/toolkit'; import { setupListeners } from '@reduxjs/toolkit/query'; import { FLUSH, PAUSE, PERSIST, persistReducer, PURGE, REGISTER, REHYDRATE } from 'redux-persist'; import { authApi } from '../api/authApi'; import { postsApi } from '../api/postsApi'; import { usersApi } from '../api/usersApi'; import authSlice from '../features/auth/authSlice'; const persistConfig = { key: 'root', version: 1, storage: AsyncStorage, blacklist: ['auth', postsApi.middleware, usersApi.middleware, authApi.middleware], // these reduce will not persist data (NOTE: blacklist rtk api slices so that to use tags) // whitelist: ['users'], //these reduce will persist data }; const getEnhancers = (getDefaultEnhancers) => { if (process.env.NODE_ENV === 'development') { const reactotron = require('../reactotronConfig/ReactotronConfig').default; return getDefaultEnhancers().concat(reactotron.createEnhancer()); } return getDefaultEnhancers(); }; /** * On api error this will be called */ export const rtkQueryErrorLogger = (api) => (next) => (action) => { // RTK Query uses `createAsyncThunk` from redux-toolkit under the hood, so we're able to utilize these matchers! if (isRejectedWithValue(action)) { console.log('isRejectedWithValue', action.error, action.payload); alert(JSON.stringify(action)); // This is just an example. You can replace it with your preferred method for displaying notifications. } return next(action); }; const reducer = combineReducers({ auth: authSlice, [postsApi.reducerPath]: postsApi.reducer, [usersApi.reducerPath]: usersApi.reducer, [authApi.reducerPath]: authApi.reducer, }); const persistedReducer = persistReducer(persistConfig, reducer); const store = configureStore({ reducer: persistedReducer, middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: { ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER], }, }).concat(postsApi.middleware, usersApi.middleware, authApi.middleware, rtkQueryErrorLogger), enhancers: getEnhancers, }); setupListeners(store.dispatch); export default store;
-
usersApi (src/api/usersApi.js):
- 此 API 获取经过身份验证的用户的个人资料,根据 Redux 中的令牌设置授权标头。
- Headers:prepareHeaders 动态地将令牌附加到每个请求(如果可用),从而允许安全且授权的 API 请求。
3. Auth Slice (src/features/auth/authSlice.js)
// src/api/authApi.js import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'; import { setToken } from '../features/auth/authSlice'; export const authApi = createApi({ reducerPath: 'authApi', baseQuery: fetchBaseQuery({ baseUrl: 'https://dummyjson.com/auth/', }), endpoints: (builder) => ({ login: builder.mutation({ query: (credentials) => ({ url: 'login', method: 'POST', body: credentials, }), async onQueryStarted(arg, { dispatch, queryFulfilled }) { try { const { data } = await queryFulfilled; dispatch(setToken(data.accessToken)); // Store the token in Redux } catch (error) { console.error('Login error:', error); } }, }), }), }); export const { useLoginMutation } = authApi;
- authSlice:Redux 切片管理特定的状态,在本例中为用户身份验证。
- 状态管理:authSlice 保留用户的令牌,用于访问受保护的 API 端点。
-
行动:
- setToken:在 Redux 状态中存储身份验证令牌。
- logout:从 Redux 中清除令牌,有效地将用户注销。
4. 用于调试的Reactotron (src/reactotronConfig/ReactotronConfig.js)
// src/api/postsApi.js import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'; // Define the postsApi slice with RTK Query export const postsApi = createApi({ // Unique key for the API slice in Redux state reducerPath: 'postsApi', // Configure base query settings, including the base URL for all requests baseQuery: fetchBaseQuery({ baseUrl: 'https://jsonplaceholder.typicode.com', }), // Define cache tag types for automatic cache invalidation tagTypes: ['Posts'], // Define API endpoints (queries and mutations) endpoints: (builder) => ({ // Query to fetch a paginated list of posts getPosts: builder.query({ // URL and parameters for paginated posts query: ({ page = 1, limit = 10 }) => `/posts?_page=${page}&_limit=${limit}`, // Tagging posts to automatically refresh this cache when needed providesTags: (result) => result ? [...result.map(({ id }) => ({ type: 'Posts', id })), { type: 'Posts', id: 'LIST' }] : [{ type: 'Posts', id: 'LIST' }], }), // Query to fetch a single post by its ID getPostById: builder.query({ // Define query with post ID in the URL path query: (id) => `/posts/${id}`, // Tag individual post by ID for selective cache invalidation providesTags: (result, error, id) => [{ type: 'Posts', id }], }), // Mutation to create a new post createPost: builder.mutation({ // Configure the POST request details and payload query: (newPost) => ({ url: '/posts', method: 'POST', body: newPost, }), // Invalidate all posts (paginated list) to refresh after creating a post invalidatesTags: [{ type: 'Posts', id: 'LIST' }], }), // Mutation to update an existing post by its ID updatePost: builder.mutation({ // Define the PUT request with post ID and updated data in the payload query: ({ id, ...updatedData }) => ({ url: `/posts/${id}`, method: 'PUT', body: updatedData, }), // Invalidate cache for both the updated post and the paginated list invalidatesTags: (result, error, { id }) => [ { type: 'Posts', id }, { type: 'Posts', id: 'LIST' }, ], }), // Mutation to delete a post by its ID deletePost: builder.mutation({ // Define the DELETE request with post ID in the URL path query: (id) => ({ url: `/posts/${id}`, method: 'DELETE', }), // Invalidate cache for the deleted post and the paginated list invalidatesTags: (result, error, id) => [ { type: 'Posts', id }, { type: 'Posts', id: 'LIST' }, ], }), }), }); // Export generated hooks for each endpoint to use them in components export const { useGetPostsQuery, // Use this when you want data to be fetched automatically as the component mounts or when the query parameters change. useLazyGetPostsQuery, // Use this when you need more control over when the query runs, such as in response to a user action (e.g., clicking a button), conditional fetching, or specific events. useGetPostByIdQuery, useCreatePostMutation, useUpdatePostMutation, useDeletePostMutation, } = postsApi;
- Reactotron:Reactotron 是一个调试工具,有助于跟踪 Redux 状态更改、监控 API 请求和检查日志。
- Setup:配置为捕获 console.log 输出和 Redux 操作。在开发模式下,此设置提供了一种强大的调试方法,无需添加额外的代码或改变生产性能。
5. 主要应用组件
// src/api/usersApi.js import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'; export const usersApi = createApi({ reducerPath: 'usersApi', baseQuery: fetchBaseQuery({ baseUrl: 'https://dummyjson.com', prepareHeaders: (headers, { getState }) => { // Get the token from the Redux auth state const { token } = getState().auth; // If the token exists, set it in the Authorization header if (token) { headers.set('Authorization', `Bearer ${token}`); } // Optional: include credentials if needed by the API headers.set('credentials', 'include'); return headers; }, }), endpoints: (builder) => ({ // Fetch user profile with token in Authorization header getUserProfile: builder.query({ query: () => '/auth/me', }), }), }); export const { useGetUserProfileQuery } = usersApi;
-
应用程序组件(src/App.js):
- App 组件将整个应用程序包装在 Provider(以使 Redux 可用)和 PersistGate(延迟渲染,直到检索到持久状态)中。
- PersistGate 确保在应用程序显示之前持久加载数据,减少加载时间不一致。
// src/MainApp.js 从 'react' 导入 React, { useEffect, useState }; 进口 { 活动指示器, 按钮, 平面列表, 莫代尔, 刷新控制, 样式表, 文本, 文本输入, 看法, 来自 'react-native'; 从 'react-native-safe-area-context' 导入 { SafeAreaView } ; 从 'react-redux' 导入 { useDispatch, useSelector }; 从 './api/authApi' 导入 { useLoginMutation } ; 进口 { 使用CreatePostMutation, 使用DeletePostMutation, 使用GetPosts查询, 使用LazyGetPosts查询, 使用更新后突变, 来自 './api/postsApi'; 从'./api/usersApi'导入{useGetUserProfileQuery}; 导入{注销}来自“./features/auth/authSlice”; const MainApp = () =>; { const [newPostTitle, setNewPostTitle] = useState(''); const [页面,setPage] = useState(1); const [postsData, setPostsData] = useState([]); const [刷新,setRefreshing] = useState(false); const [isModalVisible, setModalVisible] = useState(false); const 调度 = useDispatch(); const token = useSelector((state) => state.auth.token); // 登录突变 const [login, { isLoading: isLoggingIn }] = useLoginMutation(); // 当令牌可用时获取用户配置文件 const { 数据:userProfile,重新获取:refetchUserProfile } = useGetUserProfileQuery(未定义,{ 跳过:!令牌, }); // 获取分页帖子 常量{ 数据:帖子, 正在加载, 正在获取, 是错误, 重新获取, } = useGetPostsQuery({ 页数, 限制: 10 }); // 当你想在屏幕加载时获取数据时,使用 useQuery 钩子。例如,在个人资料屏幕上获取用户个人资料。 // 使用惰性查询刷新直接获取第1页 const [triggerFetchFirstPage,{ 数据:lazyData }] = useLazyGetPostsQuery(); // useLazyquery 当你想要控制 api 调用时使用,比如按钮点击。 const [createPost] = useCreatePostMutation(); const [updatePost] = useUpdatePostMutation(); const [deletePost] = useDeletePostMutation(); useEffect(() => { 如果(帖子){ setPostsData((prevData) => (页 === 1 ? posts : [...prevData, ...posts])); } }, [帖子, 页]); // 登录处理程序 const handleLogin = async () =>; { 尝试 { const 凭证 = { 用户名:'emilys',密码:'emilyspass' }; 等待登录(凭据); console.log('用户配置文件', 用户配置文件); 重新获取用户配置文件(); } 捕获(错误){ console.error('登录失败:', error); } }; const handleRefresh = async () =>; { 设置刷新(真); 设置页面(1); // 将页面重置为 1 以进行下一个滚动 setPostsData([]); // 清除数据以避免重复 // 显式触发第一页获取 const { 数据 } = 等待触发FetchFirstPage({ 页数: 1, 限制: 10 }); 如果(数据){ 设置帖子数据(数据); // 将帖子数据设置为第一页的结果 } 设置刷新(假); }; // 创建一个新帖子,将其添加到顶部,然后重新获取列表 const handleCreatePost = async () =>; { 如果(新帖子标题){ const { data: newPost } = wait createPost({ title: newPostTitle, body: '新帖子内容' }); 设置新帖子标题(''); setPostsData((prevData) => [newPost, ...prevData]); 重新获取(); } }; // 更新现有帖子并将“HASAN”添加到其标题中 const handleUpdatePost = async (post) =>; { const { 数据:updatePost } = 等待 updatePost({ id:帖子id, 标题: `${post.title} HASAN`, }); setPostsData((prevData) =>; prevData.map((item) => (item?.id === UpdatedPost?.id ?updatedPost : item)) ); }; // 删除帖子并立即将其从 UI 中删除 const handleDeletePost = async (id) =>; { 等待deletePost(id); setPostsData((prevData) => prevData.filter((post) => post.id !== id)); }; // 加载更多帖子以实现无限滚动 const loadMorePosts = () =>; { if (!isFetching) { setPage((上一页) => 上一页 1); } }; // 切换模态可见性 consttoggleModal = () =>; { setModalVisible(!isModalVisible); }; if (isLoading && page === 1) return <text>正在加载...</text>; if (isError) return <text> 获取帖子时出错。</text>; 返回 (
-
MainApp 组件 (src/MainApp.js):
- 状态和挂钩:管理本地状态(例如,用于帖子分页)和诸如 useLoginMutation 之类的挂钩来触发特定事件的操作。
-
登录:
- 使用 useLoginMutation 登录用户,然后触发 refetchUserProfile 加载用户配置文件数据。
- 条件查询:仅在存在有效令牌时才获取用户个人资料(跳过:!token),减少不必要的 API 调用。
-
获取帖子:
- 使用 useGetPostsQuery 获取分页帖子,通过在用户滚动时获取更多数据来支持无限滚动。
- 刷新控件:允许用户刷新帖子列表,对于移动设备上的下拉刷新功能很有用。
-
创建、更新、删除帖子:
- Create:调用createPost,立即更新帖子列表,新帖子位于顶部。
- 更新:更新时将“HASAN”附加到帖子标题。
- 删除:删除帖子并更新 UI,无需重新加载页面,这要归功于 deletePost 的缓存失效。
-
用户界面元素:
- 模态显示用户个人资料。仅当加载用户配置文件数据时才会出现配置文件按钮,从而增强用户体验。
- FlatList:以可滚动、分页格式显示帖子,增强可用性。
概括:
您的 React Native 应用程序使用 Redux Toolkit (RTK) 查询 来实现高效的数据管理和 API 交互。设置包括:
存储配置:带有 redux-persist 的 Redux 存储,用于跨应用程序会话保存特定数据,用于错误日志记录的自定义中间件,以及用于在开发模式下进行调试的 Reactotron。
-
带 RTK 查询的 API:
- authApi 通过登录突变处理身份验证,将令牌存储在 Redux 中。
- postsApi 为帖子提供 CRUD 操作,在添加、更新或删除帖子时使用缓存标签自动刷新数据。
- usersApi 使用基于动态令牌的授权标头获取用户配置文件。
Auth Slice:管理身份验证令牌并提供在登录/注销时设置或清除令牌的操作。
-
应用程序和主应用程序组件:
- 主应用程序将组件包装在 Provider 和 PersistGate 中,确保在渲染之前加载状态。
- MainApp 管理帖子的获取、创建、更新和删除。它有条件地加载数据(例如,仅当令牌存在时才获取用户配置文件),支持分页和无限滚动
- 使用 FlatList 作为分页帖子列表,使用模式作为个人资料,并使用基本样式来实现干净、有组织的布局。
完整代码->
以上是通过 RTK 查询在 React Native 中高效处理数据的详细内容。更多信息请关注PHP中文网其他相关文章!

JavaScript字符串替换方法详解及常见问题解答 本文将探讨两种在JavaScript中替换字符串字符的方法:在JavaScript代码内部替换和在网页HTML内部替换。 在JavaScript代码内部替换字符串 最直接的方法是使用replace()方法: str = str.replace("find","replace"); 该方法仅替换第一个匹配项。要替换所有匹配项,需使用正则表达式并添加全局标志g: str = str.replace(/fi

因此,在这里,您准备好了解所有称为Ajax的东西。但是,到底是什么? AJAX一词是指用于创建动态,交互式Web内容的一系列宽松的技术。 Ajax一词,最初由Jesse J创造

10款趣味横生的jQuery游戏插件,让您的网站更具吸引力,提升用户粘性!虽然Flash仍然是开发休闲网页游戏的最佳软件,但jQuery也能创造出令人惊喜的效果,虽然无法与纯动作Flash游戏媲美,但在某些情况下,您也能在浏览器中获得意想不到的乐趣。 jQuery井字棋游戏 游戏编程的“Hello world”,现在有了jQuery版本。 源码 jQuery疯狂填词游戏 这是一个填空游戏,由于不知道单词的上下文,可能会产生一些古怪的结果。 源码 jQuery扫雷游戏

本教程演示了如何使用jQuery创建迷人的视差背景效果。 我们将构建一个带有分层图像的标题横幅,从而创造出令人惊叹的视觉深度。 更新的插件可与JQuery 1.6.4及更高版本一起使用。 下载

本文讨论了在浏览器中优化JavaScript性能的策略,重点是减少执行时间并最大程度地减少对页面负载速度的影响。

Matter.js是一个用JavaScript编写的2D刚体物理引擎。此库可以帮助您轻松地在浏览器中模拟2D物理。它提供了许多功能,例如创建刚体并为其分配质量、面积或密度等物理属性的能力。您还可以模拟不同类型的碰撞和力,例如重力摩擦力。 Matter.js支持所有主流浏览器。此外,它也适用于移动设备,因为它可以检测触摸并具有响应能力。所有这些功能都使其值得您投入时间学习如何使用该引擎,因为这样您就可以轻松创建基于物理的2D游戏或模拟。在本教程中,我将介绍此库的基础知识,包括其安装和用法,并提供一

本文演示了如何使用jQuery和ajax自动每5秒自动刷新DIV的内容。 该示例从RSS提要中获取并显示了最新的博客文章以及最后的刷新时间戳。 加载图像是选择


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

EditPlus 中文破解版
体积小,语法高亮,不支持代码提示功能

ZendStudio 13.5.1 Mac
功能强大的PHP集成开发环境

VSCode Windows 64位 下载
微软推出的免费、功能强大的一款IDE编辑器

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

Dreamweaver Mac版
视觉化网页开发工具