• Vue3.x 从零开始(二)—— 重新认识 Vue 组件


    Vue 3 更新了许多组件中的语法,包括生命周期、filter、setup、teleport 等

    为了介绍这些特性,需要先了解一下 Vue 组件的基本玩法

    这篇文章介绍的内容基本都是沿用 Vue 2 的语法,只在一些细节上有所调整

     

    一、单文件组件

    通过 Vue.createApp() 创建的应用,可以使用 component 方法创建自定义组件

    const app = Vue.createApp({});
    app.component('my-component', {
      data() {
        return {
          name: 'Wise.Wrong'
        }
      },
      template: `<div>hello {{name}}</div>`,
    });

    不过在实际项目中,都是使用“单文件组件”,也就是以 .vue 文件的形式开发

    比如上面组件,改写成单文件组件就是这样的:

    <template>
      <div>hello {{ name }}</div>
    </template>
    
    <script lang="ts">
    /* 当前文件路径: src/components/my-component.vue */
    import { defineComponent } from 'vue';
    export
    default defineComponent({ name: 'MyComponent', data() { return { name: 'Wise.Wrong', }; }, }); </script>

    这里的 data 必须通过函数的形式返回一个对象,而不能直接使用对象

    // 2.x 可以接收对象,但 3.x 的 data 只能是 function 

     

    假如现在还有另一个组件 <home> ,我们希望在 <home> 组件中使用 <my-component>

    可以直接在 <home> 中引入 my-component.vue 文件,然后通过 components 属性注册组件

    上面的代码中,我在 components 中使用的是大驼峰命名组件,而在 <template> 中使用的是短线命名

    这是因为 HTML 本身不区分大小写,Vue 更建议我们在 DOM 中使用全小写的短线命名的方式

     

    二、Props

    现在我们有了一个父组件 <home> 与子组件 <my-component>

    如果我想在父组件中向子组件传递参数,可以通过 Props

    首先在子组件定义 props

    然后父组件传入对应的 props 属性值

    最终结果

    除了定义数组形式的 props 之外,还可以使用对象类型,这样就能对 props 做更多限制

    export default defineComponent({
      // ...
      props: {
        likes: String, // 仅限定类型
        years: {
          type: [String, Number],
          required: true, // 必填
        },
        info: {
          type: Object,
          default: () => ({ // 对象和数组类型的默认值只能用函数返回
            title: '这里是对象类型的 prop',
          }),
        },
        type: {
          // 自定义校验函数
          validator: (val: string) => (
            ['success', 'warning', 'danger'].indexOf(val) !== -1
          ),
        },
      },
      // ...
    });

    如果父组件传参不符合子组件中 props 定义的规则,Vue 会在控制台抛错

    更多关于 Props 的使用可以查看官方文档

     

    三、无状态组件

    在子组件中,props 和 data 一样都是直接挂载到 this 对象下的,可以通过 this.xxx 的方式取值

    对于某些功能型组件,只需要 props 不需要 data。比如这样的一个提示框组件:

    <template>
      <div :class="['alert', type]">
        <div class="alert-content">{{content}}</div>
      </div>
    </template>
    
    <script lang="ts">
    import { defineComponent } from 'vue';
    
    export default defineComponent({
      name: 'Alert',
      props: ['type', 'content'],
    });
    </script>

    这种只接收 props,没有定义 data、methods、computed 等响应数据的组件,被称为无状态组件

    在 Vue 2 中,无状态组件的初始化比普通组件快得多,所以经常会作为性能优化的一个考量

    为了创建一个无状态组件,必须在 <template> 中声明 functional

    <template functional>
    Vue 2
    </template>

    但是在 Vue 3 中,无状态组件不再需要声明 functional

    只需要像一个普通组件一样定义,只是不要添加 data 等响应数据,就像上面的提示框组件那样

    官方文档是这样解释的:

    However, in Vue 3, the performance of stateful components has improved to the point that the difference is negligible.

    但是,在Vue 3中,有状态组件的性能已提高到比肩无状态组件的程度。

    emmmmmm... 相当的膨胀...

    不过我喜欢~

     

    四、Mixin

    与无状态组件相对应的是有状态组件,也就是带有 data 等响应数据的组件,我们工作中写的组件大部分都是有状态组件

    而随着业务的不断扩大,整个应用变得非常臃肿,这时候组件共用和逻辑抽取就尤为重要,这时候可以使用 mixin

     

    假如我们有三个组件,它们的 js 部分都包含以下内容:

    <script>
    export default {
      // ...
      data() {
        return {
          // ...
          company: {
            name: 'Perfect World',
            address: 'Chongqing China',
          },
          loading: false,
        }
      },
      props: {
        // ...
        title: String
      },
      methods: {
        // ...
        fetchCompanyInfo(data) {
          this.loading = true;
          return fetch('/api')
            .then(res => res.json())
            .then(res => {
              this.company = { ...res.data };
            })
            .finally(_ => { 
              this.loading = false;
            })
        }
      },
    };
    </script>

    三个组件都包含响应数据(data) loading 和 company,以及相应的查询方法(methods) fetchInfo,还有一个来自父组件的参数(props) title

    这些逻辑完全一样,如果要在三个组件中各写一份完全一样的代码,就会非常难受,不利于维护

    有了 mixin 之后,我们就能把这些逻辑写在一个对象里面并导出

    /* mixin-test.js */
    export default {
      data: () => ({
        loading: false
      }),
      props: {
        title: String
      },
      methods: {
        fetchCompanyInfo(data) {
          // ...
        }
      }
    }

    然后在需要用到这部分逻辑的组件中引入,并注册到 mixins

    mixins 接收的是一个数组,也就是说可以同时注册多个 mixin

    <script>
    import MixinTest from "./mixin/mixin-test.js";
    
    export default {
      // ...
      mixins: [ MixinTest ],
    };
    </script>

    Vue 在初始化组件的时候,会将引入的 mixin 对象和当前组件进行合并

    在合并的时候如果遇到了同名选项,对于不同的类型 (data、props、methods) 会有不同的策略

    1. 如果是 props、methods、components、directives 冲突,会以组件本身的键值对覆盖 mixin

    2. 对于生命周期这样的钩子函数出现冲突,会先触发 mixin 中的钩子函数,然后触发组件中的钩子函数

    3. 如果 data 发生冲突,将执行浅层次的合并:

    const Mixin = {
      data: ()=> ({
        user: { name: 'Jack', id: 1 }
      })
    }
    
    const Comp = {
      mixins: [Mixin],
      data: () => ({
        user: { id: 2 }
      })
    }
    
    // 最终的结果是:
    {
      user: { id: 2 }
    }

    而在 Vue 2 中,data 会执行深层次的合并,上例的结果会变成:

    {
      user: { id: 2, name: 'Jack' }
    }

    在 Vue 2 的时代,Mixin 是封装的共用逻辑的常用手段,但这种方式一直都受到很多开发者的质疑

    首先就是 mixin 的重名问题,虽然 Vue 有一套合并策略(这个合并策略可以自定义)来处理 mixin 的重名问题

    但组件内部是无法知道 mixin 到底提供了哪些字段,如果后来的维护人员不熟悉组件中的 mixin,在维护代码的时候很容易因为重名的问题导致 bug

    而且 mixin 可以使用组件中的数据,如果维护组件的时候更改了 mixin 所依赖的数据,也会导致 bug

    为了解决这些问题,Vue 3 推出了 Composition API ,用函数式编程的思想来封装组件逻辑

    欲知 Composition API 如何,请听下回分解~

     

  • 相关阅读:
    QML学习笔记(三)-引入Font-awesome
    QML学习笔记(一)-防止鼠标穿透事件
    JS小积累(一)— 判断在线离线
    electron入门笔记(三)- 引入bootstrap
    express搭建服务器
    生成SSH密钥添加到GitHub
    python中常见的错误
    PyCharm在同一个包(package)下,如何把一个.py文件导入另外一个.py文件下
    在PyCharm中导入Numpy和Pygame模块 (win8.1)
    Pycharm中安装Pygame并写第一个程序
  • 原文地址:https://www.cnblogs.com/wisewrong/p/13744013.html
Copyright © 2020-2023  润新知