【vue2】组件基础与组件传值(父子组件传值)

简介: 组件基础概念与全局|局部组件的写法、组件之间传值(父传子、子传父)

一、组件基础

1.组件初印象

1.概念:

组件是把页面上可重用的部分封装成组件,方便项目的开发的维护

2.本质:

组件是有HTML结构,css样式,js业务逻辑的HTML自定义标签(下面详解)

上述这个描述可能存在一点点抽象,vue组件这个描述与我们之前学过的div用大盒子装载是一个意思的。我们拿官网的图来给兄弟姐妹们讲解:

385fece196f34d458e80cca81510f0e7.png

上述我们之前一般是用三个大的div(Header、Main、Aside)来封装我们的页面的再细分下去其他模块结构,现在我们用vue组件表示来就是再Root这个根组件中用引入对应的组件标签就可以了

比如上述结构可以用标签这样写:

记住组件的本质:组件是有HTML结构,css样式,js业务逻辑的HTML自定义标签

这样是不是对组件有点感觉了呢!那我们来看看我们的组件是怎么组成的

2.组件组成

一个组件(.vue文件)由三个标签组成:

小提示 : 快速生成组件三大部分的快捷键:

  • 标签,这里写组件的html结构
  • 标签,这里写组件的js代码
  • 标签,这里写组件的css代码

接下来我们来看个实际的例子

(小提示:按下<可快速生成一个.vue后缀的结构)

338cfb5f172c4a67af4c5d74d5b5775d.png

下面这张图兄弟姐妹们仔细看,对我们学vue很重要呦

764be09206a242d9ad0c9562fd2eb6a1.png

组件 = (页面)盒子 = (html)自定义标签(HTML+CSS+JS) = (代码).vue文件 = (内存)vue实例

3.组件的分类与使用

我们的组件分为两种:①局部组件 ②全局组件

1.局部组件

就是在当前的.vue后缀文件中生效,出了当前.vue后缀的文件就失效。

下面我们来看看我们怎么使用我们的组件:

三大步:①导入 ②注册 ③使用

1.导入局部组件 : 在scrip标签中导入
      import 组件名 from '组件路径'
2.挂载组件 : 在export default里面写一个属性components
      export default {
        components: {
          "标签名": 组件名
        }
      }
3.使用组件 : 像标签一样使用即可,组件可以理解为一个自定义标签
<组件名></组件名>

全局|局部组件案例:

下面我创建两个.vue文件

1.App.vue

<template>
  <!-- 1.组件html代码,组件默认会把template里面的根元素作为挂载点因此下面不需要写el -->
  <!-- 为什么组件data必须是一个函数 :因为组件是需要复用的,如果组件data是一个对象,那么组件在复用的时候就会使用相同的对象地址。一旦在一个地方被修改,其他的也会跟着修改。
        如果组件data是一个函数,组件在每一次复用的时候就会调用这个函数得到一个全新的对象,这样就可以做到在复用的时候每个组件之间的数据都是独立的,互不影响。 -->
  <div>
    <h1>我是App.vue</h1>
    <!-- 1.组件的使用 -->
    <MyPens></MyPens>
  </div>
</template>
<script>
// 1.组件业务js导入
import MyPens from '@/components/MyPens.vue'
export default {
  // 2.组件的注册
  components: {MyPens},
  // 存放vue的实例对象
  data() {
    return {
      // 在这里写data数据.必须是函数!!!,{}本质是new出来了一个
    }
  },
  methods: {},
  computed: {},
}
</script>
<style>
/* 3.组件的样式 */
</style>

2.MyPens.vue

<template>
<div> 
存放HTML结构,不写标签就会报错
<hr>
{{ msg }}
</div>
<!-- 
  1.组件中的最外层父元素只能有一个,不能添加两个平级父元素
  正确:  <div> <div></div> <div>
  错误:   <div></div> <div></div> -->
</template>
<script>
// 2.js代码,写组件js业务逻辑
export default {
    //之前vue实例的代码写在这里:可以放data,methods,计算属性、侦听器等
    //注意哟,组件里面的data是一个函数,返回值就是之前vue实例中的data对象
    data(){
        return{
            msg:'我是初映CY,我是放在MyPens中的'
        }
    },
}
</script>
<style>
/* 3.css:写组件样式 */
</style>

上述我们写了两个.vue分别为App.vue与MyPens.vue,我们在App.vue中引入我们MyPens.vue中的文件。

d1b22b1e4a034cdead18587fd62cf723.png

2.全局组件

使用方法与局部组件一样,唯一区别就是该组件的注册是在main

1.组件分类:

1.局部组件    2.全局组件

1.局部组件:

注册方法与局部组件注册一致,但是注册不在需要的.vue文件中注册,而是在main.js文件中注册

main.js(核心代码如下)

import MyButton from "@/components/MyButton.vue"//文件路径
Vue.component('MyButton',MyButton)//注册全局

