# 3.9 Divider 分割线

### 0x00 简介

组件 `Divider` 多用于区隔内容。 本文将深入分析组件源码，剖析其实现原理，耐心读完，相信会对您有所帮助。`packages/divider/src/main.vue` 文件是组件源码实现。 🔗 [组件文档 Divider](https://element.eleme.cn/#/zh-CN/component/divider) 🔗 [github源码 main.vue](https://github1s.com/ElemeFE/element/blob/dev/packages/divider/src/main.vue)

更多组件剖析详见 👉 [**📚 Element 2 源码剖析组件总览**](https://juejin.cn/post/6994721241194037255) 。

### 0x01 组件源码

组件`divider/src/main.vue` **SFC(单文件组件 single-file components )**，声明了基于模板的函数式组件。

```html
<template functional>
    // ...
</template>
<script>
export default {
  name: 'ElDivider',
  props: {
    // ...
  }
};
</script>
```

#### attributes 属性

组件定义了2个prop : `direction` 、`contentPosition`

**direction**

设置分割线方向,默认值`horizontal`，可选值为 `horizontal / vertical`，定义了 `validator` 进行验证。如果传入其他值生成无效样式 `el-divider--${direction}`。

```js
direction: {
  type: String,
  default: 'horizontal',
  validator(val) {
    return ['horizontal', 'vertical'].indexOf(val) !== -1;
  }
},
```

**contentPosition**

设置分割线文案的位置,默认值`center`，可选值为 `left / right / center`，定义了 `validator` 进行验证。如果传入其他值生成无效样式 `is-${props.contentPosition}`。

```js
contentPosition: {
  type: String,
  default: 'center',
  validator(val) {
    return ['left', 'center', 'right'].indexOf(val) !== -1;
  }
}
```

#### template 模板内容

组件将创建一个 `<div>` 元素节点,用来绘制分割线。 该节点包含1个子节点 `<div>` ，内置了 `slot` 用于自定义分割线文案。 子节点只有组件是分割线水平方向，且提供插槽内容用于自定义分割线文案时才会渲染。

```html
  <div
    v-bind="data.attrs"
    v-on="listeners"
    :class="[data.staticClass, 'el-divider', `el-divider--${props.direction}`]"
  >
    <div
      v-if="slots().default && props.direction !== 'vertical'"
      :class="['el-divider__text', `is-${props.contentPosition}`]"
     >
      <slot />
    </div>
  </div>
```

再进一步分析组件之前，先了深入了解下函数式组件。

**函数式组件 context**

函数式组件需要的一切都是通过 `context` 参数传递，组件中使用了该参数的部分属性：

* `props`：提供所有 prop 的对象
* `slots`：一个函数，返回了包含所有插槽的对象
* `data`：传递给组件的整个数据对象
* `listeners`： 一个包含了所有父组件为当前组件注册的事件监听器的对象。这是 `data.on` 的一个别名。

其他属性 `parent`、`children`、`scopedSlots`、`injections` 详见[官方文档](https://cn.vuejs.org/v2/guide/render-function.html#函数式组件) 。

`context` 接口定义 [@2.6源码 types/options.d.ts#L136](https://github.com/vuejs/vue/blob/2.6/types/options.d.ts#L136)

```js
export interface RenderContext<Props=DefaultProps> {
  props: Props;
  children: VNode[];
  slots(): any;
  data: VNodeData;
  parent: Vue;
  listeners: { [key: string]: Function | Function[] };
  scopedSlots: { [key: string]: ScopedSlot };
  injections: any
}
```

其中数据对象 `data` 类型为 `VNodeData` [官方文档](https://cn.vuejs.org/v2/guide/render-function.html#%E6%B7%B1%E5%85%A5%E6%95%B0%E6%8D%AE%E5%AF%B9%E8%B1%A1) 。

`VNodeData` 接口定义 [@2.6源码 flow/vnode.js#L35](https://github.com/vuejs/vue/blob/2.6/flow/vnode.js#L35) 。

**class Vs staticClass**

从接口声明上看两者区别在于`staticClass`只能是字符串。 `class`支持所有其他格式，例如对象或数组。

```js
declare interface VNodeData {
  // ...
  staticClass?: string;
  class?: any;
  // ...
};
```

下面使用 `Vue.compile` 实时编译模板字符串，对比区别，代码详见 [模板编译](https://cn.vuejs.org/v2/guide/render-function.html#模板编译)。

指令 `v-bind` 用于 `class`  时，Vue.js 做了专门的增强。表达式结果的类型除了字符串之外，还可以是对象或数组。

```html
<div :class="['element','active']"></div> 
```

编译(render)内容 输出 `class`

```js
function anonymous(
) {
  with(this){return _c('div',{class:['element','active']})}
}
```

不使用指令 `v-bind` 定义 `class`字符串内容时。

```html
<div class="active"></div> 
```

编译(render)内容 输出 `staticClass`

```js
function anonymous(
) {
  with(this){return _c('div',{staticClass:"active"})}
}
```

#### 组件功能

组件将创建一个 `<div>` 元素父节点,通过背景色绘制分割线。  `el-divider` 定义元素背景色， 通过`el-divider--horizontal` 或 `el-divider--vertical` 定义元素宽高实现水平、垂直分割效果

基于模板的函数式组件，可以通过 `context`访问到其独立的上下文内容。 `v-bind="data.attrs"` 使用 `data.attrs` 传递任何 HTML attribute；`v-on="listeners"` 使用 `listeners`  (即 `data.on` 的别名) 传递任何事件监听器。

`:class="[data.staticClass, 'el-divider', 'el-divider--${props.direction}']"` 根据传入prop 值设置分割线方向样式，然后将静态class 和动态生成的class 合并 merge 。

组件包含1个子节点 `<div>` ，只有组件是分割线水平方向（`props.direction !== 'vertical'`），且提供插槽内容（`slots().default`）时才会渲染。根据`contentPosition` 生成文案位置样式, `is-left`、 `is-right`或`is-center`。

***

### 0x02 组件样式

#### src/divider.scss

组件样式源码 `packages\theme-chalk\src\divider.scss` 使用混合指令 `b`、 `m`、`e`、 `when` 嵌套生成组件样式。

```scss
// 生成 .el-divider
@include b(divider) {
  // ...
  // 生成 .el-divider--horizontal
  @include m(horizontal) {
    // ...
  }
  // 生成 .el-divider--vertical
  @include m(vertical) {
    // ...
  }
  // 生成 .el-divider__text
  @include e(text) {
    // ...
    // 生成 .el-divider__text.is-left
    @include when(left) {
      // ...
    }
    // 生成 .el-divider__text.is-center
    @include when(center)  {
      // ...
    }
    // 生成 .el-divider__text.is-right
    @include when(right)  {
      // ...
    }
  }
}
```

#### lib/divider.scss

前文可知使用 `gulpfile.js`编译 `scss` 文件转换为`CSS`,经过浏览器兼容、格式压缩，最后生成 `packages\theme-chalk\lib\divider.scss`，内容格式如下。

```css
.el-divider {
  // ...
}
.el-divider--horizontal {
  // ...
}
.el-divider--vertical {
  // ...
}
.el-divider__text {
  // ...
}
.el-divider__text.is-left {
  // ...
}
.el-divider__text.is-center {
  // ...
}
.el-divider__text.is-right {
  // ...
} 
```

### 0x03 📚参考

["vm-attrs",vuejs.org](https://cn.vuejs.org/v2/api/#vm-attrs)\
["vm-listeners",vuejs.org](https://cn.vuejs.org/v2/api/#vm-listeners)\
["函数式组件",vuejs.org](https://cn.vuejs.org/v2/guide/render-function.html#函数式组件)\
[“class vs staticclass”，stackoverflow](https://stackoverflow.com/questions/57943450/is-there-any-difference-between-class-and-staticclass-in-vue-js)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://anduril.gitbook.io/learning-element/chapter03_source_code_analysis/09-divider.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
