MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

JavaScript模块打包工具Webpack基础

2022-06-077.0k 阅读

一、Webpack 是什么

Webpack 是一个现代 JavaScript 应用程序的静态模块打包工具。在 Webpack 里一切文件皆模块,通过 Loader 转换文件,通过 Plugin 注入钩子,最后输出由多个模块组合成的文件。Webpack 专注于构建模块化项目。

在传统的前端开发中,随着项目规模的不断扩大,JavaScript 代码会变得越来越复杂,文件数量也会增多。例如,我们可能有一个项目包含了页面逻辑的 JavaScript 文件、数据请求的文件、一些工具函数文件等。如果直接在 HTML 中通过 <script> 标签依次引入这些文件,不仅会导致页面加载速度慢(因为浏览器需要多次请求),而且还会面临变量命名冲突等问题。

Webpack 的出现解决了这些问题。它可以将项目中的所有模块(包括 JavaScript、CSS、图片等)进行分析和打包,生成一个或多个优化后的静态资源文件。这样在浏览器加载页面时,只需要请求这些打包后的文件,大大提高了加载效率。

二、Webpack 的安装

要使用 Webpack,首先需要安装它。Webpack 有两个核心包:webpackwebpack - cliwebpack 是主要的打包工具,而 webpack - cli 提供了在命令行中使用 Webpack 的接口。

  1. 全局安装(不推荐)

    npm install -g webpack webpack - cli
    

    全局安装虽然方便在任何项目中使用 Webpack 命令,但可能会导致不同项目因 Webpack 版本不一致而出现问题。

  2. 局部安装(推荐) 在项目目录下,执行以下命令:

    npm init -y
    npm install webpack webpack - cli --save - dev
    

    npm init -y 用于快速初始化一个 package.json 文件,--save - dev 表示将 webpackwebpack - cli 安装为开发依赖,这些依赖只在开发过程中使用,不会被打包到生产环境中。

三、Webpack 基础配置

Webpack 的配置文件通常是 webpack.config.js,位于项目根目录下。下面我们来逐步构建一个基础的配置文件。

  1. 基本配置结构

    const path = require('path');
    
    module.exports = {
        entry: './src/index.js',
        output: {
            path: path.resolve(__dirname, 'dist'),
            filename: 'bundle.js'
        }
    };
    
    • entry:指定 Webpack 打包的入口文件。这里我们指定 ./src/index.js 为入口文件,这意味着 Webpack 会从这个文件开始,分析它所依赖的所有模块。
    • output:定义了打包输出的相关配置。path 使用 path.resolve(__dirname, 'dist') 来指定输出目录为项目根目录下的 dist 文件夹。filename 则指定了输出文件的名称为 bundle.js
  2. package.json 中添加脚本 为了方便执行 Webpack 命令,我们可以在 package.jsonscripts 字段中添加脚本:

    {
        "scripts": {
            "build": "webpack --config webpack.config.js"
        }
    }
    

    这样,我们就可以通过 npm run build 来执行 Webpack 打包了。

四、Loader 的使用

Webpack 本身只能处理 JavaScript 和 JSON 文件,对于其他类型的文件,如 CSS、图片等,需要使用 Loader 来进行转换。Loader 可以将非 JavaScript 文件转换为 Webpack 能够处理的模块。

  1. 处理 CSS 文件 要处理 CSS 文件,我们需要安装 style - loadercss - loader

    npm install style - loader css - loader --save - dev
    

    然后在 webpack.config.js 中添加如下配置:

    module.exports = {
        //...其他配置
        module: {
            rules: [
                {
                    test: /\.css$/,
                    use: ['style - loader', 'css - loader']
                }
            ]
        }
    };
    
    • test:用于匹配文件路径,这里 /\.css$/ 表示匹配所有以 .css 结尾的文件。
    • use:指定使用的 Loader。style - loader 会将 CSS 代码以 <style> 标签的形式插入到 HTML 页面中,css - loader 则负责解析 CSS 文件中的 @importurl() 等语句。Loader 的执行顺序是从右到左(从下到上),所以先执行 css - loader,再执行 style - loader
  2. 处理图片文件 对于图片文件,我们可以使用 file - loaderurl - loader。这里以 url - loader 为例,它会将小图片转换为 base64 编码的字符串嵌入到 JavaScript 中,减少 HTTP 请求。

    npm install url - loader --save - dev
    

    webpack.config.js 中添加配置:

    module.exports = {
        //...其他配置
        module: {
            rules: [
                {
                    test: /\.(png|jpg|gif)$/,
                    use: [
                        {
                            loader: 'url - loader',
                            options: {
                                limit: 8192,
                                name: 'images/[name].[ext]'
                            }
                        }
                    ]
                }
            ]
        }
    };
    
    • limit:表示图片小于这个大小(单位是字节)时,会被转换为 base64 编码。这里设置为 8192 字节(即 8KB)。
    • name:指定图片输出的路径和文件名。images/[name].[ext] 表示将图片输出到 dist/images 目录下,文件名保持不变。