a60b57ef5c1e4b4e81d985585987674e.png

下面我们引入下我们的组件到别的文件中

a1a3025de2284d298f1fccb6858e26fa.png

我们引入了到App与MyPens文件中,我们打开App.vue

16df197035cf457eb087baf2f8297f42.png

可以看到我们的全局组件引入成功啦,以上就是全局与局部组件的使用方法。

【额外补充】

①浏览器默认打开加载App.vue文件

问题引入:为什么我们浏览器打开的默认是App.vue的文件呢?其实是因为我们在main.js中确定了App.vue是跟根文件入口,我们项目打开默认走的就是App.vue

这是我们的main.js文件现在的情况:

b6b4826728fa4b1e9ff4fe5a8601a565.png

当我们修改了框起来的配置之后的样子:

411179bd7f2e462fba69711127bb84fb.png

当我们按照这个配置重新运行的时候页面加载的是:(在终端使用命令npm run serve)

b3c45ea164a7487d806f67d7b5239f62.png

可以发现我们成功的将页面的默认加载文件给更改了,改成了MyPens.vue。

②提高子组件css样式的权重

问题引入:当我们的子组件与父(根组件)有重名的的时候,我们是优先加载哪一个的css样式呢?

当我们MyPens.vue与App.vue都有个大div类名为color,并且我们都设置了相同的css样式:

同类名:

f0952ae8e87d4457a823027475195a06.png

同css样式:

0636f59f2f5e47a6aab6a64ef9fb6e31.png

我们在浏览器中查看效果:

35b8e58c9f76440282d7cafef8e97671.png

发现了我们子与父发生了同名的css样式,我们优先加载的是父(根)组件的。但是我们需要子属性的咋办?使用scoped属性即可

183759792eb5444abe4b086da546b188.png

可见当我们在子组件中加载了scoped属性之后,我们子组件的样式就不会被覆盖掉,而是子显示子组件的样式,父显示父组件的样式。

二、组件传值

1.父传子

父传:

父中用v-bind来传数据

如:(v-bind:"属性名" =属性值      该指令可简写为:属性名="属性值")

<子组件> 
:arr="list"//这两种写法均可,推荐使用这种
 v-bind:id="item.id"
 <子组件> 

子收:

子组件中声明props来接收

props写的有两种:

①不声明数据类型写法(不推荐使用)

props:["属性名","属性名"]

如:

props:["name","price","id"]

注意点:是用数组包裹属性名

②声明数据类型写法(推荐使用)

对象名: {
      type: 数据类型, //注意前面需要加:解析不然是字符串类型
      default: xxx, //默认值,可以不用写默认值
    },

如:

props: {
    price: {
      type: Number, //注意前面需要加:解析不然是字符串类型
      default: 100, //默认值
        }
    }

2.子传父

子传:

this.$emit('方法名', 传递的数据)

如:

this.$emit('price', this.id)

注意点:方法名的书写需要引号

父收:

<子组件>
        @子组件定义的方法名='新方法名'
</子组件>

如:

我们在父组件中导入组件并且讲方法名命名为dochange

    <myGoods
      @price="doChange"
    ></myGoods>

在methods:{}中重新定义我们的方法dochange(此处是子组件传递过来的数据),这样我们父子传值就完成了。

methods: {
    // 接收MyGoods传来的值 3.根据定义事件来写属于我们的方法
    doChange(id) {
      //看子文件中传入的事件对象
      console.log(id)
      this.list.forEach((element) => {
        if (element.id === id) {
          element.price--
        }
      })
    },
  },

父子组件案例:

子组件MyGoods.vue

<template>
  <div class="box">
    <!-- 2.获取data中的值 -->
    <p>商品名称:{{ name }}</p>
    <p>商品价格:{{ price }}</p>
    <p>商品编号:{{ id }}</p>
    <button @click="doClick">点我来一🔪</button>
  </div>
</template>
<script>
export default {
  // 1.父传子(单项数据流):子组件中声明props,会平铺到数据中
  // props:["name","price","id"]
  // 上述写法不可传指定类型值,因此我们优化下写法:
  props: {
    name: String,
    price: {
      type: Number, //注意前面需要加:解析不然是字符串类型
      default: 100, //默认值
    },
    id: {
      type: [String, Number],
      required: true, //必传
    },
  },
  methods: {
    // 2.&emit:子传父 自定义事件
    doClick() {
      // price是我们自定义的方法,this.id是传入的实参
      this.$emit('price', this.id)
      // this.price-- //子组件与父组件绑定了,当只在子组件修改的时候,与父组件不同步因此会报错。一般处理数据都是子传父
      // console.log(this.price);
    },
  },
}
</script>
<style scoped>
div {
  border: 2px solid rgb(6, 58, 199);
}
</style>

父组件MyGoods.vue

