在 uni-app 中封装自定义组件

7,984 阅读6分钟
我正在参加跨端技术专题征文活动,详情查看:juejin.cn/post/710123…

一、什么是组件

在前端开发中,组件可谓是非常有用的工具。

什么是组件,组件就是一个封装起来的,单独且可复用的功能模块,英文叫做:Component

uni-app 中有很多内置的基础组件和扩展组件,开发者可以根据需要自行选择,虽然这些组件可以满足日常开发中大部分业务需求了,但是实际上仍然有部分需求需要个性化,因为每个项目是不一样的。

当遇到有些组件没有时,我们就可以自己去封装,当然,DCloud 插件市场中的插件也足够丰富,使用之前可以先查找是否有符合自己需求的插件,DCloud 插件市场传送门,点击直达。

使用组件的优势:

  • 可以将组件进行任意次数的复用。
  • 合理的划分组件,有助于提高应用性能。
  • 代码更加方便组织和管理,并且扩展性也更强,便于多人协同开发。
  • 组件化开发能大幅度提高应用开发效率、测试性、复用性等。

二、如何封装组件

下面以封装一个卡片为例,来体验如何封装组件。

1. 创建组件目录

首先在项目目录下新建一个 components 目录,这个目录用来存放组件。

然后可以进一步细分目录,每一个组件一个单独的目录,比如这里新建一个 my-card 目录。

image.png

2. 新建组件文件

在组件目录下新建一个 vue 文件,组件名称最好和目录名称一致(符合 easycom 规范引入)。

image.png

3. 开发组件

在组件中简单实现一个卡片:

my-card.vue

<template>
    <view class="card-wrapper">{{content}}</view>
</template>

<script>
    export default {
        name:"myCard",
        data() {
            return {
                content: "这是组件内容"
            };
        }
    }
</script>

<style>
    .card-wrapper {
        height: 150px;
        padding: 10px;
        border-radius: 10px;
        background-color: red;
    }
</style>

这个文件中,有两点需要注意:

(1)结构部分的根节点,也就是最外层元素,必须为 template 标签,而且这个 template 标签里面只能出现一个 view ,否则会造成错误。

比如:

<template>
    <view>这是第一个view</view>
    <view>这是第二个view</view>
</template>

则报错:

Errors compiling template:Component template should contain exactly one root element.

(2)一个组件中的 data 部分,要定义成一个函数,而不是一个对象。

正确(定义成函数):

data() {
    return {
        content: "这是组件内容"
    };
}

错误(定义成对象):

data: {		
    content: "这是组件内容"
}

如果将 data 部分定义成对象,控制台会报错:

[Vue warn]: The "data" option should be a function that returns a per-instance value in component definitions.

如果注意了这两点并且没有出错的话,那么一个简单的组件就封装好了。

三、如何使用组件

组件封装好后,就可以使用了,我们在另一个 vue 文件中使用这个组件,使用方法很简单:

index.vue

<template>
    <view class="content">
        <!-- 使用组件 -->
        <my-card></my-card>
    </view>
</template>

<script>
    export default {
        data() {
            return {

            }
        },
        onLoad() {

        },
        methods: {

        }
    }
</script>

<style>
    .content {
        margin: 20px;
    }
</style>

只需要在使用组件的地方写组件的名称即可,即组件文件名,注意使用的时候是双标签。

以上例子的效果为:

image.png

可以看出和以往不同的是,在使用组件的时候,并没有进行任何的引入和注册操作,其实这是 uni-appeasycom 引入机制,它将组件的引入精简到了一步。

怎么确定组件符合 easycom 规范?

只要组件安装在项目的 components 目录下,并符合 components/组件名称/组件名称.vue 目录结构,就可以不用引用、注册,直接在页面中使用,这就是符合 easycom 规范的组件。

  • easycom 是自动开启的,不需要手动开启。
  • 不管 components 目录下安装了多少组件,easycom 打包后会自动剔除没有使用的组件,对组件库的使用尤为友好。

更多关于 easycom 的介绍,点我前往 >>>

四、Props

prop 是组件中一种自定义的属性,是用来从父组件向子组件传递数据的,给子组件输入其需要的数据。

比如,卡片的背景色需要显示不同的颜色,每个卡片的颜色可能不一样,这就需要在使用卡片组件的时候给组件传递一个颜色值,这个颜色值,就是一个 prop

首先我们需要在组件中声明需要的 prop

my-card.vue

<script>
export default {
    name:"myCard",
    // 在此声明 props
    props: {
        // 卡片背景色属性
        bgColor: {
            // 值类型
            type: String,
            // 默认值
            default: '#FFFFFF'
        }
    },
    data() {
        return {
            content: "这是组件内容"
        };
    }
}
</script>

然后把声明好的 prop 应用到样式中:

my-card.vue

<template>
    <view class="card-wrapper" :style="{ backgroundColor: bgColor }">{{content}}</view>
</template>

在使用组件的时候就可以给卡片指定背景色了:

index.vue

<template>
    <view class="content">
        <!-- 使用组件 -->
        <my-card bgColor="green"></my-card>
    </view>
</template>

这时候卡片的背景色就是 bgColor 这个 prop 的值,如果使用组件的时候不传值,默认是白色。

prop 在声明的时候可以是简单的数组,也可以是对象类型,对象类型具有更丰富的操作,如类型检测、自定义验证和设置默认值。

对象类型可以有以下几个属性:

