组件的基本使用 vue2-vue3 props验证、计算属性、自定义事件、v-model

一.组件的基本使用

1.组件的注册

组件之间可以相互引用的

例如:我们想在App中引用 About组件和Home组件

4.png

vue组件的引用原则:先注册后使用

2. 注册组件的两种方式

vue中注册组件的方式分为 “全局注册”和“局部注册”两种,其中

  • 全局注册的组件,可以在全局任何一个组件内使用
  • 局部 注册的组件,只能在当前注册的范围内使用
    5.png
6.png

2.1 全局注册组件

  1. 在main.js导入 组件
  2. 使用 vue的实例.component("组件名",组件)
  3. 在想要使用的组件 使用标签名的形式 进行使用这个全局注册的组件
7.png
8.png

2.2 局部注册组件

  1. 在想要使用的组件内的script标签导入组件

  2. 在components节点注册

  3. 像使用标签一样,使用这个组件

    import xxx from 'xxx'
    export default {
        components:{
            键:值
        }
    }
    
9.png

2.3 全局组件和局部组件的区别

  • 全局组件:可以在全局的任何一个组件内使用
  • 局部组件:只可以当前注册的范围内使用

应用场景:

如果某些组件在开发期间使用的频率很高,推荐进行全局注册:

如果某些组件只在特定的情况下会被使用到,推荐进行局部注册

2.4 组件注册时名称的大小写

在进行组件的注册时,定义组件注册名称的方式有两种:

  • kebab-case 命名法(俗称 短横线命名法,例如 my-swiper、my-test)
  • PascalCase (俗称 帕斯卡命名法大驼峰命名法 ,例如 MySwiper、MyTest)

对于短横线命名来说:

  • 必须严格按照短横线名称进行使用

对于帕斯卡命名来说

  • 既可以严格按照帕斯卡名称进行使用,也可以转化为短横线名称进行使用

在实际的开发中,推荐使用帕斯卡命名法,因为它的适用性更好

2.5 通过name属性注册组件

在注册组件期间,除了可以直接提供组件的注册名称以外,还可以把组件的name属性作为注册后组件的名称

10.png

2.6 样式冲突——了解导致组件之间样式冲突的原因

默认情况下,写在.vue组件中样式会全局生效,因此很容易造成多个组件之间的样式冲突问题。导致组件之间样式冲突的根本原因是:

  • 在单页面应用程序中,所有组件的Dom结构,都是基于唯一的index.html页面进行呈现的。
  • 每个组件中的样式,都会影响整个index.html页面中的DOM元素

2.7 思考:如何解决组件样式冲突的问题

2.7.1 添加自定义属性

为每个组件分配唯一的自定义属性,在编写组件样式时,通过属性选择器控制样式的作用域,示例代码如下:

App.vue

<template>
  <div data-v-001>
    <h1 data-v-001>这是App.vue组件</h1>
    <p data-v-001>App中的p标签</p>
    <p data-v-001>App中的p标签</p>
    <hr />
    <MyList data-v-001></MyList>
  </div>
</template>

<script>
import MyList from "./MyList.vue";
export default {
  components: {
    MyList,
  },
};
</script>

<style lang="less" >
p[data-v-001] {
  color: red;
}
</style>

MyList.vue

<template>
  <div data-v-002>
    <h1 data-v-002>这是List.vue组件</h1>
    <p data-v-002>List中的p标签</p>
    <p data-v-002>List中的p标签</p>
  </div>
</template>

<script>
export default {
  name: "MyList",
};
</script>

<style lang="less" scoped>
</style>
2.7.2 在style节点下添加scoped属性

为了提高开发效率和开发体验,Vue为style节点提供了scoped属性,从而防止组件之间的样式冲突问题:

App.vue

<template>
  <div>
    <h1>这是App.vue组件</h1>
    <p>App中的p标签</p>
    <p>App中的p标签</p>
    <hr />
    <MyList></MyList>
  </div>
</template>

<script>
import MyList from "./MyList.vue";
export default {
  components: {
    MyList,
  },
};
</script>

<style lang="less"  scoped>
p {
  color: red;
}
</style>

MyList.vue

<template>
  <div>
    <h1>这是List.vue组件</h1>
    <p>List中的p标签</p>
    <p>List中的p标签</p>
  </div>
</template>

<script>
export default {
  name: "MyList",
};
</script>

<style lang="less" scoped>
</style>

2.8 /deep/样式穿透

如果给当前组件 的style节点添加了 scoped属性,则当前组件的样式不会对其子组件是不生效的。如果想让某些样式对子组件生效 ,可以使用 /deep/深度选择器

App.vue

<template>
  <div>
    <h1>这是App.vue组件</h1>
    <p>App中的p标签</p>
    <p>App中的p标签</p>
    <hr />
    <MyList></MyList>
  </div>
</template>

<script>
import MyList from "./MyList.vue";
export default {
  components: {
    MyList,
  },
};
</script>

