从小白开始手撸webpack5第一弹

497 阅读8分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第4天,点击查看活动详情

前言

大家好,我是小阵 🔥,一路奔波不停的码字业务员
身为一个前端小菜鸟,总是有一个飞高飞远的梦想,因此,每点小成长,我都想要让它变得更有意义,为了自己,也为了更多值得的人
如果喜欢我的文章,可以关注 ➕ 点赞,与我一同成长吧~😋
加我微信:zzz886885,邀你进群,一起学习交流,摸鱼学习两不误🌟

正文

作为越来越内卷的FE圈中的一员,仿佛你不懂webpack就跟不懂前端一样??

黑人问号脸

本着不卷没饭吃的心态,我重温了webpack5的文档,决定第N遍手撸一下webpack的基本配置。 来,大家左脚跟右脚一步步跟着迈!(千万不要扯着???🤐)

坏笑

基本概念

  • webpack是一个静态资源模块化打包工具
    • 静态资源指css|js|imgs(jpg|png|gif|webp...)|medio(fonts|mp4|mp3...)
    • 模块化指webpack会将每个入口引入的资源作为单独的模块进行打包
    • 打包指将我们本地开发环境的各种资源通过webpack的各种loader处理转换成浏览器可识别的资源,当然绝大部分我们还需要很多webpack的plugin参与进来
  • webpack总体分为四个模块
    • entry
      • 打包的入口文件,可以单入口,也可以多入口。一切在入口文件中引入的资源都会被webpack打包
    • output
      • webpack打包之后输出的文件目录,可以单目录也可以多目录
    • module
      • 主要是用来配置各种loaderloader是webpack的翻译官,负责将webpack不认识的资源转化成webpack认识的资源(webpack只认识js和json,可想而知loader们有多么重要)
    • plugins
      • 用于拓展webpack打包过程中的其他功能,比如集成,压缩,优化

综上,我们得知了webpack其实就是一个大的node包,这个node包做了什么事情呢? 他将我们在开发过程中引入的各种小包(包括我们自己的工具包、资源包,也包括我们又引入的其他node包)通过构建编译成一个浏览器可以正常识别的资源包

注意⚠️📢:以上的概念都是我经过了自己的理解简单描述的,具体概念细节还请参考别的文章(后续我尽量补上各个概念的具体描述文章链接)

开撸

开开心心学技术大法~~

开心

来了来了,他真的来了~

webpack初体验

首先我们需要准备一下webpack的基本环境,毕竟要用到webpack的command指令,因此,我们需要「全局安装」 webpackwebpack-cli

当然,如果你讨厌这种做法,也可以安装到本项目目录,然后通过 package.json中的script模块定制你的快捷访问方式,这个后续再说。 也可以使用npx访问项目内部的包(比如:webpack)

全局安装webpackwebpack-cli

# 因为这里直接演示webpack5,所以就不指定版本了
npm i webpack webpack-cli -g

当然,你也可以用pnpm,cnpm,或者某tnpm?😄

之后你需要指定一个webpack的入口文件,我这里在项目根目录下创建了src/index.js。 随意写上一些代码

// src/index.js 作为webpack的入口文件
function add(x, y){
    return x + y;
};

console.log('add:', add(1, 2));

然后终端试执行

webpack ./src/index.js -o ./build/built.js --mode=development

你会发现,在根目录下,生成了一个build目录,该目录下还有一个built.js文件,那这个build/built.js文件其实就是webpack从入口文件./src/index.js开始打包,之后输出到./build/built.js文件。

本次打包的模式是development,该模式是不压缩的,如果是production,则默认压缩。

如果感兴趣的话,可以点进./build/built.js文件看一下,看一下到底生成了个什么玩意儿?😄

css资源引入

接下来我们搞一下其他资源进来试下,比如css

注意:前面提到了,webpack默认只认识jsjson文件,并不认识其他资源文件,那遇到webpack不认识的资源怎么办?那就需要用loader处理这些资源,将他们变成webpack认识的js模块

因为要配置loader,单一的命令行已经不支持我们做更多更复杂的配置了,这个时候就需要一个webpack的配置文件webpack.config.js,该文件要放在项目的根目录。

我们来写一下webpack.config.js

// webpack.config.js webpack的配置文件
const { resolve } = require('path');

