>  기사  >  웹 프론트엔드  >  VueX의 소스 코드 내용에 대한 간략한 분석

VueX의 소스 코드 내용에 대한 간략한 분석

不言
不言원래의
2018-07-20 15:30:471828검색

Vuex는 Vue.js 애플리케이션용으로 특별히 개발된 상태 관리 패턴입니다. 많은 구성 요소와 복잡한 상호 작용이 포함된 단일 페이지 애플리케이션의 경우 Vuex는 구성 요소 간의 데이터 공유 및 수정을 용이하게 하는 편리하고 정확하며 예측 가능한 상태 관리 방법을 제공합니다.

파일 구조는 다음과 같습니다

  • /module/module

  • /plugins

  • helpers.js

  • index.esm.js

  • index.js

  • store.js

  • util.js

util.js

先从最简单的工具函数开始。

find函数

/**
 * Get the first item that pass the test
 * by second argument function
 *
 * @param {Array} list
 * @param {Function} f
 * @return {*}
 */
export function find (list, f) {
  return list.filter(f)[0]
}

find函数的测试用例

it('find', () => {
  const list = [33, 22, 112, 222, 43]
  expect(find(list, function (a) { return a % 2 === 0 })).toEqual(22)
})

解析:

  • 先用断言函数f过滤列表list,最后取过滤后列表的第一个元素。

deepCopy函数

/**
 * Deep copy the given object considering circular structure.
 * This function caches all nested objects and its copies.
 * If it detects circular structure, use cached copy to avoid infinite loop.
 *
 * @param {*} obj
 * @param {Array<Object>} cache
 * @return {*}
 */
export function deepCopy (obj, cache = []) {
  // just return if obj is immutable value
  if (obj === null || typeof obj !== &#39;object&#39;) {
    return obj
  }

  // if obj is hit, it is in circular structure
  const hit = find(cache, c => c.original === obj)
  if (hit) {
    return hit.copy
  }

  const copy = Array.isArray(obj) ? [] : {}
  // put the copy into cache at first
  // because we want to refer it in recursive deepCopy
  cache.push({
    original: obj,
    copy
  })

  Object.keys(obj).forEach(key => {
    copy[key] = deepCopy(obj[key], cache)
  })

  return copy
}

deepCopy的测试用例

  // 普通结构
  it(&#39;deepCopy: nornal structure&#39;, () => {
    const original = {
      a: 1,
      b: &#39;string&#39;,
      c: true,
      d: null,
      e: undefined
    }
    const copy = deepCopy(original)

    expect(copy).toEqual(original)
  })
  
  // 嵌套结构
  it(&#39;deepCopy: nested structure&#39;, () => {
    const original = {
      a: {
        b: 1,
        c: [2, 3, {
          d: 4
        }]
      }
    }
    const copy = deepCopy(original)

    expect(copy).toEqual(original)
  })
  
  // 循环引用结构
  it(&#39;deepCopy: circular structure&#39;, () => {
    const original = {
      a: 1
    }
    original.circular = original

    const copy = deepCopy(original)

    expect(copy).toEqual(original)
  })

解析:

  • 功能:支持循环引用的深克隆函数

  • 第一个if判断obj === null || typeof obj !== 'object'判断如果不是引用类型直接返回(基本类型是值拷贝),也是递归的一个出口。

  • 第二个判断hit是判断是不是循环引用,由于是循环引用,在cache中应该有缓存到一份拷贝,直接取cache的,避免再次重复拷贝一份。

  • 什么是循环引用看测试用例第三个original.circular = original,循环引用和被引用的内容是一样的,用缓存就是避免重复的克隆(内容一样)

  • original.circular是循环引用,original是被循环引用

  • 先把cope放到cache中,是在递归的时候,如果遇到循环引用,要确保cache中有一份被循环引用的copy,但是copy必须是引用类型。

  • 为什么cope必须是引用类型?循环引用保存的是引用不是内容(这时候还没拷贝完),在递归的时候并未完成拷贝,只有递归跑完了才完成拷贝,这样未来被循环引用的内容改变时(拷贝完),循环引用的内容同步改变

  • 所以const copy = Array.isArray(obj) ? [] : {}必须是引用类型。

  • 最后Object.keys可以遍历对象和数组的所有键名(只返回实例的属性,不包含原型链和Symbol),实现递归克隆。

  • 一共两个出口,一个是基本类型,另一个是循环引用。

