如何在10分钟以内完成一个业务页面 - Vue的封装艺术

真实干货,走心分享!html

如何作一个优秀的时间管理者? lzx告诉咱们,天天少睡几个小时,你就有更多的时间去作运动。程序员

对于程序员来讲,天天少点时间写业务,就有更多时间去作(hua)优(hua)化(shui)。api

咱们今天讲的是Vue中如何更灵活的封装业务组件,使效率翻倍。由于Ctrl + cCtrl + v都不足以知足咱们了,搬过来还要改一大堆东西。那怎么办嘛,还有比CV工程师更快的职业吗?数组

答案是有的!咱们称其为配置工程师,简单无脑!async

Vue组件封装技巧

其实这个方法的核心思想就是封装。可是封装方式不一样,使用起来也是天差地别。如何实现一个灵活易用的封装,这就是咱们本篇文章的主要讨论目标。函数

$attrs

最简单的封装是什么?就是往原组件外再套一层,而且保留全组件的全部功能。而后扩展本身的功能。post

真实案例

假设咱们有一个组件x-button,很遗憾这个组件竟然不支持加载状态!那咋办,为了用户体验,咱们得给它加上加载状态才行。因此咱们在它的基础上封装一个y-buttonfetch

<template>
  <x-button>
    <i v-if="loading" class="font-loading"></i>
    <slot></slot>
  </x-button>
</template>
复制代码
export default {
  name: 'YButton',
  props: {
    loading: {
      type: Boolean,
      default: false,
    }
  }
};
复制代码

这样一来,咱们的y-button就支持loading了!可是等等,若是咱们要给原来的x-button传递属性咋办?简单!ui

<template>
  <x-button :size="size">
    <i v-if="loading" class="font-loading"></i>
    <slot></slot>
  </x-button>
</template>
复制代码
export default {
  props: {
    loading: {
      type: Boolean,
      default: false,
    },
    size: {
      type: String,
      default: undefined,
    }
  }
};
复制代码

对就是这么简单,原组件的属性,有一个加一个! 好累啊!!!咱们不能这么作,这太不优雅了。好在Vue提供了$attrs能够解决这个问题。this

那么$attrs是干哈的啊?

$attrs 包含了父做用域中不做为 prop 被识别 (且获取) 的 attribute 绑定 (class 和 style 除外)

简单点来讲也就是我不要的,全丢给你。那咱们来改写一下。

<template>
  <x-button v-bind="$attrs">
    <i v-if="loading" class="font-loading"></i>
    <slot></slot>
  </x-button>
</template>
复制代码
export default {
  props: {
    loading: {
      type: Boolean,
      default: false,
    }
  }
};
复制代码

这样写,当咱们传递属性给y-button时,除了loading之外,其余所有属性都会透传给x-button。这就是$attrs的做用。

配置+模板

这是本篇关键的一步,也是效率提高最多的一步。 想想,当你对着原型图一步一步写出页面配置,页面就搭好了是什么感受。

真实案例

平常咱们用的最多,也是最多见的组件:表格+分页!讲真的,表格真的是无处不在!

优秀的表格组件有不少,但它们写起来的确很复杂,并且充斥着大量重复代码。是时候将它封装一下了!咱们以el-table为基础组件。

ElTable原始操做

首先,el-table是这样用的:

<template>
  <el-table :data="tableData">
    <el-table-column prop="date" label="日期" width="180"> </el-table-column>
    <el-table-column prop="name" label="姓名" width="180"> </el-table-column>
    <el-table-column prop="address" label="地址"> </el-table-column>
  </el-table>
</template>
复制代码
export default {
  data() {
    return {
      tableData: [
        {
          date: "2016-05-02",
          name: "王小虎",
          address: "上海市普陀区金沙江路 1518 弄"
        }
      ]
    };
  }
};
复制代码

列的配置化

如今咱们开始改造这个组件,封装一个f-table!首先咱们要将这些el-table-column都干掉。

<template>
  <div>
    <el-table>
      <el-table-column v-for="(col, i) in cols" :key="i" v-bind="col"> </el-table-column>
    </el-table>
  </div>
</template>
复制代码
export default {
  props: {
    cols: {
      type: Array,
      default: () => []
    }
  }
};
复制代码

这样咱们在使用的时候就是这样的:

<template>
  <div>
    <f-table cols=""></f-table>
  </div>
</template>
复制代码
export default {
  data() {
    return {
      cols: [
        {
          prop: "date",
          label: "日期",
          width: "180",
	  formatter: dateFormatter
        }
      ]
    };
  }
};
复制代码

