邓疯疯

邓疯疯 查看完整档案

填写现居城市  |  填写毕业院校  |  填写所在公司/组织填写个人主网站
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 个人简介什么都没有

个人动态

邓疯疯 回答了问题 · 1月8日

antd按需加载为什么仍然很大?

请问解决了吗? 我也是遇到

关注 5 回答 4

邓疯疯 收藏了文章 · 1月6日

【Vue项目总结】webpack常规打包优化方案

由于新建项目发版打包时间大概需要30分钟,发版时严重拖慢下班时间,所以特意查看了相关文档来优化打包速度,争取早点下班,^_^。

分析打包文件

要优化,先分析。我们先要知道到底是哪里拖慢我们的打包速度呢?

打包后生成文件分析

可以利用webpack-bundle-analyzer插件来分析我们打包后生成的文件

  • 安装
npm i webpack-bundle-analyzer -D
  • 使用

修改webpack.prod.conf.js文件

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin

// 构建完成后,浏览器会自动打开localhost:8080
webpackConfig.plugins.push(
  new BundleAnalyzerPlugin({
    analyzerPort: 8080,
    generateStatsFile: false
  })
)

通过图片可以看到打包后文件的具体信息

打包进度条显示,可以查看到打包进度百分比

simple-progress-webpack-plugin可以显示打包百分比

  • 安装
npm i simple-progress-webpack-plugin -D
  • 使用

修改webpack.prod.conf.js文件

const SimpleProgressWebpackPlugin = require( 'simple-progress-webpack-plugin' )
...
  plugins: [
    new SimpleProgressWebpackPlugin()
  ]
...

效果如下:

资源与依赖包的控制

通过上面进度可以看到,打包过程中,卡顿在压缩的地方过长,当项目越来越臃肿的时候,我们要需要对项目静态资源以及依赖包进行整理,

  • 图片过大的可以压缩,这里推荐一个还不错的压缩链接
  • 项目中没有使用的依赖可以删除,可以按需引用的依赖,按需引用

项目里面使用ElementUIEcharts都是全部引用挂在Vue.prototype上,现都改为按需引用。

减少文件搜索范围

  • 设置resolve.alias字段,避免打包时如果使用相对路径访问或着import文件时会层层去查找解析文件
resolve: {
  alias: {
    '@': resolve('src')
  }
}
  • 合理配置extensions扩展名

resolve.extensions能够自动解析确定的扩展,但是如果extensions扩展名过多,会导致解析过程过多,所以我们要合理配置扩展名,不要过多配置扩展名,项目引用多的文件,扩展名放在前面,我司项目中多的是vuejs文件,可以只引用这两种。

resolve: {
  extensions: ['.vue', '.js']
}
  • loader预处理文件增加include匹配特定条件

预处理各种文件时指定匹配目录后,webpack解析文件时就不会循环查找其他目录,加快解析速度。

happypack多线程执行

webpack执行预处理文件时单线程的,我们可以使用happypack来多线程处理文件。

  • 安装
npm i happypack  -D
  • 使用

修改webpack.base.js文件

const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
module: {
  rules: [
    {
      test: /\.js$/,
      loader: 'happypack/loader?id=babel', // 原始loader替换成`happypack/loader`
      include: [resolve('src')]
    }
  ]
},
plugins: [
  new HappyPack({
    // id标识 需要处理的loader
    id: 'babel',
    // loader配置和原始配置一样
    loaders: [
      {
        loader: 'babel-loader',
        options: {
          presets: ['es2015'],
          cacheDirectory: true
        }
      }
    ],
    threadPool: happyThreadPool
  })
]

babel-plugin-dynamic-import-node异步加载

babel-plugin-dynamic-import-node插件是使import() 替换成 require 编译

  • 安装
npm i babel-plugin-dynamic-import-node -D
  • 使用

修改.babelrc 文件

"env": {
  "development": {
    "plugins": ["dynamic-import-node"]
  },
  "production": {
    "plugins": ["dynamic-import-node"]
  }
}

注意:使用插件build后没有chunk files文件。

DllPlugin分包

通过DllPlugin插件分离出第三方包

  • 新建webpack.dll.conf.js
const path = require('path');
const webpack = require('webpack');
const CleanWebpackPlugin = require("clean-webpack-plugin");

module.exports = {
  entry: {
    vendor: [
      'vue',
      'vue-router',
      'vuex',
      'axios',
      'element-ui',
      'echarts'
    ]
  },
  output: {
    filename: '[name]_dll_[hash:6].js', // 产生的文件名
    path: path.resolve(__dirname, '../static/dll'),
    library: '[name]_dll_[hash:6]'
  },
  plugins: [
    new CleanWebpackPlugin({ 
      root: path.resolve(__dirname, '../static/dll'),
      dry: false // 启用删除文件
    }),
    new webpack.DllPlugin({
      name: '[name]_dll_[hash:6]',
      path: path.resolve(__dirname, '../static/dll', '[name].dll.manifest.json')
    })
  ]
};
  • 修改webpack.prod.conf.js

使用add-asset-html-webpack-plugin动态添加dll.jshtml

