从0开始的Webpack - 写Demo用的简单配置

2,231 阅读10分钟

前言

如今的前端,必定都用过Vue或者React,多多少少都有接触过webpack,我还清楚地记得第一次看Vue的Webpack配置时,仿佛在看天书一般。webpack作为前端工程化的核心知识之一,因为其功能太多,配置项又多又杂,初学时也比较难啃。

这是一个面向初学者的webpack系列博文。从0开始,一步步搭建出完整的、适用于常规环境的webpack配置。

每章都以树状结构扩深知识点,尽可能的涵盖更多更深的知识内容,同时保留每个章节的所有代码。

目录构建

首先创建一个文件,然后执行 npm init

随后创建对应子文件夹

.webpackLemo // 文件夹
│
│-- config // 存放配置文件
│
│-- public // 存放模板
│
│-- src // 存放入口文件
│
└─- package.json

写Demo用的简单配置

我们有了基本文件夹,接下来开始构建最基本配置,能满足平时写demo用。

支持打包

首先安装webpack工具库

npm i webpack webpack-cli

创建入口文件

// src/index.js

console.log("Webpack Lemo");
// public/index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Webpack Lemo</title>
</head>
<body>
    <script src="./main.js"></script> //打包后的文件为 main.js 在这里手动写入
</body>
</html>

创建配置文件

// config/webpack.config.js

const path = require('path');
module.exports = {
    entry: {
        main: './src/index.js'
    },
    output: {
        path: path.resolve(__dirname, '../build')
    }
}

entry :入口起点配置。简单来说,由html直接引用的文件称为入口起点,从这个起点开始,应用程序启动执行。例子中的 main 为入口起点的 [name] 值

output:输出配置。webpack打包完成,怎么输出,要输出哪些文件,输出到哪等相关信息的配置

output.path 要求接受一个绝对路径。 path.resolve 方法会把一个路径或路径片段的序列解析为一个绝对路径,__dirname 指向当前文件的路径

entry 配置里使用的是相对路径,这个路径默认指向代码被执行时路径,即 webpackLemo 文件夹的根目录。可通过设置 context 来修改路径上下文。

// 与正文中的配置效果等同
{
    context: path.resolve(__dirname),
	entry: {
        main: '../src/index.js'
    }
}

关于entry的更多信息 关于output的更多信息

// package.json
{
  ...
  "scripts": {
    "dev": "webpack --config ./config/webpack.config.js"
  },
  ...
}

接下来只需在命令行里执行 npm run dev 就能进行第一次打包

先忽视 warning ,可以看见webpack自动打包了 src 目录下的文件,在根目录下自动生成build文件,并将打包后的入口文件命名为 main.js 放入build文件中。

然后我们可以把 public 目录下的 index.html 手动复制到 build 目录下。

完成以上操作后,我们的目录会长成这样

.webpackLemo
│
│-- build // 存放打包后的文件
│  │-- main.js // src/index.js打包生成的文件
│  │-- index.html // 从public中手动复制
│
│-- config
│  │-- webpack.config.js // 新建webpack配置文件
│
│-- public
│  │-- index.html // 新建html模板文件
│
│-- src
│  │-- index.js // 新建入口文件
│
└─- package.json

用浏览器打开 ./build/index.html,可以看见控制台里输出了 在 ./src/index.js 里写的 console.log信息

模板处理

如果每次打包,都需要我们手动粘贴一次 index.html,那也太麻烦了。如果有一个哥们儿能 帮我们自动复制index.html文件,并且在index.html中自动引入我们打包后的文件 那就好了。

这个哥们儿叫做 html-webpack-plugin

执行安装 npm i html-webpack-plugin

然后修改文件

// public/index.html

<!DOCTYPE html>
<html lang="en">
...

<body>
	// <script src="./main.js"></script>  // 删除手动写入的 对main.js的引用
</body>
</html>
// config/webpack.config.js

