每个组件都有一个自己的 渲染Watcher ,数据变化后就会通知对应组件的 渲染Watcher ,然后调用组件自己的 updateComponent ,再调用 _update 方法进行更新。
_update 方法时,没有 prevVnode 旧的虚拟节点,所以把传进来的 vnode 存储起来_update 方法时,就可以把传进来的 vnode 和上次的 prevVnode 做新旧虚拟节点的比较了
我们在定义全局组件或局部组件时,其实内部都调用了一个叫做 Vue.extend 的 API。

Vue.component('custom-component', {
template: `<button>add</button>`
});
// 等价于
Vue.component('custom-component', Vue.extend({
template: `<button>add</button>`
}));
new Vue({
el: '#app',
data() {},
components: {
'custom-component': {
template: `<button>add</button>`
}
}
});
// 等价于
new Vue({
el: '#app',
data() {},
components: {
'custom-component': Vue.extend({
template: `<button>add</button>`
})
}
});
所以我们声明一个组件,其实就是创建了一个 Vue 的子类。
继承父类,接收 options 选项,最后返回一个子类构造函数。
options 和全局 Vue.options 合并
添加 components 策略,在组件对象上的 components 原型上添加全局 components,这样组件在自身没找到组件后就会去全局找

这里引申出一个面试题,如标题。一旦 data 是个对象而不是个函数,就会导致在声明组件子类构造函数时传入的 options 中的数据源 {data:数据源} 是同一份引用,当同一个组件使用多次并修改数据源时就会相互影响。但如果是个函数就不一样了,每次使用组件都是在 new 一个新实例,里边的 data 现在不是个对象而是函数返回的新对象,就不会出现修改同一份数据源的问题了。
把用户的组件包装成构造函数挂载到 Vue.options.components 上去,并且判断用户传入的是个 Vue.extend 函数还是对象:
