标签:自己 新版本 新功能 red 赋值 代码生成 描述 dep video
某课网有个488人名币的源码解读视频看不起,只能搜很多得资料慢慢理解,看源码能知道大佬的功能模块是怎么分块写的,怎么复用的,已经vue是怎么实现的
资料来自
vue源码
喜欢唱歌的小狮子
web喵喵喵
Vue.js源码全方位深入解析
恰恰虎的博客
learnVue
最后四集视频
总文件目录
function sum(a: number, b:number) {
return a + b;
}
src文件夹
Global-api
Vue的核心
src/core/instance/index.js
function Vue (options) {
// 安全性判断,如果不是生产环境且不是Vue的实例,在控制台输出警告
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)
}
// 下面这些Mixin都是往Vue函数的原型对象里添加方法
// 挂载初始化方法
initMixin(Vue)
// 挂载状态处理相关方法
stateMixin(Vue)
// 挂载事件响应相关方法
eventsMixin(Vue)
// 挂载生命周期相关方法
lifecycleMixin(Vue)
// 挂载视图渲染方法
renderMixin(Vue)
// 这里就是暴露给Global-api去添加全局的方法
export default Vue
export function initMixin (Vue: Class<Component>) {
// 在Vue类的原型上挂载_init()方法
// 接收类型为原始对象的options形参,此参数为非必选参数
Vue.prototype._init = function (options?: Object) {
// 将实例对象赋值给vm变量
// 这里会再次进行Component类型检查确保vm接收到的是Vue类的实例
const vm: Component = this
// 合并options对象
if (options && options._isComponent) {
// 内部组件的options初始化
initInternalComponent(vm, options)
} else {
// 否则执行合并options函数,并赋值给vm的公共属性
// 在这里的合并函数主要是解决与继承自父类的配置对象的合并
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
}
// 暴露实例对象
vm._self = vm
// 初始化实例的生命周期相关属性
initLifecycle(vm)
// 初始化事件相关属性和监听功能
initEvents(vm)
// 初始化渲染相关属性和功能
initRender(vm)
// 调用生命周期钩子函数beforeCreate
callHook(vm, 'beforeCreate')
// 初始化父组件注入属性
initInjections(vm) // resolve injections before data/props
// 初始化状态相关属性和功能
initState(vm)
// 初始化子组件属性提供器
initProvide(vm) // resolve provide after data/props
// 调用生命周期钩子函数created
callHook(vm, 'created')
// 执行DOM元素挂载函数
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
}
生命周期相关
// 导出lifecycleMixin函数,接收形参Vue,
// 使用Flow进行静态类型检查指定为Component类
export function lifecycleMixin (Vue: Class<Component>) {
// 为Vue原型对象挂载_update私有方法
// 接收vnode虚拟节点类型参数和一个可选的布尔值hydrating
Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) { ... }
// 为Vue实例挂载$forceUpdate方法,实现强制更新
Vue.prototype.$forceUpdate = function () { ... }
// 为Vue实例挂载$destroy方法
Vue.prototype.$destroy = function () { ... }
}
// 导出initLifecycle函数,接受一个Component类型的vm参数
export function initLifecycle (vm: Component) {
// 获取实例的$options属性,赋值为options变量
const options = vm.$options
let parent = options.parent
// 判断是否存在且非抽象
if (parent && !options.abstract) {
// 遍历寻找最外层的非抽象父级
while (parent.$options.abstract && parent.$parent) {
parent = parent.$parent
}
// 将实例添加到最外层非抽象父级的子组件中
parent.$children.push(vm)
}
// 初始化实例的公共属性
// 设置父级属性,如果之前的代码未找到父级,则vm.$parent为undefined
vm.$parent = parent
// 设置根属性,没有父级则为实例对象自身
vm.$root = parent ? parent.$root : vm
// 初始化$children和$refs属性
// vm.$children是子组件的数组集合
// vm.$refs是指定引用名称的组件对象集合
vm.$children = []
vm.$refs = {}
// 生命周期相关的私有属性
vm._isMounted = false
vm._isDestroyed = false
vm._isBeingDestroyed = false
}
调用生命周期的方法callHook
export function callHook (vm: Component, hook: string) {
//记录当前watch的实例
pushTarget()
//获取相应钩子的事件处理方法数组
const handlers = vm.$options[hook]
//执行对应的事件方法
if (handlers) {
for (let i = 0, j = handlers.length; i < j; i++) {
try {
handlers[i].call(vm)
} catch (e) {
handleError(e, vm, `${hook} hook`)
}
}
}
//触发@hook:定义的钩子方法
if (vm._hasHookEvent) {
// 发布订阅调用模式,$emit来自initEvent
vm.$emit('hook:' + hook)
}
//释放当前的watch实例
popTarget()
}
函数相关
export function eventsMixin (Vue: Class<Component>) {
Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component {
// 定义实例变量
const vm: Component = this
// 如果传入的event参数是数组,遍历event数组,为所有事件注册fn监听函数
if (Array.isArray(event)) {
for (let i = 0, l = event.length; i < l; i++) {
this.$on(event[i], fn)
}
}
// 返回实例本身
return vm
}
// 为Vue原型对象挂载$once方法
// 参数event只接受字符串,fn是监听函数
Vue.prototype.$once = function (event: string, fn: Function): Component { ... }
Vue.prototype.$off = function (event?: string | Array<string>, fn?: Function): Component { ... }
// 为Vue原型对象挂载$emit方法,只接受单一event
Vue.prototype.$emit = function (event: string): Component { ... }
}
// 定义并导出initEvents函数,接受Component类型的vm参数
export function initEvents (vm: Component) {
//初始化vm._events对象
// 存储父组件绑定当前子组件的事件,保存到vm._events中
vm._events = Object.create(null);
//是否存在hook:钩子事件
vm._hasHookEvent = false;
// init parent attached events
var listeners = vm.$options._parentListeners;
if (listeners) {
updateComponentListeners(vm, listeners);
}
}
渲染相关
export function renderMixin (Vue: Class<Component>) {
Vue.prototype.$nextTick = function (fn: Function) { ... }
Vue.prototype._render = function (): VNode {
const vm: Component = this
const { render, _parentVnode } = vm.$options
vm.$vnode = _parentVnode
// render 就是 render表达式,
// vnode 就是 虚拟dom
let vnode = render.call(vm._renderProxy, vm.$createElement)
return vnode
}
}
export function initRender (vm: Component) {
// 初始化实例的根虚拟节点
vm._vnode = null
// 定义实例的静态树节点
vm._staticTrees = null
// 获取配置对象
const options = vm.$options
// 设置父占位符节点
const parentVnode = vm.$vnode = options._parentVnode
// renderContext存储父节点有无声明上下文
const renderContext = parentVnode && parentVnode.context
vm.$slots = resolveSlots(vm.$options._renderChildren, renderContext)
vm.$scopedSlots = emptyObject
/*将createElement函数绑定到该实例上,该vm存在闭包中,不可修改,vm实例则固定。这样我们就可以得到正确的上下文渲染*/
vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
/*常规方法被用于公共版本,被用来作为用户界面的渲染方法*/
vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)
// $attrs & $listeners这个属性进行数据监听
const parentData = parentVnode && parentVnode.data
defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, null, true)
defineReactive(vm, '$listeners', options._parentListeners || emptyObject, null, true)
}
参数处理
export function stateMixin (Vue: Class<Component>) {
const dataDef = {}
dataDef.get = function () { return this._data }
const propsDef = {}
propsDef.get = function () { return this._props }
Object.defineProperty(Vue.prototype, '$data', dataDef)
Object.defineProperty(Vue.prototype, '$props', propsDef)
Vue.prototype.$set = set
Vue.prototype.$delete = del
Vue.prototype.$watch = function (
expOrFn: string | 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)
if (options.immediate) {
cb.call(vm, watcher.value)
}
return function unwatchFn () {
watcher.teardown()
}
}
}
export function initState (vm: Component) {
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)
}
}
// 这里展示下initData方法,其他也差不多
function initData (vm: Component) {
let data = vm.$options.data
data = vm._data = typeof data === 'function'
? getData(data, vm)
: data || {}
// 判断props对象跟data对象跟methods对象里有没有名字一样的key值
const keys = Object.keys(data)
const props = vm.$options.props
const methods = vm.$options.methods
let i = keys.length
while (i--) {
const key = keys[i]
if (process.env.NODE_ENV !== 'production') {
if (methods && hasOwn(methods, key)) {
warn(
`Method "${key}" has already been defined as a data property.`
)
}
}
if (props && hasOwn(props, key)) {
process.env.NODE_ENV !== 'production' && warn(
`The data property "${key}" is already declared as a prop. ` +
`Use prop default value instead.`
)
} else if (!isReserved(key)) {
// 代理data的值,当访问this.message时,实际上访问的是this[_data][message]
proxy(vm, `_data`, key)
}
}
// 对内容进行监听
observe(data, true)
}
上面出现的几个重要的方法
总结所有的init
当参数处理完毕,data,prop等数据已经被监听,Dep数组也已经创建好,但是是空的,这时可以看到上面的init的生命周期才写到created,我们知道在生命周期mounted前是没有dom元素的,所以在$mount
阶段才开始生成虚拟dom,$mount
方法在哪里定义的呢,查看下一篇笔记
标签:自己 新版本 新功能 red 赋值 代码生成 描述 dep video
原文地址:https://www.cnblogs.com/pengdt/p/12304000.html