选项类型说明
typeString 、 Number 、 Boolean 、 Array 、 Object 、 Date 、 Function 、 Symbol ,任何自定义构造函数、或上述内容组成的数组会检查一个 prop 是否是给定的类型,否则抛出警告
defaultany为该 prop 指定一个默认值。如果该 prop 没有被传入,则换做用这个值。对象或数组的默认值必须从一个工厂函数返回。
requiredBoolean定义该 prop 是否是必填项
validatorFunction自定义验证函数会将该 prop 的值作为唯一的参数代入。在非生产环境下,如果该函数返回一个 false 的值 (也就是验证失败),一个控制台警告将会被抛出。

简单的数组可以这样使用:

props: [
    'bgColor'
]

然后我们再给卡片加一个 prop ,来改变卡片中文字的颜色:

my-card.vue

props: {
    // 卡片背景色属性
    bgColor: {
        // 值类型
        type: String,
        // 默认值
        default: '#FFFFFF'
    },
    // 卡片文字的颜色
    fontColor: {
        // 值类型
        type: String,
        // 默认值
        default: '#000000'
    }
}

再应用到 style 中:

my-card.vue

<template>
    <view class="card-wrapper" :style="{ backgroundColor: bgColor, color: fontColor }">{{content}}</view>
</template>

使用组件时的传值:

index.vue

<my-card bgColor="green" fontColor="white"></my-card>

以上传入的颜色值都是静态的,当然也可以动态绑定,比如:

index.vue

<template>
    <view class="content">
        <!-- 使用组件 -->
        <my-card :bgColor="cardBackgroundColor" :fontColor="cardFontColor"></my-card>
    </view>
</template>

<script>
export default {
    data() {
        return {
            // 卡片背景色
            cardBackgroundColor: 'green',
            // 卡片文字颜色
            cardFontColor: 'white'
        }
    }
}
</script>

以上操作出现的效果:

image.png

五、插槽

插槽就是替换内容的,现在卡片的背景色和文字颜色可以在使用组件的时候指定,但是卡片中的内容还没有做到值的传入,我们在使用组件的时候也可以传入卡片的内容。

首先需要在组件中写入 <slot></slot> 标签。

my-card.vue

<template>
    <view class="card-wrapper" :style="{ backgroundColor: bgColor, color: fontColor }">
        <slot></slot>
    </view>
</template>

然后在使用组件的地方传入内容即可:

index.vue

<my-card :bgColor="cardBackgroundColor" :fontColor="cardFontColor">我是组件内容</my-card>

当组件渲染的时候,<slot></slot> 将会被替换为 “我是组件内容”。

插槽内可以包含任何模板代码,比如加入一个基础按钮组件:

<my-card :bgColor="cardBackgroundColor" :fontColor="cardFontColor">
    我是组件内容
    <button type="primary">按钮</button>
</my-card>

如果组件中原来就有内容,也不会受到影响,比如:

my-card.vue

<view class="card-wrapper" :style="{ backgroundColor: bgColor, color: fontColor }">
    这是组件中本来就有的内容
    <br />
    <slot></slot>
</view>

index.vue

<!-- 使用组件 -->
<my-card :bgColor="cardBackgroundColor" :fontColor="cardFontColor">
    我是组件内容
</my-card>

渲染结果为:

image.png

六、总结

关于组件的使用,先介绍到这里,这只是基本的使用方法,组件还包括其它很多知识,比如 ref 、单向数据流、自定义事件、插槽的其它使用等等,可以到 uni-app 官网进一步的学习。

简单总结一下:

  • 先定义组件,最好符合 easycom 规范。
  • 定义需要使用的 prop
  • 定义插槽,替换内容。
  • 使用组件,传入相应的 prop 和内容。

最后放一个完整的小案例:

my-card.vue

<template>
    <view class="card-wrapper" :style="{ backgroundColor: bgColor, color: fontColor }">
        欢迎使用组件
        <br />
        <slot></slot>
    </view>
</template>

<script>
    export default {
        name:"myCard",
        props: {
            // 卡片背景色属性
            bgColor: {
                // 值类型
                type: String,
                // 默认值
                default: '#FFFFFF'
            },
            // 卡片文字的颜色
            fontColor: {
                type: String,
                default: '#000000'
            }
        },
        data() {
            return {
                content: "这是组件内容"
            };
        }
    }
</script>

<style>
    .card-wrapper {
        padding: 10px;
        border-radius: 10px;
        background-color: red;
        height: 150px;
        margin-bottom: 10px;
    }
</style>

index.vue

<template>
    <view class="content">
        <!-- 使用组件 -->
        <my-card v-for="item in cardList" :bgColor="item.bg_color" :fontColor="item.font_color">
            {{item.content}}
        </my-card>
    </view>
</template>

<script>
    export default {
        data() {
            return {
                cardList: [
                    {
                        id: 1,
                        bg_color: 'red',
                        font_color: 'white',
                        content: '组件一'
                    },
                    {
                        id: 2,
                        bg_color: 'black',
                        font_color: 'white',
                        content: '组件二'
                    },
                    {
                        id: 3,
                        bg_color: 'orange',
                        font_color: 'white',
                        content: '组件三'
                    }
                ]
            }
        }
    }
</script>

<style>
    .content {
        margin: 20px;
    }
</style>

image.png

七、其它

1. uni-app 官网 >>>

2. uni-app 组件的使用 >>>