✨
blog
  • Blog
  • Element-UI
    • 2019-09-04
  • JS
    • ES6 之 Set 和 Map
    • let 和 const 声明常见概念
    • 元编程
    • ES6之字符串的扩展
    • ES6 之异步流程的前世今生(上)
    • ES6之异步流程的前世今生(下)
    • ES6 之模块你知吗
    • ES6 之解构赋值与箭头函数的妙用
    • 迭代器
    • ES5 之原型(一)
    • ES6之类(二)
    • es7之装饰器
    • es6之数组详解
    • js之this指向
    • 对象
    • vue项目配合使用canvas联动
    • 本文解决痛点:对象里面是否有值
  • MAC
    • vue源码之method
    • Mac的使用技巧
    • 前文
    • Mac常用软件(二)
    • 如何查看 Mac 端口号以及占用情况
  • Node
    • Node之Buffer详解
    • 浏览器与 node 的事件循环(event loop)有何区别
    • Node之多线程
    • node之模块解析(一)
    • 错误捕获与内存告警
  • TS
    • Record
    • 使用方法
    • 工具泛型
    • 类型体操
    • 泛型
  • chrome
    • v8 引擎
    • v8 垃圾回收机制
    • 浏览器的知识
  • flutter
    • 路由
    • 页面布局
  • go
    • index
  • html&css
    • 两栏布局
    • ES5和ES6的区别
    • ES5 和 ES6 的区别
    • HTTP详解
    • TCP 与 UDP 的区别
    • MDN
    • css modules 使用教程
    • css 居中
    • 拖拽
    • flex布局
    • h5 新增特性 html5
    • history 与 hash 路由策略
    • position 定位方式
    • rem布局
    • svg
    • web性能优化
    • 事件循环
    • 从输入网址后发生了什么
    • 前端状态管理
    • 圣杯布局与双飞翼
    • 性能优化 页面的性能统计指标
    • 本地存储的几种对比
    • 浏览器的渲染进程
    • 浏览器缓存策略详解
    • 盒模型
    • 为什么要移动端适配
    • 跨域的 N 种实现方式
  • web3
    • 常见概念
    • vue项目配合使用canvas联动
  • webgl
    • Mac使用技巧(二)
    • Node之模块解析path
  • 代码库
    • documeng的一些常见操作
    • eventBus事件
    • jquery提交
    • jquery的一些常见操作
    • 常见操作
    • 数组polyfill
    • TS代码片段
      • 面试官眼中的test unit
  • 全年安排
    • AfterShip
    • 大企业
  • 函数编程题
    • Promise问题
    • 继承
  • 前端早早聊
    • vue生态
    • 开发一款VScode语言插件
    • 简历回顾和进行复盘
    • 重新认知性能优化及其度量方法
    • 2022-09-17-音视频专场.md
      • 2022-09-17-音视频专场
    • 前端晋升专场
      • 成长的诀窍是靠自己
      • 销销帮
    • 前端监控专场
      • 字节前端监控实践
      • 李港:大前端-从无到有搭建自研前端监控系统
    • 前端跳槽
      • 50个面试官在线招聘
      • 如何识别优秀的前端猎头来跳槽大厂
      • 面试套路
    • 支付宝
      • 面试
    • 管理专场
      • 芋头:管理者眼中的web技术发展前沿
    • 组件专场
      • 基于webCompents的跨技术组件库实践
    • 面试
      • 面试辅导问题
      • 早早聊面试
      • 前端沙箱是什么? 怎么实现沙箱环境?
  • 常见总结
    • 2018年终总结-年底了,你总结了吗?我先来
    • 在逆境中成长
    • 2021年终总结
    • 2024年全年总结
    • 项目
    • Tell2.0 前端复盘
    • 复盘
    • 前端工程师素养
    • 学习方法论
    • 希望与破晓| 2022 年终总结
    • 新起点, 新征途 | 掘金年度征文
    • 稳定| 2023 年终总结
    • 趁着有风快飞翔 | 2019 年终总结
    • AfterShip
      • Emotion:用 JavaScript 编写 CSS 的强大工具
      • 个人中长期目标
      • 事故复盘
      • 时间解析
      • 国内外区别
      • 独立站建设
    • MEIZU
      • NativeApp与H5通信原理
      • SSR 原理
      • SSR的常见问题
      • CLI
      • electron 应用发布流程
      • electron
      • electron 面试
      • 数据结构与算法之美
      • mgc 一期复盘
      • 架构原理
      • 喵币管理
      • 三期复盘总结
      • 异常监控之 sentry 实践
      • 微前端
      • qiankun 原理解析
      • 快游戏一期
      • 游戏中心复盘
    • 个人准则
      • index
    • 编程猫
      • pc 接入 micro bit 方案
      • prompt engineer
      • web work 跨域解析与解决方式
      • web 中的 ai
      • 低版本 node 环境下 ffmpeg 的使用
      • 关于 taobao 源 https 过期
      • 加密 json
      • 安卓 5 和 6 的白屏解决
      • 性能排查与优化实践
      • 探月接入
      • 接入硬件
      • 新生态下的state
      • monorepo 包管理方式
      • 自修复 npm 库
      • 音频的绘制
    • 谨启
      • 音视频
      • 小程序
        • taro 规范
        • 结合 mobx 在跳转前预请求
        • Taro 浅析用法与原理
        • 前文
        • 小程序优化指南
        • 小程序内部实现原理
        • 支付相关
    • tencent
      • TAPD
        • MathJax的食用
        • canvas渲染优化策略
        • 为什么 JavaScript 是单线程的呢?
        • svg 总是不对
        • 前端库
        • 原生端和js端如何通信
        • 在旧项目中复用vue代码
        • 提升自我
        • 批量编辑优化
        • 插入业务对象
        • 编辑器
        • 挂载点
        • 性能优化对比
        • 遇到的问题
        • 项目迁移公告
        • 领导力
      • 行家
        • 实战篇
        • 职业发展、领导力、个人成长
        • 高质量沟通
  • 慕课网
    • react-native原理
    • react-native学习
  • 杂文
    • Dom 节点变动检测并录制的简单实现
    • 错误监控&错误捕获
    • NextJS与NuxtJS
    • 负载均衡的几种常用方式
    • PM2
    • service worker 控制网络请求?
    • SSL 和 TLS 的区别
    • Babel 你太美
    • echart踩坑经验
    • keyup、keydown你都知道有什么区别吗
    • 常见概念
    • 首屏加载优化与性能指标分析
    • preload 和 prefetch 的详解
    • 在项目中配置这几个关系
    • roullp 解析
    • tinymce原理浅析
    • wasm 在前端的应用
    • websocket
    • webworker
    • 项目
    • 从 ajax 到 axios
    • 从postcss 到自己开发一款插件
    • 从输入浏览器到页面展示涉及的缓存机制
    • 代码整洁之道
    • 你知道什么是aop吗
    • 函数式编程
    • 函数式编程指南
    • 前端input框文字最大值
    • 攻坚战
    • 前端书写 sdk
    • 前端文字转语音播放
    • 前端领域的 Docker 和 Kubernetes
    • 前端安全
    • 前端进阶之内存空间
    • 前端音频浅析
    • 十分钟搞定多人协作开发
    • 字符串的比较
    • 尾递归
    • 前文
    • 常见的算法可以分为以下三类
    • 手机调试--mac篇
    • 数组的原生系列
    • COOP 和 COEP - 新的跨域策略
    • 浅谈react组件书写
    • 浏览器与 Node.js 事件循环的区别
    • 由三道题引伸出来的思考
    • 移动端300ms点击延迟
    • 移动端和pc端事件
    • Git 常见疑惑
    • 我们离发 npm 包还有多远
    • 重绘和重排
    • AI 时代下的前端编程范式
    • 音频可视化实战
  • 极客时间
    • Serverless入门课
    • 二分查找
    • 二叉树
    • 全栈工程师
    • 动态规划面试宝典
    • 前端与rust
    • 散列表
    • 前端方面的Docker和Kubernetes
    • 栈
    • 深入浅出区块链
    • 玩转 vue 全家桶
    • 玩转 webpack
    • 程序员的个人财富课
    • 算法
    • 说透元宇宙
    • 跳表
    • 链表
    • 10x 程序员工作法
      • index
    • Node开发实战
      • HTTP服务的性能测试
      • JavaScript语言精髓与编程实战
      • 什么是node。js
      • svg精髓
    • ReactHooks核心原理与实战
      • ReactHooks核心原理与实战
    • Rust
      • Rust编程第一课
      • 前置篇
      • 深度思维
      • 重构
      • 类型体操
      • 基础知识
    • WebAssembly入门课.md
      • 基础篇
      • SSR的注水和脱水
      • jsBriage通信原理
      • 基础知识篇
    • 互联网的英语私教课
      • 互联网人的英语私教课
    • 代码之丑
      • 代码之丑
    • 前端全链路优化实战课
      • 网页指标
    • 图解 Google V8
      • 图解 Google V8
    • 浏览器工作原理与实践
      • 浏览器工作原理与实践
    • 算法面试通关 40 讲
      • 算法面试通关40讲
    • 跟月影学可视化
      • index
    • 软件设计之美
      • 软件设计之美
    • 重学前端
      • js
  • 后续的文件增加都会增加到上面并以编号对应
    • 1029. 两地调度
    • 151.翻转字符串里的单词
    • 2022.3.15
    • 前端数据结构
    • 前端常见算法
    • 前端常见排序
    • 恢复一棵树
  • 设计模式
    • 前端常见设计模式之MVC与MVVM
    • 前端之代理模式
    • 前端常见设计模式之单例模式
    • 前端常见设计模式之发布订阅模式
    • 前端之工厂模式
    • 观察者模式
    • 前端常见设计模式之适配器模式
  • 译文
    • [译] 如何使用CircleCI for GitHub Pages持续部署
    • 您是否优化了 API 的性能
    • [译][官方] Google 正式发布 Flutter 1.2 版本
    • 什么是 Deno ,它将取代 NodeJS ?
  • 读后感
    • JavaScript二十年
    • 1368个单词就够了
    • js编程精解
    • labuladong 的算法小抄
    • lodash常用方法
    • vue的设计与实现
    • 所有的静态资源都是get请求
    • 人生
    • 人生护城河
    • 你不知道的JavaScript
    • 前端核心知识进阶
    • 华为工作法
    • 反脆弱
    • 好好学习
    • 左耳听风
    • 摩托车维修之道
    • 数学之美
    • 深入理解svg
    • 浏览器的ESM到底是啥
    • 经济学原理
    • 编程珠玑
    • 防御式 css 精讲
    • 韭菜的自我修养
  • 雪狼
    • 2022-07-17
    • 基础知识
    • 阶一课程
      • 实战辅导一
      • 实战辅导二
  • 嵌入式
    • 树莓派
      • 排序
  • 源码
    • React
      • 核心知识点
      • errorBoundaries
      • immutable.js 的实现原理
      • React.Suspense
      • react源码分析之Fiber
      • batchedUpdate
      • Component
      • Context
      • react 源码分析之 diff 算法
      • React 中的 key 属性:原理、使用场景与注意事项
      • 使用方式
      • react源码分析之memo
      • react 源码分析之mixin
      • 实战篇
      • react源码分析之react-dom
      • 使用方式
      • scheduleWork
      • useImperativeHandle的使用与原理
      • React 书写小技巧
      • 入口和优化
      • 合成事件和原生事件的区别
      • react 性能优化
      • 构建一个 hooks
      • 浅析 styled-components
      • 生命周期
      • 组合 vs 继承
      • 通信机制
      • 高阶组件
      • 慕课网
        • 应用篇
        • 课程导学
    • ReactHook
      • useCallback
      • useContext
      • useEffect 与 useLayoutEffect
      • useHook
      • useMemo
      • useReducer
      • 原理
      • useState
      • 总结
    • Redux
      • mobx 原理解析
      • redux-saga
      • redux-thunk
      • Mobx 和 Redux 对比
      • 使用方法
      • redux 原理
    • Vite
      • Vite原理
      • Vite配置
      • 热更新原理
      • vite 为什么生产环境用 Rollup
    • Webpack
      • PostCSS
      • Webpack5 核心原理与应用实践-loader
      • Webpack5 核心原理与应用实践-plugin
      • Webpack5 核心原理与应用实践
      • 区分
      • 升级详情
      • treeShaking(树摇Tree Shaking)
      • 编写一个自己的webpack插件plugin
      • 代码分离(code-splitting)
      • webpack 打包优化
      • 基础配置
      • webpack 打包优化
      • webpack 工作原理
      • webpack 按需加载原理
      • webpack 热更新 HMR(Hot Module Replacement)
      • 缓存
      • webpack 自定义 plugin
    • next
      • tailwind
      • 什么是水合
    • sveltejs
      • index
    • tinymce
      • 并发篇
    • 源码手写系列
      • create
      • call
      • bind
      • call
      • es6 单例
      • forEach vs Map
      • instanceOf
      • new
      • reduce
      • 取两个重复数组的交集
      • 函数柯理化
      • 动态规划
      • 基于Generator函数实现async
      • 新建 js 文件
      • 手写一个 slice 方法
      • 手写一个 webpack loader
      • Plugin
      • 手写一个寄生组合式继承
      • 二叉树
      • 链表相关的操作
      • 手动实现发布订阅
      • 数组去重
      • 数组扁平化
      • 数组
      • 构造大顶堆和小顶堆
      • 深浅拷贝 深拷贝
      • 两者对比
    • vue
      • vue2
        • vm.attrs与$listeners
        • vue 和 react 的 diff 算法比较
        • vue 源码分析
        • vue 优化的 diff 策略
        • extends
        • 核心原理篇
        • keep-alive
        • vue 源码分析之 mixins
        • vue 源码分析之 nextTick
        • vue之slot
        • vnode
        • vue 源码分析之 watch
        • 原理
        • vue 源码分析之transition
        • vue 源码分析之异步组件
        • 调用的是 watch
        • 安装
        • react源码分析之portals
        • event 的实现原理(事件的实现原理)
        • 什么是h
        • 分析provide 和 inject
        • vue 源码分析之 use
        • v-model
        • vue源码分析之vuex
        • 响应式原理
        • 初始化的流程
        • 组件更新
        • 编译
        • 父子组件生命周期
        • 原理
        • 多实例
        • Vue 面试
        • 源码研读一
        • 响应式原理
        • 常见问题
        • 数组的劫持
        • vue之自定义指令
        • 运行机制全局概览
      • vue3相比vue2的提升点
        • vue composition api
        • vue3的虚拟dom优化
        • vue3层面的双向数据绑定
        • 预处理优化
  • 重构
    • notification
      • 讲解
  • 面试
    • AfterShip经历
      • JS对URL进行编码和解码
      • ShippingLabelTemplate
      • 接入keycloak详解
      • reCAPTCHA接入
      • yalc与动态解决升级的依赖包
      • RBAC 简介
      • 多语言计划
      • 接入Google登录及其主动弹出快捷登录方式
      • 读书计划
        • 传染
        • 这就是OKR
    • 编程猫经历
      • 2024.1.16
      • 2025.2.20
      • 2025.2.21
      • 2025.2.26
      • 2025.3.28
      • 2025.3.3
      • 2025.3.7
      • 行动轨迹
      • 面试主观题
    • 腾讯经历
      • 2022.02.21
      • 2022.03.30
      • 2022.04.24
      • 2022.04.25
      • 2022.04.27
      • 2022.04.28
      • 2022.04.29
      • 2022.05.05
      • 不同公司的面试关注点不同
      • 2022.05.07
      • 2022.05.09
      • 2022.05.10
      • 2022.05.11
      • 2022.05.12
      • 2022.05.13
      • 2022.05.16
      • 2022.05.17
      • 2022.05.19
      • 2022.05.27
      • 面试
      • 行动轨迹
      • 面试主观题
    • 针对字节
      • 2022.05.14
      • 2022.05.17
      • HR面试准备
      • Promise的相关题目
      • React 进阶实践指南(二)
      • React 面试准备
      • vue 与 react 有什么不同 (react 和 vue 有什么区别)
      • TypeScript 全面进阶指南
      • cookie和session区别
      • express 面试准备 koa 中间件原理
      • next面试准备
      • requestCallBack
      • interface 与 type 异同点
      • 取消 promise
      • 如何设计一个前端项目
      • 进阶篇
      • 早早聊面试准备
      • 自动化部署
      • 挖掘项目的深度
      • 面试
      • 出题指数
    • 魅族经历
      • 2020.09.11
      • 一灯
      • 一灯
      • 一灯
      • 2020.09.20
      • 2020.09.21
      • 网易二面
      • 2020.09.23
      • 头条
      • 360 金融面试题
      • 富途一面
      • 算法
      • 字节
      • 2020.11.04
      • baidu 一面
      • meta 标签的作用
      • 字节
      • 2020.11.22
      • 2020.11.25
      • 微前端接入笔记
      • 面试的基本原则