module.exports = {
    //入口文件配置
    entry: './src/index.js',
    //输出目录配置
    output: {
        //输出文件名
        filename: 'built.js',
        //输出文件目录,最终输出结果会是 目录名 + 文件名
        path: resolve(__dirname, 'build') // __dirname是node内置变量,指改文件所在目录,是一个绝对路径,resolve将__dirname和'build'拼接成一个你要输出的绝对目录地址
    },
    // 模块
    module: {
        rules: [
            {
                // 代表匹配以.css结尾的文件
                test: /\.css$/,
                // use代表需要用到多个loader,而loader的加载顺序是从右到左的,注意:是从右到左,所以这里的顺序是先引入css-loader,再引入style-loader
                // 说了引入顺序的问题,这里再说下两个loader的作用
                // css-loader:将css代码文件中的内容以字符串的形式打包到js文件中,如果看打包后的js的话,会发现css代码是这样被引入的 eval(xxxx),其中xxx就是css样式
                // style-loader:将css字符串从js中抽离到html中,以style标签的形式内嵌到html的head标签中
                use: ['style-loader', 'css-loader']
            }
        ]
    },
    // 插件
    plugins: [],
    // 打包模式,这里是development,默认不压缩打包后的资源,后续会有production模式的演示
    mode: 'development'
}

为了书写方便,也为了大家看着方便,之后只会贴上关键代码

我们从上到下来分析,首先为什么引入包要用require,因为webpack本质也是一个node包,而node需要遵循commonJs的模块化标准,所以要用require

Q:那为什么我业务开发中用esModuleimportrequire都可以呢?

A:因为那是webpack帮你处理过了呀😄

傻孩子

你的代码有webpack帮忙处理,而webpack的代码谁来处理?

当然是node了,那就必须遵循node的模块化标准嘛~~哎,闭环了,舒服。

想必代码中的注释已经解决了你大部分疑惑了,那关于loader那部分内容我拿到这里来再提一嘴(要你多嘴🤐笑哭。。)。

容我卖个萌

首先rules是一个数组,内部可以包含多个loader每个loader都可以有自己的options,当然,你也可以使用它的默认配置,就比如上面的写法['style-loader','css-loader']

如果要定义某个loader的options的话,需要将该loader写成对象的形式,将loader的配置写到options中,我这里以css-loader举例,代码如下

module:{
   rules:[
       {
           test: /\.css$/,
           loader:'css-loader',
           options:{
            ...xxx
           }
       }
   ]
}

配置文件我们搞好了,那我们试着用webpack打包下看看吧, 终端运行

# 目录是项目根目录,跟webpack.config.js同目录
# 此时webpack默认去找webpack.config.js作为配置文件
webpack

如果不是同目录或者要运行其他目录下的配置文件,可以用--config xxx.js来指定

这个时候我们看终端打包是成功的,没有报错,那怎样看具体是否ok了呢?

好吧,我需要手动在./src目录下建一个index.html文件,然后手动引入一下打包好的../build/built.js文件(也就是上面我们ouput输出的文件),

页面看效果,ok的,没问题

那么我问题来了,我手动创建index.html文件有点low,那我能否允许自己逼格高一点呢?

其实你可以再任性一点

打包html资源

你可以的,只需要借助webpack的plugins即可,什么plugin呢?html-webpack-plugin

好,既然用到了,那我们就安装下

npm i -D html-webpack-plugin

接下来上配置代码,该插件的用法也会在代码注释上

// 首先需要通过commonjs的模块化引入插件
// 注意命名要用大驼峰
const HtmlWebpackPlugin = require('html-webpack-plugin')

export.modules = {
    ...
    // 插件
    plugins:[
        // 实例化html-webpack-plugin,并做下配置
        // 自动将webpack打包好的资源引入到模板html中,如果不指定template,会自动生成一个html文件
        new HtmlWebpackPlugin({
            // 指定以哪一个html作为模板导入打包好的js或其他资源
            template:'./src/index.html'
        })
    ],
    ...
}

搞好插件之后,我们继续webpack一下,发现这个时候哪怕我们不手动在html文件中引入../build/built.js文件依然是可行的

鼓掌

打包图片资源

继续!

css搞好了,那我们搞下image

通过上面的一串操作,我想大家已经都了解了怎样写loader了,需要注意的是,解析image只需要用到一个loader,那就是url-loader,但是因为url-loaderfile-loader的子集,因此我们需要下载两个包

npm i -D url-loader file-loader

之后需要在webpack.config.js中稍微做下配置,那就废话不多说,直接上代码,解释照例会标注到代码上

