『 Vue小Case 』- Vue Prop中的 null vs undefined

一直以来,笔者在使用Vue时,习惯于在需要表示prop属性未定义时,使用undefined,而不是null。因为“undefined才是没有值,null是有值,但是值为空的对象(注意不是空对象{})”。

基于这一习惯,笔者规避掉了很多问题,对此也没有深究。

直到最近,参与项目的一些同学习惯于指定null为初始值,我也没有强制性统一。根本原因是,我自己也觉得没什么,每个人都有自己的习惯,只要大的风格不出偏差就可以接受,毕竟项目紧嘛。

直到今天遇到了下面的场景,我才发现,原来这么做会有一些奇怪的小问题。

一、场景再现

1.1 场景一:prop的值是null,会使用default的值吗?

阅读以下代码,你觉得有问题吗?

HTML:

<div id="app">  <list :items="null"></list></div>

JS:


Vue.component('list', {

  template: '<div>{{ typeof items }} {{ items.join(',') }}</div>',

  props: {

    items: {

      type: Array,

      default() {

        return [1, 2, 3]

      },

    }

  }

})


new Vue({

  el: '#app',

});

上述的代码执行之后有问题吗?停顿5s思考一下,1、2、3、4、5。

好,我们来看下示例代码,这段代码执行之后会报错(记得开启控制台查看错误,因为是JS执行错误)。因为items最终的值是null,而所以没法对null执行join()拼接。那为什么prop值为null时,default中指定的值没有生效呢?

如果你愿意,可以把null换成undefined,你会发现正如你期望的那样,default生效了。

1.2 场景二:prop设置了required,传null可以吗?

既然prop的值为null时,default不会生效,那我们能否通过required强制prop必填呢?

HTML保持场景一不变。JS做如下改动:


Vue.component('list', {

  template: '<div>{{ typeof items }} {{ items.join(',') }}</div>',

  props: {

    items: {

      type: Array,

      required: true,

    }

  }

})


new Vue({

  el: '#app',

});

同样停顿5s思考一下,1、2、3、4、5,好。我们看下 示例代码,这段代码执行时依然会报错。

Vue会给出警告,信息如下:

[Vue warn]: Invalid prop: type check failed for prop \"items\". Expected Array, got Null.(found in component <list>)

从警告内容可以看出null通过了required: true的验证,但是没有通过类型校验,最后浏览器执行报错。如果这时候,你再次尝试将null改成undefined,你会发现依然行不通,会有类似的错误。

好了,至此,我们已经看完了我所想展示的示例。可以总结为以下两个疑问:

  1. 当值为null的时候,为什么default中定义的值没有生效?(显然,当值为undefined的时候,默认值是会生效的)

  2. 当值为null/undefined的时候,为什么required: ture的校验似乎通过了,但是类型校验反倒没有通过?(显然,required: false的时候,null和undefined是都能通过类型校验的。这点文档中有提到。)

在详细解释为什么之前,我们先来熟悉下一个历史悠久的问题:“null和undefined的区别是什么?”

二、null和undefined的区别

在设计之初,JavaScript是这样区分的:null是一个表示"无"的对象,转为数值时为0;undefined是一个表示"无"的原始值,转为数值时为NaN。


Number(null) // 0

5 + null // 5


Number(undefined) // NaN

5 + undefined // NaN

不得不吐槽,真是坑呀

但后来被证明这并不可行。目前,null和undefined基本是同义的,只有一些细微的差别。

null表示没有对象,即此处不应该有值。虽然其表示不应该有值,但它是有值的,值是一个空的对象(注意不是{})。用法如下:

  1. 作为函数的参数,表示该函数的参数不是对象。

  2. 作为对象原型链的终点。

Object.getPrototypeOf(Object.prototype)// null

undefined表示缺少值,就是此处应该有一个值,但是还没有定义

  1. 变量被声明了,但没有赋值时,就等于undefined。

  2. 调用函数时,应该提供的参数没有提供,该参数等于undefined。

  3. 对象没有赋值的属性,该属性的值为undefined。

  4. 函数没有返回值时,默认返回undefined。


var i;

i // undefined


function f(x){console.log(x)}

f() // undefined


var obj = new Object();

obj.p // undefined


var ret = f();

ret // undefined

三、Vue中的Prop校验

好了,我们已经再次熟悉了一下null和undefined的区别。下面就让我们一起看下,Vue中是如何进行Prop校验以及如何对待null和undefined。

笔者阅读了Vue中关于Prop校验的代码,总结出了如下图所示的校验流程:

『 Vue小Case 』- Vue Prop中的 null vs undefined

从图中第三步可以看出,只有prop的值为undefined时,才会去获取default中的值。这解释了第一部分两个奇怪现象的第一个。

代码片段如下:

// check default valueif (value === undefined) {  value = getPropDefaultValue(vm, prop, key)  // since the default value is a fresh copy,  // make sure to observe it.  const prevShouldObserve = shouldObserve  toggleObserving(true)  observe(value)  toggleObserving(prevShouldObserve)}

从图中第四步可以看出,当required: true时,只有不传属性时,才会提示'Missing required prop。当required:false时,null和undefined都会通过校验。其他情况,则都会进行类型校验。这解释了第一部分两个奇怪现象的第二个。

代码片段如下:

// required为true,且不传属性if (prop.required && absent) {  warn(    'Missing required prop: "' + name + '"',    vm  )  return}// required为false,null和undefined校验通过if (value == null && !prop.required) {  return}// 其他情况校验typelet type = prop.type// ...if (type) {  // type校验逻辑...}

好了,我们通过Vue源码中的逻辑解释了为什么会出现第一部分中的奇怪现象。但可能还是没有理解为什么。下面我们继续来看一下大佬们的解释。

四、大佬们的解释

引用一下尤雨溪在issue#6768中的回答,部分内容及翻译如下:

null indicates the value is explicitly marked as not present and it should remain null.

null表示显式地标记值为未指定(是个空值),所以我们保留null。(这里解释了为什么不对null应用default)

undefined indicates the value is not present and a default value should be used if available.

undefined表示值没有指定,如果有默认值,则使用默认值。

required: true indicates neither null or undefined are allowed (unless a default is used)

required: true表示在default没有应用的情况下,null和undefined都不允许。

注意:还有一个小前提,就是给属性指定了类型

(此外,这句话隐式包含了required和default是可以共存的,先应用default,再判断required: true)

在这一评论中,尤雨溪还解释了为什么要这么设计。虽然这样存在歧义,但是这和null与undefined在语言本身中的含义是统一的,如果改变的会造成更多的混乱。

五、总结

比较赞同尤雨溪的解释,虽然JavaScript语言本身存在一定的设计缺陷,但我们对这些缺陷表示知道,并不轻易hack,不然就会出现更多的混淆。与语言本身保持统一是一种更好的方式。

最后,虽然本文标题是《Vue Prop中的 null vs undefined》,但其中频繁涉及到了required和default这两个概念。所以想借此机会和大家一起明确一下Vue中这两个值的具体含义。

  1. required: true表示要求传入该属性,即template中要有该属性。只要有,不管值是什么,都可校验通过(没通过是类型校验的事情)。

  2. default会在value值为undefined时生效,不管是因为没有传入属性还是属性的值就是undefined。所以当你希望触发默认值的时候,一定要使用undefined。


上一篇:springboot+mybatis,扫描不到mapper


下一篇:PHP对接百度智能云之语言处理技术