Giter VIP home page Giter VIP logo

blogs's Introduction

blogs's People

Contributors

mytheart avatar

Watchers

 avatar  avatar

blogs's Issues

MVVM/MVC/Redux核心概念

Redux核心概念

action reducer store

MVC

它是一个UI的解决方案,用于降低UI,以及UI关联的数据的复杂度。

传统的服务器端的MVC

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vt7TOLof-1600448457535)(assets/2019-08-20-13-18-58.png)]

环境:

  1. 服务端需要响应一个完整的HTML
  2. 该HTML中包含页面需要的数据
  3. 浏览器仅承担渲染页面的作用

以上的这种方式叫做服务端渲染,即服务器端将完整的页面组装好之后,一起发送给客户端。

服务器端需要处理UI中要用到的数据,并且要将数据嵌入到页面中,最终生成一个完整的HTML页面响应。

为了降低处理这个过程的复杂度,出现了MVC模式。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8eP3wBqO-1600448457539)(assets/2019-08-20-13-29-14.png)]

Controller: 处理请求,组装这次请求需要的数据
Model:需要用于UI渲染的数据模型
View:视图,用于将模型组装到界面中

前端MVC模式的困难

React解决了 数据 -> 视图 的问题

  1. 前端的controller要比服务器复杂很多,因为前端中的controller处理的是用户的操作,而用户的操作场景是复杂的。
  2. 对于那些组件化的框架(比如vue、react),它们使用的是单向数据流。若需要共享数据,则必须将数据提升到顶层组件,然后数据再一层一层传递,极其繁琐。 虽然可以使用上下文来提供共享数据,但对数据的操作难以监控,容易导致调试错误的困难,以及数据还原的困难。并且,若开发一个大中型项目,共享的数据很多,会导致上下文中的数据变得非常复杂。

比如,上下文中有如下格式的数据:

value = {
    users:[{},{},{}],
    addUser: function(u){},
    deleteUser: function(u){},
    updateUser: function(u){}
}

前端需要一个独立的数据解决方案

Flux

Facebook提出的数据解决方案,它的最大历史意义,在于它引入了action的概念

action是一个普通的对象,用于描述要干什么。action是触发数据变化的唯一原因

store表示数据仓库,用于存储共享数据。还可以根据不同的action更改仓库中的数据

示例:

var loginAction = {
    type: "login",
    payload: {
        loginId:"admin",
        loginPwd:"123123"
    }
}

var deleteAction = {
    type: "delete",
    payload: 1  // 用户id为1
}

Redux

在Flux基础上,引入了reducer的概念

reducer:处理器,用于根据action来处理数据,处理后的数据会被仓库重新保存。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IWD4FUVX-1600448457544)(assets/2019-08-20-14-23-05.png)]

Redux源码分析

  • 入口函数 index.js
export { default as createStore } from "./createStore"
export { default as bindActionCreators } from "./bindActionCreators"
export { default as combineReducers } from "./combineReducers"
export { default as applyMiddleware } from "./applyMiddleware"
export { default as compose } from "./compose"
  • utils/ActionTypes.js
/**
 * 得到一个指定长度的随机字符串
 * @param {*} length 
 */
function getRandomString(length) {
  return Math.random().toString(36).substr(2, length).split("").join(".")
}

export default {
  INIT() {
    return `@@redux/INIT${getRandomString(6)}`
  },
  UNKNOWN() {
    return `@@redux/PROBE_UNKNOWN_ACTION${getRandomString(6)}`
  }
}
  • utils/isPlainObject.js
/**
 * 判断某个对象是否是一个plain-object
 * @param {*} obj 
 */
export default function isPlainObject(obj) {
  if (typeof obj !== "object") {
    return false;
  }
  return Object.getPrototypeOf(obj) === Object.prototype;
}
  • createStore.js
import ActionTypes from "./utils/ActionTypes"
import isPlainObject from "./utils/isPlainObject"

/**
 * 实现createStore的功能
 * @param {function} reducer reducer
 * @param {any} defaultState 默认的状态值
 */