...
// 引入html-webpack-plugin
const HtmlWebpackPlugin = require('html-webpack-plugin'); 

module.exports = {
    ...
    output: {
        ...
        filename: '[name].bundle.js',  // 添加filename配置
    },
    plugins: [ // 注意是个数组
        new HtmlWebpackPlugin({  // 在plugin中使用 html-webpack-plugin
            template: './public/index.html' // 配置模板项地址
        }),
    ]
}

plugins:配置webpack插件的地方

关于plugins的更多信息

filename 配置项中使用了[name],这里的[name]为输出的模块名。除了[name],webpack还提供了别的模板语法,可在这里找到所有信息 output.filename

现在我们再执行 npm run dev 可以看见这次打包生成的文件名为main.bundle.js,并且 build 目录下的index.html 文件里自动引入了 main.bundle.js

完成以上操作后,我们的目录会长成这样

.webpackLemo
│
│-- build 
│  │-- main.js // 第一次打包生成的文件
│  │-- main.bundle.js // 第二次打包生成的文件
│  │-- index.html // 使用 html-webpack-plugin 后生成的html文件
│
...

文件清理

可以看见第二次打包时,我们修改了输出配置,生成了新的文件,但是第一次打包的内容还是被保留了下来。其实第一次打包的内容已经用不上了,有没有什么哥们儿能帮我们自动删除不需要的内容?

有,这个哥们儿叫做 clean-webpack-plugin

执行安装 npm i clean-webpack-plugin

然后修改文件

// config/webpack.config.js

...
// 引入clean-webpack-plugin
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
    ...
    output: {
        path: path.resolve(__dirname, '../build'),
        filename: '[name].[contenthash].js', // 我们再修改一次输出文件名
    },
    plugins: [
        ...
        new CleanWebpackPlugin(); // 使用 clean-webpack-plugin
    ]
}

现在我们第三次执行 npm run dev ,可以看见build目录下头两次生成的入口文件都被清除了,只保留了第三次生成的内容。

完成以上操作后,我们的目录就长成这样

.webpackLemo
│
│-- build 
│  │-- main.417cd68a8235c5cd4f89.js // 第三次打包生成的文件
│  │-- index.html
│
...

到目前为止,已经支持了基本功能打包,下一步来处理css样式和file文件

打包css和file文件

对css的支持

对css文件的处理有2种方式:

  1. 将css代码以 <style> 标签的形式嵌入 html 中

  2. 生成css文件,以 <link> 标签的形式在 html 中引入

这里我们只讨论第一种形式,第二种会在 代码分割(卫星) 中介绍

我们需要4个插件

  • style-loader (github) 将css代码以 <style> 标签的形式嵌入 html 中
  • css-loader (github) 解析通过模块化引入的css文件
  • postcss-loader (github) 提供预处理css的一些能力,拥有许多子插件,提供了许多能力
  • autoprefixer (github) postcss-loader的子插件,提供厂商前缀自动补全能力,如 -m-

loader和plugin的区别

// 官方文档原文
While loaders are used to transform certain types of modules, plugins can be leveraged to perform a wider range of tasks like bundle optimization, asset management and injection of environment variables.

// 渣译
loader一般用来转换某些类型的modules,而插件可以用来执行更广泛的任务,例如对包的优化、资源管理、环境注入等

// 大白话
loader是一个转换器,将A文件进行编译成B文件,比如:将A.less转换为A.css,单纯的文件转换过程。
plugin是一个扩展器,丰富了webpack本身,针对是loader结束后,webpack打包的整个过程,它并不直接操作文件,而是基于事件机制工作,会监听webpack打包过程中的某些节点,执行广泛的任务

执行安装 npm i style-loader css-loader postcss-loader autoprefixer

然后修改文件

// ./config/webpack.config.js