需要注意

  1. add-asset-html-webpack-plugin要在HtmlWebpackPlugin后引入;
  2. html-webpack-plugin依赖包版本4.0.0-alpha会出个问题,添加上去的路径会变成undefined需要是3.2.0版本
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin');
...
plugins: [
  // 插入dll json
  new webpack.DllReferencePlugin({
    context: path.join(__dirname),
    manifest: require('../static/dll/vendor.dll.manifest.json')
  }),
  new HtmlWebpackPlugin(),
  // 插入 dll js
  new AddAssetHtmlPlugin([{ 
    publicPath: config.build.assetsPublicPath + 'static/dll/',  // 注入到html中的路径
    outputPath: 'static/dll/', // 输出文件目录
    filepath: resolve('static/dll/*.js'), // 文件路径
    includeSourcemap: false,
    typeOfAsset: "js"
  }])
]

总结

项目经过以上优化,打包从30分钟,到2分钟不到,整体还有优化空间,可以使用其他cdn等优化方式。

查看原文

邓疯疯 赞了文章 · 1月6日

【Vue项目总结】webpack常规打包优化方案

由于新建项目发版打包时间大概需要30分钟,发版时严重拖慢下班时间,所以特意查看了相关文档来优化打包速度,争取早点下班,^_^。

分析打包文件

要优化,先分析。我们先要知道到底是哪里拖慢我们的打包速度呢?

打包后生成文件分析

可以利用webpack-bundle-analyzer插件来分析我们打包后生成的文件

  • 安装
npm i webpack-bundle-analyzer -D
  • 使用

修改webpack.prod.conf.js文件

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin

// 构建完成后,浏览器会自动打开localhost:8080
webpackConfig.plugins.push(
  new BundleAnalyzerPlugin({
    analyzerPort: 8080,
    generateStatsFile: false
  })
)

通过图片可以看到打包后文件的具体信息

打包进度条显示,可以查看到打包进度百分比

simple-progress-webpack-plugin可以显示打包百分比

  • 安装
npm i simple-progress-webpack-plugin -D
  • 使用

修改webpack.prod.conf.js文件

const SimpleProgressWebpackPlugin = require( 'simple-progress-webpack-plugin' )
...
  plugins: [
    new SimpleProgressWebpackPlugin()
  ]
...

效果如下:

资源与依赖包的控制

通过上面进度可以看到,打包过程中,卡顿在压缩的地方过长,当项目越来越臃肿的时候,我们要需要对项目静态资源以及依赖包进行整理,

  • 图片过大的可以压缩,这里推荐一个还不错的压缩链接
  • 项目中没有使用的依赖可以删除,可以按需引用的依赖,按需引用

项目里面使用ElementUIEcharts都是全部引用挂在Vue.prototype上,现都改为按需引用。

减少文件搜索范围

  • 设置resolve.alias字段,避免打包时如果使用相对路径访问或着import文件时会层层去查找解析文件
resolve: {
  alias: {
    '@': resolve('src')
  }
}
  • 合理配置extensions扩展名

resolve.extensions能够自动解析确定的扩展,但是如果extensions扩展名过多,会导致解析过程过多,所以我们要合理配置扩展名,不要过多配置扩展名,项目引用多的文件,扩展名放在前面,我司项目中多的是vuejs文件,可以只引用这两种。

resolve: {
  extensions: ['.vue', '.js']
}
  • loader预处理文件增加include匹配特定条件

预处理各种文件时指定匹配目录后,webpack解析文件时就不会循环查找其他目录,加快解析速度。

happypack多线程执行

webpack执行预处理文件时单线程的,我们可以使用happypack来多线程处理文件。

  • 安装
npm i happypack  -D
  • 使用

修改webpack.base.js文件

const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
module: {
  rules: [
    {
      test: /\.js$/,
      loader: 'happypack/loader?id=babel', // 原始loader替换成`happypack/loader`
      include: [resolve('src')]
    }
  ]
},
plugins: [
  new HappyPack({
    // id标识 需要处理的loader
    id: 'babel',
    // loader配置和原始配置一样
    loaders: [
      {
        loader: 'babel-loader',
        options: {
          presets: ['es2015'],
          cacheDirectory: true
        }
      }
    ],
    threadPool: happyThreadPool
  })
]

babel-plugin-dynamic-import-node异步加载

babel-plugin-dynamic-import-node插件是使import() 替换成 require 编译

  • 安装
npm i babel-plugin-dynamic-import-node -D
  • 使用

修改.babelrc 文件

"env": {
  "development": {
    "plugins": ["dynamic-import-node"]
  },
  "production": {
    "plugins": ["dynamic-import-node"]
  }
}

注意:使用插件build后没有chunk files文件。

DllPlugin分包

通过DllPlugin插件分离出第三方包

  • 新建webpack.dll.conf.js
const path = require('path');
const webpack = require('webpack');
const CleanWebpackPlugin = require("clean-webpack-plugin");

module.exports = {
  entry: {
    vendor: [
      'vue',
      'vue-router',
      'vuex',
      'axios',
      'element-ui',
      'echarts'
    ]
  },
  output: {
    filename: '[name]_dll_[hash:6].js', // 产生的文件名
    path: path.resolve(__dirname, '../static/dll'),
    library: '[name]_dll_[hash:6]'
  },
  plugins: [
    new CleanWebpackPlugin({ 
      root: path.resolve(__dirname, '../static/dll'),
      dry: false // 启用删除文件
    }),
    new webpack.DllPlugin({
      name: '[name]_dll_[hash:6]',
      path: path.resolve(__dirname, '../static/dll', '[name].dll.manifest.json')
    })
  ]
};
  • 修改webpack.prod.conf.js

