这是我参与11月更文挑战的第29天,活动详情查看:2021最后一次更文挑战
在前文对Vue
初始化流程中的initState
进行梳理时,对于data
初始化进行数据劫持的内容进行了详细的讲解,留了props
、methods
等选项没有深入探索,大致代码如下:
export function initState(vm) {
const opts = vm.$options; // 获取用户传递的所有选项
// 初始化 props
if (opts.props) initProps(vm, opts.props);
// 初始化 methods
if (opts.methods) initMethods(vm, opts.methods);
// 初始化 data
if (opts.data) {
initData(vm);
} else {
observe((vm._data = {}), true /* asRootData */);
}
// 初始化 computed
if (opts.computed) initComputed(vm, opts.computed);
// 初始化 watch
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch);
}
}
initData
的过程主要做了两件事情:数据劫持和依赖收集
根据initData
可以看到Vue
初始化选项的一个顺序:props
、methods
、data
、coomputed
、watch
在initState
开始之前会有一步对所有的options
进行合并的操作
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
);
在mergeOptions
中会调用normalizeProps
函数,其作用就是将用户传递的props
标准化
const mergeOptions = (parent, child, vm) => {
normalizeProps(child, vm);
}
格式化props
熟悉Vue
的小伙伴知道props
可以是简单的数组,也可以是对象,对象的形式可以配置类型检测、自定义校验和设置默认值,如上面案例使用的是对象的形式,虽然Vue
支持两种形式来定义props
,那么最终需要将两者抹平差异化,统一处理
数组语法定义props
Vue.component('arr-demo', {
props: ['fooProps', 'barProps']
})
对象语法定义props
Vue.component('obj-demo', {
props: {
fooProps: {
type: String,
},
barProps: {
type: String,
},
}
})
两者处理之后的数据格式是一致的
{
fooProps: undefined,
barProps: undefined
}
转为对象
需要封装一个工具函数将两者统一处理,在开始处理之前先介绍两个小工具函数
- 判断是否为普通对象
const isPlainObject = obj => {
return Object.prototype.toString.call(obj) === "[object Object]";
}
camelCasec
格式转变为kebab-case
格式
在Vue
中,props
在JS中使用驼峰命名格式(camelCasec
),在HTML
中使用中划线命名格式(kebab-case
),封装camelize
函数进行转变处理
const camelizeRE = /-(\w)/g;
const camelize = (str) => {
return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : ""));
};
接下来实现如何将两种定义props
写法进行统一
/**
* 将用户传递的props进行处理,全部转换为对象格式
*/
const normalizeProps = options => {
const props = options.props;
// 没有传递props
if (!props) return;
// 处理之后的 props 格式
const res = {};
let i, val, name;
if (Array.isArray(props)) {
// 处理 数组 格式定义props
i = props.length;
// 遍历数组
while (i--) {
val = props[i];
if (typeof val === "string") {
name = camelize(val);
res[name] = { type: null };
}
}
} else if (isPlainObject(props)) {
// 处理 对象 格式写法
// 遍历对象
for (const key in props) {
val = props[key];
name = camelize(key);
res[name] = isPlainObject(val) ? val : { type: val };
}
}
// 使用处理后的数据覆盖久的数据
options.props = res;
}
通过normalizeProps
可以将props
为['fooProps', 'barProps']
转变为
{
fooProps: {type: null},
barProps: {type: null}
}
虽然和最终的形式还存在一些差异,但是我们已经完成一大步,下文让我们一起看看如何初始化props
,加油奥利给