redux-saga

news2025/1/16 20:57:02

redux-saga

官网:About | Redux-Saga
中文网:自述 · Redux-Saga


redux-saga 是一个用于管理 异步获取数据(副作用) 的redux中间件;它的目标是让副作用管理更容易,执行更高效,测试更简单,处理故障时更容易…

学习 redux-saga 之前,需要先掌握 ES6 中的 Iterator迭代器 和 Generator生成器 !!

1. redux-thunk与redux-saga的比较 

redux中的数据流
action ———> reducer ———> state

  • action是一个纯粹对象(plain object)
  • reducer是一个纯函数(和外界没有任何关系)
  • 都只能处理同步的操作

redux-thunk中间件的处理流程
action1 ———> middleware ———> action2 ———> reducer ———> state

/* redux-thunk中间件的部分源码 */
'use strict';
function createThunkMiddleware(extraArgument) {
    var middleware = function middleware(_ref) {
      var dispatch = _ref.dispatch,
          getState = _ref.getState;
      return function (next) {
        return function (action) {
          if (typeof action === 'function') {
            // 如果返回的action是个函数,则把函数执行「在函数中完成异步操作,用传递的dispatch单独实现派发!!」
            return action(dispatch, getState, extraArgument);
          }
          return next(action);
        };
      };
    };
    return middleware;
}

弊端:异步操作分散到每一个action中;而且返回函数中的代码具备多样性!!

redux-saga中间件的工作流程
redux-saga中提供了一系列的api,可以去监听纯粹对象格式的action,方便单元测试!!
action1 ———> redux-saga监听 ———> 执行提供的API方法 ———> 返回描述对象 ———> 执行异步操作 ———> action2 ———> reducer ———> state

这样说完,大家可能是不太理解的,那么接下来,我们去做一个案例,详细解读一下redux-saga的语法和优势!!

常用的中间件

  • redux-logger 输出派发日志(开发环境下)
  • redux-thunk / redux-promise 实现异步派发
  • redux-saga 实现异步派发的 管理的
  • redux-persist 实现公共状态持久化存储 [ 实施同步数据到 localStorage中;当页面刷新或者重新打开的时候,会把存储的信息放到 redux 容器中一份 ]

2. redux-saga的基础知识

备注:需要先准备一套基于 “redux、redux-thunk” 实现的投票案例,我们在此基础上去修改 !!

安装中间件
$ npm install redux-saga
$ yarn add redux-saga

使用中间件
store/index.js

  1. 创建 sagaMiddleware
  2. 插件中引入 sagaMiddleware
  3. 启动saga ( 需要创建一个saga文件 )
import { createStore, applyMiddleware } from 'redux';
import createSagaMiddleware from 'redux-saga';
import reducer from './reducer';
import saga from './saga';
// saga
const sagaMiddleware = createSagaMiddleware();
// 创建store容器
const store = createStore(
    reducer,
    applyMiddleware(sagaMiddleware)
);
// 启动saga
sagaMiddleware.run(saga);
export default store;

saga中间件原理:

 saga.js:

import {
    take, takeLatest, throttle, debounce,
    call, apply, fork, delay, put, select, all, cancel
} from 'redux-saga/effects';
import * as TYPES from './action-types';

/* 工作区域 */
const workingCount = function* workingCount(action) {
    yield delay(2000);
    yield put({
        type: TYPES.DEMO_COUNT,
        payload: action.payload
    });
};
const workingSupport = function* workingSupport() {
    yield delay(1000);
    yield put({
        type: TYPES.VOTE_SUP
    });
};
const workingOppose = function* workingOppose() {
    yield delay(1000);
    yield put({
        type: TYPES.VOTE_OPP
    });
};

/* 创建监听器,监听派发的异步任务 */
const saga = function* saga() {
    /* while (true) {
        let action = yield take(`${TYPES.DEMO_COUNT}@SAGA@`);
        yield fork(workingCount, action);
        yield fork(workingSupport, action);
        yield fork(workingOppose, action);
    } */
    yield takeLatest(`${TYPES.DEMO_COUNT}@SAGA@`, workingCount);
    yield takeLatest(`${TYPES.VOTE_SUP}@SAGA@`, workingSupport);
    yield takeLatest(`${TYPES.VOTE_OPP}@SAGA@`, workingOppose);
};
export default saga;

store/action-types.js

