从零玩转Webpack4~5+实现原理笔记(二)

一、webpack-JS-Tree-Shaking

1.什么是Tree-Shaking?

过滤掉无用的JS代码和CSS代码, 我们称之为Tree-Shaking
例如: 在a.js中引入了b模块, b模块中有2个方法, 但是我只用到了1个方法
默认情况下会将b模块中所有代码都打包到a.js中,
为了提升网页性能降低打包体积, 我们可以只将用到的方法打包到a.js中

2.webpack中如何开启Tree-Shaking?

Tree-Shaking

2.1开发环境

webpack.config.js配置, 告诉webpack只打包导入模块中用到的内容

optimization: {
   usedExports: true
},

package.json配置, 告诉webpack哪些文件不做Tree-Shaking

"sideEffects": ["*.css", "*.less", "*.scss"],

2.2生产环境

无需进行任何配置, webpack默认已经实现了Tree-Shaking

注意点:

  • 只有ES Modle导入才支持Tree-Shaking
  • 任何导入的文件都会受到 tree shaking 的影响。

这意味着,如果在项目中使用类似 css-loader 并导入 CSS 文件,
则需要将其添加到 side effect 列表中,以免在生产模式中无意中将它删除:

3.CSS模块Tree-Shaking

不光JS模块可以进行Tree-Shaking, CSS模块也可以进行Tree-Shaking

3.0 方法一

官网说明已经被弃用

purifycss-webpack—npm

3.0.1 如何开启CSS模块Tree-Shaking

purifycss-webpack

3.0.2 安装相关插件
npm i -D purifycss-webpack purify-css glob-all
3.0.3 配置插件
const PurifyCSS = require("purifycss-webpack");
const glob = require("glob-all");

new PurifyCSS({
    paths: glob.sync([
        // 要做CSS Tree Shaking的路径文件
        path.resolve(__dirname, "./*.html"),
        path.resolve(__dirname, "./src/js/*.js"),
    ])
}),

在这里插入图片描述

3.1 方法二

purgecss-webpack-plugin

3.1.1 安装相关插件
npm i -D purgecss-webpack-plugin purify-css glob-all
3.1.2 配置插件
const PurgeCSSPlugin = require("purgecss-webpack-plugin");
const glob = require("glob-all");

new PurgeCSSPlugin({
    paths: glob.sync([
        // 要做CSS Tree Shaking的路径文件
        path.resolve(__dirname, "./*.html"),
        path.resolve(__dirname, "./src/js/*.js"),
    ])
}),
const PurgeCSSPlugin = require('purgecss-webpack-plugin')
const glob = require('glob-all')
const PATHS = {
  src: path.join(__dirname, 'src')
}

// In the webpack configuration
new PurgeCSSPlugin({
  paths: glob.sync(`${PATHS.src}/**/*`, { nodir: true })
})

二.代码分割

1.什么是Code-Splitting(代码分割)?

默认情况下webpack会将所有引入的模块都打包到一个文件中,
这样就导致了打包后的文件比较大, 以及修改文件后用户需要重新下载所有打包内容问题

例如: 在a.js中引入了b.js, 那么a.js和b.js都会被打包到bundle.js中
如果a.js有1MB, b.js也有1MB, 那么打包之后的文件就有2MB
那么用户第一次打开网页的时候就需要下载2MB的文件
问题的关键在于, 如果我们修改了a.js, 但没有修改b.js
重新打包后用户需要重新下载新打包的文件(因为用户本地缓存的是a和b的合体)
这样就导致了每次修改了其中一个文件用户都要重新下载所有内容

在这里插入图片描述
解决方案: 将不经常修改的内容打包到另一个文件中, 这样每次修改后用户就只用下载修改后的文件
没有被修改的文件由于用户上一次打开已经缓存在了本地就不用下载了, 这样性能也提升了
Code-Splitting就是将不经常修改的模块打包到单独的文件中, 避免每次修改用户都需要重新下载所有内容

2.如何开启Code-Splitting

2.1手动分割(了解)

  • 在单独文件中引入模块, 将模块中的内容添加到window上
  • 修改配置文件同时打包多个文件
entry: {
    custom: "./src/js/custom.js", // 先打包会被先引入
    main: "./src/js/index.js",
},
output: {
    filename: "js/[name].js", // name是入口entry的key
    path: path.resolve(__dirname, "bundle")
},