使用add-asset-html-webpack-plugin动态添加dll.jshtml

需要注意

  1. add-asset-html-webpack-plugin要在HtmlWebpackPlugin后引入;
  2. html-webpack-plugin依赖包版本4.0.0-alpha会出个问题,添加上去的路径会变成undefined需要是3.2.0版本
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin');
...
plugins: [
  // 插入dll json
  new webpack.DllReferencePlugin({
    context: path.join(__dirname),
    manifest: require('../static/dll/vendor.dll.manifest.json')
  }),
  new HtmlWebpackPlugin(),
  // 插入 dll js
  new AddAssetHtmlPlugin([{ 
    publicPath: config.build.assetsPublicPath + 'static/dll/',  // 注入到html中的路径
    outputPath: 'static/dll/', // 输出文件目录
    filepath: resolve('static/dll/*.js'), // 文件路径
    includeSourcemap: false,
    typeOfAsset: "js"
  }])
]

总结

项目经过以上优化,打包从30分钟,到2分钟不到,整体还有优化空间,可以使用其他cdn等优化方式。

查看原文

赞 14 收藏 10 评论 0

邓疯疯 收藏了文章 · 1月6日

基于vue-cli的webpack打包优化实践及探索

转眼已经是2019年,短短三四年时间,webpack打包工具成为了前端开发中必备工具,曾经一度的面试题都是问,请问前端页面优化的方式有哪些?大家也是能够信手拈来的说出缓存、压缩文件、CSS雪碧图以及部署CDN等等各种方法,但是今天不一样了,可能你去面试问的就是,请问你是否知道webpack的打包原理,webpack的打包优化方法有哪些?所以该说不说的,笔者闲着没事研究了一下webpack的打包优化,可能大家都有看过类似的优化文章~ 但是笔者还是希望能够给大家一些新的启发~

1、准备工作:测速与分析bundle

既然我们要优化webpack打包,肯定要提前对我们的bundle文件进行分析,分析各模块的大小,以及分析打包时间的耗时主要是在哪里,这里主要需要用到两个webpack插件,speed-measure-webpack-plugin和webpack-bundle-analyzer,前者用于测速,后者用于分析bundle文件。

具体配置

const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
const smp = new SpeedMeasurePlugin({
  outputFormat:"human",
});
module.exports = {
configureWebpack: smp.wrap({
    plugins: [
      new webpack.ProvidePlugin({
        $: "zepto",
        Zepto: "zepto",
      }),
      new BundleAnalyzerPlugin(),
    ],
    optimization: {
      splitChunks: {
        cacheGroups: {
          echarts: {
            name: "chunk-echarts",
            test: /[\\/]node_modules[\\/]echarts[\\/]/,
            chunks: "all",
            priority: 10,
            reuseExistingChunk: true,
            enforce: true,
          },
          demo: {
            name: "chunk-demo",
            test: /[\\/]src[\\/]views[\\/]demo[\\/]/,
            chunks: "all",
            priority: 20,
            reuseExistingChunk: true,
            enforce: true,
          },
          page: {
            name: "chunk-page",
            test: /[\\/]src[\\/]/,
            chunks: "all",
            priority: 10,
            reuseExistingChunk: true,
            enforce: true,
          },
          vendors: {
            name: "chunk-vendors",
            test: /[\\/]node_modules[\\/]/,
            chunks: "all",
            priority: 5,
            reuseExistingChunk: true,
            enforce: true,
          },
        },
      },
    },
  })
}

由于是基于vue-cli脚手架的,所以其实vue-cli中已经帮你做了一些优化的工作,可以看到,原先项目最初的配置设置了splitchunk,进行代码分割,这在大型项目中是很有必要的,毕竟你不希望你的用户阻塞加载一个5MB大小的JS文件,所以做代码分割和懒加载是很有必要的。
说远了,我们来看看这个配置,你需要用smp对配置进行再包裹,因为SpeedMeasurePlugin会对你的其他Plugin对象包裹一层代理,这样的目的是为了能够知道plugin开始和结束的时间~
其次,BundleAnalyzerPlugin就跟普通的plugin一样,加载plugins数组的后面即可。
接下来我们看一下最初的打包时间以及包内容分析:

image.png

image.png

可以看到项目中较大的三个包,其中两个包是我们的第三方依赖,three.js、lottie、lodash、echarts等。

2、开始逐步优化

2.1缩小文件查找和处理范围

这是webpack优化中的常规操作,基本就是对模块和文件查找的优化,以及减少loader对一些不必要模块的处理,但是vue-cli中的loader并没有暴露给我们操作,所以其内置的loader处理无法由我们进行优化,但是其实vue-cli中的配置项已经对loader的查找路径进行了优化,如果你的项目也是使用了vue-cli,你可以通过以下命令行查看你现有的配置文件是怎样的:

npx vue-cli-service inspect > output.js

具体可以翻阅vuecli官方文档。

