从 0 到 1 实现一个框架BootstrapVue: Lesson 4

51 阅读1分钟

从 0 到 1 实现一个框架BootstrapVue: Lesson 4

如何实现一个<BAspect />

1.首先看最核心的 render function

h 的第一个参数是 tag, 比如'div', 表示一种 html 元素 h 的第二个参数是 VNode 的属性, 这里只有 class, 用于调整组件的样式 h 的第三个参数是 VNode 的 children VNode, 这里有$sizer, $content

return h(this.tag, { staticClass: `${CLASS_NAME} d-flex` }, [$sizer, $content]);

我们看一下这两个 children. $content 有 slot. 而$sizer没有. 然后样式上有一点差别.相同点是, 两个都受 flex 影响. 不同点是: $sizer自己的高度是 0, 但是有一个 paddingBottom. $content宽度要占满 100%.然后要向左偏移 100%. 这是因为 flex 布局下, 两者本应该是并排显示的, 但是向左偏移后, 两者就又合到一起了, 而且利用 paddingBottom 占据一个 minimum 的高度.

const $sizer = h("div", {
  staticClass: `${CLASS_NAME}-sizer flex-grow-1`,
  style: { paddingBottom: this.padding, height: 0 },
});

const $content = h(
  "div",
  {
    staticClass: `${CLASS_NAME}-content flex-grow-1 w-100 mw-100`,
    style: { marginLeft: "-100%" },
  },
  this.normalizeSlot()
);

2.看一下组件实现的核心功能

这里是传一个 aspect ratio. 即长宽比. 比如显示器大部分是 '16:9'. 组件会根据这个长宽比, 在固定宽度下, 算出一个对应的高度. 这个高度是组件的最小高度.

{
  "prop": "aspect",
  "description": "Aspect as a width to height numeric ratio (such as `1.5`) or `width:height` string (such as '16:9')"
}

这个计算也非常简单, 就是先校验输入的长宽比对不对, 然后取出来转成 float 类型, 再去计算比率, 最后转换成对应的百分比, 比如'100%'

padding() {
  const aspect = this.aspect
  let ratio = 1
  if (RX_ASPECT.test(aspect)) {
    // Width and/or Height can be a decimal value below `1`, so
    // we only fallback to `1` if the value is `0` or `NaN`
    const [width, height] = aspect.split(RX_ASPECT_SEPARATOR).map(v => toFloat(v) || 1)
    ratio = width / height
  } else {
    ratio = toFloat(aspect) || 1
  }
  return `${100 / mathAbs(ratio)}%`
}

3.技术总结

这可能是我们讲到过的最简单的一个组件了. 但是麻雀虽小, 仍五脏俱全.

  1. 编写组件的核心是想清楚你需要实现什么东西

这里我们是要实现一个占高的东西, 因此使用了 $sizer, $content. $sizer用来占高, $content用 slot 来接收后面的内容.

  1. 有了实现方向, 再去探讨具体的技术

这里只需要一个 prop, 即aspect, 通过约定的形式, 比如'16:9'.很容易解析出长宽, 最后反应到样式上的百分比上去.