Browse Source

[2.0] Support circular structure for logger (#328)

* support circular structure for logger

* move utility functions in index.js to utils.js
katashin 8 năm trước cách đây
mục cha
commit
cdd09b5966
5 tập tin đã thay đổi với 107 bổ sung15 xóa
  1. 1 12
      src/index.js
  2. 4 2
      src/plugins/logger.js
  3. 59 0
      src/util.js
  4. 2 1
      test/unit/jasmine.json
  5. 41 0
      test/unit/util.js

+ 1 - 12
src/index.js

@@ -1,6 +1,7 @@
 import devtoolPlugin from './plugins/devtool'
 import applyMixin from './mixin'
 import { mapState, mapMutations, mapGetters, mapActions } from './helpers'
+import { isObject, isPromise, assert } from './util'
 
 let Vue // bind on install
 
@@ -157,10 +158,6 @@ class Store {
   }
 }
 
-function assert (condition, msg) {
-  if (!condition) throw new Error(`[vuex] ${msg}`)
-}
-
 function updateModule (targetModule, newModule) {
   if (newModule.actions) {
     targetModule.actions = newModule.actions
@@ -339,14 +336,6 @@ function enableStrictMode (store) {
   }, { deep: true, sync: true })
 }
 
-function isObject (obj) {
-  return obj !== null && typeof obj === 'object'
-}
-
-function isPromise (val) {
-  return val && typeof val.then === 'function'
-}
-
 function getNestedState (state, path) {
   return path.length
     ? path.reduce((state, key) => state[key], state)

+ 4 - 2
src/plugins/logger.js

@@ -1,18 +1,20 @@
 // Credits: borrowed code from fcomb/redux-logger
 
+import { deepCopy } from '../util'
+
 export default function createLogger ({
   collapsed = true,
   transformer = state => state,
   mutationTransformer = mut => mut
 } = {}) {
   return store => {
-    let prevState = JSON.parse(JSON.stringify(store.state))
+    let prevState = deepCopy(store.state)
 
     store.subscribe((mutation, state) => {
       if (typeof console === 'undefined') {
         return
       }
-      const nextState = JSON.parse(JSON.stringify(state))
+      const nextState = deepCopy(state)
       const time = new Date()
       const formattedTime = ` @ ${pad(time.getHours(), 2)}:${pad(time.getMinutes(), 2)}:${pad(time.getSeconds(), 2)}.${pad(time.getMilliseconds(), 3)}`
       const formattedMutation = mutationTransformer(mutation)

+ 59 - 0
src/util.js

@@ -0,0 +1,59 @@
+/**
+ * Get the first item that pass the test
+ * by second argument function
+ *
+ * @param {Array} list
+ * @param {Function} f
+ * @return {*}
+ */
+function find (list, f) {
+  return list.filter(f)[0]
+}
+
+/**
+ * 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 !== 'object') {
+    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
+}
+
+export function isObject (obj) {
+  return obj !== null && typeof obj === 'object'
+}
+
+export function isPromise (val) {
+  return val && typeof val.then === 'function'
+}
+
+export function assert (condition, msg) {
+  if (!condition) throw new Error(`[vuex] ${msg}`)
+}

+ 2 - 1
test/unit/jasmine.json

@@ -1,7 +1,8 @@
 {
   "spec_dir": "test/unit",
   "spec_files": [
-    "test.js"
+    "test.js",
+    "util.js"
   ],
   "helpers": [
     "../../node_modules/babel-register/lib/node.js"

+ 41 - 0
test/unit/util.js

@@ -0,0 +1,41 @@
+import { deepCopy } from '../../src/util'
+
+describe('util', () => {
+  it('deepCopy: nornal structure', () => {
+    const original = {
+      a: 1,
+      b: 'string',
+      c: true,
+      d: null,
+      e: undefined
+    }
+    const copy = deepCopy(original)
+
+    expect(copy).toEqual(original)
+  })
+
+  it('deepCopy: nested structure', () => {
+    const original = {
+      a: {
+        b: 1,
+        c: [2, 3, {
+          d: 4
+        }]
+      }
+    }
+    const copy = deepCopy(original)
+
+    expect(copy).toEqual(original)
+  })
+
+  it('deepCopy: circular structure', () => {
+    const original = {
+      a: 1
+    }
+    original.circular = original
+
+    const copy = deepCopy(original)
+
+    expect(copy).toEqual(original)
+  })
+})