forEachValue

/**
 * forEach for object
 */
export function forEachValue (obj, fn) {
  Object.keys(obj).forEach(key => fn(obj[key], key))
}

测试用例

  it(&#39;forEachValue&#39;, () => {
    let number = 1

    function plus (value, key) {
      number += value
    }
    const origin = {
      a: 1,
      b: 3
    }

    forEachValue(origin, plus)
    expect(number).toEqual(5)
  })

解析:

  • 一个遍历对象的函数(支持对象和数组)

  • fn(value, key)但是回调函数第一个参数是值,第二个参数是键值

isObject

export function isObject (obj) {
  return obj !== null && typeof obj === &#39;object&#39;
}

测试用例

  it(&#39;isObject&#39;, () => {
    expect(isObject(1)).toBe(false)
    expect(isObject(&#39;String&#39;)).toBe(false)
    expect(isObject(undefined)).toBe(false)
    expect(isObject({})).toBe(true)
    expect(isObject(null)).toBe(false)
    expect(isObject([])).toBe(true)
    expect(isObject(new Function())).toBe(false)
  })

解析:

  • 判断是不是对象,这里没有判断是不是原生对象,数组也是通过的。

  • 由于typeof null === 'object'要先判断是不是null

isPromise

export function isPromise (val) {
  return val && typeof val.then === &#39;function&#39;
}

测试用例

  it(&#39;isPromise&#39;, () => {
    const promise = new Promise(() => {}, () => {})
    expect(isPromise(1)).toBe(false)
    expect(isPromise(promise)).toBe(true)
    expect(isPromise(new Function())).toBe(false)
  })

解析:

  • 判断是不是Promise

  • 首先判断val不是undefined,然后才可以判断val.then,避免报错

  • 判断依据是val.then是不是函数

assert

export function assert (condition, msg) {
  if (!condition) throw new Error(`[vuex] ${msg}`)
}

