Vue2源码阅读——Shared

428 阅读3分钟

Shared文件夹源码阅读

constants.js

涉及一些常量,比如 服务端渲染, 资源类型(组件, 指令, 过滤器), 生命周期

export const SSR_ATTR = 'data-server-rendered' // 服务端渲染

export const ASSET_TYPES = [
  'component', //  组件
  'directive', // 指令 v-model v-if v-for v-show v-bind(:) v-on(@)....
  'filter' // 过滤器
]

export const LIFECYCLE_HOOKS = [ // 生命周期
  'beforeCreate',
  'created',
  'beforeMount',
  'mounted',
  'beforeUpdate',
  'updated',
  'beforeDestroy',
  'destroyed',
  'activated',
  'deactivated',
  'errorCaptured',
  'serverPrefetch'
]

util.js

涉及到一些常用的工具函数

1. 定义空对象

const emptyObject = Object.freeze({}) 冻结对象, 使得对象无法被操作

2. 判断参数是否定义

function isUndef (v) {
  return v === undefined || v === null
}
function isDef (v) {
  return v !== undefined && v !== null
}

3. 判断参数否是真假

function isTrue (v){
  return v === true
}

function isFalse (v) {
  return v === false
}

4. 判断是否是基本数据类型

function isPrimitive (value) {
  return (
    typeof value === 'string' ||
    typeof value === 'number' ||
    // $flow-disable-line
    typeof value === 'symbol' ||
    typeof value === 'boolean'
  )
}

5. 判断是否是对象, 是否是引用类型

typeof null 是 object, 所以需要额外的判断

function isObject (obj){
  return obj !== null && typeof obj === 'object'
}

6. 转为原生对象

const _toString = Object.prototype.toString
function toRawType (value){
  return _toString.call(value).slice(8, -1)
}

7. 判断是否是纯对象

export function isPlainObject (obj){
  return _toString.call(obj) === '[object Object]'
}

8. 判断是否是正则

export function isRegExp (v) {
  return _toString.call(v) === '[object RegExp]'
}

9. 判读是否是有效的数组索引

function isValidArrayIndex (val) {
  const n = parseFloat(String(val))
  return n >= 0 && Math.floor(n) === n && isFinite(val)
}

10. 判断是否是Promise

function isPromise (val){
  return (
    isDef(val) &&
    typeof val.then === 'function' &&
    typeof val.catch === 'function'
  )
}

11. 转为字符串

数组和纯对象直接使用JSON.stringify

function toString (val){
  return val == null
    ? ''
    : Array.isArray(val) || (isPlainObject(val) && val.toString === _toString)
      ? JSON.stringify(val, null, 2)
      : String(val)
}

12. 转为数字

function toNumber (val){
  const n = parseFloat(val)
  return isNaN(n) ? val : n
}

13. 判断参数是否是保留标签或者属性

// 函数柯里化
export function makeMap (
  str: string,
  expectsLowerCase?: boolean
): (key: string) => true | void {
  const map = Object.create(null)
  const list: Array<string> = str.split(',')
  for (let i = 0; i < list.length; i++) {
    map[list[i]] = true
  }
  return expectsLowerCase
    ? val => map[val.toLowerCase()]
    : val => map[val]
}

/**
 * Check if a tag is a built-in tag.
 */
export const isBuiltInTag = makeMap('slot,component', true)

/**
 * Check if an attribute is a reserved attribute.
 */
export const isReservedAttribute = makeMap('key,ref,slot,slot-scope,is')

14. 从数组中删除元素

export function remove (arr: Array<any>, item: any): Array<any> | void {
  if (arr.length) {
    const index = arr.indexOf(item)
    if (index > -1) {
      return arr.splice(index, 1)
    }
  }
}

15. 判断对象本身是否含有该属性 而非原型链

const hasOwnProperty = Object.prototype.hasOwnProperty
export function hasOwn (obj: Object | Array<*>, key: string): boolean {
  return hasOwnProperty.call(obj, key)
}

16. 缓存函数(高阶函数)

如果缓存中有了, 直接返回, 否则掉用对应的函数,并且缓存起来

export function cached<F: Function> (fn: F): F {
  const cache = Object.create(null)
  return (function cachedFn (str: string) {
    const hit = cache[str]
    return hit || (cache[str] = fn(str))
  }: any)
}

17. 连字符转为驼峰

// y-component => yComponent 
const camelizeRE = /-(\w)/g
export const camelize = cached((str: string): string => {
  return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : '')
})

18. 首字母大写

export const capitalize = cached((str: string): string => {
  return str.charAt(0).toUpperCase() + str.slice(1)
})

19. 驼峰转为连字符

// yComponent => y-component
const hyphenateRE = /\B([A-Z])/g
export const hyphenate = cached((str: string): string => {
  return str.replace(hyphenateRE, '-$1').toLowerCase()
})

20. bind函数的polyfill

使用 call 和 apply 函数

function polyfillBind (fn: Function, ctx: Object): Function {
  function boundFn (a) {
    const l = arguments.length
    return l
      ? l > 1
        ? fn.apply(ctx, arguments)
        : fn.call(ctx, a)
      : fn.call(ctx)
  }

  boundFn._length = fn.length
  return boundFn
}
function nativeBind (fn: Function, ctx: Object): Function {
  return fn.bind(ctx)
}

