Vue原理:格式化props

1,318 阅读2分钟

这是我参与11月更文挑战的第29天,活动详情查看:2021最后一次更文挑战

在前文对Vue初始化流程中的initState进行梳理时,对于data初始化进行数据劫持的内容进行了详细的讲解,留了propsmethods等选项没有深入探索,大致代码如下:

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初始化选项的一个顺序:propsmethodsdatacoomputedwatch

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,加油奥利给