2.2自动分割

代码分割

webpack会自动判断是否需要分割, 如果需要会自动帮助我们风格

// 告诉webpack需要对代码进行分割
optimization: {
    splitChunks: {
        chunks: "all"
    }
},
entry: {
    main: "./src/js/index.js",
},
output: {
    filename: "js/[name].js",
    path: path.resolve(__dirname, "bundle")
},

三.异步加载

1.什么是异步加载模块?

  • 同步加载: import $ from ‘jquery’;

例如: 在index.js中导入了10个模块, 那么只要index.js被执行, 就会一次性将10个模块加载进来

  • 异步加载: import(‘jquery’).then(({default: $ }) => {使用模块代码});

例如: 在index.js中导入了10个模块, 那么哪怕index.js被执行, 也要看是否满足加载条件才去加载

lazy-loading

特点:

对于异步加载的模块无需配置, webpack会自动分割

2.场景

点击后才加载(用到的时候再加载)

<body>
<button>我是按钮</button>
</body>
const oBtn = document.querySelector('button');
oBtn.onclick = function() {
    getComponment().then(($div) => {
        document.body.appendChild($div[0]);
    });
};
// 方法一:
/*
function getComponment() {
    return import('jquery').then(({ default: $ }) => {
        const $div = $('<div>我是div</div>');
        return $div;
    });
}
 */
 // 方法二:
async function getComponment() {
    const { default: $ } = await import('jquery');
    const $div = $('<div>我是div</div>');
    return $div;
}

报错,因为异步加载的语法是ESMAscript提案中的语法,并不是正式标准的语法,ESlint就检查不到

在这里插入图片描述
修改ESlint的配置

// .eslintrc.js
"ecmaVersion": 11

四.webpack-Preferching-Preloading

prefetchingpreloading

1.什么是Prefetching and Preloading

通过异步加载(懒加载)的方式确实可以优化我们的代码
但是也存在一定的弊端, 弊端就是用到的时候再加载, 那么用户需要等待加载完成后才能使用

例如: 弹出登录框的时候有一些业务逻辑, 如果这些业务逻辑使用懒加载的话, 那么只有加载完用户才能操作登录框

解决方案:

加载完当前需要使用的所有模块之后, 在空闲的时间提前把异步加载的模块也加载进来
这样既不会影响到第一次的访问速度, 还可以提升异步加载的速度较少用户等待的时间
所以就有了Prefetching和Preloading

1.1 Prefetching

空闲的时候加载
也就是等当前被使用的模块都加载完空闲下来的时候就去加载, 不用等到用户用到时再加载(用到的时候再加载,加载的就是缓存中的)

1.2 Preloading

和其它模块一起加载(不推荐, 了解即可)
也就是和当前被使用的模块一起加载

2.使用方式

prefetchingpreloading

异步加载时写上魔法注释即可

/* webpackPrefetch: true */
/* webpackPreload: true */
async function getComponment() {
    const { default: $ } = await import(/* webpackPrefetch: true */'jquery');
    const $div = $('<div>我是div</div>');
    return $div;
}

3.利用魔法注释修改分割代码的名称

异步加载时在加载模块前面写上魔法注释

import(/* webpackChunkName: "jquery" */"jquery").then();
async function getComponment() {
    const { default: $ } = await import(/* webpackPrefetch: true *//* webpackChunkName: "jquery" */'jquery');
    const $div = $('<div>我是div</div>');
    return $div;
}

五.webpack长缓存优化

1.浏览器缓存问题

浏览器会自动缓存网页上的资源, 以便于提升下次访问的速度
但正式因为浏览器的缓存机制, 导致文件内容被修改之后只要文件名称没有发生变化
就不会重新去加载修改之后的资源, 所以刷新网页后显示的还是修改之前的内容

为了解决这个问题, 我们就需要在打包文件的时候给"文件名称加上内容的hash值"
一旦内容发生了变化, 内容的hash值就会发生变化, 文件的名称也会发生变化
一旦文件的名称发生了变化, 浏览器就会自动去加载新打包的文件

2.hash/chunkhash/contenthash

2.1 hash:

根据每次编译打包的内容生成的哈希值, 每次打包都不一样, 不能很好利用缓存, 不推荐

2.2 chunkhash:

根据不同的入口文件(Entry)进行依赖文件解析、构建对应的chunk,生成对应的哈希值。(根据依赖的文件生成的,依赖的文件发生变化就会发生变化)

