Vue.js第3课-深入理解Vue组件(part01)

一、组件使用中的细节

1、使用 is 解决 h5 标签上的一些小 bug

面代码中,我们创建了一个全局组件 row,并设置了模板 template,然后将这个组件添加到 table 的 tbody 中,这是一个正常的使用组件的方法,但是打开页面,可以看到模板正常显示,结构却不对,tr 渲染到了 tbody 之外。

Vue.js第3课-深入理解Vue组件(part01)_第1张图片

这是因为在 h5 规范中,正确结构应该是 table > tbody > tr > td,但是现在将组件 row 加到了 tbody 中,所以元素的结构发生了错乱。

接下来看一下如何使用 is 来解决 h5 标签上的 bug,在 tbody 中还是用 tr 标签,将组件的名称放到 is 中去,意思是将 tr 解析为 row 组件。同样在使用 ul 或其他这样标签的时候,也建议用这种方式,以免出现兼容性的错误。

Vue.js第3课-深入理解Vue组件(part01)_第2张图片

2、子组件里定义 data 必须是一个函数

将上面一段代码修改一下,把 td 中的内容放到子组件的 data 中进行存储,看是否能成功渲染出来:

打开页面,发现报错了,是因为组件中的 data 必须是一个 function,不能是一个对象。

Vue.js第3课-深入理解Vue组件(part01)_第3张图片

再修改一下代码,在 function 中返回 content 的值:

此时再刷新页面,可以看到数据能正常的显示。

总结:在子组件里定义 data 的时候,data 必须是个函数,而不能是一个对象,之所以这么设计,是因为一个子组件不像是根组件,只会被调用一次,他可能在不同的地方被调用多次(在例子中我们调用了三次),每一个子组件的数据不希望和其他子组件产生冲突,也就是说每一个子组件都应该有自己的数据,如上例子,每个 row 对应的数据对应的是各自的数据,而不应该共享一套数据,通过一个函数返回对象的目的,就是为了让每一个子组件都有一个独立的数据存储,这样就不会出现多个子组件互相影响的情况。

3、ref(引用) 的使用

Vue 不建议我们在代码里操作 DOM,但是在处理一些极其发杂的动画效果,你不操作 DOM,光靠 Vue 的数据绑定,有的时候处理不了这样的情况,所以在有一些必要的情况下,还真就得操作 DOM。那么,在 Vue 中如何操作DOM 呢?需要通过 ref 这种引用的形式来获取 DOM并操作。

首先给 div 标签起一个的名字,比如 hello,接着,我的需求是这样的:点击 Div 的时候,把里边的内容打印出来。

在这里,因为给 div 起了一个引用的名字叫 ref,所以当被点击的时候,可以通过

this.$refs.hello

来获取该 DOM 元素,他指的是整个 Vue 实例里所有的引用(可以在 Vue 事例中再添加一个 DOM 元素,并给他设置 ref,打印一下 this.$refs,可以看到这个 DOM 的 ref 值也显示在其中),所有的引用中有一个名叫 hello 的引用,他指向的就是这个 Div 所指向的 DOM 节点,然后就可以通过 innerHMTL 来获取 DOM 元素中的内容了。

Hello World!

到这里会发现,这些之和 DOM 节点有关系,和组件并没有关系,实际上不是这样的,现在的 DIV 是一个标签,在标签上加一个 ref,通过

this.$refs

获取到这个引用的时候,你获取到的是一个 DOM 元素,那假设,这是一个组件呢?如果一个组件上加了 ref,通过 this.$refs.hello 获取到的是什么呢?其实,你获取到的是这个组件的引用,接下来通过一个计数器的例子再来理解一下 ref。

{{total}}
Vue.js第3课-深入理解Vue组件(part01)_第4张图片

这个时候,一个 counter 求和的工作就做完了。

总结:当 ref 是写到一个标签上的时候,通过

this.$refs.名字

获取到的内容实际上的标签对应的 dom 元素。当在一个组件上写 ref,通过 “this.$refs.名字”获取到的内容实际上 counter 子组件的一个引用。

二、父子组件的数据传递

上一节学习 ref 的时候,讲解了子组件如何通过事件触发的形式向父组件传递数据,这一节将会系统的借助计数器(counter)这个例子来讲解父子组件之间更多的数据传递方式。

