“真正“的ElementUI按需加载

5,518 阅读6分钟

好的,今天我们来 卸下巨人的肩膀来给自己站站 通过源码做到 ElementUI真正的按需加载。

前言

好吧,其实就是因为业务有普通轮播图和卡片轮播图的功能。作为一个蔡蔡的练习生,又不会自己手撸一个出来,vue-awesome-swiper又太大了(打包后 600kb),找到的轮播图组件虽然小但又不好用。

盯着原型图看啊看,诶?这些功能 ElementUI不全都有吗,而且还支持按需加载,这样打包后应该也不会特别大吧?搞起来搞起来!

Element UI

啥也别说了,先直接引入康康效果先

// main.js
import Vue from 'vue'
import App from './App.vue'

import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
}).$mount('#app')
<template>
  <div id="app">
    <el-carousel :interval="5000" arrow="always">
      <el-carousel-item v-for="item in 4" :key="item">
        <h3>{{ item }}</h3>
      </el-carousel-item>
    </el-carousel>
    <el-carousel :interval="4000" type="card" height="200px">
      <el-carousel-item v-for="item in 6" :key="item">
        <h3 class="medium">{{ item }}</h3>
      </el-carousel-item>
    </el-carousel>
  </div>
</template>

嗯!就你了!!让我康康打包后的大小 npm run build -- --report

啊这,有点大!加上按需加载呢?我只是想要一个轮播图而已啊 0.0

根据 ElementUI官网—按需加载 配置一波。

// main.js

import Vue from 'vue'
import App from './App.vue'

import {
  Carousel,
  CarouselItem,
} from 'element-ui';

Vue.use(Carousel)
Vue.use(CarouselItem)

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
}).$mount('#app')
// babel.config.js

module.exports = {
  presets: [
    '@vue/cli-plugin-babel/preset'
  ],
  "plugins": [
    [
      "component",
      {
        "libraryName": "element-ui",
        "styleLibraryName": "theme-chalk"
      }
    ]
  ]
}

npm run serve 嗯,还正常。

让我再康康打包后的大小! npm run build -- --report

确实小了不少!但不论如何,这也都还是引入了一个ElementUI寄人篱下的感觉总是有点微妙,还可能用上了不少浪费的代码。

那我们就从ElementUI源码中把轮播图的代码拆下来当成自己的吧 git clone https://github.com/ElemeFE/element.git

冷静分析一波目录解构

element-dev
    ├──.github                 // github文件
    ├── build                  // webpack配置文件
    ├── examples               // API文档?组件demo
    ├── node_modules           // 模块依赖目录
    ├── packages               // 组件的源码目录
        ├── 所有组件
    ├── src                    // 源码目录
        ├── directive          // 自定义指令
        ├── locale             
        ├── mixins             // Vue混合器
        ├── transition         // 样式过渡效果
        ├── utils              // 工具类包
        ├── index.js           // 入口文件
    ├── test                   // 测试目录
    ├── types                  // typescript文件包
    ├── .babelrc               // babel配置文件
    ├── .eslintignore          // eslint配置忽略文件
    ├── .eslintrc              // eslint配置
    ├── .gitignore             // git忽略文件
    ├── package.json           // npm包核心文件
    ├── components.json        // 组件列表json
    ├── yarm.lock              // yram版本控制文件
    ├── package-lock.json      // npm包版本控制文件
    ├── ......

翻出轮播图组件源码

Carousel 走马灯 结合使用el-carouselel-carousel-item标签就得到了一个走马灯。

根据官网的指路,那应该就是这两个了

element-dev/packages/carousel
element-dev/packages/carousel-item

看看都有哪些依赖项

先看一下 element-dev/packages/carousel/src/main.vue
import throttle from 'throttle-debounce/throttle'
import {
  addResizeListener,
  removeResizeListener
} from 'element-ui/src/utils/resize-event'

throttle-debounce/throttle是个 npm包,先不管它。

再看看element-ui/src/utils/resize-event

import ResizeObserver from 'resize-observer-polyfill';

好的,也是个包。main的依赖就这些了。