resolve:{
  modules: [path.resolve(__dirname, 'node_modules')],
  alias:{
    'three':path.resolve(__dirname, './node_modules/three/build/three.min.js'),
    'zepto$':path.resolve(__dirname, './node_modules/zepto/dist/zepto.min.js'),
    'swiper$':path.resolve(__dirname, './node_modules/swiper/dist/js/swiper.min.js'),
    'lottie-web$':path.resolve(__dirname, './node_modules/lottie-web/build/player/lottie.min.js'),
    'lodash$':path.resolve(__dirname, './node_modules/lodash/lodash.min.js'),
  }
},
module:{
  noParse:/^(vue|vue-router|vuex|vuex-router-sync|three|zepto|swiper|lottie-web|lodash)$/
},
  • 通过modules指定查找第三方模块的路径。
  • 通过alias指定第三方模块直接查找到打包构建好的压缩js文件。
  • 通过module指定noparse,对第三方模块不再进行分析依赖。

优化效果:2s?
image.png

可以看到时间就减少了两三秒,在30s波动,感觉没有多大差别。

2.2尝试使用happypack

由于在进行webpack优化前,翻阅了很多有关webapck优化的文章,所以笔者也想尝试一下用happypack来优化打包时间。
在想要用happypack进行的打包之前,大抵有这两种说法:
1、webpack4中已经默认是多线程打包了,所以happypack打包效果不明显;
2、vue不支持happypack打包,需要设置thread-loader。
但是笔者想了一下,还是试试看把,大不了我只对JS和CSS文件设置happypack。
但是问题又来了,vue-cli内置封装了loader,这个时候我要怎么拿到它的配置,改写里面的loader配置呢。
通过翻阅vue-cli的官方文档我们可以看到以下使用介绍:

configureWebpack
Type: Object | Function
如果这个值是一个对象,则会通过 webpack-merge 合并到最终的配置中。
如果这个值是一个函数,则会接收被解析的配置作为参数。该函数及可以修改配置并不返回任何东西,也可以返回一个被克隆或合并过的配置版本。

为此,笔者特地调试进了vue-cli的源码一探究竟:
流程介绍:
由于我们执行命令行vue-cli-service build,其实是先去node_modules的.bin文件夹下查找相应的可执行文件,.bin下的vue-cli-service会映射到相应的第三方库内的执行文件。
所以我们可以找到这个可执行文件的地址:
/node_modules/@vue/cli-service/bin/vue-cli-service.js
找到了入口,接下来我们想要进入nodejs的调试,在以往的开发中,我们会通过node --inspect app.js的方式启动一个后台服务,然后在谷歌浏览器里进入调试界面(F12选择绿色的那个小按钮)
但是这里却犯了难,由于我们的打包构建是一次执行的,不同于一个后台服务,是实时监听的,服务一直启动着。查阅了一下,如果是普通的nodejs文件想要调试的话,需要通过这样的方式:

node --inspect-brk=9229 app.js

所以,为了强行走进去vue-cli的源码进行调试,可看vue-cli的处理流程,我们需要这样输入以下命令行:

node --inspect-brk=9229 node_modules/@vue/cli-service/bin/vue-cli-service.js build 

上面的这个命令行,等价于vue-cli-service build。
通过这样的方式,我们终于走进了vue-cli的源码,看了它的执行流程,你可以在对应的位置打下断点,查看此时的作用域内的变量数据。
image.png
可以看到vue-cli源码里的这一段操作,会执行我们传入的函数,判断函数有没有返回值来决定是否要merge进其内部配置的config。
通过这段代码我们可以看出,如果我们configWepack配置为函数,之后通过参数的形式获取到config配置项,本身是一个对象,对象是保留引用的形式,所以如果我们直接对传入的config对象进行修改,就可以实现我们最初的目标!修改vue-cli内置的loader!
当然,除了断点进入里面看配置,刚才也说了,我们可以通过命令行输出为一个output文件查看现有的配置。
这里可以给大家截图看一下vue-cli内部的配置:
image.png
可能有点废话了,但是通过断点的方式,我们可以看到vue-cli其实已经对js文件设置了exclude,同时也帮我们设置好了cache-loader,意味着webpack常规的优化方式之一,使用cache-loader缓存它也帮我们做了。
回到最初的起点,我们想要处理的是针对JS和CSS的loader,于是模仿大多数的配置,我进行了以下修改:

  configureWebpack:(config)=>{
    console.log("webpack config start");
    let originCssRuleLoader = config.module.rules[6].oneOf[0].use;
    let newCssRuleLoader = 'happypack/loader?id=css';
    config.module.rules[6].oneOf[0].use = newCssRuleLoader
    config.module.rules[6].oneOf[1].use = newCssRuleLoader
    config.module.rules[6].oneOf[2].use = newCssRuleLoader
    config.module.rules[6].oneOf[3].use = newCssRuleLoader
    ...//other code
 }

尝试对css的loader配置进行修改。之后对plugins进行一下配置:

plugins: [
    new HappyPack({
      id: 'css',
      threads: 4,
      loaders: originCssRuleLoader
    }),
  ],

