Sep
20
2 months ago
Activity icon
issue

sqshada issue comment sqshada/articles

sqshada
sqshada

react-redux

redux 三大原则

  • 单向数据流
  • state 只读:如果想要改变 state,只能触发 action 执行 reducer
  • 纯函数执行:每个 reducer 都是一个纯函数,里面不要执行任何副作用,返回值作为新的 state,state 会触发 store 中的 subscribe

发布订阅思想

中间件思想

redux 应用了中间件 compose,中间件的作用是 强化 dispatch ,传统的 dispatch 是不支持异步的,但是可以针此点做强化,比如 redux-thunkredux-actions 等中间件。

dva 也写了一个 redux 支持 promise 的中间件

const compose = (...funcs) => {
  return funcs.reduce((f, g) => (x) => f(g(x)));
}

redux 核心 api

createStore

创建一个 store,将 store 传递给 React 应用。

const Store = createStore(rootReducer,initialState,middleware)

combineReducers

合并多个 reducers

/* 将 number 和 PersonalInfo 两个reducer合并   */
const rootReducer = combineReducers({ number:numberReducer,info:InfoReducer })

applyMiddleware

注册中间件,支持多个参数,每个参数都是一个中间件,每次出发 action,中间件执行

const middleware = applyMiddleware(logMiddleware)

React-Redux

Provider

Provider 作用就是保存 redux 中的 store,分配给需要 state 的子孙组件

connect

React-Redux 提供高阶组件 connect,被包裹后提供如下功能

  • 能够从 props 中获取改变 state 的方法 Store.dispatch
  • 如果 connect 有第一个参数,那么会将 redux state 中的数据,映射到当前组件的 props 中,子组件可以消费
  • 当需要的 state 有变化的时候,会通知当前组件更新,重新渲染视图
function connect(mapStateToProps?, mapDispatchToProps?, mergeProps?, options?)

mapStateToProps

const mapStateToProps = state => ({ number: state.number })

组件依赖 redux 的 state,映射到业务的 props 中。

mapDispatchToProps

const mapDispatchToProps = dispatch => {
  return {
    numberAdd: () => dispatch({ type: 'ADD' }),
    setInfo: () => dispatch({ type: 'SET' }),
  }
}

将 redux 中的 dispatch 方法,映射到业务组件的 props 中。

mergeProps

/*
* stateProps , state 映射到 props 中的内容
* dispatchProps, dispatch 映射到 props 中的内容。
* ownProps 组件本身的 props
*/
(stateProps, dispatchProps, ownProps) => Object

options

{
  context?: Object,   // 自定义上下文
  pure?: boolean, // 默认为 true , 当为 true 的时候 ,除了 mapStateToProps 和 props ,其他输入或者state 改变,均不会更新组件。
  areStatesEqual?: Function, // 当pure true , 比较引进store 中state值 是否和之前相等。 (next: Object, prev: Object) => boolean
  areOwnPropsEqual?: Function, // 当pure true , 比较 props 值, 是否和之前相等。 (next: Object, prev: Object) => boolean
  areStatePropsEqual?: Function, // 当pure true , 比较 mapStateToProps 后的值 是否和之前相等。  (next: Object, prev: Object) => boolean
  areMergedPropsEqual?: Function, // 当 pure 为 true 时, 比较 经过 mergeProps 合并后的值 , 是否与之前等  (next: Object, prev: Object) => boolean
  forwardRef?: boolean, //当为true 时候,可以通过ref 获取被connect包裹的组件实例。
}
sqshada
sqshada
  • React-Redux 通过上下文来保存传递 Store,但是上下文保存的还有 subscription
  • subscription 可以理解为订阅器,订阅 state 的变化,另一方面通知对应的组件更新。Provider 中的订阅器为根订阅器
  • 在 Provider 中,进行真正的绑定订阅功能,其原理内部调用了 store.subscribe
  • 订阅器的核心:层层订阅,上订下发
    • 每一个 connect 包装的组件内部也有一个 subscription。
    • 调用 trySubscription 时,订阅器会和上一级订阅器建立关联,store 中 state 的变化只会通知给 Provider 中的根订阅器。根 subscription 不会派发更新,而是下发给子代订阅器。

