高频面试题:
vue
中的data
为啥是函数?
答案是:是不是一定是函数,得看场景。并且,也无需担心什么时候该将data
写为函数还是对象,因为vue
内部已经做了处理,并在控制台输出错误信息。
一、new Vue
场景
new Vue({el: "#app",// 方式一:对象data: {obj: {name: "qb",}},// 方式二:工厂函数// data () {// return {// obj: {// name: "qb",// }// }// },template: `<div>{{obj.name}}</div>`
});
这种场景主要为项目入口或者多个html
页面各实例化一个Vue
时,这里的data
即可用对象的形式,也可用工厂函数返回对象的形式。因为,这里的data
只会出现一次,不存在重复引用而引起的数据污染问题。
二、组件场景
Vue.component("countComponent", {data() {return {count: 1};},template: `<div><button @click='changeCount'>递增</button><span>{{count}}</span></div>`,methods: {changeCount() {this.count++;}}
});
new Vue({el: "#app",template: `<div><countComponent></countComponent><countComponent></countComponent></div>`
});
首先定义全局组件countComponent
,然后将该组件重复使用两次,当定义全局组件的时候,会执行Vue
的component
方法:
// ASSET_TYPES定义在文件shared/constants.js文件中
export const ASSET_TYPES = ['component','directive','filter'
]
// 以下ASSET_TYPES遍历绑定方法的定义在initGlobalAPI(Vue)全局方法挂载阶段完成
import { ASSET_TYPES } from 'shared/constants'
import { isPlainObject, validateComponentName } from '../util/index'
export function initAssetRegisters (Vue: GlobalAPI) {/** * Create asset registration methods. */ASSET_TYPES.forEach(type => {Vue[type] = function ( id: string,definition: Function | Object ): Function | Object | void {if (!definition) {return this.options[type + 's'][id]} else {/* istanbul ignore if */if (process.env.NODE_ENV !== 'production' && type === 'component') {validateComponentName(id)}if (type === 'component' && isPlainObject(definition)) {definition.name = definition.name || iddefinition = this.options._base.extend(definition)}if (type === 'directive' && typeof definition === 'function') {definition = { bind: definition, update: definition }}this.options[type + 's'][id] = definitionreturn definition}}})
}
这里的场景是component
,那么会执行到definition = this.options._base.extend(definition)
进行组件构造函数的实现,这里的this.options._base
就是构造函数Vue
,extend
方法为:
//Vue.extend 方法的定义在initGlobalAPI(Vue)全局方法挂载阶段完成
export function initExtend (Vue: GlobalAPI) {Vue.extend = function (extendOptions: Object): Function {extendOptions = extendOptions || {}const Super = thisconst SuperId = Super.cidconst cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})if (cachedCtors[SuperId]) {return cachedCtors[SuperId]}const name = extendOptions.name || Super.options.nameif (process.env.NODE_ENV !== 'production' && name) {validateComponentName(name)}const Sub = function VueComponent (options) {this._init(options)}Sub.prototype = Object.create(Super.prototype)Sub.prototype.constructor = SubSub.cid = cid++Sub.options = mergeOptions(Super.options,extendOptions)// ...}
}
定义完组件构造函数Sub
后,在为其合并options
时,会执行到mergeOptions
:
/**
* Merge two option objects into a new one.
* Core utility used in both instantiation and inheritance.
*/
export function mergeOptions (parent: Object,child: Object,vm?: Component
): Object {// ...const options = {}let keyfor (key in parent) {mergeField(key)}for (key in child) {if (!hasOwn(parent, key)) {mergeField(key)}}function mergeField (key) {const strat = strats[key] || defaultStratoptions[key] = strat(parent[key], child[key], vm, key)}return options
}
在当前例子中,会通过const options = {}
定义一个空对象,然后分别将parent
和child
上的属性合并到options
上,此时data
的合并策略为:
strats.data = function (parentVal,childVal,vm
) {if (!vm) {if (childVal && typeof childVal !== 'function') {process.env.NODE_ENV !== 'production' && warn('The "data" option should be a function ' +'that returns a per-instance value in component ' +'definitions.',vm);return parentVal}return mergeDataOrFn(parentVal, childVal)}return mergeDataOrFn(parentVal, childVal, vm)
};
这里childVal
类型为object
,即typeof childVal !== 'function'
成立,进而在开发环境会在控制台输出警告并且直接返回parentVal
,说明这里压根就没有把childVal
中的任何data
信息合并到options
中去。
总结
vue
中已经帮我们控制台输出警告,并且不会让组件中的data
合并到options
中去,那么,很友好的处理了开发者的强行将data
写成对象的可能性。
最后
整理了一套《前端大厂面试宝典》,包含了HTML、CSS、JavaScript、HTTP、TCP协议、浏览器、VUE、React、数据结构和算法,一共201道面试题,并对每个问题作出了回答和解析。
有需要的小伙伴,可以点击文末卡片领取这份文档,无偿分享
部分文档展示:
文章篇幅有限,后面的内容就不一一展示了
有需要的小伙伴,可以点下方卡片免费领取