本以为这样就OK了,但是很遗憾的告诉大家,报错了...
image.png
可以看到报错的内容,是在处理vue文件的时候,出了错误。
如何解决
笔者百度了,也谷歌了,大抵是说happypack不支持vue-loader,同时,根据报错也查了一下处理的方案,通过设置parallel参数,也还是无效。
笔者甚至怀疑是自己的happypack配置不对,于是我把配置原样移植配置到另一个非vue项目中,一切运行正常。
答案:此题无解~
原因分析:
由于vue文件中会含有CSS,所以vue-loader会提取出其中的css,交给其他loader处理,vue-loader-plugin会通过在vue文件后面加上查询字符串来告诉其他loader,针对这个文件要做处理。意味着什么呢?我们的vue-loader在处理文件的时候,通知其他loader处理,但是此时的loader配置已经被我们改写成了happypack,而vue又与happypack不兼容,最终导致了报错。很遗憾的告诉大家,vue-cli接入happypack--失败。
(注:这一部分主要是笔者在webpack优化过程中的探索,虽然最终不能让自己的webpack打包很好的优化,但是在这个探索的过程中,我们也可以学到很多~包括 vue-cli对配置对象的处理?如何调试普通文件nodejs代码?vue-loader中对vue文件的处理流程?vue-loader-plugin帮我们做了什么事?而这些都是要自己慢慢翻阅,慢慢踩坑去了解的~)

2.3使用dllplugin

和大多数的webpack优化教程一样,笔者也尝试了利用dllplugin进行优化,该插件的本质,是提取出我们常用的第三方模块,单独打成一个文件包,之后插入到我们的html页面中,这样我们以后每次打包,都不需要针对第三方模块进行处理,毕竟第三方模块动辄成千上万行。
流程介绍:

  • 1、配置webpack.dll.js针对第三方库打包
  • 2、vue.config.js中配置plugin
  • 3、html中引入dll打包出来的js文件。(一般采用部署CDN的方式)

由于项目中有很多大型的第三方库,类似three、echart等,所以笔者进行了以下配置:(webpack.dll.js)

const webpack = require("webpack")
const path = require("path")
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
    entry: {
        vuebundle: [
            'vue',
            'vue-router',
            'vuex',
        ],
        utils:[
            'lodash',
            'swiper',
            'lottie-web',
            'three',
        ],
        echarts:[
            'echarts/lib/echarts',
            "echarts/lib/chart/bar",
            "echarts/lib/chart/line",
            "echarts/lib/component/tooltip",
            "echarts/lib/component/title",
            "echarts/lib/component/legend",
        ]

    },
    output: {
        path: path.resolve(__dirname, './static/'),
        filename: '[name].dll.js',
        library: '[name]_library'
    },
    plugins: [
        new webpack.DllPlugin({
            path: path.join(__dirname, 'build', '[name]-manifest.json'),
            name: '[name]_library'
        })
    ]
}

针对不同的库的大小进行划分,打了三个包,为啥不打成一个包?一个包那就太大了,你并不希望你的用户加载一个大型JS文件包而阻塞,影响页面性能。
接下里是vue.config.js的配置:

plugins: [
      new webpack.ProvidePlugin({
        $: "zepto",
        Zepto: "zepto",
      }),
      new DllReferencePlugin({
        manifest: require('./build/echarts-manifest.json'),
      }),
      new DllReferencePlugin({
        manifest: require('./build/utils-manifest.json'),
      }),
      new DllReferencePlugin({
        manifest: require('./build/vuebundle-manifest.json'),
      }),
      new BundleAnalyzerPlugin(),
    ]

引入了DllPlugin。接下来配置HTML:
(由于笔者没将DLL打包出来的js文件上传到CDN,所以只能本地自己起个node服务器返回静态资源了)

  <body>
     <div id="app"></div>
    <!-- built files will be auto injected -->
    <script type="text/javascript" data-original="http://localhost:3000/echarts.dll.js"></script>
    <script type="text/javascript" data-original="http://localhost:3000/utils.dll.js"></script>
    <script type="text/javascript" data-original="http://localhost:3000/vuebundle.dll.js"></script>
  </body>

然后npm run serve,开始页面调试和开发~
舒服~
优化结果:
image.png
由于少了大型第三方库,所以时间控制在了20s左右了。优化相对比较明显~

3、优化与探索总结

优化到这,基本就结束了。
webpack常见的优化方式,优化路径查找、设置缓存、happypack以及dllplugin,前两项vue-cli已经帮我们做了一些,而happypack由于不和vue兼容,导致无法接入,dllplugin通过单独提取第三方库,取得了明显优化。
当然,笔者也尝试剔除了一些项目中无用的代码,不过也是不痛不痒。
webpack优化方式总结:

  • 1、优化模块查找路径
  • 2、剔除不必要的无用的模块
  • 3、设置缓存:缓存loader的执行结果(cacheDirectory/cache-loader)
  • 4、设置多线程:HappyPack/thread-loader
  • 5、dllplugin提取第三方库

当然,这是针对开发的优化,如果是针对部署上的优化呢?我们可以设置splitchunk、按需加载、部署CDN等,这里就不展开了。

最后

希望这篇文章能够大家有所收获~ webpack已经是前端仔必备技能了~有空大家钻研一下webpack的配置和原理,也是会有所收获的!谢谢观看~

查看原文

邓疯疯 赞了文章 · 1月6日

基于vue-cli的webpack打包优化实践及探索

转眼已经是2019年,短短三四年时间,webpack打包工具成为了前端开发中必备工具,曾经一度的面试题都是问,请问前端页面优化的方式有哪些?大家也是能够信手拈来的说出缓存、压缩文件、CSS雪碧图以及部署CDN等等各种方法,但是今天不一样了,可能你去面试问的就是,请问你是否知道webpack的打包原理,webpack的打包优化方法有哪些?所以该说不说的,笔者闲着没事研究了一下webpack的打包优化,可能大家都有看过类似的优化文章~ 但是笔者还是希望能够给大家一些新的启发~

