善用代码的“元信息”

在C/C++的时代我们所用的代码信息只有代码内容本身。编译器很少可以告诉我们代码运行在哪里以及运行的文件是哪些。

而在语言逐渐发展之后,一些特殊的信息能够在运行时被我们获取,如运行文件路径。通过这些信息和元编程的概念,我们实现一些特殊的程序来减少代码中重复的部分。

vue为例:


Vue的相同特性组件的一种优化思路

定义

相同特性组件是指,一组相同props以及listeners等特性,并完成类似功能的组件。

比如在我的项目中,表格的每个单元格可能由不同的类型组成:输入都为实际的数据,但展示位不同的样式。我的部分目录结构如下

# .../table/cell/
#   Text.vue
#   Color.vue
#   Number.vue
#   Image.vue
#   ...

每个组件接收的参数都是一个JS的标准值,我们需要把不同的值转换成不同的显示方式。比如"#ffffff"Text组件下显示为单纯的字符串,而在Color组件下则应当展示为一个白色的颜色块。

问题

当组件数量不多时我们可以引用每一个组件到父组件中,但是当表格内容足够复杂,可能需要引用十几个基础组件时,import语句看起来就不那么“美观”了。尤其是页面越来越多时,每个页面都需要引用大量的基础组件,对import/components的管理以及日后可能的重构都是一种极大的负担。

解决方案

使用一个组件大致可以分为三步:

  1. 引用组件 import 组件 from ‘组件.vue’
  2. 声明组件 components: {..., 组件, ...}
  3. 调用组件 <组件 ...props/>

我们可以针对每一步来进行优化。

引用组件

我们使用的import语句会在webpack的转换下变成webpack的require,一般情况下我们需要显示的引用所有需要的组件,webpack才会将代码打包至生成文件中。

实际上webpack提供了一个require.context方法来实现整个目录(一定条件)的打包加载。我们可以使用这个特性来加载我们需要的任何组件:

  // cell.js
  const requireContext = require.context('./cell', true, /vue$/)

  const components = {}

  requireContext.keys().forEach(key => {
    const nKey = /* 把实际的文件名转化成我想要的组件名  */;
    components[nKey] = requireContext(key).default
  })

这样我们就得到了一个map,包含了一系列特性相似的组件。

:由于webpack不确定运行时你会使用什么组件,所以它会将所有匹配到的组件全部打包至目标代码。

声明组件 & 调用组件

上一部中我们已经得到了nKey => 组件的map,所以可以直接使用到组件的components选项中使用,然而这样还是让组件产生了大量的components依赖,组件特性类似的性质也让我们有一种更统一的声明以及调用组件的方法:Proxy组件

声明 Proxy 组件

代理组件的作用就是通过组件名称,生成对应组件并传递组件需要的props以及listeners等信息。在这里我提供一个简单的实现:

  // Proxy.js
  export default {
    name: 'table-cell-proxy',
    functional: true,
    inheritAttrs: false,
    props: {
      name: {
        type: String,
        required: true
      }
    },
    render (createElement, {props, data}) {
      return createElement(components[props.name], data)
    }
  }

由于表格渲染会大量使用单元格中的组件,所以proxy组件最好是functional的以提供最高的效率。proxy组件接受一个name参数用于找到对应的组件,并将所有其他的属性传递至实际渲染的组件中。其中需要的components变量即为上一步生成的map。

调用 Proxy 组件

iView中table的所有数据都使用render渲染:


 columns () {
    return this.getColumnsInfo()
      .map(({title, key, type, meta}) => {
        return {
          title,
          key,
          render (h, params) {
            return h(Proxy, {
              props: {
                name: type,
                value: params.row[key],
                meta
              }
            })
          }
        }
      })
  }
  getColumnsInfo() {
    return [
      {
        title: 'ID',
        key: 'id',
        type: 'id'
      },
      {
        title: 'Name',
        key: 'name',
        type: 'text'
      },
      {
        title: 'Theme'
        key: 'theme',
        type: 'color',
        meta: {
          format: '#rgba'
        }
      },
      ...
  }

其中this.getColumnsInfo()返回了一个列表,其中包含的type代表了我们要渲染的组件类型,meta代表这个类型相关的一些额外的数据。

小结

通过以上的努力,我们把组件的引用以及声明组织到了一个目录下:

# .../table/
#   cells/
#     Text.vue
#     Color.vue
#     Number.vue
#     Image.vue
#     ...
#   cell.js
#   Proxy.js

调用时也只需要引用Proxy组件,这样使得我们有更多的精力去关注真正的业务代码,并更方便的去组织相同特性组件。

总结

本文主要介绍了如何通过Proxy组件以及require.context特性来组织相同特性组件的代码。用途不仅包括表格页面的单元格组件,也可以包括表单页面的输入组件等等,具体业务具体分析。也可以把Proxy组件看成一种抽象组件,不提供具体实现但提供了相同的接口。

本文提供的代码不为实际项目代码,仅供参考。


以上就是通过目录来组织相同特性组件的一个思路,利用了webpack的一些特性,我们利用这些目录信息,来简化了大量的代码。

Java里操作这些信息则更加方便,因为Java自带反射的机制、包名也是用路径来划分。实际上spring-boot的自动配置就用了这种类似的思想,引用了pom后自动扫目录并加载应该自动加载的配置和类,使得需要人工培植的地方大大减少。

这种实现虽然非常方便,但是要求使用者理解哪些元信息会影响到运行时,否则可能出现一些奇怪的BUG。也如我在之前的文章里提到了,以人为本,不要过度设计。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容