由 GitBook 提供支持
在本页
  • 可组合的 reducer
  • react-redux
  • 同构应用
  • 同构项目实战
  • redux 异步中间件原理

这有帮助吗?

  1. 源码
  2. Redux

redux 原理

store 实例持有当前状态

const createStore = (reducer) => {
    let state = {};
    let listeners = []

    getState = () => state;

    dispatch(listener) {
            state = reducer(state, action);
            listeners.forEach(listener => listener())
        },

        subscribe(listener) {

            listeners.push(listener)

            return () => {
                listeners = listeners.filter(item => {
                    item !== listener
                })
            }
        }

    return {
        getState,
        dispatch,
        subscribe
    }
}

if (process.env.NODE_ENV== 'production') {
        const store.dispatch = addLoggingToDispatch(store);
    }

    addLoggingToDispatch(store) => {
        const rawDispatch = store.dispatch;

        if (!console.log(group)) {
            return rawDispatch
        }

        return (action) => {
            console.log('dayin', get.state())

            //调用原始的 dispatch 并记录返回值
            const returnValue = rawDispatch(action);

            console.log('dayin', get.state())
            return returnValue
        }
    }

    addPromiseDispatch(store) => {
        const rawDispatch = store.dispatch;

        return (action) => {
            if (typeof action.then() == 'function') {
                return action.then(rawDispatch)
            }

            return rawDispatch(action)
        }
    }

