Vue3 组件化开发(2)——组件的拆分

2,720 阅读6分钟

「这是我参与2022首次更文挑战的第22天,活动详情查看:2022首次更文挑战」。

比如我们现在开发一个 App 组件,这个组件中的内容结构如下:

image-20211223223709882

这个组件的内容包含三大块:headermainfooter,如果我们将所有的内容都放在这个 App 组件中的话,随着代码量越来越多,最后会变得越来越难以维护,而且对于可扩展性和可维护性来说都是非常差的。

所以在真实开发中,我们就会对这些代码进行拆分,将它们拆分成一个个小的组件,每个小的组件都有属于自己的逻辑和功能,我们只需要在对应的组件里开发对应的逻辑和功能就可以了。

那么,上面这个 App 组件中的代码如果想通过组件化的方式进行拆分的话,该如何进行拆分呢?比如,我们可以根据下面的结构进行拆分:

组件的拆分

按照如上的拆分方式拆分组件后,我们开发对应的逻辑时只需要去对应的组件中编写即可。

下面,我们打开刚才创建的 learn_component 项目,为了便于讲解多个知识点,我们先删除 src 目录下的 assetscomponentsApp.vue,只保留 main.js,后续将要讲解的每个知识点都放在 src 目录下的一个对应文件夹中,比如,现在我们要讲的第一个知识点是关于组件的拆分和嵌套的,那么我们在 src 目录下新建 01_组件的拆分和嵌套,然后,我们在 01_组件的拆分和嵌套 文件夹下新建 App.vue 文件,开始开发 App 组件。

在使用 VS Code 开发 .vue 文件时,默认情况下 VS Code 是不认识 .vue 文件的,所以你在 .vue 文件中写的代码可能就没有高亮,因此,我们可以安装相应的 VS Code 扩展已提供 .vue 文件的支持。这里推荐使用 Volar(之前官方说过后续会基于 Volar 开发一个属于 Vue 官方的 VS Code 插件),或者使用 Vetur,我这里暂且选择使用 Vetur 扩展。另外,还推荐安装一个提供 Vue 代码片段(snippets)的扩展:Vue VSCode Snippets 或者 Vue 3 Snippets(这两个扩展你也可以都安装),借助于它们提供的代码片段,可以提高我们的开发效率。我这里选择使用 Vue VSCode Snippets 扩展(因为个人觉得 Vue 3 Snippets 提示的代码过多了,有时候会提示一些不想要的东西),需要注意的是 Vue VSCode Snippets 依赖 Vetur,所以必须先启用 Vetur

首先,我们要知道,App.vue 中代码的结构是这样的:

<template>
  
</template>

<<script>
  export default {
    
  }
</script>>

<style scoped>

</style>

但如果我们每次开发新组件(.vue 文件)都手动敲一遍这个结构可能有点麻烦,所以,我们可以借助于安装的 Vue VSCode Snippets 扩展(安装后还需要启用)自动帮我们完成 Vue 代码片段的创建。前期我们主要使用 vbase-css 片段,输入 vbase 后选择 vbase-css 代码片段即可:

image-20211225113841749

效果如下:

<template>
  <div>

  </div>
</template>

<script>
  export default {
    
  }
</script>

<style scoped>

</style>

这里,<template> 下会有一个 <div> 作为根元素,其实在真实开发中,我们还是比较习惯给它一个根元素的(比如我们要开发 Header 组件,就会用一个 <div> 作为根元素,甚至会给这个 <div> 一个 id)。当然,目前 Vue 3 中也可以不加根元素了,但事实上,Vue 3 内部会使用 fragment(片段)来实现根元素的效果(其实 Vue 3fragment 这个东西是借鉴了 React,因为 React 中也是要求多个根组件需要有一个根的,但如果不用根的话可以使用 fragment)。

前面我们把所有的代码都写在了 App.vue 中,我们已经说过,这样做后期会很臃肿难以维护,那么现在,我们就要使用组件拆分的方式将 App.vue 拆分成多个组件再组合起来。比如拆分前 App 组件中模板的结构是这样的:

<template>
  <div id="app">
    <div class="header">
      <h2>Header</h2>
      <h2>NavBar</h2>
    </div>
    <div class="main">
      <h2>Banner</h2>
      <ul>
        <li>商品信息1</li>
        <li>商品信息2</li>
        <li>商品信息3</li>
        <li>商品信息4</li>
        <li>商品信息5</li>
      </ul>
    </div>
    <div class="footer">
      <h2>Footer</h2>
    </div>
  </div>
</template>

拆分后,就多了三个独立的组件:Header.vueMain.vueFooter.vue

image-20211225104626743

Header.vue

<template>
  <div class="header">
    <h2>Header</h2>
    <h2>NavBar</h2>
  </div>
</template>

<script>
  export default {
    
  }
</script>

<style scoped>

</style>

Main.vue

