基于rollup的组件库打包体积优化小结

背景

前段时间对公司内部的组件库(类似element-ui)做了打包体积优化,现在抽点时间记录下。以前也做过构建速度的优化,具体可以看组件库的webpack构建速度优化

一些存在的问题

最开始打包是基于webpack的,在按需加载上存在的体积冗余会比较大,如:

  1. webpack打包特有的模块加载器函数,这部分其实有些多余,最好去掉
  2. 使用babel转码时,babel带来的helper函数全部是内联状态,需要转成importrequire来引入
  3. 使用transform-rumtime对一些新特性添加polyfill,也是内联状态,需要转成importrequire来引入
  4. vue-loader带来的额外代码,如normalizeComponent,不做处理也是内联
  5. transform-vue-jsx带来的额外函数引入,如mergeJSXProps,不做处理也是内联

以上几个问题,如果只是一份代码,那不会有太大问题,但是如果是按需加载,用户一旦引入多个组件,以上的代码就会出现多份,带来严重的影响

import { Button, Icon } from 'gs-ui'

以上代码会转成

import Button from 'gs-ui/lib/button.js'
import Icon from 'gs-ui/lib/icon.js'

这样,就会出现多份相同的helper函数代码,多份webpack的模块加载器函数,而且还不好去重

寻找解决方案

讨论过后主要有以下几种选择

采用后编译

我们也认同这种方案,采用后编译可以解决上面的各种问题,也有组件库是这样做的,比如cube-ui,但是这样有些不方便,因为用户需要设置各种alias,还要保证好各种编译环境,如jsx,而且未来可能会引入flow,会更加不方便,所以暂时不考虑

使用rollup打包,设置external(当然webpack也可以)外联helper函数

使用rollup打包,可以直接解决问题1和问题4,设置external可以解决transform-runtime等带来的helper,这取决于相关插件实现时是不是通过importrequire来添加helper的,如果是直接copy的话,那就还得另找办法。最后决定就这种方案进行尝试

使用rollup对打包进行重构

使用rollup打包可能某些习惯和webpack有些出入,在这里很多事需要引入插件来完成,比如引入node_modules中的模块的话,需要加入rollup-plugin-node-resolve,加载commonjs模块需要引入rollup-plugin-commonjs等等。另外还有些比较麻烦的,比如经常会这样写

import xx from './xx-folder'

然后希望模块打包器可以识别成

import xx from './xx-folder/index.js'

rollup里还是需要用插件来完成这件事,找到的插件都没能满足各种需求,比如还需要对alias也能识别然后加上index.js,最后还是需要自己实现这个插件

基本的rollup配置应该差不多是这样的

{
 output: {
  format: 'es',
  // file: xx,
  // paths:
 },
 input: 'xxx',
 plugins: [
  vue({
   compileTemplate: true,
   htmlMinifier: {
    customAttrSurround: [[/@/, new RegExp('')], [/:/, new RegExp('')]],
    collapseWhitespace: true,
    removeComments: true
   }
  }),
  babel({
   ...babelrc({
    addModuleOptions: false,
    addExternalHelpersPlugin: false
   }),
   exclude: 'node_modules/**',
   runtimeHelpers: true
  }),
  localResolve({
   components: path.resolve(__dirname, '../components')
  }),
  alias({
   components: path.resolve(__dirname, '../components'),
   resolve: ['.js', '.vue']
  }),
  replace({
   'process.env.NODE_ENV': JSON.stringify('development')
  })
 ],
 // external
}

这里采用的rollup-plugin-vue的版本是v3.0.0,不采用v4,因为打包出来的体积更小,功能完全满足组件库需要。因为会存在各种约定,比如组件肯定是存在render函数(不一定指的就是手写renderjsx,只是不会有在js中使用template这种情况,这样的好处是可以使用runtime-onlyvue),组件肯定不存在style部分等等。

babel的配置上基本不会有改变,只是rollup-plugin-babel加上了runtimeHelpers,用来开启transform-runtme的。可能你会觉得为了更精简体积,应该去掉transform-runtime,这点我持保留意见,这里使用transform-runtime的主要作用是为了接管babel-helpers,因为这个babel-helpers无法被external。另外整个组件库用到的babel-runtime其实也不多,主要是类似Object.assign这样的函数,像这些函数,使用的话还是需要加上transform-runtime的,或者需要自己实现,感觉没什么必要。类似Array.prototype.includes这种无法被transform-runtime处理的还是会避免使用的

localResolve是自己实现的插件,用来添加index.js,并且能支持alias

alias插件用来添加alias,并且需要设置后缀

replace插件用来替换一些环境变量,比如开发环境会有错误提示,生成环境不会有,这里展示的是开发环境的配置。

配置external

所有优化的关键在于external上,除了最基本的vue需要external外,还有比如Button组件内部依赖了Icon组件,那是需要把Icon组件external

// Button 组件
import Icom from 'components/icon'

其实就是所有的组件和共用的util函数都需要external,当然这里本来就存在了,不是本次优化要做的

主要要处理的是babel-helperhelper函数,但是这里不能做到,我也没有去了解babel是如何对这块进行处理的,最后还是需要transform-runtime来接管它。