export default function createStore(reducer, defaultState, enhanced) {
    //enhanced表示applymiddleware返回的函数
    if (typeof defaultState === "function") {
        //第二个参数是应用中间件的函数返回值
        enhanced = defaultState;
        defaultState = undefined;
    }
    if (typeof enhanced === "function") {
        //进入applyMiddleWare的处理逻辑
        return enhanced(createStore)(reducer, defaultState);
    }

    let currentReducer = reducer, //当前使用的reducer
        currentState = defaultState; //当前仓库中的状态

    const listeners = [];  //记录所有的监听器(订阅者)

    function dispatch(action) {
        //验证action
        if (!isPlainObject(action)) {
            throw new TypeError("action must be a plain object");
        }
        //验证action的type属性是否存在
        if (action.type === undefined) {
            throw new TypeError("action must has a property of type");
        }
        currentState = currentReducer(currentState, action)
        //运行所有的订阅者(监听器)
        for (const listener of listeners) {
            listener();
        }
    }

    function getState() {
        return currentState;
    }

    /**
     * 添加一个监听器(订阅器)
     */
    function subscribe(listener) {
        listeners.push(listener); //将监听器加入到数组中
        let isRemove = false;//是否已经移除掉了
        return function () {
            if (isRemove) {
                return;
            }
            //将listener从数组中移除
            const index = listeners.indexOf(listener);
            listeners.splice(index, 1);
            isRemove = true;
        }
    }

    //创建仓库时,需要分发一次初始的action
    dispatch({
        type: ActionTypes.INIT()
    })

    return {
        dispatch,
        getState,
        subscribe
    }
}
  • combineReducers.js
import isPlainObject from "./utils/isPlainObject"
import ActionTypes from "./utils/ActionTypes"

function validateReducers(reducers) {
  if (typeof reducers !== "object") {
    throw new TypeError("reducers must be an object");
  }
  if (!isPlainObject(reducers)) {
    throw new TypeError("reducers must be a plain object");
  }
  //验证reducer的返回结果是不是undefined
  for (const key in reducers) {
    if (reducers.hasOwnProperty(key)) {
      const reducer = reducers[key];//拿到reducer
      //传递一个特殊的type值
      let state = reducer(undefined, {
        type: ActionTypes.INIT()
      })
      if (state === undefined) {
        throw new TypeError("reducers must not return undefined");
      }
      state = reducer(undefined, {
        type: ActionTypes.UNKNOWN()
      })
      if (state === undefined) {
        throw new TypeError("reducers must not return undefined");
      }
    }
  }
}

export default function (reducers) {
  //1. 验证
  validateReducers(reducers);
  /**
   * 返回的是一个reducer函数
   */
  return function (state = {}, action) {
    const newState = {}; //要返回的新的状态
    for (const key in reducers) {
      if (reducers.hasOwnProperty(key)) {
        const reducer = reducers[key];
        newState[key] = reducer(state[key], action);
      }
    }
    return newState; //返回状态
  }
}
  • bindActionCreators.js
export default function (actionCreators, dispatch) {
  if (typeof actionCreators === "function") {
    return getAutoDispatchActionCreator(actionCreators, dispatch);
  }
  else if (typeof actionCreators === "object") {
    const result = {}; //返回结果
    for (const key in actionCreators) {
      if (actionCreators.hasOwnProperty(key)) {
        const actionCreator = actionCreators[key]; //取出对应的属性值
        if (typeof actionCreator === "function") {
          result[key] = getAutoDispatchActionCreator(actionCreator, dispatch);
        }
      }
    }
    return result;
  }
  else {
    throw new TypeError("actionCreators must be an object or function which means action creator")
  }
}

/**
 * 得到一个自动分发的action创建函数
 */
function getAutoDispatchActionCreator(actionCreator, dispatch) {
  return function (...args) {
    const action = actionCreator(...args)
    dispatch(action);
  }
}
  • compose.js
export default function compose(...funcs) {
  if (funcs.length === 0) {
    return args => args; //如果没有要组合的函数,则返回的函数原封不动的返回参数
  }
  else if (funcs.length === 1) {
    //要组合的函数只有一个
    return funcs[0];
  }

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

  // return function (...args) {
  //     let lastReturn = null; //记录上一个函数返回的值,它将作为下一个函数的参数
  //     for (let i = funcs.length - 1; i >= 0; i--) {
  //         const func = funcs[i];
  //         if (i === funcs.length - 1) {//数组最后一项
  //             lastReturn = func(...args)
  //         }
  //         else {
  //             lastReturn = func(lastReturn)
  //         }
  //     }
  //     return lastReturn;
  // }
}
  • applyMiddleware.js
import compose from "./compose"
/**
 * 注册中间件
 * @param  {...any} middlewares 所有的中间件
 */