<template>
  <div>
    <h1>我是父组件</h1>
    <!-- 3.使用组件 -->
    <myGoods
      v-for="item in list"
      :key="item.id"
      :name="item.name"
      :price="item.price"
      :id="item.id"
      @price="doChange"
    ></myGoods>
  </div>
</template>
<script>
// 1.导入组件
import MyGoods from '@/components/MyGoods.vue'
export default {
  components: { MyGoods },
  //2. 注册事件
  data() {
    return {
      list: [
        {
          name: '苹果手机',
          price: 8888,
          id: 1,
        },
        {
          name: '小米手机',
          price: 5888,
          id: 2,
        },
        {
          name: '华为手机',
          price: 7888,
          id: 3,
        },
      ],
    }
  },
  methods: {
    // 接收MyGoods传来的值 3.根据定义事件来写属于我们的方法
    doChange(id) {
      //看子文件中传入的事件对象
      console.log(id)
      this.list.forEach((element) => {
        if (element.id === id) {
          element.price--
        }
      })
    },
  },
}
</script>
<style>
div {
  border: 2px solid rgb(234, 226, 12);
}
</style>

好了兄弟姐妹们,先看下实际的效果:


9f2d2262e9bd45c897bd2ec9fcd7282c.gif


可以看到我们的传值是完全OK的,父组件App.vue里面的数据与我们子文件MyGoods里面的数据是一摸一样的。可得知这两者数据相同并且得到了修改。

下面我们来尝试下,我们父传子之后,我们子不传递给父的情况:

f3f3b3cf42454cf39197d6c137a91875.png

这张图就灵活的显示了当我们在子组件中不传递数据给我们的父组件时,我们只是在我们的页面修改数据,这样就出现了一个小小的BUG,我们页面显示的(子组件数据)与我们App.vue中的数据并不是同步更新的,且当我们点击 来一刀 的时候右上交也会有报错提示。所以可以得出一个结论:当我们需要修改父中的数据我们需要传递过去给父改,我们的props是单项传输之读取的,需要$emit()传过去。



相关文章
|
22天前
|
JavaScript
在 Vue 中处理组件选项与 Mixin 选项冲突的详细解决方案
【10月更文挑战第18天】通过以上的分析和探讨,相信你对在 Vue 中使用 Mixin 时遇到组件选项与 Mixin 选项冲突的解决方法有了更深入的理解。在实际开发中,要根据具体情况灵活选择合适的解决方案,以确保代码的质量和可维护性。
75 7
|
5天前
|
存储 JavaScript 开发者
Vue 组件间通信的最佳实践
本文总结了 Vue.js 中组件间通信的多种方法,包括 props、事件、Vuex 状态管理等,帮助开发者选择最适合项目需求的通信方式,提高开发效率和代码可维护性。
|
5天前
|
存储 JavaScript
Vue 组件间如何通信
Vue组件间通信是指在Vue应用中,不同组件之间传递数据和事件的方法。常用的方式有:props、自定义事件、$emit、$attrs、$refs、provide/inject、Vuex等。掌握这些方法可以实现父子组件、兄弟组件及跨级组件间的高效通信。
|
21天前
|
缓存 JavaScript UED
Vue 的动态组件与 keep-alive
【10月更文挑战第19天】总的来说,动态组件和 `keep-alive` 是 Vue.js 中非常实用的特性,它们为我们提供了更灵活和高效的组件管理方式,使我们能够更好地构建复杂的应用界面。深入理解和掌握它们,以便在实际开发中能够充分发挥它们的优势,提升我们的开发效率和应用性能。
43 18
|
16天前
|
缓存 JavaScript UED
Vue 中实现组件的懒加载
【10月更文挑战第23天】组件的懒加载是 Vue 应用中提高性能的重要手段之一。通过合理运用动态导入、路由配置等方式,可以实现组件的按需加载,减少资源浪费,提高应用的响应速度和用户体验。在实际应用中,需要根据具体情况选择合适的懒加载方式,并结合性能优化的其他措施,以打造更高效、更优质的 Vue 应用。
|
20天前
|
前端开发 UED
vue3知识点:Suspense组件
vue3知识点:Suspense组件
29 4
|
19天前
|
JavaScript 前端开发 测试技术
组件化开发:创建可重用的Vue组件
【10月更文挑战第21天】组件化开发:创建可重用的Vue组件
23 1
|
20天前
|
JavaScript 前端开发 Java
《vue3第五章》新的组件,包含:Fragment、Teleport、Suspense
《vue3第五章》新的组件,包含:Fragment、Teleport、Suspense
26 2
|
20天前
|
Java
vue3知识点:Teleport组件
vue3知识点:Teleport组件
25 1
|
23天前
|
存储 JavaScript
Vue 组件间通信的方式有哪些?
Vue组件间通信主要通过Props、Events、Provide/Inject、Vuex(状态管理)、Ref、Event Bus等实现,支持父子组件及跨级组件间的高效数据传递与状态共享。