1、准备工作:测速与分析bundle

既然我们要优化webpack打包,肯定要提前对我们的bundle文件进行分析,分析各模块的大小,以及分析打包时间的耗时主要是在哪里,这里主要需要用到两个webpack插件,speed-measure-webpack-plugin和webpack-bundle-analyzer,前者用于测速,后者用于分析bundle文件。

具体配置

const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
const smp = new SpeedMeasurePlugin({
  outputFormat:"human",
});
module.exports = {
configureWebpack: smp.wrap({
    plugins: [
      new webpack.ProvidePlugin({
        $: "zepto",
        Zepto: "zepto",
      }),
      new BundleAnalyzerPlugin(),
    ],
    optimization: {
      splitChunks: {
        cacheGroups: {
          echarts: {
            name: "chunk-echarts",
            test: /[\\/]node_modules[\\/]echarts[\\/]/,
            chunks: "all",
            priority: 10,
            reuseExistingChunk: true,
            enforce: true,
          },
          demo: {
            name: "chunk-demo",
            test: /[\\/]src[\\/]views[\\/]demo[\\/]/,
            chunks: "all",
            priority: 20,
            reuseExistingChunk: true,
            enforce: true,
          },
          page: {
            name: "chunk-page",
            test: /[\\/]src[\\/]/,
            chunks: "all",
            priority: 10,
            reuseExistingChunk: true,
            enforce: true,
          },
          vendors: {
            name: "chunk-vendors",
            test: /[\\/]node_modules[\\/]/,
            chunks: "all",
            priority: 5,
            reuseExistingChunk: true,
            enforce: true,
          },
        },
      },
    },
  })
}

由于是基于vue-cli脚手架的,所以其实vue-cli中已经帮你做了一些优化的工作,可以看到,原先项目最初的配置设置了splitchunk,进行代码分割,这在大型项目中是很有必要的,毕竟你不希望你的用户阻塞加载一个5MB大小的JS文件,所以做代码分割和懒加载是很有必要的。
说远了,我们来看看这个配置,你需要用smp对配置进行再包裹,因为SpeedMeasurePlugin会对你的其他Plugin对象包裹一层代理,这样的目的是为了能够知道plugin开始和结束的时间~
其次,BundleAnalyzerPlugin就跟普通的plugin一样,加载plugins数组的后面即可。
接下来我们看一下最初的打包时间以及包内容分析:

image.png

image.png

可以看到项目中较大的三个包,其中两个包是我们的第三方依赖,three.js、lottie、lodash、echarts等。

2、开始逐步优化

2.1缩小文件查找和处理范围

这是webpack优化中的常规操作,基本就是对模块和文件查找的优化,以及减少loader对一些不必要模块的处理,但是vue-cli中的loader并没有暴露给我们操作,所以其内置的loader处理无法由我们进行优化,但是其实vue-cli中的配置项已经对loader的查找路径进行了优化,如果你的项目也是使用了vue-cli,你可以通过以下命令行查看你现有的配置文件是怎样的:

npx vue-cli-service inspect > output.js

具体可以翻阅vuecli官方文档。

resolve:{
  modules: [path.resolve(__dirname, 'node_modules')],
  alias:{
    'three':path.resolve(__dirname, './node_modules/three/build/three.min.js'),
    'zepto$':path.resolve(__dirname, './node_modules/zepto/dist/zepto.min.js'),
    'swiper$':path.resolve(__dirname, './node_modules/swiper/dist/js/swiper.min.js'),
    'lottie-web$':path.resolve(__dirname, './node_modules/lottie-web/build/player/lottie.min.js'),
    'lodash$':path.resolve(__dirname, './node_modules/lodash/lodash.min.js'),
  }
},
module:{
  noParse:/^(vue|vue-router|vuex|vuex-router-sync|three|zepto|swiper|lottie-web|lodash)$/
},
  • 通过modules指定查找第三方模块的路径。
  • 通过alias指定第三方模块直接查找到打包构建好的压缩js文件。
  • 通过module指定noparse,对第三方模块不再进行分析依赖。

优化效果:2s?
image.png

可以看到时间就减少了两三秒,在30s波动,感觉没有多大差别。

2.2尝试使用happypack

由于在进行webpack优化前,翻阅了很多有关webapck优化的文章,所以笔者也想尝试一下用happypack来优化打包时间。
在想要用happypack进行的打包之前,大抵有这两种说法:
1、webpack4中已经默认是多线程打包了,所以happypack打包效果不明显;
2、vue不支持happypack打包,需要设置thread-loader。
但是笔者想了一下,还是试试看把,大不了我只对JS和CSS文件设置happypack。
但是问题又来了,vue-cli内置封装了loader,这个时候我要怎么拿到它的配置,改写里面的loader配置呢。
通过翻阅vue-cli的官方文档我们可以看到以下使用介绍:

configureWebpack
Type: Object | Function
如果这个值是一个对象,则会通过 webpack-merge 合并到最终的配置中。
如果这个值是一个函数,则会接收被解析的配置作为参数。该函数及可以修改配置并不返回任何东西,也可以返回一个被克隆或合并过的配置版本。