为什么 React-Redux 会采用 subscription 订阅器进行订阅,而不是直接采用 store.subscribe 呢 ?

  • 首先 state 的改变,Provider 是不能直接下发更新的,如果下发更新,那么这个更新是整个应用层级上的,还有一点,如果需要 state 的组件,做一些性能优化的策略,那么该更新的组件不会被更新,不该更新的组件反而会更新了。
  • 父 Subscription -> 子 Subscription 这种模式,可以逐层管理 connect 的状态派发,不会因为 state 的改变而导致更新的混乱。
Activity icon
issue

sqshada issue sqshada/articles

sqshada
sqshada

计算机网络-

image

  • 应用层:通过应用进程间的交互来完成特定的网络应用。互联网中应用层协议很多,比如 DNS、HTTP协议,应用层交互的的数据单元是报文
  • 运输层:运输层的任务就是向两台主机中进程之间的通信提供通用的数据传输服务。比如呀 TCP、UDP 协议。
  • 网络层:网络层负责为分组交换网上的不同主机提供通信服务。发送数据时,网络层把运输层产生的报文段或者用户数据封装成分组或者包进行传送。网络层的另一个任务是要选择合适的路由,使源主机运输层所传下来的分组,能够通过网络中的路由器找到目的主机。
  • 数据链路层:数据链路层将网络层交下来的 IP 数据报组装成帧,在相邻两个结点间的链路上传送帧。
  • 物理层:物理层上传输的数据单位是比特。
Sep
19
2 months ago
Activity icon
issue

sqshada issue comment sqshada/articles

sqshada
sqshada

react-redux

redux 三大原则

  • 单向数据流
  • state 只读:如果想要改变 state,只能触发 action 执行 reducer
  • 纯函数执行:每个 reducer 都是一个纯函数,里面不要执行任何副作用,返回值作为新的 state,state 会触发 store 中的 subscribe

发布订阅思想

中间件思想

redux 应用了中间件 compose,中间件的作用是 强化 dispatch ,传统的 dispatch 是不支持异步的,但是可以针此点做强化,比如 redux-thunkredux-actions 等中间件。

dva 也写了一个 redux 支持 promise 的中间件

const compose = (...funcs) => {
  return funcs.reduce((f, g) => (x) => f(g(x)));
}

redux 核心 api

createStore

创建一个 store,将 store 传递给 React 应用。

const Store = createStore(rootReducer,initialState,middleware)

combineReducers

合并多个 reducers

/* 将 number 和 PersonalInfo 两个reducer合并   */
const rootReducer = combineReducers({ number:numberReducer,info:InfoReducer })

applyMiddleware

注册中间件,支持多个参数,每个参数都是一个中间件,每次出发 action,中间件执行

const middleware = applyMiddleware(logMiddleware)

React-Redux

Provider

Provider 作用就是保存 redux 中的 store,分配给需要 state 的子孙组件

connect

React-Redux 提供高阶组件 connect,被包裹后提供如下功能

  • 能够从 props 中获取改变 state 的方法 Store.dispatch
  • 如果 connect 有第一个参数,那么会将 redux state 中的数据,映射到当前组件的 props 中,子组件可以消费
  • 当需要的 state 有变化的时候,会通知当前组件更新,重新渲染视图
function connect(mapStateToProps?, mapDispatchToProps?, mergeProps?, options?)

mapStateToProps

const mapStateToProps = state => ({ number: state.number })

组件依赖 redux 的 state,映射到业务的 props 中。

mapDispatchToProps

const mapDispatchToProps = dispatch => {
  return {
    numberAdd: () => dispatch({ type: 'ADD' }),
    setInfo: () => dispatch({ type: 'SET' }),
  }
}

将 redux 中的 dispatch 方法,映射到业务组件的 props 中。

mergeProps

/*
* stateProps , state 映射到 props 中的内容
* dispatchProps, dispatch 映射到 props 中的内容。
* ownProps 组件本身的 props
*/
(stateProps, dispatchProps, ownProps) => Object

options