export default function (...middlewares) {
  return function (createStore) { //给我创建仓库的函数
    //下面的函数用于创建仓库
    return function (reducer, defaultState) {
      //创建仓库
      const store = createStore(reducer, defaultState);
      let dispatch = () => { throw new Error("目前还不能使用dispatch") };
      const simpleStore = {
        getState: store.getState,
        dispatch: store.dispatch
      }
      //给dispatch赋值
      //根据中间件数组,得到一个dispatch创建函数的数组
      const dispatchProducers = middlewares.map(mid => mid(simpleStore));
      dispatch = compose(...dispatchProducers)(store.dispatch);
      return {
        ...store,
        dispatch
      }
    }
  }
}

React Hook学习笔记


Hook简介

  1. Hook专门用于增强函数组件的功能(Hook在类组件中是不能使用的),使之理论上可以成为类组件的替代品
  2. 官方强调:没有必要更改已经完成的类组件,官方目前没有计划取消类组件,只是鼓励使用函数组件
  3. Hook(钩子)本质上是一个函数(命名上总以use开头),该函数可以挂载任何功能
  4. Hook的种类:useState、useEffect、useContext、useReducer、自定义Hook

state Hook

  1. state Hook是一个在函数组件中使用的函数(useSatte),用于在函数组件中使用状态
  2. useState函数
  • 函数有一个参数,这个参数的值表示状态的默认值
  • 函数的返回值是一个数组,该数组一定包含两项:当前状态的值、改变状态的函数
  1. 一个函数组件中可以有多个状态,这种做法非常有利于横向切分关注点

  2. useState原理

  3. 注意细节

    1. useSatte最好写在函数的起始位置,便于阅读
    2. useState严禁出现在代码块(循环、判断)中
    3. useSate返回的函数(第二项),引用不变(节约内存空间)
    4. 使用函数改变数据,若数据和之前的数据完全相等(会使用Object.is比较),不会导致重新渲染,以达到优化效率的目的
    5. 使用函数改变数据,传入的值不会和原来的数据合并,而是直接替换
    6. 如果要实现强制刷新组件:
    • 类组件:使用this.forceUpdate()强制刷新
    • 函数组件:使用一个空对象的useState
    1. 如果某些状态之间没有必然的联系,应该分化为不同的状态,而不要合并成一个对象
    2. 和类组件一样,函数组件中改变状态可能是异步的(在DOM事件中,状态改变后不会立即更改,而是等事件运行完之后一起改变),多个状态变化会合并并以提高效率,此时,不能信任之前的状态,而应该使用回调函数的方式改变状态。

effect Hook

effect Hook:用于在函数组件中处理副作用

副作用

  1. ajax请求
  2. 计时器
  3. 其它异步操作
  4. 更改真实Dom对象
  5. 本地存储
  6. 其它会对外部产生影响的操作

useEffect函数,该函数接受一个函数作为参数,接受的函数就是需要进行副作用操作的函数

注意细节

  1. 副作用函数运行的时间点,是在页面完成真实的UI渲染之后。因为副作用的执行是异步的,并且不会阻塞浏览器
    1. 与类组件中componentDidMount和componentDidUpdata的区别:
    • componentDidMount和componentDidUpdata,更改了真实Dom,但是用户还没有看到UI就更新,是同步的
    • useEffect中的副作用函数,更改了真实Dom,并且用户已经看到了UI更新后再执行副作用函数,是异步的
  2. 每个函数组件中,可以多次使用useEffect,但不要放入判断或者循环等代码块中
  3. useEffect中的副作用函数,可以有返回值,返回值必须是一个函数,该函数叫清理函数
    1. 该函数运行时间点,在每次运行副作用函数之前
    2. 组件首次渲染不会执行
    3. 组件被销毁是一定执行
  4. useEffect函数,可以传递第二个参数
    1. 第二个参数是一个数组,数组中记录该副作用的依赖数据
    2. 当组件重新渲染后,只有依赖数据与上一次不一样时,才会执行副作用
    3. 当传递了依赖数据后,如果数据没有发生变化((例只传一个空数组):
    • 副作用函数仅在第一次渲染后执行
    • 清理函数仅在卸载组件后运行
  5. 副作用函数中,如果使用了函数上下文中的变量,则由于闭包的影响,会导致副作用函数中变量不会实时变化
  6. 副作用函数在每次注册时,会覆盖掉之前的副作用函数,因此,尽量保持副作用函数稳定,否则控制起来会比较复杂

自定义Hook

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.