在生产环境里把一些公共库和程序入口文件区分开,单独打包构建,
接着我们采用chunkhash的方式生成哈希值,那么只要我们不改动公共库的代码,就可以保证其哈希值不会受影响。

注意点: 只支持css和js, 不支持img等其它资源

过去使用的

2.3 contenthash(推荐):

根据某个文件内容生成的哈希值, 只要某个文件内容发生改变,该文件的contenthash就会发生变化

注意点:webpack4contenthash热更新有冲突, 所以在开发模式想使用contenthash需要关闭热更新,webpack5不冲突

但是一般情况下我们需要通过hash解决的是线上代码的内容更新问题, 所以开发模式无关紧要

new MiniCssExtractPlugin({
      filename: "css/[name].[contenthash:8].css", // 8:hash的位数
    })

3.manifest

webpack在打包时,会把库和业务代码之间的关系manifest
它既存在于业务代码(main.js),也存在于库中(vendor.js),
旧版webpack中(webpack4之前),mainfest在每次打包的时候的时候可能会变化,
所以contenthash值也会跟着变化。配置runtimeChunk后,会把manifest提取到runtime中,这样打包就不会影响到其他js了。

optimization: {
    runtimeChunk: "single",
    splitChunks: {
        chunks: "all",
    },
}

六.webpack-SliptChunksPlugin

1.什么是Split-Chunks-Plugin?

webpack在代码分割的时候底层使用的其实是Split-Chunks-Plugin来实现代码分割的
所以这个插件的作用就是进行代码分割

2.Split-Chunks-Plugin相关配置

Split-Chunks-Plugin

module.exports = {
 optimization: {
    splitChunks: {
    chunks: "async", // 对那些代码进行分割 async(只分割异步加载模块)、all(所有导入模块)
    minSize: 30000, // 表示被分割的代码体积至少有多大才分割(单位是字节)
    minChunks: 1,  //  表示至少被引用多少次数才分割,默认为1
    maxAsyncRequests: 5, // 异步加载并发最大请求数(保持默认即可)
    maxInitialRequests: 3, // 最大的初始请求数(保持默认即可)
    automaticNameDelimiter: '~', // 命名连接符
    name: true, // 拆分出来块的名字使用0/1/2... 还是指定名称
    cacheGroups: { // 缓存组, 将当前文件中导入的所有模块缓存起来统一处理
        vendors: { // 分割从node_modules目录中导入的模块
            test: /[\\/]node_modules[\\/]/,
            priority: -10 // 优先级, 值越小越优先
        },
        default: { // 分割从其它地方导入的模块
            minChunks: 2,
            priority: -20,
            reuseExistingChunk: true // 如果当前代码块包含的模块已经有了,就不在产生一个新的代码块
       	 		}
    		}
		}
	}
}

默认情况下如果所有的模块都是从node_modules中导入的,
那么会将所有从node_modules中导入的模块打包到同一个文件中去

默认情况下如果所有的模块都不是从node_modules中导入的,
那么会将所有不是从node_modules中导入的模块打包到同一个文件中去

如果当前文件中导入的模块有的是从node_modules中导入的, 有的不是从node_modules中导入的
那么就会将所有从node_modules中导入的打包到一个文件中
那么就会将所有不是从node_modules中导入的,中导入的打包到另一个文件中

在这里插入图片描述

七、webpack-Provide-Plugin

1.模块各种引入方式

1.1在HTML中全局引入

<script src="https://code.jquery.com/jquery-3.4.1.js"></script>

特点: 什么地方都可以使用

1.2通过npm安装通过import局部引入

特点: 只能在import导入的模块中使用

2.什么是Provide-Plugin?

Provide-Plugin

自动加载模块,而不必到处 import 或 require
默认情况下模块中的数据都是私有的, 所以想要使用模块必须先导入模块
如果说在a.js中想要使用jQuery, 那么就必须在a.js中导入jQuery模块
如果说在b.js中想要使用jQuery, 那么就必须在b.js中导入jQuery模块

const Webpack = require("webpack");
plugins: [
	new Webpack.ProvidePlugin({
   		$: "jquery"
	})
]

3.ESlint报错

在这里插入图片描述

ESlint报错:当访问当前源文件内未定义的变量时,no-undef 规则将发出警告。如果你想在一个源文件里使用全局变量,推荐你在 ESLint 中定义这些全局变量,这样 ESLint 就不会发出警告了。你可以使用注释或在配置文件中定义全局变量。

