github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/public/libs/vue-1.0.24/src/util/options.js (about) 1 import Vue from '../instance/vue' 2 import config from '../config' 3 import { 4 extend, 5 set, 6 isObject, 7 isArray, 8 isPlainObject, 9 hasOwn, 10 camelize, 11 hyphenate 12 } from './lang' 13 import { warn } from './debug' 14 import { commonTagRE, reservedTagRE } from './component' 15 16 /** 17 * Option overwriting strategies are functions that handle 18 * how to merge a parent option value and a child option 19 * value into the final value. 20 * 21 * All strategy functions follow the same signature: 22 * 23 * @param {*} parentVal 24 * @param {*} childVal 25 * @param {Vue} [vm] 26 */ 27 28 var strats = config.optionMergeStrategies = Object.create(null) 29 30 /** 31 * Helper that recursively merges two data objects together. 32 */ 33 34 function mergeData (to, from) { 35 var key, toVal, fromVal 36 for (key in from) { 37 toVal = to[key] 38 fromVal = from[key] 39 if (!hasOwn(to, key)) { 40 set(to, key, fromVal) 41 } else if (isObject(toVal) && isObject(fromVal)) { 42 mergeData(toVal, fromVal) 43 } 44 } 45 return to 46 } 47 48 /** 49 * Data 50 */ 51 52 strats.data = function (parentVal, childVal, vm) { 53 if (!vm) { 54 // in a Vue.extend merge, both should be functions 55 if (!childVal) { 56 return parentVal 57 } 58 if (typeof childVal !== 'function') { 59 process.env.NODE_ENV !== 'production' && warn( 60 'The "data" option should be a function ' + 61 'that returns a per-instance value in component ' + 62 'definitions.', 63 vm 64 ) 65 return parentVal 66 } 67 if (!parentVal) { 68 return childVal 69 } 70 // when parentVal & childVal are both present, 71 // we need to return a function that returns the 72 // merged result of both functions... no need to 73 // check if parentVal is a function here because 74 // it has to be a function to pass previous merges. 75 return function mergedDataFn () { 76 return mergeData( 77 childVal.call(this), 78 parentVal.call(this) 79 ) 80 } 81 } else if (parentVal || childVal) { 82 return function mergedInstanceDataFn () { 83 // instance merge 84 var instanceData = typeof childVal === 'function' 85 ? childVal.call(vm) 86 : childVal 87 var defaultData = typeof parentVal === 'function' 88 ? parentVal.call(vm) 89 : undefined 90 if (instanceData) { 91 return mergeData(instanceData, defaultData) 92 } else { 93 return defaultData 94 } 95 } 96 } 97 } 98 99 /** 100 * El 101 */ 102 103 strats.el = function (parentVal, childVal, vm) { 104 if (!vm && childVal && typeof childVal !== 'function') { 105 process.env.NODE_ENV !== 'production' && warn( 106 'The "el" option should be a function ' + 107 'that returns a per-instance value in component ' + 108 'definitions.', 109 vm 110 ) 111 return 112 } 113 var ret = childVal || parentVal 114 // invoke the element factory if this is instance merge 115 return vm && typeof ret === 'function' 116 ? ret.call(vm) 117 : ret 118 } 119 120 /** 121 * Hooks and param attributes are merged as arrays. 122 */ 123 124 strats.init = 125 strats.created = 126 strats.ready = 127 strats.attached = 128 strats.detached = 129 strats.beforeCompile = 130 strats.compiled = 131 strats.beforeDestroy = 132 strats.destroyed = 133 strats.activate = function (parentVal, childVal) { 134 return childVal 135 ? parentVal 136 ? parentVal.concat(childVal) 137 : isArray(childVal) 138 ? childVal 139 : [childVal] 140 : parentVal 141 } 142 143 /** 144 * Assets 145 * 146 * When a vm is present (instance creation), we need to do 147 * a three-way merge between constructor options, instance 148 * options and parent options. 149 */ 150 151 function mergeAssets (parentVal, childVal) { 152 var res = Object.create(parentVal || null) 153 return childVal 154 ? extend(res, guardArrayAssets(childVal)) 155 : res 156 } 157 158 config._assetTypes.forEach(function (type) { 159 strats[type + 's'] = mergeAssets 160 }) 161 162 /** 163 * Events & Watchers. 164 * 165 * Events & watchers hashes should not overwrite one 166 * another, so we merge them as arrays. 167 */ 168 169 strats.watch = 170 strats.events = function (parentVal, childVal) { 171 if (!childVal) return parentVal 172 if (!parentVal) return childVal 173 var ret = {} 174 extend(ret, parentVal) 175 for (var key in childVal) { 176 var parent = ret[key] 177 var child = childVal[key] 178 if (parent && !isArray(parent)) { 179 parent = [parent] 180 } 181 ret[key] = parent 182 ? parent.concat(child) 183 : [child] 184 } 185 return ret 186 } 187 188 /** 189 * Other object hashes. 190 */ 191 192 strats.props = 193 strats.methods = 194 strats.computed = function (parentVal, childVal) { 195 if (!childVal) return parentVal 196 if (!parentVal) return childVal 197 var ret = Object.create(null) 198 extend(ret, parentVal) 199 extend(ret, childVal) 200 return ret 201 } 202 203 /** 204 * Default strategy. 205 */ 206 207 var defaultStrat = function (parentVal, childVal) { 208 return childVal === undefined 209 ? parentVal 210 : childVal 211 } 212 213 /** 214 * Make sure component options get converted to actual 215 * constructors. 216 * 217 * @param {Object} options 218 */ 219 220 function guardComponents (options) { 221 if (options.components) { 222 var components = options.components = 223 guardArrayAssets(options.components) 224 var ids = Object.keys(components) 225 var def 226 if (process.env.NODE_ENV !== 'production') { 227 var map = options._componentNameMap = {} 228 } 229 for (var i = 0, l = ids.length; i < l; i++) { 230 var key = ids[i] 231 if (commonTagRE.test(key) || reservedTagRE.test(key)) { 232 process.env.NODE_ENV !== 'production' && warn( 233 'Do not use built-in or reserved HTML elements as component ' + 234 'id: ' + key 235 ) 236 continue 237 } 238 // record a all lowercase <-> kebab-case mapping for 239 // possible custom element case error warning 240 if (process.env.NODE_ENV !== 'production') { 241 map[key.replace(/-/g, '').toLowerCase()] = hyphenate(key) 242 } 243 def = components[key] 244 if (isPlainObject(def)) { 245 components[key] = Vue.extend(def) 246 } 247 } 248 } 249 } 250 251 /** 252 * Ensure all props option syntax are normalized into the 253 * Object-based format. 254 * 255 * @param {Object} options 256 */ 257 258 function guardProps (options) { 259 var props = options.props 260 var i, val 261 if (isArray(props)) { 262 options.props = {} 263 i = props.length 264 while (i--) { 265 val = props[i] 266 if (typeof val === 'string') { 267 options.props[val] = null 268 } else if (val.name) { 269 options.props[val.name] = val 270 } 271 } 272 } else if (isPlainObject(props)) { 273 var keys = Object.keys(props) 274 i = keys.length 275 while (i--) { 276 val = props[keys[i]] 277 if (typeof val === 'function') { 278 props[keys[i]] = { type: val } 279 } 280 } 281 } 282 } 283 284 /** 285 * Guard an Array-format assets option and converted it 286 * into the key-value Object format. 287 * 288 * @param {Object|Array} assets 289 * @return {Object} 290 */ 291 292 function guardArrayAssets (assets) { 293 if (isArray(assets)) { 294 var res = {} 295 var i = assets.length 296 var asset 297 while (i--) { 298 asset = assets[i] 299 var id = typeof asset === 'function' 300 ? ((asset.options && asset.options.name) || asset.id) 301 : (asset.name || asset.id) 302 if (!id) { 303 process.env.NODE_ENV !== 'production' && warn( 304 'Array-syntax assets must provide a "name" or "id" field.' 305 ) 306 } else { 307 res[id] = asset 308 } 309 } 310 return res 311 } 312 return assets 313 } 314 315 /** 316 * Merge two option objects into a new one. 317 * Core utility used in both instantiation and inheritance. 318 * 319 * @param {Object} parent 320 * @param {Object} child 321 * @param {Vue} [vm] - if vm is present, indicates this is 322 * an instantiation merge. 323 */ 324 325 export function mergeOptions (parent, child, vm) { 326 guardComponents(child) 327 guardProps(child) 328 if (process.env.NODE_ENV !== 'production') { 329 if (child.propsData && !vm) { 330 warn('propsData can only be used as an instantiation option.') 331 } 332 } 333 var options = {} 334 var key 335 if (child.extends) { 336 parent = typeof child.extends === 'function' 337 ? mergeOptions(parent, child.extends.options, vm) 338 : mergeOptions(parent, child.extends, vm) 339 } 340 if (child.mixins) { 341 for (var i = 0, l = child.mixins.length; i < l; i++) { 342 parent = mergeOptions(parent, child.mixins[i], vm) 343 } 344 } 345 for (key in parent) { 346 mergeField(key) 347 } 348 for (key in child) { 349 if (!hasOwn(parent, key)) { 350 mergeField(key) 351 } 352 } 353 function mergeField (key) { 354 var strat = strats[key] || defaultStrat 355 options[key] = strat(parent[key], child[key], vm, key) 356 } 357 return options 358 } 359 360 /** 361 * Resolve an asset. 362 * This function is used because child instances need access 363 * to assets defined in its ancestor chain. 364 * 365 * @param {Object} options 366 * @param {String} type 367 * @param {String} id 368 * @param {Boolean} warnMissing 369 * @return {Object|Function} 370 */ 371 372 export function resolveAsset (options, type, id, warnMissing) { 373 /* istanbul ignore if */ 374 if (typeof id !== 'string') { 375 return 376 } 377 var assets = options[type] 378 var camelizedId 379 var res = assets[id] || 380 // camelCase ID 381 assets[camelizedId = camelize(id)] || 382 // Pascal Case ID 383 assets[camelizedId.charAt(0).toUpperCase() + camelizedId.slice(1)] 384 if (process.env.NODE_ENV !== 'production' && warnMissing && !res) { 385 warn( 386 'Failed to resolve ' + type.slice(0, -1) + ': ' + id, 387 options 388 ) 389 } 390 return res 391 }