...
module.exports = {
    ...
    module: { // 这里是对象
        rules: [ // 这里是数组
            { // 数组中的每个具体配置
                test: /\.css$/, // 匹配 .css文件
                use: [ // 当匹配上 .css 文件后,会依次使用loader进行处理
                    'style-loader',
                    'css-loader',
                    {
                        loader: 'postcss-loader',
                        options: {
                            plugins: [
                                require('autoprefixer')
                            ]
                        }
                    }
                ]
            }
        ]
    }
}

module:各种模块应该如何处理,由module进行配置

关于module的更多信息

loader的处理顺序为从下到上。正文中loader处理顺序为:postcss-loader、css-loader、style-loader

// ./src/index.js
import './index.css'

...
// ./src/index.css

body {
    width: 100%;
    height: 200px;
  	background-image: linear-gradient(to right, red , yellow);
}
// ./package.json
{
    ...
    "browserslist": [
        "iOS >= 6",
        "Android >= 4",
        "IE >= 9"
    ]
}

browserslist 用来配置 我们的应用会在哪些平台、哪些浏览器和浏览器版本上使用。 autoprefixer 会读取该项值,用来觉得需要添加哪些前缀,如果运行在最新的浏览器上,也许不会添加相应的前置信息

browserslist 默认值:> 0.5%, last 2 versions, Firefox ESR, not dead

关于browserslist的更多信息

现在执行 npm run dev ,build 目录里没有新增 .css文件,用浏览器打开 ./build/index.html 文件,可以看见背景有个渐变色,从控制台里可以看见样式代码以 <style> 标签的形式嵌入到了html中,并且自带了厂商前缀

完成以上操作后,我们的目录就长成这样

.webpackLemo
│
│-- src 
│  │-- index.js
│  │-- index.css // 新增加的css代码
│
...

对less的支持 (非必要)

less其实就是css的预处理器,一些代码里可能会用sass,但是原理类似,这里就用less举例

在上一节的基础上,额外安装一个 less-loader 处理less文件

执行安装 npm i less-loader

然后修改文件

// ./config/webpack.config.js

...
module.exports = {
    ...
    module: {
        rules: [
            ...
            {
                test: /\.less$/,
                use: [
                    'style-loader',
                    'css-loader',
                    'less-loader',  // 只增加一行less-loader 处理less文件
                    {
                        loader: 'postcss-loader',
                        options: {
                            plugins: [
                                require('autoprefixer')
                            ]
                        }
                    }
                ]
            }
        ]
    }
}
// ./src/index.js

import './index.less' // 引入less文件

...
// ./src/index.less

body {
    width: 100%;
    height: 200px;
  	background-image: linear-gradient(to right, red , yellow);
}

执行 npm run dev ,得到的效果和处理css一样

完成以上操作后,我们的目录就长成这样

.webpackLemo
│
│-- src 
│  │-- index.js
│  │-- index.css
│  │-- index.less
│
...

对file文件的支持

其实只要是文件,都可以处理,这里用图片文件举例

可用2种 loader 进行处理

  • file-loader (github) 解析通过模块化引入的文件,在打包的时候会获取打包后的地址,并在代码中返回这个地址,使得文件可以被读取
  • url-loader (github) 作用同file-loader,但是能把文件压缩成base64的形式,一般用于图片

这两个loader作用比较类似,最大的区别在于 file-loader 会生成文件, url-loader 会把文件数据压缩成base64直接使用,不会生成文件。两者能一起使用,在某些场景下比使用单一loader效果更好,可参考 这篇博文(卫星)

这里我用file-loader举例

执行安装 npm i file-loader

然后修改文件

// ./config/webpack.config.js

...
module.exports = {
    ...
    module: {
        rules: [
            ...
            {
                test: /\.(png|svg|jpg|gif)$/,
                use: [
                    'file-loader'
                ]
            }
        ]
    }
}
// ./src/index.js

...
import img from '../statics/imgs/img.jpg'