ESlint-globals

// .eslinrc.js
globals: {
    $: true
  },

八、webpack-imports-loader

1.什么是imports-loader?

webpack-imports-loader
npm-imports-loader

imports-loaderProvide-Plugin功能一样可以实现全局导入,
但是imports-loader的功能比Provide-Plugin更强大,
imports-loader除了可以实现全局导入以外, 还可以修改全局this指向

默认情况下模块中的this指向一个空对象, 我们可以通过imports-loader实现让this指向window

2.imports-loader注意点:

  • 2.1在企业开发中如何需要实现全局导入, 更推荐使用ProvidePlugin来实现

    因为ProvidePlugin是webpack内置的官方插件更靠谱

  • 2.2使用imports-loader修改this指向, 系统会自动将我们的代码放到一个立即执行函数中,这就导致了在打包时候import不在第一行, 会报错

    import必须写在最前面

在这里插入图片描述

  • 2.3如何解决?

无需修改this指向, 直接在模块中使用window

 {
        test: /\.js$/,
        exclude: /node_modules/, // 告诉webpack不处理哪一个文件夹
        // Webpack4~
        // 以下代码的含义: 但凡在JS中用到了$就去自动加载jQuery
        // loader: 'imports-loader?$=jquery',
        // 以下代码的含义: 修改模块中this的指向
        // loader: 'imports-loader?this=>window'
        // Webpack5
        use: [
          {
            loader: "imports-loader",
            options: {
              imports: {
                moduleName: "jquery",
                name: "$",
              },
              // wrapper: 'window' // 修改this指向
            },
          },
        ],
      },

更推荐ProvidePlugin全局导入,ProvidePlugin是webpack内置的官方插件更靠谱

九、webpack-resolve

1.什么是resolve?

resolve

resolve用于配置导入模块的解析规则

2.resolve常用配置

2.1映射导入路径, 简化导入代码

import 'bootstrap/dist/css/bootstrap.min.css';

但是这种写法每次都要写很长的一串路径, 有没有办法可以简化呢?

简化后的样子

import 'bootstrap';

那就是通过resolve的alias

resolve: {
    // 创建 import 或 require 的别名,来确保模块引入变得更简单
    alias: {
        bootstrap: path.resolve(__dirname, "bootstrap/dist/css/bootstrap.css")
    },
}

2.2修改入口查找顺序, 简化导入代码

resolve: {
    // 指定模块入口的查找顺序
    mainFields: ["style", "main"],
}

2.3 修改查找顺序, 简化导入代码

resolve: {
    // 指定导入模块查找顺序
    extensions: [".css", ".js"]
}

2.4 通过import导入模块的时候会先在node_modules中查找, 找不到再逐级向上查找

这样在打包的时候非常消耗性能, 能不能在打包的时候让webpack只去指定的目录查找

那就是通过resolve的modules

resolve: {
    // 指定查找范围, 告诉webpack只在node_modules中查找
    modules: ["node_modules"],
}

十、webpack-noParse

1.什么是noParse属性?

默认情况下无论我们导入的模块(库)是否依赖于其它模块(库), 都会去分析它的依赖关系
但是对于一些
独立的模块(库)而言, 其根本不存在依赖关系
, 但是webpack还是会去分析它的依赖关系
这样就大大降低了我们打包的速度.
所以对于一些独立的模块(库), 我们可以提前告诉webpack不要去分析它的依赖关系
这样就可以提升我们的打包速度.

那么如何告诉webpack这是一个独立的模块(库)呢?
我们可以通过noParse属性来告诉webpack

2.配置

noParse

module:{
	noParse: /jquery|lodash/
}

十一、webpack-IgnorePlugin

1.什么是IgnorePlugin?

IgnorePluginwebpack的一个内置插件
IgnorePlugin用于忽略第三方包指定目录,让指定目录不被打包进去

2.使用场景

使用moment.js对时间进行格式化,并且使用中文包,但是在默认情况下,所有的文件包都会被打包

解决方法:
webpack-IgnorePlugin

使用webpack-IgnorePlugin将moment.js中所有的语言包忽略,使用哪种语言再另外引入

