1、若空则赋值简写
Observer.prototype.observeArray = function (items) {
var i = items.length
while (i--) {
var ob = Observer.create(items[i])
if (ob) {
(ob.parents || (ob.parents = [])).push(this)
}
}
}
exports.$on = function (event, fn) {
(this._events[event] || (this._events[event] = []))
.push(fn)
modifyListenerCount(this, event, 1)
return this
}
善长用闭包:
exports.$once = function (event, fn) {
var self = this
function on () {
self.$off(event, on)
fn.apply(this, arguments)
}
on.fn = fn
this.$on(event, on)
return this
}
2、遍历数组简写
Observer.prototype.walk = function (obj) {
var keys = Object.keys(obj)
var i = keys.length
while (i--) {
this.convert(keys[i], obj[keys[i]])
}
}
Observer.prototype.notify = function () {
this.dep.notify()
var parents = this.parents
if (parents) {
var i = parents.length
while (i--) {
parents[i].notify()
}
}
}
Dep.prototype.notify = function () {
// stablize the subscriber list first
var subs = _.toArray(this.subs)
for (var i = 0, l = subs.length; i < l; i++) {
subs[i].update()
}
}
3、清洗对象
/**
* "clean" a getter/setter converted object into a plain
* object copy.
*
* @param {Object} - obj
* @return {Object}
*/
function clean (obj) {
return JSON.parse(JSON.stringify(obj))
}
4、数据劫持
对象是一个属性集,属性可以是普通属性或方法。一个属性与这个集合的关系有三种:Writable(写),Enumerable(枚举),Configurable(配置),ES3规范约定了这这三个值默认false。
属性本身还具有两种性质:Name(名称)和Value(值),综上所述,”声明“一个对象本质上就是为它所有的属性描述清楚上述5种性质。
在ES3中,只能用对象直接量快速定义了属性的名称和值。
在ES5中,可以用属性描述符来完整的定义属性。
var tmp = { a: 1, b: 2 } // 对象直接量定义对象
Object.defineProperty(tmp, 'a' ,{ set: function(newVal) { a = newVal } })
tmp.a // undefined 因为属性a定义了属性描述符,但没有get函数
tmp.b // 2
tmp.a = 5
Object.defineProperty(tmp, 'a' ,{ get: function() { return a }, set: function(newVal) { a = newVal } })
tmp.a // 5 调用属性a的属性描述符get函数
#### 属性描述符默认值汇总
* 拥有布尔值的键 `configurable`、`enumerable` 和 `writable` 的默认值都是 `false`。
* 属性值和函数的键 `value`、`get` 和 `set` 字段的默认值为 `undefined`。
5、熟练操作DOM
exports.$after = function (target, cb, withTransition) {
target = query(target)
if (target.nextSibling) { // nextSibling 属性可返回某个元素之后紧跟的节点(处于同一树层级中)。
this.$before(target.nextSibling, cb, withTransition)
} else { // parentNode 属性可返回某节点的父节点。
this.$appendTo(target.parentNode, cb, withTransition)
}
return this
}
previousSibling 属性与 previousElementSibling 属性的差别:
* previousSibling 属性返回元素节点之前的兄弟节点(包括文本节点、注释节点);
* previousElementSibling 属性只返回元素节点之前的兄弟元素节点(不包括文本节点、注释节点);
6、修改原型实现继承
exports.extend = function (extendOptions) {
extendOptions = extendOptions || {}
var Super = this
var isFirstExtend = Super.cid === 0
if (isFirstExtend && extendOptions._Ctor) {
return extendOptions._Ctor
}
var name = extendOptions.name || Super.options.name
var Sub = createClass(name || 'VueComponent')
Sub.prototype = Object.create(Super.prototype)
Sub.prototype.constructor = Sub
Sub.cid = cid++
Sub.options = _.mergeOptions(
Super.options,
extendOptions
)
Sub['super'] = Super
// allow further extension
Sub.extend = Super.extend
// create asset registers, so extended classes
// can have their private assets too.
config._assetTypes.forEach(function (type) {
Sub[type] = Super[type]
})
// enable recursive self-lookup
if (name) {
Sub.options.components[name] = Sub
}
// cache constructor
if (isFirstExtend) {
extendOptions._Ctor = Sub
}
return Sub
}
/**
* A function that returns a sub-class constructor with the
* given name. This gives us much nicer output when
* logging instances in the console.
*
* @param {String} name
* @return {Function}
*/
function createClass (name) {
return new Function(
'return function ' + _.classify(name) +
' (options) { this._init(options) }'
)()
}
7、关于NODE_ENV
cross-env NODE_ENV=production node build.js
这样设置之后,我们可以在脚本中使用process.env.NODE_ENV了,但是不能在模块中使用,要想在模块当中直接使用,我们还需要一些配置。
##### DefinePlugin
DefinePlugin可以定义一些全局变量,让我们在模块当中直接使用,不用做任何声明。
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
})
8、指令使用、钩子使用
1. directives/public/cloak.js(v-cloak)源码:
module.exports = {
bind: function () {
var el = this.el
this.vm.$once('hook:compiled', function () {
el.removeAttribute('v-cloak')
})
}
}
2. directives/public/text.js(v-text)源码:
var _ = require('../../util')
module.exports = {
bind: function () {
this.attr = this.el.nodeType === 3
? 'data'
: 'textContent'
},
update: function (value) {
this.el[this.attr] = _.toString(value)
}
}
3. instance/lifecycle.js源码:
/**
* Create and bind a directive to an element.
*
* @param {String} name - directive name
* @param {Node} node - target node
* @param {Object} desc - parsed directive descriptor
* @param {Object} def - directive definition object
* @param {Vue} [host] - transclusion host component
* @param {Object} [scope] - v-for scope
* @param {Fragment} [frag] - owner fragment
*/
exports._bindDir = function (descriptor, node, host, scope, frag) {
this._directives.push(
new Directive(descriptor, this, node, host, scope, frag)
)
}
9、响应式
instance/state.js源码:
/**
* Initialize the data.
*/
exports._initData = function () {
var optionsDataFn = this.$options.data
var optionsData = optionsDataFn && optionsDataFn()
if (optionsData) {
this._data = optionsData
}
var data = this._data
// proxy data on instance
var keys = Object.keys(data)
var i, key
i = keys.length
while (i--) {
key = keys[i]
this._proxy(key)
}
// observe data
Observer.create(data, this)
}
/**
* Proxy a property, so that
* vm.prop === vm._data.prop
*
* @param {String} key
*/
exports._proxy = function (key) {
if (!_.isReserved(key)) {
// need to store ref to self here
// because these getter/setters might
// be called by child scopes via
// prototype inheritance.
var self = this
Object.defineProperty(self, key, {
configurable: true,
enumerable: true,
get: function proxyGetter () {
return self._data[key]
},
set: function proxySetter (val) {
self._data[key] = val
}
})
}
}
observer/dep.js源码:
var _ = require('../util')
var uid = 0
/**
* A dep is an observable that can have multiple
* directives subscribing to it.
*
* @constructor
*/
function Dep () {
this.id = uid++
this.subs = []
}
// the current target watcher being evaluated.
// this is globally unique because there could be only one
// watcher being evaluated at any time.
Dep.target = null
/**
* Add a directive subscriber.
*
* @param {Directive} sub
*/
Dep.prototype.addSub = function (sub) {
this.subs.push(sub)
}
/**
* Remove a directive subscriber.
*
* @param {Directive} sub
*/
Dep.prototype.removeSub = function (sub) {
this.subs.$remove(sub)
}
/**
* Add self as a dependency to the target watcher.
*/
Dep.prototype.depend = function () {
Dep.target.addDep(this)
}
/**
* Notify all subscribers of a new value.
*/
Dep.prototype.notify = function () {
// stablize the subscriber list first
var subs = _.toArray(this.subs)
for (var i = 0, l = subs.length; i < l; i++) {
subs[i].update()
}
}
module.exports = Dep
observer/index.js源码:
/**
* Define a reactive property on an Object.
*
* @param {Object} obj
* @param {String} key
* @param {*} val
*/
function defineReactive (obj, key, val) {
var dep = new Dep()
var childOb = Observer.create(val)
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function metaGetter () {
if (Dep.target) {
dep.depend()
if (childOb) {
childOb.dep.depend()
}
}
return val
},
set: function metaSetter (newVal) {
if (newVal === val) return
val = newVal
childOb = Observer.create(newVal)
dep.notify()
}
})
}
src/watcher.js源码:
/**
* 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
}
10、运行环境嗅探
// Browser environment sniffing
var inBrowser = exports.inBrowser =
typeof window !== 'undefined' && Object.prototype.toString.call(window) !== '[object Object]'
exports.isIE9 = inBrowser && navigator.userAgent.toLowerCase().indexOf('msie 9.0') > 0
exports.isAndroid = inBrowser && navigator.userAgent.toLowerCase().indexOf('android') > 0
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。