Vue 打包优化之 externals 抽离公共的第三方库

news2024/10/7 8:28:58

使用 @vue/cli 脚手架构建的 Vue 全家桶项目,默认配置下,打包后会把 vuevue-routeraxiosvuexelement-uiecharts 等公共库打包在一起,导致基础 chunkvendor 包体积特别大,有时一个文件能达到 3-5MB,这会大大影响首次加载速度。因此需要抽离第三方公共库,配合使用 CDN 加速。

Vue Externals

一、前言

项目依赖:

{
  "name": "vue-web",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "start": "vue-cli-service serve --mode local",
    "dev": "vue-cli-service serve --mode dev",
    "test": "vue-cli-service build --mode test",
    "serve": "vue-cli-service serve ",
    "s": "nodemon --watch vue.config.js --exec \"npm start\"",
    "build": "vue-cli-service build",
    "build:az": "vue-cli-service build --report",
    "git": "tive git -c tive.git.config.js",
    "lint": "vue-cli-service lint"
  },
  "dependencies": {
    "axios": "^0.18.1",
    "d3": "^6.7.0",
    "dagre-d3": "^0.6.4",
    "echarts": "^5.3.3",
    "element-ui": "^2.15.9",
    "v-clipboard": "^2.2.3",
    "vue": "^2.7.10",
    "vue-router": "^3.6.5",
    "vuex": "^3.6.2"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "^3.12.1",
    "@vue/cli-plugin-eslint": "^3.12.1",
    "@vue/cli-service": "^3.12.1",
    "compression-webpack-plugin": "^3.0.0",
    "html-webpack-externals-plugin": "^3.8.0",
    "less": "^3.13.1",
    "less-loader": "^4.1.0",
    "msw": "^0.47.3",
    "msw-tools": "latest",
    "uglifyjs-webpack-plugin": "^2.2.0",
    "vue-template-compiler": "^2.7.10",
    "webpack-bundle-analyzer": "^4.7.0"
  },
  "eslintConfig": {
    "root": true,
    "env": {
      "node": true
    },
    "extends": [
      "plugin:vue/essential",
      "eslint:recommended"
    ],
    "rules": {},
    "parserOptions": {
      "parser": "babel-eslint"
    }
  },
  "postcss": {
    "plugins": {
      "autoprefixer": {}
    }
  },
  "browserslist": [
    "> 1%",
    "last 2 versions",
    "not dead",
    "not ie 11"
  ],
  "msw": {
    "workerDirectory": "public"
  }
}

案例:项目整体使用了 element-ui,其中后台服务消费监控可视化引入了 echarts,元数据表血缘关系图使用了 d3dagre-d3,这几个库本身体积就不小,打包到一起后体积更大。

二、优化配置

  1. 安装 html-webpack-externals-plugin
npm i -D html-webpack-externals-plugin
  1. 配置 vue.config.js
const CompressionWebpackPlugin = require('compression-webpack-plugin')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')
const HtmlWebpackExternalsPlugin = require('html-webpack-externals-plugin')

const isProduction = process.env.NODE_ENV === 'production'

module.exports = {
  publicPath: '/datalk/',
  assetsDir: 'static',
  lintOnSave: false,
  productionSourceMap: false,
  devServer: {
    headers: {
      'Access-Control-Allow-Origin': '*',
    },
    host: '0.0.0.0',
    port: 1024,
    disableHostCheck: true,
    proxy: {
      '/api': {
        target: 'https://tiven.cn/api',
        changeOrigin: true,
        ws: true,
        pathRewrite: {
          '^/api': '',
        },
      },
    },
  },
  configureWebpack: (config) => {
    if (isProduction) {
      config.performance = {
        hints: false,
      }
      config.plugins.push(
        new CompressionWebpackPlugin({
          algorithm: 'gzip',
          test: new RegExp('\\.(' + ['js', 'css'].join('|') + ')$'),
          threshold: 10240,
          minRatio: 0.8,
        })
      )
    } else {
      config.devtool = 'source-map'
    }
    
    // HtmlWebpackExternalsPlugin 关键配置 start
    config.plugins.push(
      new HtmlWebpackExternalsPlugin({
        externals: [
          {
            module: 'vue',
            entry: {
              path: 'dist/vue.min.js',
              type: 'js',
            },
            global: 'Vue',
          },
          {
            module: 'element-ui',
            entry: ['lib/index.js', 'lib/theme-chalk/index.css'],
            supplements: ['lib/theme-chalk/fonts/'],
            global: 'ELEMENT',
          },
          {
            module: 'axios',
            entry: {
              path: 'dist/axios.min.js',
            },
            global: 'axios',
          },
          {
            module: 'echarts',
            entry: {
              path: 'dist/echarts.min.js',
              attributes: {
                async: '',
                // defer: '',
              },
            },
            global: 'echarts',
            // append: true,
          },
          {
            module: 'd3',
            entry: {
              path: 'dist/d3.min.js',
              attributes: {
                async: '',
              },
            },
            global: 'd3',
          },
          {
            module: 'dagre-d3',
            entry: {
              path: 'dist/dagre-d3.min.js',
              attributes: {
                async: '',
              },
            },
            global: 'dagreD3',
          },
        ],
        // hash: true, // 设置后会在引用脚本时加上 hash,如下所示:
        // <script src='/datalk/vendor/vue/dist/vue.min.js?f452a239c2c0156e7b83'></script>
        // outputPath: 'static/lib', // 输出目录,默认为 vendor
        // publicPath: '/assets/', // 公共路径,默认为 /
      })
    )
    // HtmlWebpackExternalsPlugin 关键配置 end
    
    // 生成打包报告
    if (process.env.npm_lifecycle_event === 'build:az') {
      config.plugins.push(new BundleAnalyzerPlugin())
    }
  },
}