plugins:[
	new Webpack.IgnorePlugin({
      resourceRegExp: /^\.\/locale$/,
      contextRegExp: /moment$/,
    })
]
import moment from 'moment';
import 'moment/locale/zh-cn.js';

moment.locale('zh-cn');
const time = moment('20111031', 'YYYYMMDD').fromNow();
console.log('time', time);

十二、webpack-externals

1.什么是externals?

externals的作用就是将不会发生变化的第三方模块(库)设置为外部扩展,避免将这些内容打包到我们的项目中, 从而提升打包速度

2.externals使用

externals

2.1手动全局引入第三方模块

 <!--
    window.jQuery;
    window.$;
    -->
    <script src="https://code.jquery.com/jquery-3.4.1.js"></script>
    <!--
    window._;
    -->
    <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.15/lodash.min.js"></script>

2.2在配置文件中告诉webpack这是一个外部扩展库, 不需要打包

module.exports = {
  /*
    告诉webpack哪些第三方模块不需要打包
    * */
  externals: {
    /*
      以下配置的含义:
      告诉webpack我们在通过import导入jquery的时候, 不是导入node_modules中的jquery
      而是导入我们全局引入的jquery
      * */
    jquery: "jQuery",
    lodash: "_",
  },
  }

十三、webpack-dll动态链接库

1.什么是dll动态链接库?

dll动态链接库externals功能其实是一样的,
都是用于防止重复打包不会发生变化的第三方模块,
都是用于提升webpack打包效率的
只不过externals不太符合前端的模块化思想, 所以就有了dll动态链接库

2.如何实现让第三方模块只打包一次

2.1单独配置一个config.js文件打包不会发生变化的第三方库

// webpack.config.dll.js
module.exports = {
    mode: 'production',
    entry: {
        vendors: 'jquery'
    },
    output: {
        filename: '[name].[contenthash:8].js',
        path: path.resolve(__dirname, 'dll'),
        library: '[name]' // 表示打包的是一个库, 表示将打包的内容通过全局变量暴露出去
    }
};

2.2通过插件将打包好的库引入到界面上

npm install --save-d add-asset-html-webpack-plugin
new AddAssetHtmlWebpackPlugin({
    filepath: path.resolve(__dirname, 'dll/vendors.js')
})

注意点: 该插件需要配合HtmlWebpackPlugin使用, 并且需要在HtmlWebpackPlugin后创建

2.3生成动态库的映射关系

因为我们有可能将几个库打包到一个文件中, 所以需要生成一个映射文件方便
webpack能够从中找到对应的库

// webpack.config.dll.js
plugins: [
    new Webpack.DllPlugin({
        name: '[name]',
        path: path.resolve(__dirname, 'dll/[name].manifest.json')
    })
]

注意点: 这里的name必须和library一致

2.4告诉webpack去哪里查找动态库

在打包的时候如何webpack回到指定的映射文件查找对应的动态库,
找打了那么就不会重新打包动态库中的内容了, 如果找不到才会重新打包

// webpack.config.commmon.js
new Webpack.DllReferencePlugin({
    manifest: path.resolve(__dirname, 'dll/vendors.manifest.json')
})

3.优化

1.dll动态链接库使用步骤?

  • 1.1 新建一个配置文件, 专门用于打包不会变化的第三方库
  • 1.2 在打包项目的配置文件中, 通过add-asset-html-webpack-plugin将提前打包好的库插入到html中
  • 1.3 在专门打包第三方的配置文件中添加生成清单配置
  • 1.4 在打包项目的配置文件中, 告诉webpack打包第三方库的时候先从哪个清单文件中查询,如果清单包含当前用到的第三方库就不打包了. 因为已经在html中手动引入了

2.动态链接库的优势

  • 2.1 不用手动插入
  • 2.2 所有第三方库只会被打包一次

3.当前动态链接库存在的问题

1)问题1:所有的库打包在同一个文件不科学,不同的库打包在不同的文件
webpack.config.dll.js

entry: {
        venders: ['jquery','lodash'],
    },

===改为=>

 entry: {
        jquery: ['jquery'],
        lodash: ['lodash'],
    },
