Vite+Vue3项目实战:组件化开发与通信指南

一、典型的Vite+Vue3项目结构

续上文成功创建Vue3项目的脚手架,通过visual Studio Code软件打开刚刚创建的文件夹,将会看到这样一个项目结构。

使用Vite构建Vue3项目时,项目结构通常遵循一定的组织规则,以保持代码的清晰和可维护性。以下是一个典型的Vite+Vue3项目结构分析: (部分省略)

补充说明:

①'index.html': 项目的入口HTML文件,其中包含一些基本的HTML结构 和用于挂载Vue应用的''<div id="app"></div>''元素

②router': 路由配置目录,通常包含一个`index.ts`文件,用于设置路由规则

③' store': 状态管理目录,通常包含一个`index.ts`文件,用于配置Vuex或Pinia等状态管理库

二、单组件开发

1、概述:

单组件开发是指在前端框架Vue.js中,将一个Vue组件的模板、逻辑和样式封装在一个单独的文件中进行开发的方式 。这种文件通常以'.vue'为后缀,因此也常被称为单文件组件(Single File Component,简称SFC)。

2、在单组件开发中,一个组件通常包含以下三个部分:

(1) <template>:

这部分定义了的HTML结构,也就是组件的外观。

(2) '<script>':

这部分包含了组件的JavaScript逻辑,定义了组件的行为和数据

(3)'<style>':

这部分写明了组件的样式。通过添加`scoped`属性,可以确保这些样式仅应用于当前组件,避免样式冲突。

3、代码示例:

html 复制代码
<script setup>
   import { reactive } from "vue";
   import Header from "./components/Header.vue";
   import Footer from "./components/Footer.vue";
   import Nav from "./components/Nav.vue";
   const propStudent = {
        name: "李雷",
        age: 19,   
   }
   const propTeacher = reactive( {
        name: "韩梅梅",
        age: 25,   
   })
   const addTeacherAge = () => {
        propTeacher.age ++ 
        console.log(`教师年龄为${propTeacher.age}`);      
   }
</script>

<template>
    <!-- 实验一: 在父组件里,设置子组件的"字符串型"属性(进而用于父组件向子组件传递数据) -->
    <Header propName="王五" propAge="21"  />
    <!-- 实验二: 在父组件里,设置子组件的"对象型"属性(进而用于父组件向子组件传递数据)-->
    <Nav v-bind="propStudent"  />
    <!-- 实验二: 在父组件里,设置子组件的"响应型"属性(进而用于父组件向子组件传递数据) -->
    <Footer v-bind="propTeacher"  />
    <button v-on:click="addTeacherAge">在父组件中增加教师年龄</button>
</template>

<style scoped>

</style>

三、父组件向子组件传递数据

1、概述:

父组件向子组件传递数据是组件化编程中的一种常见操作,它允许父组件 将与子组件有关的信息或数据通过特定的属性(称为props)传递给子组件 。这种方式保证了组件之间的数据传递是清晰定义和可维护的 ,同时也遵循了单向数据流的设计原则,即数据总是从父组件流向子组件,子组件不允许直接修改这些props的值,而是通过事件向上反馈信息,由父组件来决定是否及如何更新数据。

2、父组件向子组件传递数据的过程通常涉及以下步骤:

(1)定义 Props: 父组件在其子组件 标签上定义一些属性,并将数据作为这些属性的值传递。

(2)接收 Props: 子组件在其定义中声明接收这些属性,并在其内部通过'props'对象访问这些数据。

3、示例1:

(1)父组件(App.vue)

html 复制代码
<!-- 父组件的模板 -->
<template>
  <div>
    <child-component :message="helloMessage" />
  </div>
</template>

<script>
import ChildComponent from './components/Header.vue';

export default {
  components: {
    ChildComponent
  },
  data() {
    return {
      helloMessage: 'Hello from Parent!'
    };
  }
}
</script>

在上述代码中,':message="helloMessage"'是一个prop绑定,它将父组件中的'helloMessage'数据传递给'ChildComponent'。