rollupexternal配置是支持函数类型的,大概看tranform-runtime这个插件源码可以找到addImport这些方法,可以知道polyfill是通过import来引入的,可以被external,所以只需要在rollup配置的external添加上类似函数就可以达到我们想要的效果

{
 external (id) {
  // 对babel-runtime进行external
  return /^babel-runtime/.test(id) // 当然别忘了还有很多 比如vue等等,这里就不写了
 }
}

这里就可以解决问题2和问题3

另外问题5,这个是如何来的呢,比如在写jsx时,可能会这样写

// xx组件
export default {
 render () {
  return (
   <div>
    <ToolTip {...{props: tooltipProps}} />
    {/* other */}
   </div>
  )
 }
}

在某个组件中依赖了另一个组件,考虑到扩展性,是支持对另一个组件进行props设置的,所以经常会这样写,在template中的话就类似于v-bind="tolltipProps"

这个时候transform-vue-jsx插件是会引入一个helper函数的,也就是babel-helper-vue-jsx-merge-props,大概看看transform-vue-jsx源码也可以得知,这个helper也是import进来的,所以可以把external改成

{
 external (id) {
  return /^babel/.test(id)
 }
}

这样就可以做到对所有helper都使用import的形式来引入,而且使用rollup打包后的代码更可读,大概长这样

// Alert组件
import _defineProperty from 'babel-runtime/helpers/defineProperty';
import Icon from 'gs-ui/lib/icon.js';

var Alert = { render: function render() {
  var _class;

  var _vm = this;var _h = _vm.$createElement;var _c = _vm._self._c || _h;return _c('transition', { attrs: { "name": "gs-zoom-in-top" } }, [_vm.show ? _c('div', { class: (_class = { 'gs-alert': true }, _defineProperty(_class, 'gs-alert-' + _vm.type, !!_vm.type), _defineProperty(_class, 'has-desc', _vm.desc || _vm.$slots.desc), _class) }, [_vm.showIcon ? _c('div', { staticClass: "gs-alert-icon", class: { "gs-alert-icon-top": !!_vm.desc } }, [_vm._t("icon", [_c('gs-icon', { attrs: { "name": _vm.icon } })])], 2) : _vm._e(), _vm._v(" "), _c('div', { staticClass: "gs-alert-content" }, [_vm.title || _vm.$slots.default ? _c('div', { staticClass: "gs-alert-title" }, [_vm._t("default", [_vm._v(_vm._s(_vm.title))])], 2) : _vm._e(), _vm._v(" "), _vm.desc || _vm.$slots.desc ? _c('div', { staticClass: "gs-alert-desc" }, [_vm._t("desc", [_vm._v(_vm._s(_vm.desc))])], 2) : _vm._e(), _vm._v(" "), _vm.closable ? _c('div', { staticClass: "gs-alert-close", on: { "click": _vm.close } }, [_vm._t("close", [_vm._v(" " + _vm._s(_vm.closeText) + " "), !_vm.closeText ? _c('gs-icon', { attrs: { "name": "close" } }) : _vm._e()])], 2) : _vm._e()])]) : _vm._e()]);
 }, staticRenderFns: [],
 name: 'GsAlert',
 components: _defineProperty({}, Icon.name, Icon),
 // props
 // data

 // methods
};

/* istanbul ignore next */
Alert.install = function (Vue) {
 Vue.component(Alert.name, Alert);
};

export default Alert;

vue插件把vue组件中的template转成render函数,babel插件做语法转换,因为external的存在,保留了模块关系,整个代码看起来很清晰,很舒服,不像webpack,都会添加一个模块加载函数...

优化后和优化前的体积对比

