Element源码分析系列2-Container(布局容器)

栏目: 编程工具 · 发布时间: 6年前

内容简介:所谓布局容器,就是页面的整体结构,一般来说就是容器,里面才是上面4个子组件且只能是上面4个之一,下面依次分析每个组件的源码顾名思义,它是一个容器组件,承载子组件,容器组件应该有什么样的特性,无非就是块状,高度自适应等,Element加了一条规则:当子元素中包含

所谓布局容器,就是页面的整体结构,一般来说就是 <header>,<footer>,<aside>,<main> 等构成的页面总体架构,官网的示例图如下

Element源码分析系列2-Container(布局容器)
Element的处理是最外层有一个 container

容器,里面才是上面4个子组件且只能是上面4个之一,下面依次分析每个组件的源码

Container组件

顾名思义,它是一个容器组件,承载子组件,容器组件应该有什么样的特性,无非就是块状,高度自适应等,Element加了一条规则:当子元素中包含 <el-header><el-footer> 时,全部子元素会垂直上下排列,否则会水平左右排列,这是不是很神奇,下面上代码来探究一下原理,代码 点此

<template>
  <section class="el-container" :class="{ 'is-vertical': isVertical }">
    <slot></slot>
  </section>
</template>

<script>
  export default {
    name: 'ElContainer',
    //自定义属性,其他组件会用到
    componentName: 'ElContainer',
    props: {
      direction: String
    },
    computed: {
      isVertical() {
        if (this.direction === 'vertical') {
          return true;
        } else if (this.direction === 'horizontal') {
          return false;
        }
        return this.$slots && this.$slots.default
          ? this.$slots.default.some(vnode => {
            const tag = vnode.componentOptions && vnode.componentOptions.tag;
            return tag === 'el-header' || tag === 'el-footer';
          })
          : false;
      }
    }
  };
</script>
复制代码

这就是一个典型的单文件vue组件形式,只不过样式部分被分离出去了,首先来看 template 部分,发现其实就是 <section> 的封装而已,后面的组件也都采用了利于SEO的语义化tag标签而不是div,其实它们本质上就是块状元素,功能和div是一样的,注意 <section> 里面的 <slot> ,这就是承载子组件的插槽,如果不写则将会没有任何子元素,且是一个匿名插槽,接下来我们来看样式, el-container 这个类

@import "mixins/mixins";

@include b(container) {
  display: flex;
  flex-direction: row;
  flex: 1;
  flex-basis: auto;
  box-sizing: border-box;
  min-width: 0;

  @include when(vertical) {
    flex-direction: column;
  }
}
复制代码

可以看出 <section> 其实是flex布局,且默认方向为横向,其中 flex-basis:auto 设置其基准长度为自适应,这里的 flex:1 表示如果container被父container包围,那么它会分配剩余的宽或高

然后我们来看js部分, isVertical 是一个计算属性,首先判断是否有 direction 属性,如果有则直接返回水平还是垂直方向布局,如果没有则通过判断子元素来确定布局方向,重点就是这里的代码了

isVertical() {
    ...
    return this.$slots && this.$slots.default
      ? this.$slots.default.some(vnode => {
        const tag = vnode.componentOptions && vnode.componentOptions.tag;
        return tag === 'el-header' || tag === 'el-footer';
      })
      : false;
}
复制代码

它是一个3元运算符,首先判断 this.$slots&& this.$slots.default ,如果不存在直接返回false,不存在的情况就是子元素为空。 this.$slots 是组件的实例属性,组件是可复用的Vue的实例,和 new Vue()一样是实例,因此有以下属性

Element源码分析系列2-Container(布局容器)
所以 this.$slots.default 返回了所有没有被包含在具名插槽中的子元素,如果这些子元素存在,那么开始依次遍历这些子元素,注意 some 高阶函数的使用,这里就是要在所有子元素中查看是否存在 <el-header> 或者 <el-footer> , some 所遍历的数组只要有一个为true,则整体返回true,否则为false,而 every

则是要所有都是true才返回true,这里的高阶函数简化了代码量,如果用一般的for循环显得不那么优雅

接下来是如何判断子元素是 <el-header> 或者 <el-footer> ,这里首先要明确 this.$slots.default 是个数组,里面的每个元素都是一个 VNodeVNode 是虚拟dom中的虚拟节点,当组件被编译时,每个 <...> 就会生成一个虚拟的节点,大致结构如下图

Element源码分析系列2-Container(布局容器)
、 拥有tag,childern,text等属性,而 componentOptions

里面才包含元素的tag名称,注意VNode的tag不是我们想要的,进入Vue源码查找相关代码

const vnode = new VNode(
    `vue-component-${Ctor.cid}${name ? `-${name}` : ''}`,
    data, undefined, undefined, undefined, context,
    { Ctor, propsData, listeners, tag, children },
    asyncFactory
  )
复制代码

发现第一个参数就是tag,其实是有前缀的,而 componentOptions{ Ctor, propsData, listeners, tag, children } 这么个对象,代表组件选项属性,这里面的tag才是我们想要的,打印出VNode证明了上面的说法

Element源码分析系列2-Container(布局容器)

因此这里给我们一个怎么判断子元素类型的一个启发方法,可以借鉴。综上,这个计算属性的函数就能够通过子元素来判断自身flex的布局方向了

Footer组件

由于剩下的组件都很相似,这里就分析一个 Footer ,代码如下

<template>
  <footer class="el-footer" :style="{ height }">
    <slot></slot>
  </footer>
</template>

<script>
  export default {
    name: 'ElFooter',
    //自定义属性,便于其他组件获取该组件的名称
    componentName: 'ElFooter',
    props: {
      height: {
        type: String,
        default: '60px'
      }
    }
  };
</script>
复制代码

由此可见仍然是封装了原生的 <footer> ,并有一个默认高度,footer本质上就是一个块状元素而已,下面来查看scss代码

@import "mixins/mixins";
@import "common/var";

@include b(footer) {
  padding: $--footer-padding;
  box-sizing: border-box;
  flex-shrink: 0;
}
复制代码

注意 flex-shrink:0 这个代码,这表示当父容器宽度不够时,footer不会收缩而是保持原本的宽度,也就是说footer是永远不会被压缩,就算超出容器。 flex-shrink 默认为1,也就是所有元素等比例收缩,对于下图这种形式的布局

Element源码分析系列2-Container(布局容器)
其中header和footer的 flex-shrink 都是0,也就是不会压缩,而main中的代码 flex:1 表示了只有main会被压缩或者扩张,flex:1是 flex:grow:1,flex:shrink:1,flex-basis:auto 的简写,又因为 flex:grow

默认值为0,所以只有main会被压缩或扩张,header和footer都不变

总结

综上,整个布局其实就是对原生的封装,主要就是 container 的处理,如果浏览器不支持flex,则上述布局无效


以上所述就是小编给大家介绍的《Element源码分析系列2-Container(布局容器)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Clean Architecture

Clean Architecture

Robert C. Martin / Prentice Hall / 2017-9-20 / USD 34.99

Practical Software Architecture Solutions from the Legendary Robert C. Martin (“Uncle Bob”) By applying universal rules of software architecture, you can dramatically improve developer producti......一起来看看 《Clean Architecture》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具