// webpack.confog.common.js
	new HtmlWebpackPlugin({
      // 指定打包的模板, 如果不指定会自动生成一个空的
      template: "./src/index.html",
    }),
    // jquery
	new AddAssetHtmlPlugin({ filepath: path.resolve(__dirname,'dll/jquery.dll.js') }),
    new Webpack.DllReferencePlugin({
      manifest:path.resolve(__dirname,'dll/jquery.manifest.json')
    }),
    // lodash
    new AddAssetHtmlPlugin({ filepath: path.resolve(__dirname,'dll/lodash.dll.js') }),
    new Webpack.DllReferencePlugin({
      manifest:path.resolve(__dirname,'dll/lodash.manifest.json')
    }),

如果我们提前打包生成了多个文件和清单, 那么需要手动增加插入的文件和查询的清单

4.如何解决

通过NodeJS代码动态添加

const path = require('path');
const Webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin');
const fs = require('fs');

const plugins = [
    new HtmlWebpackPlugin({
        // 指定打包的模板, 如果不指定会自动生成一个空的
        template: './src/index.html'
    }),
    new CleanWebpackPlugin(),
    new CopyWebpackPlugin([{
        from: './doc',
        to: 'doc'
    }]),
    new MiniCssExtractPlugin({
        filename: 'css/[name].[contenthash:8].css'
    }),
    /*
    以下代码的含义:
    在打包moment这个库的时候, 将整个locale目录都忽略掉
    * */
    new Webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)
];
// 从dll文件中获取到所有提前打包生成了多个文件和清单,根据文件的后缀动态的插入
const dllPath = path.resolve(__dirname, 'dll');
const files = fs.readdirSync(dllPath);
files.forEach(function (file) {
   if(file.endsWith(".js")){
       plugins.push(new AddAssetHtmlPlugin({
           filepath: path.resolve(__dirname, 'dll', file)
       }));
   }else if(file.endsWith(".json")){
       plugins.push(new Webpack.DllReferencePlugin({
           manifest: path.resolve(__dirname, 'dll', file)
       }));
   }
});
module.exports = {
	plugins:plugins
}

十四、webpack-HappyPack

1.什么是HappyPack?

默认情况下webpack打包项目是单线程的, 为了提升打包速度, 充分发挥多核 CPU 电脑的威力
我们可以通过HappyPack让webpack实现多线程打包

2.HappyPack使用

HappyPack

npm install --save-dev happypack

开启多线程可以打包js,css,图片…
?id=js/css…

modules:{
	rules:[
		{
    		test: /\.js$/,
    		exclude: /node_modules/,
    		use: 'happypack/loader?id=js'
		},
	]
}
plugins:[
	new HappyPack({
        id: 'js',
        use: [{
            test: /\.js$/,
            exclude: /node_modules/,
            loader: 'babel-loader',
            options: {
                ... ...
            }
        }]
    })
]

十五、webpack打包多页应用

0.需求利用webpack打包生成两个页面

一个页面叫做index, 一个页面叫做detail

1.如何打包多也应用?

  • 1.1 有多少个界面就指定多少个入口, 并给不同的入口指定不同的名称
  • 1.2 有多少个界面就创建多少个HtmlWebpackPlugin, 并给不同的界面配置不同的名称
  • 1.3 在HtmlWebpackPlugin中通过chunks属性告知需要插入到当前界面的文件
entry: {
        index: './src/js/index.js',
        detail: './src/js/detail.js',
},
/*
默认只有index.html
生成多页面使用HtmlWebpackPlugin,指明文件名filename
*/
new HtmlWebpackPlugin({
    // 指定打包的模板, 如果不指定会自动生成一个空的
    template: "./src/index.html",
    filename:"index.html",
    chunks: ['index', 'vendors~index']
  }),
  /*
chunks
不写chunks,所有的文件全部插入到所有的html中,
在chunks中指明需要插入的文件
*/
  new HtmlWebpackPlugin({
    // 指定打包的模板, 如果不指定会自动生成一个空的
    template: "./src/index.html",
    filename:"detail.html",
    chunks: ['detail', 'vendors~detail']
  }),

未指明chunks
在这里插入图片描述

2. 优化

1.当前打包多页面应用存在的问题

  • 1.1有多少个界面就要编写多少个入口
  • 1.2有多少个界面就要创建多少个HtmlWebpackPlugin
    并且每个HtmlWebpackPlugin中的配置都不一样

2.如何解决以上问题

  • 2.1入口还是必须手动指定, 但是创建多少个HtmlWebpackPlugin和
    如何配置HtmlWebpackPlugin可以通过代码动态生成
    也就是原理和动态创建AddAssetHtmlPlugin/DllReferencePlugin一样