export const VOTE_SUP = "VOTE_SUP";
export const VOTE_OPP = "VOTE_OPP";

export const DEMO = "DEMO";

 store/reducer

// demoReducer
import * as TYPES from '../action-types';
import _ from '../../assets/utils';
let initial = {
    num: 0
};
export default function demoReducer(state = initial, action) {
    state = _.clone(state);
    let { payload = 1 } = action;
    switch (action.type) {
        case TYPES.DEMO:
            state.num += payload;
            break;
        default:
    }
    return state;
};

// index.js
import { combineReducers } from 'redux';
import voteReducer from './voteReducer';
import demoReducer from './demoReducer';
const reducer = combineReducers({
    vote: voteReducer,
    demo: demoReducer
});
export default reducer;

Demo.jsx组件中

import React from "react";
import { Button } from 'antd';
import { useSelector, useDispatch } from 'react-redux';
const Demo = function Demo() {
    const { num } = useSelector(state => state.demo),
        dispatch = useDispatch();
    return <div>
        <span style={{ fontSize: 20, paddingLeft: 10 }}>
            {num}
        </span>
        <br />
        <Button type="primary"
            onClick={() => {
                //基于dispatch进行派发....
            }}>
            按钮
        </Button>
    </div>;
};
export default Demo;

 saga.js 工作流程

第一部分:创建监听器「基于saga的辅助函数」

  • take(pattern)

  • takeEvery(pattern, saga, …args)

  • takeLatest(pattern, saga, ..args)

  • throttle(ms, pattern, saga, ..args)

第二部分:创建执行函数「基于Effect创建器(API)」

  • put(action)

  • call(fn, …args)

  • fork(fn, …args)

  • select(selector, …args)

每一次组件派发后发生的事情
每一次在组件中,基于 dispatch(action) 的时候:

  • 首先会通知 reducer 执行

  • 然后再去通知 saga 中的监听器执行

 

 

关于监听器创建的细节
组件中

<Button type="primary"
    onClick={() => {
        dispatch({
            type: "DEMO-SAGA",
            payload: 10
        });
    }}>
    按钮
</Button>

 saga.js -> take 函数的运用

import * as TYPES from './action-types';
const working = function* working(action) {
    // 等价于 dispatch派发:通知reducer执行
    yield put({
        type: TYPES.DEMO,
        payload: action.payload
    });
};
export default function* saga() {
    /* // 创建监听器,当监听到派发后,才会继续向下执行
    // 特征:只能监听一次
    // action:可以获取派发时传递的action
    let action = yield take("DEMO-SAGA");
    yield working(action); */

    // 可基于循环,创建无限监听机制
    while (true) {
        let action = yield take("DEMO-SAGA");
        yield working(action);
    }
};

saga.js -> 其它监听器辅助函数的运用

const working = function* working(action) {
    console.log('AAA');
    // 设置延迟函数:等待2000ms后,才会继续向下执行!!
    yield delay(2000);
    yield put({
        type: TYPES.DEMO,
        payload: action.payload
    });
};
export default function* saga() {
    /* // 派发后,立即通知异步的working执行;
    // 但是在working没有处理完毕之前,所有其他的派发任务都不在处理!!
    while (true) {
        let action = yield take("DEMO-SAGA");
        yield working(action);
    } */

    /* // 每一次派发任务都会被执行
    yield takeEvery("DEMO-SAGA", working); */

    /* // 每一次派发任务都会被执行,但是会把之前没有处理完毕的干掉
    yield takeLatest("DEMO-SAGA", working); */

    /* // 每一次派发的任务会做节流处理;在频繁触发的操作中,1000ms内,只会处理一次派发任务
    yield throttle(1000, "DEMO-SAGA", working); */

    /* // 每一次派发的任务会做防抖处理;在频繁触发的操作中,只识别最后一次派发任务进行处理
    yield debounce(1000, "DEMO-SAGA", working); */
};

saga.js -> yield call/select…

import * as TYPES from './action-types';
import http from '../api/http';

const working = function* working() {
    // 获取目前的公共状态信息
    let { num } = yield select(state => state.demo);

    // 从服务器获取数据
    // let result = yield apply(null, http.get, ['/api/news/latest']);
    let result = yield call(http.get, '/api/news/latest');
    console.log(result); //从服务器获取的数据

    yield put({
        type: TYPES.DEMO
    });
};
export default function* saga() {
    yield takeLatest("DEMO-SAGA", working);
};