我们可以将这种包装过程收敛 -- 声明一个数组, 即中间件数组 Redux 核心思想就是将 dispatch 增强改造的函数(中间件)先存起来,然后提供给 Redux.

Redux 负责依次执行. 这样每一个中间件都对 dispatch 一次进行改造, 并将改造后的 dispatch 即 next 向下传递, 即将控制权转移给下一个中间件, 完成进一步的增强

const configureStore = () => (
  const store= createStore(App) ;
  const middlewares = [];
  if (process . env . NODE_ENV !== 'production') {

    middlewares.push(addLoggingToDispatch);

  }
  middlewares.push(addPromiseSupportToDispatch);
  wrapDispatchWithMiddlewares(store , middlewares)
  return store;
}

wrapDispatchWithMiddlewares 接收一个 middlewares 数组 和最纯净的 store.dispatch

const wrapDispatchWithMiddlewares = (store, middlewares) => {
  middlewares
    .slice()
    .reverse()
    .forEach(
      (middleware) => (store.dispatch = middleware(store)(store.dispatch))
    );
};

const promise = (store) => (next) => (action) => {
  return (action) => {
    if (typeof action.then() == "function") {
      return action.then(next);
    }

    return next(action);
  };
};

const logger = (store) => (next) => {
  if (!console.log(group)) {
    return next;
  }
  return (action) => {
    console.log("dayin", get.state());

    //调用原始的 dispatch 并记录返回值
    const returnValue = next(action);

    console.log("dayin", get.state());
    return returnValue;
  };
};
export default function applyMiddleware(...middlewares) {
  return (next) => (reducer, initialState) => {
    var store = next(reducer, initialState);
    var dispatch = store.dispatch;
    var chain = [];

    var middlewareAPI = {
      getState: store.getState,
      dispatch: (action) => dispatch(action),
    };

    chain = middlewares.map((middleware) => middleware(middlewareAPI));

    dispatch = compose(...chain, store.dispatch);

    return {
      ...store,
      dispatch,
    };
  };
}

