webpack配置进阶篇

在开发vue时,发现即使使用了路由懒加载,webpack打包出来的静态文件依然还是有点大,因此,在开发途中抽空出来对webpack打包进一步探索(虽然包都是共用的,但本篇内容是基于vue-lic脚手架的框架,请自行衍生)

推荐一个非常好用的可视化分析插件 webpack-bundle-analyzer

它可以将打包后的内容束展示为方便交互的直观树状图,这样你就可以非常直观的看到构建的包中引入了那些内容,以方便你进行优化。

  • 配置

// 常规配置

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

module.exports = {

  plugins: [

    new BundleAnalyzerPlugin()

  ]

}

// vue中配置启动或不启动

if (config.build.bundleAnalyzerReport) {

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

  webpackConfig.plugins.push(new BundleAnalyzerPlugin())

}

// package.json

"analyz": "set NODE_ENV=production && set npm_config_report=true && npm run build"  // window系统

或者

"analyz": "NODE_ENV=production && npm_config_report=true && npm run build"

  • 效果图(网上的动态图)
111.png
为 VUE 项目添加 全局变量

-通过DefinePlugin插件配置


new webpack.DefinePlugin({

      'process.env': env,

       LOCAL_SERVICE: JSON.stringify('')

    })

// 即可在vue中直接引用设置的参数 LOCAL_SERVICE

为 VUE 对象添加 对象变量

-在入口文件main.js中


import Vue from 'vue'

Vue.prototype.LOCAL_SERVICE = LOCAL_SERVICE ;

// 即可在vue对象中直接引用设置的参数 this.LOCAL_SERVICE

在VUE项目中引入第三方插件或js
  • 方法一:直接在主入口html文件引入(利用外部扩展externals)

<script src="./static/jquery-1.12.4.js"></script>

// 在webpack中配置externals,防止将某些 import 的包(package)打包到 bundle 中,而是在运行时(runtime)再去从外部获取这些扩展依赖(external dependencies)

externals: {

  'jquery': 'jQuery'

}

// 引入

import $ from 'jquery';

  • 方法二: 采用CDN方式,直接在主文件html中引入个全局变量/方法

  • 方法三:利用插件webpack-require-http (不支持es6以上语法)(该方法回返回一个promise)


## 值得注意的是,该方法使用了promise,不会阻碍到后面方法的执行