saga.js -> yield fork

const query1 = function* query1() {
    console.log(1);
    yield delay(2000);
};
const query2 = function* query2() {
    console.log(2);
    yield delay(2000);
};
const working = function* working() {
    /* // 串行
    yield call(query1);
    yield call(query2); */

    /* // 并行:无阻塞调用
    yield fork(query1);
    yield fork(query2); */

    console.log(3);
};
export default function* saga() {
    yield takeLatest("DEMO-SAGA", working);
};

3. 基于redux-saga重写Vote案例

组件

import { useSelector, useDispatch } from 'react-redux';
import * as TYPES from '../store/action-types';
...
const Vote = function Vote() {
    const { supNum, oppNum } = useSelector(state => state.vote),
        dispatch = useDispatch();
    return <VoteBox>
        ...
        <div className="footer">
            <Button type="primary"
                onClick={() => {
                    dispatch({
                        type: TYPES.VOTE_SUP
                    });
                }}>
                支持
            </Button>
            <Button type="primary"
                onClick={() => {
                    dispatch({
                        type: "VOTE-SUP-SAGA"
                    });
                }}>
                异步支持
            </Button>

            <Button type="primary" danger
                onClick={() => {
                    dispatch({
                        type: TYPES.VOTE_OPP
                    });
                }}>
                反对
            </Button>
            <Button type="primary" danger
                onClick={() => {
                    dispatch({
                        type: "VOTE-OPP-SAGA"
                    });
                }}>
                反对异步
            </Button>
        </div>
    </VoteBox>;
};
export default Vote;

saga.js

import { takeLatest, put, delay } from 'redux-saga/effects';
import * as TYPES from './action-types';

const voteSupWorking = function* voteSupWorking() {
    yield delay(2000);
    yield put({
        type: TYPES.VOTE_SUP
    });
};

const voteOppWorking = function* voteOppWorking() {
    yield delay(2000);
    yield put({
        type: TYPES.VOTE_OPP
    });
};

export default function* saga() {
    yield takeLatest("VOTE-SUP-SAGA", voteSupWorking);
    yield takeLatest("VOTE-OPP-SAGA", voteOppWorking);
};

redux-saga 常用API总结

1. 在组件中基于dispatch派发的时候,派发的action对象中的type属性「派发的行为标识」,它的命名上需要注意一个细节!!
   因为:每一次派发,一定会把reducer执行一遍,再去saga中间中,判断此任务是否被监听...
   如果打算进行“同步派发”:
      则我们派发的行为标识需要和reducer中做判断的行为标识保持一致!!
      并且在saga中,不要再对这个标识进行监听了!!
      这样的标识,我们可以在 store/action-types 中进行统一管理!!
   如果打算进行“异步派发”:
      我们派发的标识,“一定不能”和reducer中做判断的标识一样!!
      需要saga中对这个标识进行监听!监听到派发后,进行异步的操作处理!!
      我们可以在正常标识的后面加“@SAGA@”「规范:我自己定义的」
      当异步操作结束,我们基于 yield put 进行派发的时候,设置的派发标识,要和reducer中做判断的标识一样!!

2. yield take(异步标识):创建监听器,监听派发指定标识的异步任务
   + 单纯这样处理,只会被监听一次,我们特殊处理一下
   while (true) {
      let action = yield take(异步标识);
      yield workingCount(action);
   }

3. yield takeEvery(异步标识,要执行的方法)
   + 等价于上述基于while(true)的操作!!
   + 本身就可以实现一直监听的操作!!被监到后,把传递进来的函数执行!!
   yield takeEvery(异步标识, workingCount);

   yield takeLatest(异步标识,working)
   + 和takeEvery一样,每一次异步派发都会被监测到,都会把working执行
   + 只不过,在执行working之前,会把正在运行的操作都结束掉,只保留当前最新的「也就是最后一次」
   + 对异步派发任务的防抖处理「结束边界」

   yield throttle(ms, 异步标识, working);
   + 对异步派发进行节流处理:组件中频繁进行派发操作,我们控制一定的触发频率「依然会触发多次,只不过做了降频」
   + 它不是对执行的方法做节流,而是对异步任务的监测做节流:第一次异步任务被监测到派发后,下一次监测到,需要过“ms”这么长时间!!

   yield debounce(ms, 异步标识, working);
   + 和takeLatest一样,也是做防抖处理「只识别一次」
   + 但是原理和takeLatest是不一样的,和throttle类似:它是对异步任务的监测做防抖处理,在指定的“ms”时间内,我们触发多次,任务也只能被监测到一次「监测最后一次」,把working执行一次!!

