04Vue3-组件化开发

组件基础

目录

    • 组件基础
      • 组件基本实例
      • 组件间的通信
        • 1. 父组件传递子组件
        • 2. 子组件传递父组件
      • 父子组件之间的访问方法
        • 1. 子组件调用父组件的方法
        • 2. 父组件调用子组件的方法
      • 插槽slot
        • 1. 基本使用
        • 2. 后备内容
        • 3. 具名插槽
        • 4. 作用域插槽

组件基本实例

App.vue

<template>
  <div>
    <span style="color: blueviolet">App.Vuespan>
  div>
  <HelloWorld>HelloWorld>
template>

<script>
import HW from './components/newHelloWorld'
export default {
  name: 'App',
  data() {
    return {
    }
  },
  components: {
   //若键值名称相同,则写一个即可
    HelloWorld: HW      
  }
}
script>

<style scoped>
style>

newHelloWorld.vue

<template>
  <div>
    <h1>{{msg}}h1>
  div>
template>

<script>
export default {
name: "newHelloWorld",
  data() {
    return {
      msg: 'Hello World!!!!'
    }
  }
}
script>

<style scoped lang='scss'>
/*scoped 可设置样式只在当前组件使用,不往下传递给子组件*/
style>

main.js

import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')

index.html

<div id="app">div>

在这里插入图片描述

组件间的通信

在这里插入图片描述
例子组件层次
在这里插入图片描述

父组件 App.vue

<template>
  <section class="conn">
    <header class="header">
      <my-header>my-header>
    header>
    <div class="main">
      <div class="content">
        <my-main>my-main>
      div>
      <div class="siderbar">
        <my-sider-bar>my-sider-bar>
      div>
    div>
    <footer class="footer">footer>
  section>
template>

<script>
import MyHeader from "@/components/MyHeader";
import MySiderBar from "@/components/MySiderBar";
import MyMain from "@/components/MyMain";
export default {
  name: 'App',
  //子组件声明
  components: {
    MyHeader,
    MyMain,
    MySiderBar
  }
}
script>

<style scoped lang="scss">
$w:600px;
$color1:#ccc;
$color2:#888;

html,body {
  margin: 0;
  padding: 0;
}
.conn {
  width: $w;
  background-color: $color1;
  height: 500px;
  margin: 0 auto;
}
.header {
  width: $w;
  height: 80px;
  background-color: $color2;
}
.main {
  width: 100%;
  height: 300px;
  background-color: yellow;
}
.footer {
  width: 100%;
  height: 100px;
  background-color: green;
}
.content {
  width: 70%;
  height: 300px;
  float: left;
  background-color: rebeccapurple;
}
.siderbar {
  width: 30%;
  height: 300px;
  float: left;
  background-color: aqua;
}
style>

MyHeader.vue

<template>
  <div>
    <h1>{{msg}}h1>
  div>
  <my-conn>my-conn>
  <my-bar>my-bar>
template>

<script>
import MyConn from "@/components/childComp/MyConn";
import MyBar from "@/components/childComp/MyBar";
export default {
name: "MyHeader",
  data() {
    return {
      msg: 'Hello World!!!!'
    }
  },
  //子组件
  components: {
    MyBar,
    MyConn
  }
}
script>

<style scoped>style>

MyMain.vue

<template>
  <my-conn>my-conn>
template>

<script>
import MyConn from "@/components/childComp/MyConn";
export default {
  name: "MyMain",
  components: {
    MyConn
  }
}
script>

<style scoped>style>

MySiderBar.vue

<template>
  <my-bar>my-bar>
  <my-bar>my-bar>
  <my-bar>my-bar>
template>

<script>
import MyBar from "@/components/childComp/MyBar";
export default {
  name: "MySiderBar",
  components: {
    MyBar
  }
}
script>

<style scoped>style>

MyConn.vue

<template>
  <div class="myconn">
    {{mess}}
  div>
template>

<script>
export default {
  name: "MyConn",
  data() {
    return {
      mess: 'this is main test'
    }
  }
}
script>

<style scoped>
.myconn {
  width: 90%;
  height: 150px;
  background-color: brown;
  margin: 10px;
}
style>

MyBar.vue

<template>
  <div class="mybar">
    bar
  div>
template>

<script>
export default {
name: "MyBar"
}
script>

<style scoped>
.mybar {
  width: 50px;
  height: 50px;
  margin: 10px;
  background-color: cornflowerblue;
}
style>

效果
在这里插入图片描述

1. 父组件传递子组件

Prop 是你可以在组件上注册的一些自定义 attribute。当一个值传递给一个 prop attribute 的时候,它就变成了那个组件实例的一个 property
一个组件默认可以拥有任意数量的 prop,任何值都可以传递给任何 prop。

HTML 中的 attribute 名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名:
传递的属性值时,属性名父组件和子组件最好一样,传递MyTitle,子组件就使用MyTitle

App.vue传递给MyMain.vue一个msg值和title值
v-bind 来动态传递 prop