{
  context?: Object,   // 自定义上下文
  pure?: boolean, // 默认为 true , 当为 true 的时候 ,除了 mapStateToProps 和 props ,其他输入或者state 改变,均不会更新组件。
  areStatesEqual?: Function, // 当pure true , 比较引进store 中state值 是否和之前相等。 (next: Object, prev: Object) => boolean
  areOwnPropsEqual?: Function, // 当pure true , 比较 props 值, 是否和之前相等。 (next: Object, prev: Object) => boolean
  areStatePropsEqual?: Function, // 当pure true , 比较 mapStateToProps 后的值 是否和之前相等。  (next: Object, prev: Object) => boolean
  areMergedPropsEqual?: Function, // 当 pure 为 true 时, 比较 经过 mergeProps 合并后的值 , 是否与之前等  (next: Object, prev: Object) => boolean
  forwardRef?: boolean, //当为true 时候,可以通过ref 获取被connect包裹的组件实例。
}
sqshada
sqshada
Activity icon
issue

sqshada issue sqshada/articles

sqshada
sqshada

react-redux

redux 三大原则

  • 单向数据流
  • state 只读:如果想要改变 state,只能触发 action 执行 reducer
  • 纯函数执行:每个 reducer 都是一个纯函数,里面不要执行任何副作用,返回值作为新的 state,state 会触发 store 中的 subscribe

发布订阅思想

中间件思想

redux 应用了中间件 compose,中间件的作用是 强化 dispatch ,传统的 dispatch 是不支持异步的,但是可以针此点做强化,比如 redux-thunkredux-actions 等中间件。

dva 也写了一个 redux 支持 promise 的中间件

const compose = (...funcs) => {
  return funcs.reduce((f, g) => (x) => f(g(x)));
}

核心 api

Activity icon
issue

sqshada issue comment sqshada/articles

sqshada
sqshada

react-router

react-router-dom 、react-router 和 history 库三者什么关系

  • historyreact-router 核心,里面集成了 popStatepushState 等路由现实方法。
  • react-router 封装了 RouterRouteSwitch 等组件,实现了路由的改变到组件的更新。
  • react-router-domreact-router 的基础上添加了 LinkBrowserRouterHashRouter 组件。

单页面实现核心原理

原理:切换 url,监听 url 的变化,从而渲染不同的页面组件

history 模式

改变路由:history.pushState、history.replaceState

监听路由:history.popState

注意⚠️:用 history.pushState() 或者 history.replaceState() 不会触发 popState 事件。 popState 事件只会在浏览器某些行为下触发, 比如点击后退、前进按钮或者调用 history.back()、history.forward()、history.go()方法。

hash 模式

改变路由:window.location.hash 监听路由:onhashchange

Router Hooks

  • useHistory:直接访问 history 不再通过 props
  • useParams:直接访问到路由参数
  • useLocation:直接返回当前 URL 的 location 对象
sqshada
sqshada

Router 是整个应用路由的传递者和派发更新者。

BrowserRouter 或者 HashRouter,两者关系就是 Router 作为一个传递路由和更新路由的容器,而 BrowserRouter 或 HashRouter 是不同模式下向容器 Router 中注入不同的 history 对象。

  • React-Router 通过 context 上下文方式传递的路由信息,context 改变,使消费 context 组件更新。
  • props.history 通过 BrowserRouter 或 HashRouter 创建 history 对象,当路由改变,会触发 listen 方法,传递生成新的 location 对象,通过 setState 改变 context 中的 value,所以路由的改变本质上是 location 改变带来的作用。

Route

Route 核心就是:匹配路由、路由匹配、渲染组件。

四种 Route 编写格式

<Switch>
  <Route path='/router/component'   component={RouteComponent}   /> { /* Route Component形式 */ }
  <Route path='/router/render'  render={(props)=> <RouterRender { ...props }  /> }  {...mes}  /> { /* Render形式 */ }
  <Route path='/router/children'  > { /* chilren形式 */ }
      <RouterChildren  {...mes} />
  </Route>
  <Route path="/router/renderProps"  >
      { (props)=> <RouterRenderProps {...props} {...mes}  /> }  {/* renderProps形式 */}
  </Route>
</Switch>
Activity icon
issue

sqshada issue comment sqshada/articles

sqshada
sqshada

react-router

react-router-dom 、react-router 和 history 库三者什么关系

  • historyreact-router 核心,里面集成了 popStatepushState 等路由现实方法。
  • react-router 封装了 RouterRouteSwitch 等组件,实现了路由的改变到组件的更新。
  • react-router-domreact-router 的基础上添加了 LinkBrowserRouterHashRouter 组件。

单页面实现核心原理