4. working工作区中使用的EffectsAPI
   yield delay(ms) 设置延迟操作「和我们之前自己写的delay延迟函数类型」,只有延迟时间到达后,其下面的代码才会继续执行!!

   yield put(action) 派发任务到reducer,等价于dispatch

   let { ... } = yield select(mapState)
     + 基于mapState函数,返回需要使用的公共状态
     + yield处理后的结果,就是返回的公共状态,我们可以解构赋值
     let { num } = yield select(state => state.demo);

   let result = yield call(方法, 实参1, 实参2, ...)
     + 基于call方法,可以把指定的函数执行,把实参一项项的传递给方法
     + 真实项目中,我们一般基于call方法,实现从服务器获取数据
     + result就是异步调取接口成功,从服务器获取的信息
     + ...
   let result = yield apply(this, 方法, [实参1, 实参2, ...]);

   yield fork(方法, 实参1, 实参2, ...)
     + 以 非阻塞调用 的形式执行方法

 

模拟接口

import {
    take, takeEvery, takeLatest, throttle, debounce,
    call, apply, fork, delay, put, select, all
} from 'redux-saga/effects';
import * as TYPES from './action-types';

/* 模拟了两个接口 */
const api = {
    queryData(id, name) {
        return new Promise(resolve => {
            setTimeout(() => {
                let result = {
                    code: 0,
                    data: [10, 20, 30, 40]
                };
                resolve(result);
            }, 2000);
        });
    },
    queryBanner() {
        return new Promise(resolve => {
            setTimeout(() => {
                let result = {
                    code: 0,
                    data: '轮播图数据'
                };
                resolve(result);
            }, 1000);
        });
    }
};

/* 创建执行函数,在任务被监听后,去做异步操作「Generator函数」 */
const workingCount = function* workingCount(action) {
    /* 
    // let { num } = yield select(state => state.demo);
    yield delay(2000);
    // let { code, data } = yield call(api.queryData, 108, '珠峰');
    // let { code, data } = yield apply(null, api.queryData, [108, '珠峰']);
    yield put({
        type: TYPES.DEMO_COUNT,
        payload: action.payload
    }); 
    */

    /* 
    // 基于yield call处理,实现的是标准的串行效果:上一个请求成功,才会发送下一个请求
    let { data } = yield call(api.queryData, 100, '珠峰培训');
    console.log('第一个请求成功:', data);
    let { data: data2 } = yield call(api.queryBanner);
    console.log('第二个请求成功:', data2); 
    */

    /* 
    // 如果想实现并行效果,则基于yield all处理:等待所有请求都成功,再向下继续执行
    let { home, banner } = yield all({
        home: call(api.queryData, 100, '珠峰培训'),
        banner: call(api.queryBanner)
    });
    console.log(home, banner); //分别获取了两个请求成功的结果 
    */
};

/* 创建监听器,监听派发的任务「Generator函数」 */
const saga = function* saga() {
    yield takeLatest(`${TYPES.DEMO_COUNT}@SAGA@`, workingCount);
};
export default saga;

 saga

import {
    take, takeLatest, throttle, debounce,
    call, apply, fork, delay, put, select, all, cancel
} from 'redux-saga/effects';
import * as TYPES from './action-types';

/* 工作区域 */
const workingCount = function* workingCount(action) {
    yield delay(2000);
    yield put({
        type: TYPES.DEMO_COUNT,
        payload: action.payload
    });
};
const workingSupport = function* workingSupport() {
    yield delay(1000);
    yield put({
        type: TYPES.VOTE_SUP
    });
};
const workingOppose = function* workingOppose() {
    yield delay(1000);
    yield put({
        type: TYPES.VOTE_OPP
    });
};

