zoukankan      html  css  js  c++  java
  • webpack增量打包多页应用

    参考:https://blog.csdn.net/m0_37803704/article/details/79140039

    一,webpack打包存在的问题

    webpack的打包顺序:

    1.  
      var path = require('path');
    2.  
      module.exports = {
    3.  
      entry: {
    4.  
      one: "./src/one.js",
    5.  
      two: "./src/two.js"
    6.  
      },
    7.  
      output: {
    8.  
      path: path.resolve(__dirname, 'dist'),
    9.  
      filename: "[name].js"
    10.  
      }
    11.  
      };

    1,找到入口文件

    2,根据入口文件,找出具有依赖关系的文件js/css

    3,最后,把css/js全部打包成一个js包

    好的,打包完成,打包了整个世界,那么问题来了:


    产品说:按钮颜色不对,给我改成#ccc

    技术:好的,这就改。

    然后就有了如下流程:

    1,找到了entry -> js -> componet -> button.less修改了一个色值

    2,执行webpack打包

    这时就暴露了问题:

    1,明明只是修改了一个色值,却要从入口开始重新打包

    2,业务代码明明没有变化,却也被牵连了

    3,最后生成的js要全部推到线上,覆盖掉线上原本没问题的业务js,纯粹是增加风险

     

    二,思考解决方案

    首先想到的是,既然只修改一个文件,那能不能重新打包一个文件呢?

    1,单独打包更新的文件?

    这种方案,很快就被自我否定了。

    因为:

    1,从入口打包的文件,已经通过依赖关系,把老版本button.less打入了最终输出的js文件

    2,单独打包button.less输出了一个独立的button.js,这个文件需要手动引入到html中,一旦这类文件制造的多了,根本无法维护

    经过反复思考,单独打包每一个文件的想法,不符合webpack的设计初衷,从入口打包的流程是不能够产生变化的

    在这个问题上卡了真的很久.....很久

    2,能否通过依赖关系,打包某一个入口?

    由于我面临的场景是多页应用,所以存在多个入口,那么既然如此,那么能否通过依赖关系,找到需要更新的入口呢?

    这种方案,也思索了很久,后来也被否定了。

    因为:

    1,webpack没有适合输出模块依赖关系的插件,遍寻无果啊

    2,通过webpack的stats分析指标,能够输出依赖关系,但数据量太大,如果不加过滤,目前项目输出12W行json信息,还需要花力气处理一遍这个信息才能拿到关系

    3,如果一个组件被多个入口引用,那么需要找到每一个引用的入口点,再重新打包每个被波及的入口

    上面尤其是第三点,完全不符合我们想增量发布的目的,如果改了一个button组件,要重新打包二三十个入口,这完全没有增量发布的意义

    在这个问题上又纠结了很久......很久


    三,合理的解决方案

    经过前面两个问题后,我发现思考的方向完全是错误的,总是妄想改变webpack的打包方式,简直就是跟它的理念对着干。

    既然不能改变webpack的打包方式,那么我能否改变webpack的输出结果呢?

    其实webpack关于缓存方面的功能,提供了很多功能强大的插件,例如:

    • CommonsChunkPlugin可以用来在打包的时候提取公共js代码

    • ExtractTextPlugin可以用来从js中提出css,将其输出到一个独立的文件

    利用这两个插件,我们能够将我们打包的精度加以划分,将公共引用的部分打包为一个单独的文件

    如果公共引用的部分变为了一个单独的文件,再添加上hash进行缓存,当再次修改的时候只要更新hash,这样我们不就能够确定,究竟改动了哪个文件了吗

    既然如此,我们一步一步进行探索:

    1,首先使用CommonsChunkPlugin,提取公共js

    现在我们创建测试入口文件:

    src/one.js:

    1.  
      import jquery from 'jquery';
    2.  
      console.log('one');

    src/two.js:

    1.  
      import jquery from 'jquery';
    2.  
      console.log('two');

    webpack.config.js

    1.  
      var path = require('path');
    2.  
      module.exports = {
    3.  
      entry: {
    4.  
      one: "./src/one.js",
    5.  
      two: "./src/two.js"
    6.  
      },
    7.  
      output: {
    8.  
      path: path.resolve(__dirname, 'dist'),
    9.  
      filename: "[name].js"
    10.  
      }
    11.  
      };

    执行webpack

    输出了2个文件,大小都是271kb,这是因为one.js和two.js都引用了jquery,jquery打包了2次,分别打包到了两个文件中

    这样显然不是很友好,像jquery这种文件,显然平时不会改动,还是缓存起来比较好,修改webpack.config.js

    1.  
      var webpack = require("webpack");
    2.  
      var path = require('path');
    3.  
      module.exports = {
    4.  
      entry: {
    5.  
      one: "./src/one.js",
    6.  
      two: "./src/two.js"
    7.  
      },
    8.  
      output: {
    9.  
      path: path.resolve(__dirname, 'dist'),
    10.  
      filename: "[name].js"
    11.  
      },
    12.  
      plugins:[
    13.  
      new webpack.optimize.CommonsChunkPlugin({
    14.  
      name: "common",
    15.  
      }),
    16.  
      ]
    17.  
      };

    现在我们添加了CommonsChunkPlugin插件,它的作用是提取公共js,再次执行webpack

    可以看到one.js和two.js的大小已经不到1k了,而common则274k,可以看到jquery已经被打包到了common.js当中

    2,为文件添加hash

    1.  
      var webpack = require("webpack");
    2.  
      var path = require('path');
    3.  
      module.exports = {
    4.  
      entry: {
    5.  
      one: "./src/one.js",
    6.  
      two: "./src/two.js"
    7.  
      },
    8.  
      output: {
    9.  
      path: path.resolve(__dirname, 'dist'),
    10.  
      filename: "[name].[hash:6].js"
    11.  
      },
    12.  
      plugins:[
    13.  
      new webpack.optimize.CommonsChunkPlugin({
    14.  
      name: "common",
    15.  
      }),
    16.  
      ]
    17.  
      };

    上面修改了output的输出内容[name].[hash].js

    现在执行webpack:

    可以看到打包的三个文件都有了hash,但需要主意,此时每个文件的hash都是一样的

    再次执行一遍webpack:

    可以看到,两次构建输出的结果一致,这很好,因为没有修改文件,自然不希望hash发生改变

    那么接下来,修改一下文件:one.js

    1.  
      import jquery from 'jquery';
    2.  
      console.log('修改one');

    悲剧了,所有文件全部修改了hash,查看输出的结果:

    可以发现只修改一个文件,却修改了全部文件的hash,这个问题很严重,显然不是我们想要的

    3,使用chunkhash替代hash

    webpack中关于缓存,提供了好几种添加hash的方法,其中就有chunkhash

    chunkhash简单来说,就是根据模块内容来添加hash,既然这样的话,只要文件没有改变,就不会生成新的hash

    1.  
      var webpack = require("webpack");
    2.  
      var path = require('path');
    3.  
      module.exports = {
    4.  
      entry: {
    5.  
      one: "./src/one.js",
    6.  
      two: "./src/two.js"
    7.  
      },
    8.  
      output: {
    9.  
      path: path.resolve(__dirname, 'dist'),
    10.  
      filename: "[name].[chunkhash:8].js"
    11.  
      },
    12.  
      plugins:[
    13.  
      new webpack.optimize.CommonsChunkPlugin({
    14.  
      name: "common",
    15.  
      }),
    16.  
      ]
    17.  
      };

    如上图,修改filename:[name].[chunkhash:8]/js

    执行webpack

    可以看到这一次生成的hash是4897....

    但是输出的每个文件的hash却不是4897....

    很好,接下来再执行一次webpack:

    可以看到两次输出之间hash并没有发生变化

    现在,修改one.js,再执行webapck

    1.  
      import jquery from 'jquery';
    2.  
      console.log('使用chunkhash后修改one');

    可以看到two.js的hash没有改变one.js的hash改变了,但common.js的hash竟然也改了...

    4,提取manifest

    前面用CommonsChunkPlugin提取代码后,公共的代码已经被抽离,但是他们之间肯定存在一个映射关系,例如

    之所以commonjs的hash会变,是因为修改one.js生成了新的hash,而jquery又与one.js存在映射关系,映射关系会更新,也就是说common.js它要从新的one.js中提取了jquery

    manifest就可以简单理解为模块映射关系的集合,而这个manifest将随着这些被分离出来的代码共同打包!!!

    所以现在分离manifest

    1.  
      var webpack = require("webpack");
    2.  
      var path = require('path');
    3.  
      module.exports = {
    4.  
      entry: {
    5.  
      one: "./src/one.js",
    6.  
      two: "./src/two.js"
    7.  
      },
    8.  
      output: {
    9.  
      path: path.resolve(__dirname, 'dist'),
    10.  
      filename: "[name].[chunkhash:8].js"
    11.  
      },
    12.  
      plugins:[
    13.  
      new webpack.optimize.CommonsChunkPlugin({
    14.  
      name: "common",
    15.  
      }),
    16.  
      new webpack.optimize.CommonsChunkPlugin({
    17.  
      name: 'manifest' // 用于提取manifest
    18.  
      })
    19.  
      ]
    20.  
      };

    这里主要是利用CommonsChunkPlugin的一个功能,通过默认的名字,来提取公共代码,因为webpack打包的是有有一个默认模块就是manifest,所以我们可以通过这个来实现

    现在我们执行webpack:

    可以看到,多输出了一个manifest.js

    接下来,再修改one.js

    1.  
      import jquery from 'jquery';
    2.  
      console.log('分离manifest后修改one');

    可以看到,现在只有one.js和manifest.js的hash发生了改变,common.js被成功缓存了

    使用代码对比工具,比较两次manifest之间的区别,可以看到确实是映射的chunkid发生了改变

    5,使用webpack-md5-hash插件

    前面我们输出了一个manifest.js,但这样还需要单独处理这个manifest.js,所以可以使用webpack的另一个插件webpack-md5-hash

    1.  
      var webpack = require("webpack");
    2.  
      var WebpackMd5Hash = require('webpack-md5-hash');
    3.  
      var path = require('path');
    4.  
      module.exports = {
    5.  
      entry: {
    6.  
      one: "./src/one.js",
    7.  
      two: "./src/two.js"
    8.  
      },
    9.  
      output: {
    10.  
      path: path.resolve(__dirname, 'dist'),
    11.  
      filename: "[name].[chunkhash:8].js"
    12.  
      },
    13.  
      plugins:[
    14.  
      new WebpackMd5Hash(),
    15.  
      new webpack.optimize.CommonsChunkPlugin({
    16.  
      name: "common",
    17.  
      }),
    18.  
      ]
    19.  
      };
    20.  
       

    执行一次打包:

    没有manifest输出,修改one.js

    1.  
      import jquery from 'jquery';
    2.  
      console.log('使用WebpackMd5Hash修改one');

    再次打包:

    这一次仅有one.js的hash发生了改变

    虽然webpack-md5-hash解决了我们的问题,但这也让打包的模块关系变成了黑盒,存在一定的未知风险,还需要仔细实践评估是否有问题

    6,打包修改频率超级低的库

    前面已经抽离出来了公共代码,但是还存在问题,假如这时候又需要引入lodash,那common的hash是否会改变?

    修改one.js

    1.  
      import jquery from 'jquery';
    2.  
      import lodash from 'lodash';
    3.  
      console.log('引入lodash修改one');

    修改two.js

    1.  
      import jquery from 'jquery';
    2.  
      import lodash from 'lodash';
    3.  
      console.log('引入lodash修改two');

    这一次,所有文件的hash都发生了改变,不仅如此,而且更显著的是common的体积增大了

    这就意味者lodash也被打进了common当中,但这本身是一个错误的行为,lodash和jquery,平时根本不会对其进行修改,既然如此,那还需要优化,把他们单独打包出去

    现在修改webapack.config.js

    1.  
      var webpack = require("webpack");
    2.  
      var WebpackMd5Hash = require('webpack-md5-hash');
    3.  
      var path = require('path');
    4.  
      module.exports = {
    5.  
      entry: {
    6.  
      two: "./src/two.js",
    7.  
      one: "./src/one.js",
    8.  
      common:['jquery','lodash']
    9.  
      },
    10.  
      output: {
    11.  
      path: path.resolve(__dirname, 'dist'),
    12.  
      filename: "[name].[chunkhash:8].js"
    13.  
      },
    14.  
      plugins:[
    15.  
      new WebpackMd5Hash(),
    16.  
      new webpack.optimize.CommonsChunkPlugin({
    17.  
      name: "common",
    18.  
      }),
    19.  
      ]
    20.  
      };

    这一次在入口处添加了一个common,common单独指向了jquery和lodash,这一次我们执行打包

    此时,输出的内容没有明显变化,同样是3个文件,大小也完全一致,hash也没有问题

    可以看到,common的大小是817k

    如果这时,再应用了其他的包呢?例如引入react

    修改one.js

    1.  
      import jquery from 'jquery';
    2.  
      import lodash from 'lodash';
    3.  
      import react from 'react';
    4.  
      console.log('引入react修改one');

    修改two.js

    1.  
      import jquery from 'jquery';
    2.  
      import lodash from 'lodash';
    3.  
      import react from 'react';
    4.  
      console.log('引入react修改one');

    执行webpack

    问题来了,common的大小增加了,很显然react被打包进去了,但如果我们此时,只想永久缓存jquery和lodash呢,这该怎么办?

    修改webpack.config.js

    1.  
      var webpack = require("webpack");
    2.  
      var WebpackMd5Hash = require('webpack-md5-hash');
    3.  
      var path = require('path');
    4.  
      module.exports = {
    5.  
      entry: {
    6.  
      two: "./src/two.js",
    7.  
      one: "./src/one.js",
    8.  
      common:['jquery','lodash']
    9.  
      },
    10.  
      output: {
    11.  
      path: path.resolve(__dirname, 'dist'),
    12.  
      filename: "[name].[chunkhash:8].js"
    13.  
      },
    14.  
      plugins:[
    15.  
      new WebpackMd5Hash(),
    16.  
      new webpack.optimize.CommonsChunkPlugin({
    17.  
      name: 'common',
    18.  
      minChunks:Infinity
    19.  
      })
    20.  
      ]
    21.  
      };

    这一次,添加了一句话minChunks:Infinity

    minChunks属性的可以设置为2,意思是引用次数为2的模块就抽离出来,而Infinity则表示无限,无限就意味着不会有多余的被打包进来

    现在执行webpack打包

    可以看到现在common又恢复了816k,当然react也没有抽出来,还在两个文件当中,接下来继续抽离react

    1.  
      var webpack = require("webpack");
    2.  
      var WebpackMd5Hash = require('webpack-md5-hash');
    3.  
      var path = require('path');
    4.  
      module.exports = {
    5.  
      entry: {
    6.  
      two: "./src/two.js",
    7.  
      one: "./src/one.js",
    8.  
      common:['jquery','lodash'],
    9.  
      react:['react','react-redux']
    10.  
      },
    11.  
      output: {
    12.  
      path: path.resolve(__dirname, 'dist'),
    13.  
      filename: "[name].[chunkhash:8].js"
    14.  
      },
    15.  
      plugins:[
    16.  
      new webpack.optimize.CommonsChunkPlugin({
    17.  
      name: ['react','common'], // 用于提取manifest
    18.  
      minChunks:Infinity
    19.  
      }),
    20.  
      new WebpackMd5Hash(),
    21.  
      ]
    22.  
      };

    通过上面的构建,我们已经将不会改动的类库,单独打包并维持住了hash。

    7,引入HashedModuleIdsPlugin固定模块id

    前面看似完美,但如果我们现在改变一下入口的顺序

    1.  
      entry: {
    2.  
      react:['react','react-redux'],
    3.  
      two: "./src/two.js",
    4.  
      one: "./src/one.js",
    5.  
      common:['jquery','lodash'],
    6.  
      }

    可以看到common和react公共库的hash又变了,这是因为,模块id是根据webpack的解析顺序增量的,如果变换解析顺序,那模块id也会随之改变。

    所以就需要HashedModuleIdsPlugin了,它是根据模块相对路径生成模块标识,如果模块没有改变,那模块标识也不会改变

    1.  
      var webpack = require("webpack");
    2.  
      var WebpackMd5Hash = require('webpack-md5-hash');
    3.  
      var path = require('path');
    4.  
      module.exports = {
    5.  
      entry: {
    6.  
      common:['jquery','lodash'],
    7.  
      react:['react','react-redux'],
    8.  
      two: "./src/two.js",
    9.  
      one: "./src/one.js",
    10.  
      },
    11.  
      output: {
    12.  
      path: path.resolve(__dirname, 'dist'),
    13.  
      filename: "[name].[chunkhash:8].js"
    14.  
      },
    15.  
      plugins:[
    16.  
      new webpack.optimize.CommonsChunkPlugin({
    17.  
      name: ['react','common'], // 用于提取manifest
    18.  
      minChunks:Infinity
    19.  
      }),
    20.  
      new webpack.HashedModuleIdsPlugin(),
    21.  
      new WebpackMd5Hash(),
    22.  
      ]
    23.  
      };

    现在打包后,模块的标识不再是id了,而是一个四位的编码了,这样就可以固定住ip地址了。

    8,使用extract-text-webpack-plugin提取css文件

    在src下创建one.css:

    1.  
      body{
    2.  
      color:blue;
    3.  
      }

    two.css

    1.  
      h1{
    2.  
      font-size:24px;
    3.  
      }

    修改one.js和two.js引入css

    1.  
      import jquery from 'jquery';
    2.  
      import lodash from 'lodash';
    3.  
      import react from 'react';
    4.  
      import './one.css'
    5.  
      console.log('引入css修改one');

    修改webpack.config.js

    1.  
      var webpack = require("webpack");
    2.  
      var WebpackMd5Hash = require('webpack-md5-hash');
    3.  
      var path = require('path');
    4.  
      var ExtractTextPlugin = require("extract-text-webpack-plugin");
    5.  
      module.exports = {
    6.  
      entry: {
    7.  
      common: ['jquery', 'lodash'],
    8.  
      react: ['react', 'react-redux'],
    9.  
      two: "./src/two.js",
    10.  
      one: "./src/one.js",
    11.  
      },
    12.  
      output: {
    13.  
      path: path.resolve(__dirname, 'dist'),
    14.  
      filename: "[name].[chunkhash:8].js"
    15.  
      },
    16.  
      module: {
    17.  
      rules: [
    18.  
      {
    19.  
      test: /.css$/,
    20.  
      use: ExtractTextPlugin.extract({
    21.  
      fallback: "style-loader",
    22.  
      use: "css-loader"
    23.  
      })
    24.  
      }
    25.  
      ]
    26.  
      },
    27.  
      plugins: [
    28.  
      new webpack.optimize.CommonsChunkPlugin({
    29.  
      name: ['react', 'common'], // 用于提取manifest
    30.  
      minChunks: Infinity
    31.  
      }),
    32.  
      new ExtractTextPlugin("[name].[chunkhash:8].css"),
    33.  
      new webpack.HashedModuleIdsPlugin(),
    34.  
      new WebpackMd5Hash()
    35.  
      ]
    36.  
      };

    执行webpack:

    可以看到,成功输出了js和css,但是有点疑问的是,one.css和one.js的hash是一样的,这样的话,如果我们改变one.css呢?

    修改one.css,再次打包:

    发现css的hash没有任何变化。

    接着再修改one.js,再次打包:

    这一次one.js和one.css的hash同时改变了。

    9,使用contenthash提取固定css的hash

    • When using the ExtractTextWebpackPlugin, use [contenthash] to obtain a hash of the extracted file (neither [hash] nor [chunkhash] work).

    webpack output文档种有写,当提取css后,用contenthash添加hash

    1.  
      var webpack = require("webpack");
    2.  
      var WebpackMd5Hash = require('webpack-md5-hash');
    3.  
      var path = require('path');
    4.  
      var ExtractTextPlugin = require("extract-text-webpack-plugin");
    5.  
      module.exports = {
    6.  
      entry: {
    7.  
      common: ['jquery', 'lodash'],
    8.  
      react: ['react', 'react-redux'],
    9.  
      two: "./src/two.js",
    10.  
      one: "./src/one.js",
    11.  
      },
    12.  
      output: {
    13.  
      path: path.resolve(__dirname, 'dist'),
    14.  
      filename: "[name].[chunkhash:8].js"
    15.  
      },
    16.  
      module: {
    17.  
      rules: [
    18.  
      {
    19.  
      test: /.css$/,
    20.  
      use: ExtractTextPlugin.extract({
    21.  
      fallback: "style-loader",
    22.  
      use: "css-loader"
    23.  
      })
    24.  
      }
    25.  
      ]
    26.  
      },
    27.  
      plugins: [
    28.  
      new webpack.optimize.CommonsChunkPlugin({
    29.  
      name: ['react', 'common'], // 用于提取manifest
    30.  
      minChunks: Infinity
    31.  
      }),
    32.  
      new ExtractTextPlugin("[name].[contenthash:8].css"),
    33.  
      new webpack.HashedModuleIdsPlugin(),
    34.  
      new WebpackMd5Hash()
    35.  
      ]
    36.  
      };

    这一次,只是修改了输出的hash,conenthash代表的是文本文件内容的hash值,也就是只有style文件的hash值。

    执行webpack:

    one.js和one.css的hash变的不一样了

    接下来,修改one.css

    1.  
      body{
    2.  
      color:white;
    3.  
      }

    再次执行webpack:

    至此,只有one.css发生了变化,准备工作基本就到这里了

    四,优化多页打包时间,稳定hash

    1,约束入口

    因为是多页应用,是通过扫入口文件来进行的打包,规则为js文件为入口文件,jsx为引用的资源不被识别为入口

    通过BundleAnalyzerPlugin插件分析,发现有部分组件被打包为了入口,梳理一遍后,重新打包,打包时间减少了2/3,当然这是在填以前的坑

    生产打包时间是74578ms

    此时压缩和不压缩的打包时间也是3倍的关系:

    开发打包时间是24780ms

    好的,围绕这两个时间,我们开始优化

    2,使用UglifyjsWebpackPlugin开启多线程打包

    首先要做的其实是稳定hash,但因为生产环境的打包速度太慢,所以我们先优化打包速度,webpack默认提供的打包是单线程的

    1.  
      const UglifyJSPlugin = require('uglifyjs-webpack-plugin')
    2.  
       
    3.  
      module.exports = {
    4.  
      plugins: [
    5.  
      new UglifyJSPlugin({
    6.  
      parallel: true
    7.  
      })
    8.  
      ]
    9.  
      }

    这个插件是webpack3提供的,至于低版本webapck的话,需要谨慎处理,不过效果很明显

    现在生产打包时间是51690ms,比之前提速了1/3

    3,使用HappyPack多线程加速loader

    1.  
      var HappyPack = require('happypack');
    2.  
      var os = require('os');
    3.  
      var happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
    4.  
       
    5.  
      ...
    6.  
      module: {
    7.  
      rules: [ {
    8.  
      test: /.js[x]?$/,
    9.  
      exclude: /(node_modules|bower_components)/,
    10.  
      loader: 'happypack/loader?id=happybabel',
    11.  
      include: path.join(__dirname, 'static/assets/js')
    12.  
      }
    13.  
      }
    14.  
      plugins: [
    15.  
      new HappyPack({
    16.  
      id: 'happybabel',
    17.  
      loaders: ['babel-loader?cacheDirectory=true'],
    18.  
      threadPool: happyThreadPool,
    19.  
      cache: true,
    20.  
      verbose: true
    21.  
      }),

    上面module的rules属性中loader原本事babel-loader,现在将它变成了一个任务,其中有一个id,id对应的就是plugins中的happyPack实例

    此时,我们开启了babel-loader的多线程模式

    现在生产打包时间是43855ms,比之前又提速了1/9,这只是babel-loader,我们还可以为其它的loader开启

    接着处理less,css,style等loader,这些结合可以一口气搞定

    1.  
      module: {
    2.  
      rules: [{
    3.  
      test: require.resolve('zepto'),
    4.  
      loader: 'exports-loader?window.Zepto!script-loader'
    5.  
      }, {
    6.  
      test: /.js[x]?$/,
    7.  
      exclude: /(node_modules|bower_components)/,
    8.  
      loader: 'happypack/loader?id=happybabel',
    9.  
      include: path.join(__dirname, 'static/assets/js')
    10.  
      }, {
    11.  
      test: /.less$/,
    12.  
      use: extractTextPlugin.extract({
    13.  
      fallback: "style-loader",
    14.  
      // use: ["css-loader" + (ENV ? '?minimize' : ''), "less-loader", "postcss-loader"]
    15.  
      use: ["happypack/loader?id=postcss"]
    16.  
      })
    17.  
      }]
    18.  
      }
    19.  
      plugins: [
    20.  
      new HappyPack({
    21.  
      id: 'happybabel',
    22.  
      loaders: ['babel-loader?cacheDirectory=true'],
    23.  
      threadPool: happyThreadPool,
    24.  
      // cache: true,
    25.  
      verbose: true
    26.  
      }),
    27.  
      new HappyPack({
    28.  
      id: 'postcss',
    29.  
      loaders: ["css-loader" + (ENV ? '?minimize' : ''), "less-loader",'postcss-loader'],
    30.  
      threadPool: happyThreadPool,
    31.  
      // cache: true,
    32.  
      verbose: true
    33.  
      }),

    这样,我们即处理了babel,同时也搞定了css,less,postcss这些loader

    上图happy[任务名],可以看到打包行为全都开启了多线程,效果显著

    现在生产打包时间是35130ms,此时已经比第一此非优化的时候,提升了一倍的速度

    4,使用dll拆分代码

    经过前面的过程,想必已经意识到了纯静态得库和组件都需要与打包环节分离开,这就需要dll技术了

    dll技术,其实就是将修改频率低或基本不修改且引用次数多的内容,单独打包

    因为设计dll后,config文件的数量剧增,所以需要重新整理目录结构

    例如上图,将每一个webpack拆分出去,把所有配置文件分离开,例webpack.dev.js:

    1.  
      var base = require('./webpack.base.js');
    2.  
      var config = {
    3.  
      entry: require('./dev/entry.js'),
    4.  
      output: require('./dev/output.js'),
    5.  
      plugins: require('./dev/plugins.js'),
    6.  
      devtool: 'eval-source-map'
    7.  
      }
    8.  
      //把配置文件暴露出去;
    9.  
      module.exports = Object.assign(base,config);
    10.  
       

    ok,基础拆分webpack完成后,我们创建一个webpack.dll.libs.js用于打包类库

    1.  
      module.exports = {
    2.  
      libs: [
    3.  
      'react',
    4.  
      'react-dom',
    5.  
      'react-motion',
    6.  
      'react-redux',
    7.  
      'redux',
    8.  
      'axios',
    9.  
      'prop-types',
    10.  
      'classnames',
    11.  
      ]
    12.  
      }

    修改plugins插件:

    1.  
      var webpack = require('webpack');
    2.  
      var dirVars = require('../common/dir.js');
    3.  
      var path = require('path');
    4.  
      var UglifyJsPlugin = require('uglifyjs-webpack-plugin');//多线程打包
    5.  
      var getDefaultPlugins = require('../common/plugins.js').getDefaultPlugins;
    6.  
      var AssetsPlugin = require('assets-webpack-plugin');//输出映射表
    7.  
       
    8.  
      var plugins =[
    9.  
      new webpack.DllPlugin({
    10.  
      path: dirVars.dllLibsManiFest,
    11.  
      }),
    12.  
      new UglifyJsPlugin({
    13.  
      parallel: true,
    14.  
      cache: true
    15.  
      }),
    16.  
      new AssetsPlugin({
    17.  
      filename: 'static/dll/libs-rev-manifest.json'
    18.  
      }),
    19.  
      ]
    20.  
      module.exports = plugins.concat(getDefaultPlugins())

    现在执行webpack

    可以看到,只需要1s,就打包了所有的类库,接下来,修改webpack.prod.js

    在plugins中添加:

    1.  
      new webpack.DllReferencePlugin({
    2.  
      manifest: 'static/dll/libs-rev-manifest.json'
    3.  
      }),

    此时当我们执行webpack.prod.js进行打包,当扫描到libs中的打包的内容时,就不会重复打包

    4,开始继续约束hash

    前面已经彻底搞定了打包,但破坏性很大,所以需要系统的验证hash是否存在问题

    case1:js改变

    修改一个业务代码的js,添加一句注释,再次打包

    可以看到文件hash发生了改变,但很不幸,vendor也发生了改变

    解决方案:添加webpack-md5-hash插件,使用之后,再次验证,发现vendorjs的hash不再发生变化

    case2:less改变

    只有一个css的hash发生了变化,没问题

    case3:修改一个入口下自己封装出去的公共方法

    上面修改了一个入口内公共使用的tools插件,最终是入口的hash发生了改变,没问题

    case4:修改公共方法组件js

    主要是多个入口都会引用的组件

    测试,只有单独打包出去的components的hash修改了

    case5:修改公共方法组件less

    只有一个hash发生了改变

    case6:添加一个公共组件

    只有components的hash发生了改变

    未优化前打包时间180-200s

    优化:

    1.  
      1,约束入口,严格明确入口文件筛选条件后
    2.  
      生产打包:74578ms
    3.  
      开发打包:24780ms
    4.  
      2,开启多线程压缩后
    5.  
      生产打包:51690ms
    6.  
      3,开启多线程编译
    7.  
      生产打包:35130ms
    8.  
      开发打包:15031ms
    9.  
      4,拆包
    10.  
      分解了打包过程,类库4s,组件4s,业务20s,总体30s左右

    最终,流程变得可控,打包实现了定制化,hash得到了保持。

    如何学习大数据?学习没有资料?

    想学习大数据开发技术,Hadoop,spark,云计算,数据分析等技术,在这里向大家推荐一个学习资料分享群:894951460,里面有大牛已经整理好的相关学习资料,希望对你们有所帮助。

     
  • 相关阅读:
    『转』QueryPerformanceFrequency()
    『转』C++中虚析构函数的作用
    存储过程的优缺点
    一个工作7年的软件工程师的总结(收藏)
    存储过程分页算法(收藏)
    Ajax原理(收藏)
    七大秘籍成就职场王者(收藏)
    视图的优缺点
    SQL索引全攻略
    .aspx、MasterPage、.ascx加载顺序
  • 原文地址:https://www.cnblogs.com/kuangke/p/14702448.html
Copyright © 2011-2022 走看看