组件详解
- 1、组件样式控制
- 1.1、组件定义与使用
- 1.2、全局样式控制
- 1.3、局部作用域样式控制
- 1.4、深度样式控制
- 2、组件通信之props
- 2.1、组件关系
- 2.1.1、父与子关系模式
- 2.1.2、子与父关系模式
- 2.1.3、祖与孙关系模式
- 2.1.4、其他关系(非父子与祖孙)模式
1、组件样式控制
1.1、组件定义与使用
<template><div><h2>App组件标题</h2><p>count:{{ count }}</p><button@click="increment">增加</button></div></template><script>import{ref}from'vue';exportdefault{setup(){constcount=ref(0);constincrement=()=>{count.value++;};return{count,increment}}}</script><style>p{background:#ccc;}</style>App.vue是项目根组件文件,下面尝试定义子组件文件并嵌套在App.vue中。比如在components目录下定义HelloWorld.vue子组件文件。值得一提的是,Vue组件文件中可以缺少script、template、style中的任意一个部分,程序不会报任何错误。
components/HelloWorld.vue文件代码如下。
<template><div><h3>HelloWorld组件标题</h3><div>内部div内容</div></div></template>在App.vue的script中引入components/HelloWorld.vue组件文件,并直接在template中使用HelloWorld组件标签。值得一提的是,之所以能在template中直接使用HelloWorld组件标签,是因为在import引入组件后,Vue会自动注册此组件。
修改后的App.vue文件代码如下。
<template><div><h2>App组件标题</h2><p>count:{{ count }}</p><button@click="increment">增加</button><HelloWorld/></div></template><scriptsetup>importHelloWorldfrom'./components/HelloWorld.vue';import{ref}from'vue';constcount=ref(0);constincrement=()=>{count.value++;};</script><style>p{background:#ccc;}</style>1.2、全局样式控制
在组件中定义的样式,默认是全局有效的。也就是说,其可以作用于当前组件中的标签、子组件的根标签及外部的标签。下面我们来测试一下,给App组件的style标签添加全局样式,代码如下。
div{border:1px solid #aaa;margin:20px;}添加全局样式后的页面效果如图所示。
从图中可以看出,App组件中的样式既影响了当前组件的div,也影响了子组件的所有div和外部页面中的div。
产生该效果的原因也非常简单,因为App组件的style标签中的样式,在打包后就会生成全局样式,没有额外添加其他的限制条件。
1.3、局部作用域样式控制
在App组件的style标签中添加scoped属性,不需要为其指定属性值,它的本质是“scoped=“true””的简写方式,在项目开发中我们采用的都是简写方式,如下所示。
<stylescoped>只修改App组件的style标签,内部的其余样式不做任何修改,修改代码后的页面效果和代码结构如图所示。
从页面效果可以看出,此时的样式只影响当前组件的div和子组件HelloWorld的根标签div,而不再影响子组件的子标签div和外部的div。由此可以得出局部作用域样式的特点:只作用于当前组件的所有标签和子组件的根标签。
局部作用域样式的原理其实并不复杂,其内部主要做了两件事:
- 一旦style声明为scoped,当前组件的所有标签和子组件的根标签就都会自动添加名为data-v-xxx的唯一标识属性。
- 在项目打包运行的页面中,style中的样式选择器的最右侧添加了名为data-v-xxx的属性选择器。这就让局部作用域样式只能作用于带data-v-xxx属性的标签,而此时只有当前组件的标签和子组件的根标签带有此属性,子组件的子标签和外部标签都没有此属性,因此局部作用域样式就只能影响当前组件的标签和子组件的根标签。
1.4、深度样式控制
如何能让组件的局部样式(也就是局部作用域样式)影响子组件的子标签呢?这就需要使用Vue提供的深度作用域选择器来实现。代码其实很简单,只需要将需要进行深度选择的标签用“:deep()”来包含,它就能匹配并影响子组件的子标签。
比如,我们想在App组件的局部作用域样式中,改变HelloWorld子组件的子标签h3的样式,那么可以在App组件中编写如下代码。
<stylescoped>p{background:#ccc;}div{border:1px solid #aaa;margin:20px;}div h3{font-size:40px;}</style>但在运行项目后,开发者会发现标题文字并没有变大,也就是样式没有影响HelloWorld子组件的子标签h3。原因即局部作用域样式是不能作用到子组件的子标签的,此时可以使用深度作用域选择器来实现,也就是使用“:deep()”来包含h3,代码如下。
div :deep(h3) { font-size: 40px; }此时样式已经作用到子组件的子标签h3上了,页面效果如图所示。
深度作用域选择器的原理是什么呢?我们来看一下打包生成的样式就可以知道了,如下所示。
其本质就是将最右侧的属性选择器移动到了deep声明的左侧,这也就意味着整个属性选择器对目标元素没有了属性的要求,这样就可以成功匹配子组件的子标签了。
2、组件通信之props
2.1、组件关系
在App父组件(根组件)中嵌套了HelloWorld子组件。这就意味着Vue的组件是可以互相嵌套的。那么这样的嵌套是一定存在层次关系的,嵌套组件之间也一定存在沟通和信息传递。
2.1.1、父与子关系模式
在App组件中嵌套了HelloWorld子组件,那么相对于HelloWorld子组件而言,App就是它的父组件,因此父与子关系模式是从上到下的,如图所示。
2.1.2、子与父关系模式
将App父组件与HelloWorld子组件的关系换一个视角,从HelloWorld子组件的角度来看App父组件,就变成了从下到上的子与父关系模式,如图所示。
2.1.3、祖与孙关系模式
父下会有子,而子又会再包含子,因此父与孩子的孩子之间的关系就变成了祖孙关系,并且除了祖与孙,还会存在祖与曾孙、祖与玄孙等更深层次的关系。值得一提的是,只要超过了父子,孙、曾孙、玄孙都可以简化为祖与孙关系模式,如图所示。
2.1.4、其他关系(非父子与祖孙)模式
当然父组件可能会包含一个子组件,也可能会包含多个子组件,而子组件中仍旧可能会存在孙、曾孙、玄孙等更深层级的嵌套子组件,那么从横向视角来看,子组件2与子组件1、子组件2与孙组件1、孙组件2与曾孙组件1等关系就成了更为复杂的跨越父与子、祖与孙单向顺序的非父子与祖孙关系模式,如图所示。