require(['https://cdn.bootcss.com/jquery/3.1.1/jquery.min.js').then(( )=> {

        console.log($, 9338)

    })

Promise.all([

      require('https://cdn.bootcss.com/jquery/3.1.1/jquery.min.js'),

      require('https://cdn.bootcss.com/jquery/3.1.1/jquery.min.js')

    ]).then(() => {

        console.log( $, 999998)

    })

  • 方法四:会在后面的打包体积方法中体现
为 VUE 项目添加 PWA 解决发布后刷新报错问题

不管是部署在 IIS,还是 nginx,每次应用部署后,再次访问因为旧的 js 已经不存在,所以页面访问的时候会整个报错,报错的结果就是一个白屏,为了解决这个问题,使用 PWA ,这样就可以将 js 缓存到本地,再次发布后,service-worker.js 会使旧的 js 失效,重新请求并缓存 js。

1.无需客户端,少量流量即可安装 2.可添加到主屏并全屏运行 3.离线功能,响应更快,及时更新 4.PUSH能力 5.数据传输必须是https

这个暂时只在应用上看过具体代码,无实操过。推荐文章 https://www.cnblogs.com/morang/p/9622394.html

移动web缓存(可忽略,查看资料顺带的)

推荐文章: http://caibaojian.com/mobile-cache.html

把依赖库拆分出来,解决使用代码分割和异步加载打包后首次加载的文件过大问题
  • 方法一:CommonsChunkPlugin

推荐两篇描述CommonsChunkPlugin比较好的文章 https://segmentfault.com/a/1190000012828879https://segmentfault.com/a/1190000014221669

  • 首先说下chunk文件的种类,主要有以下三种:

webpack当中配置的入口文件(entry)是chunk,可以理解为entry chunk

入口文件以及它的依赖文件通过code split(代码分割)出来的也是chunk,可以理解为children chunk

通过CommonsChunkPlugin创建出来的文件也是chunk,可以理解为commons chunk

  • CommonsChunkPlugin可配置属性

name:可以是已经存在的chunk(一般指入口文件)对应的name,那么就会把公共模块代码合并到这个chunk上;否则,会创建名字为name的commons chunk进行合并

filename:指定commons chunk的文件名

chunks:指定source chunk,即指定从哪些chunk当中去找公共模块,省略该选项的时候,默认就是entry chunks

minChunks:既可以是数字,也可以是函数,还可以是Infinity

  • 直接抽取第三方库放在一个文件上

plugins: [

        //  查看依赖项是否来自node_modules文件夹,如果是,则将其输出到单独的文件commonChunks.js中

        new webpack.optimize.CommonsChunkPlugin({

            name: 'commonChunks',

            minChunks: function (module,count) {

                console.log(module.resource,`引用次数${count}`);

                //"有正在处理文件" + "这个文件是 .js 后缀" + "这个文件是在 node_modules 中"

                return (

                    module.resource &&

                    /\.js$/.test(module.resource) &&

                    module.resource.indexOf(path.join(__dirname, './node_modules')) === 0

                )

            }

        }),

        // 抽离webpack运行文件

        new webpack.optimize.CommonsChunkPlugin({

            name: 'runtime',

            filename: '[name].js',

            chunks: ['vendor']

        }),

    ]

  • 由于采用了路由懒加载,实现了代码分割和异步加载,因此需要用到children和async属性,将懒加载的路由文件公用的模块代码,抽离打包成一个单独的文件,并且该文件是按需加载的,如果某个路由没有使用到这些公用模块,是不会加载进来的,其中包括js,vue组件,从而减少每个页面生成的js体积,优化切换页面速度

#### minChunks可以设置为数字、函数和Infinity

        数字:模块被多少个chunk公共引用才被抽取出来成为commons chunk

        函数:接受 (module, count) 两个参数,返回一个布尔值,你可以在函数内进行你规定好的逻辑来决定某个模块是否提取成为commons chunk

        Infinity:只有当入口文件(entry chunks) >= 3 才生效,用来在第三方库中分离自定义的公共模块

####  children

        指定为true的时候,就代表source chunks是通过entry chunks(入口文件)进行code split出来的children chunks

        children和chunks不能同时设置,因为它们都是指定source chunks的

        children 可以用来把 entry chunk 创建的 children chunks 的共用模块合并到自身,但这会导致初始加载时间较长

#### async:即解决children:true时合并到entry chunks自身时初始加载时间过长的问题。async设为true时,commons chunk 将不会合并到自身,而是使用一个新的异步的commons chunk。当这个  children chunk 被下载时,自动并行下载该commons chunk

new webpack.optimize.CommonsChunkPlugin({

        name: "app",  // 根入口文件

        minChunks: 3, 

        children: true,

        // deepChildren: true,

        async: true,

    })

  • 方法二:把依赖的库,先整体都不作处理地打包出来(值得注意的是,使用该方法,一定要合理跟CommonsChunkPlugin搭配,不建议把package.json中的依赖统一打包,这样会导致dll文件比较大,反而起不到想要的优化)

由于采用CommonsChunkPlugin,虽然可以拆分出很多小文件,如单独第三方库文件集合,自定义的公共模块集合等,但每次打包都会全部去重新打包一次,这样实际开发中会不友好。而实际上我们不会一直去更新我们引用的依赖库,所以dll的做法就等于是,事先先打包好依赖库,然后只对每次都修改的js做打包。

  • 以vue项目为例,首先创建一个名为 webpack.dll.config.js 的文件

const path = require("path"),

    UglifyJsPlugin = require('uglifyjs-webpack-plugin'),

          AssetsPlugin = require('assets-webpack-plugin'),

     webpack = require("webpack");

var vendors = [

  'vue', 

  'vue-router', 

  'vuex'

];

module.exports = {

  entry: {

    vendor: ['vuex','vuex-persistedstate','vue-router']

                //读取package.json里的依赖,normalize.css除外,打包会报错

                //如果使用了chrome的vue-devtool,那打包的时候把vue也排除掉,因为压缩过的vue是不能使用vue-devtool的

                // 我的dll通用来打第三方插件库和自身封装的不会修改的库,

  },

  output: {

    path: path.join(__dirname, '../static'),
              filename: 'dll.[name]_[hash:6].js',
              library: '[name]_[hash:6]'

  },

        // 如果依赖库需要编译,必须带上module

        module: {

 rules: [
 
             //vue文件解析
 
            {

                 test: /\.vue$/,
 
                loader: 'vue-loader'

             },

             //js文件解析
 
           {

                 test: /\.js$/,
 
                 loader: 'babel-loader',

                include: [resolve('src')],
 
             }
 
         ]
 
 },
 
 // 如果依赖库引用别的依赖文件使用的是别名,则必须配置resolve,而且配置别名,打包出来体积相对小很多
 
 resolve: {
 
     //忽略后缀的文件,路径自动查找,如果在同目录下js和vue同名,则需要加后缀作为区分  
 
         extensions: ['.js', '.vue', '.json'],
 
         //模块解析
 
         modules: [
 
             resolve('src'),
 
            resolve('node_modules')

         ],
 
         //路径别名
 
         alias: {

             'src': resolve('src'),
 
             'router': resolve('src/router/index.js')
 
              // 等等
 
         }
 
 },

  plugins: [

                //   Dllplugin里的path,会输出一个vendor-manifest.json,这是用来做关联id的,打包的时候不会打包进去,所以不用放到static里

      new webpack.DllPlugin({

 path: path.join(__dirname, '../', '[name]-manifest.json'),
 
           name: '[name]_[hash:6]'
 
        }),
 
         // 对依赖文件进行压缩混淆
 
         new webpack.optimize.UglifyJsPlugin({ // js、css都会压缩
 
             mangle: {
 
                 except: ['$super', '$', 'exports', 'require', 'module', '_']
 
            },
 
             compress: {
 
                 warnings: false
 
             }
 
         }),
 
         // 生成bundle-config.json,用于把带hash的dll插入到html中
 
         new AssetsPlugin({
 
           filename: 'bundle-config.json',
 
           path: './'
 
         })

  ]

}; 

  • 生成dll.vendor.js(static文件夹中)、bundle-config.json(根目录下)和vendor.manifest.json(根目录下)文件

// 添加package.json中的运行命令

"build:dll": "webpack -p --progress --config build/webpack.dll.conf.js"

// 运行命令 npm run build:dll 即可生成

  • 修改build/webpack.base.conf.js,添加DllReferencePlugin的配置

const manifest = require('../vendor-manifest.json')

// 关联dll拆分出去的依赖

 new webpack.DllReferencePlugin({

       manifest

  })

  • 引入HTML中

// 修改html生成插件HtmlWebpackPlugin,添加多一个属性

// 加载dll文件

vendorJsName: bundleConfig.vendor.js

// 在html文件的底部添加

<script src="./static/<%= htmlWebpackPlugin.options.vendorJsName %>"></script>

  • 结果,很明显 CommonsChunkPlugin和dll两者结合起来,一个大型项目,也只有一个js入口文件是因为highcharts而导致超过200k的,而页面js更是只有几十K。
微信图片_20190111113418.png
拆分css文件,防止样式文件过大
    var ExtractTextPlugin = require('extract-text-webpack-plugin')
    var CSSSplitWebpackPlugin = require('css-split-webpack-plugin').default

    new ExtractTextPlugin({
      filename: utils.assetsPath('css/[name].[contenthash].css'),
      allChunks: true,
    }),
    // IE9会忽略你生成的CSS包中的大于4000个选择器的部分
    new CSSSplitWebpackPlugin({
      filename: utils.assetsPath('css/[name]-[part].[ext]'),
      size: 1500,// size:单个文件中允许的最大CSS规则数,防止css样式过大,即选择器
    })
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容