学一点Webpack配置:基本配置

特别声明:为感谢社区广大朋友对小站的支持,自2019年10月1日至2019年11月11日开通年费VIP通道,年费价格为 ¥299.00元。如果您喜欢小站的内容,可以点击开通会员进行全站阅读。如果您对付费阅读有任何建议或想法,欢迎发送邮件至: airenliao@gmail.com!(^_^)

这两天朋友圈流行这么一张图:

多么形象的展示了前端学习的曲线图。真可谓是一言难尽呀,现在的前端真不好学,乱而杂。如果你要是再看看@Kamran Ahmed整理的2017年2018年2019年现代Web开发者要掌握的Roadmap,估计更会泪崩:

点击这里可以查看大图

现状是如此,未来可能会更混乱,但我们不应该去抱怨,应该更应该保持一颗爱学习的心,继续往前行走。

学点Webpack配置方面的知识

Webpack是构建工具中必不可少的一部分:

作为现代Web开发者就需要对Webpack有所了解,哪怕掌握的不够深入,略知皮毛也对我们自己的工作或学习都是有所帮助的。比如说吧,前段时间折腾React环境下的CSS Modules,就是因为自己对Webpack不了解,有些坑踩了无法立刻解决,就算借助互联网,解的也是知半解(而且现在技术更新太快,网上有些教程根本走不通,不踩不知道,一踩只有泪)。正因为这个原因,促使自己去了解Webpack更多的知识。接下来的内容是一些基础,主要会介绍怎么用Webpack来构建自己的开发环境,感兴趣的请继续往下阅读。

Webpack是什么

一直以来,在我自己的印象和理解中,都认为Webpack是一个构建工具。主要用来构建开发的工程体系。但从其官网来看,告诉我Webpack是一个模块Bundler(捆绑器):

那么,Webpack到底是一个构建工具(或者说一个构建系统)还是一个模块Bundler(捆绑器)呢?答案是:

Webpack既是一个构建系统,也是一个捆绑器

Webpack不是先构建你的资源(Assets),然后再bundle你的模块,它把你的资源本身就当做是一个模块。这些模块可以被导入修改操作等,最后才被打包到你最后的bundle。

