菜鸟读element源码二 el-row

2,462 阅读5分钟

开篇

本来想把el-row和el-col放在同一篇来写,然鹅发现对于一个菜鸟来说,简单的el-row(它不简单)已经让我学到很多。

内容

这是我的row.js文件

模仿代码中均以el-test 替换el, 目的是为了边模仿边测试和el组件的效果对比

export default {
  // 驼峰命名法
  name: 'ElTestRow',
  props: {
    gutter: Number,
    
    // 渲染的标签类型
    tag: {
      type: String,
      default: 'div'
    },

    //是否使用flex布局
    type: String,

    justify: {
      type: String,
      default: 'start'
    },

    align: {
      type: String,
      default: 'top'
    }
  },

  render(h) {
    let classStyle = []
    let style = {}
    //为什么在设置gutter的时候  父组件要设置margin?  是为了保证col第一个和最后一个仍然贴紧row的左侧和右侧  但是这样做会撑开el-row
    if (this.gutter) {
      style.marginLeft = `-${this.gutter/2}px`
      style.marginRight = style.marginLeft
    }

    classStyle = [
      'el-test-row',
      this.justify !== 'start' ? `is-justify-content-${this.justify}` : '',
      this.align !== 'top' ? `is-align-items-${this.align}` : '',
      {'el-test-row-flex': this.type === 'flex'}
    ]

    return h(this.tag,{
      class: classStyle,
      style
    }, this.$slots.default)
  },
}

用render函数形式创建插件el-row。在VUE官网上有说Vue 推荐在绝大多数情况下使用模板来创建你的 HTML。然而在一些场景中,你真的需要 JavaScript 的完全编程的能力。这时你可以用渲染函数,它比模板更接近编译器。 。在以前看这句话,说我感觉就是render有更快的编译渲染速度?(感觉是这个描述)。后来听了尤大在Vue conf上的演讲发现并不是这样。 使用render并不是因为它有更快的编译速度,相反,template模板更快些。render的好处在于它足够灵活,这个你可以参见vue官网的例子render。 我们举个例子吧

<div id="demo">
      <p class="demo1">静态的我</p>
      <p class="demo1">静态的我</p>
      <p class="demo1">{{message}}</p>
      <p class="demo1">静态的我</p>
      <p class="demo1">静态的我</p>
      <p class="demo1">静态的我</p>
    </div>

像这样的一个静态模板,如果你要做视图更新的时候,你觉得怎样消耗最小?我们只用检测 <p class="demo1">{{message}}</p>这一行的更新就可以了。但是render不一样。render在每次message改变的时候都动态重新生成模板,因此我们不能直接追踪到message的变化,并且在这个例子上,性能消耗render会更高,(它重绘了页面)。因此render的优点是足够灵活,但也是因为它足够灵活的原因,在diff的时候也会造成很大的性能浪费。慎用render

另外,感觉render在某些时候相对于template更加灵活一点,你可以想一下如果用template row组件该怎么写。

关于render函数该如何使用,我觉得看官网就够了cn.vuejs.org/v2/guide/re…。不要怕,写就完了! 在仿制过程中产生了疑问,在element的row.js中 是使用计算属性监控gutter

computed: {
    style() {
      const ret = {};

      if (this.gutter) {
        ret.marginLeft = `-${this.gutter / 2}px`;
        ret.marginRight = ret.marginLeft;
      }
      
      return ret;
    }
  },

**疑问一:为什么要使用计算属性来改变style?(已解决于2019年7月10日,见文章尾)

在我的row文件中,我并没有使用计算属性判断gutter的改变,但是在测试过程中,改变gutter,视图一样会发生变化,所以我不是太理解为什么要使用computed

总结

学到了两个方面的内容

  • 1、仿制的过程中我也仿制了样式,因为element的样式也是值得我深入学习的 所以记录了class的部分连写规则
 两个class连写   
  class1>class2    两个class之间有>号 代表选择父级为class1的所有class2
  class1  class2   两个class 之间有空格 代表class1内部的所有class2 class2出现在class1的子级元素才会有效果
  class1class2     两个class之间无空格  代表两个class出现在同一个元素上 class2才会有效果
  class1,class2    两个class之间有逗号  代表选择所有的class1,class2
  • 2、因为在el-col存在float属性,因此会存在难以撑起父组件的问题,通过查阅资料,有多种改变方法,我选择了添加伪类
.el-test-row:after{
  content: '';
  clear: both;
  display: block;
}

同时我就看到element的row.css在class前后都添加了伪类

.el-row::after,
.el-row::before {
  display: table;
  content: "";
}

疑问2 为什么前后都要加伪类,不是很懂(我觉得加一个就够了😂)

提出一点我觉得需要优化的地方

在使用gutter的时候,element会在class el-row前后分别添加margin-left margin-right,以确保位于最左和最右的el-col是和el-row无间隔(目前我认为是这样)但是这样会撑开el-row的边框,因此在我以为,gutter可能需要一些优化,保证gutter不会撑开el-row的宽度。

疑问一答案

render函数在每一次有变量改变的时候都会触发,gutter的改变会触发style的改变,render也会跟着再次执行,但是如果按照我原来的写法把gutter改变导致样式改变写到render里面,那么在每一次render执行的时候都会去重新计算style,这是很没有必要的,比如说我们改变的是typejutify这时候我们并不需要重新计算style,这时候style的值直接取缓存里面的就成,减少了重复计算style,优化了性能。

PS:由大佬总结来的一句话就是 computed有缓存一位不爱负责的大佬