middlewareAPI 是第三方中间件需要使用的参数,即原始的 store.getState 和 dispatch 方法, 这些参数在中间件中是否会全部应用到,自然要看每个中间件的应用场景和需求。

chain 数组中的每一项都是对原始 dispatch 的增强,并进行控制权转移. 所以就有了 dispatch = compose(...chain, store.dispatch)

这里的 dispatch 函数就是增强后的 dispatch. 因此, compose 方法接收了 chain 数组和原始的 store.dispatch 方法.

export default function compose(...funcs) {
  if (funcs.length === 0) {
    return (arg) => arg;
  }

  if (funcs.length === 1) {
    return funcs[0];
  }

  return funcs.reduce(
    (a, b) =>
      (...args) =>
        a(b(...args))
  );
}

写一个中间件的套路

const customMiddleware = (store) => (next) => (action) => {};
store.dispatch({
  type: 'CHANGE_THEME',
  payload: 'light'
})


const CHANGE_THEME = store => next => action => {
  //拦截目标 action

  if(action.type  === 'CHANGE_THEME'){
    if(localStorage.getItem('theme') !=== action.payload){
      localStorage.setItem('theme', action.payload)
    }
  }

  return next(action)
}

// 业务初始化的时候
store.dispatch({
  type: 'CHANGE_THEME',
  payload: localStorage.getItem('theme') || 'dark'
})