...

module.exports = {
    ...
    // 模块
    module: {
        rules: [
            ...
            {
                // 匹配以png、jpg和gif结尾的图片资源
                test:/\.(png|jpg|gif)$/,
                loader:'url-loader',
                options:{
                    // 图片大小小于8kb的时候会被base64处理
                    // 优点:减少请求数量(减轻服务器压力,让服务器优先处理其他自资源请求)
                    // 缺点:图片体积更大(相应的,打包之后体积也会稍大,base64会存到最终的包中)
                    limit: 8 * 1024,
                    // 给图片重命名
                    // hash:10 :因为打包后的图片资源名称默认是一长串hash值,那我们这里取hash值的前十位就可以了,毕竟也不需要那么长的名字,还会加大包大小(蝇子腿再小,那也是肉呀)
                    // ext:是文件的后缀名,打包前是png,打包后就依然是png,jpg和gif同理
                    name: '[hash:10].[ext]'
                }
            }
        ]
    },
    ...
}

这个时候我们在之前的css文件中引入一张图片,假设引入的是./react.png,这个时候打包是没问题的,我们打开网页源代码看到的img标签也是没问题的。

那么问题来了,image不可能只在css中使用吧,如果我在html使用呢??

ok,我们来试一下,在./src/index.html文件中写了个标签

<img src='./react.png' alt='react' />

我继续webpack一下,哦吼?报错了?

惊讶

我们看提示发现是webpack解析到html文件的时候识别不了./react.png这个资源。哦?刚才不是说只用url-loader就可以了吗?你在骗我!

悲痛欲绝

莫慌莫慌,url-loader只能处理css中的图片资源,那我html中的图片资源还需要另外的loader来处理,什么loader呢?html-loader

是的,你没听错,一个名字取的像是要处理html似的,其实就只是处理html中的image资源。。。

手动狗头。。

那么上代码,我们只需要在原来的基础上加上html-loader的处理就可以了

{
    // 匹配以.html结尾的文件
    test:/\.html$/,
    // 识别html中的图片资源并引入,之后该资源转交到url-loader手中
    loader:'html-loader'
}

然后我接着webpack,哦吼?没有报错,那我们看页面,没出来图片,纳尼!!! 我打开源代码,看到了以下img标签

<img src="[object Module]" alt="" />

为什么呢?

原来html-loader默认是commonJs的模块,而 url-loader使用的是esModule的模块,那他们之间自然就不能正常解析了,那怎么办呢? 只需要传入url-loader中的一个配置局可以了,什么配置呢?

{
    // 匹配以png、jpg和gif结尾的图片资源
    test:/\.(png|jpg|gif)$/,
    loader:'url-loader',
    options:{
        ...
        esModule:false
    }
}

是不是很降智?? 不不不,只是人家options提供的好而已。。

无语凝噎

打包其他资源

接上,我们image和css都搞好了,那其他资源岂不是都很有限了?

黑人笑

上更多资源,例如font|media|mp4

大家看到了,我用了【等】,是的,没错,接下来我们只需要用一个loader就可以处理除了jscsshtml资源以外的任意资源(当然,你的资源一定要是真实存在的,我随便造一个.zzz后缀的可不行哦^_^)

注意:这次真的只需要一个loader,我们可不兴跟图片资源学习的呀!罒ω罒

这个loader上面已经提到过了,那就是file-loader,一个强大的loader,好,我们上面已经安装过了,那直接上配置代码

{
    // 排除掉css、js和html资源之外的所有资源
    exclude: /.\(css|js|html)$/,
    // 是的,我就是这么简单,启用默认配置就可以,当然,更多配制会在后面的【生产环境篇】讲解
    loader: 'file-loader'
}

验证: 没问题!!赞

注意:记得验证的时候,这些资源一定要在入口文件中引入,否则webpack并不会追踪到这些文件,当然也不会打包

省略号

好吧,我从头到尾都没有贴验证结果,只是文字描述了,那是因为我掘金写的时候跟代码不在一个时间段哈哈哈哈。。。

我之后会把代码上传到github,感兴趣的同学可以自己clone一下自己验证下看看,当然,好心又帅气的朋友们也会自觉的点个Star啊什么的~

偷笑

结语

往期好文推荐「我不推荐下,大家可能就错过了史上最牛逼vscode插件集合啦!!!(嘎嘎~)😄」