IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    有了这些方法,webpack你也可以自己配

    limingcan发表于 2023-07-17 14:04:28
    love 0

    前言

    通过上篇文章——弄懂这几个概念后,我对webpack有了更新的理解 的讲解,我们大致了解了:

    • 使用webpack过程中出现的一些概念
    • 结合打包过程中出现的概念,浅析了webpack打包流程,让大家对打包流程有个大致的理解

    我们都知道,我们在实际开发过程中,使用webpack无非就是为了最后输出浏览器能运行的css、img、js、html等前端资源。

    那么,为了更贴近实际,我们这篇文章,就以如何输出css、img、js、html等前端资源为目的,学习一下如何配置webpack。

    学习大纲

    这篇文章主要讲解以下基础配置:

    • 处理css、less文件
    • 处理图片(img)、字体(font)、音乐(audio)等前端资源文件
    • 编译es6+语法及API
    • 处理html文件

    webpack文档对配置写的算挺详细了,所以这里我也不想复制粘贴又写一遍,这样不如大家直接看文档。

    授人以鱼不如授人以渔,学习不能硬学,应该要有一些方法跟技巧。因此,我更想在讲解配置的过程中,总结一些方法,让我们更好的理解webpack的配置项。这样,一些类似的一些配置,我们照葫芦画瓢,看文档就可以配出来。

    备注

    文章涉及到的案例已经上传到 github:

    • 为了阅读方便,文章只贴了相关代码,建议fork一下,看看完整代码;或者跟着文章一起边看边敲,这样印象会更深刻一些
    • 创作不易,如果觉得有帮助的话,欢迎star🌟

    处理css、less文件

    我们先用 learn-03 这个案例来看看webpack如何处理样式文件。

    我们安装相应的loader解析:

    • less-loader:解析less为css
    • css-loader:将css解析为webpack能识别的有效模块
    • style-loader:将解析出来的css插入到<header />中
    npm install less less-loader css-loader style-loader -D

    然后配置一下:

    // webpack.config.js
    module.exports = {
        ...,
        module: {
            rules: [
                {
                    test: /.(css|less)$/, 
                    use: [
                        'style-loader',
                        'css-loader',
                        'less-loader',
                    ],
                },
            ]
        }
    }

    loader的解析是逆序执行(或从右到左),因此这个配置会按照(less-loader → css-loader → style-loader)以下顺序执行:

    1. 将less解析成css
    2. 再将结果传给css-loader,解析成webpack认识的有效模块
    3. 再将结果传给style-loader,将解析出来的样式插入到<header />)的顺序执行。

    less、css、入口文件代码为:

    // index.less
    *{
        margin: 0;
        padding: 0;
    }
    h1{
        color: red;
        margin-top: 20px;
    }
    
    /* index.css */
    h2{
        color: green;
    }
    
    // index.js
    import './assets/index.less';
    import './assets/index.css';

    输出:

    我们会发现我们写的样式处理成功,并且被插入到<head/>中。

    总结

    我们 上篇文章 说过:

    • 我们通过import或require进来的资源文件,或我们项目中的每个文件,都可以看作为一个独立的模块
    • Loader的作用就是把这些模块(module)转换成webpack能够识别的有效模块

    webpack人性化的暴露了module配置项,就是专门用来配置相关loader,以此解析相应的module。因此,如果我们想解析我们源码中使用的某些模块,我们就应该:

    • 先下载相应的loader
    • 直接找到module配置项,根据loader使用方法直接配置

    所以,我们按照上面总结的方法跟思想,可以试试自己配置处理一下.scss或者.txt文件


    ok,我们成功得到了我们项目需要的样式(css),接下来我们再看看如何处理图片(img)等前端资源文件

    处理前端资源文件

    前端资源文件很多,在这里,我们粗略的把它们先分成两类:

    • 常用类:一般就是img、font、video等文件
    • 特殊类:.csv、.xml等文件

    这些资源只要import或者require进我们的源码,那么它一样也是模块。既然是模块,我们就需要安装相应的loader进行解析,然后在module配置项配置。

    在wepack5之前,对于常用类的资源,我们一般需要安装以下loader来解析:

    • raw-loader:将文件解析成字符串
    • file-loader:将import/require()进来的文件,解析为一个完整引用的url,并将文件发送到输出目录中
    • url-loader:可以把文件转成base64

    我们经常用到的是file-loader、url-loader这两个。因为常用类资源,经常以url形式引入到我们项目中;或者为了减少http请求,直接转换成base64

    但是在webpack5后,webpack已经内置上述几个loader的功能,所以我们可以不用再安装以上的loader了;我们可以直接使用webpack自带的 asset module。

    webpack5之后内置的资源模块:

    • asset/source:等同于raw-loader
    • asset/resource: 等同于file-loader
    • asset/inline:等同于url-loader
    • asset:比较灵活,同时拥有asset/resource与asset/inline的功能。如果设置了文件大小限制,文件没有超出这个大小的话,会转为base64;如果没设置大小限制,则功能跟file-loader一样
    这是我十分爱webpack5的原因,因为我们不要安装那么多乱七八糟的loader

    我们用 learn-04 这个案例,看看怎么使用资源模块来解析我们的前端资源。

    我们把上一章解析样式的配置一起加进来,在less跟js中分别使用图片资源。

    我们有两个图片:

    • preview.gif:大小为349kb
    • ecj-cli.png:大小为4kb
    // webpack.config.js
    module.exports = {
        ...,
        module: {
            rules: [
                {
                    test: /.(css|less)$/, 
                    use: [
                        'style-loader',
                        'css-loader',
                        'less-loader',
                    ],
                },
                {
                    test: /\.(png|svg|jpg|jpeg|gif)$/i,
                    type: 'asset',
                    parser: {
                        dataUrlCondition: {
                          maxSize: 1024 * 10 // 当图片 < 10kb 转成base64
                        }
                    },
                    generator: {
                        filename: '[name][ext]' // 设置输出的文件名字
                    }
                }
            ]
        }
    }

    入口文件:

    // index.js
    import './assets/index.less';
    
    const img = new Image();
    img.src = require('./assets/ecj-cli.png');
    img.width = 500;
    document.querySelector('#js_logo').append(img);

    样式:

    .bg_logo{
        width: (1434/3px);
        height: (1212/3px);
        background-image: url('./preview.gif');
        background-size: 100%;
    }

    输出结果:

    compile_resource

    我们会发现样式跟js都处理图片成功:

    • preview.gif大于10kb,所以没有被转为base64,而是返回了一个完整的引用路径(相当于file-loader的功能)
    • ejc-cli.png小于10kb的则被转为了base64(相当于url-loader的功能)。

    上面的gif图,是我开发的一个在日常项目中十分实用的命令行工具 ejc-cli 。它可以将对接人员收集的Excel数据,按一定格式转成我们代码所需要的json数据。它十分方便我们开发维护数据,并且还有跟其他人员对接数据。

    感兴趣的朋友可以看看 👉🏻 了解 ejc-cli

    总结

    通过import或者require进我们源码的资源,一样是一种模块。所以需要下载相应的loader,然后到module配置项进行配置。

    对于常用类资源(img、font、video、audio等等):

    • 因为常用,所以webpack5把处理它们的功能内置了,这样我们不用额外安装loader,我们直接使用asset module来管理
    • 我们一般都使用asset/resource、asset/inline、asset这几个模块来管理常用类资源。因为我们常用类的资源,经常会在我们项目中引入使用,我们需要把它们解析成引入完整的url,或者为了减少http请求,直接转换成base64

    对于特殊类资源(.csv、.xml等等):

    • 我们可以理解为,因为不常用,所以webpack没有把它们的功能内置
    • 因此,我们需要安装相应的loader来解析。例如我们想解析.csv文件,我们需要安装csv-loader来解析

    按照上面总结的方法,可以试试自己配置处理一下video或者font等文件


    好,到目前,我们已经得到了css、img。我们接下来看看,如何通过webpack处理得到我们的js

    编译es6+

    时至今日,js发展迅速,为了提高开发效率和代码质量,每年都会有一些新的语法或者新的API出现。

    例如,在语法方面出现了:

    • 箭头函数:()=>
    • 类语法糖:class People {}
    • 解构赋值:const [x, y] = [1, 2];
    • ...

    在API方面又有:

    • Promise
    • Array.prototype.includes()
    • Object.keys()
    • ...

    但是这些新的内容在一些低版本浏览器上是不支持的,所以我们需要通过webpack来将我们的es6+编译成这些低版本浏览器能支持的版本。

    我们先用 learn-05 这个案例,看看我们怎么配置webpack编译es6+。

    我们安装相关依赖:

    ...
    "devDependencies": {
        "@babel/core": "^7.22.8",
        "@babel/plugin-transform-runtime": "^7.22.7",
        "@babel/preset-env": "^7.22.7",
        "babel-loader": "^9.1.3",
        "webpack": "^5.88.1",
        "webpack-cli": "^5.1.4"
    },
    "dependencies": {
        "core-js": "^3.31.1"
    },

    入口文件。使用了class、Promise、()=>{}等es6+语法及API:

    // index.js
    class People {
        constructor(name) {
            this.name = name;
        }
        sayHi() {
            return Promise.resolve(this.name);
        }
    }
    const Lee = new People('Lee');
    Lee.sayHi().then(name => console.log(`Hi, I am ${name}.`));

    配置webpack:

    // webpack.config.js
    module.exports = {
        ...,
        module: {
            rules: [
                {
                    test: /\.m?js$/,
                    exclude: /(node_modules|bower_components)/,
                    use: {
                        loader: 'babel-loader'
                    }
                }
            ]
        }
    }

    配置Babel。在项目根目录新建babel.config.js文件:

    // babel.config.js
    const presets = [
        [
            '@babel/preset-env',
            {
                useBuiltIns: 'usage',
                corejs: {
                    version: require('core-js/package.json').version,
                    proposals: true
                }
            }
        ]
    ];
    const plugins = [
        '@babel/plugin-transform-runtime',
    ];
    module.exports = {presets, plugins}

    设置要兼容的目标浏览器。在package.json添加browserslist字段:

    "browserslist": [
        "ie 11"
    ]

    在ie 11运行的结果:

    compile_es6

    我们会发现:

    • 我们写的es6+在ie 11运行成功
    • Promise这个es6+ API,也被添加到了ie 11里

    说明我们编译的es6+是成功的。

    关于Babel

    在上面配置中,大家会发现,如果我们想用webpack编译es6+,我们还需要在根目录添加babel.config.js文件,步骤比处理其他模块要复杂麻烦的多。这是因为,编译es6+的核心,是需要Babel来配合的。Babel是编译es6+的一个工具。

    由于本篇文章主要是讲解webpack,所以在这里,我们对Babel就大概提一下。以下是Babel常用的一些配置。

    • 如果我们开发的项目是应用程序或大型的项目,我们可以这么配置:

      const presets = [
          [
              '@babel/preset-env',
              {
                  modules: false,
                  useBuiltIns: 'entry', // or useBuiltIns: 'usage',
                  corejs: {
                      version: '3.27.2',
                      proposals: true
                  }
              }
          ]
      ];
      const plugins = [
          '@babel/plugin-transform-runtime'
      ];
      module.exports = {plugins, presets};
      
      // index.js
      // If use useBuiltIns: 'entry'
      import 'core-js/stable'
    • 如果我们是想开发一个第三方库,我们可以这么配置:

      // Babel配置
      const presets = [
          [
              '@babel/preset-env',
              {
                  modules: false
              }
          ]
      ];
      const plugins = [
          [
              '@babel/plugin-transform-runtime',
              {
                  corejs: {
                      version: 3,
                      proposals: true
                  }
              }
          ]
      ];
      module.exports = {plugins, presets};

    如果你想对Babel已经有个了解,但处于懵懵懂懂的状态;又或者你想学习Babel,可以看看我之前写的关于Babel的专栏 👉🏻 Babel专栏。

    相信我,看完你一定会有所收获。

    总结

    • js一样是import或者require进我们的源码,那么它也是模块。所以我们也要下载对应的loader(babel-loader)并且在module配置项配置它。
    • 此外,我们除了配置babel-loader,最重要的是还需要配置Babel(babel.config.js)。

    所以,如果想要“编译es6+”,我们除了要配置webapck,最重要的是我们还要配置babel.config.js。如果你不想只是一味的复制粘贴Babel配置,你还必须得学习Babel相关知识。


    还记得我们使用webpack的目的吗?输出浏览器能运行的css、img、js、html等前端资源。

    OK,到目前我们已经通过webpack处理了css、img、js。我们再来看看如何通过webpack处理得到我们的html

    处理html文件

    在上面的例子中,我们想在浏览器看效果,我们需要:

    • 在根目录新建一个html文件
    • 在新建的html文件中,手动引入打包出来的资源文件

    这个过程太麻烦了,有没有一种办法,我们只用提供一个html模板文件,然后通过webpack编译后,它自动帮我们把打包后的文件都引入好。这样,我们打包完直接运行html就能在浏览器看效果就好了呢?

    webpack这么人性化的东西,当然是有的。

    我们先用 learn-06 这里例子看看如何利用webpack达到我们上述的效果。

    安装相关插件:

    npm i html-webpack-plugin -D

    这次webpack配置来点不一样的:
    我们设置打包后的js储存目录为js文件夹;为打包后的js名字增加5个长度的hash值。

    // webpack.config.js
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const path = require('path');
    
    module.exports = {
        entry: {
            index: './src/index.js'
        },
        output: {
            path: path.resolve(__dirname, './dist'),
            filename: './js/[name]-[chunkhash:5].js',
            clean: true
        },
        
        plugins: [
            new HtmlWebpackPlugin({
                filename: path.resolve(__dirname, './dist/[name].html'),
                template: path.resolve(__dirname, './index.html'),
                title: 'HtmlWebpackPlugin',
            })
        ],
    }

    打包后的文件结构为:

    dist
    ├── index.html
    └── js
        └── index-efbfd.js

    打包后的html如下:

    compile_html

    我们会发现:

    • 我们的html、js都被打包输出到dist文件夹下,这十分方便我们部署(之前我们是在根目录下新建index.html,因此打包后dist文件夹内只有img、js等文件)
    • 打包后的html会帮我们自动引入好我们打包后的js文件

    分析

    从上面的例子我们可以看出,想要webpack处理html文件,我们是需要在webpack提供的plugins配置项来配置的。可能有些人会疑惑html也是一个模块,为什么是在plugins配置,而不是在module里面配置?我们来分析一下。

    我们项目所有文件当然可以看作是一个模块,但是在module里面配置还是在plugins里面配置,取决于我们对这些模块的使用目的。一般我们在自己的源码中使用到这个模块,则需要相应的Loader来解析。

    在上文讲解如何处理img、js时,我们的目的是为了在源码中解析这些文件:

    • img,我们需要将它转化成base64
    • js,我们需要将es6+的东西编译成es5及以下

    并且,我们的文件都是通过import或是require()的方式,引入到我们的项目中,因此这时候当然需要相应的Loader来转换这些模块。

    但是在处理html文件时,我们的目的并不是为了要解析html,我们只是想让打包出来的html自动引用我们的js、img,这更像是使用自动引入的功能;而且,我们也没有把我们的html引入进我们的项目,因此我们当然不需要相应的Loader来解析。

    还记得我们 上篇文章 我们讲解过:Loader用于转化模块,Plugin则用来加强webpack打包编译时的功能。

    在此处我们处理html文件,目的更多侧重的是想要让打包出来的html自动引用我们的js、img这个功能。因此我们需要在plugins配置项来配置,增强webpack打包编译时的功能。

    同理,假设我们想要在项目中解析某个html文件(在我们项目中,import html from './x.html'),那我们就得安装相应的loader(html-loader),并且在module配置项目中配置。

    总结

    在module还是plugins配置项里配置,取决于我们对这些模块的使用目的。


    至此,我们已经成功通过配置webpack拿到了一个项目中需要的css、img、js、html;这也相当于学会了webpack的一些基本配置。

    大家可以通过上面总结的一些方法,自己完成对sass、font、video等资源的处理,加深一下印象。

    学习总结

    通过上面对一些配置项的讲解,我们可以有以下总结:

    • 想要解析css,需要安装相应的loader,并且在module里配置
    • 想要解析img、font、video等常用类资源,我们不用安装loader(webpack已经内置解析他们的功能)。我们一般在module配置项里,使用asset/resource、asset/inline、asset这三个内置模块来解析
    • 想要解析js,我们除了要安装相应的loader(babel-loader),在module配置项里配置,最重要的是我们还要学习Babel的相关知识(感兴趣可以看看我的 👉 Babel专栏)
    • 想要解析html文件,我们需要在plugins配置项里配置html-webpack-plugin插件
    • 在module还是plugins配置项里配置,取决于我们对这些模块的使用目的

    通过上面的学习,我们整理一下,输出一个完整的webpack基础配置,让大家加深印象:

    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const path = require('path');
    
    module.exports = {
        entry: {
            index: './src/index.js'
        },
        output: {
            path: path.resolve(__dirname, './dist'),
            filename: './js/[name]-[chunkhash:5].js',
            clean: true
        },
        module: {
            rules: [
                {
                    test: /.(css|less)$/, 
                    use: [
                        'style-loader',
                        'css-loader',
                        'less-loader',
                    ],
                },
                {
                    test: /\.(png|svg|jpg|jpeg|gif)$/i,
                    type: 'asset',
                    parser: {
                        dataUrlCondition: {
                            maxSize: 1024 * 10 // When the image size is < 10kb it will be converted to base64
                        }
                    },
                    generator: {
                        filename: '[name]-[chunkhash:5][ext]' // Set the name of the output file
                    }
                },
                {
                    test: /\.m?js$/,
                    exclude: /(node_modules|bower_components)/,
                    use: {
                        loader: 'babel-loader'
                    }
                }
            ]
        },
        plugins: [
            new HtmlWebpackPlugin({
                filename: path.resolve(__dirname, './dist/[name].html'),
                template: path.resolve(__dirname, './src/index.html'),
                title: 'Webpack Basic Configuration',
            })
        ],
    }

    完整的基础配置在 learn-07 ,建议大家看看完整版,这里只贴了webpack.config.js

    最后

    • 希望这篇文章的一些方法总结,学习目的能让大家更好的学会如何配置webapck
    • 后面的文章将会深入配置。现实项目中,大多分开发环境、生产环境,因此我们将学习如何针对开发环境、生产环境不同的环境,进行不同webpack的配置,这更贴近我们现实项目。如果感兴趣的话可以关注一下这个 👉 专栏
    • 文章涉及到的Babel知识,如果你感兴趣,想学习,也可以看看 👉 Babel专栏
    • 文章涉及到的案例已经上传到 github,欢迎star或fork学习

    最后的最后,如果大家觉得文章有帮助到,创作不易,还请大家多点赞收藏评论,如果有异同点,欢迎评论讨论。



沪ICP备19023445号-2号
友情链接