可组合的 reducer

我们可以记录每一个 action 和状态,当程序出现问题时, 首先查询究竟是哪一个状态发生错误,然后回溯,看前一个触发错误的 action 是否准确.因为 action 都是 JavaScript 对象,完全可读,所以我们完全有能力识别出 action 的正确性. 在持久性方面 数据采用 JavaScript 象来存储,可以方便地使用 Ison parse 等相关序列 方法,可以在任何需要数据的地方进行 载。同时,对于同构渲染,我们也可以在服务端就进行状态的设定,完成服务端直 。当页 面出现错误时,我们可以用典型的 try ... catch 捕获错误,并记录当前的状态以及 action ,这对于 生成页面错误报告也是非常有意义 对于优化共 ,我们可以设想这样的场景:在页面中, 一个用户可以点击关注按钮去关注另外一个用户。在点击关注之后 ,可以在发送网络请求之前 便对 state 进行最初的更改,同时把相关的 action 记录在一个队列中 ,稍后再发送关注的请求 如果请求最终失败,则返回相应的状态,并提示用户.

const getVisibleTodos = (state , filter) => {
  switch (filter) {
    case  'all' :
    return state ;
    case 'completed' :
    return state.filter => (t => t.completed) ;
  }
}
const undoable = (reducer) => {
  const initialState = {
    past: [],
    present: reducer(undefined, {}),
  };

  return (state = initialState, action) => {
    const { past, present } = state;
    if (action.type === "UNDO") {
      return {
        past: past.slice(0, -1),
        present: past[past.length - 1],
      };
    }
    return {
      past: [...past, present],
      present: reducer(present, action),
    };
  };
};

