HuiWang111 / offline-request

compatibility offline and online request tools

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

offline-request

install

npm i offline-request

yarn add offline-request

usage

这里主要演示离线时的代码,非离线时就是正常使用axios发送请求

  • 创建一个 OfflineRequest 实例
// utils
import OfflineRequest, { NetWork } from 'offline-request'
import Axios from 'axios'

const network = new NetWork()
export const offlineRequest = new OfflineRequest(
    Axios.create(),
    () => network.isOnline // 如果不根据网络状态判断则不需要使用NetWork类,只需要保证改该函返回boolean即可
);
  • client端页面逻辑
import { FC, useState, ChangeEvent, useRef, useEffect } from 'react'
import { Button, Row, Col, Input } from 'antd'
import { useAppContext } from 'hooks'
import { observer } from 'mobx-react-lite'
import { offlineRequest } from 'utils'

export const Dashboard: FC = observer(() => {
    const [text, setText] = useState('');
    const [list, setList] = useState<string[]>([]);
    const { api, store } = useAppContext()
    const handleLogout = () => {
        api.auth.logout(store.auth)
    }

    const handleInputChange = (e: ChangeEvent<HTMLInputElement>) => {
        setText(e.target.value);
    }

    const handlePressEnter = () => {
        setList([...list, text]);
        setText('');

        offlineRequest.put('/todos', { text }); // 发送离线请求,添加一条数据
    }

    useEffect(() => {
        const fetchTodos = async () => {
            const res = await offlineRequest.get('/todos/query?a=1&b=2'); // 从indexdb中获取数据
            setList(res!.data.map((item: { text: string }) => item.text));
        }

        fetchTodos();
    }, []);

    return (
        <>
            <Row align='middle' justify='center' style={{ height: 300 }}>
                <Col>
                    <Button type='primary' onClick={handleLogout}>退出登录</Button>
                </Col>
            </Row>
            <Row align='middle' justify='center'>
                <Col>
                    <Input
                        value={text}
                        onChange={handleInputChange}
                        onPressEnter={handlePressEnter}
                    />
                </Col>
            </Row>
            {
                list.map((item, index) => {
                    return (
                        <Row key={index} align='middle' justify='center'>
                            <Col>{item}</Col>
                        </Row>
                    );
                })
            }
        </>
    )
})
  • server端离线服务逻辑
// server/index.ts
import Dexie from 'dexie';
import { offlineRequest } from 'utils';
import { todoController } from './todos';

export const runServer = () => {
    const db = new Dexie("test_database");

    todoController(db, offlineRequest.server);
}
// server/todos.ts
import Dexie from 'dexie';
import { OfflineRequestServer } from 'offline-request';

export const todoController = (db: Dexie, router: OfflineRequestServer) => {
    db.version(1).stores({
        todos: 'text'
    });
    
    router.get('/todos/query', async (ctx) => {
        const { query } = ctx.request;
        console.log(query, 'query'); // {a: '1', b: '2'}

        const todos = await db.table('todos').toArray();
        
        ctx.body = {
            data: todos,
            status: 200,
            statusText: 'ok'
        };
    });

    router.put('/todos', async (ctx) => {
        const { data } = ctx.request;

        await db.table('todos').add(data);

        ctx.body = {
            data: 'ok',
            status: 200,
            statusText: 'ok'
        };
    });
}

此时服务和页面相关的逻辑都完成了,还差最后一步,将服务运行起来等待client发起请求

// index.tsx项目入口文件
import ReactDOM from 'react-dom'
import App from 'app/App'
import { Router } from 'react-router-dom'
import { history } from 'app/history'
import { runServer } from 'server'
import 'styles/index.less'

const init = () => {
    ReactDOM.render(
        <Router history={history}>
            <App />
        </Router>,
        document.getElementById('root')
    )
}

init()

runServer() // 将离线服务跑起来

OfflineRequest模块

