Skip to content

Webpack

一、webpack基础介绍

1、入口entry

js
// 进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。
module.exports = {
  entry: './path/to/my/entry/file.js'
};
  • 单个入口

    用法:entry: string|Array<string>

    js
    module.exports = {
      entry: './path/to/my/entry/file.js'
    };
    
    module.exports = {
      entry: {
        main: './path/to/my/entry/file.js'
      }
    };
  • 对象语法

    js
    // 用法:entry: {[entryChunkName: string]: string|Array<string>}
    module.exports = {
      entry: {
        app: './src/app.js',
        adminApp: './src/adminApp.js'
      }
    };
    
    // 多页面应用程序
    module.exports = {
      entry: {
        pageOne: './src/pageOne/index.js',
        pageTwo: './src/pageTwo/index.js',
        pageThree: './src/pageThree/index.js'
      }
    };

2、输出output

js
// output 属性告诉 webpack 在哪里输出它所创建的 bundle,以及如何命名这些文件。
const path = require('path');

module.exports = {
  entry: './path/to/my/entry/file.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'my-first-webpack.bundle.js'
  }
};

// filename 用于输出文件的文件名
  • 多个入口起点

    js
    module.exports = {
      entry: {
        app: './src/app.js',
        search: './src/search.js'
      },
      output: {
        filename: '[name].js',
        path: __dirname + '/dist'
      }
    };
    
    // 写入到硬盘:./dist/app.js, ./dist/search.js

3、模式mode

js
// 通过选择 development, production 或 none 之中的一个,来设置 mode 参数,你可以启用 webpack 内置在相应环境下的优化。其默认值为 production。
module.exports = {
  mode: 'production'
};

4、loader

  • loader支持链式传递。链中的每个loader会将转换应用在已经处理过的资源上。
  • loader可以是同步的,也可以是异步的。
  • loader运行在Node.js中,并且可以执行任何Node.js可以做到的操作
  • loader可以通过options对象配置
js
//webpack 只能理解 JavaScript 和 JSON 文件。loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效 模块,以供应用程序使用,以及被添加到依赖图中
const path = require('path');

module.exports = {
  output: {
    filename: 'my-first-webpack.bundle.js'
  },
  module: {
    rules: [
      { test: /\.txt$/, use: 'raw-loader' }
    ]
  }
};

使用loader的三种方式:配置、内联、CLI;

  • 配置

    js
    // loader 从右到左地取值(evaluate)/执行(execute)。在下面的示例中,从 sass-loader 开始执行,然后继续执行 css-loader,最后以 style-loader 为结束。
    module.exports = {
      module: {
        rules: [
          {
            test: /\.css$/,
            use: [
              { loader: 'style-loader' },
              {
                loader: 'css-loader',
                options: {
                  modules: true
                }
              },
              { loader: 'sass-loader' }
            ]
          }
        ]
      }
    };
  • 内联

    js
    // 使用 ! 将资源中的loader分开
    import Styles from 'style-loader!css-loader?modules!./styles.css';
  • CLI

    js
    // 这会对 .jade 文件使用 jade-loader,以及对 .css 文件使用 style-loader 和 css-loader。
    webpack --module-bind jade-loader --module-bind 'css=style-loader!css-loader'

5、插件plugin

js
// loader 用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量。
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 通过 npm 安装
const webpack = require('webpack'); // 用于访问内置插件

module.exports = {
  module: {
    rules: [
      { test: /\.txt$/, use: 'raw-loader' }
    ]
  },
  plugins: [
    // 插件可以携带参数/选项
    new HtmlWebpackPlugin({template: './src/index.html'})
  ]
};

二、webpack的热更新

HMR全称Hot Module Replacement,模块热替换;开启方式:

js
const webpack = require('webpack')
module.exports = {
    // ...
    devServer: {
        // 开启 HMR 特性
        hot: true
        // hotOnly: true
    }
}

三、webpack的构建流程

  • 初始化流程:从配置文件和shell语句中读取合并参数,并初始化需要使用的插件和配置插件等执行环境所需要的参数

  • 编译构建流程:从Entry发出,针对每一个Module穿行调用对应的Loader去翻译文件内容,再找到该Module依赖的Module,递归的进行编译处理

    • compile开始编译

      执行run方法之后首先会触发complie,构建一个Compilation对象,会进行:执行模块创建、依赖收集、分块、打包等

    • make从入口分析模块及其依赖的模块,创建这些模块对象

      完成Compilation对象之后,开始从entry入口文件开始读取,主要执行_addModuleChain()函数

    • build-module构建模块

      调用配置的loaders,将模块转成标准的JS模块,输出对应的抽象语法树AST,方便webpack后面对代码分析

    • seal封装构建结果

      主要是生成chunks,对chunks进行一系列的优化操作,并生成主要输出的代码

    • emit把各个chunk输出到结果文件

      根据配置的路径和文件名机型输出

  • 输出流程:对编译后的Module组合成Chunk,把Chunk转换成文件,输出到文件系统