上面代码,我们先创建一个局部组件 counter,可以在模板中先写死一个数据 0,之后在父组件中通过 components 来注册这个组件,这样就可以在父组件的模板里使用这个组件了。

首先我们要讲父组件怎么向子组件传递数据,父组件通过属性的形式来传递数据,可以在子组件中写一个 count='0',或者是 :count='0',加冒号的话,传递给子组件的 0 或 1,就变成一个数字了,如果不加冒号,传给子组件的 0 或 1 实际上就是一个字符串,因为如果加了冒号,后面双引号里的内容实际上就是一个 js 表达式了,不是字符串了,所以他就是一个数字类型了。

父组件通过属性的形式向 counter 这个子组件传递了一个名字叫做 count 的属性,那子组件要接收一下,才能使用这个数据,怎么接受呢?要在这个局部组件中写一个 props,指的是子组件需要接收父组件传递过来的什么内容?要接收一个 count 这样一个属性的内容,写了 props 后,就可以直接在 template 中通过 {{}} 来引用父组件传递过来的数据了。

在 Vue 中,父组件向子组件传值,都是通过属性的形式来传递的,接下来,我们要把子组件累加的这个功能给他做上去,在子组件 template 中绑定一个点击事件 clickFun,每一次被点击了,执行 count++,到页面上,发现确实好用,但是在控制台有报错,警告不要直接修改父组件传递过来的数据。

Vue.js第3课-深入理解Vue组件(part01)_第5张图片

这是因为在 Vue 中有一个单项数据流的概念,也就是父组件可以通过属性向子组件传递参数,传递的参数可以随便的进行修改,这是没问题的,也就是父组件可以随意的向子组件传递参数,但是子组件绝对不能反过来,去修改父组件传递过来的参数,举例来说,如果父组件自身有一个属性是3,过一会又变成4传递给子组件,这是没有任何问题的,但是传递给子组件,子组件接收到数据后,子组件只能用这个数据,不能去修改,之所以 Vue 中有这个单项数据流的概念,原因在于,一旦你的子组件接收的这个 count 不是一个基础类型的数据,而是一个类似 object 的引用类型数据,在子组件里改变了一些传递过来的数据内容,有可能接收的这个引用型的数据还被其他的子组件做使用,这样的话,你这个子组件改变了数据,不仅仅影响了自己的组件,还有可能对其他的子组件造成影响,所以 Vue 这个单项数据流,你子组件不能改变父组件的数据,那如果确实要改变这个 count 的这个值该怎么办?

可以在这个子组件中定义一个 data,return 一个对象,可以在这个对象中定义一个名叫 number 的属性,它的初始值是 this.count,也就是,我从父组件接收到一个 count 这样的数据,把 count 数据复制了一份,放到子组件自己的 data 里,这样的话,在下面,我就不用 count 这个内容了,取而代之,我用我自己的 number,当自身被点击的时候,也不去加 count 了,去加自己的 number。

接着继续完善功能,接下来看子组件向父组件传递数据,之前已经讲过了,通过 $.emit() 来传递,现在来更深入的讲解一下。

{{total}}

在实例中添加一个放总数的 DOM,在根实例中 data 下可以先将 total 写死,因为两个 count 的初始值分别为 1,2,所以可将 total 先定死为 3,实际上这不是一个正确的写法,目前可以先来做个练习,到后面,可以通过计算属性,避免一些数据的冗余。到页面上看一下,1,2,3 没有问题。

Vue.js第3课-深入理解Vue组件(part01)_第6张图片

子组件每一次点击的时候,他可以往外携带一些数据,或者说他向父组件传递一下内容,怎么传递呢?子组件向父组件传值,我们通过事件的形式,也就是在子组件被点击的时候,可以通过 this.emit() 中还可以传入第二个参数,或多个参数,“this.emit 中第二个参数也要修改为 2,,它的效果就是,每点击一次,就加2,然后需要告诉父组件,每次改变,都增加2,所以在父组件 methods 中添加一个子组件改变数据的方法 clickChange,给他传递一个参数 step,指每一次增加多少,因为子组件中 $emit 传入的是 2,所以 step 也就是 2。