<style lang="less"  scoped>
.title {
  color: blue; //未加/deep/前,实际样式选择器是这样的 .title[data-v-417c4fe3]
}
</style>

MyList.vue

<template>
  <div>
    <h1 class="title">这是List.vue组件</h1>
    <p>List中的p标签</p>
    <p>List中的p标签</p>
  </div>
</template>

<script>
export default {
  name: "MyList",
};
</script>

<style lang="less" scoped>
</style>
2.8.1 使用/deep/后

App.vue

<template>
  <div>
    <h1>这是App.vue组件</h1>
    <p>App中的p标签</p>
    <p>App中的p标签</p>
    <hr />
    <MyList></MyList>
  </div>
</template>

<script>
import MyList from "./MyList.vue";
export default {
  components: {
    MyList,
  },
};
</script>

<style lang="less"  scoped>
/deep/ .title {
  color: blue; // 加了 /deep/ 后,实际上是这样的 [data-v-417c4fe3] .title
}
</style>

MyList.vue

<template>
  <div>
    <h1 class="title">这是List.vue组件</h1>
    <p>List中的p标签</p>
    <p>List中的p标签</p>
  </div>
</template>

<script>
export default {
  name: "MyList",
};
</script>

<style lang="less" scoped>
</style>
2.8.2 注意点

/deep/是vue2.x中实现样式穿透的方案

在vue3.x中推荐使用:deep() 代替 /deep/

11.png

3.组件的props

为了提高组件的复用性,在封装vue组件时需要遵守如下的原则:

  • 组件的DOM结构Style样式要尽量复用
  • 组件要展示的数据,尽量由组件的使用者提供

为了方便使用者为组件提供要展示的数据,vue组件提供了props的概念。

3.1 什么是组件的props

props是组件的自定义属性,组件的使用者可以通过props把数据传递到子组件内部,供子组件内部进行使用。

代码示例如下:

12.png

props的作用:父组件通过props向子组件传递要展示的数据

props的好处:提高了组件的复用性

3.2 在组件中声明props

在封装vue组件时,可以把动态的数据项声明为props自定义属性。

自定义属性可以在当前组件的模板结构中被直接使用。示例代码如下:

MyArticle.vue(子组件)

<template>
  <div>
    <h3>标题:{{ title }}</h3>
    <h5>作者:{{ author }}</h5>
  </div>
</template>

<script>
export default {
  props: ["title", "author"], //父组件传递给子组件的数据,必须在props节点中声明
};
</script>

<style lang="less" scoped>
</style>

App.vue(父组件)

<template>
  <div>
    <h1>这是App.vue组件</h1>
    <hr />
    <MyArticle title="黑马程序员" author="黑马"></MyArticle>
  </div>
</template>

<script>
import MyArticle from "./MyArticle.vue";
export default {
  components: {
    MyArticle,
  },
};
</script>

<style lang="less" scoped>
</style>

3.3 无法使用未声明的props

如果父组件给子组件传递了未声明的props属性,则这些属性会被忽略,无法被子组件使用,示例代码如下:

13.png
14.png

3.4 动态绑定props的值

可以使用v-bind属性绑定的形式,为组件动态绑定props的值,示例代码如下:

15.png

3.5 props的大小写命名

组件中如果使用 "camelCase(驼峰命名法)"声明了props属性的名称,则有两种方式为其绑定属性的值:

  1. 直接使用驼峰命名的名称
  2. 将驼峰命名转化为短横线命名进行使用(大写字母在前面加一个'-'然后大写字母转化为小写)
16.png
17.png

3.6 动态绑定HTML的class

3.6.1 三元表达式

可以通过三元表达式动态为元素绑定class的类名。示例代码如下:

18.png
3.6.2 以数组语法绑定HTML的class

如果元素需要动态绑定多个class类名,此时可以使用数组的语法格式

19.png
20.png
3.6.3 对象语法

使用数组语法绑定class会导致模板结构臃肿问题,此时可以使用对象语法进行简化

21.png

3.7 动态绑定内联的style

:style对象语法十分直观——看起来非常像css,但其实是一个javascript对象。css property名可以用驼峰命名法(camelCase)或短横线命名法(kebab-case,记得用引号括起来)来命名

22.png

4.props 验证

4.1 什么是props验证

指的是:在封装组件时,对外界传过来的props数据进行合法性的校验,从而防止数据不合法的问题。

23.png

使用数组类型的props节点的缺点无法为每个prop指定具体的数据类型

4.2 对象类型的props节点

使用对象类型的props节点,可以为每个prop定义具体的数据类型

24.png

4.3 props验证

对象类型的props节点提供了多种数据验证方案,例如:

  1. 基础的类型检查
  2. 多个可能的类型
  3. 必填项校验
  4. 属性默认值
  5. 自定义验证函数
4.3.1 基础的类型检查

可以直接为组件的prop属性指定具体的基础的类型检查,从而防止组件的使用者为其绑定错误类型的数据

