Repository: vuejs/vue
Pull request: make vm.$watch api consistent with watch option(#5604)

#5604 

<!--
Please make sure to read the Pull Request Guidelines:
https://github.com/vuejs/vue/blob/dev/.github/CONTRIBUTING.md#pull-request-guidelines
-->

<!-- PULL REQUEST TEMPLATE -->
<!-- (Update "[ ]" to "[x]" to check a box) -->

**What kind of change does this PR introduce?** (check at least one)

- [ ] Bugfix
- [x] Feature
- [ ] Code style update
- [ ] Refactor
- [ ] Build-related changes
- [ ] Other, please describe:

**Does this PR introduce a breaking change?** (check one)

- [ ] Yes
- [x] No

If yes, please describe the impact and migration path for existing applications:

**The PR fulfills these requirements:**

- [x] It's submitted to the `dev` branch for v2.x (or to a previous version branch), _not_ the `master` branch
- [x] When resolving a specific issue, it's referenced in the PR's title (e.g. `fix #xxx[,#xxx]`, where "xxx" is the issue number)
- [x] All tests are passing: https://github.com/vuejs/vue/blob/dev/.github/CONTRIBUTING.md#development-setup
- [x] New/updated tests are included


Changed files:
filename: src/core/instance/state.js
patch: @@ -247,16 +247,20 @@ function initWatch (vm: Component, watch: Object) {
   }
 }
 
-function createWatcher (vm: Component, key: string, handler: any) {
-  let options
+function createWatcher (
+  vm: Component,
+  keyOrFn: string | Function,
+  handler: any,
+  options?: Object
+) {
   if (isPlainObject(handler)) {
     options = handler
     handler = handler.handler
   }
   if (typeof handler === 'string') {
     handler = vm[handler]
   }
-  vm.$watch(key, handler, options)
+  return vm.$watch(keyOrFn, handler, options)
 }
 
 export function stateMixin (Vue: Class<Component>) {
@@ -287,10 +291,13 @@ export function stateMixin (Vue: Class<Component>) {
 
   Vue.prototype.$watch = function (
     expOrFn: string | Function,
-    cb: Function,
+    cb: any,
     options?: Object
   ): Function {
     const vm: Component = this
+    if (isPlainObject(cb)) {
+      return createWatcher(vm, expOrFn, cb, options)
+    }
     options = options || {}
     options.user = true
     const watcher = new Watcher(vm, expOrFn, cb, options)

filename: test/unit/features/instance/methods-data.spec.js
patch: @@ -21,14 +21,17 @@ describe('Instance methods data', () => {
   describe('$watch', () => {
     let vm, spy
     beforeEach(() => {
+      spy = jasmine.createSpy('watch')
       vm = new Vue({
         data: {
           a: {
             b: 1
           }
+        },
+        methods: {
+          foo: spy
         }
       })
-      spy = jasmine.createSpy('watch')
     })
 
     it('basic usage', done => {
@@ -81,6 +84,30 @@ describe('Instance methods data', () => {
       }).then(done)
     })
 
+    it('handler option', done => {
+      var oldA = vm.a
+      vm.$watch('a', {
+        handler: spy,
+        deep: true
+      })
+      vm.a.b = 2
+      waitForUpdate(() => {
+        expect(spy).toHaveBeenCalledWith(oldA, oldA)
+        vm.a = { b: 3 }
+      }).then(() => {
+        expect(spy).toHaveBeenCalledWith(vm.a, oldA)
+      }).then(done)
+    })
+
+    it('handler option in string', () => {
+      vm.$watch('a.b', {
+        handler: 'foo',
+        immediate: true
+      })
+      expect(spy.calls.count()).toBe(1)
+      expect(spy).toHaveBeenCalledWith(1)
+    })
+
     it('warn expresssion', () => {
       vm.$watch('a + b', spy)
       expect('Watcher only accepts simple dot-delimited paths').toHaveBeenWarned()