为此,笔者特地调试进了vue-cli的源码一探究竟:
流程介绍:
由于我们执行命令行vue-cli-service build,其实是先去node_modules的.bin文件夹下查找相应的可执行文件,.bin下的vue-cli-service会映射到相应的第三方库内的执行文件。
所以我们可以找到这个可执行文件的地址:
/node_modules/@vue/cli-service/bin/vue-cli-service.js
找到了入口,接下来我们想要进入nodejs的调试,在以往的开发中,我们会通过node --inspect app.js的方式启动一个后台服务,然后在谷歌浏览器里进入调试界面(F12选择绿色的那个小按钮)
但是这里却犯了难,由于我们的打包构建是一次执行的,不同于一个后台服务,是实时监听的,服务一直启动着。查阅了一下,如果是普通的nodejs文件想要调试的话,需要通过这样的方式:

node --inspect-brk=9229 app.js

所以,为了强行走进去vue-cli的源码进行调试,可看vue-cli的处理流程,我们需要这样输入以下命令行:

node --inspect-brk=9229 node_modules/@vue/cli-service/bin/vue-cli-service.js build 

上面的这个命令行,等价于vue-cli-service build。
通过这样的方式,我们终于走进了vue-cli的源码,看了它的执行流程,你可以在对应的位置打下断点,查看此时的作用域内的变量数据。
image.png
可以看到vue-cli源码里的这一段操作,会执行我们传入的函数,判断函数有没有返回值来决定是否要merge进其内部配置的config。
通过这段代码我们可以看出,如果我们configWepack配置为函数,之后通过参数的形式获取到config配置项,本身是一个对象,对象是保留引用的形式,所以如果我们直接对传入的config对象进行修改,就可以实现我们最初的目标!修改vue-cli内置的loader!
当然,除了断点进入里面看配置,刚才也说了,我们可以通过命令行输出为一个output文件查看现有的配置。
这里可以给大家截图看一下vue-cli内部的配置:
image.png
可能有点废话了,但是通过断点的方式,我们可以看到vue-cli其实已经对js文件设置了exclude,同时也帮我们设置好了cache-loader,意味着webpack常规的优化方式之一,使用cache-loader缓存它也帮我们做了。
回到最初的起点,我们想要处理的是针对JS和CSS的loader,于是模仿大多数的配置,我进行了以下修改:

  configureWebpack:(config)=>{
    console.log("webpack config start");
    let originCssRuleLoader = config.module.rules[6].oneOf[0].use;
    let newCssRuleLoader = 'happypack/loader?id=css';
    config.module.rules[6].oneOf[0].use = newCssRuleLoader
    config.module.rules[6].oneOf[1].use = newCssRuleLoader
    config.module.rules[6].oneOf[2].use = newCssRuleLoader
    config.module.rules[6].oneOf[3].use = newCssRuleLoader
    ...//other code
 }

尝试对css的loader配置进行修改。之后对plugins进行一下配置:

plugins: [
    new HappyPack({
      id: 'css',
      threads: 4,
      loaders: originCssRuleLoader
    }),
  ],

本以为这样就OK了,但是很遗憾的告诉大家,报错了...
image.png
可以看到报错的内容,是在处理vue文件的时候,出了错误。
如何解决
笔者百度了,也谷歌了,大抵是说happypack不支持vue-loader,同时,根据报错也查了一下处理的方案,通过设置parallel参数,也还是无效。
笔者甚至怀疑是自己的happypack配置不对,于是我把配置原样移植配置到另一个非vue项目中,一切运行正常。
答案:此题无解~
原因分析:
由于vue文件中会含有CSS,所以vue-loader会提取出其中的css,交给其他loader处理,vue-loader-plugin会通过在vue文件后面加上查询字符串来告诉其他loader,针对这个文件要做处理。意味着什么呢?我们的vue-loader在处理文件的时候,通知其他loader处理,但是此时的loader配置已经被我们改写成了happypack,而vue又与happypack不兼容,最终导致了报错。很遗憾的告诉大家,vue-cli接入happypack--失败。
(注:这一部分主要是笔者在webpack优化过程中的探索,虽然最终不能让自己的webpack打包很好的优化,但是在这个探索的过程中,我们也可以学到很多~包括 vue-cli对配置对象的处理?如何调试普通文件nodejs代码?vue-loader中对vue文件的处理流程?vue-loader-plugin帮我们做了什么事?而这些都是要自己慢慢翻阅,慢慢踩坑去了解的~)

2.3使用dllplugin

和大多数的webpack优化教程一样,笔者也尝试了利用dllplugin进行优化,该插件的本质,是提取出我们常用的第三方模块,单独打成一个文件包,之后插入到我们的html页面中,这样我们以后每次打包,都不需要针对第三方模块进行处理,毕竟第三方模块动辄成千上万行。
流程介绍:

  • 1、配置webpack.dll.js针对第三方库打包
  • 2、vue.config.js中配置plugin
  • 3、html中引入dll打包出来的js文件。(一般采用部署CDN的方式)

由于项目中有很多大型的第三方库,类似three、echart等,所以笔者进行了以下配置:(webpack.dll.js)