<my-main msg="hello" :title="msg">my-main>
......
data() {
    return {
      msg:'this is app data msg'
    }
}

MyMain.vue从props接收,会发现我们能够在组件实例中访问这个值,就像访问 data 中的值一样。

<template>
  <my-conn>my-conn>
  {{msg}} {{title}}
template>

<script>
import MyConn from "@/components/childComp/MyConn";
export default {
  name: "MyMain",
  //从上一层组件传过来的一个值,接收一个值
  props: ['msg','title'],
  components: {
    MyConn
  }
}
script>
<style scoped>style>

效果
在这里插入图片描述
如果传递一个数组
App.vue

<my-main msg="hello" :title="msg" :article="article">my-main>
......
data() {
    return {
      msg:'this is app data msg',
      article: ['one','two','three']
    }
}

MyMain.vue

<template>
  <my-conn>my-conn>
  {{msg}} {{title}}
  <br>
  <span v-for="(item,index) in article" :key="index">{{item}}<br>span>
template>

<script>
import MyConn from "@/components/childComp/MyConn";
export default {
  name: "MyMain",
  //就要采用对象的方法,写法与数组方式不同
  props: {
    msg: {
      type: String,
      default:'#####'   //设置缺省值,若无传值,等同于在data处声明一个msg:'####'一样
    },
    title: {
      type: String,
      required: true    //表明该属性值必传,否则报错
    },
    article: {
      type: Array,
      default () {                    //Vue2的写法
        return ['aaa','bbb','ccc']
      },
      // default: ['aaa','bbb','ccc']   Vue3支持的写法
    }
  },
  components: {
    MyConn
  }
}
script>
<style scoped>style>

效果
在这里插入图片描述
props可以多层传递,MyMain可以传递给MyConn,写法一样
MyConn.vue

<template>
  <div class="myconn">
    <p>conn contentp>
    <span v-for="(item,index) in article" :key="index">{{item}}<br>span>
  div>
template>

<script>
export default {
  name: "MyConn",
  props: {
    article: {
      type: Array
    }
  }
}
script>

<style scoped>
.myconn {
  width: 90%;
  height: 150px;
  background-color: brown;
  margin: 10px;
}
style>

MyMain.vue

<template>
  <my-conn :article="article">my-conn>
  {{msg}} {{title}}
  <br>
  <span v-for="(item,index) in article" :key="index">{{item}}<br>span>
template>

而App.vue里声明的article属性,一旦有变化,MyMain和MyConn就会随之变化

效果
在这里插入图片描述

2. 子组件传递父组件

子组件MyConn.vue
当点击这个按钮时,会触发changenum()方法,子组件可以通过调用内建的$emit方法并传入事件名称来触发一个事件,同时传递值

<template>
  <div class="myconn">
    <button @click="changenum(2)">让父组件的+2button>
    <br>
  div>
template>
<script>
export default {
  name: "MyConn",
  methods: {
    changenum(num) {
      this.$emit('mycountevent', num);
    }
  }
}
script>
<style scoped>
.....
style>

父组件MyMain.vue
组件实例提供了一个自定义事件的系统来解决这个问题。父级组件可以像处理 native DOM 事件一样通过 v-on@监听子组件实例的任意事件:

<template>
  <div style="width: 200px;height: 50px;background-color: yellow">父组件的count:{{count}}div>
  <my-conn @mycountevent="mydemo">my-conn>
  
template>

<script>
import MyConn from "@/components/childComp/MyConn";
export default {
  name: "MyMain",
  components: {
    MyConn
  },
  data() {
    return {
      count:0
    }
  },
  methods: {
    mydemo(data) {
      this.count += data;
    }
  }
}
script>
<style scoped>style>

效果
04Vue3-组件化开发_第1张图片

父子组件之间的访问方法

1. 子组件调用父组件的方法

子组件MyConn.vue
子组件调用父组件可以采用$parent

<template>
  <div class="myconn">
    <button @click="changenum">++button>
    <br>
  div>
template>

<script>
export default {
  name: "MyConn",
  methods: {
    changenum() {
      this.$parent.add();
    }
  }
}
script>

<style scoped>
......
style>

父组件MyMain.vue
父组件声明了一个add()

<template>
  <div style="width: 200px;height: 50px;background-color: yellow">父组件的count:{{count}}div>
template>

<script>
import MyConn from "@/components/childComp/MyConn";
export default {
  name: "MyMain",
  components: {
    MyConn
  },
  data() {
    return {
      count:0
    }
  },
  methods: {
    add() {
      this.count ++;
    }
  }
}
script>

<style scoped>style>

效果
在这里插入图片描述
若是孙子组件想要访问爷爷组件的方法,也用$parent但是要打两个$parent,也可直接直接使用$root
孙子组件MyConn.vue

methods: {
    changenum() {
      this.$parent.add();
      console.log(this.$parent.count)              //访问MyMain.vue的count
      console.log(this.$parent.$parent.msg)        //访问App.vue的msg
      this.$parent.$parent.appmet()                //访问App.vue的appmet方法
      //等价于
      this.$root.appmet()
    }
  }