<template>
  <div class="main">
    <h2>Banner</h2>
    <ul>
      <li>商品信息1</li>
      <li>商品信息2</li>
      <li>商品信息3</li>
      <li>商品信息4</li>
      <li>商品信息5</li>
    </ul>
  </div>
</template>

<script>
  export default {
    
  }
</script>

<style scoped>

</style>

Footer.vue

<template>
  <div class="footer">
    <h2>Footer</h2>
  </div>
</template>

<script>
  export default {
    
  }
</script>

<style scoped>

</style>

这样拆分完成后,怎么在 App.vue 中引入拆分出来的这三个组件呢?有两种方案:

  1. 将拆分出来的这三个组件都注册成全局组件,但没有必要,因为全局组件一旦注册,就一直不会销毁了,这样会有一点点浪费性能的;
  2. 当某个组件(比如这里的 App.vue)用到拆分出来的这三个组件时,对它们做局部注册

所以我们采用局部注册组件的方案在 App.vue 中导入这三个组件并使用它们:

<template>
  <div id="app">
    <!-- 3. 注册完这 3 个组件之后,在这里使用它们 -->
    <Header></Header>
    <Main></Main>
    <Footer></Footer>
  </div>
</template>

<script>
  // 1. 导入这 3 个组件
  import Header from './Header.vue';
  import Main from './Main.vue';
  import Footer from './Footer.vue';

  export default {
    // 2. 在 App.vue 中导出的对象中的 components 选项中注册这 3 个组件
    components: {
      Header,
      Main,
      Footer
    }
  }
</script>

<style scoped>

</style>

注意:

  1. 在导入组件时,.vue 后缀可以不加,因为我们当前的项目是用 Vue CLI 4 创建的,而 Vue CLI 4 是基于 Webpack 的,而 Webpack 中有一个 resolve.extensions 配置选项(里面会配置一些后缀名,用来提供给 Webpack 尝试帮助我们解析没有写上文件后缀名的路径),虽然我们没有配置过它,但 Vue CLI 已经帮我们配置过了,并且有帮我们在 resolve.extensions 中配置上 .vue

image-20211225120228818

所以我们导入 .vue 文件时,可以不写 .vue 后缀。但是,不写 .vue 后缀可能会存在没有代码提示的问题,我们待会会说到。

  1. <template> 中使用组件时,组件的名称如果只有一个大写字母开头的单词,写成大写字母开头或小写字母开头都是可以的,但我们这里的例子情况有点特殊,需要使用大写字母,因为 <heade><main><footer> 都是原本就存在的 HTML 元素,所以我们不能通过以小写字母开头的 <heade><main><footer> 来使用对应的 3 个组件,否则会被解析为对应的 HTML 元素而不是我们定义的组件。不过在开发中,一般不会把组件名称命名成某个 HTML 元素的名称,所以在使用组件时我们通常会全部使用小写字母,然后如果有多个单词时就用 - 连字符将多个单词进行连接。更多内容可以查阅官方文档:v3.vuejs.org/guide/compo…

拆分完组件并重新使用拆分出来的组件后,我们再来更新下 src/main.js 文件中导入 App 组件时的路径:

import { createApp } from 'vue'
import App from './01_组件的拆分和嵌套/App.vue'

createApp(App).mount('#app')

然后我们运行 npm run serve 把项目跑起来看下:

image-20211225134519583

可以看到,重新引入拆分出来的 3 个组件后,它们的内容都是可以正常显示的。

但是,现在 Main.vue 中其实还可以分成两块内容,如果我们还想对它们做进一步拆分呢?我们可以这样做:

  1. 再新建两个 .vue 文件:

    image-20211225135236827

    然后将 Main.vue 中的内容抽离到这两个文件中:

    MainBanner.vue 中的内容:

    <template>
      <h2>Banner</h2>
    </template>
    
    <script>
      export default {
        
      }
    </script>
    
    <style scoped>
    
    </style>
    

    MainProductList.vue 中的内容:

    <template>
      <ul>
        <li>商品信息1</li>
        <li>商品信息2</li>
        <li>商品信息3</li>
        <li>商品信息4</li>
        <li>商品信息5</li>
      </ul>
    </template>
    
    <script>
      export default {
        
      }
    </script>
    
    <style scoped>
    
    </style>
    
  2. 然后在 Main.vue 中引用这两个拆分出来的组件(跟前面在 App.vue 中引入 3 个组件的思路是一样的):

    <template>
      <div class="main">
        <main-banner></main-banner>
        <main-product-list></main-product-list>
      </div>
    </template>
    
    <script>
      import MainBanner from './MainBanner.vue';
      import MainProductList from './MainProductList.vue';
    
      export default {
        components: {
          MainBanner,
          MainProductList
        }
      }
    </script>
    
    <style scoped>
    
    </style>
    

再来看下效果:

image-20211225152238231

效果和之前是一样的。

以上,就是关于组件的拆分以及嵌套的说明,在真实开发中,就会按照这种拆分组件的方式,对组件进行各种各样的拆分,拆分之后再讲组件组合在一起,最终形成整个的 App