/* 创建监听器,监听派发的异步任务 */
const saga = function* saga() {
    /* while (true) {
        let action = yield take(`${TYPES.DEMO_COUNT}@SAGA@`);
        yield fork(workingCount, action);
        yield fork(workingSupport, action);
        yield fork(workingOppose, action);
    } */
    yield takeLatest(`${TYPES.DEMO_COUNT}@SAGA@`, workingCount);
    yield takeLatest(`${TYPES.VOTE_SUP}@SAGA@`, workingSupport);
    yield takeLatest(`${TYPES.VOTE_OPP}@SAGA@`, workingOppose);
};
export default saga;

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

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

相关文章

C#:Krypton控件使用方法详解(第十讲) ——kryptonColorButton

今天介绍的Krypton控件中的kryptonColorButton&#xff0c;下面介绍这个控件的外观属性&#xff1a;Cursor属性&#xff1a;表示鼠标移动过该控件的时候&#xff0c;鼠标显示的形状。属性值如下图所示&#xff1a;EmptyBorderColor属性&#xff1a;表示当所选颜色为空时&#x…

七、JUC并发工具

文章目录JUC并发工具CountDownLatch应用&源码分析CountDownLatch介绍CountDownLatch应用CountDownLatch源码分析有参构造await方法countDown方法CyclicBarrier应用&源码分析CyclicBarrier介绍CyclicBarrier应用CyclicBarrier源码分析CyclicBarrier的核心属性CyclicBarr…

echarts实现知识图谱,生产项目

echarts实现知识图谱&#xff0c;生产项目内容简介效果演示代码逻辑结束语内容简介 在实际生产项目中&#xff0c;需要对后端知识数据进行展示。需求如下&#xff1a; 点击节点可以展示与此节点相关的节点信息右键点击节点可以对节点的信息进行修改悬浮在节点上可以查看节点的…

通用人工智能(AGI):人工智能的下一个阶段

除了人工智能(AI)的改进和新应用之外&#xff0c;大多数人都认为&#xff0c;当通用人工智能(AGI)出现时&#xff0c;人工智能的下一次飞跃将发生。我们将AGI宽泛地定义为机器或计算机程序理解或学习人类可以完成的任何智力任务的假设能力。然而&#xff0c;对于何时以及如何实…

系统性能测试指标

性能测试的目的 1.评估系统的能力&#xff0c;测试中得到的负荷和响应时间数据可以被用于验证所计划的模型的能力&#xff0c;并帮助作出决策。 2.识别体系中的弱点&#xff1a;受控的负荷可以被增加到一个极端的水平&#xff0c;并突破它&#xff0c;从而修复体系的瓶颈或薄…

leetcode: Swapping Nodes in a Linked List

leetcode: Swapping Nodes in a Linked List1. 题目描述2. 题目解答3. 总结1. 题目描述 You are given the head of a linked list, and an integer k.Return the head of the linked list after swapping the values of the kth node from the beginning and the kth node f…

ECMAScript 详解

ECMAScript 历史我们首先来看 ECMA 是什么。ECMA&#xff0c;是欧洲计算机制造商协会&#xff08;European Computer Manufacturers Association&#xff09;的简称&#xff0c;是一家国际性会员制度的信息和电信标准组织。1994 年之后&#xff0c;由于组织的标准牵涉到很多其他…

【Leetcode 剑指Offer】第 6 天 搜索与回溯算法(简单)

搜索与回溯剑指 Offer 32 - I. 从上到下打印二叉树层序遍历 广搜 BFScollections双端队列 deque剑指 Offer 32 - II. 从上到下打印二叉树 II剑指 Offer 32 - III. 从上到下打印二叉树 III剑指 Offer 32 - I. 从上到下打印二叉树 题;从上到下打印出二叉树的每个节点&#xff0c…

单通道说话人语音分离——DPRNN(Dual-Path Recurrent Neural Network)

参考文献&#xff1a;《DUAL-PATH RNN: EFFICIENT LONG SEQUENCE MODELING FOR TIME-DOMAIN SINGLE-CHANNEL SPEECH SEPARATION》 DPRNN网络是Con-Tasnet的改进网络 Con-Tasnet介绍详情请看上一篇文章 单通道说话人语音分离——Conv-TasNet(Convolutional Time-domain audio…

应用程序性能优化方案,web服务五级缓存优化,服务器性能优化...

winfrom 全局异常捕获WPF 全局异常捕获Asp.Net全局异常捕获MVC 全局异常捕获AspNetNetCore 全局异常捕获一级缓存html/css/js 前端缓存二级缓存Asp.Net MVC AspNetCore 客户端缓存设置三级缓存服务端缓存四级redis 数据库缓存服务端缓存五级sqlserver 数据库缓存设置分布式缓存…

