GVKun编程网logo

webpack打包优化解决方案(webpack 打包优化)

14

在本文中,我们将为您详细介绍webpack打包优化解决方案的相关知识,并且为您解答关于webpack打包优化的疑问,此外,我们还会提供一些关于00-认识webpack-为什么要模块化-webpack打

在本文中,我们将为您详细介绍webpack打包优化解决方案的相关知识,并且为您解答关于webpack 打包优化的疑问,此外,我们还会提供一些关于00-认识webpack - 为什么要模块化 -webpack打包过程、react webpack打包之后 图片裂了的原因和解决方法、vue webpack打包优化操作技巧、vue-cli3 webpack 打包优化的有用信息。

本文目录一览:

webpack打包优化解决方案(webpack 打包优化)

webpack打包优化解决方案(webpack 打包优化)

单页应用首次进入项目会获取一部分数据,之后将JS包分片,走到那块再去加载那块的JS。
这样跨页面重复的JS,CSS不必再去获取,跨页面就不会出现进度条。这样减少了等待时间,提升了用户体验,省去了不必要的流量。
但是单页应用也有一个显著的问题:首次进入的时候,加载的资源太多,白屏时间太长。

这里介绍一些常用的webpack打包优化解决方案

  1. 使用插件查看项目所有包及体积大小
  2. webpack外部扩展
  3. DLL方式

一、查看项目打包

webpack有个插件,可以查看项目一共打了多少包,每个包的体积,每个包里面的包情况。
首先下载插件

npm intall webpack-bundle-analyzer --save-dev

同时在webpack.config.js配置

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

webpackConfig.plugins.push(new BundleAnalyzerPlugin());

在package.json中添加命令

“script”: {
    "analyz": "NODE_ENV=production npm_config_report=true npm run deploy:prod"
}

我的webpack是1.X的 webpack 2.X的同学请看

然后命令行输入

npm run analyz

开始构建,根据项目大小不同,时间也不同。
过一会输出结果如下:

可以清晰的看到项目中一共有多少个包,包的体积是多少,里面加载了哪些东西,大小是多少。
这里演示的是一个干净的demo,打的包很少,体积也都很小,在真正项目中可能截图如下:

可以看到五颜六色的十分好看。
这里面 app.js 和 vendor.js是项目的主文件,其中包含了少部分业务代码和大部分公共依赖。
剩余的 number.hash.chunk.js是业务分片的代码,其中包含了大部分业务代码和少部分公共依赖。
到这里,我们就是用工具完成对项目打包的展示。

二、webpack外部扩展

列出了项目中较大的包,剩下的事情就是想办法如何减小这些包的体积(将一个大包拆成多个小包)。
项目中产生较大的包的原因可以从两个方面去考虑:

  1. 项目中引入的依赖包过于庞大;
  2. 业务代码集中在一块写,或者是业务代码写的比较繁琐;

对于这两个问题,我们可以从两个方面着手解决:

  1. 抽离项目中公共依赖的、不常变动的、体积较大的包;
  2. 将一个较大的业务代码文件,拆成多个较小的文件,异步加载(或者优化业务代码)。

这里面第二项涉及到改动业务代码,具体的情况就不同了,适合查看 如何优化JS代码。
我们来讨论第一种方法,在不改动业务代码的情况下,如何减小公共依赖。

要知道这些依赖是我们需要的,不可能排除不引入。
但是他们都是全局依赖的,万年不变的,可以使用浏览器自己的缓存来实现不重复加载。
具体做法就是:
将项目中需要的一些公共依赖包,并且不常变动的,单独取出,不再每次都打包编译(如React,Redux等)。
而是通过使用script标签形式cdn引入,这样在有缓存的情况下,这些资源均走缓存,不必加载。

具体做法:

总结需要抽离的公共依赖。

这些依赖需要满足一定的条件:

  1. 体积较大;
  2. 不常更新;
  3. 依赖较多;
  4. 是前置依赖;

常见的满足这类条件的包有:

  1. react
  2. react-dom
  3. redux
  4. react-redux
  5. moment
  6. jquery

