# 组件库下载和使用
`@epoint-mrc/em-components` 是基于 M8 跨端框架开发的,符合新点小程序规范的跨端组件库。
**M8 移动前端跨端框架已默认集成了本组件库,开发者无须再次下载。**
**同时,跨端框架已配置好了`easycom`配置,vue 文件内无需引用、注册跨端 UI 组件,可直接在 template 中使用。**
#### 通过函数调用的组件
组件库中部分组件可以通过函数调用。
为简化引入方式,在跨端框架中,将这些组件函数注册成为全局函数,当使用函数方式调用组件时,会在编译时自动引入对应文件。
以下是跨端框架为组件库注册的全局函数:
```js
// 框架通过 ProvidePlugin 插件为页面注入对应文件
new webpack.ProvidePlugin({
// 键为全局函数名,值为该函数对应的文件路径
Toast: [require.resolve('@epoint-mrc/em-components/src/components/em-toast/index.js'), 'default'],
Dialog: [require.resolve('@epoint-mrc/em-components/src/components/em-dialog/index.js'), 'default'],
EmMap: [require.resolve('@epoint-mrc/em-components/src/components/em-map/index.js'), 'default'],
Notify: [require.resolve('@epoint-mrc/em-components/src/components/em-notify/index.js'), 'default'],
ImagePreview: [require.resolve('@epoint-mrc/em-components/src/components/em-image-preview/index.js'), 'default'],
SelectPerson: [require.resolve('@epoint-mrc/em-components/src/components/em-select-person/index.js'), 'default'],
Locale: [require.resolve('@epoint-mrc/em-components/src/components/locale/index.js'), 'default'],
// 方便ejs调用的专属组件
EjsApi: [require.resolve('@epoint-mrc/em-components/src/components/ejs-api/index.js'), 'default'],
}),
```
## 组件的公共属性
每个组件都有以下两个公共属性,方便开发者简单修改组件样式。
| 参数 | 说明 | 类型 | 默认值 | ::: ifdef M83 weex 端支持 ::: endif |
| :----------- | :----------------: | :----- | :----- | :----------------------------------- |
| custom-class | 自定义组件额外类名 | string | - | 否 |
| custom-style | 自定义组件额外样式 | string | - | 是(有可能不会生效,优先级规则不同) |
**注意**
1. 直接在组件上设置`class`来修改样式,在小程序中可能不会生效,所以建议通过以上两个属性修改组件样式。
2. 以上属性旨在为开发者需要简单修改样式的场景下提供便利,例如设置组件间的间距等。
3. 若有更复杂的样式修改,或者需要修改组件内部节点的样式,以上属性无法满足,建议直接通过样式穿透修改内部节点样式。
4. 小程序有一套自己的样式隔离策略,这与`vue`的`scoped`属性不同,若开启了小程序的样式隔离,通过`::v-deep`也无法作用在子组件内部。
支付宝小程序的样式隔离是默认关闭的,所以可以在外部对子组件内部节点类名进行修改。而微信小程序一般情况下自定义组件的样式隔离是默认开启的,所以若要修改组件库中的组件的样式,可以有两种方式:
(1)跨端组件库通过微信小程序的外部样式类实现 `custom-class` 功能,简单场景下,可以通过`custom-class` 修改组件最外层的样式。
(2)若要修改组件内部节点样式,请添加样式隔离选项关闭微信小程序默认的样式隔离:
```js
export default {
options: {
styleIsolation: 'shared'
}
};
```
然后就可以直接对子组件的类名选择器进行修改。关于微信小程序的样式隔离请看:[组件样式隔离](https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/wxml-wxss.html#%E7%BB%84%E4%BB%B6%E6%A0%B7%E5%BC%8F%E9%9A%94%E7%A6%BB)。
部分框架组件由于内部设置了`styleIsolation: 'shared'`,导致在微信小程序端无法正常使用 `custom-class`,这种情况下请使用 `custom-style` 或者参考以上第四点使用样式穿透直接修改组件内部样式类。
微信小程序端无法正常使用 `custom-class`的组件有:`em-field`、`em-search`、`em-date-picker`、`em-steps`。
5. 注意,使用子组件的插槽时,插槽内的自定义节点样式选择器请不要嵌套在子组件内部节点样式类选择器中,这样写的样式在微信小程序中不会生效。
```html
<em-panel title="标题" desc="描述信息" status="状态">
<view class="text">容器</view>
</em-panel>
```
```scss
::v-deep .em-panel {
.text {
color: red; // 这个不会生效
}
}
.text {
color: yellow; // 这个正常生效
}
```
::: ifdef M84
## M8.4 调整
M8.4 框架改为使用 Vue3,跨端组件库在 M8.3 基础上要相应做出一些调整:
**1. v-model 双向绑定**
所有组件双向绑定的默认 prop 由`value`改为`modelValue`,默认触发事件由`input`改为`update:modelValue`;
**2. 作用域插槽注意**
测试发现当前版本的 uniapp 框架存在以下 bug:
支付宝小程序端,默认插槽如果是一个作用域插槽,且设置了后备内容,在父组件访问子组件的数据时,该默认插槽不会正常渲染。
例如以下情况,父组件中的子组件将不会显示默认插槽中自定义的内容:
子组件`child-component`:
```html
<template>
<view class="content">
<slot :msg="msg">后备内容</slot>
</view>
</template>
<script setup lang="ts">
const msg = '你好';
</script>
```
父组件:
```html
<template>
<view class="content">
<child-component>
<template #default="{ msg }">{{ msg }}</template>
</child-component>
</view>
</template>
```
解决方案:可以换一种方式,通过判断是否存在 `$slots.default` 来设置后备内容。
即: 将以上子组件`child-component`改为下面的代码:
```html
<template>
<view class="content">
<view v-if="!$slots.default">后备内容</view>
<template v-else>
<slot :msg="msg"></slot>
</template>
</view>
</template>
<script setup lang="ts">
const msg = '你好';
</script>
```
**3. 组合式 API 注意**
为了适配组合式 API,全局组件新增默认的`ref`值`emXXXRef`。
如果项目使用组合式 API 编写代码,使用如`Toast`这样的全局组件时,在支付宝小程序端,原本的默认`ref`值`emToast`会与引入 setup 中的组件名冲突,所以新增一个`ref`值`emToastRef`来进行兼容。
同时,为了让全局方法可以拿到页面中的组件节点,需要使用 `defineExpose` 将`ref`变量暴露出来。
示例:
```html
<template>
<view class="content">
<em-button @click="onClick">点击提示</em-button>
<em-toast ref="emToastRef"></em-toast>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const emToastRef = ref();
const onClick = () => {
Toast('测试');
};
defineExpose({
emToastRef
});
</script>
```
M8.4 框架中,建议全局组件的`ref`值都改为`emXXXRef`,与组件名进行区分。
::: endif