原理:切换 url,监听 url 的变化,从而渲染不同的页面组件

history 模式

改变路由:history.pushState、history.replaceState

监听路由:history.popState

注意⚠️:用 history.pushState() 或者 history.replaceState() 不会触发 popState 事件。 popState 事件只会在浏览器某些行为下触发, 比如点击后退、前进按钮或者调用 history.back()、history.forward()、history.go()方法。

hash 模式

改变路由:window.location.hash 监听路由:onhashchange

Router Hooks

  • useHistory:直接访问 history 不再通过 props
  • useParams:直接访问到路由参数
  • useLocation:直接返回当前 URL 的 location 对象
sqshada
sqshada

`import { createHashHistory as createHistory } from "history";

/**

  • The public API for a that uses window.location.hash. */ class HashRouter extends React.Component { history = createHistory(this.props);

render() { return ; } }`

通过 createHashHistory 创建 history 对象传递给 Router 组件

Activity icon
issue

sqshada issue comment sqshada/articles

sqshada
sqshada

react-router

react-router-dom 、react-router 和 history 库三者什么关系

  • historyreact-router 核心,里面集成了 popStatepushState 等路由现实方法。
  • react-router 封装了 RouterRouteSwitch 等组件,实现了路由的改变到组件的更新。
  • react-router-domreact-router 的基础上添加了 LinkBrowserRouterHashRouter 组件。

单页面实现核心原理

原理:切换 url,监听 url 的变化,从而渲染不同的页面组件

history 模式

改变路由:history.pushState、history.replaceState

监听路由:history.popState

注意⚠️:用 history.pushState() 或者 history.replaceState() 不会触发 popState 事件。 popState 事件只会在浏览器某些行为下触发, 比如点击后退、前进按钮或者调用 history.back()、history.forward()、history.go()方法。

hash 模式

改变路由:window.location.hash 监听路由:onhashchange

Router Hooks

  • useHistory:直接访问 history 不再通过 props
  • useParams:直接访问到路由参数
  • useLocation:直接返回当前 URL 的 location 对象
sqshada
sqshada

BrowserRouter

import { createBrowserHistory as createHistory } from "history";
class BrowserRouter extends React.Component {
  history = createHistory(this.props) 
  render() {
    return <Router history={this.history} children={this.props.children} />;
  }
}

通过 createBrowserHistory 创建 history 对象传递给 Router 组件

Activity icon
issue

sqshada issue comment sqshada/articles

sqshada
sqshada

react-router

react-router-dom 、react-router 和 history 库三者什么关系

  • historyreact-router 核心,里面集成了 popStatepushState 等路由现实方法。
  • react-router 封装了 RouterRouteSwitch 等组件,实现了路由的改变到组件的更新。
  • react-router-domreact-router 的基础上添加了 LinkBrowserRouterHashRouter 组件。

单页面实现核心原理

原理:切换 url,监听 url 的变化,从而渲染不同的页面组件

history 模式

改变路由:history.pushState、history.replaceState

监听路由:history.popState

注意⚠️:用 history.pushState() 或者 history.replaceState() 不会触发 popState 事件。 popState 事件只会在浏览器某些行为下触发, 比如点击后退、前进按钮或者调用 history.back()、history.forward()、history.go()方法。

hash 模式

改变路由:window.location.hash 监听路由:onhashchange

Router Hooks

  • useHistory:直接访问 history 不再通过 props
  • useParams:直接访问到路由参数
  • useLocation:直接返回当前 URL 的 location 对象
sqshada
sqshada

listen

history 使用观察者模式,来进行 location 的监听。任何时候 location 改变,history 对象都会调用所有的监听函数。location 改变后会触发 context (React context)进行更新,消费 context 的组件都会重新渲染,当前 Route 会重新渲染,通过对当前 location 的 pathname 进行匹配

Activity icon
issue

sqshada issue comment sqshada/articles

sqshada
sqshada

koa

主体流程

listen

  listen (...args) {
    debug('listen')
    const server = http.createServer(this.callback())
    return server.listen(...args)
  }

使用 http.createServer 创建了服务,koa 内部自行实现 this.callback(),将回调放入 createServer

callback

  callback () {
    const fn = compose(this.middleware)

    if (!this.listenerCount('error')) this.on('error', this.onerror)

    const handleRequest = (req, res) => {
      const ctx = this.createContext(req, res)
      return this.handleRequest(ctx, fn)
    }

    return handleRequest
  }