另外一些包文件如 Antd库文件,整个包较大,但是每次用什么取什么的话,库文件也会按需加载,不必单独取出。
还有这类库文件不建议单独取出,因为里面可能会有bug,需要更新。

使用CDN引入资源

以我的demo为例:我需要抽离出的文件有 react,react-dom,react-router,redux,react-redux,history。
将这些文件放到cnd上,注意,这些文件要是压缩版本,并且是用ES5编写的,否则浏览器报错。

<head>
  <title>React Starter Kit</title>
  <Meta charset="utf-8">
  <Meta name="viewport" content="width=device-width,initial-scale=1">
  <!-- 体积较大的包 -->
  <script src="https://cdn.bootcss.com/react/15.0.0/react-with-addons.min.js"></script>
  <script src="https://cdn.bootcss.com/react/15.0.0/react-dom.min.js"></script>
  <script src="https://cdn.bootcss.com/react-router/3.0.0/ReactRouter.min.js"></script>
  <script src="https://cdn.bootcss.com/redux/3.6.0/redux.min.js"></script>
  <script src="https://cdn.bootcss.com/react-redux/5.0.1/react-redux.min.js"></script>
  <script src="https://cdn.bootcss.com/history/4.5.0/history.min.js"></script>
</head>

配置webpack.conf.js

资源已经引入,接下来需要配置webpack,使其打包的时候不在将这些资源打包。

const webpackConfig = {
    name: 'client',target: 'web',devtool: config.compiler_devtool,resolve: {
        root: paths.client(),extensions: ['','.js','.jsx','.json'],},externals: {
        'react': 'React','react-dom': 'ReactDOM','react-router': 'ReactRouter','redux': 'Redux','history': 'History'
    },module: {},}

这里externals告诉webpack那些资源从哪里寻找。
该对象的键表示 require 或者 import 时候的字符串
值表示的当前环境下的变量,比如引入React之后,React被作为全局对象,webpack就回去寻找React对象。
如果其中有一个找不到,打包就会失败。

配置vendor.js

接下来配置vendor,使vendor也不打包该些JS