五、Plugin 的使用

Plugin 用于扩展 Webpack 的功能,它可以在 Webpack 构建过程中的不同阶段执行自定义的操作。

  1. 清理输出目录 在每次构建前清理 dist 目录是一个好习惯,我们可以使用 clean - webpack - plugin 来实现。

    npm install clean - webpack - plugin --save - dev
    

    webpack.config.js 中使用:

    const CleanWebpackPlugin = require('clean - webpack - plugin');
    
    module.exports = {
        //...其他配置
        plugins: [
            new CleanWebpackPlugin()
        ]
    };
    

    这样每次执行 npm run build 时,dist 目录都会被清理,确保输出的是最新的打包结果。

  2. 生成 HTML 文件 html - webpack - plugin 可以自动生成一个 HTML 文件,并将打包后的 JavaScript 文件插入到这个 HTML 文件中。

    npm install html - webpack - plugin --save - dev
    

    webpack.config.js 中配置:

    const HtmlWebpackPlugin = require('html - webpack - plugin');
    
    module.exports = {
        //...其他配置
        plugins: [
            new HtmlWebpackPlugin({
                template: './src/index.html'
            })
        ]
    };
    

    这里 template 指定了 HTML 模板文件为 ./src/index.htmlhtml - webpack - plugin 会根据这个模板生成一个新的 HTML 文件,并将打包后的 bundle.js 插入到合适的位置。

六、Webpack 的模式

Webpack 有三种模式:developmentproductionnone。不同的模式会启用不同的优化策略。

  1. development 模式webpack.config.js 中设置:

    module.exports = {
        //...其他配置
        mode: 'development'
    };
    

    development 模式会启用 NamedChunksPluginNamedModulesPlugin,这有助于在开发过程中更方便地调试。同时,打包后的文件不会进行压缩,方便查看代码。

  2. production 模式

    module.exports = {
        //...其他配置
        mode: 'production'
    };
    

    production 模式会启用各种优化,如压缩代码、移除未使用的代码(Tree - shaking)等,以提高项目在生产环境中的性能。

  3. none 模式

    module.exports = {
        //...其他配置
        mode: 'none'
    };
    

    none 模式表示不启用任何默认优化,需要手动配置所有的优化选项,一般在特殊需求时使用。

七、Webpack 的模块热替换(HMR)

模块热替换(Hot Module Replacement,简称 HMR)是 Webpack 提供的一项功能,它允许在应用程序运行过程中,替换、添加或删除模块,而无需重新加载整个页面。这大大提高了开发效率,尤其是在开发单页应用(SPA)时。

  1. 启用 HMRwebpack.config.js 中,首先要设置 devServer

    module.exports = {
        //...其他配置
        devServer: {
            contentBase: path.join(__dirname, 'dist'),
            hot: true
        }
    };
    

    contentBase 指定了开发服务器的根目录为 disthot: true 表示启用 HMR。

    然后,在入口文件(如 src/index.js)中,需要添加对 HMR 的支持:

    if (module.hot) {
        module.hot.accept();
    }
    

    module.hot.accept() 表示接受当前模块的热替换。

  2. CSS 的 HMR 对于 CSS 文件,style - loader 已经默认支持 HMR。当 CSS 文件发生变化时,页面会自动更新样式,而无需刷新页面。

八、Webpack 的代码分割

随着项目的不断扩大,打包后的文件可能会变得非常大,影响页面加载速度。Webpack 的代码分割功能可以将代码拆分成多个小块,按需加载。

  1. 使用 splitChunks 进行代码分割webpack.config.js 中添加如下配置:

    module.exports = {
        //...其他配置
        optimization: {
            splitChunks: {
                chunks: 'all'
            }
        }
    };
    

    chunks: 'all' 表示对所有类型的 chunks(如入口 chunk 和异步 chunk)都进行代码分割。Webpack 会自动将所有模块中重复的部分提取出来,生成一个单独的文件。

  2. 动态导入实现代码分割 在 JavaScript 中,可以使用动态导入(import())来实现代码分割。例如:

    document.getElementById('btn').addEventListener('click', function () {
        import('./module.js').then((module) => {
            module.doSomething();
        });
    });
    

    这里 import('./module.js') 是一个动态导入,Webpack 会将 module.js 单独打包成一个文件。当按钮被点击时,才会加载这个文件,实现了按需加载。

