指标
- 把握源码学习办法
- vue初始化原理解剖
- 深刻了解数据响应式
配置环境
- 拷贝代码: git clone https://github.com/vuejs/vue.git
- npm install
- 装置打包工具rollup npm i -g rollup
- 批改package.json 中dev的打包配置”dev”: “rollup -w -c scripts/config.js –sourcemap –environment TARGET:web-full-dev”,
- 执行脚本 npm run dev
- 在examples文件夹中增加test文件
文件目录
vue
├──dist #公布目录
├──examples #范例,测试代码在这里
├──flow #试代码
├──packages #外围代码之外的独立库
├──scripts #构建脚本
├──src #源码
├──test #ts类型申明,下面flow
└──types #对flow的类型申明
src # 源码
├──compiler # 编辑器相干
├──core # 外围代码
├──────components # 通用组建如 keep-alive
├──────global-api # 全局API
├──────instance # 构建函数等
├──────observer # 响应式相干
├──────vdom # 虚构DOM相干
└──────platforms # 平台独特的代码 代码扩充
vue源码源码剖析-初始化流程
入口
dev脚本中 scripts/config.js 配置文件
web-full-dev 示意执行时的配置项
'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
},
入口文件
门路src/platforms/web/entry-runtime-with-compiler.js
首先是扩大$mount办法, 解决template或者是el选项
// 扩大$mount
const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (el?: string | Element,hydrating?: boolean): Component {el = el && query(el)
if (el === document.body || el === document.documentElement) {
process.env.NODE_ENV !== 'production' && warn(
`Do not mount Vue to <html> or <body> - mount to normal elements instead.`
)
return this
}
// 用户配置的选项
const options = this.$options
// 首先判断render是否存在
if (!options.render) {
// 获取template选项
let template = options.template
// 判断template是否存在 如果template不存在判断el是否存在
if (template) {
} else if (el) {
template = getOuterHTML(el)
}
if (template) {
// 获取到html模板字符之后 执行编译过程
}
}
// 执行挂载
return mount.call(this, el, hydrating)
}
Vue.prototype.$mount的起源
门路: src/platforms/web/runtime/index.js
// 实现了一个patch函数 次要用于初始化和更新
Vue.prototype.__patch__ = inBrowser ? patch : noop
// 实现了$mount
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
el = el && inBrowser ? query(el) : undefined
// 定义$mount 挂载组件, 将vnode转化为node
return mountComponent(this, el, hydrating)
}
查找vue
门路: src/core/index.js
// 初始化全局api 比方component/ fitter/ directive/ use/ mixin/ util/ extend
initGlobalAPI(Vue)
查找vue引入
门路: src/core/instance/index.js
申明了Vue构造函数
function Vue (options) {
if (process.env.NODE_ENV !== 'production' &&
!(this instanceof Vue)
) {
warn('Vue is a constructor and should be called with the `new` keyword')
}
// 初始化办法
this._init(options)
}
// 实例属性 实例办法
initMixin(Vue) // 混入了_init()办法
stateMixin(Vue) // $data/$props/ $set() $delete() $watch()
eventsMixin(Vue) // $emit() / $on() $off() $once() 事件相干办法
lifecycleMixin(Vue) // _update / $forceUpdate/ $destory 生命周期相干办法
renderMixin(Vue) // $nextTick()/ _render() // 渲染相干
initMixin办法
门路: src/core/instance/init.js
// 实现一个_init办法 _init的目标是供外部应用
Vue.prototype._init = function (options?: Object) {
//做选项合并 将传入的选项和vue的默认选项进行合并
if (options && options._isComponent) {
} else {
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
}
// 初始化外围代码
vm._self = vm
initLifecycle(vm) // $parent $root $children $refs
initEvents(vm) // 自定义事件监听
initRender(vm) // $slots/ $createElement 定义$attrs 和$listeners响应式
// 下面办法在beforeCreate之前, 进行初始筹备
callHook(vm, 'beforeCreate') // 派发beforeCreate, 在beforeCreate中能够拜访下面三个内容
initInjections(vm) // resolve injections before data/props
initState(vm) // props/ methods/ data/ computed /watch 数据初始化
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created')
// 当设置了el选项时, 主动调用$mount
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
整体流程:
new Vue() => 创立vue实例
_init() => 初始化数据, 属性, 事件
$mount() => 执行挂载, 将虚构dom转化成实在dom
mountComponent()=> 将虚构dom转化成实在dom
new Watcher() => 创立组件渲染watcher
updateComponent() => 执行初始化或更新
_update() => 初始化或更新, 将虚构dom转化成实在dom
_render() 渲染组件,获取vdom~~~~
Vue源码剖析 数据响应式
vue2实现双向绑定,应用了js对象的Object.defineProperty()对data的setter/getter办法进行拦挡, 联合公布订阅模式,在getter中进行订阅.在setter中进行公布告诉,让所有订阅者实现响应
具体实现是在初始化Vue时, 会调用initState(vm)办法
initState(vm)
门路: src/core/instance/state.js
export function initState (vm: Component) {.....
// 判断data是否存在
if (opts.data) { //如果data存在,走这里,因为要看数据的双向绑定,所以找到initData
initData(vm)
} else {
observe(vm._data = {}, true /* asRootData */)
}
}
initData(vm)
function initData (vm: Component) {....
// 传进来的data可能是函数,或者对象,目标是避免数据净化;
let data = vm.$options.data
data = vm._data = typeof data === 'function'
? getData(data, vm)
: data || {}
....
//代理、反复判断
const keys = Object.keys(data)
while (i--) {....
//属性和办法不能是反复的判断
if (process.env.NODE_ENV !== 'production') {}
if (props && hasOwn(props, key)) {}
}
// 对data数据响应式解决
observe(data, true /* asRootData */)
}
对data数据响应式解决
门路: src/core/observer/index.js
export function observe (value: any, asRootData: ?boolean): Observer | void {....
// 为传进来的对象value,创立一个observer实例;任何一个对象都随同一个observer实例
// 返回一个observer实例
// 用observer实例,来判断类型 以及外部响应式解决
// 如果做过响应式,那么_ob_ 存在,就间接返回
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
ob = value.__ob__
} else if (...){...}else{...
// 如果未解决,就创立一个新的实例 ob实例
ob = new Observer(value)
}
}
new Observer(value)
门路: src/core/observer/index.js
export class Observer {
constructor (value: any) {
// 能够了解为小管家:比方在data中的数据是对象包对象的存在, data:{obj:{foo:foo}},对对象来说,是对对象新 增和删除的告诉工作;由小管家来告诉的。
// 当须要批改obj外面的属性,是在肚子里产生的事件,所以须要小管家来进行解决
// 如果obj是一个数组,选项的删除也是须要小管家
// 对象属性变动或者数组元素变动须要小管家告诉更新
this.dep = new Dep() //小管家
//判断传入的value类型,做相应的解决
if (Array.isArray(value)) { //当是Array时的解决
//笼罩数组实例的原型
}else{
//对象的解决
this.walk(value)
}
}
walk (obj: Object) {....
const keys = Object.keys(obj)
//遍历以后的所有keys
for (let i = 0; i < keys.length; i++) {
//对每个key进行拦挡
defineReactive(obj, keys[i])
}
}
}
defineReactive
//做递归解决
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
// 依赖收集
const value = getter ? getter.call(obj) : val
// Dep.target 能够了解为watcher的实例,每次触发watcher实例的时候,会手动触发,get事件一下获取以后 值,将实例放在Dep.target上;
if (Dep.target) {
// dep和watcher是n对n的关系,除了页面渲染{{name}}会触发watcher以外;还有可能是computed 或者w atch触发的事件监听;
// 双向增加两者关系
dep.depend()
// 若存在子ob
if (childOb) {
// 把以后的watcher和子ob中的dep建设关系
childOb.dep.depend()
if (Array.isArray(value)) {
dependArray(value)
}
}
}
return value
},
set: function reactiveSetter (newVal) {}
}
}
dep.depend
门路:src/core/observer/dep.js
depend () {
if (Dep.target) {
//执行的是以后watcher的addDep
Dep.target.addDep(this)
}
}
Dep.target.addDep
找到watcher的addDep事件
门路:src/core/observer/watcher.js
addDep (dep: Dep) { //dep和wacter互相保留的关系
const id = dep.id
if (!this.newDepIds.has(id)) { // 是否和以后的dep建设关系
// 没有就创立dep和watcher的关系
this.newDepIds.add(id)
this.newDeps.push(dep)
if (!this.depIds.has(id)) {
// 创立dep和watcher
dep.addSub(this)
}
}
}
dep.addSub
src/core/observer/dep.js
export default class Dep {
//往本人的sub 外面去push
addSub (sub: Watcher) {
this.subs.push(sub)
}
}
发表回复