const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const Webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin');
const fs = require('fs');
const HappyPack = require('happypack');

const config = {
    /*
    resolve: 告诉webpack如何解析导入的模块
    * */
    resolve: {
    /*
    entry: 指定需要打包的文件
    * */
    entry: {
        index: './src/js/index.js',
        detail: './src/js/detail.js',
        account: './src/js/account.js',
    },
    /*
    output: 指定打包之后的文件输出的路径和输出的文件名称
    * */
    output: {
        /*
        filename: 指定打包之后的JS文件的名称
        * */
        filename: 'js/[name].[contenthash:8].js',
        /*
        path: 指定打包之后的文件存储到什么地方
        * */
        path: path.resolve(__dirname, 'bundle')
    },
    /*
    module: 告诉webpack如何处理webpack不能够识别的文件
    * */

    /*
    plugins: 告诉webpack需要新增一些什么样的功能
    * */
    // plugins: plugins
};
.....
config.plugins = makePlugins(config);

function makePlugins(config){
    const plugins = [
	......
    // console.log(Object.keys(config.entry));
    Object.keys(config.entry).forEach(function (key) {
        plugins.push(new HtmlWebpackPlugin({
            // 指定打包的模板, 如果不指定会自动生成一个空的
            template: './src/index.html',
            filename: key + '.html',
            chunks: [key, 'vendors~'+key]
        }));
    });
    const dllPath = path.resolve(__dirname, 'dll');
    const files = fs.readdirSync(dllPath);
    files.forEach(function(file) {
        if (file.endsWith('.js')) {
            plugins.push(new AddAssetHtmlPlugin({
                filepath: path.resolve(__dirname, 'dll', file)
            }));
        } else if (file.endsWith('.json')) {
            plugins.push(new Webpack.DllReferencePlugin({
                manifest: path.resolve(__dirname, 'dll', file)
            }));
        }
    });
    return plugins;
}
module.exports = config;

十六、webpack-bundle-analyzer

1.什么是webpack-bundle-analyzer?

webpack-bundle-analyzer是一个可视化的打包优化插件
webpack-bundle-analyzer会将打包的结果以图形化界面的方式展示给我们
从webpack-bundle-analyzer生成的图形化界面中我们可以很清楚的知道
模块之间的依赖关系
模块大小
模块有没有重复打包, 重复引用等
从而针对性的对我们的代码进行优化

2.安装

webpack-bundle-analyzer

npm install --save-dev webpack-bundle-analyzer
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
  plugins: [
    new BundleAnalyzerPlugin()
  ]
}
new BundleAnalyzerPlugin({
  //  可以是`server`,`static`或`disabled`。
  //  在`server`模式下,分析器将启动HTTP服务器来显示软件包报告。
  //  在“静态”模式下,会生成带有报告的单个HTML文件。
  //  在`disabled`模式下,你可以使用这个插件来将`generateStatsFile`设置为`true`来生成Webpack Stats JSON文件。
  analyzerMode: 'server',
  //  将在“服务器”模式下使用的主机启动HTTP服务器。
  analyzerHost: '127.0.0.1',
  //  将在“服务器”模式下使用的端口启动HTTP服务器。
  analyzerPort: 8888,
  //  路径捆绑,将在`static`模式下生成的报告文件。
  //  相对于捆绑输出目录。
  reportFilename: 'report.html',
  //  模块大小默认显示在报告中。
  //  应该是`stat`,`parsed`或者`gzip`中的一个。
  //  有关更多信息,请参见“定义”一节。
  defaultSizes: 'parsed',
  //  在默认浏览器中自动打开报告
  openAnalyzer: true,
  //  如果为true,则Webpack Stats JSON文件将在bundle输出目录中生成
  generateStatsFile: false,
  //  如果`generateStatsFile`为`true`,将会生成Webpack Stats JSON文件的名字。
  //  相对于捆绑输出目录。
  statsFilename: 'stats.json',
  //  stats.toJson()方法的选项。
  //  例如,您可以使用`source:false`选项排除统计文件中模块的来源。
  //  在这里查看更多选项:https:  //github.com/webpack/webpack/blob/webpack-1/lib/Stats.js#L21
  statsOptions: null,
  logLevel: 'info' // 日志级别。可以是'信息','警告','错误'或'沉默'。
})

在这里插入图片描述


学习笔记❥(^_-),版权归Jonathan

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值