const myimg = new Image();
myimg.src = img;
document.body.appendChild(myimg);
...

执行 npm run dev ,可以看见build文件里增加了打包后的图片文件,打开 ./build/index.html 后也能看到相应的图片被挂载到页面上

完成以上操作后,我们的目录就长成这样

.webpackLemo
│
│-- build 
│  │-- 051fa5032109fbc8005c856a48cf1c52.jpg
│  │-- index.html
│  │-- main.a14556a5d43741e69cdd.js
│
│-- statics // 存放静态资源
│  │-- imgs // 存放图片
│  │  │-- img.jpg
│
...

到现在,我们已经支持css打包和文件打包,自己写demo时,使用这样的webpack配置已经OK了,但是我每次改了代码都要重新执行一次 npm run dev ,真的太麻烦,如果能让webpack自动刷新页面就好了。

构建开发环境

让我们来构建一个开发环境,让我们每次更新的时候,webpack能自动帮我们打包,并且能实时刷新页面.

要能实现实时刷新页面,webpack提供了3种能力

  1. webpack watch mode webpack 观察模式,最简单的方法,但功能因此也少了很多
  2. webpack-dev-server 相对简单的web server,并且具备live reloading(实时重新加载) 功能
  3. webpack-dev-middleware 最复杂的一种,需要自己写服务配置,但可塑性也最高

关于dev-server的更多信息

关于3种配置的更多信息

这里我们采用 webpack-dev-server

执行安装 npm i webpack-dev-server

然后修改文件

// ./config/webpack.config.js

...
module.exports = {
    mode: 'development', // 设置mode为'development'模式
    devtool: 'cheap-module-eval-source-map', // 设置source-map
    devServer: {
        contentBase: './build',
        open: true
    },
    ...
}

mode:相当于设置一套预设优化配置,默认为 none。详细可查看 官网文档

devtool:用于配置source-map,用来增强调试能力,不同的值会影响构建和重构建的速度。 详细可查看 官方文档

// package.json

{
    ...
    "scripts": {
        // 把dev改成build
        "build": "webpack --config ./config/webpack.config.js",
        // 这里改成dev,并使用新命令项
        "dev": "webpack-dev-server --config ./config/webpack.config.js"
	},
	...
}

接下来执行 npm run dev ,webpack帮我们启动了一个端口号为8080的服务器,并自动打开了浏览器,我们修改代码,能自动帮我们打包新代码,并刷新页面。

总结

到这里,一个能打包、能处理css、处理文件、自启服务器、修改代码后能自动打包,自动刷新页面的配置就完成了。这样一个简单的配置已经实现个人写demo的需求,例如跑跑算法代码,测试一段代码的输出等等。

工具梳理

本章使用:

    • webpack、webpack-cli webpack工具库
    • webpack-dev-server webpack web-server
  1. loader

    • file-loader 解析通过模块化方式引入的文件,输出成文件
    • url-loader (非必要) 通过模块化方式引入的文件,以base64的形式输出
    • style-loader 将css代码以 <style> 标签的形式嵌入 html 中
    • css-loader 解析通过模块化引入的css文件
    • postcss-loader 提供预处理css的一些能力,拥有许多子插件,提供了许多能力
    • less-loader (非必要) 解析less代码文件
  2. plugins

    • clean-webpack-plugin 每次打包能清空打包文件夹里之前的内容
    • html-webpack-plugin 能自动生成Html文件,并自动引入打包生成的js文件
    • autoprefix postcss-loader的子插件,提供厂商前缀自动补全能力,如 -m-

文档梳理

尾声

本章节代码仓库代码仓库

本章节Github-blog: Github-blog

如果文中有错误/不足/需要改进/可以优化的地方,希望能在评论里友善提出,作者看到后会在第一时间里处理

如果你喜欢这篇文章,👍点个赞再走吧,github的星星⭐是对作者持续创作的支持❤️️