diff --git a/src/decorator/utils/getStateLifeDecorator.js b/src/decorator/utils/getStateLifeDecorator.js
index df84426..30ad323 100644
--- a/src/decorator/utils/getStateLifeDecorator.js
+++ b/src/decorator/utils/getStateLifeDecorator.js
@@ -11,18 +11,19 @@ import {
   observable,
   isObservableArray
 } from 'mobx'
+import logger from '../../utils/logger'
 
 export const assignState = action(
   function (self, property, val) {
     const setVal = (value = val) => {
       if (self[property] !== value) {
-        // console.log(name + ' set', property, value)
+        logger.debug(name + ' set', property, value)
         self[property] = value
       }
     }
 
     if (typeof val !== 'undefined') {
-      // console.log('before load url: `' + property + '`:', this[property]);
+      logger.debug('before load `' + property + '`:', self[property])
       if (val == null) {
         setVal()
       }
@@ -31,7 +32,9 @@ export const assignState = action(
           // remove overflow items if arrays
           if (
             Array.isArray(val) &&
-            (isObservableArray(self[property]) || Array.isArray(self[property]))
+            (
+              isObservableArray(self[property]) || Array.isArray(self[property])
+            )
             && val.length < self[property].length
           ) {
             self[property].splice(val.length, self[property].length - val.length)
@@ -58,13 +61,34 @@ export const assignState = action(
         setVal()
       }
 
-      // console.log('after loaded url: `' + property + '`:', this[property]);
+      logger.debug('after load: `' + property + '`:', self[property])
     }
   }
 )
 
+// const g = {}
+function extendsHideProps(target, propKey, value) {
+  const old = target[propKey] && target[propKey]
+  if (typeof old === 'object' && old !== null) {
+    // only saved on top parent
+    if (!Array.isArray(old)) {
+      Object.assign(old, value)
+    }
+    else {
+      old.push(value)
+    }
+    return
+  }
+  Object.defineProperty(target, propKey, {
+    value,
+    configurable: true,
+    enumerable: false
+  })
+}
+
 export default (config = {}, name = 'state-life') => {
   config = config || {}
+  // const collection = g[name] = g[name] || {}
 
   return (urlKey, options = {}, target, property, descriptor) => {
     if (typeof urlKey !== 'string') {
@@ -73,112 +97,143 @@ export default (config = {}, name = 'state-life') => {
     }
     options = options || {}
     const { initKey = 'init', exitKey = 'exit', updateKey } = options
-    const assignStateValue = function () {
-      return assignState.call(null, this, property, config.get(urlKey))
+    const assignStateValue = function (self, property, urlKey) {
+      return assignState(self, property, config.get(urlKey))
     }
 
     if ('value' in descriptor && typeof descriptor.value === 'function') {
       throw new Error('`' + name + '` can NOT use in member method')
     }
 
-    // maybe observable
-    // if ('get' in descriptor) {
-    //   throw new Error('`' + name + '` can NOT use in getter')
-    // }
     if ('initializer' in descriptor) {
-      console.warn('`' + property + '`' + 'is unobservable,', name, 'would may it to be observable.')
+      logger.warn('`' + property + '`' + 'is unobservable,', name, 'would make it to be observable.')
       descriptor = observable(target, property, descriptor)
     }
-    // https://github.com/mobxjs/mobx/issues/1382
-    let dispose
-    let syncUrlTimer
-    let syncUrlFn
-
-    // eslint-disable-next-line no-inner-declarations
-    function release() {
-      dispose && dispose()
-      dispose = null
-      if (syncUrlTimer) {
-        clearTimeout(syncUrlTimer)
-        syncUrlFn && syncUrlFn()
-        syncUrlTimer = void 0
-        syncUrlFn = void 0
+    const hidePropKey = `__[[${name}_origin_hooks]]__`
+    const hideArrPropKey = `__[[${name}_array]]__`
+    // Firstly!
+    // Supports inheritance
+    if (!target[hidePropKey] || !target[hidePropKey][property]) {
+      const hooks = {
+        init: target[initKey],
+        update: target[updateKey],
+        exit: target[exitKey]
       }
+      extendsHideProps(target, hidePropKey, {
+        [property]: hooks
+      })
     }
 
-    let originExit = target[exitKey]
-    target[exitKey] = function (...args) {
-      config.exit && config.exit(this, property, urlKey)
-      // console.log('dispose ' + name + ' `' + property + '`')
-      release()
-      return originExit && originExit.call(this, ...args)
+    if (!target[hideArrPropKey]) {
+      extendsHideProps(target, hideArrPropKey, [])
     }
-
-    // eslint-disable-next-line no-use-before-define
-    target[initKey] = init(target[initKey], 'init')
-
-    if (updateKey) {
-      target[updateKey] = (
-        function (origin) {
-          return action(function (...args) {
-            assignStateValue.call(this)
-            return origin && origin.call(this, ...args)
-          })
-        }
-      )(target[updateKey])
+    let i = target[hideArrPropKey].findIndex(([p]) => p === property)
+    if (i >= 0) {
+      target[hideArrPropKey].splice(i, 1)
     }
+    const func = (
+      function () {
+        let dispose
+        let syncUrlTimer
+        let syncUrlFn
+
+        // eslint-disable-next-line no-inner-declarations
+        function release() {
+          dispose && dispose()
+          dispose = null
+          if (syncUrlTimer) {
+            clearTimeout(syncUrlTimer)
+            syncUrlFn && syncUrlFn()
+            syncUrlTimer = void 0
+            syncUrlFn = void 0
+          }
+        }
 
-    // eslint-disable-next-line no-inner-declarations,no-unused-vars
-    function init(origin, actionType) {
-      return action(function (...args) {
-        config.init && config.init(this, property, urlKey)
-
-        // console.log(actionType + ' ' + name + ' `' + property + '`')
-        release()
-        assignStateValue.call(this)
-
-        let isFirst = true
-        dispose = autorun(
-          () => {
-            // 一段时间内的修改以最后一次为准
-            if (syncUrlTimer) {
-              clearTimeout(syncUrlTimer)
-              syncUrlTimer = void 0
-            }
-
-            let obj = { [urlKey]: this[property] }
-            // invoke the deep `getter` of this[property]
-            // noop op
-            try {
-              JSON.stringify(obj)
-            } catch (err) {
-              console.error('[Stringify]', obj, 'Error happened:', err)
-            }
+        return {
+          init: function () {
+            config.init && config.init(this, property, urlKey)
+            logger.debug('init ' + name + ' `' + property + '`')
+            release()
+            assignStateValue(this, property, urlKey)
+
+            let isFirst = true
+            dispose = autorun(() => {
+              // 一段时间内的修改以最后一次为准
+              if (syncUrlTimer) {
+                clearTimeout(syncUrlTimer)
+                syncUrlTimer = void 0
+              }
 
-            syncUrlFn = (isFirst = false) => {
-              let save = isFirst ? (
-                config.saveFirstTime || config.save
-              ) : config.save
-              save.call(config, urlKey, this[property], config.fetch())
-              syncUrlTimer = void 0
-              syncUrlFn = void 0
-            }
+              let obj = { [urlKey]: this[property] }
+              // invoke the deep `getter` of this[property]
+              // noop op
+              try {
+                JSON.stringify(obj)
+              } catch (err) {
+                console.error('[Stringify]', obj, 'Error happened:', err)
+              }
 
-            if (isFirst) {
-              if (options.initialWrite) {
-                syncUrlFn(true)
+              syncUrlFn = (isFirst = false) => {
+                let save = isFirst ? (
+                  config.saveFirstTime || config.save
+                ) : config.save
+                // console.log('save', urlKey, property, this[property])
+                save.call(config, urlKey, this[property], config.fetch())
+                syncUrlTimer = void 0
+                syncUrlFn = void 0
               }
-              isFirst = false
-              return
-            }
 
-            syncUrlTimer = setTimeout(syncUrlFn, 250)
+              if (isFirst) {
+                if (options.initialWrite) {
+                  syncUrlFn(true)
+                }
+                isFirst = false
+                return
+              }
+              syncUrlTimer = setTimeout(syncUrlFn, 250)
+            })
+          },
+          update: function () {
+            assignStateValue(this, property, urlKey)
+          },
+          exit: function () {
+            config.exit && config.exit(this, property, urlKey)
+            logger.debug('dispose ' + name + ' `' + property + '`')
+            release()
           }
-        )
+        }
+      }
+    )()
+    extendsHideProps(target, hideArrPropKey, [property, func])
+
+    const hooks = target[hidePropKey]
+    const arrays = target[hideArrPropKey]
+    const callHook = (self, hookName, args) => {
+      return typeof hooks[hookName] === 'function'
+             && hooks[hookName].apply(self, args)
+    }
 
-        return origin && origin.call(this, ...args)
+    target[initKey] = function (...args) {
+      arrays.forEach(([, { init }]) => {
+        init.call(this)
       })
+      return callHook(this, initKey, args)
     }
+    if (updateKey) {
+      target[updateKey] = function (...args) {
+        arrays.forEach(([, { update }]) => {
+          update.call(this)
+        })
+        return callHook(this, updateKey, args)
+      }
+    }
+    target[exitKey] = function (...args) {
+      arrays.forEach(([, { init }]) => {
+        init.call(this)
+      })
+      return callHook(this, exitKey, args)
+    }
+
     return descriptor && { ...descriptor, configurable: true }
   }
 }
diff --git a/src/utils/logger.js b/src/utils/logger.js
index e085d63..388f8a5 100644
--- a/src/utils/logger.js
+++ b/src/utils/logger.js
@@ -8,18 +8,14 @@
 
 export default {
   debug: function (...args) {
-    if (process.env.NODE_ENV !== 'production') {
-      console.log(...args)
+    if (global && global.VM_DEBUG || process.env.NODE_ENV !== 'production') {
+      console.log('[react-mobx-vm] Debug:',...args)
     }
   },
   warn: function (...args) {
-    if (process.env.NODE_ENV !== 'production') {
-      console.warn(...args)
-    }
+    console.warn('[react-mobx-vm] Warning:', ...args)
   },
   error: function (...args) {
-    if (process.env.NODE_ENV !== 'production') {
-      console.error(...args)
-    }
+    console.error('[react-mobx-vm] Error:',...args)
   }
 }
diff --git a/test/decorator-urlSync.test.js b/test/decorator-urlSync.test.js
index b56c9d3..8ddd478 100644
--- a/test/decorator-urlSync.test.js
+++ b/test/decorator-urlSync.test.js
@@ -13,6 +13,7 @@ import * as React from 'react'
 import ReactDOM from 'react-dom'
 import { hashHistory } from 'react-router'
 import { stringify as oStringify, parse as oParse } from 'qs'
+
 function stringify(obj) {
   return '?' + oStringify(obj)
 }
@@ -37,6 +38,7 @@ describe('decorator-urlSync', function () {
       return null
     }
   }
+
   @bindView(View)
   class App extends Root {
     @urlSync
@@ -55,7 +57,7 @@ describe('decorator-urlSync', function () {
     }
 
     @urlSync
-    @observable arr = [{ a: 'a' }, 'b'];
+    @observable arr = [{ a: 'a' }, 'b']
 
     @urlSync
     @observable
@@ -69,6 +71,11 @@ describe('decorator-urlSync', function () {
   beforeEach(() => {
     dom = document.createElement('div')
     registerUrlSync(hashHistory)
+    hashHistory.push({
+      path: '/',
+      search: '',
+      query: null
+    })
   })
   test('decorator-urlSync simple', async (done) => {
     class Simple extends App {
@@ -83,6 +90,7 @@ describe('decorator-urlSync', function () {
           )
       }
     }
+
     vm = Simple.create()
     let root = vm.root
     let arr = vm.arr
@@ -130,7 +138,6 @@ describe('decorator-urlSync', function () {
       })
     })
     await mockDelay()
-    await mockDelay()
     expect(
       JSON.stringify(vm.arr)
     ).toEqual(
@@ -148,20 +155,67 @@ describe('decorator-urlSync', function () {
   })
 
   // https://github.com/mobxjs/mobx/issues/1382
-  test('extends and observable', () => {
-    class P {
+  test('extends and observable', async (done) => {
+    class P extends App {
       @urlSync
       @observable a = 'x';
+      @urlSync
+      @observable b = 'y'
+
+      @urlSync('i')
+      @observable int = 23
+
+      @urlSync('pArr')
+      @observable arr = ['1', '2']
 
       @observable v = 'pv'
     }
+
     class S extends P {
       @urlSync('xx')
       @observable a = 's';
+      @urlSync('yy')
+      @observable b = 't'
+      @urlSync('ii')
+      @observable int = 222
+      @urlSync('s')
+      @observable str = 't'
 
       @observable v = 'sv'
     }
 
     expect(new S().v).toBe('sv')
+
+    // expect(
+    //   Object.keys(s['__[[urlsync_origin_hooks]]__'])
+    // ).toEqual(
+    //   ['str', 'num', 'int', 'obj', 'arr', 'root', 'a', 'b']
+    // )
+    vm = S.create({ a: 'abc', b: 'bbb' })
+    ReactDOM.render(
+      <RouterV3 history={hashHistory} routes={{ path: '/', component: vm }}/>,
+      dom
+    )
+    await mockDelay()
+    expect(
+      parse(hashHistory.getCurrentLocation().search)
+    ).toEqual({})
+
+    vm.a = 'update'
+    vm.b = 'updateB'
+    vm.str = 's'
+    vm.num = 234
+    vm.int = 22222
+    await mockDelay()
+    expect(
+      parse(hashHistory.getCurrentLocation().search)
+    ).toEqual({
+      ii: '22222',
+      num: '234',
+      s: 's',
+      xx: 'update',
+      yy: 'updateB'
+    })
+    done()
   })
 })