const webpack = require("webpack")
const path = require("path")
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
    entry: {
        vuebundle: [
            'vue',
            'vue-router',
            'vuex',
        ],
        utils:[
            'lodash',
            'swiper',
            'lottie-web',
            'three',
        ],
        echarts:[
            'echarts/lib/echarts',
            "echarts/lib/chart/bar",
            "echarts/lib/chart/line",
            "echarts/lib/component/tooltip",
            "echarts/lib/component/title",
            "echarts/lib/component/legend",
        ]

    },
    output: {
        path: path.resolve(__dirname, './static/'),
        filename: '[name].dll.js',
        library: '[name]_library'
    },
    plugins: [
        new webpack.DllPlugin({
            path: path.join(__dirname, 'build', '[name]-manifest.json'),
            name: '[name]_library'
        })
    ]
}

针对不同的库的大小进行划分,打了三个包,为啥不打成一个包?一个包那就太大了,你并不希望你的用户加载一个大型JS文件包而阻塞,影响页面性能。
接下里是vue.config.js的配置:

plugins: [
      new webpack.ProvidePlugin({
        $: "zepto",
        Zepto: "zepto",
      }),
      new DllReferencePlugin({
        manifest: require('./build/echarts-manifest.json'),
      }),
      new DllReferencePlugin({
        manifest: require('./build/utils-manifest.json'),
      }),
      new DllReferencePlugin({
        manifest: require('./build/vuebundle-manifest.json'),
      }),
      new BundleAnalyzerPlugin(),
    ]

引入了DllPlugin。接下来配置HTML:
(由于笔者没将DLL打包出来的js文件上传到CDN,所以只能本地自己起个node服务器返回静态资源了)

  <body>
     <div id="app"></div>
    <!-- built files will be auto injected -->
    <script type="text/javascript" data-original="http://localhost:3000/echarts.dll.js"></script>
    <script type="text/javascript" data-original="http://localhost:3000/utils.dll.js"></script>
    <script type="text/javascript" data-original="http://localhost:3000/vuebundle.dll.js"></script>
  </body>

然后npm run serve,开始页面调试和开发~
舒服~
优化结果:
image.png
由于少了大型第三方库,所以时间控制在了20s左右了。优化相对比较明显~

3、优化与探索总结

优化到这,基本就结束了。
webpack常见的优化方式,优化路径查找、设置缓存、happypack以及dllplugin,前两项vue-cli已经帮我们做了一些,而happypack由于不和vue兼容,导致无法接入,dllplugin通过单独提取第三方库,取得了明显优化。
当然,笔者也尝试剔除了一些项目中无用的代码,不过也是不痛不痒。
webpack优化方式总结:

  • 1、优化模块查找路径
  • 2、剔除不必要的无用的模块
  • 3、设置缓存:缓存loader的执行结果(cacheDirectory/cache-loader)
  • 4、设置多线程:HappyPack/thread-loader
  • 5、dllplugin提取第三方库

当然,这是针对开发的优化,如果是针对部署上的优化呢?我们可以设置splitchunk、按需加载、部署CDN等,这里就不展开了。

最后

希望这篇文章能够大家有所收获~ webpack已经是前端仔必备技能了~有空大家钻研一下webpack的配置和原理,也是会有所收获的!谢谢观看~

查看原文

赞 31 收藏 23 评论 0

邓疯疯 提出了问题 · 2020-02-25

用serverless无法部署egg到腾讯的云函数

一直在等待中无法部署成功

# serverless.yml
MyComponent:
  component: "@serverless/tencent-egg"
  inputs:
    region: ap-guangzhou 
    functionName: egg-function
    code: ./
    functionConf:
      timeout: 10
      memorySize: 128
      environment:
        variables:
          TEST: vale
      vpcConfig:
        subnetId: ''
        vpcId: ''
    apigatewayConf:
      protocol: https
      environment: release

配置如上
登陆之后显示这个,
DEBUG ─ Compressing function egg-function file to D:workspaceserverlessegg.serverless/egg-function.zip.
然后就一直卡在这里,一直在发布中的状态

关注 2 回答 1

邓疯疯 提出了问题 · 2020-02-25

用serverless无法部署egg到腾讯的云函数

一直在等待中无法部署成功

# serverless.yml
MyComponent:
  component: "@serverless/tencent-egg"
  inputs:
    region: ap-guangzhou 
    functionName: egg-function
    code: ./
    functionConf:
      timeout: 10
      memorySize: 128
      environment:
        variables:
          TEST: vale
      vpcConfig:
        subnetId: ''
        vpcId: ''
    apigatewayConf:
      protocol: https
      environment: release

配置如上
登陆之后显示这个,
DEBUG ─ Compressing function egg-function file to D:workspaceserverlessegg.serverless/egg-function.zip.
然后就一直卡在这里,一直在发布中的状态

关注 2 回答 1

邓疯疯 关注了专栏 · 2020-02-25

猪哥python

微信搜索:裸睡的猪

关注 2667

邓疯疯 关注了专栏 · 2020-02-25

前端小渣

关注「菜鸟学前端」,回复「666」,领取一揽子前端技术书籍

关注 166

邓疯疯 关注了用户 · 2020-02-25

lzg9527 @michael_5c03399eed011

专注web前端开发,熟悉html5,css3,javascript,vue,react

关注 7552

认证与成就

  • 获得 0 次点赞
  • 获得 1 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 1 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2020-02-25
个人主页被 97 人浏览