摘要:大概过了一遍工具类后,开始看实例的具体实现原文地址项目地址实现了的初始化函数方法会在实例创建的时候被调用初始化了实例的共有属性如还有一堆私有属性如等等最后再是初始化实例状态事件生命周期等等在实现上比较有趣使用来实现对的和方法利用获取
</>复制代码
大概过了一遍 util 工具类后,开始看 Vue 实例的具体实现
原文地址
项目地址
src/instance/init.js 实现了 Vue 的 _init 初始化函数
</>复制代码
import { mergeOptions } from "../../util/index"
let uid = 0
export default function (Vue) {
Vue.prototype._init = function (options) {
...
}
}
_init 方法会在实例创建的时候被调用:
</>复制代码
function Vue(options) {
this._init(options);
}
init 初始化了 Vue 实例的共有属性如 $el, $parent, $root, $children, $refs, $els还有一堆私有属性如_watchers, _directives, _uid, isVue, _events等等,最后再是初始化实例状态、事件、生命周期等等
在实现 $root 上比较有趣:
</>复制代码
this.$parent = options.parent
this.$root = this.$parent
? this.$parent.$root
: this
state
src/instance/state.js
</>复制代码
/**
* Accessor for `$data` property, since setting $data
* requires observing the new object and updating
* proxied properties.
*/
Object.defineProperty(Vue.prototype, "$data", {
get () {
return this._data
},
set (newData) {
if (newData !== this._data) {
this._setData(newData)
}
}
})
_setData
使用 defineProperty 来实现对 Vue.$data 的 get 和 set
</>复制代码
Vue.prototype._setData = function (newData) {
newData = newData || {}
var oldData = this._data
this._data = newData
var keys, key, i
// unproxy keys not present in new data
keys = Object.keys(oldData)
i = keys.length
while (i--) {
key = keys[i]
if (!(key in newData)) {
this._unproxy(key)
}
}
// proxy keys not already proxied,
// and trigger change for changed values
keys = Object.keys(newData)
i = keys.length
while (i--) {
key = keys[i]
if (!hasOwn(this, key)) {
// new property
this._proxy(key)
}
}
oldData.__ob__.removeVm(this)
observe(newData, this)
this._digest()
}
_setData 方法利用 Object.keys 获取对象的属性列表,在利用 key in obj来判断是否存在属性 key,然后决定是否 proxy 或者 unproxy
_initComputed</>复制代码
/**
* Setup computed properties. They are essentially
* special getter/setters
*/
function noop () {}
Vue.prototype._initComputed = function () {
var computed = this.$options.computed
if (computed) {
for (var key in computed) {
var userDef = computed[key]
var def = {
enumerable: true,
configurable: true
}
if (typeof userDef === "function") {
def.get = makeComputedGetter(userDef, this)
def.set = noop
} else {
def.get = userDef.get
? userDef.cache !== false
? makeComputedGetter(userDef.get, this)
: bind(userDef.get, this)
: noop
def.set = userDef.set
? bind(userDef.set, this)
: noop
}
Object.defineProperty(this, key, def)
}
}
}
function makeComputedGetter (getter, owner) {
var watcher = new Watcher(owner, getter, null, {
lazy: true
})
return function computedGetter () {
if (watcher.dirty) {
watcher.evaluate()
}
if (Dep.target) {
watcher.depend()
}
return watcher.value
}
}
初始化计算属性,即 computed 的实现,从文档可以看到我们既可以使用 function 来确定怎么获取某个值,也可以使用 get 和 set 对象来确定值的获取和更新,底层的实现是 watcher
methods</>复制代码
/**
* Setup instance methods. Methods must be bound to the
* instance since they might be passed down as a prop to
* child components.
*/
Vue.prototype._initMethods = function () {
var methods = this.$options.methods
if (methods) {
for (var key in methods) {
this[key] = bind(methods[key], this)
}
}
}
/**
* Initialize meta information like $index, $key & $value.
*/
Vue.prototype._initMeta = function () {
var metas = this.$options._meta
if (metas) {
for (var key in metas) {
defineReactive(this, key, metas[key])
}
}
}
watcher
</>复制代码
import { extend, warn, isArray, isObject, nextTick } from "./util/index"
import config from "./config"
import Dep from "./observer/dep"
import { parseExpression } from "./parsers/expression"
import { pushWatcher } from "./batcher"
let uid = 0
export default function Watcher (vm, expOrFn, cb, options) {
// mix in options
if (options) {
extend(this, options)
}
var isFn = typeof expOrFn === "function"
this.vm = vm
vm._watchers.push(this)
this.expression = isFn ? expOrFn.toString() : expOrFn
this.cb = cb
this.id = ++uid // uid for batching
this.active = true
this.dirty = this.lazy // for lazy watchers
this.deps = Object.create(null)
this.newDeps = null
this.prevError = null // for async error stacks
// parse expression for getter/setter
if (isFn) {
this.getter = expOrFn
this.setter = undefined
} else {
var res = parseExpression(expOrFn, this.twoWay)
this.getter = res.get
this.setter = res.set
}
this.value = this.lazy
? undefined
: this.get()
// state for avoiding false triggers for deep and Array
// watchers during vm._digest()
this.queued = this.shallow = false
}
/**
* Add a dependency to this directive.
*
* @param {Dep} dep
*/
Watcher.prototype.addDep = function (dep) {
var id = dep.id
if (!this.newDeps[id]) {
this.newDeps[id] = dep
if (!this.deps[id]) {
this.deps[id] = dep
dep.addSub(this)
}
}
}
/**
* Evaluate the getter, and re-collect dependencies.
*/
Watcher.prototype.get = function () {
this.beforeGet()
var scope = this.scope || this.vm
var value
try {
value = this.getter.call(scope, scope)
} catch (e) {
if (
process.env.NODE_ENV !== "production" &&
config.warnExpressionErrors
) {
warn(
"Error when evaluating expression "" +
this.expression + "". " +
(config.debug
? ""
: "Turn on debug mode to see stack trace."
), e
)
}
}
// "touch" every property so they are all tracked as
// dependencies for deep watching
if (this.deep) {
traverse(value)
}
if (this.preProcess) {
value = this.preProcess(value)
}
if (this.filters) {
value = scope._applyFilters(value, null, this.filters, false)
}
if (this.postProcess) {
value = this.postProcess(value)
}
this.afterGet()
return value
}
/**
* Set the corresponding value with the setter.
*
* @param {*} value
*/
Watcher.prototype.set = function (value) {
var scope = this.scope || this.vm
if (this.filters) {
value = scope._applyFilters(
value, this.value, this.filters, true)
}
try {
this.setter.call(scope, scope, value)
} catch (e) {
if (
process.env.NODE_ENV !== "production" &&
config.warnExpressionErrors
) {
warn(
"Error when evaluating setter "" +
this.expression + """, e
)
}
}
// two-way sync for v-for alias
var forContext = scope.$forContext
if (forContext && forContext.alias === this.expression) {
if (forContext.filters) {
process.env.NODE_ENV !== "production" && warn(
"It seems you are using two-way binding on " +
"a v-for alias (" + this.expression + "), and the " +
"v-for has filters. This will not work properly. " +
"Either remove the filters or use an array of " +
"objects and bind to object properties instead."
)
return
}
forContext._withLock(function () {
if (scope.$key) { // original is an object
forContext.rawValue[scope.$key] = value
} else {
forContext.rawValue.$set(scope.$index, value)
}
})
}
}
/**
* Prepare for dependency collection.
*/
Watcher.prototype.beforeGet = function () {
Dep.target = this
this.newDeps = Object.create(null)
}
/**
* Clean up for dependency collection.
*/
Watcher.prototype.afterGet = function () {
Dep.target = null
var ids = Object.keys(this.deps)
var i = ids.length
while (i--) {
var id = ids[i]
if (!this.newDeps[id]) {
this.deps[id].removeSub(this)
}
}
this.deps = this.newDeps
}
/**
* Subscriber interface.
* Will be called when a dependency changes.
*
* @param {Boolean} shallow
*/
Watcher.prototype.update = function (shallow) {
if (this.lazy) {
this.dirty = true
} else if (this.sync || !config.async) {
this.run()
} else {
// if queued, only overwrite shallow with non-shallow,
// but not the other way around.
this.shallow = this.queued
? shallow
? this.shallow
: false
: !!shallow
this.queued = true
// record before-push error stack in debug mode
/* istanbul ignore if */
if (process.env.NODE_ENV !== "production" && config.debug) {
this.prevError = new Error("[vue] async stack trace")
}
pushWatcher(this)
}
}
/**
* Batcher job interface.
* Will be called by the batcher.
*/
Watcher.prototype.run = function () {
if (this.active) {
var value = this.get()
if (
value !== this.value ||
// Deep watchers and Array watchers should fire even
// when the value is the same, because the value may
// have mutated; but only do so if this is a
// non-shallow update (caused by a vm digest).
((isArray(value) || this.deep) && !this.shallow)
) {
// set new value
var oldValue = this.value
this.value = value
// in debug + async mode, when a watcher callbacks
// throws, we also throw the saved before-push error
// so the full cross-tick stack trace is available.
var prevError = this.prevError
/* istanbul ignore if */
if (process.env.NODE_ENV !== "production" &&
config.debug && prevError) {
this.prevError = null
try {
this.cb.call(this.vm, value, oldValue)
} catch (e) {
nextTick(function () {
throw prevError
}, 0)
throw e
}
} else {
this.cb.call(this.vm, value, oldValue)
}
}
this.queued = this.shallow = false
}
}
/**
* Evaluate the value of the watcher.
* This only gets called for lazy watchers.
*/
Watcher.prototype.evaluate = function () {
// avoid overwriting another watcher that is being
// collected.
var current = Dep.target
this.value = this.get()
this.dirty = false
Dep.target = current
}
/**
* Depend on all deps collected by this watcher.
*/
Watcher.prototype.depend = function () {
var depIds = Object.keys(this.deps)
var i = depIds.length
while (i--) {
this.deps[depIds[i]].depend()
}
}
/**
* Remove self from all dependencies" subcriber list.
*/
Watcher.prototype.teardown = function () {
if (this.active) {
// remove self from vm"s watcher list
// we can skip this if the vm if being destroyed
// which can improve teardown performance.
if (!this.vm._isBeingDestroyed) {
this.vm._watchers.$remove(this)
}
var depIds = Object.keys(this.deps)
var i = depIds.length
while (i--) {
this.deps[depIds[i]].removeSub(this)
}
this.active = false
this.vm = this.cb = this.value = null
}
}
/**
* Recrusively traverse an object to evoke all converted
* getters, so that every nested property inside the object
* is collected as a "deep" dependency.
*
* @param {*} val
*/
function traverse (val) {
var i, keys
if (isArray(val)) {
i = val.length
while (i--) traverse(val[i])
} else if (isObject(val)) {
keys = Object.keys(val)
i = keys.length
while (i--) traverse(val[keys[i]])
}
}
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/85145.html
摘要:所以整个的核心,就是如何实现这三样东西以上摘自囧克斯博客的一篇文章从版本开始这个时候的项目结构如下源码在里面,为打包编译的代码,为打包后代码放置的位置,为测试代码目录。节点类型摘自资源另一位作者关于源码解析 本项目的源码学习笔记是基于 Vue 1.0.9 版本的也就是最早的 tag 版本,之所以选择这个版本,是因为这个是最原始没有太多功能拓展的版本,有利于更好的看到 Vue 最开始的骨...
摘要:给的实例注入一个的属性,这也就是为什么我们在的组件中可以通过访问到的各种数据和状态源码位置,是怎么实现的源码位置是对的的初始化,它接受个参数,为当前实例,为的,为执行的回调函数,为当前模块的路径。 20190221 请简述一下vuex实现原理 对vuex基础概念有不懂的可以点这里 vuex实现原理我们简单过一遍源码 地址 https://github.com/vuejs/vuex 首...
摘要:如何优雅的动态添加这里我们需要用到的实例化,首先我们来看的思路,贴一段源码使用基础构造器,创建一个子类。因为作为一个的扩展构造器,所以基础的功能还是需要保持一致,跟构造器一样在构造函数中初始化。 [Vue]如何实现全屏遮罩(附Vue.extend和el-message源码学习) 在做个人项目的时候需要做一个类似于电子相册浏览的控件,实现过程中首先要实现全局遮罩,结合自己的思路并阅读了(...
摘要:如何优雅的动态添加这里我们需要用到的实例化,首先我们来看的思路,贴一段源码使用基础构造器,创建一个子类。因为作为一个的扩展构造器,所以基础的功能还是需要保持一致,跟构造器一样在构造函数中初始化。 [Vue]如何实现全屏遮罩(附Vue.extend和el-message源码学习) 在做个人项目的时候需要做一个类似于电子相册浏览的控件,实现过程中首先要实现全局遮罩,结合自己的思路并阅读了(...
阅读 2973·2021-09-22 15:43
阅读 5319·2021-09-06 15:02
阅读 917·2019-08-29 13:55
阅读 1753·2019-08-29 12:58
阅读 3147·2019-08-29 12:38
阅读 1322·2019-08-26 12:20
阅读 2325·2019-08-26 12:12
阅读 3401·2019-08-23 18:35