Vue.js第3课-深入理解Vue组件(part01)_第7张图片

三、组件参数校验与非 props 特性

1、组件的参数校验

接下来先看一个未经过校验的例子。首先创建一个名字叫做 child 的子组件,在父组件中去调用这个子组件,直接用 child 就可以了,此时去页面上看,child 就可以显示出来了。

1.1、参数的校验

有的时候父组件需要往子组件里传递参数,例如我们一般可以在父组件里写一个 content="Hello World!",通过这种形式,父组件向子组件传递参数,那么组件的校验指的是什么?你父组件向子组件传递了一个内容,那子组件有权对这个内容做一些约束,这些约束我们就可以把它叫做参数的校验。父组件传递 content,子组件势必就要接收 content,我们先把 content 写到 props 里面,写完之后,就可以在模板里通过插值表达式使用这个 content 了。

下一步,就会有这样的需求,调用子组件传递的这个 content,我要做一些约束,例如你传递过来的 content 必须是一个字符串,如果有这样的需求该怎么办呢?如果要实现这样的需求,props 里就不写一个数组了,而是写一个对象,对象的键就是接收的参数的名字,叫做 content,可以写一个 String,这么去写的意思是:子组件接收到 content 这个属性必须是一个 String 字符串的类型,那现在父组件调用子组件,传递 content 的时候,“Hello World!”肯定是一个字符串,所以页面上此时不会有任何问题,我们修改一下父组件,让他传递一个数字,如果直接把 “Hello World!” 修改为“123”,那他依然传递的是一个字符串,回忆一下上一节的内容,如果传递的是数字,就要在属性前加冒号:

这个时候再到页面上看,content 内容虽然正常渲染了,但是却报了一个警告,说类型检测有问题,子组件希望 content 是一个字符串,但是父组件传递过来的却是 Number。

Vue.js第3课-深入理解Vue组件(part01)_第8张图片

假设我希望传递过来的是一个数字,该怎么写呢?直接把 String 改为 Number 就可以了:

此时页面就不会报错了。有时还会有这样的需求,我希望传过来的数据既可以是字符串,也可以是数组,这个时候,可以借助数组的语法,把 Number 和 String 放到一个数组里面,它的意思就是,子组件接收的 content 属性,要么是属性,要么是字符串,所以这个时候,传数字或字符串都不会报错。

Vue.js第3课-深入理解Vue组件(part01)_第9张图片

假设传一个对象呢?

Vue.js第3课-深入理解Vue组件(part01)_第10张图片

就会报错了,说我期待你传的是一个 Number 或 String,但是你传的却是一个 Object,所以就不对了。通过这个例子,就知道什么是组件参数校验了,也就是子组件接收什么参数,是有规则定义的,当然,这些规则还可以变得更复杂。

1.2、参数更复杂的校验

content 后面,不仅仅可以跟 Number,String 一个数组,实际上还可以跟一个对象。

1.2.1、 type 类型的校验

以写一个 type 加上 String,这个组件接收一个名叫 content 的属性,它的类型必须是 String。

打开页面,会告诉我们传的必须是 String,但真正传入的却是一个 Object,所以会报出一个警告。

Vue.js第3课-深入理解Vue组件(part01)_第11张图片

除了 type 还可以写一个 required。

1.2.2、required 接收的属性是否必传

required 的意思是,我这个子组件,接收 content 这个属性,这个属性是否是必传的,例如 required 如果设置为 true,如果在子组件中不传 content,看一下:

Vue.js第3课-深入理解Vue组件(part01)_第12张图片

页面报错,告诉你 content 必传,但是现在缺少了这个 content,如果把 required 改为 false,就不会报任何错误了。

除了写 required 之外,还可以写一个 default。

1.2.3、default 默认值的使用

default 可以随便写一些内容,比如“default value”。

Vue.js第3课-深入理解Vue组件(part01)_第13张图片

可以看到 default value 显示到页面上了。我们这个 child 组件主要接收一个属性,这个属性叫做 content,他不是必填的,也就是这个 content 可传可不传,但是假设你不传,他会使用一个 default 默认值,这个默认值就是 default value,所以你看,在父组件调用子组件的时候,没有传 content,那么子组件里 content 内容就是这个默认的 default value。假设父组件调用子组件的时候,传递了 content,等于一个 “Hello World!”,这个时候,默认值就不会生效了。