export const bind = Function.prototype.bind
  ? nativeBind
  : polyfillBind

21. 类数组专为数组

export function toArray (list: any, start?: number): Array<any> {
  start = start || 0
  let i = list.length - start
  const ret: Array<any> = new Array(i)
  while (i--) {
    ret[i] = list[i + start]
  }
  return ret
}

22. 对象浅拷贝

export function extend (to: Object, _from: ?Object): Object {
  for (const key in _from) {
    to[key] = _from[key]
  }
  return to
}

23. 对象数组转为对象

export function toObject (arr: Array<any>): Object {
  const res = {}
  for (let i = 0; i < arr.length; i++) {
    if (arr[i]) {
      extend(res, arr[i])
    }
  }
  return res
}

24. 空函数

export function noop (a?: any, b?: any, c?: any) {}

25. 返回false函数

export const no = (a?: any, b?: any, c?: any) => false

26. 原值返回

export const identity = (_: any) => _

27. 生成字符串包含数组对象的所有静态值

export function genStaticKeys (modules: Array<ModuleOptions>): string {
  return modules.reduce((keys, m) => {
    return keys.concat(m.staticKeys || [])
  }, []).join(',')
}

28. 判断两个值是否相等(松散)

  1. 两个值 相等直接返回true
  2. 如果两个值都是 对象
    • 2.1 如果两个值都是 数组, 那么判断长度 以及 循环判断 数组值是否相等
    • 2.2 否则 如果两个值 都是 日期类型, 那么判断 两个 时间戳是否相等
    • 2.3 否则 两个值是纯对象, 判断键名数量是否相等, 以及键值是否相等
    • 2.4 否则直接返回 false
  3. 否则 转为 字符串 进行比较
  4. 否则 返回 false
export function looseEqual (a: any, b: any): boolean {
  if (a === b) return true
  const isObjectA = isObject(a)
  const isObjectB = isObject(b)
  if (isObjectA && isObjectB) { // 如何A和B都是对象
    try {
      const isArrayA = Array.isArray(a)
      const isArrayB = Array.isArray(b)
      if (isArrayA && isArrayB) { // 如是都是数组,那么判断每个值是否相等
        return a.length === b.length && a.every((e, i) => {
          return looseEqual(e, b[i])
        })
      } else if (a instanceof Date && b instanceof Date) {
        return a.getTime() === b.getTime()
      } else if (!isArrayA && !isArrayB) {
        const keysA = Object.keys(a)
        const keysB = Object.keys(b)
        return keysA.length === keysB.length && keysA.every(key => {
          return looseEqual(a[key], b[key])
        })
      } else {
        /* istanbul ignore next */
        return false
      }
    } catch (e) {
      /* istanbul ignore next */
      return false
    }
  } else if (!isObjectA && !isObjectB) {
    return String(a) === String(b)
  } else {
    return false
  }
}

29. 判断元素在数组中的索引(松散)

export function looseIndexOf (arr: Array<mixed>, val: mixed): number {
  for (let i = 0; i < arr.length; i++) {
    if (looseEqual(arr[i], val)) return i
  }
  return -1
}

30. 函数只执行一次

export function once (fn: Function): Function {
  let called = false
  return function () {
    if (!called) {
      called = true
      fn.apply(this, arguments)
    }
  }
}

知识拓展

1. Object.defineProperty

在对象上,定义新的属性 或者 修改现有属性

  1. 基本语法: Object.defineProperty(obj, prop, descriptor)
  2. 参数说明: obj: 定义的对象, prop: 新增或者修改的属性, descriptor: 属性描述符
  3. 返回值: 被传递的对象

描述符(descriptor)

允许添加或者修改对象的属性, 通过赋值操作符的普通属性是可以被枚举的(在for...inObject.keys中被枚举出来, 通过descriptor可以对属性进行额外配置, 比如 是否可以 重新赋值, 是否可以被枚举等等, 描述符分为两种形式:数据描述符存取描述符

共有的属性

  1. configurable 是否可配置 默认值: false
  2. enumerable 是否允许枚举 默认值: false

1. 数据描述符

  1. value: 属性值, 默认为: undefined
  2. writable: 是否可以通过赋值运算符进行 改变 value 值, 默认为 false

2. 存取描述符 (这也是 Vue的实现的原理)

  1. get: getter函数 获取 属性值,如果没有getter函数,那么就是 undefined
  2. set: setter函数 设置 属性值
var obj = { } ,  _tmpValue = ''
Object.defineProperty(obj, 'name', {
  configurable: false, // 默认值: false
  enumerable: false, // 默认值: false
  get: function () {
    console.log('获取值')
    return _tmpValue
  },
  set: function (value) {
    console.log('设置值')
    _tmpValue = value
  }
})
console.log(obj.name) // 1. 获取值  2. ''
obj.name = '请叫我张先森' // 1. 设置值
console.log(obj.name) // 1. 获取值  2. '请叫我张先森'

2. Object.freeze()

方法可以冻结一个对象,使得无法 对冻结的对象 进行任何的操作, 属性的修改,删除已经新增属性,简单来说就是 无法进行任何的操作