const offlineRequest = new OfflineRequest(httpClient: AxiosInstance, isOnline: () => boolean);
  • 参数

    • httpClient
      • Axios实例,理论上只要有get/post/patch/put/delete等方法的对象都可以
    • isOnline
      • 用于判断使用http请求,还是使用前端消息通信请求
      • 前端消息通信请求使用发布订阅模式模拟http网络请求,使得前端代码与操作indexDB的逻辑可以拆分开来,避免耦合
  • offlineRequest.isOnline() 手动调用一次用于判断当前是使用离线还是在线请求。 注意:大部分情况下不需要调用这个方法,get/post/patch/put/delete等方法中默认会调用该方法判断走http请求还是走前端消息通信请求

  • 请求方法 会自动调用 isOnline() 方法判断需要走http请求还是走前端消息通信请求。

    • isOnline() 结果为 true,则使用axios发送网络请求
    • isOnline() 结果为 false,则调用写好的离线服务,操作indexDB
offlineRequest.get(url: string, config?: AxiosRequestConfig): Promise<AxiosResponse | undefined>;

offlineRequest.delete(url: string, config?: AxiosRequestConfig): Promise<AxiosResponse | undefined>;

offlineRequest.post(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<AxiosResponse | undefined>;

offlineRequest.put(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<AxiosResponse | undefined>;

offlineRequest.patch(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<AxiosResponse | undefined>;

Network模块

用于判断当前网络状态

const network = new Network(pollingConfig?: PollingConfig)

// network.isOnline为true时网络状态正常
// network.isOnline为false时表示当前无网络
  • pollingConfig 轮询检查网络状态的配置
export interface PollingConfig {
    enabled?: boolean; // 是否开启轮询检查网络,如果不开启,只会监听 'online' 和 'offline'事件来更新网络状态,默认开启
    url?: string; // 轮询请求地址,默认请求地址为https://httpbin.org/get
    timeout?: number; // xhr.timeout 参考https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest/timeout
    interval?: number; // 轮询间隔时间
}
  • network.startPolling() 手动开始轮询。 注意:当pollingConfig.enabled不为false时(默认就是true),创建 network 实例的时候就会开始轮询,不需要调用 startPolling() 手动开始

  • network.stopPolling() 手动停止轮询。

  • network.isOnline 布尔值,表示当前有网还是无网。

  • network.ping() 手动调用一次请求测试网络网络状态。 轮询的实现就是每隔一段时间调用一次该方法。

const isOnline = await network.ping();

Context实例

离线服务路由回调函数拿到的对象。

router.get('/todos/query', async (ctx) => {
    const { query } = ctx.request;
    console.log(query, 'query');

    const todos = await db.table('todos').toArray();
    
    ctx.body = {
        data: todos,
        status: 200,
        statusText: 'ok'
    };
});
  • ctx.body set属性。当执行 ctx.body = ... 时,就会将等号后面的数据直接返回给前端。(仿照的Koa)

  • ctx.request 前端传递过来的一些参数。

    1. ctx.request.pathname 请求的路由,如:/todos/query

    2. ctx.request.method 请求方法。

    3. ctx.request.query 请求url附带的查询参数(已转化为对象)。

    offlineRequest.get('/todos/query?a=1&b=2');
    
    router.get('/todos/query', async (ctx) => {
        const { query } = ctx.request;
        // 此时query就是 {a: "1", b: "2"}
    });
    1. ctx.request.qs 请求url附带的查询参数(字符串)。
    offlineRequest.get('/todos/query?a=1&b=2');
    
    router.get('/todos/query', async (ctx) => {
        const { qs } = ctx.request;
        // 此时qs就是 "a=1&b=2"
    });
    1. ctx.request.data 请求传递的数据。
    const text = '123'
    offlineRequest.put('/todos', { text });
    
    router.put('/todos', async (ctx) => {
        const { data } = ctx.request;
        // 此时data就是 { text: '123' }
    });
    1. ctx.request.config 请求相关的配置。参考类型AxiosRequestConfig

About

compatibility offline and online request tools


Languages

Language:TypeScript 87.1%Language:JavaScript 12.9%