简单地说,Webpack其最核心的功能就是 解决模板之间的依赖,把各个模块按照特定的规则和顺序组织在一起,最终合并成一个JS文件(比如bundle.js。这个整个过程也常常被称为是模块打包。换句话说,Webpack是一个指令集合的配置文档,然后通过配置好的这些指令去驱动程序做一些指令要求要做的事情。而这些动作都是通过自己写的规则去做编译,而且通过JavaScript的引入(import)语法让Webpack知道需要它帮忙编译什么东西(比如Pug、Sass等等)。所以我们始终会有一个入口文件(比如index.js)注入那些Preprocess,让那些Preprocess可以通过这些入口文件的JavaScript让Webpack去根据相关的配置指令编译它,然后打包到一个出口文件中,比如bundles.js

为什么要用Webpack

一直以来,在开发Web页面或Web应用程序的时候,都习惯性的将不同资源放置在不同的文件目录之中,比如图片放置在images(或img)下,样式文件放置在styles(或css)中,脚本文件放在js和模板文件放置在pages中。一直以来,发布的时候都会一次性的将所有资源打包发布,不管这些资源用到了还是没用到(事实上很多时候自己都分不清楚哪资源被使用)。用一句话来描述就是:依赖太复杂,太混乱,无法维护和有效跟踪。比如哪个样式文件引用了a.img,哪个样式文件引用了b.img;另外页面到底是引用了a.css呢还是b.css呢?

而Webpack这样的工具却能很多好的解决它们之间的依赖关系,使其打包后的结果能运行在浏览器上。其目前的工作方式主要被分为两种:

  • 将存在依赖关系的模块按照特定规则合并成为单个.js文件,一次性全部加载进页面
  • 在页面初始时加载一个入口模块,其他模块异步加载

相比于Parcel、Rollup具有同等功能的工具而言,Webpack还具有其他的优势:

  • Webpack支持多种模块标准:这对于一些同时使用多种模块标准的工程非常有用,Webpack会帮我们处理好不同类型模块之间的依赖关系
  • Webpack有完备的代码分割解决方案:它可以分割打包后的资源,首屏只加载必要的部分,不太重要的功能放到后面动态地加载
  • Webpack可以处理各种类型的资源:除了JavaScript之外,Webpack还可以处理样式、模板、图片等资源。开发者要做的只是将之些资源导入,而无需关注其他

另外,Webpack还拥有一个强大的社区。这也是其受开发者青眯的原因之一。接下来,我们还是实际一点,动手来撸码。

从零开始构建你自己的开发环境

为了更好的理解Webpack能帮我们做什么,我打算从零开始构建一个属于自己的开发环境。可能在后面的内容中会涉及到很多关键词,比如Webpack、loaders、Babel、sourcemaps、React、TypeScript,CSS Modules等等。接下来一步一步的学习中会了解到这些单词和相关技术。

后面会一步一步的带大家如何使用Webpack配置适合自己的开发环境,会涉及到一些相关技术,但不会深入到具体技术细节中。

在写这篇文章所具备的环境是:Node是v10.9.0,NPM是v6.9.2,Webpack是v4.34.0,React是v16.8.6,TypeScript是v3.4.4,Sass是v.6.9.0,PostCSS是v.6.9.0等。接下来的内容会以配置React + TypeScript + CSS Modules + PostCSS为主线,从零开始一个项目。另外,接下来的内容会以不同分支的形式将代码放置在Github上。感兴趣的可以直接将仓库克隆下来,切换到对应步骤的分支,查看代码。

Step01:初始化项目

请将Git分支切换到step1分支查看代码

首先在你的本地创建一个项目,比如我这里创建了一个webpack-sample项目:

⇒ mkdir webpack-sample && cd webpack-sample

进入到新创建的项目目录下,执行npm init或者npm init -y命令来初始化项目,执行完该命令之后,在你的命令终端会看到类似下图这样的命令询问,你可以根据你自己的需要去输入你想要的内容,或者一路Enter键执行下去:

此时你的项目根目录下会增加一些文件和文件夹:

|--webpack-sample/
|----node_modules/
|----package.json
|----package-lock.json

其中package.json文件里将包含一些项目信息:

注意,这个文件随着后面的步骤完成,会增加更多的内容。

package-lock.json文件是当 node_modules/package.json 发生变化时自动生成的文件,它的主要功能是 确定当前安装的包的依赖,以便后续重新安装的时候生成相同的依赖,而忽略项目开发过程中有些依赖已经发生的更新

在Step01中,我们对package.json文件只做一个修改,删除"main": "index.js"入口,并添加"private":true选项,以便确保安装包是私有的,这样可以防止意外发布你的代码。

Step02:安装Webpack和初始配置Webpack

请将分支切换到step2查看代码

在这一步,先来安装Webpack。执行下面的命令安装Webpack配置所需要的包:

⇒ npm i webpack webpack-cli webpack-dev-server -D

此时打开package.json文件,你会发现在文件中有一个新增项 devDependencies

{
    // 其他项信息在这省略,详细请查看该文件

    "devDependencies": {
        "webpack": "^4.35.0",
        "webpack-cli": "^3.3.5",
        "webpack-dev-server": "^3.7.2"
    }
}

注意,在命令终端使用npm i安装依赖关系时,如果带后缀 -D(或--save-dev 安装的包会记录在"devDependencies"下;如果使用 --save后缀(我们后面会用到)安装的包会记录在"dependencies"下。两者的区别是:

  • "devDependencies"dev开发时的依赖包
  • dependencies:程序运行时的依赖包

为了验证Webpack是否能正常工作,这个时候我们需要创建一些新的文件。在webpack-sample根目录下创建/src目录,并且在该目录下创建一个index.js文件:

⇒ mkdir src && cd src && touch index.js

执行完上面的命令,你会发现你的项目目录结构变成下图这样:

我们在新创建的/src/index.js文件下添加一行最简单的JavaScript代码:

console.log("Hello, Webpack!(^_^)~~")

保存之后回到命令终端,第一次执行有关于Webpack相关的命令:

⇒ npx webpack src/index.js --output dist/bundle.js

执行完上面的命令后,如果看到下图这样的结果,那么要恭喜你,Webpack的安装已经成功,你可以在你的命令终端执行有关于Webpack相关的命令:

回到项目中,会发现项目根目下自动创建了一个/dist目录,而且该目录下包含了一个bundle.js文件:

执行完上面的命令之后,可以看到有相关的警告信息。那是因为Webpack4增加了mode属性,用来表示不同的环境。mode模式具有developmentproductionnone三个值,其默认值是production 。也就是说,在执行上面的命令的时候,我们可以带上相应的mode属性的值,比如说,设置none来禁用任何默认行为:

⇒  npx webpack src/index.js --output dist/bundle.js --mode none

执行到这里,只知道我们可以运行Webpack相关命令。并不知道/src/index.js的代码是否打包到/dist/bundle.js中。为此,我们可以在/dist目录下创建一个index.html

⇒  cd dist && touch index.html

并且将生成出来的bundle.js引入到新创建的index.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Hello Webpack (^_^) ~~~</title>
    </head>
    <body>
        <script src="./bundle.js"></script>
    </body>
</html>

在浏览器中打开/dist/index.html,或者在命令行中执行:

⇒  npm i -g http-server
⇒  http-server dist

http-server是一个启动服务器的npm包,执行上面的命令之后,就可以在浏览器中访问http://127.0.0.1:8080/(访问的/dist/index.html),在浏览器的console.log控制台中,可以看到src/index.js的脚本输出的值:

上面的过程足以验证,你的Webpack能正常的工作了。

不过,当你需要构建的东西越复杂,需要的标志就会越多。在某种程度上说,就会变得难以控制。这个时候我们就需要一个文件来管理这些配置。接下来我们需要创建一个webpack.config.js这样的一个文件,用来配置Webpack要做的事情。注意,这个文件是一个node.js文件,所以你可以在任何节点文件中执行任何你能够执行的操作。你也可以写成json文件,但是node文件更强大一些。

首先们先创建Webpack的配置文件,在webpack-sample根目录下创建一个/build目录,然后在该目录下添加一个名为webpack.config.js文件:

⇒  mkdir build && cd build && touch webpack.config.js

执行完上面的命令之后,你会发现你的项目文件目录结构变成下面这样了:

这个时候,新创建的webpack.config.js文件里面是一片空白,它就是Webpack的配置文件,将会导出一个对象的JavaScript文件。我们需要在这个文件中添加一些配置:

var webpack = require('webpack');
var path = require('path');
var DIST_PATH = path.resolve(__dirname, '../dist');  // 声明/dist的路径

module.exports = {
    // 入口JS路径
    // 指示Webpack应该使用哪个模块,来作为构建其内部依赖图的开始
    entry: path.resolve(__dirname,'../src/index.js'),


    // 编译输出的JS入路径 
    // 告诉Webpack在哪里输出它所创建的bundle,以及如何命名这些文件
    output: {
        path: DIST_PATH,        // 创建的bundle生成到哪里
        filename: 'bundle.js',    // 创建的bundle的名称
    },

    // 模块解析
    module: {

    },

    // 插件
    plugins: [

    ],

    // 开发服务器
    devServer: {

    }
}

Webpack配置是标准的 Node.js CommonJS模块,它通过require来引入其他模块,通过module.exports导出模块,由Webpack根据对象定义属性进行解析。

上面很简单,到目前为止只通过entry设置了入口起点,然后通过output配置了打包文件输出的目的地和方式。你可能也发现了,在配置文件中还有modulepluginsdevServer没有添加任何东西。不需要太急,后面会一步一步带着大家把这里的内容补全的,而且随着配置的东西越来越多,整个webpack.config.js也会更变越复杂。

完成webpack.config.js的基础配置之后,回到package.json文件,并在"scripts"下添加"build": "webpack --config ./build/webpack.config.js"

// package.json

{
    // ...

    "scripts": {
        "build": "webpack --config ./build/webpack.config.js",
        "test": "echo \"Error: no test specified\" && exit 1"
    },
}

这样做的,为让我们直接在命令终端执行相关的命令就可以实现相应的功能。比如上面配置的build,在命令终端执行:

⇒  npm run build

上面的命令执行的效果前面提到的npx webpack src/index.js --output dist/bundle.js --mode none等同。同样有警告信息,主要是mode的配置没有添加。在上面的配置中添加:

{
    "scripts": {
        "build": "webpack --config ./build/webpack.config.js --mode production",
        "test": "echo \"Error: no test specified\" && exit 1"
    },
}

再次执行npm run build,不会再有警告信息。你可以试着修改/src/index.js的代码:

alert(`Hello, Webpack! Let's Go`);

重新编译之后,打开/dist/index.html你会发现浏览器会弹出alert()框:

为了开发方便,不可能通过http-server来启用服务。我们可以把这部分事件放到开发服务器中来做,对应的就是devServer,所以我们接着在webpack.config.js中添加devServer相关的配置:

// webpack.config.js

// 开发服务器
devServer: {
    hot: true,                  // 热更新,无需手动刷新
    contentBase: DIST_PATH,     // 
    host: '0.0.0.0',            // host地址
    port: 8080,                 // 服务器端口
    historyApiFallback: true,   // 该选项的作用所用404都连接到index.html
    proxy: {
        "/api": "http://localhost:3000" // 代理到后端的服务地址,会拦截所有以api开头的请求地址
    }
}

有关于devServer更详细的配置参数描述,可以查阅读Webpack官网相关文档

build类似,需要在package.jsonscripts中添加相关的命令:

// package.json

"scripts": {
    "build": "webpack --config ./build/webpack.config.js --mode production",
    "dev": "webpack-dev-server --config ./build/webpack.config.js --mode development --open",
    "test": "echo \"Error: no test specified\" && exit 1"
},

保存所有文件,在命令行中执行npm run dev就可以启动服务器:

你可以验证一下,修改/src/index.js

document.addEventListener('DOMContentLoaded', () => {
    const h1Ele = document.createElement('h1')

    document.body.append(h1Ele);

    h1Ele.innerText = 'Hello Webpack (^_^)'

    h1Ele.style.color = '#f46';
})

保存该文件之后,浏览器会立刻刷新,你将看到修改之后的变化:

Step03: 优化Webpack配置

请将分支切换到step3查看代码

Step02中,开发和生产环境相关的配置都集成在webpack.config.js一个文件中。为了更好的维护代码,在Step03中做一些优化。把webpack.config.js拆分成三个部分:

  • 公共配置:把开发和生产环境需要的配置都集中到公共配置文件中,即webpack.common.js
  • 开发环境配置:把开发环境需要的相关配置放置到webpack.dev.js
  • 生产环境配置:把生产环境需要的相关配置放置到webpack.prod.js

先在/build目录下创建上面提到的三个配置文件。在命令终端执行下面的命令即可:

⇒  cd build && touch webpack.common.js webpack.dev.js webpack.prod.js

这个时候,整个项目目录结构变成下图这样:

Step02中遗留下来的webpack.config.js文件将会从/build目录中移除。

为了更好的管理和维护这三个文件,需要安装一个webpack-merge插件:

⇒  npm i webpack-merge -D

执行完上面的命令之后,package.json文件中的devDependencies会增加webpack-merge相关的配置:

// package.json

{
    //... 省略的信息请查看原文件
    "devDependencies": {
        "webpack": "^4.35.0",
        "webpack-cli": "^3.3.5",
        "webpack-dev-server": "^3.7.2",
        "webpack-merge": "^4.2.1"
    }
}

接下来分别给webpack.common.jswebpack.dev.jswebpack.prod.js文件添加相关的配置:

Webpack公共配置

在公共配置文件webpack.common.js文件中添加相应的配置:

const webpack = require('webpack');
const path =  require('path');
const DIST_PATH = path.resolve(__dirname, '../dist/');  // 声明/dist的路径

module.exports = {
    // 入口JS路径
    // 指示Webpack应该使用哪个模块,来作为构建其内部依赖图的开始
    entry: path.resolve(__dirname,'../src/index.js'),


    // 编译输出的JS入路径 
    // 告诉Webpack在哪里输出它所创建的bundle,以及如何命名这些文件
    output: {
        path: DIST_PATH,        // 创建的bundle生成到哪里
        filename: 'bundle.js',    // 创建的bundle的名称
    },

    // 模块解析
    module: {

    },

    // 插件
    plugins: [

    ]
}

Webpack开发环境配置

接着给Webpack开发环境配置文件webpack.dev.js添加下面的相关配置:

const webpack = require('webpack');
const path = require('path');
const merge = require('webpack-merge');

const commonConfig = require('./webpack.common.js');

const DIST_PATH = path.resolve(__dirname, '../dist/');  // 声明/dist的路径

module.exports = merge(commonConfig, {
    mode: 'development', // 设置webpack mode的模式

    // 开发环境下需要的相关插件配置
    plugins: [

    ],

    // 开发服务器
    devServer: {
        hot: true,                  // 热更新,无需手动刷新
        contentBase: DIST_PATH,     // 
        host: '0.0.0.0',            // host地址
        port: 8080,                 // 服务器端口
        historyApiFallback: true,   // 该选项的作用所用404都连接到index.html
        proxy: {
            "/api": "http://localhost:3000" // 代理到后端的服务地址,会拦截所有以api开头的请求地址
        }
    }
})

Webpack生产环境配置

继续给Webpack生产环境配置文件webpack.prod.js添加相关配置:

const webpack = require('webpack');
const path = require('path');
const merge = require('webpack-merge');

const commonConfig = require('./webpack.common.js');

module.exports = merge(commonConfig, {
    mode: 'production', // 设置Webpack的mode模式

    // 生产环境下需要的相关插件配置
    plugins: [

    ],
})

上面的配置信息只是将Step02webpack.config.js分成三个文件来配置,随着后续添加相应的配置信息,那么这三个文件中的配置信息会越来越多,也会越来越复杂。

修改完Webpack的配置之后,对应的package.json中的scripts中的信息也要做相应的调整:

// package.json
{
    // ... 其他配置信息请查看原文件
    "scripts": {
        "build": "webpack --config ./build/webpack.prod.js --mode production",
        "dev": "webpack-dev-server --config ./build/webpack.dev.js --mode development --open",
        "test": "echo \"Error: no test specified\" && exit 1"
    },
}

这个时候重新在命令终端执行

// 执行build命令,重新打包
⇒  npm run build

// 执行dev命令
⇒  npm run dev

这仅仅是最基础部分的优化,因为我们的配置还是最简单的,后续我们添加了别的配置之后,也会在相应的步骤做相应的优化。

Step04: 配置React开发环境

请将分支切换到step4查看代码

经过前面三步,我们完成了Webpack的基本配置,知道文件入口,出口,打包以及开发,生产等环境。接下来,我们来给工程配置React相关的环境。

React的环境需要先安装reactreact-dom。所以先在命令终端中执行下面的命令:

⇒  npm i react react-dom --save

执行完上面的命令之后,在package.json文件中的dependencies增加了ractreact-dom相应的信息:

// package.json
{
    "dependencies": {
        "react": "^16.8.6",
        "react-dom": "^16.8.6"
    },
}

为了验证React相关的环境是否能正常工作,将/src/index.js中的内容做一些修改:

// /src/index.js

import React from 'react';
import ReactDOM from 'react-dom';

import App from './components/App'

ReactDOM.render(<App />, document.getElementById('root'))

在这个index.js中引用了App组件。所以我们在/src目录下新增components/目录,并在该目录下新增App.js

// src/components/App.js

import React from 'react';

export default class App extends React.Component {
    render() {
        return (
            <h1>Hello Webpack and React! (^_^)</h1>
        )
    }
}

另外在/src/新增一个模板文件index.html

<!-- /src/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Hello Webpack (^_^) ~~~</title>
</head>
<body>
    <div id="root"></div>
</body>
</html>

这个时候你在命令终端不管是执行npm run build还是npm run dev都无法正常运行,会报错:

首先我要告诉你的是Step04这一步的操作并没有任何问题,主要是在编译的过程中缺少必要的东西。那就是Babel相关的配置。接下来的Step05将会添加Babel相关的配置。

Step05:添加Babel相关的配置

请将分支切换到step5查看代码

Step04中会失败主要是因为Webpack只识别JavaScript文件,而且只能编译ES5。实际上ES6(甚至后面要说的JSX),Webpack它根本不认识。那么要解决这个问题,就需要借助Babel来处理。先安装需要的插件:

⇒  npm i babel-loader @babel/core @babel/preset-env @babel/plugin-transform-runtime @babel/plugin-transform-modules-commonjs @babel/preset-react -D

⇒  npm i @babel/runtime --save

执行完上面的命令之后,package.json文件在dependenciesdevDependencies添加了新的配置信息:

// package.json

{
    // ...省略的信息可以查看原文件

    "dependencies": {
        "@babel/runtime": "^7.4.5",
        "react": "^16.8.6",
        "react-dom": "^16.8.6"
    },
    "devDependencies": {
        "@babel/core": "^7.4.5",
        "@babel/plugin-transform-modules-commonjs": "^7.4.4",
        "@babel/plugin-transform-runtime": "^7.4.4",
        "@babel/preset-env": "^7.4.5",
        "@babel/preset-react": "^7.0.0",
        "babel-loader": "^8.0.6",
        "webpack": "^4.35.0",
        "webpack-cli": "^3.3.5",
        "webpack-dev-server": "^3.7.2",
        "webpack-merge": "^4.2.1"
    }
}

接着在webpack-sample根目录下创建.babelrc文件来配置Babel:

{
    "presets": [
        [
            "@babel/preset-env",
            {
                "targets": {
                    "browsers": [
                        "> 1%",
                        "last 5 versions",
                        "ie >= 8"
                    ]
                }
            }
        ],
        "@babel/preset-react"
    ],
    "plugins": [
        "@babel/plugin-transform-runtime",
        "@babel/plugin-transform-modules-commonjs"
    ]
}

有关于Babel更详细的配置可以点击这里阅读,这里不再做过多的阐述。

最后在webpack.common.js配置文件中的module中添加rules来处理.js.jsx文件,这也是我们添加的第一个有关于Webpack的Loader相关的东西:

// webpack.common.js

module.exports = {
    // ... 省略的信息查看原文件代码

    // 模块解析
    module: {
        rules: [
            {
                test: /\.(js|jsx)$/,
                exclude: /node_modules/,
                use: {
                    loader: "babel-loader"
                }
            }
        ]
    },
}

这个时候执行npm run build可以正常的执行,但是执行npm run dev还是会报错:

执行npm run dev不成功是因为我们的模板文件没有自动插入到/dist目录下。为了让/src/目录下的模板文件index.html能自动编译到/dist目录下,并且所有的.js引用能自动插入到index.html中。我们需要使用Webpack的两个插件:

  • 生成index.html文件,并且自动插入到/d
剩余80%内容付费后可查看

如需转载,烦请注明出处:https://www.w3cplus.com/javascript/webpack-config-part1.html

如果文章中有不对之处,烦请各位大神拍正。如果你觉得这篇文章对你有所帮助,打个赏,让我有更大的动力去创作。(^_^)。看完了?还不过瘾?点击向作者提问!

赏杯咖啡,鼓励他创作更多优质内容!
返回顶部