下面的截图是生产环境的版本,也就是没有了代码提示,也已经压缩混淆后的代码体积对比
左边是优化前,右边是优化后

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Rollup处理并打包JS文件项目实例代码

    关于Rollup rollup是一款用来es6模块打包代码的构建工具(支持css和js打包).当我们使用ES6模块编写应用或者库时,它可以打包成一个单独文件提供浏览器和Node.js来使用. 它的优点有如下: 能组合我们的脚本文件. 移除未使用的代码(仅仅使用ES6语法中). 在浏览器中支持使用 Node modules. 压缩文件代码使文件大小尽可能最小化. Rollup最主要的优点是它是基于ES2015模块的,相比于webpack或Browserify所使用的CommonJS模块更加有效率,

  • 基于rollup的组件库打包体积优化小结

    背景 前段时间对公司内部的组件库(类似element-ui)做了打包体积优化,现在抽点时间记录下.以前也做过构建速度的优化,具体可以看组件库的webpack构建速度优化 一些存在的问题 最开始打包是基于webpack的,在按需加载上存在的体积冗余会比较大,如: webpack打包特有的模块加载器函数,这部分其实有些多余,最好去掉 使用babel转码时,babel带来的helper函数全部是内联状态,需要转成import或require来引入 使用transform-rumtime对一些新特性添加

  • Vue 打包体积优化方案小结

    Vue-cli3 打包体积优化方案 前言: 公司项目完成后 ,打包完成后有1.18MB,其实感觉还行了,但是还可以有优化的地方,对于咱们有精益求精(有没有还是有点*数的)的精神下再去优化,可以先在项目中安装webpack-bundle-analyzer可以看到各个文件的大小 npm install webpack-bundle-analyzer -save-dev 在vue.config.js中进行配置 module.exports = { chainWebpack: config => { c

  • tree shaking对打包体积优化及作用

    目录 背景 有啥用? 实践 前置准备 打包 sideEffects 副作用 sideEffects的使用 优化体积 背景 大家平时在查 webpack构建体积优化 ,可能都会查到 tree-shaking 这个东西,很多人看到这个东西,就会把它背下来,用来应付以后面试官可能会问到的情况. 但是,又有多少人去真的了解一下 tree-shaking 呢?自己去实践一下看 tree-shaking 到底起了哪些作用?对于我们的打包体积的优化又有多少呢? 有啥用? Tree Shaking中文含义是摇树

  • Vue3从0搭建Vite打包组件库使用详解

    目录 打包配置 声明文件 打包配置 本篇文章将介绍如何使用 vite 打包我们的组件库,同时告诉大家如何使用插件让打包后的文件自动生成声明文件(*.d.ts) vite 专门提供了库模式的打包方式,配置其实非常简单,首先全局安装 vite 以及@vitejs/plugin-vue pnpm add vite @vitejs/plugin-vue -D -w 在 components 文件下新建vite.config.ts配置文件 import { defineConfig } from "vit

  • 详解使用React进行组件库开发

    最近针对日常业务需求使用react封装了一套[组件库], 大概记录下整个开发过程中的心得.由于篇幅原因,在这里只对开发过程中比较纠结的选型和打包等进行讨论,后续再对具体组件的封装进行讨论. 概述 我们都知道,组件化的开发模式对于我们的开发效率有着极大的提升,针对我们日常使用的基本组件进行封装,可以大量的简化我们对于基本UI的关注度,让我们的工作聚焦在业务逻辑上,很好的分离业务与基础UI的代码,使得整个项目更有调理,这也是我们要进行本组件库开发的原因. 然而现有React开源组件有很多,像ant-

  • 如何使用Vue3.2+Vite2.7从0快速打造一个UI组件库

    目录 1. 前言 2. 使用Vite搭建官网 2.1 创建项目 2.1.1. 全局安装vite(这里我装的时候是2.7.2) 2.1.2. 构建一个vue模板(项目名可以改成自己的名字) 2.1.3. 装好之后按照提示逐步执行命令 2.2 基本完成官网的搭建 2.2.1. 下载vue-router 2.2.2. 创建home首页与doc文档页 以及顶部导航栏 2.2.3. 配置路由 3. 封装一个Button组件 4. 封装Markdown组件介绍文档 4.1. 下载 4.2. main.ts中

  • 教你搭建按需加载的Vue组件库(小结)

    按需加载的原理 按需加载,本质上是把一个组件库的不同组件 拆分成不同文件 ,按照需要引用对应的文件,而该文件暴露一个 install方法 ,供Vue.use使用. 比如:我只想引用element库里的一个Button组件 import Button from 'element-ui/lib/Button.js' import Button from 'element-ui/lib/theme-chalk/Button.css' Vue.use(Button); 上面的写法比较繁琐,而且需要知道每

  • 一个vue组件库发布到npm的完整实现过程

    目录 新建项目 创建组件库 打包部署 使用 总结 新建项目 初始化一个vue项目 src下面创建一个plugins文件夹,index.js文件,这个文件是组件的出口文件. npm install @/vue-cli -g npm create vue-components cd vue-components npm run serve 创建组件库 src下新建一个plugins文件夹 然后创建toast组件toast/index.vue 这里为了能够展示模版,div标签后面的>去掉了. <te

  • 使用webpack和rollup打包组件库的方法

    前言 之前做了一个loading的样式组件,为了实现代码的可重用性,将这个小项目打包并且发布在了npm上.在一次次的打包发包过程中经历了一个有一个报错,@buzuosheng/loading这个组件已经到了2.7.0版本,虽然还有一些要调整的地方,但总算是可以用了. webpack和rollup对比 webpack算是使用程序员使用最多的打包工具,面试中往往会问到webpack的相关问题,而rollup被问到的要少很多.导致这种现象的一个原因是,应用开发使用webpack,库开发使用rollup

  • Mint UI 基于 Vue.js 移动端组件库

    官网地址 http://mint-ui.github.io/ Mint UI 包含丰富的 CSS 和 JS 组件,能够满足日常的移动端开发需要.通过它,可以快速构建出风格统一的页面,提升开发效率. 真正意义上的按需加载组件.可以只加载声明过的组件及其样式文件,无需再纠结文件体积过大. 考虑到移动端的性能门槛,Mint UI 采用 CSS3 处理各种动效,避免浏览器进行不必要的重绘和重排,从而使用户获得流畅顺滑的体验. 依托 Vue.js 高效的组件化方案,Mint UI 做到了轻量化.即使全部引

随机推荐