跳转到内容

Vue

刀刀
0字
0分钟
2026/8/16

Vue 组件

Vue 组件概念

组件就是可复用的 Vue 文件( Vue 实例, 封装标签, 样式和 JavaScript 代码)。

组件化 :封装的思想,把页面上可重用的部分封装为组件,从而方便项目的 开发 和 维护

一个页面, 可以拆分成一个个组件,一个组件就是一个整体, 每个组件可以有自己独立的 结构 样式 和 行为( html , cssjs )

Vue 组件使用

  1. 创建组件,封装代码

  2. 引入组件,注册组件

    • 全局注册:

      main.jsvue.compnent(自定义组件名, 组件对象)

      main.js
      js
      // 目标: 全局注册 (一处定义到处使用)
      // 1. 创建组件 - 文件名.vue
      // 2. 引入组件
      import Pannel from './components/Pannel'
      // 3. 全局 - 注册组件
      Vue.component('PannelG', Pannel)
    • 局部注册:某个 Vue 文件中,components:{ 自定义组件名: 组件对象 }

      App.vue
      js
      import Pannel from './components/Pannel_1'
      export default {
        // 3. 局部 - 注册组件
        components: {
          PannelL: Pannel,
        },
      }
  3. 使用组件

    把注册的组件名当成标签名使用在结构中即可。

Vue scoped 作用

scpoed 添加到 style 标签上的属性;作用:使得样式只作用于当前文件的标签上。

Vue 组件通信

因为每个组件的变量和值都是独立的。

父: 使用其他组件的 Vue 文件。父组件中,通过标签属性的方式将数据传递给子组件。

子: 被引入的组件(嵌入)。子组件中,props 定义变量,用于接收父组件传递过来的参数。

Vue 父传子

关键代码:props

步骤:

  1. 在文件夹 components 下创建子组件 Product.vue

    vue
    <template>
      <div class="my-product">
        <h3>标题: {{ title }}</h3>
        <p>价格: {{ price }}元</p>
        <p>{{ intro }}</p>
      </div>
    </template>
  2. 组件内在 props 定义变量, 用于接收外部传入的值。

    vue
    <script>
    export default {
      props: ['title', 'price', 'intro'],
    }
    </script>
  3. App.vue 中引入注册组件, 使用时, 传入具体数据给组件显示。

    App.vue
    vue
    <template>
      <div>
        <Product
          title="好吃的口水鸡"
          price="50"
          intro="开业大酬宾, 全场8折"
        ></Product>
        <Product
          title="好可爱的可爱多"
          price="20"
          intro="老板不在家, 全场1折"
        ></Product>
        <Product title="好贵的北京烤鸭" price="290" :intro="str"></Product>
      </div>
    </template>
    
    <script>
    // 引入组件
    import Product from './components/MyProduct'
    export default {
      data() {
        return {
          str: '好贵啊, 快来啊, 好吃',
        }
      },
      // 3. 注册组件
      components: {
        Product, // Product: Product // key和value变量名同名 - 简写
      },
    }
    </script>

Vue 父向子配合循环

把数据循环分别传入给组件内显示。每次循环,变量和组件互不影响。

vue
<template>
  <div class="my-product">
    <h3>标题: {{ title }}</h3>
    <p>价格: {{ price }}</p>
    <p>{{ intro }}</p>
  </div>
</template>
<script>
export default {
  props: ['title', 'price', 'intro'],
}
</script>
vue
<template>
  <div>
    <myproduct
      v-for="item in list"
      :key="item.id"
      :title="item.proname"
      :price="item.proprice"
      :intro="item.info"
    ></myproduct>
  </div>
</template>

<script>
import myproduct from './components/myProduct.vue'
export default {
  data() {
    return {
      list: [
        {
          id: 1,
          proname: '超级好吃的棒棒糖',
          proprice: 18.8,
          info: '开业大酬宾, 全场8折',
        },
        {
          id: 2,
          proname: '超级好吃的大鸡腿',
          proprice: 34.2,
          info: '好吃不腻, 快来买啊',
        },
        {
          id: 3,
          proname: '超级无敌的冰激凌',
          proprice: 14.2,
          info: '炎热的夏天, 来个冰激凌了',
        },
      ],
    }
  },
  components: { myproduct },
}
</script>