(2)子组件(Header)

html 复制代码
<!-- 子的模板 -->
<template>
  <div>
    <p>{{ message }}</p>
  </div>
</template>

<script>
export default {
  props: ['message']
}
</script>

在子组件中,'props: ['message']'声明了子组件期望从父组件接收一个名为'message'的prop,然后可以在模板中使用这个prop显示数据。

运行结果:

4、示例2

(1)父组件(App.vue)

html 复制代码
<!-- 父组件的模板 -->
<template>
  <div>
    <Header :propName="userName" :propAge="userAge" />
  </div>
</template>
<script setup>
import Header from './components/Header.vue';
const userName = '王晓';
const userAge = 19;
</script>

注释:

①'<template>' 标签:这是Vue组件的模板部分,它定义了组件的结构和HTML标记。

② '<Header :propName="userName" :propAge="userAge" />':这行代码是在父组件中使用了子组件'Header'。'Header'组件通过props(属性)接收数据。 这里有两个属性绑定:

':propName="userName"':这是一个prop绑定,'propName'是子组件'Header'中定义的一个prop名称,它被绑定到父组件中的'userName'变量。这里的`userName`的值是字符串`'王晓'`。

':propuserAge"':这是另一个prop绑定,'propAge'是子组件'Header'中的另一个prop名称,它被绑定到父组件中的'userAge'变量。这里的'userAge'的值是数字'19'。

③'import Header from'./components/Header.vue';':这行代码导入 了名为'Header'的子组件,它位于当前目录下的'components'文件夹中,并且文件名为'Header.vue'。

④ 'const userName = '王晓';'和'const userAge = 19;':这两行代码定义了 一个名为'userName'和'userAge'的常量,并赋值为字符串"王晓"和数字'19'。

(2)子组件(Header.vue)

html 复制代码
<!-- 子的模板 -->
<template>
    <div>
        <h3>示例2</h3>
<p>{{propName}}</p >
<p>{{propAge}}</p >
    </div>
</template>
<script setup>
    const props = defineProps(
        ["propName", "propAge"]
    )
    console.log(props);
</script>
<style scoped>
    div {
        width:25%;
    }
</style>

注释:

①<p>{{propName}}</p>和<p>{{propAge}}</p>是段落元素,其中的**{{propName}}和{{propAge}}** 是一个绑定表达式,用于显示传递给组件的propName属性的值和用于显示传递给组件的propAge属性的值

②const props = defineProps(["propName", "propAge"]) : 使用defineProps定义 了组件的两个属性:propName和propAge。 console.log(props): 在控制台打印出传递给组件的props对象,这将包含propName和propAge属性及其值

③这个组件在接收了propName和propAge后,会将这些值显示在页面上,并在控制台输出这些属性的当前值。

运行结果:

控制台:

四、子组件向父组件传递数据

1、概述:

在Vue中,子组件向父组件传递数据通常是通过自定义事件 来实现的。这种机制允许子组件在其内部状态发生变化时通知父组件父组件可以监听这些事件并响应

2、实现步骤:

(1) 父组件在引用子组件的时候,绑定一个自定义事件监听器

(2)子组件通过'$emit'方法触发 这个自定义事件,并将需要传递的数据作为参数传递给父组件

(3)父组件在自定义事件的监听器中接收这些数据,并可以根据这些数据更新自身的状态

3、子组件(Header.vue)

html 复制代码
<template>
  <div>
    <button @click="sendMessageToParent">发送数据到父组件</button>
  </div>
</template>

<script setup>
import { defineEmits } from 'vue';

// 定义一个自定义事件及其可以接收的参数类型
const emit = defineEmits(['messageFromHeader']);

// 方法用于在点击按钮时触发自定义事件,并传递数据
function sendMessageToParent() {
  const message = 'Hello from Header!';
  emit('messageFromHeader', message);
}
</script>

注释:

(1)'<template>'部分定义了子组件的HTML结构。包含了一个按钮'<button>',用户点击这个按钮将会发送数据到父组件。