测试用例:

  it(&#39;assert&#39;, () => {
    expect(assert.bind(null, false, &#39;Hello&#39;)).toThrowError(&#39;[vuex] Hello&#39;)
  })

解析:

  • 断言函数,断言不通过抛出一个自定义错误信息的Error

index.jsindex.esm.js

index.js

import { Store, install } from &#39;./store&#39;
import { mapState, mapMutations, mapGetters, mapActions, createNamespacedHelpers } from &#39;./helpers&#39;

export default {
  Store,
  install,
  version: &#39;__VERSION__&#39;,
  mapState,
  mapMutations,
  mapGetters,
  mapActions,
  createNamespacedHelpers
}

index.esm.js

import { Store, install } from &#39;./store&#39;
import { mapState, mapMutations, mapGetters, mapActions, createNamespacedHelpers } from &#39;./helpers&#39;

export default {
  Store,
  install,
  version: &#39;__VERSION__&#39;,
  mapState,
  mapMutations,
  mapGetters,
  mapActions,
  createNamespacedHelpers
}

export {
  Store,
  install,
  mapState,
  mapMutations,
  mapGetters,
  mapActions,
  createNamespacedHelpers
}

解析:

  • 区别就是index.esm.jsindex.js多了个一个导入模式

  • import Vuex, { mapState } from 'index.esm.js':有两种方式导入

  • import Vuex from 'index.js':只有一种方式导入

mixin.js

export default function (Vue) {
  const version = Number(Vue.version.split(&#39;.&#39;)[0])

  if (version >= 2) {
    Vue.mixin({ beforeCreate: vuexInit })
  } else {
    // override init and inject vuex init procedure
    // for 1.x backwards compatibility.
    const _init = Vue.prototype._init
    Vue.prototype._init = function (options = {}) {
      options.init = options.init
        ? [vuexInit].concat(options.init)
        : vuexInit
      _init.call(this, options)
    }
  }

  /**
   * Vuex init hook, injected into each instances init hooks list.
   */

  function vuexInit () {
    const options = this.$options
    // store injection
    if (options.store) {
      this.$store = typeof options.store === &#39;function&#39;
        ? options.store()
        : options.store
    } else if (options.parent && options.parent.$store) {
      this.$store = options.parent.$store
    }
  }
}

解析:

  • 为什么每个组件都拥有(store属性,也即每个组件都能拿到)store

  • Vue2直接用mixin和钩子函数beforeCreate,Vue1用外观(装饰者)模式重写Vue._init函数。

  • vuexInit

  • /plugins

    helpers.js 🎜🎜🎜 🎜index.esm.js🎜🎜🎜🎜index.js🎜🎜🎜🎜store.js🎜🎜🎜🎜util.js🎜🎜

util.js

🎜가장 간단한 유틸리티 기능으로 시작하세요. 🎜🎜🎜find function🎜🎜rrreee🎜find function의 테스트 사례🎜rrreee🎜분석: 🎜🎜🎜🎜먼저 assert function f를 사용하여 list list를 필터링하고 마지막으로 필터링된 목록의 첫 번째 요소입니다. 🎜🎜🎜🎜deepCopy 기능🎜🎜rrreee🎜deepCopy 테스트 사례🎜rrreee🎜분석: 🎜🎜🎜🎜기능: 순환 참조를 지원하는 딥 클로닝 기능🎜🎜🎜🎜첫 번째 if 판단 obj = == null || typeof obj !== 'object' 참조 유형이 아닌 경우 직접 반환되며(기본 유형은 값 복사) 재귀의 출구이기도 합니다.
🎜🎜🎜🎜두 번째 판단 hit은 순환 참조이므로 캐시에 캐시된 복사본이 있어야 합니다. 다시는 반복하지 않으려면 복사본을 만드세요. 🎜🎜🎜🎜순환 참조란 무엇인가요? 세 번째 테스트 사례 original.circular = 원본을 참조하세요. 순환 참조와 참조된 콘텐츠는 반복 복제를 피하기 위해 사용됩니다. 동일)🎜 🎜🎜🎜original.circular는 순환 참조, original은 순환 참조 🎜🎜🎜🎜먼저 cope에 넣어주세요 >캐시, 반복 시 순환 참조가 발생하는 경우 캐시에서 순환 참조되는 <code>사본이 있는지 확인하세요. 그러나 사본은 다음과 같아야 합니다. 참조 유형. 🎜🎜🎜🎜왜 cope가 참조 유형이어야 합니까? 순환 참조는 내용이 아닌 참조를 저장합니다(현재 복사가 완료되지 않았습니다). 이 경우 재귀가 완료된 후에만 복사가 완료됩니다. 앞으로 는 순환참조됩니다의 내용이 변경되면(복사가 완료되면) <code>순환참조의 내용도 동시에 변경됩니다🎜🎜🎜🎜so const copy = Array.isArray(obj) ? [] : {} code>는 참조 유형이어야 합니다. 🎜🎜🎜🎜마지막으로 <code>Object.keys는 객체 및 배열의 ​​모든 키 이름을 순회하여(프로토타입 체인 및 기호를 제외한 인스턴스의 속성만 반환) 재귀 복제를 구현할 수 있습니다. 🎜🎜🎜🎜출구는 총 2개 있는데, 하나는 기본형이고 다른 하나는 순환참조형입니다. 🎜🎜🎜🎜forEachValue🎜🎜rrreee🎜테스트 사례🎜rrreee🎜분석: 🎜🎜🎜🎜객체를 순회하는 함수(객체 및 배열 지원)🎜🎜🎜🎜fn(value, key) code>그러나 콜백 함수의 첫 번째 매개변수는 값이고 두 번째 매개변수는 키 값입니다🎜🎜🎜🎜isObject🎜🎜rrreee🎜Test case🎜rrreee🎜분석: 🎜🎜🎜🎜인지 확인 객체, 여기에는 아무것도 없습니다. 그것이 네이티브 객체인지 확인하기 위해 배열도 전달됩니다. 🎜🎜🎜🎜typeof null === 'object'는 먼저 null인지 여부를 확인해야 하기 때문입니다🎜🎜🎜🎜isPromise🎜🎜rrreee🎜테스트 사례🎜rrreee🎜분석: 🎜🎜🎜🎜 Promise🎜🎜🎜 🎜먼저 val이 정의되지 않은 것이 아니라고 판단한 다음 val.then을 판단하여 오류 보고를 방지할 수 있습니다🎜🎜🎜🎜판단의 기준은 val.then이 함수인지 여부입니다🎜🎜🎜🎜assert 🎜🎜rrreee🎜테스트 사례: 🎜rrreee🎜Analytic: 🎜🎜🎜🎜Assert 함수, 사용자 정의 오류 메시지를 발생시켜 주장하지 않음 Error🎜🎜<h4> <code>index.jsindex.esm.js code> 🎜index.js🎜rrreee🎜index.esm.js🎜rrreee🎜분석: 🎜🎜🎜🎜The 차이점은 index.esm.jsindex.js🎜🎜🎜🎜import Vuex, 'index.esm.js의 { mapState }보다 가져오기 모드가 하나 더 있다는 것입니다. ': 두 가지가 있습니다 가져오는 방법은 한 가지뿐입니다 🎜🎜🎜🎜'index.js'에서 Vuex를 가져옵니다: 가져오는 방법은 한 가지뿐입니다 🎜🎜mixin.jsrrreee🎜분석: 🎜🎜 🎜🎜모든 컴포넌트에 🎜(store 속성, 즉 모든 컴포넌트가 이를 얻을 수 있음)🎜store🎜🎜🎜🎜Vue2는 beforeCreate에 mixin 및 Hook 기능을 직접 사용합니다. , Vue1은 모양(데코레이터) 모드를 사용하여 Vue를 다시 작성합니다. 🎜🎜🎜🎜vuexInit는 컴포넌트🎜🎜🎜🎜🎜(옵션은 `new Vue(options)`의 옵션입니다.)🎜옵션이 있습니다. 가게🎜
  • beforeCreateVue의 순환 후크이므로 this는 현재 구성 요소 인스턴스를 가리킵니다. 따라서 this.$store스토어를 현재 컴포넌트에 직접 삽입할 수 있습니다beforeCreateVue的周期钩子,this指向当前组件实例,所以this.$store可以把store直接注入当前组件

  • 所有组件都是继承于一个全局Vue的,全局mixin组件周期钩子beforeCreate,这样每个组件都能自动注入store,也即每个组件都能直接通过$store拿到全局Vuenew Vue({ el: 'app', store, router })store


  • 相关推荐:

    谈谈我对vuex的理解

    모든 컴포넌트는 글로벌 Vue, 글로벌 믹스인 컴포넌트 사이클에서 상속됩니다. HookbeforeCreate, 그러면 각 구성 요소가 저장소에 자동으로 삽입될 수 있습니다. 즉, 각 구성 요소는 전역 Vuenew Vue({ el: 'app ', store, router } )스토어

    #🎜🎜##🎜🎜#관련 추천: #🎜🎜##🎜🎜#<a href="http://www.php.cn/js-tutorial-368383.html" target="_self"><code>Vuex에 대한 나의 이해 코드>#🎜🎜##🎜🎜##🎜🎜##🎜🎜#vuex(상태 관리)의 Vue.js#🎜🎜##🎜🎜#

    위 내용은 VueX의 소스 코드 내용에 대한 간략한 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

    성명:
    본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.