官方文档 - 对不同构建版本的解释 dist\README.md https://github.com/vuejs/vue/tree/dev/dist
提示:阅读源码时vscode默认会自动检测js语法,在vscode设置中改为false,不校验"javascript.validate.enable": false,
从package.json文件的启动命令可以找到配置文件,script/config.js,继续变可找到入口配置
// Runtime+compiler development build (Browser)
'web-full-dev': {
entry: resolve('web/entry-runtime-with-compiler.js'), dest: resolve('dist/vue.js'),
format: 'umd',
env: 'development',
alias: {
he: './entity-decoder' },
banner
},
则dev模式的入口文件为 src/platform/web/entry-runtime-with-compiler.js
src/platforms/web/entry-runtime-with-compiler.js
const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
// 获取document元素
el = el && query(el)
/* istanbul ignore if */ // el元素不能是body和html
if (el === document.body || el === document.documentElement) {
process.env.NODE_ENV !== 'production' && warn(
`Do not mount Vue to or - mount to normal elements instead.`
)
return this
}
const options = this.$options
// resolve template/el and convert to render function
// 如果没有render函数就处理template模板,将template编译为render函数
if (!options.render) {
...
}
return mount.call(this, el, hydrating)
}
这里根据import 引入一直查找,可找到四个导出Vue相关的模块儿
初始化 Vue 的静态方法 src/core/global-api/index.js
// 注册 Vue 的静态属性/方法
initGlobalAPI(Vue)
// src/core/global-api/index.js
// 初始化 Vue.config 对象
Object.defineProperty(Vue, 'config', configDef)
// exposed util methods.
// NOTE: these are not considered part of the public API - avoid relying on // them unless you are aware of the risk.
// 这些工具方法不视作全局API的一部分,除非你已经意识到某些风险,否则不要去依赖他们
Vue.util = {
warn,
extend,
mergeOptions,
defineReactive
}
// 静态方法 set/delete/nextTick
Vue.set = set
Vue.delete = del
Vue.nextTick = nextTick
// 2.6 explicit observable API
// 让一个对象可响应
Vue.observable = <T>(obj: T): T => {
observe(obj)
return obj
}
// 初始化 Vue.options 对象,并给其扩展
// components/directives/filters/_base
Vue.options = Object.create(null)
ASSET_TYPES.forEach(type => {
Vue.options[type + 's'] = Object.create(null) })
// this is used to identify the "base" constructor to extend all plain- object
// components with in Weex's multi-instance scenarios.
Vue.options._base = Vue
// 设置 keep-alive 组件
extend(Vue.options.components, builtInComponents)
// 注册 Vue.use() 用来注册插件
initUse(Vue)
// 注册 Vue.mixin() 实现混入
initMixin(Vue)
// 注册 Vue.extend() 基于传入的 options 返回一个组件的构造函数
initExtend(Vue)
// 注册 Vue.directive()、 Vue.component()、Vue.filter()
initAssetRegisters(Vue)
定义 Vue 的构造函数 src/core/instance/index.js
// 此处不用class的原因是因为之后方便给Vue实例混入实例成员
function Vue (options) {
if (process.env.NODE_ENV !== 'production' &&
!(this instanceof Vue)
// 判断是否使用new 方法来创建实例 instanceof 运算符用来检测 constructor.prototype 是否存在于参数 object 的原型链上。
) {
warn('Vue is a constructor and should be called with the `new` keyword')
}
// 调用 _init()方法
this._init(options)
}
// 注册 vm 实例的init方法初始化; 为实例注册方法
initMixin(Vue)
// 注册 vm 实例的状态方法,$data/$props/$set/$delete/$watch
stateMixin(Vue)
// 注册 vm 实例的事件方法,观察者模式,$on/$once/$off/$emit
eventsMixin(Vue)
// 注册 vm 实例的部分生命周期方法, _update/$forceUpdate/$destroy
lifecycleMixin(Vue)
// 注册 vm 实例的render函数和$nextTick函数
renderMixin(Vue)
初始化 _init() 方法 src/core/instance/init.js
// src\core\instance\init.js
export function initMixin (Vue: Class<Component>) {
// 给 Vue 实例增加 _init() 方法
// 合并 options / 初始化操作
Vue.prototype._init = function (options?: Object) {
// a flag to avoid this being observed // 如果是 Vue 实例不需要被 observe vm._isVue = true
// merge options
// 合并 options
if (options && options._isComponent) {
// optimize internal component instantiation
// since dynamic options merging is pretty slow, and none of the // internal component options needs special treatment. initInternalComponent(vm, options)
} else {
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor), options || {
},
vm
) }
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
initProxy(vm)
} else {
vm._renderProxy = vm
}
// expose real self
vm._self = vm
// vm 的生命周期相关变量初始化
// $children/$parent/$root/$refs
initLifecycle(vm)
// vm 的事件监听初始化, 父组件绑定在当前组件上的事件 initEvents(vm)
// vm 的编译render初始化
// $slots/$scopedSlots/_c/$createElement/$attrs/$listeners initRender(vm)
// beforeCreate 生命钩子的回调
callHook(vm, 'beforeCreate')
// 把 inject 的成员注入到 vm 上
initInjections(vm) // resolve injections before data/props // 初始化状态 vm 的 _props/methods/_data/computed/watch initState(vm)
// 初始化 provide
initProvide(vm) // resolve provide after data/props
// created 生命钩子的回调
callHook(vm, 'created')
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
vm._name = formatComponentName(vm, false) mark(endTag)
measure(`vue ${
vm._name} init`, startTag, endTag)
}
// 如果没有提供 el,调用 $mount() 挂载 if (vm.$options.el) {
vm.$mount(vm.$options.el) }
} }
一些总结
1、Vue的observe方法,将数据变为响应式,就是给对象添加get、set方法,set方法中触发此属性的发布者后通知观察者,观察者再去做相应变化,get方法中,判断此属性是否有观察者添加,有就添加。
2、在Vue中当数据被调用,或者被改变时都会调用 watcher 的 get 方法,在get方法中就会触发Dep的target属性去将watcher添加到次数据的Dep的观察者列表中,使得dep可以在数据改变时触发watcher变化。如何添加观察者的核心。
3、这里有两个get,watcher的get去触发触发数据的get,在数据的get中添加观察者。
4、watcher的get是怎么被调用的呢,实例化时在watcher的构造函数中就会调用get方法;也就是new watcher的地方就会调用。
渲染Watcher,将渲染的函数独立放出 在 Watcher 的get中去调用函数,其中渲染函数会调用用到变量的get,此时就可以给对应的属性添加Watcher,到对应的Dep中,切注册updata为该渲染函数,则当其中任意一个变量变化都会执行该渲染函数去渲染。
new Watcher(vm, updateComponent, noop, {
before () {
if (vm._isMounted && !vm._isDestroyed) {
callHook(vm, 'beforeUpdate')
}
}
}, true /* isRenderWatcher */)
Vue中修补了,原生数组中可以修改数组的方法,为其增加了响应式监听,所以在使用这些方法改变数组的方法时,就可更新视图
方法有
const methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
所以在vue中
var vm = new Vue({
el:'#app',
data: {
arr:[1,2,3]
}
})
vm.arr.push(8); //可以更新视图
vm.arr[0] = 100; //不会更新视图
vm.arr.length = 0; //不会更新视图
位置 src\core\observer\dep.js
疑问记录,给子对象收集依赖过程; 已解决
注意: 对象不能是 Vue 实例,或者 Vue 实例的根数据对象。 示例
注意: 目标对象不能是一个 Vue 实例或 Vue 实例的根数据对象。
vm.$watch( expOrFn, callback, [options] )
功能:
观察 Vue 实例变化的一个表达式或计算属性函数。回调函数得到的参数为新值和旧值。表达式只 接受监督的键路径。对于更复杂的表达式,用一个函数取代。
参数:
查看渲染 watcher 的执行过程
在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
利用了Promise在微任务中的立即执行,既此时正好是DOM更新完成,还没有渲染到浏览器上,执行回调,回调完成后,去将DOM渲染到浏览器。
Promise的兼容问题,Vue是优先使用Promise,然后是MutationObserver,都是微任务,如果都不支持,最后就是setTimeout,添加宏任务。