再来写一个更复杂的校验。

1.2.4、validator 验证器

要求 content 这个字符串的长度必须在某个长度之间,例如不能小于 5 位,可以借助 validator 的一个配置项来实现,在 validator 中配置一个函数,他会有个形参叫做 value,可以返回 value.length > 5,然后将 content 里的字符删除剩5个以内的字符。

意思是,我子组件要接受一个属性,属性的名字叫做 content,类型必须是一个字符串,同时我要对你传入的这个 content 通过校验器做一个校验,这个 value 就是指你传入的内容,我要求传入的这个人字符串长度必须大于5,上面代码中只传入了两个字符,validator 返回 false,所以校验不通过,所以页面报错了,如果大于5就不会报错。

Vue.js第3课-深入理解Vue组件(part01)_第14张图片

以上就是组件校验的几个重点,接下来看一下非 props 特性。

2、非 props 特性

说到非 props 特性他一定和 props 特性相对应,先来看一下什么是 props 特性:

2.1、props 特性

就拿上边的例子来看,props 特性指的是当你的父组件使用子组件的时候,通过属性向子组件传值的时候,恰好子组件里声明了对父组件传递过来的属性的一个接收,也就是说,父组件调用子组件的时候,传递了 content,子组件恰好在 props 里又申明了这个 content,所以父子组件有一个对应关系,如果你这么去写这种形式的属性,我们就把他叫做 props 特性。

props 特性有什么样的特点呢?打开页面,看一下 DOM 结构。

Vue.js第3课-深入理解Vue组件(part01)_第15张图片

可以看到,有一个 div,里边内容 "he",所以在子组件传递的 content 是不会在 DOM 标签中显示的。

props 还有一个特点,当父组件传递了 content,子组件接收了 content 之后,在子组件里就可以直接通过插值表达式,或者通过 this.content 去取得 content 里边的内容了,所以上边这么写,父组件传递了 content 过来,子组件就能把这个内容显示出来,这就叫做 props 特性。

下来看看什么叫做非 props 特性。

2.2、非 props 特性

非 props 特性指的是,父组件向子组件传递了一个属性,但是子组件并没有 props 这块内容,也就是说子组件并没有声明接收父组件传递过来的内容,如果是这一种情况,我们看一下页面的效果:

Vue.js第3课-深入理解Vue组件(part01)_第16张图片

首先,页面就会报一个错误,说 content 没有被定义,无法使用,这是因为父组件向子组件传递了 content,但是这个时候子组件并没有去接子组件传过来的 content,你不去接收,子组件里就没法使用这个 content,一旦你用,就报错了,这是非 props 特性的第一个情况,就是如果你定义了一个非 props 特性,这个时候 content="he"就是一个非 props 特性。非 props 特性里子组件是没有办法获取到父组件的内容的,因为你压根就没有申明你要获取的内容,所以就没法用。

非 props 特性还有第二个特点,在 template 中不用插值表达式,可以直接写一个 Hello World!

如果现在使用的是一个非 props 特性,那么这个非 props 特性实际上是会显示在 DOM 的属性之中的,我们打开页面看一下:

Vue.js第3课-深入理解Vue组件(part01)_第17张图片

可以看到,div 上面有一个 HTML 属性,上面写着 content="He",很明显,当我们申明一个非 props 特性,它的第二个特点是,这个属性会展示在子组件最外层的 DOM 标签的属性里面。

小结:props 特性要求父组件传,子组件接,然后可以在子组件里直接用父组件传过来的数据,同时 props 特性他不会把属性显示在你的 DOM 标签中,非 props 特性是父组件传,但是子组件不接,那么在子组件里就没法使用父组件传来的数据,同时非 props 特性对应的属性值,其实会显示在子组件最外层的 DOM 标签的属性里面。当然,实际的生产环境中,非 props 特性使用的场景并不是特别的多,这里简要的做一个了解就可以了。


长得好看的都会关注我的 o(≧v≦)o~~

你可能感兴趣的:(Vue.js第3课-深入理解Vue组件(part01))