在这里插入图片描述

2. 父组件调用子组件的方法

$children$refs
父组件MyMain.vue

<template>
  <button @click="subson">让子组件-1 button>
  <my-conn ref="child">my-conn>
template>

<script>
import MyConn from "@/components/childComp/MyConn";
export default {
  name: "MyMain",
  components: {
    MyConn
  },
  data() {
    return {
      count:0
    }
  },
  methods: {
    subson() {
      console.log('父组件的subson()');
      this.$refs.child.sub()
    }
  }
}
script>

<style scoped>style>

子组件MyConn.vue

<template>
  <div class="myconn">
    子组件的num:{{num}}
  div>
template>

<script>
export default {
  name: "MyConn",
  data() {
    return {
      num: 0
    }
  },
  methods: {
    sub() {
      this.num--;
    }
  }
}
script>

<style scoped>
....
style>

效果
在这里插入图片描述

插槽slot

Vue 实现了一套内容分发的 API,这套 API 的设计灵感源自 Web Components 规范草案,将 元素作为承载分发内容的出口。
插槽可以实现组件的扩展性 , 抽取共性, 保留不同

1. 基本使用

插槽slot是把父组件把数据渲染完了,再插到子组件里
子组件 MyBar.vue

<template>
  <div class="mybar">
    <h6>{{title}}h6>
    <slot>slot>      
  div>
template>

<script>
export default {
name: "MyBar",
  data() {
    return {
      title: 'title'
    }
  }
}
script>

<style scoped>
.mybar {
  width: 80px;
  height: 80px;
  margin-bottom:10px;
  background-color: cornflowerblue;
}
style>

父组件MySiderBar.vue

<template>
  <my-bar>
    <button>提交button>
  my-bar>
  <my-bar>
    <a href="#">提交a>
  my-bar>
  <my-bar>
    <p>提交文本p>
  my-bar>
template>

<script>
import MyBar from "@/components/childComp/MyBar";
export default {
  name: "MySiderBar",
  components: {
    MyBar
  }
}
script>

<style scoped>style>

效果
在这里插入图片描述

2. 后备内容

插槽slot还可以包含任何模板代码,包括 HTML,即设置默认值,若父组件中的不提供任何插槽内容时:

<my-bar>my-bar>
<my-bar>my-bar>
<my-bar>my-bar>

而子组件为一个插槽设置具体的后备 (也就是默认的) 内容,它会在没有提供内容的时候被渲染

<div class="mybar">
    <h6>{{title}}h6>
    <slot><button>提交button>slot>
div>

效果
在这里插入图片描述

3. 具名插槽

多个插槽,若没有设置插槽名称,会把所有的slot都替换掉,我们需要把slot插槽设置名称(具名插槽),按名字指定替换哪个slot,v-slot 简写为 #
v-slot:header 可以被简写为 #header
一个不带 name 的 出口会带有隐含的名字“default”。
子组件 MyBar.vue

<template>
  <div class="mybar">
    <h6>{{title}}h6>
    
    <slot name="one"><button>提交button>slot><br>
    <slot name="two">firstslot><br>
    <slot>secondslot>
  div>
template>

父组件MySiderBar.vue

注意:v-slot 只能添加在 上

<template>
  <my-bar>my-bar>
  <my-bar>
     
     
    <template v-slot:one>
      <a href="#" >替换文本a>
    template>
    
    <template v-slot:default>
      <a href="#" >更新文本a>
    template>
  my-bar>
  <my-bar> my-bar>
template>

效果
在这里插入图片描述

4. 作用域插槽

有时让插槽内容能够访问子组件中才有的数据是很有用的。当一个组件被用来渲染一个项目数组时,这是一个常见的情况,我们希望能够自定义每个项目的渲染方式。

在使用插槽时,父组件若想使用子组件在插槽里的属性值,可以像下面这样:
在这里插入图片描述

要想使 user和sex可用于父级提供的 slot 内容,我们可以添加一个 元素并将其绑定为属性
绑定在 元素上的 attribute 被称为插槽 prop。现在在父级作用域中,我们可以使用带值的 v-slot 来定义我们提供的插槽 prop 的名字:

子组件MyBar.vue

<template>
  <div class="mybar">
    <slot name="one"><button>提交button>slot><br>
    <slot name="two" :sex="sex">性别:男slot><br>
    <slot :user="user">名字:{{ user.name }}slot>
  div>
template>
<script>
export default {
name: "MyBar",
  data() {
    return {
      title: 'title',
      user: {name:'zhangsan'},
      sex: '男'
    }
  }
}
script>

父组件MySiderBar.vue

 <my-bar>
    <template v-slot:one>
      <a href="#" >替换文本a>
    template>
    
   <template v-slot:two="setSex">
      {{ setSex.sex}}
    template>
    
    <template v-slot:default="newuser">
      <a href="#" >{{ newuser.user.name }}a>
    template>
  my-bar>

效果
在这里插入图片描述

你可能感兴趣的:(Vue3,学习,vue)