react-redux

将所有的业务组件嵌套在由 react-redux 提供的 provider 组件当中,并将所生成的 store 设置为 provider 组件的参数, provider 组件便感知到 store.

context 用来使 react 子孙组件可以直接"越级"获取父组件的信息,这样就不需要一层层通过 props 向下传递. 核心目标是使 props.store 放到 context 中; connect 作为一个柯理化的高级函数,可以根据一级参数计算筛选出 store 信息,根据二级参数 component 返回一个高级组件 connectComponent 即可

同构应用

就是用户必须等待 Java Script 脚本加载完成,且真正执行时才会发起数据请求。接下来,等待 数据成功返回后,脚本完成页面内容渲染,用户才可以得到最终页面。这样做直接降低了页面 首屏展现的时间,特别是在移动互联网环境下,对首屏加载性能的影响很大。

不利于 SEO 和存在性能问题 页面的数据内容主要由 JavaScript 脚本动态生成,因此非常不利于搜索引擎获取该页面的信息. 且对首屏加载性能的影响很大

服务端渲染技术会把数据请求过程放在服务端,相对于前后端分离的方式,获取数据更加 提前,页面模板结合数据的渲染处理也在服务端完成。结合 React 技术,基本的组件拼接在服 务端完成 并最终输出相对完整的 HTML 返回给浏览器端。

服务端渲染主要侧重架构层面的实现,而同构更侧重代码复用