九、Webpack 的性能优化

  1. 优化打包速度

    • 减少模块搜索范围:在 webpack.config.js 中,通过 resolve.modules 可以指定模块的搜索路径,减少不必要的搜索。例如:
    module.exports = {
        //...其他配置
        resolve: {
            modules: [path.resolve(__dirname,'src'), 'node_modules']
        }
    };
    

    这样 Webpack 会先在 src 目录下搜索模块,然后再去 node_modules 中搜索,提高搜索效率。

    • 使用缓存babel - loader 等 Loader 可以启用缓存。例如:
    module.exports = {
        //...其他配置
        module: {
            rules: [
                {
                    test: /\.js$/,
                    use: {
                        loader: 'babel - loader',
                        options: {
                            cacheDirectory: true
                        }
                    }
                }
            ]
        }
    };
    

    cacheDirectory: true 表示启用 babel - loader 的缓存,下次构建时如果文件没有变化,会直接使用缓存结果,加快打包速度。

  2. 优化输出文件大小

    • Tree - shaking:在 production 模式下,Webpack 会自动启用 Tree - shaking,移除未使用的代码。要确保代码是 ES6 模块语法,并且使用 package.json 中的 sideEffects 字段来告知 Webpack 哪些模块有副作用,避免误删代码。例如:
    {
        "sideEffects": ["*.css"]
    }
    

    这里表示 CSS 文件有副作用,不能被 Tree - shaking 移除。

    • 压缩代码production 模式下,Webpack 会使用 terser - webpack - plugin 来压缩 JavaScript 代码。也可以通过配置该插件来进一步优化压缩效果。例如:
    const TerserPlugin = require('terser - webpack - plugin');
    
    module.exports = {
        //...其他配置
        optimization: {
            minimizer: [
                new TerserPlugin({
                    parallel: true,
                    terserOptions: {
                        compress: {
                            drop_console: true
                        }
                    }
                })
            ]
        }
    };
    

    parallel: true 表示启用多线程压缩,加快压缩速度。drop_console: true 表示移除所有的 console.log 语句,进一步减小文件大小。

十、Webpack 与其他工具的结合

  1. 与 Babel 的结合 Babel 是一个 JavaScript 编译器,用于将 ES6+ 代码转换为向后兼容的 JavaScript 代码,以支持旧版本的浏览器。要在 Webpack 中使用 Babel,首先安装相关依赖:

    npm install @babel/core @babel/preset - env babel - loader --save - dev
    

    然后在 webpack.config.js 中配置:

    module.exports = {
        //...其他配置
        module: {
            rules: [
                {
                    test: /\.js$/,
                    exclude: /node_modules/,
                    use: {
                        loader: 'babel - loader',
                        options: {
                            presets: ['@babel/preset - env']
                        }
                    }
                }
            ]
        }
    };
    

    @babel/preset - env 是一个智能预设,它会根据目标浏览器环境自动确定需要转换的语法。

  2. 与 ESLint 的结合 ESLint 是一个 JavaScript 代码检查工具,用于发现并报告代码中的问题,以保证代码质量。安装相关依赖:

    npm install eslint eslint - loader --save - dev
    

    webpack.config.js 中添加配置:

    module.exports = {
        //...其他配置
        module: {
            rules: [
                {
                    test: /\.js$/,
                    exclude: /node_modules/,
                    enforce: 'pre',
                    use: 'eslint - loader'
                }
            ]
        }
    };
    

    enforce: 'pre' 表示 eslint - loader 在其他 Loader 之前执行,这样可以在代码转换之前就发现语法问题。同时,还需要在项目根目录下配置 .eslintrc 文件来定义 ESLint 的规则。

通过以上对 Webpack 基础的介绍,我们可以看到 Webpack 在现代 JavaScript 项目开发中起着至关重要的作用。从基础的安装、配置,到各种 Loader、Plugin 的使用,以及性能优化等方面,都为开发者提供了强大的工具和灵活的配置选项,帮助我们构建高效、可维护的前端应用程序。随着项目的不断发展和需求的变化,我们还可以进一步深入研究 Webpack 的高级特性,以满足更复杂的开发需求。