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(布局容器)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

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

用户至上:用户研究方法与实践(原书第2版)

用户至上:用户研究方法与实践(原书第2版)

凯茜·巴克斯特 / 王兰、杨雪、苏寅 / 机械工业出版社 / 2017-5-1 / 99

《UI/UE系列丛书 用户至上:用户研究方法与实践(原书第2版)》是用户研究方法指南,谷歌用户体验研究员十几年工作经验结晶,从理论到实战,包含完整的实操案例,是设计以人为中心产品的实用手册。 《UI/UE系列丛书 用户至上:用户研究方法与实践(原书第2版)》包含五个部分共15章。入门篇包括第1~5章:介绍用户体验入门,如何理解目标用户,道德与法律问题,如何搭建研究设施,如何选择用户体验研究方......一起来看看 《用户至上:用户研究方法与实践(原书第2版)》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器