(2)'<script setup>' 部分定义了子组件的逻辑。

① 引入了'defineEmits'函数,用于定义组件可以触发的事件以及这些事件可以接收的参数类型。

②使用'defineEmits'定义了一个名为'messageFromHeader'的自定义事件。

③ 定义了一个名为'sendMessageToParent'的函数,当按钮被点击时触发。函数内部创建了一个字符串'message',并通过'emit'函数触发'messageFromHeader'事件,将字符串作为参数传递给父组件。

4.父组件(App.vue)

html 复制代码
<template>
  <div>
    <Header @messageFromHeader="handleMessageFromHeader" />
    <p>从子组件接收到的消息:{{ message }}</p>
  </div>
</template>

<script setup>
import { ref } from 'vue';
import Header from './components/Header.vue';

// 用于接收子组件传递的数据
const message = ref('');

// 事件处理器,用于处理子组件发来的消息
function handleMessageFromHeader(msg) {
  message.value = msg;
}
</script>

注释:

(1)'<template>' 部分定义了组件的HTML结构。

  • 包含了一个'<Header>'组件的标签,用于引入子组件。

  • 有一个段落'<p>'用于显示从子组件接收到的消息。

  • 使用了'{{ message }}'来绑定父组件中的'message'数据,以便动态显示。

  1. '<script setup>'部分,用于定义组件的逻辑。
  • 引入了'ref'函数,它用于创建响应式数据。

  • 引入了'Header'组件,它位于'./components/Header.vue'路径下。

  • 定义了一个名为'message'的响应式数据,初始值为空字符串。

  • 定义了一个名为'handleMessageFromHeader'的事件处理器函数,它用于接收子组件传递的数据。函数将接收到的消息'msg'赋值给'message',从而更新显示的内容。

运行结果:

小结实现步骤:

  1. 父组件通过('<Header>')标签引入子组件,并监听子组件发出的('messageFromHeader')事件。

  2. 父组件定义了一个事件处理器('handleMessageFromHeader')来接收从子组件传递的数据,并将其存储在响应式数据('message')中。

  3. 子组件定义了一个按钮,当用户点击按钮时,执行('sendMessageToParent')函数。

  4. ('sendMessageToParent')函数通过('emit')函数触发('messageFromHeader')事件,并将要发送的消息作为参数传递。

  5. 当父组件监听到('messageFromHeader')事件时,执行('handleMessageFromHeader')函数,更新显示从子组件接收到的消息。

五、跨组件通信

1、概述:

在Vue框架中,跨组件通信指的是组件之间进行数据和方法传递的过程,特别是当这些组件不是简单的父子关系时。 Vue中的跨组件通信是为了在组件之间共享数据和状态实现复杂的交互逻辑,从而构建更加灵活和可维护的前端应用程序。

2、常见的跨组件通信方式

(1) Props 和 Events

这是 Vue 中最常用的父子组件通信方式 。 父组件通过 props 向子组件传递数据 , 子组件则可以通过自定义事件 ( $ emit ) 向父组件发送消息 。代码示例:

①App.vue部分
html 复制代码
<template>
  <div>
    <h1>父组件</h1>
    <Headercomponents :some-prop="message" @some-event="handleEvent" />
  </div>
</template>

<script>
import Headercomponents from './components/Header.vue';

export default {
  components: {
    Headercomponents
  },
  data() {
    return {
      message: 'Hello from Parent Component!'
    };
  },
  methods: {
    handleEvent(data) {
      console.log('Event received from Header:', data);
    }
  }
}
</script>
②Header.vue
html 复制代码
<template>
  <div>
    <h2>子组件</h2>
    <p>{{ someProp }}</p>
    <button @click="sendMessageToParent">Send Message to Parent</button>
  </div>
</template>

<script>
export default {
  props: {
    someProp: String
  },
  methods: {
    sendMessageToParent() {
      this.$emit('some-event', 'Hello from Header Component!');
    }
  }
}
</script>

注释:

在这个示例中:

  • 父组件通过`<Headercomponents :some-prop="message" ...>`向子组件传递了一个名为`someProp`的prop,其值为`message`。

  • 子组件在其`props`定义`中接收了`someProp`,并在模板中显示它。

  • 子组件有一个按钮,当点击按钮时,会通过`this.$emit('some-event', 'Hello from Header Component!')`向父组件发送名为`some-event`的事件,并传递了一条消息。

  • 父通过`@some-event="handleEvent"`监听这个事件,并在`handleEvent`方法中处理它,打印从子组件接收到的消息。

运行结果:

(2) Provide / Inject :

Vue 提供了一个 provide/inject 机制 , 允许祖先组件通过 provide 选项提供数据或方法 , 而任何后代组件都可以通过 inject 选项来接收这些数据或方法 , 无论组件层次结构有多深 。代码示例:

①App.vue部分
html 复制代码
<script setup>
  import { provide, ref } from 'vue'
  import Header from "./components/Header.vue"
  /*
    provide用于某个上层组件将数据广播给所有下层组件。
    若使用了provide和inject来进行数据传递,则一般不需要再使用defineProps
  */
  //【实验1】某个上层组件向所有下层组件广播普通数据
  const web =  {name:"百度一下", url:"www.baidu.com"}
  provide("provideWeb", web)
  //【实验2】某个上层组件向所有下层组件广播响应式数据
  const user = ref(0)
  provide("provideUser",user)
  //【实验3】某个上层组件向所有下层组件广播函数
  const userAdd = () => {
    user.value++
  } 
  provide("provideFuncUserAdd",userAdd)
</script>

<template>
  <h3>我是上层的App组件, 用户数为: {{ user }}</h3>
  <Header/>
</template>

<style scoped></style>
②Nav.vue部分
html 复制代码
<script setup>
    import { inject } from 'vue'
    //【实验1】下层组件通过inject注入上层App组件广播的普通数据
    const web = inject("provideWeb")
    console.log("provideWeb:", web)
    //【实验2】下层组件通过inject注入上层App组件广播的函数
    const funcUserAdd = inject("provideFuncUserAdd")
    console.log("provideFuncUserAdd:", funcUserAdd)
    //【实验3】下层组件通过inject注入上层Header组件广播的普通数据
    const url = inject("provideUrl")
    console.log("provideUrl:", url)
</script>

<template>
    <h3>我是底层的Nav组件</h3>
    <button @click="funcUserAdd">添加用户</button>
</template>

<style scoped></style>
③ Header.vue部分
html 复制代码
<script setup>
    import { provide, inject } from 'vue'
    import Nav from "./Nav.vue"
    //【实验1】下层组件通过inject注入上层App组件广播的响应式数据
    const user = inject("provideUser")
    console.log("provideUser:", user.value)
    //【实验2】某个上层组件向所有下层组件广播函数
    const str = "www.xiaomi.com"
    provide("provideUrl", str)
</script>

<template>
    <h3>我是中间的Header组件</h3>
    <Nav/>
</template>

<style scoped></style>

六、插槽

1、概述:

在Vue中,插槽(Slots)是一种用于组合组件的机制 ,它允许父组件向子组件内部传递内容,而无需担心子组件的内部实现细节。简单来说,插槽就是子组件中用来嵌入父组件内容的占位符

2、主要有以下几种类型:

(1)匿名插槽(默认插槽):

匿名插槽是插槽的基本形式,没有特定的名字 。当父组件在子组件标签内部插入内容时,这些内容会被填充到子组件内部的匿名插槽中

(2)具名插槽:

具名插槽具有特定的名字,父组件可以根据这个名字将内容插入到指定的位置。在一个子组件内部,可以通过`<slot>`标签定义具名插槽,并为其指定一个名字。

(3)作用域插槽:

作用域插槽允许父组件接收子组件传递的数据,并在父组件中可以使用这些数据。通常用于列表渲染等场景,使得父组件可以访问子组件的数据。

3、代码示例:

(1)匿名插槽(默认插槽):

①App.vue部分
html 复制代码
<template>
  <div>
    <Card>
      <p>这是自定义的内容(会被默认插入默认插槽)</p>
      <!-- 【实验1】把代码片段插入默认插槽,可以不使用<template>标签封装 -->
      <h3>插入默认插槽的三级标题</h3>
    </Card>
  </div>
</template>

<script setup>
  import Card from './components/Card.vue';
</script>
② Card.vue 部分
html 复制代码
<template>
  <div>
    <!-- 定义一个默认插槽(不需要命名) -->
    <div class="default-slot">
      <slot></slot>
    </div>
  </div>
</template>

<script setup></script>

<style>
  .default-slot{
    background-color: antiquewhite;
  }
</style>

运行结果:

(2)具名插槽:

①App.vue部分
html 复制代码
<template>
  <div>
    <Card>
      <!-- 【实验】把代码片段插入具名插槽,必须使用<template>标签封装,并且用v-slot:指定插槽的名字,v-slot:简写形式为# -->
      <template v-slot:footer>
        <button @click="handleClick">我是插入具名插槽的按钮</button>
      </template>
    </Card>
  </div>
</template>

<script setup>
  import Card from './components/Card.vue';
  const handleClick = () => {
    alert('插入具名插槽的按钮被点击了!');
  };
</script>
② Card.vue 部分
html 复制代码
<template>
  <div>
    <!-- 定义一个具名插槽(需要通过name命名,最常用) --> 
    <div class="named-slot">
      <slot name="footer"></slot>
    </div> 
  </div>
</template>

<script setup></script>

<style>
  .named-slot{
    background-color: gainsboro;
  }
</style>

运行结果:

(3)作用域插槽:

①App.vue部分
html 复制代码
<template>
  <div>
    <Card>
      <!-- 【实验】作用域插槽:子组件将url和title数据配置到 name="user" 的插槽中,
       当父组件将某个片段插入该插槽时,便可通过 v-slot:user="data" (data是随便取的名字) 来接收这些数据-->
      <template v-slot:user="data">  
        子组件通过作用域插槽传来的数据: {{ data.url }} , {{ data.title }}
      </template>  
    </Card>
  </div>
</template>

<script setup>
  import Card from './components/Card.vue';
  const handleClick = () => {
    alert('插入具名插槽的按钮被点击了!');
  };
</script>
② Card.vue 部分
html 复制代码
<template>
  <div>
    <!-- 定义一个作用域插槽(需要通过name命名,所以它也是具名插槽的一种,但要求在插槽里配置一些数据) -->
    <div class="scoped-slot">
      <slot name="user" url="www.baidu.com" title="百度一下"></slot>
    </div>
  </div>
</template>

<script setup></script>

<style>
  .scoped-slot{
    background-color: pink;
  }
</style>

运行结果;

相关推荐
工业互联网专业7 分钟前
基于springboot+vue的高校社团管理系统的设计与实现
java·vue.js·spring boot·毕业设计·源码·课程设计
Channing Lewis1 小时前
如何实现网页不用刷新也能更新
前端
白宇横流学长1 小时前
基于SpringBoot+Vue的旅游管理系统【源码+文档+部署讲解】
vue.js·spring boot·旅游
努力搬砖的程序媛儿2 小时前
uniapp广告飘窗
前端·javascript·uni-app
dfh00l2 小时前
firefox屏蔽debugger()
前端·firefox
张人玉2 小时前
小白误入(需要一定的vue基础 )使用node建立服务器——vue前端登录注册页面连接到数据库
服务器·前端·vue.js
大大。2 小时前
element el-table合并单元格
前端·javascript·vue.js
一纸忘忧2 小时前
Bun 1.2 版本重磅更新,带来全方位升级体验
前端·javascript·node.js
杨.某某2 小时前
若依 v-hasPermi 自定义指令失效场景
前端·javascript·vue.js
猫猫村晨总2 小时前
基于 Vue3 + Canvas + Web Worker 实现高性能图像黑白转换工具的设计与实现
前端·vue3·canvas