所谓同构,就是指前后端共用一套代码或逻辑,而在这套代码或逻辑中,理想的状况是在浏览器端进一步渲染的过程中,判断己有的 DOM 结构和即将渲染出的结构是否相同,若相同,则不重新渲染 DOM 结构,只需要进行事件绑定即可.

同构更像是服务端渲染和浏览器渲染的交集, 它弥补了服务端和浏览器端的差异,从而使得同一套代码或逻辑得以统一运行.

同构得劣势: 增加了服务端得 ttfb 时间,ttfb 时间指得是从浏览器发起最初的网络请求,到从服务器接收到第一个字节得这段时间.它包含了 tcp 连接时间\发送 http 请求得时间和获得相应消息的第一个字节时间.

react 也实现了相应得 api, ReactDomServer 对象可以实现服务端渲染,ReactDOMServer 对象主要提供了 renderToString 和 renderToStaticMarkup()

renderToString 接收一个 react element , 并将此 element 转化为 html 字符串,通过浏览器端返回,因此,在服务端将页面拼接字符串插入 html 文档中并返回给浏览器,完成初步服务端渲染的目的.

renderToStrin 生成的 HTML 字符串的每个 DOM 节点都有一个 data-react-id 属性, 根节点会有一个 data-checksum 属性。

当服务端和浏览器端渲染的组件具有相同的 props 和 dom 结构时,该 react 组件只会渲染一次

React 16 还提供了 renderToNodeStream 法实现服务端渲染。该方法将持续产生字节流, 返回 Readable stream 。最终通过流形式返回的 HTML 字符串,与 renderToString 返回的 HTML 字符串并无差 。

服务端处理 内容时是实时 向浏览器端传输数据 ,而不是一次性处理完成后才开始向浏览器端返回结果的.这样做的好处是可以缩短 ttfb 时间

注意事项:

  • 在服务端并不存在直接组件挂载的浏览器环境,所以 react 组件只有 componentDidMount 之前的生命周期方法有效。因此在 getlnitia!State render 等组件方法 中不能用到浏览器的一些特性,比如访问 localStorage window 等。合理的做法 ,将 依赖浏览器环境的操作放到 componentDidMount 中处理。

  • 在服务端拉取数据后, 在很多场景下浏览器端也需要拉取数据,进行二次渲染.为了实 现更好的代码复用, 种典型的做法是把请求数据的逻辑放到 React 组件的静态方法中。 这样不管是浏览器端还是服务端,在需要获取最新数据时都可以直接访 该方法, 以实 现代码复用

关于数据请求逻辑的问题 因为服务端不存在 JAX 概念,在 Node.js 环境下, 一般 使用 http.request 来完成请求。为了达到代码复用的效果,可以使用 isomorphic tch 包对 请求逻辑的服务端和浏览器端 一致性进行封装。