Boolean
Number
String
Array
Function
Object
Symbol
Date
4.3.2 多个可能的类型

如果某个prop属性值的类型不唯一,此时可以通过数组的形式,为其指定多个可能的类型。示例代码如下:

info: [String, Number]
4.3.3 必填项校验

如果某个prop属性是必填项,必须让组件的使用者为其传递属性的值。可以通过如下方式:

25.png
  • type指定的类型
  • required:是否为必填项
4.3.4 默认值

在封装组件时,可以为某个prop属性指定默认值,具体代码如下:

export default{
    props:{
        propA:{
            type:Number,
            default:30
        }
    }
}
  • type:类型
  • default:默认值
4.3.4 自定义验证函数

在封装组件时,可以为prop属性指定自定义验证函数,从而对prop属性的值进行更加精确的控制

26.png

App.vue

<template>
  <div>
    <h1>App.vue组件</h1>
    <hr />
    <Count :state="false" :info="1" type="success"></Count>
  </div>
</template>

<script>
import Count from "./Count.vue";
export default {
  components: {
    Count,
  },
};
</script>

<style lang="less" scoped>
</style>

Count.vue

<template>
  <div>
    <p>数量:{{ count }}</p>
    <p>状态:{{ state }}</p>
  </div>
</template>

<script>
export default {
  props: {
    count: {
      type: Number,
      default: 100,
    },
    state: Boolean,
    info: [String, Number],
    type: {
      validator(value) {
        return ["success", "warning", "danger"].indexOf(value) !== -1;
      },
    },
  },
};
</script>

<style lang="less" scoped>
</style>

5.计算属性

5.1 什么是计算属性

计算属性本质上是一个function函数,它可以实时监听data数据的变化,并return一个计算后的新值,供组件渲染DOM时使用

5.2 如何声明计算属性

计算属性需要以function函数的形式声明到组件的computed选项

  • 直接使用名字即可,后面不需要加小括号
  • 计算属性侧重于得到一个计算的结果,因此计算属性中必须要有return返回值

示例代码如下:

App.vue

<template>
  <div>
    <h1>App根组件</h1>
    <hr />
    <MyCounter></MyCounter>
  </div>
</template>

<script>
import MyCounter from "./MyCounter.vue";
export default {
  components: {
    MyCounter,
  },
};
</script>

<style lang="less" scoped>
</style>

MyCounter.vue

<template>
  <div>
    <input type="text" v-model.number="count" />
    <p>{{ count }}乘以2的值为: {{ getCount }}</p>
  </div>
</template>

<script>
export default {
  name: "MyCounter",
  data() {
    return {
      count: 1,
    };
  },
  computed: {
    getCount() {
      return this.count * 2;
    },
  },
};
</script>

<style lang="less" scoped>
</style>

5.3 计算属性的使用注意点

  • 计算属性必须定义在computed节点中
  • 计算属性必须是一个function函数
  • 计算属性必须有返回值
  • 计算属性必须当做普通属性使用

5.4 计算属性VS方法

相对于方法来说,计算属性会缓存计算的结果,只有计算属性的依赖项发生了变化,才会重新进行运算。因此,计算属性的性能较好。

5.自定义事件

5.1 什么是自定义事件

在封装组件时,为了让组件的使用者可以监听到组件内状态的变化,此时需要用到组件的自定义事件

27.png

5.2 自定义事件的3个使用步骤

在封装组件时:

声明自定义事件

触发自定义事件

在使用组件时:

监听自定义事件

5.2.1 声明自定义事件

开发者在为自定义组件封装的自定义事件,必须事先在emits节点中声明,示例代码如下:

28.png
5.2.2 触发自定义事件

emits声明的自定义事件,可以通过this.$emit("自定义事件的名称")来触发,示例代码如下:

29.png
5.2.3 监听自定义事件

在使用自定义组件时,可以通过v-on监听自定义事件。示例代码如下:

30.png

5.3 自定义事件传参

在调用this.$emit()第二个参数为自定义事件传参数,示例代码如下:

31.png

6.组件上的v-model指令

6.1 为什么需要在组件上使用v-model

v-model是双向数据绑定指令,当需要维护组件内外数据的同步时,可以在组件上使用v-model指令。示意图如下:

32.png
  • 外界数据的变化自动同步到counter组件中
  • counter组件中数据的变化,也会自动同步到外界

6.2 在组件上使用v-model的步骤

6.2.1 父组件向子组件同步数据
  1. 父组件通过v-bind属性绑定的形式,把数据传递给子组件
  2. 子组件中,通过props接收父组件传过来的数据
33.png
6.2.2 子组件向父组件同步数据
34.png
  1. 在v-bind指令前添加v-model指令
  2. 在子组件中声明emits自定义事件,格式为update:xxx (xxx是props某个属性名)
  3. 调用$emit()触发自定义事件,更新父组件中的数据

7.总结

组件的基本使用.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容