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. 判断两个值是否相等(松散)
- 两个值 相等直接返回true
- 如果两个值都是 对象
- 2.1 如果两个值都是 数组, 那么判断长度 以及 循环判断 数组值是否相等
- 2.2 否则 如果两个值 都是 日期类型, 那么判断 两个 时间戳是否相等
- 2.3 否则 两个值是纯对象, 判断键名数量是否相等, 以及键值是否相等
- 2.4 否则直接返回 false
- 否则 转为 字符串 进行比较
- 否则 返回 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
在对象上,定义新的属性 或者 修改现有属性
- 基本语法:
Object.defineProperty(obj, prop, descriptor) - 参数说明: obj: 定义的对象, prop: 新增或者修改的属性, descriptor: 属性描述符
- 返回值: 被传递的对象
描述符(descriptor)
允许添加或者修改对象的属性, 通过赋值操作符的普通属性是可以被枚举的(在
for...in和Object.keys中被枚举出来, 通过descriptor可以对属性进行额外配置, 比如 是否可以 重新赋值, 是否可以被枚举等等, 描述符分为两种形式:数据描述符 和 存取描述符
共有的属性
- configurable 是否可配置 默认值: false
- enumerable 是否允许枚举 默认值: false
1. 数据描述符
- value: 属性值, 默认为:
undefined - writable: 是否可以通过赋值运算符进行 改变 value 值, 默认为 false
2. 存取描述符 (这也是 Vue的实现的原理)
- get: getter函数 获取 属性值,如果没有getter函数,那么就是
undefined - 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()
方法可以冻结一个对象,使得无法 对冻结的对象 进行任何的操作, 属性的修改,删除已经新增属性,简单来说就是 无法进行任何的操作