var onServer = typeof window === ’ undefined ’ ;
if (onServer) {//服务端逻辑) else {//浏览器端逻辑}

在服务端使用 redux 常见的套路和做法

serialize-javascript window.INITIAL_STATE = ${JSON.stringify(store.getState())}

防范:xss serialize-javascript

同构项目实战

这是因为 TTFB 时间是服务器响应首宇节的时间,采用流的渐进式渲染可以 大限度地缩短服务器响应时间,从而使浏览器可以更快地接收到信息

当初始的 HTML 文档被完全加载和解析完成之后, DOMContentLo ade 事件被触发,而无须等待样式表、图片和子框架加载完成: Load 事件用于检测页面是否完全加载完成。具体来说,如果 HTML 文档 中包含脚本,则脚本会阻塞文档的解析,在处理完脚本之后,浏览器再继续解析 HTML 文档。在任何情况下 ,触发 DOMContentLoaded 事件都不需要等待图片等其他资源加载完成。

让 setState 连续更新的方法:

  1. 将一个回调函数传入 setState 方法中, 即著名的函数式用法.

setState((prevState, props) => {
  count: prevState.count + 1;
});
  1. 把 setState 更新之后的逻辑封装到一个函数中,并作为第二个参数传给 setState.这个函数逻辑将会在更新后由 React 代理执行. this.setState(updater, [callback])

  2. 把更新之后的逻辑放在一个合适的生命周期,例如 componentDidMount 或者 componentDidUpdate

react Element 描述了用户在屏幕上看到的事物. 抽象地说, React element 是一个描述了 DOM 节点的对象.babel 会对 jsx -> react.createElement

在数据持久度上,不同状态的数据大体分为三类 快速变更型、中等持续型和长远稳定型数据

快速变更型数据在应用中代表了某些原子级别的信息,其显著特点是变更频率最快 比如一个文本输入框数据值,可能会随着用户输入在短时间内持续发生变化. 这类数据显然更适合 维护在 React 组件之内.

对于中等持续型数据,当用户浏览或使用应用时,这类数据往往会在页面刷新前保持稳定。比如从异步请求接口通过 AJAX 方式得来的数据;或者在个人中心页面,用户编辑信息提交的数据.

长远稳定型数据是指在页面多次刷新或者多次访问期间都保持不变的数据。因为 Redux store 会在每次页面加载后都重新生成,因此这类数据显然应该存储在 Redux 以外的其他地方, 比如服务端数据库或者本地存储中。

Redux store 已经有了 一个现成的方法 reducer ,即 store.replaceReducer 于它,我们可以动态实现传递一个新的 reducer 来取代旧的 reducer 先假设在 上,己经有了初始状态的 reducer 此时如果需要再添加一个 splitReducer 则可 这样做

当任何一个组件使用 setState 方法时, React 都会认为该组件变“脏”了,触发组件本身重新渲染。同时因其始终维护两套虚拟的 DOM ,其中一套是更新后的虚拟的 DOM ;另一套是前一个状态的虚拟的 DOM. 通过对这两套虚拟的 DOM 运用 diff 算法,找到需要变化的最小单元 集,然后把这个最小单元集应用在真实的 DOM 当中.

DOM 点跨层级移动忽略不计。 拥有相同类的两个组件生成相似的树形结构,拥有不同类的两个组件生成不同的树形 结构 React 对组件树进行分层比较,两棵树只会对同一层级的节点进行比较。 ·当对同 一层级的节点进行 较时,对于不 同的组件类型 ,直接将 个组件替换为新类 型组件。

render () {
  return <Myinput o口Change={this props update bind(this)) /> ;
}

或者

render( ) {
  return <Myinput onChange= { () => this . props. update () ) />;
}

使用 bind 方法,每次渲染时都会 建一 新函数,对内存造成不必要的消耗。在完成 this 绑定的情况下,提倡的做法是

onChange() {
  this.props doUpdate()
}
render () {
  return <Myinput onChange={this . onChange)/> ;
}
render () {
return <Subcomponent i terns= {this. props. i terns I I [] } />
}

这样做会在每次渲染且 this.props.items 不存在时都创建 空数 。更好的做法是:

const EMPTY ARRAY= [] ;
render() {
return <Subcomponent tems={th props terns I I EMPTY ARRAY}/>
}

redux 异步中间件原理

异步中间件本质上是增强了 dispatch 函数的功能。它通过中间件机制拦截 action,并在 action 到达 reducer 之前执行一些额外的逻辑(比如异步操作)。然后再决定是否将 action 传递给 reducer。

const middleware = (store) => (next) => (action) => {
  // 在这里处理 action
  // next 是调用下一个中间件或 reducer 的函数
  return next(action);
};
  • store: Redux store 的实例,包含 getState 和 dispatch。

  • next: 一个函数,用于将 action 传递给下一个中间件或最终的 reducer。

  • action: 当前处理的 action。

中间件通过柯里化(currying)依次应用到 dispatch 上,最终形成一个增强版的 dispatch 函数。

redux-thunk 是一个比较常用的中间件,它允许 action 创建函数返回一个函数代替一个 action 对象。这个函数接收 dispatch 和 getState 作为参数,可以在函数体内部执行异步代码。

function thunk({ dispatch, getState }) {
  return (next) => (action) => {
    if (typeof action === "function") {
      // 如果 action 是一个函数,就执行它,并传入 dispatch 和 getState
      return action(dispatch, getState);
    }
    // 否则直接传递给下一个中间件
    return next(action);
  };
}
上一页使用方法下一页Vite

最后更新于2个月前

这有帮助吗?