webpack5学习指南-入门篇

637 阅读7分钟

webpack学习

基本使用

初始化项目

npm init -y

安装webpack

// 安装webpack核心包和webpack命令行工具包(webpack-cli),webpack-cli是为了执行核心包的命令
npm install webpack webpack-cli --save-dev

新增webpack配置文件,命名固定为webpack.config.js

基本概念

  • entry(入口)

    入口起点(entry point) 指示 webpack 应该使用哪个模块,来作为构建其内部 依赖图(dependency graph) 的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。

    默认值是 ./src/index.js,但你可以通过在 webpack configuration 中配置 entry 属性,来指定一个(或多个)不同的入口起点。例如:

    module.exports = {
        entry: './src/index.js'
    }
    
  • output(输出)

    output 属性告诉 webpack 在哪里输出它所创建的 bundle,以及如何命名这些文件。主要输出文件的默认值是 ./dist/main.js,其他生成文件默认放置在 ./dist 文件夹中。

    你可以通过在配置中指定一个 output 字段,来配置这些处理过程:

    module.exports = {
        entry: './src/index.js',
        putput: {
            path: resolve(__dirname, 'dist'), // 输出文件夹的绝对路径,__dirname指当前文件所在的目录
            filename: 'main.js' // 输出的文件名
        }
    }}
    
  • loader

    webpack 只能理解 JavaScript 和 JSON 文件,这是 webpack 开箱可用的自带能力。loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效模块,以供应用程序使用,以及被添加到依赖图中

    module: {
        rules: [
            // 测试自己的loader  use: resolve(__dirname, 'loaders', 'raw-loader.js')
            { test: /\.txt$/, use: 'raw-loader' }
        ]
    },
    
  • plugin(插件,本质是一个class)

    loader 用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量

    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html'
        })
    ]
    
  • mode(模式)

    日常的开发工作中,一般会有两套构建环境

    一套开发时使用,构建结果用于本地开发调试,不进行代码压缩,打印debug信息,包含sourcemap文件

    一套构建后的结果直接应用于线上,即代码经过压缩,运行时不打印debug信息,静态文件不包含sourcemap

    webpack4.x版本引入了mode的概念

    当你指定使用production mode时,默认启用各种性能优化的功能,包括构建结果优化以及webpack运行性能优化

    如果使用的development mode,则开启debug工具,运行时打印详细错误信息,以及更加快速的增量编译构建

    环境差异

    • 开发环境
      • 需要生成sourcemap文件
      • 需要打印debug信息
      • 需要live reload或者hot reload的功能(热更新)
    • 生成环境
      • 可能需要分离css成单独的文件,以便多个页面共享一个css文件
      • 需要压缩html/css/js代码
      • 需要压缩图片等资源文件
  • 开发服务器

    可以使用watch和live server模拟webpack-dev-server的开发模式,但是有不足之处,主要是每次修改保存都是将所有源代码重新编译,编译成功都要进行文件读写操作,不能实现局部刷新

    安装webpack-dev-server

    npm i webpack-dev-server -D
    

    配置devServer

    // devServer会启动一个http开发服务器,把一个文件夹作为静态根目录
    // 为了提高性能,使用的内存文件系统(webpack使用的memory-fs,类似fs)
    devServer: {
        // 设置静态资源路径,查找资源的优先级是先从打包好的目录查找,找不到在去设置的directory里面找
        static: {
            // 告诉服务器从哪里提供内容
            directory: join(__dirname, 'static'),
            // 用来决定应该从哪里提供 bundle
            // publicPath: '/serve'
        },
        devMiddleware: {
            writeToDisk: true, // 如果指定此选项为true,也会把打包后的文件写入硬盘一份
        },
        compress: true, // 是否启用压缩
        port: 8080, // http的端口号
        open: true,  // 自动打开浏览器
    },
    
  • HMR(热替换)

    替换替换局部内容,其余内容不变

    在webpack.config.js中设置如下字段,此时热替换并未生效,修改title.js文件,整个页面都刷新,需要在入口文件写这段代码

    devServer: {
        hot: true
    },
    
    // 入口文件
    if (module.hot) {
        // 如果开启了热更新,就把需要开启热更新的模块添加进来
        module.hot.accept(['./js/title.js'], () => {
            console.log('title.js模块更新');
        })
    }
    
  • 支持react热替换

    安装@pmmmwh/react-refresh-webpack-plugin和react-refresh

    npm install -D @pmmmwh/react-refresh-webpack-plugin react-refresh
    

    在webpack.config.js里面配置插件

    const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin')
    new ReactRefreshWebpackPlugin()
    

    在babel.config.js配置插件,具体实现热替换的是react-refresh

    plugins: [
        ['react-refresh/babel']
    ]
    
  • 支持vue热更新

    本文以vue3进行测试,先安装依赖vue、vue-loader和@vue/compiler-sfc

    npm i vue
    npm i vue-loader @vue/compiler-sfc -D
    

    编写vue文件

    <template>
        <div class="example">{{ msg }}</div>
    </template>
    
    <script setup>
    import { ref } from "vue";
    const msg = ref("测试");
    </script>
    
    <style>
    .example {
        color: skyblue;
    }
    </style>
    

    入口文件引入vue文件

    import { createApp } from 'vue'
    import App from './App.vue'
    createApp(App).mount(document.getElementById('app'))
    

    wbepack.config.js配置对vue的支持

    // 配置loader
    {
        test: /\.vue$/,
        use: ['vue-loader']
    }
    // 配置插件
    new VueLoaderPlugin()
    

    启动devserver会发现会有警告

    index.js:7550 Feature flags __VUE_OPTIONS_API__, __VUE_PROD_DEVTOOLS__ are not explicitly defined. You are running the esm-bundler build of Vue, which expects these compile-time feature flags to be globally injected via the bundler config in order to get better tree-shaking in the production bundle.
    
    For more details, see https://link.vuejs.org/feature-flags.
    

    解决方案使用webpack的DefinePlugin插件配置全局变量

    new DefinePlugin({
        __VUE_OPTIONS_API__: JSON.stringify(false),
        __VUE_PROD_DEVTOOLS__: JSON.stringify(true)
    }),
    
  • 支持css\less\sass

    安装css-loader和style-loader,css-loader用来翻译处理@import和url(),style-loader可以将css插入到DOM中

    npm install css-loader style-loader -D
    

    安装sass(dart-sass)和sass-loader,less less-loader,建议使用dart-sass替换node-sass,sass-loader和less-loader的作用是把scss、less转换成css

    npm install sass sass-loader less less-loader -D
    
    // webpack.config.js对css/scss的配置
    {
        test: /\.css$/,
        use: [
            'style-loader',
            {
                loader: 'css-loader',
                options: {
                    importLoaders: 1,
                    esModule: false
                }
            },
            'postcss-loader'
        ]
    },
    {
        test: /\.scss$/,
        use: [
            'style-loader',
            {
                loader: 'css-loader',
                options: {
                    importLoaders: 2
                }
            },
            'postcss-loader',
            'sass-loader'
        ]
    },
    
  • 拆分css

    安装依赖mini-css-extract-plugin(mini-css-extract-plugin记得不能和style-loader一起使用,style-loader是将样式用style标签包裹引入到html中,mini-css-extract-plugin是将样式抽离到单独的文件导出link url,html中使用link标签引入)

    npm install mini-css-extract-plugin -D
    
    const MiniCssExtractPlugin = require("mini-css-extract-plugin")
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [MiniCssExtractPlugin.loader, { loader: "css-loader", options: { importLoaders: 1 } }, "postcss-loader"]
            },
            {
                test: /\.scss$/,
                use: [MiniCssExtractPlugin.loader, { loader: "css-loader", options: { importLoaders: 2 } }, "postcss-loader", "sass-loader"]
            }
        ]
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: "css/[name].[hash:6].css",
            chunkFilename: "[id].css"
        })
    ]
    
  • 压缩css

    安装依赖css-minimizer-webpack-plugin

    npm i css-minimizer-webpack-plugin -D
    
    const CssMinimizerPlugin = require("css-minimizer-webpack-plugin")
    optimization: {
        minimizer: [
            // 在 webpack@5 中,你可以使用 `...` 语法来扩展现有的 minimizer(即 `terser-webpack-plugin`),将下一行取消注释
            `...`,
            new CssMinimizerPlugin(),
        ],
    },
    
  • 支持图片

    安装file-loader、url-loader和html-loader

    file-loader可以读取文件,拷贝到打包目录(可以通过options修改文件名字)

    url-loader作用与file-loader类型,只是options多了一个limit参数,如果文件的大小小于limit时,就转成base64字符串嵌入到HTML中,简单来说url-loader是file-loader的增强,文件大于limit时,交给file-loader处理,否则自己处理

    注意:webpack5新增了资源模块,资源模块(asset module)是一种模块类型,它允许使用资源文件(字体,图标等)而无需配置额外 loader,当在 webpack 5 中使用旧的 assets loader(如 file-loader/url-loader/raw-loader 等)和 asset 模块时,你可能想停止当前 asset 模块的处理,并再次启动处理,这可能会导致 asset 重复,你可以通过将 asset 模块的类型设置为 'javascript/auto' 来解决

    npm install file-loader url-loader html-loader -D
    
    {
        test: /\.(jpg|jepg|png|gif|bmp)$/, use: [{
            loader: 'file-loader',
            options: {
                name: '[hash:10].[ext]',//拷贝后的名字
                esModule: false, // 不使用esmodule,如果使用esmodule,使用引入的文件需要.default来获取
            }
        }]
    }
    
    {
        test: /\.(jpg|jepg|png|gif|bmp)$/, use: [{
            loader: 'url-loader',
            options: {
                name: '[hash:10].[ext]',//拷贝后的名字
                esModule: false, // 不使用esmodule,如果使用esmodule,使用引入的文件需要.default来获取,
                limit: 8 * 1024 // 如果文件的大小小于limit(小于8kb)时,就转成base64字符串嵌入到HTML中,否则行为与file-loader一样 
            }
        }]
    }
    

    在webpack使用图片的几种方式

    1. 直接通过import或者require引入,file-loader或url-loader处理
    2. 放在静态文件根目录下,通过html的img标签直接引用,需要配置devServer的static
    3. 在css中通过url引入图片 css-loader来进行解析处理
  • asset(资源类型模块)

    webpack5已经内置资源类型模块

    asset/resource --- file-loader

    asset/inline --- url-loader

    asset/source --- raw-loader

    asset可以设置体积大小阈值去使用asset/resource或者asset/inline进行处理

    {
        test: /\.(png|svg|gif|jpe?g)$/,
        type: 'asset/resource',
        generator: {
            filename: "img/[name].[hash:4][ext]"
        }
    },
    {
        test: /\.(png|svg|gif|jpe?g)$/,
        type: 'asset/inline'
    },
    {
        test: /\.(png|svg|gif|jpe?g)$/,
        type: 'asset',
        generator: {
            filename: "img/[name].[hash:4][ext]"
        },
        parser: {
            dataUrlCondition: {
                maxSize: 10 * 1024
            }
        }
    }
    
  • 转义ES6/ES7/JXS

    babel是一个编译javascript的平台,可以把ES6/ES7,React的JSX转义成ES5

    安装依赖,babel-loader使用babel和webpack转义javascript文件,@babel/core是babel编译的核心包,@babel/preset-env是每一个环境的预设

    npm install babel-loader @babel/core @babel/preset-env -D
    

    1.把ES6转成ES6语法树(@babel/core实现)

    2.调用预设perset-env把ES6语法树转成ES5语法树(@babel/preset-env实现)

    3.把ES5语法树转成ES5代码(@babel/core实现)

    {
        test: /\.jsx?$/, use: [
            {
                loader: 'babel-loader',
                options: {
                    // persets是预设,预设是一系列插件的集合
                    presets: [
                        "@babel/preset-env",
                        "@babel/preset-react"
                    ],
                    // plugins是插件,因为不常用,所以不集合到预设里
                    plugins: [
                        ["@babel/plugin-proposal-decorators", { legacy: true }],
                        ["@babel/plugin-proposal-class-properties", { loose: true }]
                    ]
                },
    
            }
        ],
        exclude: /node_modules/ // 不处理node_modules下的js,否则打包会报corejs的错误
    },
    

    可以把babel-loader的配置抽离到根目录的babel-config.js文件下(需要手动新建这个文件),

    // babel-config.js的配置
    module.exports = {
        presets: ['@babel/preset-env']
    }
    
  • polyfill

    webpack4之前是直接会处理,webpack5为了提高打包效率,需要我们手动配置了,babel官方推荐不要直接安装使用@babel/polyfill,改用core-js和regenerator-runtime来配置

    安装core-js和regenerator-runtime,core-js安装3的版本

    npm i core-js regenerator-runtime -D
    

    在抽离出来的babel.config.js里配置

    module.exports = {
        presets: [
            [
                '@babel/preset-env',
                {
                    // useBuiltIns的值:false-不对当前的js做处理,usage-根据源代码当中使用到的新语法进行处理,entry-根据配置的要兼容的浏览器来处理(不管源代码是否使用,只要这个浏览器需要就都处理)
                    useBuiltIns: "entry",
                    // 默认使用的corejs版本是2,使用usage要指定版本为3,否则会报错
                    corejs: 3
                }
            ]
        ]
    }
    
  • resolve模块解析规则

    这些选项能设置模块如何被解析

    resolve: {
        // 尝试按顺序解析这些后缀名。如果有多个文件有相同的名字,但后缀名不同,webpack 会解析列在数组首位的后缀的文件 并跳过其余的后缀
        extensions: ['.js', '.json', '.wasm', '.ts', '.jsx', '.vue'],
        // 创建 import 或 require 的别名,来确保模块引入变得更简单
        alias: {
            '@': resolve(__dirname, 'src')
        }
    },
    
  • source-map(映射,在调试时定位到源代码的位置)

    不同阶段使用的配置

    // 开发阶段
    devtool: 'source-map'或者'cheap-module-source-map'
    
  • ts-loader编译ts

    安装依赖ts-loader,默认ts-loader会把ts安装下来,如果打包报错提示没有安装ts,就需要手动安装一下

    npm i ts-loader -D
    npm i typescript -D
    

    webpack.config.js配置对ts的支持

    {
        test: /\.ts$/,
        use: ['ts-loader']
    },
    
  • babel-loader编译ts

    安装@babel/preset-typescript

    npm install --save-dev @babel/preset-typescript
    

    直接使用babel-loader处理ts,有语法错误,依然能够打包成功,如果想要打包前对语法做校验可以在命令上增加tsc --noEmit

    "build": "tsc --noEmit && webpack", // 需要noEmit,否则会生成一个.js的文件
    
  • 压缩js

    安装依赖terser-webpack-plugin

    npm i terser-webpack-plugin -D
    
    optimization: {
    minimizer: [
        // 在 webpack@5 中,你可以使用 `...` 语法来扩展现有的 minimizer(即 `terser-webpack-plugin`),将下一行取消注释
        // `...`,
        // 压缩css
        new CssMinimizerPlugin(),
        // 压缩js
        new TerserPlugin()
    ],
    },
    

配置文件解释

  • .browserslistrc

    配置兼容的浏览器的信息,用于postcss和babel转换代码时指定要兼容哪些浏览器,根据编写的条件来获取符合的浏览器版本,这部分是由caniuse提供的caniuse

    > 1%  // 市场占有率>1%
    last 2 version // 该浏览器最近的两个版本
    not dead // 浏览器还在更新使用(根据24个月内,是否有修改来判定是否死亡)
    
  • babel.config.js

    会根据配置的预设/插件来转换js/jsx/ts/tsx等代码,转换过程会拿.browserslistrc配置的条件来转换

    module.exports = {
        presets: ['@babel/preset-env']
    }
    
  • postcss.config.js

    会根据配置的预设/插件来转换css代码,转换过程会拿.browserslistrc配置的条件来转换