2023年湖北建设厅资料员证怎么考需要什么条件?启程别

2023年湖北建设厅资料员证怎么考需要什么条件&#xff1f;启程别 湖北资料员目前已经开始组织考试了&#xff0c;各位想要考资料员的现在可以开始准备报考相关准备工作了。资料员怎么考需要什么条件&#xff1f;启程别告诉你 建设厅资料员证怎么考 网报&#xff1a;七大员全国…

ARM学习(十八)jinkozone调试问题总结

笔者来聊聊ARM的函数的调用规则 1、ARM函数调用规则介绍首先介绍几个术语&#xff0c; AAPCS&#xff1a;Procedure Call Standard for the ARM Architecture APCS&#xff1a;ARM Procedure Call Standard TPCS&#xff1a;Thumb Procedure Call Standard ATPCS&#xff1a;AR…

5.3 redis分布式锁【Java 面试第三季】

redis分布式锁【Java 面试第三季】前言推荐redis分布式锁概览47_redis分布式锁前情说明48_boot整合redis搭建超卖程序-上49_boot整合redis搭建超卖程序-下50_redis分布式锁0151_redis分布式锁0252_redis分布式锁0353_redis分布式锁0454_redis分布式锁0555_redis分布式锁0656_re…

【Java|golang】2363. 合并相似的物品---桶排序

给你两个二维整数数组 items1 和 items2 &#xff0c;表示两个物品集合。每个数组 items 有以下特质&#xff1a; items[i] [valuei, weighti] 其中 valuei 表示第 i 件物品的 价值 &#xff0c;weighti 表示第 i 件物品的 重量 。 items 中每件物品的价值都是 唯一的 。 请你…

报名投票链接怎么做做一个投票的链接怎么做微信投票链接怎么做

近些年来&#xff0c;第三方的微信投票制作平台如雨后春笋般络绎不绝。随着手机的互联网的发展及微信开放平台各项基于手机能力的开放&#xff0c;更多人选择微信投票小程序平台&#xff0c;因为它有非常大的优势。1.它比起微信公众号自带的投票系统、传统的H5投票系统有可以图…

案例|政务大数据平台数据安全建设实践

《关于加强数字政府建设的指导意见》、《全国一体化政务大数据体系建设指南》&#xff0c;对全面开创数字政府建设新局面作出部署&#xff0c;保障数据安全&#xff0c;提升数字政府基础设施的支撑能力&#xff0c;也明确成为数字政府建设探索与实践中的重点任务。那么&#xf…

王道计算机网络课代表 - 考研计算机 第五章 传输层 究极精华总结笔记

本篇博客是考研期间学习王道课程 传送门 的笔记&#xff0c;以及一整年里对 计算机网络 知识点的理解的总结。希望对新一届的计算机考研人提供帮助&#xff01;&#xff01;&#xff01; 关于对 “传输层” 章节知识点总结的十分全面&#xff0c;涵括了《计算机网络》课程里的全…

klee内存模型

klee内存模型一.LLVM基础二.Klee中相关的类2.1.基础类2.2.内存管理相关类三.示例3.1.示例13.2.示例23.3.示例33.4.示例4这篇blog主要通过一些简单的示例来了解以下klee对内存的建模方式。 首先一个C语言程序在运行时&#xff0c;内存主要包括&#xff1a; 代码段&#xff0c;程…

如何从零到一的设计一套轻易云数据集成平台这样的系统架构

一个集成平台的架构设计需要考虑多个方面&#xff0c;包括系统架构、技术选型、数据存储、安全设计等。下面是参考轻易云数据集成平台的架构设计思路&#xff1a;系统架构首先需要确定系统的整体架构&#xff0c;这包括前后端分离、微服务架构、容器化部署等。根据需求和规模的…

老字号白酒企业——金徽酒借力泛微,升级门户,实现统一办公

金徽酒股份有限公司前身系康庆坊、万盛魁等多个徽酒老作坊基础上组建的省属国营大型白酒企业&#xff0c;曾用名甘肃陇南春酒厂&#xff0c;是国内建厂最早的中华老字号白酒酿造企业之一。2016年3月10日&#xff0c;金徽酒在上海证券交易所挂牌上市。 &#xff08;图片素材来自…