再看看element-dev/packages/carousel-item/src/item.vue`
import { autoprefixer } from 'element-ui/src/utils/util';

element-ui/src/utils/util

import Vue from 'vue';
import { isString, isObject } from 'element-ui/src/utils/types';
那么所有的引入依赖项就是
// main.vue

import throttle from 'throttle-debounce/throttle'
// resize-event依赖
import ResizeObserver from 'resize-observer-polyfill';
import {
  addResizeListener,
  removeResizeListener
} from 'element-ui/src/utils/resize-event'

// item.vue
import { autoprefixer } from 'element-ui/src/utils/util';

你的码就是我的码 那我们开始吧

开始偷窃一个轮播图

创建目录

先创建一个carousel目录存放轮播图组件,以及存工具类函数的目录utils。接着就把文件都丢进去好了。

├─components
│  ├─caursel
│  │      item.vue
│  │      main.vue
│  │
└─utils
        resize-event.js
        util.js
安装包以及删除无用代码
  1. 先安装包
// ElementUI源码中包的版本
"resize-observer-polyfill": "^1.5.0",
"throttle-debounce": "^1.0.1"

直接按照两个包的官网的最新版来安装了,出了事再看🙄

resize-observer-polyfill cnpm install resize-observer-polyfill --save-dev 1.5.1

throttle-debounce cnpm install throttle-debounce --save ^2.2.1

官网中 throttle-debounce的引入为 import { throttle } from 'throttle-debounce';与源码中不同

当然你也可以指定版本安装

cnpm install resize-observer-polyfill@1.5.0 --save-dev

cnpm install throttle-debounce@1.0.1 --save

  1. 删除无用代码

由于目录已经都不一样了,所以源码中element-ui/src/utils/resize-event要更换为新目录的路径

import {
  addResizeListener,
  removeResizeListener
} from '@/utils/resize-event'

// item.vue
import { autoprefixer } from '@/utils/util';

打开文件可以看到,其实引入的并不是全部函数。那我们就直接删除无用的吧。

// resize-event.js   三个函数均有使用

/**
 * util.js
 * import { autoprefixer } from 'element-ui/src/utils/util';
 * 函数非常的多,而我们只需要 autoprefixer的函数。
 * 引入的 types文件轮播图中也没有用到。
 * 因此我们只需要保留一个函数即可。
 */

export const autoprefixer = function (style) {
  if (typeof style !== 'object') return style;
  const rules = ['transform', 'transition', 'animation'];
  const prefixes = ['ms-', 'webkit-'];
  rules.forEach(rule => {
    const value = style[rule];
    if (rule && value) {
      prefixes.forEach(prefix => {
        style[prefix + rule] = value;
      });
    }
  });
  return style;
};
使用组件

引入并注册组件后直接按照官网Carousel 走马灯使用组件

<template>
  <div id="app">
    <el-carousel :interval="5000" arrow="always">
      <el-carousel-item v-for="item in 4" :key="item">
        <h3>{{ item }}</h3>
      </el-carousel-item>
    </el-carousel>
  </div>
</template>

<script>
import ElCarousel from '@/components/caursel/main'
import ElCarouselItem from '@/components/caursel/item'
export default {
  name: 'App',
  components: {
    ElCarousel,
    ElCarouselItem
  }
}
</script>

<style lang="scss">
.el-carousel__item h3 {
  color: #475669;
  font-size: 18px;
  opacity: 0.75;
  line-height: 300px;
  margin: 0;
}

.el-carousel__item:nth-child(2n) {
  background-color: #99a9bf;
}

.el-carousel__item:nth-child(2n + 1) {
  background-color: #d3dce6;
}
</style>

控制台没有报错,但样式、字体图标很明显的...忘记加载了

引入样式以及字体图标
  1. 找到字体图标css文件element-dev/packages/theme-chalk/src/icon.css
@font-face {
  font-family: 'element-icons';
  src: url("fonts/element-icons.woff") format("woff"), url("fonts/element-icons.ttf") format("truetype");
  /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
  font-weight: normal;
  font-display: "auto";
  font-style: normal;
}

可以看出,一共引入了两个字体图标文件fonts/element-icons.woff fonts/element-icons.ttf

三个文件 icon.css fonts/element-icons.woff fonts/element-icons.ttf一起复制到styles/fonts目录下并引入

icon.css中的路径记得要更改

  1. 找到组件样式css文件element-dev/packages/theme-chalk/src下的carousel.css carousel-item.css

分别引入

// main.vue中
<style lang="scss" scoped>
@import './carousel.css';
</style>

// item.vue中
<style lang="scss" scoped>
@import './carousel-item.css';
</style>

那么,所有都准备好了。我们可以对着Carousel 走马灯直接使用了

效果

新加一个 卡片式的轮播

这里字体图标其实只用到了一个箭头的 icon,但两个字体文件就有 82.1kb了。完全可以自己在iconfont中定制。

打包

npm run build -- --report

然而,打包出来却比按需加载还大了了 49.59kb !

core.js

对比了一下两个打包文件的分析,发现是core.js的大小不同。相差了48.25kb。再细看,原来多出来的部分,是多了一些为兼容ES6+的代码。

查了半天资料没搞懂。VueCLI-浏览器兼容里明明是默认useBuiltIns: 'usage'检测代码中ES6/7/8等的使用情况,仅仅加载代码中用到的polyfills,为什么还会啥也丢了上去。即使把包换成和ElementUI一样的版本也一样。有知道的带哥指导指导😥

总结

也就是说,如果你想要弄一套自己能随便修改只要其中的功能的 UI库,那你大可可以按照上面的说法去做!

先找到组件所有的依赖项 --> 引入依赖 --> 引入样式文件 --> 引入字体图标

此次 轮播图 carousel用到的文件如下
// 源码组件 .vue文件
element-dev/packages/carousel
element-dev/packages/carousel-item

// 源码组件 样式、字体文件
element-dev/packages/theme-chalk/src/icon.css
element-dev/packages/theme-chalk/src/fonts/element-icons.woff
element-dev/packages/theme-chalk/src/fonts/element-icons.ttf

// 源码组件 util工具函数文件
element-ui/src/utils/resize-event
element-ui/src/utils/util

// npm包
throttle-debounce/throttle
resize-observer-polyfill
全局注册 Global Registration
import Vue from 'vue'
import App from './App.vue'

import ElCarousel from '@/components/caursel/main'
import ElCarouselItem from '@/components/caursel/item'

Vue.component('el-carousel', ElCarousel)
Vue.component('el-carousel-item', ElCarouselItem)
局部注册 Local Registration
<template>
  <div id="app">
    <el-carousel :interval="5000" arrow="always">
      <el-carousel-item v-for="item in 4" :key="item">
        <h3>{{ item }}</h3>
      </el-carousel-item>
    </el-carousel>
  </div>
</template>

<script>
import ElCarousel from '@/components/caursel/main'
import ElCarouselItem from '@/components/caursel/item'
export default {
  name: 'App',
  components: {
    ElCarousel,
    ElCarouselItem
  }
}
</script>

源码地址

element-carousel源码