callback 实现 handleRequest 方法,将 req,res 拼接成 ctx,交给 this.handleRequest 处理

createContext

将 req,res 拼接成 ctx

中间件机制

中间件注册

  use (fn) {
      // 限制传入的必须是函数,否则报错
      if (typeof fn !== 'function') throw new TypeError('middleware must be a function!')
      debug('use %s', fn._name || fn.name || '-')

      // 将函数push进middleware数组
      this.middleware.push(fn)

      // 返回自己,方便链式调用
      return this
    }

Application 实例维护一个 middleware 的数组,提供 use 方法注册中间件,也就是 push 到 middleware 数组中,依赖于数组进行实现。

洋葱模型

image

通过 use 会注册中间件,并且中间件也有顺序

const Koa = require('koa');

// 应用程序
const app = new Koa();

// 中间件1
app.use(async (ctx, next) => {
    console.log(1); // 第1步
    await next(); // 第2步
    console.log(2); // 第6步
});

// 中间件2
app.use(async (ctx, next) => {
    console.log(3); // 第3步
    await next(); // 第4步
    console.log(4); // 第5步
});

app.listen(8000, () => {
    console.log(`Server is starting`);
});


// 1 3 4 2

callback 方法中,通过 koa-compose,将中间价数组转化为一个 fn 的执行函数

const compose = require('koa-compose')
...

module.exports = class Application extends Emitter {
  ...
  callback () {
    const fn = compose(this.middleware)

    if (!this.listenerCount('error')) this.on('error', this.onerror)

    const handleRequest = (req, res) => {
      const ctx = this.createContext(req, res)
      return this.handleRequest(ctx, fn)
    }

    return handleRequest
  }
  
  handleRequest (ctx, fnMiddleware) {
    ...
    const onerror = err => ctx.onerror(err)
    const handleResponse = () => respond(ctx)
    ...
    return fnMiddleware(ctx).then(handleResponse).catch(onerror)
  }
}

然后通过 fnMiddleware(ctx).then(handleResponse).catch(onerror) 执行 里面的逻辑封装在 koa-compose 中,这也是最精华的部分

koa-compose


module.exports = compose

function compose (middleware) {
  // 校验必须是数组
  if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array!')
  // 校验每一个元素,必须是函数
  for (const fn of middleware) {
    if (typeof fn !== 'function') throw new TypeError('Middleware must be composed of functions!')
  }

  // 返回首个函数
  return function (context, next) {
    let index = -1
    return dispatch(0) // 执行第一个中间件,第一个执行完递归执行下一个
    function dispatch (i) {
      // 防止中间件中,多次调用next()
      if (i <= index) return Promise.reject(new Error('next() called multiple times'))
      index = i
      let fn = middleware[i]
      if (i === middleware.length) fn = next
      if (!fn) return Promise.resolve()
      try {
        return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
      } catch (err) {
        return Promise.reject(err)
      }
    }
  }
}

去掉类型判断和异常判断代码


module.exports = function compose (middleware) {
  return function (context, next) {
    let index = -1
    return dispatch(0) 
  
    function dispatch (i) {
      index = i
      let fn = middleware[i]
      if (i === middleware.length) fn = next
      if (!fn) return Promise.resolve()
      
      return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
    }
  }
}
sqshada
sqshada

模拟实现中间件

class Koa {
  constructor() {
    this.middleware = [];
  }
  use(middleware) {
    this.middleware.push(middleware);
    return this;
  }
  compose(middleware) {
    return function (context, next) {
      let index = -1;
      return dispatch(0);

      function dispatch(i) {
        index = i;
        let fn = middleware[i];
        if (i === middleware.length) next = null;
        if (!fn) return Promise.resolve();

        return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
      }
    };
  }
  handleRequest(ctx, fnMiddleware) {
    return fnMiddleware(ctx);
  }
  test() {
    const fn = this.compose(this.middleware);
    this.handleRequest(1, fn);
  }
}

const app = new Koa();
app.use(async (ctx, next) => {
  console.log(1);
  await next();
  console.log(2);
});

// 中间件2
app.use(async (ctx, next) => {
  console.log(3);
  await next();
  console.log(4);
});

app.test();

Previous