数据获取方式

咱们经过最原始的调用知道el-table是经过

<el-table :data="tableData">
复制代码

data属性来传递数据的。咱们只要直接透传数据数组就行了,可是咱们不这样作,由于这里咱们能够作更多。咱们不直接传入数据,而是传入获取数据的方法

<template>
  <div>
    <el-table :data="tableData">
      <!-- ... -->
    </el-table>
  </div>
</template>
复制代码
export default {
  props: {
    // ...
    fetch: {
      type: Function,
      default: () => Promise.resolve({ rows: [] })
    }
  },
  data() {
    return {
      loading: false,
      tableData: []
    };
  },
  created() {
    this.fetchData()
  },
  methods: {
    async fetchData() {
      this.loading = true;
      try {
        const { rows } = await this.fetch();
        this.tableData = rows;
      } catch (error) {
        console.error(error);
      } finally {
        this.loading = false;
      }
    }
  }!
};
复制代码

你能够看到咱们在获取数据的时候能够设置表格的加载状态,处理错误。这里咱们还能够将分页集成进去,不过这个后面再讲。这能够省去咱们不少工做!

咱们再来看看组件调用代码:

<template>
  <div>
    <f-table :cols="cols" :fetch="fetchUsers"></f-table>
  </div>
</template>
复制代码
export default {
  data() {
    return {
      cols: [
        {
          prop: "date",
          label: "日期",
          width: "180"
        }
      ]
    };
  },
  methods: {
    fetchUsers() {
      return {
        rows: [{ name: "xxx", date: "xx" }]
      };
    }
  }
};
复制代码

咱们只须要关心怎么获取数据,要展现什么数据。没了!

分页

OK!下面咱们顺带解决分页问题,咱们使用的是el-pagination

ElPagination原始操做

<template>
  <div>
    <el-pagination :current-page.sync="currentPage" :total="total"> </el-pagination>
  </div>
</template>
复制代码
export default {
  data() {
    return {
      currentPage: 1,
      total: 0
    };
  }
};
复制代码

整合到FTable

<template>
  <div>
    <el-table :data="tableData">
      <!-- -->
    </el-table>
    <el-pagination @current-change="fetchData" :current-page.sync="currentPage" :total="total">
    </el-pagination>
  </div>
</template>
复制代码
export default {
  // ...
  data() {
    return {
      // ...
      currentPage: 1,
      total: 0
    };
  },
  methods: {
    async fetchData() {
      // ...
      const { rows, total } = await this.fetch(this.currentPage);
      this.tableData = rows;
      this.total = total;
    }
  }
};
复制代码

咱们看一下咱们加了哪些东西:

  • fetch的时候多返回了total代表总数据,以便于分页
  • fetch的时候向函数中传入了当前页码
  • 在页码改变的时候从新获取数据

而咱们的组件调用代码,仅仅只增长了几行:

<f-table :cols="cols" :fetch="fetchUsers"></f-table>
复制代码
methods: {
  async fetchUsers(currentPage) {
    const query = {
      page: currentPage
    };
    const { rows, total } = await api.getUsers(query);
    return { rows, total };
  }
}
复制代码

这样,一个带有自动分页的表格组件就封装好了,使用起来十分简单。

固然这并不能知足你的全部需求我知道。好比你想要使用表格最原始的el-table-column,给表格列加个按钮,加个输入框什么的。

具名slot

要定制内容的时候,使用slot是最合适的。可是如何加在咱们的f-table里面呢?

还记得咱们使用cols来配置el-table-column。咱们只须要代表某个col是某个slot就行了。

<template>
  <div>
    <el-table :data="tableData">
      <template v-for="(col, i) in cols">
        <slot v-if="col.slot" :name="col.slot" />
        <el-table-column v-else :key="i" v-bind="col"> </el-table-column>
      </template>
    </el-table>
  </div>
</template>
复制代码

使用方法:

<template>
  <div>
    <f-table :cols="cols" :fetch="fetchUsers">
      <template slot="action">
        <el-table-column>使用原生ElTableColumn的用法</el-table-column>
      </template>
    </f-table>
  </div>
</template>
复制代码
export default {
  data() {
    return {
      cols: [
        {
          slot: 'operation'
        },
        {
          prop: "date",
          label: "日期",
          width: "180"
        }
      ]
    };
  }
}
复制代码

小结

一旦习惯这种方法,再配上各类formatter,写页面真的无脑,就差改为拖拽了(有没有嗅到什么)!

写做不易,恳求你们动动手指点个关注点个赞!

原文 - 个人小破站

相关文章
相关标签/搜索