代码拉取完成,页面将自动刷新
Vue2.x,获取源码git地址:https://github.com/vuejs/vue
在vue-dev的package.json文件添加 --sourcemap,用于生成.map文件
"dev": "rollup -w -c scripts/config.js --environment TARGET:web-full-dev --sourcemap",
// "dev": "rollup -w -c scripts/config.js --sourcemap --environment TARGET:web-full-dev"
生成vue.js和vue.js.map文件
yarn install
yarn run dev
编辑器配置
{
"javascript.validate.enable": false,
}
辅助工具
https://vue-next-template-explorer.netlify.app/
https://template-explorer.vuejs.org/
https://astexplorer.net/
recast
https://www.npmjs.com/package/recast
simple-html-parser
https://www.npmjs.com/package/simple-html-parser
目录结构
src
├── compiler
├── core
├── platforms
│ ├── web
src\core\instance\index.js
// 定义Vue函数,即定义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')
}
/**
* 在new Vue({})时
* 即调用_init函数
* 该函数被定义时间
* initMixin(Vue)
*/
this._init(options)
}
/**
* 注册_init函数:
* this._init(options)
* 在new Vue({})时调用,初始化vm实例
* 处理实例对象数据
* src\core\instance\init.js
*/
initMixin(Vue)
/**
* 使用Object.defineProperty
* 特殊处理
* 在Vue.prototype上挂载以下函数
* $data、$props
* $set、$watch、$delete
*/
stateMixin(Vue)
/**
* 混入与事件相关的函数
* $on、$off、$once、$emit
* 采用发布-订阅的设计模式
*/
eventsMixin(Vue)
/**
* 混入与生命周期相关的函数
* _update、$forceUpdate、$destroy
* _update函数中调用vm.__patch__函数
* 即进行diff算法,比较新旧vdom节点,
* 并渲染成真实dom元素
* 在渲染视图时调用,包括首次渲染、数据更新时
*/
lifecycleMixin(Vue)
/**
* 通过installRenderHelpers(Vue.prototype)函数:
* 在Vue.prototype上定义许多编译时使用的辅助函数
* 混入与渲染相关的函数
* $nextTick、与Vue.nextTick对应
* _render:
* 函数体部分代码:
* const { render, _parentVnode } = vm.$options
* vnode = render.call(vm._renderProxy, vm.$createElement)
* 返回值:vnode
* vm.$createElement,即传入的h函数
* 在调用this._init(options)时给vm实例上添加的$createElement函数
* 即./init.js中调用initRender(vm)时添加
*/
renderMixin(Vue)
/**
* 以上函数,在Vue.prototype上挂在函数,即定义Vue实例方法
*/
export default Vue
src\core\index.js
/**
* 在Vue上挂载静态方法
* 例如以下方法:
* Vue.set、Vue.del、Vue.nextTick、Vue.observable
* Vue.extend、Vue.mixin、Vue.use、
* Vue.component、Vue.directive、Vue.filter
* 在Vue.options.components上挂载组件: keep-alive等
*/
initGlobalAPI(Vue)
// Object.defineProperty
Object.defineProperty(Vue.prototype, '$isServer', {
get: isServerRendering
})
Object.defineProperty(Vue.prototype, '$ssrContext', {
get () {
/* istanbul ignore next */
return this.$vnode && this.$vnode.ssrContext
}
})
// expose FunctionalRenderContext for ssr runtime helper installation
Object.defineProperty(Vue, 'FunctionalRenderContext', {
value: FunctionalRenderContext
})
src\platforms\web\runtime\index.js
// \platforms\目录,代表与平台相关的代码定义,即与web平台相关
// 该文件里主要定义与平台相关的方法
import { mountComponent } from 'core/instance/lifecycle'
// 在Vue.config上挂载与平台相关的方法
Vue.config.mustUseProp = mustUseProp
Vue.config.isReservedTag = isReservedTag
Vue.config.isReservedAttr = isReservedAttr
Vue.config.getTagNamespace = getTagNamespace
Vue.config.isUnknownElement = isUnknownElement
// 定义与web平台相关的自有指令、组件
extend(Vue.options.directives, platformDirectives) // 注册和平台相关的全局指令:v-model、v-show
extend(Vue.options.components, platformComponents) // 注册和平台相关的全局组件:v-transition、v-transition-group
// --install platform patch function
Vue.prototype.__patch__ = inBrowser ? patch : noop // 把虚拟DOM转换成真实 DOM
/**
* public mount method
* 定义$mount函数,挂载方法设置,挂载DOM到页面
* $mount函数是与平台相关函数
* 在this._init(options)中调用:
* vm.$mount(vm.$options.el)
* _init函数在new Vue({})时调用,
* 使用initMixin函数挂载到Vue.prototype上
*/
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
el = el && inBrowser ? query(el) : undefined
/**
* new Watcher(vm, updateComponent, noop, {})
* updateComponent是关键函数
* updateComponent = () => {vm._update(vm._render(), hydrating)}
* 通知视图更新
* 在lifecycleMixin(Vue)时定义vm._update函数
* 在renderMixin(Vue)时定义vm._render函数
* vm.$el = vm.__patch__(prevVnode, vnode)
*/
return mountComponent(this, el, hydrating)
}
src\platforms\web\entry-runtime-with-compiler.js
// 从该文件入口依次往上查找,引用关系: 4=>3=>2=>1
import { compileToFunctions } from './compiler/index'
// 重写$mount函数,判断是否传入render属性; 若没有传入,通过template生成render属性并在options上挂载
const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
el = el && query(el)
const options = this.$options
// resolve template/el and convert to render function
if (!options.render) {
let template = options.template
if (template) {
// 根据是否定义render属性,执行相应逻辑
} else if (el) {
template = getOuterHTML(el) // 通过传入el,将dom转化为template
}
if (template) {
/**
* 关键步骤、即获取render属性,并在options上挂载
* 在mount.call(this, el, hydrating)会使用options.render
* 在new Watcher(vm, updateComponent, noop, {})时调用,获取vnode节点
* 查看: app.$options.render app.$options.staticRenderFns
* app.$options.staticRenderFns存放静态根节点的生成函数,
* 在app.$options.render中使用下标获取相应函数,进行生成相应节点,如: _m(0)
* 参考src\core\instance\render-helpers\render-static.js -- this.$options.staticRenderFns[index]
* 使用缓存,减少静态根节点对应的vdom的创建次数
* 获取vnode: app.$options.render.call(app)
*/
const { render, staticRenderFns } = compileToFunctions(template, {
outputSourceRange: process.env.NODE_ENV !== 'production',
shouldDecodeNewlines,
shouldDecodeNewlinesForHref,
delimiters: options.delimiters,
comments: options.comments
}, this)
options.render = render
}
}
return mount.call(this, el, hydrating) // 执行web\runtime\index.js里定义的mount函数
}
/**
* 编译函数:
* compileToFunctions
* 返回值: render, staticRenderFns
* 挂载Vue.compile函数,
* 传递一个HTML字符串返回render函数
*/
Vue.compile = compileToFunctions
export default Vue
src\core\instance\state.js
export function initState (vm: Component) {
/**
* vm是Vue实例对象,可以通过自定义watch、computed观察vm._watchers数量
*/
vm._watchers = []
const opts = vm.$options
if (opts.props) initProps(vm, opts.props)
if (opts.methods) initMethods(vm, opts.methods)
if (opts.data) {
initData(vm)
} else {
observe(vm._data = {}, true /* asRootData */)
}
if (opts.computed) initComputed(vm, opts.computed)
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch)
}
}
function initData (vm: Component) {
// observe data - 响应式数据处理
observe(data, true /* asRootData */)
}
src\core\observer\index.js
export function observe (value: any, asRootData: ?boolean): Observer | void {
if (!isObject(value) || value instanceof VNode) {
return
}
let ob: Observer | void
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
ob = value.__ob__
} else if (
shouldObserve &&
!isServerRendering() &&
(Array.isArray(value) || isPlainObject(value)) &&
Object.isExtensible(value) &&
!value._isVue
) {
ob = new Observer(value) // 关键函数,数据响应式处理
}
if (asRootData && ob) {
ob.vmCount++
}
return ob
}
export class Observer {
value: any;
dep: Dep;
vmCount: number; // number of vms that have this object as root $data
constructor (value: any) {
this.value = value
// 在数据响应式处理前,已调用initRender(vm)函数,创建两个Dep实例,即id:0、1的两个实例
this.dep = new Dep()
this.vmCount = 0
def(value, '__ob__', this) // 挂载__ob__属性
if (Array.isArray(value)) {
if (hasProto) {
protoAugment(value, arrayMethods)
} else {
copyAugment(value, arrayMethods, arrayKeys)
}
this.observeArray(value)
} else {
this.walk(value)
}
}
}
export function defineReactive (
obj: Object,
key: string,
val: any,
customSetter?: ?Function,
shallow?: boolean
) {
const dep = new Dep()
const property = Object.getOwnPropertyDescriptor(obj, key)
if (property && property.configurable === false) {
return
}
// cater for pre-defined getter/setters
const getter = property && property.get
const setter = property && property.set
if ((!getter || setter) && arguments.length === 2) {
val = obj[key]
}
let childOb = !shallow && observe(val)
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
const value = getter ? getter.call(obj) : val
if (Dep.target) {
dep.depend()
if (childOb) {
childOb.dep.depend()
if (Array.isArray(value)) {
dependArray(value)
}
}
}
return value
},
set: function reactiveSetter (newVal) {
const value = getter ? getter.call(obj) : val
/* eslint-disable no-self-compare */
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
/* eslint-enable no-self-compare */
if (process.env.NODE_ENV !== 'production' && customSetter) {
customSetter()
}
// #7981: for accessor properties without setter
if (getter && !setter) return
if (setter) {
setter.call(obj, newVal)
} else {
val = newVal
}
childOb = !shallow && observe(newVal)
dep.notify()
}
})
}
export default class Dep {
depend () {
if (Dep.target) {
Dep.target.addDep(this)
}
}
}
// dep.notify() => watcher.update()
export default class Watcher {
update () {
/* istanbul ignore else */
if (this.lazy) {
this.dirty = true
} else if (this.sync) {
this.run()
} else {
queueWatcher(this)
}
}
}
src\platforms\web\entry-runtime-with-compiler.js
/**
* 关键步骤、即获取render属性,并在options上挂载
* 在mount.call(this, el, hydrating)会使用options.render
* 在new Watcher(vm, updateComponent, noop, {})时调用,获取vnode节点
*/
const { render, staticRenderFns } = compileToFunctions(template, {
outputSourceRange: process.env.NODE_ENV !== 'production',
shouldDecodeNewlines,
shouldDecodeNewlinesForHref,
delimiters: options.delimiters,
comments: options.comments
}, this)
options.render = render
/**
* src\platforms\web\runtime\index.js
* 执行Vue.prototype.$mount函数
*/
return mount.call(this, el, hydrating)
src\compiler\to-function.js
// 解析compileToFunctions函数,返回render, staticRenderFns
// src\compiler\to-function.js
/**
* compile
* 将模板字符串编译为对象
* 包含{ render, staticRenderFns }
*/
const compiled = compile(template, options)
// src\compiler\create-compiler.js
// 编译核心函数
const compiled = baseCompile(template.trim(), finalOptions)
// src\compiler\index.js
export const createCompiler = createCompilerCreator(function baseCompile (
template: string,
options: CompilerOptions
): CompiledResult {
// 编译template为ast抽象语法树
const ast = parse(template.trim(), options)
if (options.optimize !== false) {
/**
* 优化ast抽象语法树
* 1. 标记静态节点: 不包含动态内容的节点
* 2. 标记静态根节点: 不包含动态内容且含义子标签的节点
*/
optimize(ast, options)
}
/**
* 生成字符串形式的render函数
* const code = ast ? genElement(ast, state) : '_c("div")'
*/
const code = generate(ast, options)
return {
ast,
render: code.render,
staticRenderFns: code.staticRenderFns
}
})
// src\compiler\to-function.js
// 将字符串形式的代码转化成函数
res.render = createFunction(compiled.render, fnGenErrors)
res.staticRenderFns = compiled.staticRenderFns.map(code => {
return createFunction(code, fnGenErrors)
})
return (cache[key] = res)
src\core\instance\lifecycle.js
/**
* vm._render()
* 系开发者直接传入,或通过template属性编译生成
* 调用该函数可获取vnode节点
* 传入vm._update函数中,用于__patch__函数,比较新旧节点差异
*/
updateComponent = () => {
vm._update(vm._render(), hydrating)
}
// vm._render()
vnode = render.call(vm._renderProxy, vm.$createElement)
// 在调用getter函数时,完成依赖收集处理,以便在调用setter函数时,通知Watcher,调用其update函数更新视图
new Watcher(vm, updateComponent, noop, {
before () {
if (vm._isMounted && !vm._isDestroyed) {
callHook(vm, 'beforeUpdate')
}
}
}, true /* isRenderWatcher */)
src\core\vdom\patch.js
// patch => createElm => createComponent => createChildren
function patch (oldVnode, vnode, hydrating, removeOnly) {
if (isUndef(oldVnode)) {
/**
* empty mount (likely as component), create new root element
* 将组件实例化为真实dom节点,进入该判断
* 即.$mount()未传入参数,仅创建组件,没有挂载到页面上
*/
isInitialPatch = true
createElm(vnode, insertedVnodeQueue)
} else {
// 新旧节点都存在,进入该判断
const isRealElement = isDef(oldVnode.nodeType)
if (!isRealElement && sameVnode(oldVnode, vnode)) {
// patch existing root node -- 视图更新操作,执行diff算法
patchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, removeOnly)
} else {
// 判断是否是真实dom节点,即首次渲染,初始化,创建一个空节点
if (isRealElement) {
oldVnode = emptyNodeAt(oldVnode)
}
const oldElm = oldVnode.elm
const parentElm = nodeOps.parentNode(oldElm)
createElm(
vnode,
insertedVnodeQueue,
// extremely rare edge case: do not insert if old element is in a
// leaving transition. Only happens when combining transition +
// keep-alive + HOCs. (#4590)
oldElm._leaveCb ? null : parentElm,
nodeOps.nextSibling(oldElm)
)
}
}
// 创建节点
function createElm(
vnode,
insertedVnodeQueue,
parentElm,
refElm,
nested,
ownerArray,
index
) {
// 处理组件的情况
if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {
return
}
if (isDef(tag)) {
// 标签节点逻辑
vnode.elm = vnode.ns
? nodeOps.createElementNS(vnode.ns, tag)
: nodeOps.createElement(tag, vnode)
setScope(vnode)
if (__WEEX__) {
} else {
// 递归调用createElm函数,创建真实节点
createChildren(vnode, children, insertedVnodeQueue)
if (isDef(data)) {
invokeCreateHooks(vnode, insertedVnodeQueue)
}
// 子节点创建完毕后,依次插入元素,最后挂载到页面上
insert(parentElm, vnode.elm, refElm)
}
} else if (isTrue(vnode.isComment)) {
// 注释节点逻辑
vnode.elm = nodeOps.createComment(vnode.text)
// 插入元素,挂载到页面上
insert(parentElm, vnode.elm, refElm)
} else {
// 文本节点逻辑
vnode.elm = nodeOps.createTextNode(vnode.text)
// 插入元素,挂载到页面上
insert(parentElm, vnode.elm, refElm)
}
}
// createElement => _createElement => vnode = createComponent(Ctor, data, context, children, tag)
// 在createComponent函数中通过data.hooks初始化组件,进行挂载、new Watcher操作
function createComponent (vnode, insertedVnodeQueue, parentElm, refElm) {
let i = vnode.data
if (isDef(i)) {
const isReactivated = isDef(vnode.componentInstance) && i.keepAlive
if (isDef(i = i.hook) && isDef(i = i.init)) {
i(vnode, false /* hydrating */)
}
if (isDef(vnode.componentInstance)) {
initComponent(vnode, insertedVnodeQueue)
insert(parentElm, vnode.elm, refElm)
if (isTrue(isReactivated)) {
reactivateComponent(vnode, insertedVnodeQueue, parentElm, refElm)
}
return true
}
}
}
// 一个组件对应一个Watcher实例,Vue实例对应一个Watcher实例,即Vue2.x中是(n+1)个Watcher实例
// 通过以下vm.$createElement将hooks挂载到组件的data上
vnode = render.call(vm._renderProxy, vm.$createElement)
src\core\vdom\patch.js
function patchVnode(
oldVnode,
vnode,
insertedVnodeQueue,
ownerArray,
index,
removeOnly
) {
if (isUndef(vnode.text)) {
// 新节点中没有text属性,即对比子节点
if (isDef(oldCh) && isDef(ch)) {
// 新旧节点都有子节点,即updateChildren操作
if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly)
} else if (isDef(ch)) {
// 新节点有子节点,旧节点没有子节点
if (process.env.NODE_ENV !== 'production') {
checkDuplicateKeys(ch)
}
if (isDef(oldVnode.text)) nodeOps.setTextContent(elm, '')
addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue)
} else if (isDef(oldCh)) {
// 旧节点有子节点,新节点没有子节点
removeVnodes(oldCh, 0, oldCh.length - 1)
} else if (isDef(oldVnode.text)) {
// 旧节点有文本,新节点没有文本
nodeOps.setTextContent(elm, '')
}
} else if (oldVnode.text !== vnode.text) {
// 新节点中有text属性,更新text属性
nodeOps.setTextContent(elm, vnode.text)
}
}
function updateChildren(parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) {
let oldStartIdx = 0
let newStartIdx = 0
let oldStartVnode = oldCh[0]
let oldEndVnode = oldCh[oldEndIdx]
let newStartVnode = newCh[0]
let newEndVnode = newCh[newEndIdx]
while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
// 四种主要节点比较
// oldStartVnode-newStartVnode,oldEndVnode-newEndVnode
// oldStartVnode-newEndVnode,oldEndVnode-newStartVnode
// 以上四种情况都不满足,newStartVnode与oldCh数组一次比较,++newStartIdx
if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)
idxInOld = isDef(newStartVnode.key)
? oldKeyToIdx[newStartVnode.key]
: findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)
// 后续操作
}
if (oldStartIdx > oldEndIdx) {
refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm
addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue)
} else if (newStartIdx > newEndIdx) {
removeVnodes(oldCh, oldStartIdx, oldEndIdx)
}
}
function findIdxInOld(node, oldCh, start, end) {
for (let i = start; i < end; i++) {
const c = oldCh[i]
if (isDef(c) && sameVnode(node, c)) return i
}
}
数据响应式处理
new Vue({})
this._init(options)
// src\core\instance\init.js部分代码
/**
* 依次对传入参数进行初始化处理并挂载到vm上
* props、methods、data、computed、watch
* 在该函数中亦对数据进行响应式处理
* 初始化vm._watchers,vm._watchers = []
* src\core\instance\state.js
*/
initState(vm)
/**
* 判断vm.$options.el
* 进行创建dom节点,最终挂载dom元素操作
* src\platforms\web\entry-runtime-with-compiler.js
* 执行web平台下定义的$mount函数
*/
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
模板编译
// src\platforms\web\entry-runtime-with-compiler.js
const { render, staticRenderFns } = compileToFunctions(template, {
outputSourceRange: process.env.NODE_ENV !== 'production',
shouldDecodeNewlines,
shouldDecodeNewlinesForHref,
delimiters: options.delimiters,
comments: options.comments
}, this)
options.render = render
options.staticRenderFns = staticRenderFns
/**
* src\platforms\web\runtime\index.js
* 执行Vue.prototype.$mount函数
*/
return mount.call(this, el, hydrating)
// compileToFunction函数部分代码
function baseCompile (
template: string,
options: CompilerOptions
): CompiledResult {
// 编译template为ast抽象语法树
const ast = parse(template.trim(), options)
if (options.optimize !== false) {
/**
* 优化ast抽象语法树
* 1. 标记静态节点: 不包含动态内容的节点
* 2. 标记静态根节点: 不包含动态内容且含义子标签的节点
*/
optimize(ast, options)
}
/**
* 生成字符串形式的render函数
* const code = ast ? genElement(ast, state) : '_c("div")'
*/
const code = generate(ast, options)
return {
ast,
render: code.render,
staticRenderFns: code.staticRenderFns
}
}
new Watcher
// src\core\instance\lifecycle.js
// return mountComponent(this, el, hydrating)
updateComponent = () => {
vm._update(vm._render(), hydrating)
}
// 创建Vue实例与Vue组件时,均会实例化Watcher
new Watcher(vm, updateComponent, noop, {
before() {
if (vm._isMounted && !vm._isDestroyed) {
callHook(vm, 'beforeUpdate')
}
}
}, true /* isRenderWatcher */)
页面渲染
// vm._update(vm._render(), hydrating)
if (!prevVnode) {
// initial render
vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)
} else {
// updates
vm.$el = vm.__patch__(prevVnode, vnode)
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。