四、webpack proxy工作原理

基本行为就是接收客户端发送的请求后转发给其他服务器,中间服务器:webpack-dev-server

1、webpac-dev-server

js
// ./webpack.config.js
const path = require('path')
module.exports = {
    // ...
    devServer: {
        contentBase: path.join(__dirname, 'dist'),
        compress: true,
        port: 9000,
        proxy: {
            '/api': {
                target: 'https://api.github.com'
            }
        }
        // ...
    }
}

五、loader

对模块的源码进行转换,在import或加载模块时预处理文件。

在webpack中,任何文件都是模块

六、Plugin

是一种计算机应用程序,他和主应用程序相互交互,提供特定的功能;始终顿寻一定规范的应用程序接口编写出来的程序,只能运行在程序规定的系统下,因为其需要调用原纯净系统提供的函数库,或者数据。

js
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 
const webpack = require('webpack'); // 
module.exports = {
        ...
    plugins: [
        new webpack.ProgressPlugin(),
        new HtmlWebpackPlugin({ template: './src/index.html' }),
    ],
};
js
const pluginName = 'ConsoleLogOnBuildWebpackPlugin';
class ConsoleLogOnBuildWebpackPlugin {
    apply(compiler) {
        compiler.hooks.run.tap(pluginName, (compilation) => {
            console.log('webpack 构建过程开始 ');
        });
    }
}

module.exports = ConsoleLogOnBuildWebpackPlugin;

/*
compiler hook的tap第一个参数,应该是驼峰命名的插件名称
整个编译周期的声明周期钩子如下:

entry-option:初始化option
run
compile:开始编译,在创建compilation之前
compilation:生成好了compilation对象
make:从entry开始递归分析依赖,准备对每一个模块进行build
after-compile:编译build过程结束
emit:在将内存中assets内容写到磁盘文件夹之前
after-emit:在将内存中assets内容写到磁盘文件夹之后
done:完成所有的编译过程
failed:编译失败的时候
*/

八、怎么提高webpack的构建速度

1、优化loader配置

在使用loader时,可以通过配置include、exclude、test属性来匹配文件

2、合理使用resolve.extensions

解析文件时,自动添加扩展名

3、优化resolve.modules

js
module.exports = {
    resolve: {
        // 使用绝对路径知名第三方模块存放位置,以减少搜索步骤
        // 其中 __dirname 表示当前工作目录 
        modules: [path.resolve(__dirname, 'node_modules')]
    },
};

4、优化resolve.alias

配置alias减少查找过程

js
module.exports = {
    ...
    resolve:{
        alias:{
            "@":path.resolve(__dirname,'./src')
        }
    }
}

5、使用DLLPlugin插件

6、使用cache-loader

7、terser启动多线程

8、合理使用sourceMap

九、前端项目优化

1、JS代码压缩

js
const TerserPlugin = require('terser-webpack-plugin')
module.exports = {
    ...
  optimization: {
    minimize: true,
    minimizer: [
        new TerserPlugin({
            parallel: true // 
        })
     ]
   }
}

2、CSS代码压缩

通常是去除无用的空格等

js
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
module.exports = {
    // ...
    optimization: {
        minimize: true,
        minimizer: [
            new CssMinimizerPlugin({
                parallel: true
            })
        ]
    }
}

3、HTML文件代码压缩

js
module.exports = {
    plugin: [
        new HtmlwebpackPlugin({
            minify: {
                minifyCSS: false, // 是否压缩CSS
            },
            collapseWhitespace: false, // 是否折叠空格
            removeComments: true // 是否移除注释
        })
    ]
}

4、文件大小压缩

js
new ComepressionPlugin({
    test:/\.(css|js)$/,  // 哪些文件需要压缩
    threshold:500, // 设置多大开始压缩
    minRatio:0.7, // 压缩的比例
    algorithm:"gzip", // 采用的压缩算法
})

5、图片压缩

6、Tree Shaking

7、代码分离

8、内联chunk