compiler_vendors : [
    // 'react',// 'react-redux',// 'react-router',// 'redux',],

接下来再次运行 npm run analyz

对比第一次的效果图,很明显app.js由原来的625kb减少到了78kb,
原来第二大的vendor.js现在已经很小了。
但是要注意的是,并不是包越小越好,越小的包反而越耗费链接。
应该让你的包里面的业务代码占大多数。
后来被告知,最大的包的体积压缩之后80k以内就可以。

三、DLL方式

dll 全称是:dynamic link library(动态链接库)
dll方式也就是通过配置,告诉webpack指定库在项目中的位置,从而直接引入,不将其打包在内。
上面介绍的方式是将包放到cdn上,build的时候不在引入对应的包;
dll方式就是指定包在项目中,build的时候不在打包对应的包,使用的时候引入。
webpack通过webpack.DllPluginwebpack.DllReferencePlugin两个内嵌插件实现此功能。

新建webpack.dll.config.js

const webpack = require('webpack');

module.exports = {
    entry: {
        bundle: [
            'react','react-dom',//其他库
            ],output: {
        path: './build',filename: '[name].js',library: '[name]_library'
    },plugins: [
        new webpack.DllPlugin({
            path: './build/bundle.manifest.json',name: '[name]_library',})
    ]
};

webpack.DllPlugin选项:

  • path:manifest.json文件的输出路径,这个文件会用于后续的业务代码打包;
  • name:dll暴露的对象名,要跟output.library保持一致;
  • context:解析包路径的上下文,这个要跟接下来配置的 webpack.config.js 一致。

运行文件

运行:webpack --config webpack.dll.config.js

生成两个文件,一个是打包好的bundlejs,另外一个是bundle.mainifest.json,大致内容如下:

{
  "name": "bundle_library","content": {
    "./node_modules/react/react.js": 1,"./node_modules/react/lib/React.js": 2,"./node_modules/process/browser.js": 3,"./node_modules/object-assign/index.js": 4,"./node_modules/react/lib/ReactChildren.js": 5,"./node_modules/react/lib/PooledClass.js": 6,"./node_modules/react/lib/reactProdInvariant.js": 7,//其他引用
}

配置webpack.config.js

const webpack = require('webpack');
var path = require('path');
module.exports = {
  entry: {
    main: './main.js',output: {
    path: path.join(__dirname,"build"),publicPath: './',filename: '[name].js'
  },module: {
    loaders:[
      { test: /\.(png|jpg)$/,loader: 'url-loader?limit=8192'},{
        test: /\.jsx?$/,loaders: ['babel-loader?presets[]=es2015&presets[]=react'],include: path.join(__dirname,'.')
      }
    ]
  },plugins: [
     new webpack.DllReferencePlugin({
      context: '.',manifest: require("./build/bundle.manifest.json"),}),]
};

webpack.DllReferencePlugin的选项中:

  • context:需要跟之前保持一致,这个用来指导webpack匹配manifest.json中库的路径;
  • manifest:用来引入刚才输出的manifest.json文件。

参考链接:

  1. 层层优化webpack打包体积
  2. webpack打包bundle.js体积大小优化
  3. webpack 按需打包加载
  4. webpack dllPlugin 使用

完整的项目demo

00-认识webpack - 为什么要模块化 -webpack打包过程

00-认识webpack - 为什么要模块化 -webpack打包过程

webpack在不进行任何配置的情况下,他只认识js。

为什么要打包?

因为一个个小文件,我们合成一个,这样请求就只请求一次。

webpack除了打包之外,还具有翻译官的功能?

loader把浏览器看不懂的代码翻译成浏览器看的懂的代码。

 Plugin?

对文件做点别的事情

不管是loader 还是 plugin 都是可插拔,意思就是你什么时候想用,你就装进来,不想用,删掉。所以说webpack不仅强大,而且灵活。


 

为什么要模块化?模块化的好处?

 

 

 如上图,传统的一个页面去引入不同的js就是如上一个个去引入,但是这样会有命名冲突的问题,A.js 和B.js中可能有相同的变量命名。

解决方法:

在A.js中和B.js 中加上命名空间

 

 

命名空间会造成一些问题: 

 

 

 如上图,把这个autoLogin命名空间的模块暴露出去了,其实暴露的只是想让login 给别人用,但是全部都给别人了,如果别人误操作去 autoLogin.username = '张三', 不小心把username给改掉了,那么可能你login方法里去做的登录名和密码就永远也不符合了,这不符合开闭原则。

所以,单纯使用命名空间只是为了起到被覆盖,冲突的作用,但是并不能保证随意的访问和篡改,那么如何做呢?

我们用函数作用域,函数的闭包机制,保护里面的变量不被污染。也叫作模块作用域:暴露该暴露的,隐藏该隐藏的。

 

 

 如上图:这样利用闭包就保护了私有变量,又能只暴露我想暴露出去的这个方法。这样外面再去 :

 就拿不到里面的变量了。就不会被人篡改了。这就是所谓的开闭原则

 

 

 改写一个比较标准的模块化写法:

 

 

 上面的为什么要用一个对象包裹一下tell方法?不能直接赋值给susanModule吗?

 

 

 

 如图以上对比:就是给susanModule 让其为一个对象,对象是叫做tell,tell是个与value同名的key,以后调用的时候就是 susanModule.tell()


 

 综上:我们就可以得到模块化的优点:

1.作用域的封装 :模块内部的实现不会暴露在危险的全局作用域中,上面就是通过暴露tell接口,供外面使用。

2.重用性:如上面的,只需要执行susanModule.tell 就可以拿到这个方法

3.解除耦合 :如果把代码全部扔在一个文件中,出现BUG排查起来很难找,这样模块化后就容易找到定位到错误的模块。


 

 webpack打包原理和过程:

1.把你写的模块(js文件)中的每个逻辑代码块当做webpack一个自执行函数的入参(入参是一个数组,你写的这些代码逻辑块都会装到这个数组中)

 

第一步:

 

 

 第二步:把模块包装起来放入自执行函数中的数组(数组都是模块的入参)

 

 第三步:实现模块加载的方法,并把它放到模块执行的环境中,确保模块间可以相互调用。

 

1.首选去检查,你现在塞给我的这个模块啊我是不是已经加载过了,我去已加载模块的对象中去查这个id,发现已经加载过了,就不进行二次加载了,就直接把加载过的这个结果给你返回过来。

2.如果这个模块没有加载过,那我把其存到那个已加载的对象中,存的时候添加一个moduleId属性,就是给其做个记录

3.接着我就把他的this指到module.exports的导出结果上去,并且去执行已经包装好的模块逻辑。这一步就是让模块逻辑进行执行

4.最后把模块的返回值给return 出去。

第四步:把执行入口文件的逻辑放在一个函数表达式中,并立即执行这个函数(只的就是下面的这个外层的自执行函数)


 

react webpack打包之后 图片裂了的原因和解决方法

react webpack打包之后 图片裂了的原因和解决方法

image.png

如上图,图片裂了,而其他的图片好好的。

image.png

上图就是这个项目关于图片转码的配置,在看了配置之后,发现图片的大小是有限制的,也就是limit限制的大小是10kb?(额,这不重要)
而我这个图片250k太大了!(我觉得我就是个二百五)

但是讲道理,这个图片就算不转换成base64,也应该可以访问的,但实际上……
image.png

没错,图片的地址在打包之后因为我更改了原本的static的位置,所以访问不到于是发生了错误。
打包之前,我的static的位置没有变更,所以相对路径就可以直接寻到。
但打包之后,我把static与index.html文件分开了,所以导致访问失效。
既然找到原因,那么有两种方法:

  1. 不更改配置,把打包好的文件中的图片单独拎出来放到一个能找到的地方,
  2. 改配置,别的不变

1方法太邪道,于是我果断选择了2 。
其实很简单,重新编辑一下如图一的options下的publicPath,放到能找到的位置就可以了。
image.png

虽然这样一来,开发的时候大图片会裂开,但打包后的位置就不会出错了。
本着开发无所谓上线要看起来正常的项目标准,我无视了按下葫芦起的瓢。

以上。

vue webpack打包优化操作技巧

vue webpack打包优化操作技巧

临近春节,公司很多同事都提前回家过年,剩余人员根据禅道去修改bug,当bug修正完毕以后,我们需要重新打包给运维,上测试服给测试同事提测,但是由于项目本体比较庞大,所以打包时间太过漫长(二十五分钟以上:sob:),所以有了打包优化的想法(其实想法早就有了,但是因为平时工作计划比较充实,所以一直没有去完成这个工作),这次正好有时间,所以去重新考虑了这个问题!

webpack是react项目标配的打包工具,和NPM搭配起来使用管理模块实在非常方便。

  webapck 把所有的静态资源都看做是一个 module,通过 webpack,将这些 module 组成到一个 bundle 中去,从而实现在页面上引入一个 bundle.js,来实现所有静态资源的加载。

话不多说,直接开始正文吧

先给大家看一下项目的目录结构:

就是正常的项目结构,简单说一下吧:

  • build文件夹包含的是一些打包配置的一下东西
  • config文件夹是项目的基础配置
  • dist是打包之后的文件
  • node_modules是项目的依赖包
  • src文件夹里面是项目的源码
  • static文件夹里面放的是一些项目使用的静态资源
  • index.html是项目的首页
  • package.json文件是项目的配置json
  • yarn.lock是使用yarn锁定项目用的依赖

优化思路

项目打包时间长,原因无外乎就是项目整体比较庞大、依赖复杂、组件之前拆分不够合理。

对于这三个问题呢,我们可以针对下面这几个方面去做一下处理:

  • 对项目进行路由屏蔽,只打包自己需要打包的部分(我司就是好几个项目合并在了一起,至于原因则是 需求类似,所以放在一起比较省事 -_-||| 开发过程中是省了不少事,但是现在一样要还的!!!!)
  • 依赖关系复杂,这里说的是项目中的依赖模块比较多,像我们现在这个项目,光算依赖包的话就有40+,另外一个重要原因就是组件之间存在相同引用的依赖。解决思路是把项目中重用的依赖抽离出来进行单独打包。
  • 组件在写的过程中,需要考虑好这个组件的使用方向,以及实现功能,不能混为一谈。

实际操作

有了整体的思路,那么开搞就可以啦 去webpack文档去看了一下有一个DllPlugin,这个插件就是帮助我们解决问题的关键,下面是我webpack.dll.config的代码:

rush:js;"> var path = require("path"); var webpack = require("webpack"); function resolve (dir) { return path.join(__dirname,'..',dir) } module.exports = { // 你想要打包的模块的数组 entry: { vendor: ['vue','lodash','vuex','axios','vue-router','iview','element-ui','echarts','xlsx','jquery','vue-fullcalendar','vue-cookie','handsontable'] },output: { path: path.join(__dirname,'../dist/vendor-dll-js'),// 打包后文件输出的位置 filename: '[name].dll.js',library: '[name]_library' // vendor.dll.js中暴露出的全局变量名。 // 主要是给DllPlugin中的name使用, // 故这里需要和webpack.DllPlugin中的`name: '[name]_library',`保持一致。 },resolve: { extensions: ['.js','.vue','.json'],alias: { 'vue$': 'vue/dist/vue.esm.js','@': resolve('src'),} },plugins: [ new webpack.DllPlugin({ path: path.join(__dirname,'.','[name]-manifest.json'),name: '[name]_library',context: __dirname }),// 压缩打包的文件,与该文章主线无关 new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } }) ] };

我们需要将项目中具有重用性的包抽离出来,放在vendor数组里面,然后在下面output里面定义一下打包输出的文件路径,然后在resolve里面配置解析参数,最后定义使用的DllPlugin插件,UglifyJsPlugin是压缩js的插件

DllPlugin里的path,会输出一个vendor-manifest.json,这是用来做关联id的,打包的时候不会打包进去,所以不用放到static里 然后运行一下 webpack -p --progress --config build/webpack.dll.conf.js

成功以后,static下会有dll.vendor.js,根目录下会有vendor.manifest.json 各自打开看一下,就会看到依赖库的源码和匹配id

ok,到这里,抽离依赖库的事情就完成了,那么接下来问题就是怎么引用呢,怎么在dev和build跑呢?

这里补了一点dll和commonsChunk概念上的区别,commonsChunk之所以慢和大,是因为每次run的时候,都会去做一次打包,而实际上我们不会一直去更新我们引用的依赖库,所以dll的做法就等于是,事先先打包好依赖库,然后只对每次都修改的js做打包。

继续上面的步骤,我们需要根据生成的json文件去修改webpack.base.config文件:

rush:js;"> const manifest = require('../vendor-manifest.json') ...... plugins: [ new webpack.DllReferencePlugin({ manifest }) ]

然后打开index.html,在底部加上

总结

以上是小编为你收集整理的vue webpack打包优化操作技巧全部内容。

如果觉得小编网站内容还不错,欢迎将小编网站推荐给好友。

vuevuevuewebpack打包webpack打包优化

vue-cli3 webpack 打包优化

vue-cli3 webpack 打包优化

一、生成打包分析文件

1、package.json 中配置 report 命令

"scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint",
    "report": "vue-cli-service build --report"
}

2、终端运行命令

npm run report // 打包并生成分析文件

3、查看打包后的 dist 文件夹

image.png
可以看到多了一个 report.html 文件

4、双击打开 report.html 文件

image.png
可以看到打包后的每个文件的大小,每个文件都生成了哪些模块代码,以及不同模块的大小

二、路由懒加载

import Vue from ''vue''
import Router from ''vue-router''
import index from ''@/views/index.vue''

Vue.use(Router)

export default new Router({
  mode: ''history'',
  base: process.env.BASE_URL,
  routes: [
    {
      path: ''/'',
      name: "index",

      // 1. 普通引用
      component: index

      // 2. 路由懒加载
      component: resolve =>  require([''@/views/index.vue''], resolve)

      // 3. 路由懒加载,并且命名chunk名字
      component: () => import(/* webpackChunkName: ''index'' */ ''@/views/index.vue'') 

    }
  ]
});

三、外部引入模块

1、 将公共包放入 public 文件夹

image.png

2、 在 index.html 中引入公共包 js

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
        <link rel="icon" href="<%= BASE_URL %>favicon.ico">
        <link rel="stylesheet" href="<%= BASE_URL %>css/vuetify.min.css">
        <title>index</title>
    </head>
    <body>
        <div id="app"></div>
        <script type="text/javascript" src="<%= BASE_URL %>js/vue.min.js"></script>
        <script type="text/javascript" src="<%= BASE_URL %>js/vuetify.min.js"></script>
        <script type="text/javascript" src="<%= BASE_URL %>js/vuex.min.js"></script>
        <script type="text/javascript" src="<%= BASE_URL %>js/vue-router.min.js"></script>
        <script type="text/javascript" src="<%= BASE_URL %>js/axios.min.js"></script>
        <script type="text/javascript" src="<%= BASE_URL %>js/moment.min.js"></script>
    </body>
</html>

3、在 vue.config.js 中配置 externals

configureWebpack: (config) => {

    // production:生产环境,development:开发环境
    config.mode = process.env.NODE_ENV

    config.externals = {
        // key:vue是项目里引入时候要用的,value:是开发依赖库的主人定义的不能修改
        ''vue'': ''Vue'',
        ''vue-router'': ''VueRouter'',
        ''vuex'': ''Vuex'',
        ''axios'': ''axios'',
        "vuetify": "vuetify",
        "moment": "moment",
    }
}

4、注释公共包的引用

// import Vue from ''vue'' // 注释或删除代码
// import Router from ''vue-router'' // 注释或删除代码
import index from ''@/views/index.vue''

四、开启 gzip 压缩

1、安装 compression-webpack-plugin

cnpm i -D compression-webpack-plugin

2、在 vue.config.js 中配置

const CompressionPlugin = require(''compression-webpack-plugin'');

configureWebpack: (config) => {
    config.plugins = [
        ...config.plugins,
        new  CompressionPlugin({
            test: /\.js$|\.html$|\.css/,
            threshold: 10240,
            deleteOriginalAssets: false
        })
    ]
}

3、服务器端开启 gzip

Nginx服务器配置:

server {
    gzip on;
    gzip_buffers 4 16K;
    gzip_comp_level 5;
    gzip_min_length 100k;
    gzip_types text/plain application/x-javascript application/javascript application/json text/css application/xml text/javascript image/jpeg image/gif image/png;
    gzip_disable "MSIE [1-6\]\."; 
    gzip_vary on;
}

gzip on|off; // 是否开启gzip
gzip_min_length 100k; // 压缩的最小长度(再小就不要压缩了,意义不在)
gzip_buffers 4 16k; // 缓冲(压缩在内存中缓冲几块? 每块多大?)
gzip_comp_level 5; // 压缩级别(级别越高,压的越小,越浪费CPU计算资源)
gzip_types text/plain; // 对哪些类型的文件用压缩 如txt,xml,html,css,js等
gzip_vary on|off; // 是否传输gzip压缩标志

五、打包去掉console.log

1、安装 babel-plugin-transform-remove-console

cnpm install babel-plugin-transform-remove-console --save-dev

2、修改 babel.config.js 文件

let plugins = []
if (process.env.NODE_ENV === ''production'') {
  plugins.push("transform-remove-console")
}
module.exports = {
  presets: ["@vue/app"],
  plugins
}

我们今天的关于webpack打包优化解决方案webpack 打包优化的分享已经告一段落,感谢您的关注,如果您想了解更多关于00-认识webpack - 为什么要模块化 -webpack打包过程、react webpack打包之后 图片裂了的原因和解决方法、vue webpack打包优化操作技巧、vue-cli3 webpack 打包优化的相关信息,请在本站查询。

本文标签:

上一篇使用webpack实现react的热更新(webpack 热更新原理)

下一篇前端清单第 27 期:React Patent License 回复,Shopify WebVR 购物,原生 JS MVC 设计