三、配置说明

  • module :库名,也就是 package.json 中的包依赖名。
  • entry :入口,有几种类型,string | array<string> | object | array<object | string>,可以设置 CDN 地址,如:https://cdn.tiven.cn/assets/js/vue.min.js ;也可以设置文件路径,如:dist/vue.min.js,相对于项目的路径就是:node_modules/vue/dist/vue.min.js
  • global :注册到 window 上的全局变量,注意不能配错,否则代码会报错。
  • supplements :补充文件,在上边 element-ui 配置中,因为 css 文件中依赖了大量的 font 字体文件,所以在打包时需要把这些依赖文件根据相对路径复制到 dist 对应的目录中。
  • attributes :设置引用这些抽离出去包的 scriptlink 标签的属性,deferasynccrossoriginglobal 等等,可以根据需要进行配置。

因为首页首次渲染不依赖 echartsd3dagre-d3 等第三方库,所以给 script 标签加上了 async 属性,脚本相对于页面的其余部分异步地执行(当页面继续进行解析时,脚本将被执行),这样可以不阻塞页面渲染,提升首屏加载速度,提高用户体验。

四、打包对比

使用 webpack-bundle-analyzer 生成打包报告,优化前如图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2NUhr3UV-1669705401578)(https://tiven.cn/static/img/img-dist-01-WIKK_8kojGzKsT2hnqHZg.jpg)]

Vue BundleAnalyzer Report

优化后如图所示:

Vue BundleAnalyzer Report

公共包被抽离出去,chunk 包总体积从 2.8MB 变成 670KB ,减小了 70% 多,优化效果很明显。

五、打包输出

打包后 dist 目录:

dist/
  static/
    css/
    ...
    img/
    ...
    js/
      app.f462a90f.js
      app.f462a90f.js.gz
      chunk-0af562fc.fcb27ef3.js
      chunk-1f6412f4.625202a5.js
      chunk-1f6412f4.625202a5.js.gz
      ...
  vendor/
    axios/
      dist/
        axios.min.js  
        axios.min.js.gz
    d3/
      dist/
        d3.min.js    
        d3.min.js.gz
    dagre-d3/
      dist/
        dagre-d3.min.js    
        dagre-d3.min.js.gz
    echarts/
      dist/
        echarts.min.js    
        echarts.min.js.gz
    element-ui/
      lib/
        theme-chalk/
          fonts/
            element-icons.ttf
            element-icons.woff
          index.css
          index.css.gz
        index.js    
        index.js.gz
    vue/
      dist/
        vue.min.js    
        vue.min.js.gz
  favicon.ico      
  index.html

打包后,会发现把这些抽离出去的包直接引入到 index.html 中,如下所示:

<body>
  <noscript>
    <strong>
      We're sorry but regeng doesn't work properly without JavaScript enabled. Please enable it to continue.
    </strong>
  </noscript>
  <div id='app'></div>
  <script src='/datalk/vendor/vue/dist/vue.min.js'></script>
  <script src='/datalk/vendor/element-ui/lib/index.js'></script>
  <script src='/datalk/vendor/axios/dist/axios.min.js'></script>
  <script src='/datalk/vendor/echarts/dist/echarts.min.js' async></script>
  <script src='/datalk/vendor/d3/dist/d3.min.js' async></script>
  <script src='/datalk/vendor/dagre-d3/dist/dagre-d3.min.js' async></script>
  <script src='/datalk/static/js/app.f462a90f.js'></script>
</body>

六、踩坑记录

element-ui 配置 externals 时,可能会遇到这样的报错:Uncaught ReferenceError: ElementUI is not defined at element-ui (external "ElementUI":1:1) ,这说明 element-ui 模块的 global 参数配置错了,在全局 window 上找不到,你可能配置的是 ElementUI、Element、element-ui,这些都是不对的。必须是 global: 'ELEMENT'


欢迎访问:天问博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/45694.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ThinkPHP和uniapp开发的CRM售后管理系统(客户、合同、工单、任务、报价、产品、库存、出纳、收费)

ThinkPHP和uniapp开发的CRM售后管理系统无加密的开源源码(可用于自营外包项目(多主体)、可用于外包定制开发项目) 主要功能&#xff1a;客户、合同、工单、任务、报价、产品、库存、出纳、收费&#xff0c; 适用于&#xff1a;服装鞋帽、化妆品、机械机电、家具装潢、建材行业…

NR CSI(三) CQI

微信同步更新&#xff0c;欢迎关注同名modem协议笔记 这篇主要看下CQI的相关内容&#xff0c;CQI在spec上描述的内容比较少&#xff0c;主要是和调制方式和码率相关&#xff0c;所以这篇的内容也比较简短。先看下CSI Report Quantity 上报测量量。 很早之前有人问我你知道各个…

【面试题】DOM

1. DOM的本质 DOM(Document Object Model)&#xff0c;文档对象模型。DOM的本质是从HTML文件中解析出来的一棵树。DOM的数据结构是树形结构&#xff08;DOM树&#xff09; 2. DOM节点操作 2.1 获取DOM节点 <!DOCTYPE html> <html lang"en"> <head…

【毕业设计】30-基于单片机矿井瓦斯_气体浓度_烟雾浓度报警设计(原理图+源代码+仿真+答辩论文+答辩PPT)

【毕业设计】30-基于单片机矿井瓦斯/气体浓度/烟雾浓度报警设计&#xff08;原理图源代码仿真答辩论文答辩PPT&#xff09; 文章目录【毕业设计】30-基于单片机矿井瓦斯/气体浓度/烟雾浓度报警设计&#xff08;原理图源代码仿真答辩论文答辩PPT&#xff09;任务书设计说明书摘要…

Kafka(二)- Kafka集群部署

文章目录一、安装部署1. 集群规划2. 虚拟机前置准备工作&#xff08;1&#xff09;配置IP&#xff08;2&#xff09;修改主机名称和hosts文件&#xff08;3&#xff09;关闭防火墙&#xff0c;关闭防火墙开机自启&#xff08;4&#xff09;克隆虚拟机3. 集群部署&#xff08;1&…

Oracle中ALTER TABLE的五种用法(三)

首发微信公众号&#xff1a;SQL数据库运维 原文链接&#xff1a;https://mp.weixin.qq.com/s?__bizMzI1NTQyNzg3MQ&mid2247485212&idx1&sn450e9e94fa709b5eeff0de371c62072b&chksmea37536cdd40da7a94e165ce4b4c6e70fb1360d51bed4b3566eee438b587fa231315d0a5a…

BP神经网络PID从Simulink仿真到PLC控制实现(含博途PLC完整SCL源代码)

单神经元自适应PID控制博途PLC完整源代码,请参看下面的文章链接: 博途PLC单神经元自适应PID控制_RXXW_Dor的博客-CSDN博客_单神经元pid控制1、单神经元作为构成神经网络的基本单位,具有自学习和自适应能力,且结构简单易于计算,传统的PID具有结构简单、调整方便和参数整定…

RationalDMIS 2020一平面与两偏置圆找正(原点不在偏置圆上)

在批量加工箱体、杠杆、盖板类零件时,大多是以工件上一个平面和两定位孔作为定位基准实现组合定位,简称"一面两孔(一面两销)定位。 采用"一面两孔,定位,能使工件在各道工序上的定位基准统一,进而可减少因定位基准多次变换而产生的定位误差,提高工件的加工精…

学术论文引言、正文和致谢格式规范标准 - 易智编译EaseEditing

1 引言1.1 定义 国家标准GB7713-87规定&#xff1a;“引言(或绪论)简要说明研究工作的目的、范围、相关领域的前人工作和知识空白、理论基础和分析、研究设想、研究方法和实验设计、预期结果和意义等。 引言应言简意赅&#xff0c;不要与摘要雷同&#xff0c;不要成为摘要的注…

vue3 antd table表格的增删改查(二)input输入框根据关键字搜索【后台管理系统 请求后端接口 前后端交互】

input输入框——关键字模糊搜索知识调用场景复现准备工作解决方法step1 请求接口step2 根据id搜索step3 优化处理&#xff08;输入框监听&#xff09;知识调用 功能实现可能要用到的知识&#xff1a;vue3数据变化侦测&&信息筛选过滤.filter() .map() .forEach(). find…

【SVA】SystemVerilog assertion语法速查

seq与property|->,|>\##[*n ][n ]andintersectorfirst_match![在这里插入图片描述](https://img-blog.csdnimg.cn/015bf766991947e0bbe95356ba2a0036.png)throughoutwithinifended局部变量与赋值在sequence、property中调用display[->1]$rose $fell $isunknow$stable$…

#国产工业软件#外行人看工业软件,接轨还是出轨?

​ 有些人整天特么的自毁自恨&#xff0c;说&#xff1a;“什么我们中国人不追求真理&#xff0c;什么我们中国人没有科学精神&#xff0c;我们大部分科研人员都是混饭吃的&#xff0c;所有人都忙着生活没有人拼搏搞科研。“ 你们这些神经病该歇歇了&#xff0c;要是欧洲社会…

springboot高校学生宿舍水电费报修考勤管理系统

宿舍水电管理系统的开发过程中&#xff0c;采用B / S架构&#xff0c;主要使用jsp技术进行开发&#xff0c;中间件服务器是Tomcat服务器&#xff0c;使用Mysql数据库和Eclipse开发环境。该宿舍水电管理系统包括学生、维修员和管理员。其主要功能包括宿舍公告、维修员、学生、宿…

IBO对中文A文学课程IA有何规定?

又到11月中旬&#xff0c;许多IB学校开始确定IA和EE选题。在IB选课时&#xff0c;IB学生必选语言A&#xff08;母语&#xff09;和语言B。在语言A科目组的课程中&#xff0c;大部分中国的IB学生都会选择IB语文&#xff08;中文A&#xff09;。 众所周知&#xff0c;历经改革&am…

解决nginx: [emerg] unknown directive “stream“ in /etc/nginx/nginx.conf问题

文章目录1.未报错时nginx配置&#xff1a;2.报错时nginx配置&#xff1a;3.增加配置报错&#xff1a;4.增加配置位置如下&#xff1a;5.解决办法&#xff1a;6.测试&#xff1a;nginx -t1.未报错时nginx配置&#xff1a; #user nginx; user root; worker_processes auto;er…

【C++】STL —— map和set的模拟实现

目录 一、基础铺垫 二、基本结构分析 1. 节点结构分析 2. 模板参数中仿函数分析 三、正向迭代器 四、封装完成的红黑树 五、map的模拟实现 六、set的模拟实现 一、基础铺垫 在前面的博客中我们了解了map和set的基本使用&#xff0c;以及对二叉搜索树、AVL树和红黑树的…

数据结构学习笔记(VI):图

目录 1 图的基本概念 6.1 图的基本概念 1.定义 2.边 3.连通 4.子图 5.连通分量 6. 生成树与森林 7.权 8.特殊的图 2 图的存储 2.1 邻接矩阵法 1.实现 2.性能分析 3.性质 2.2 邻接表法 1.实现 2.对比 2.3 十字链表与邻接多重表 1.十字链表存储有向图 2…

梦开始的地方——C语言柔性数组

文章目录柔性数组什么是柔性数组&#xff1f;柔性数组的使用柔性数组的优点柔性数组 什么是柔性数组&#xff1f; 在C99中&#xff0c;结构体最后一个元素它允许是一个未知大小的数组&#xff0c;这就叫做柔性数组成员。 这个概念听起来可能有点不可以思议&#xff0c;但它的…

KVC原理与数据筛选

作者&#xff1a;宋宏帅 1 前言 在技术论坛中看到一则很有意思的KVC案例&#xff1a; interface Person : NSObject property (nonatomic, copy) NSString *name; property (nonatomic, assign) NSInteger age; end Person *person [Person new]; person.name "Tom&q…

[附源码]计算机毕业设计springboot基于JavaWeb的学校社团活动管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…