Vue 父向子单向数据流

父到子的数据流向,叫单向数据流 ,在 Vue 中需要遵循单向数据流原则。

  1. 父组件的数据发生了改变,子组件会自动跟着变。

  2. 子组件不能直接修改父组件传递过来的 props 数据。

    注意:

    关于子组件不能直接修改父组件传递过来的 props 数据中,指的是不能修改其引用的地址(因为是数组类型的数据,属于复杂数据类型),比如用 map 方法或者 filter 方法修改了原数组返回了新数组并覆盖了原数据,此时就是改变了引用地址。

    js
    this.arr = this.arr.filter((item) => item.id != id)

    但是如果仅仅改变了数组内的值,其地址没有发生变化,则可以让子组件改变 props 内的数据。如 splicepush 等方法。

    js
    let index = this.arr.findIndex((item) => item.id == id)
    this.arr.splice(index, 1)

原因:

子组件修改了不通知父组件,会造成数据不一致。因此 Vue 规定,props 是只读的。

父组件传给子组件的是一个对象,子组件修改对象的属性,是不会报错的,对象是引用类型, 互相更新

总结: 所以 props 变量本身是不能重新赋值的。

Vue 子传父

子组件中:在恰当时机(一般是触发某个数据时候)通过 $emit 自定义事件,把值传递给父组件。

父组件中:使用子组件标签上绑定自定义事件来接收数据。

步骤:

  • 子:

    js
    @自定义事件名="methods函数"
    methods函数(){
      this.$emit(自定义名称,索引或id,数值)
    }
    • 参数 1:自定义的名称,可随便更换。
    • 参数 2:由父传子获得的索引或 id
    • 参数 3:变量或数值。
  • 父:

    js
    自定义名称="自定义函数名"
    自定义函数名(参数1,参数2){}
    • 参数 1:接收子组件传递过来的索引或 id
    • 参数 2:接收子组件传递过来的数值。

整体流程如下所示:

vue
  <div class="my-product">
    <h3>标题: {{ title }}</h3>
    <p>价格: {{ price }}</p>
    <p>{{ intro }}</p>
    <button @click="kanFn">砍价</button>
  </div>
</template>
<script>
export default {
  props: ["title", "price", "intro", "index"],
  methods: {
    kanFn() {
      this.$emit("subprice", this.index, 2);
    },
  },
};
</script>
vue
<template>
  <div>
    <h1>hello world</h1>
    <aa></aa>
    <bb></bb>
    <myproduct
      v-for="(item, index) in list"
      :key="item.id"
      :title="item.proname"
      :price="item.proprice"
      :intro="item.info"
      :index="index"
      @subprice="fn"
    ></myproduct>
  </div>
</template>

<script>
import myproduct from './components/myProduct.vue'
export default {
  data() {
    return {
      list: [
        {
          id: 1,
          proname: '超级好吃的棒棒糖',
          proprice: 18.8,
          info: '开业大酬宾, 全场8折',
        },
        {
          id: 2,
          proname: '超级好吃的大鸡腿',
          proprice: 34.2,
          info: '好吃不腻, 快来买啊',
        },
        {
          id: 3,
          proname: '超级无敌的冰激凌',
          proprice: 14.2,
          info: '炎热的夏天, 来个冰激凌了',
        },
      ],
    }
  },
  methods: {
    // 这里的参数是由子组件中的this.$emit中第二第三个参数传来的
    fn(index, price) {
      this.list[index].proprice = this.list[index].proprice - price
    },
  },
  components: { myproduct },
}
</script>

总结

父自定义事件和方法, 等待子组件触发事件给方法传值。

Vue 跨组件通信

作用:让非父子的组件能够使用通用的组件进行通讯:事件总线(event-bus)。

