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

JohnsonGH
32 声望1 粉丝