核心语法: 在 src 文件夹下创建一个 EventBus 的文件夹,里面放一个 index.js文件,用于定义事件总线 bus 对象。

js
import Vue from 'vue'
// 导出空白vue对象
export default new Vue()

创建一个子组件 a.vue

引入事件总线 /EventBus/index.js ,为其绑定事件,触发事件后用 $emit() 方法传递参数。

  • 参数 1:自定义方法名称。
  • 参数 2:变量。
a.vue
vue
<template>
  <div>
    <h1>我是a</h1>
    <button @click="give">点我</button>
  </div>
</template>

<script>
import eventBus from '../EventBus/index.js'
export default {
  data() {
    return {
      msg: '我是a',
    }
  },
  methods: {
    give() {
      eventBus.$emit('sendata', this.msg)
    },
  },
}
</script>

创建一个子组件 b.vue

引入事件总线 /EventBus/index.js ,创建好 b.vue 组件后立即监听,使用的方法是创建一个 created 函数,在其之内用 $on 方法来监听自定义事件方法,返回回调函数。

b.vue
vue
<template>
  <div>
    <h1>我是b</h1>
  </div>
</template>

<script>
import eventBus from '../EventBus/index.js'
export default {
  created() {
    eventBus.$on('sendata', (val) => {
      console.log(val)
    })
  },
}
</script>

App.vue 组件中引入两个子组件。

App.vue
vue
<template>
  <div>
    <aa></aa>
    <bb></bb>
  </div>
</template>

<script>
import aa from './components/a.vue'
import bb from './components/b.vue'
export default {
  components: { aa, bb },
}
</script>

总结

空的Vue对象, 只负责 on 注册事件, emit 触发事件, 一定要确保$on 先执行

Vue 拓展

Vue 原生事件绑定到组件

vue
<base_input @input.navite="inputFn"></base_input>

如上有一个 base_input 的子组件,想要使用原生 input 框自带的输入事件,可使用 .navite 修饰符。但是当该子组件根节点不是 input 输入框而是 label (如下),则加了修饰符也不会生效。

vue
<label>
  {{ label }}
  <input
    v-bind="$attrs"
    v-bind:value="value"
  >
</label>

因此官方特地提供了一个 $listeners ,它是一个对象,里面包含了作用在这个组件上的所有监听器。例如:

js
{
  focus: function (event) { /* ... */ }
  input: function (value) { /* ... */ },
}

有了这个 $listeners,可以配合 v-on="$listeners" 将所有的事件监听器指向这个组件的某个特定的子元素。对于类似 <input> 的你希望它也可以配合 v-model 工作的组件来说,为这些监听器创建一个类似下述 inputListeners 的计算属性通常是非常有用的:

js
Vue.component('base-input', {
  inheritAttrs: false,
  props: ['label', 'value'],
  computed: {
    inputListeners: function () {
      var vm = this
      // `Object.assign` 将所有的对象合并为一个新对象
      return Object.assign(
        {},
        // 我们从父级添加所有的监听器
        this.$listeners,
        // 然后我们添加自定义监听器,
        // 或覆写一些监听器的行为
        {
          // 这里确保组件配合 `v-model` 的工作
          input: function (event) {
            vm.$emit('input', event.target.value)
          },
        },
      )
    },
  },
  template: `
    <label>
      {{ label }}
      <input
        v-bind="$attrs"
        v-bind:value="value"
        v-on="inputListeners"
      >
    </label>
  `,
})

现在 <base-input> 组件是一个完全透明的包裹器了,也就是说它可以完全像一个普通的 <input> 元素一样使用了:所有跟它相同的 attribute 和监听器都可以工作,不必再使用 .native 监听器。

贡献者

The avatar of contributor named as duyidao duyidao
The avatar of contributor named as 刀刀 刀刀
The avatar of contributor named as v_duyilin v_duyilin
The avatar of contributor named as 平安喜乐 平安喜乐

页面历史

刀刀博客累计访客 人;文档累计访问量共