webpack4.x最详细入门指南

栏目: 编程工具 · 发布时间: 6年前

内容简介:本文主要从webpack4.x入手,会对平时常用的Webpack配置一一讲解,各个功能点都有对应的详细例子,所以本文也比较长,但如果你能动手跟着本文中的例子完整写一次,相信你会觉得Webpack也不过如此。简单来说,它其实就是一个

前言

本文主要从webpack4.x入手,会对平时常用的Webpack配置一一讲解,各个功能点都有对应的详细例子,所以本文也比较长,但如果你能动手跟着本文中的例子完整写一次,相信你会觉得Webpack也不过如此。

一、什么是webpack,为什么使用它?

webpack4.x最详细入门指南

1.1 什么是webpack?

简单来说,它其实就是一个 模块打包器

1.2 为什么使用它?

如果像以前开发时一个html文件可能会引用十几个js文件,而且顺序还不能乱,因为它们存在依赖关系,同时对于ES6+等新的语法,less, sass等CSS预处理都不能很好的解决……,此时就需要一个处理这些问题的工具。

Webpack就是为处理这些问题而生的,它就是把你的项目当成一个整体,通过一个入口主文件(如:index.js),从这个文件开始找到你的项目所有的依赖文件并处理它们,最后打包成一个(或多个)浏览器可识别的JavaScript文件。

二、一个简单的打包例子

2.1 准备工作

首先新建一个空文件夹,用于创建项目,在终端中进入文件夹,如下我在桌面建了一个名为webpack-project的文件夹,使用终端进入文件夹后(如果对命令行不太熟悉,可参考我的博客: 前端常用命令行 ),使用 npm init 命令创建一个package.json文件。

npm init

webpack4.x最详细入门指南

输入这个命令后,终端会问你一系列诸如项目名称,项目描述,作者等信息,不过如果你不打算发布这个模块,直接一路回车就好。(也可以使用 npm init -y 这个命令来一次生成package.json文件,这样终端不会询问你问题)。

2.2 安装webpack

如果你想一步到位的话,就把全局webpack和本地项目webpack全都先装了,因为后面一些模块会用到。安装本地项目webapck时把webpack-cli也装上,因为webpack模块把一些功能分到了webpack-cli模块,安装方法如下:

npm install webpack --global                //这是安装全局webpack命令
npm install webpack webpack-cli --save-dev  //这是安装本地项目模块

tips:

上述命令可采用简写, install 可简写为 i , --global 可简写为 -g , --save-dev 可简写为 -D (这个命令是用于把配置添加到package.json的开发环境配置列表中,后面会提到), --save 可简写为 -S ,同时国内我们可以采用cnpm,配置方法可 去这里 查看,这样安装速度会相对较快。如下:

cnpm i webpack -g               //这是安装全局webpack命令
cnpm i webpack webpack-cli -D   //这是安装本地项目模块

2.3 新建文件

webpack-project 文件夹中新建两个文件夹,分别为src文件夹和dist文件夹,接下来再创建三个文件:

  • index.html --放在dist文件夹中;
  • hello.js --放在src文件夹中;
  • index.js --放在src文件夹中;
     
    此时,项目结构如下:
     
    webpack4.x最详细入门指南  

我们在 index.html 中写下 html 代码,它的作用是为了引入我们打包后的js文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Webpack Project</title>
</head>
<body>
    <div id='root'></div>
    <script src="bundle.js"></script>   <!--这是打包之后的js文件,我们暂时命名为bundle.js-->
</body>
</html>

我们在 hello.js 中导出一个模块:

// hello.js
module.exports = function() {
    let hello = document.createElement('div');
    hello.innerHTML = "Long time no see!";
    return hello;
  };

然后在 index.js 中引入这个模块( hello.js ):

//index.js 
const hello = require('./hello.js');
document.querySelector("#root").appendChild(hello());

上述操作就相当于我们把 hello.js 模块合并到了 index.js 模块,之后我们打包时就只需把 index.js 模块打包成 bundle.js ,然后供 index.html 引用即可,这就是最简单的webpack打包原理。

2.4 开始进行webpack打包

在终端中使用如下命令进行打包:

// webpack全局安装的情况下
webpack src/index.js --output dist/bundle.js  
// --output可简写为-o

上述就相当于把 src 文件夹下的 index.js 文件打包到 dist 文件下的 bundle.js ,这时就生成了 bundle.jsindex.html 文件引用。

结果如下:

webpack4.x最详细入门指南 可以看出webpack同时编译了 index.jshello.js ,现在打开 index.html ,可以看到如下结果:

webpack4.x最详细入门指南

没错,我们已经成功使用webpack进行打包,原来webpack也不过如此嘛!但是,每次都在终端中输入这么长的命令,感觉好烦啊,还好有懒人方法,让我们看看。

2.5 通过配置文件来使用webpack

其实webpack是有很多功能的,也是很方便的,我们可以在当前项目的根目录下新建一个配置文件 webpack.config.js ,我们写下如下简单配置代码,目前只涉及入口配置(相当于我们的 index.js ,从它开始打包)和出口配置(相当于我们打包生成的 bundle.js )。

// webpack.config.js
module.exports = {
    entry: __dirname + "/src/index.js", // 入口文件
    output: {
        path: __dirname + "/dist", //打包后的文件存放的地方
        filename: "bundle.js" //打包后输出文件的文件名
    }
}
 

注: __dirname 是node.js中的一个全局变量,它指向当前执行脚本所在的目录,即C:\Users\sjt\DeskTop\webpack-project(这是我当前的目录)

但平时我们看到的脚手架配置也比较喜欢采用node.js的 path 模块来处理绝对路径,所以我们也可以采用如下的写法,和上述的效果是一样的:

// webpack.config.js
const path = require('path');
module.exports = {
    entry: path.join(__dirname, "/src/index.js"), // 入口文件
    output: {
        path: path.join( __dirname, "/dist"), //打包后的文件存放的地方
        filename: "bundle.js" //打包后输出文件的文件名
    }
}

注: path.join 的功能是拼接路径片段。

有了这个配置文件,我们只需在终端中运行 webpack 命令就可进行打包,这条命令会自动引用webpack.config.js文件中的配置选项,示例如下:

webpack4.x最详细入门指南

搞定,是不是这样更方便了,感觉没那么low了,但还能不能更便捷智能呢?那必须的!

2.6 更智能的打包方式

我们现在只在终端中使用 webpack 命令来进行打包,要是以后在打包的同时还有更多的操作呢,那不是还得写上更多的命令?所以我们得想办法把这些命令都集成起来,这时候之前的 package.json 文件就派上用场了。

现在的package.json文件大概就是如下这样:

{
  "name": "webpack-project",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1" //我们要修改的是这里,JSON文件不支持注释,引用时请清除
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "webpack": "^4.23.1",
    "webpack-cli": "^3.1.2"
  }
}

修改如下:

{
  "name": "webpack-project",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "webpack", //改成这样,注意使用时把注释删掉
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "webpack": "^4.23.1",
    "webpack-cli": "^3.1.2"
  }
}

注:package.json中的script会按你设置的命令名称来执行对应的命令。

这样我们就可以在终端中直接执行 npm start 命令来进行打包, start 命令比较特殊,可以直接 npm 加上 start 就可以执行,如果我们想起其他的名称,如 build 时,就需要使用 npm run 加上 build ,即 npm run build 命令。

现在我们执行 npm start 命令:

webpack4.x最详细入门指南

OK,搞定,是不是很简单,但webpack的功能远不止于此,下面我们继续。

三、构建本地服务器

现在我们是通过打开本地文件来查看页面的,看起来总感觉比较low,看别人用vue,react框架时都是运行在本地服务器上的,那我们能不能也那样呢?那必须的!

3.1 webpack-dev-server配置本地服务器

Webpack提供了一个可选的本地开发服务器,这个本地服务器基于node.js构建,它是一个单独的组件,在webpack中进行配置之前需要单独安装它作为项目依赖:

cnpm i webpack-dev-server -D

devServer作为webpack配置选项中的一项,以下是它的一些配置选项:

  • contentBase :设置服务器所读取文件的目录,当前我们设置为"./dist"
  • port :设置端口号,如果省略,默认为 8080
  • inline :设置为 true ,当源文件改变时会自动刷新页面
  • historyApiFallback :设置为 true ,所有的跳转将指向 index.html
     
    现在我们把这些配置加到 webpack.config.js 文件上,如下:
     
    // webpack.config.js
    const path = require('path');
    module.exports = {
    entry: path.join(__dirname, "/src/index.js"), // 入口文件
    output: {
        path: path.join( __dirname, "/dist"), //打包后的文件存放的地方
        filename: "bundle.js" //打包后输出文件的文件名
    },
    devServer: {
        contentBase: "./dist", // 本地服务器所加载文件的目录
        port: "8088",   // 设置端口号为8088
        inline: true, // 文件修改后实时刷新
        historyApiFallback: true, //不跳转
    }
    }

    我们继续在 package.json 文件中添加启动命令:

    {
    "name": "webpack-project",
    "version": "1.0.0",
    "description": "",
    "main": "index.js",
    "scripts": {
    "build": "webpack",
    "dev": "webpack-dev-server --open"
    },
    "keywords": [],
    "author": "",
    "license": "ISC",
    "devDependencies": {
    "webpack": "^4.23.1",
    "webpack-cli": "^3.1.2",
    "webpack-dev-server": "^3.1.10"
    }
    }

    我们把 start 命令名称改为了 build ,这样比较语义化,平时的脚手架也多数采用这个名称,我们用 dev (development的缩写,意指开发环境)来启动本地服务器, webpack-dev-server 就是启动服务器的命令, --open 是用于启动完服务器后自动打开浏览器,这时候我们自定义命令方式的便捷性就体现出来了,可以多个命令集成在一起运行,即我们定义了一个 dev 命令名称就可以同时运行了 webpack-dev-server--open 两个命令。

    现在在终端输入 npm run dev 运行服务器:

    webpack4.x最详细入门指南

这样我们即可在http://localhost:8088/中查看页面(退出服务器,可使用 ctrl+c 后,再按 y 确认,即可退出服务器运行)

3.2 Source Maps调试配置

作为开发,代码调试当然少不了,那么问题来了,经过打包后的文件,你是不容易找到出错的地方的, Source Map 就是用来解决这个问题的。

通过如下配置,我们会在打包时生成对应于打包文件的 .map 文件,使得编译后的代码可读性更高,更易于调试。

// webpack.config.js
const path = require('path');
module.exports = {
    entry: path.join(__dirname, "/src/index.js"), // 入口文件
    output: {
        path: path.join( __dirname, "/dist"), //打包后的文件存放的地方
        filename: "bundle.js" //打包后输出文件的文件名
    },
    devServer: {
        contentBase: "./dist", // 本地服务器所加载文件的目录
        port: "8088",  // 设置端口号为8088
        inline: true, // 文件修改后实时刷新
        historyApiFallback: true, //不跳转
    },
    devtool: 'source-map'  // 会生成对于调试的完整的.map文件,但同时也会减慢打包速度
}

配置好后,我们再次运行 npm run build 进行打包,这时我们会发现在 dist 文件夹中多出了一个 bundle.js.map 文件如下:

webpack4.x最详细入门指南

如果我们的代码有bug,在浏览器的调试 工具 中会提示错误出现的位置,这就是 devtool: 'source-map' 配置项的作用。

四、Loaders

loaders是webpack最强大的功能之一,通过不同的loader,webpack有能力调用外部的脚本或工具,实现对不同格式的文件的处理,例如把 scss 转为 css ,将ES66、ES7等语法转化为当前浏览器能识别的语法,将JSX转化为js等多项功能。

Loaders需要单独安装并且需要在 webpack.config.js 中的 modules 配置项下进行配置,Loaders的配置包括以下几方面:

  • test :一个用以匹配loaders所处理文件的拓展名的正则表达式(必须)
  • loader :loader的名称(必须)
  • include/exclude :手动添加必须处理的文件(文件夹)或屏蔽不需要处理的文件(文件夹)(可选);
  • options :为loaders提供额外的设置选项(可选)
     
     

    4.1 配置css-loader

    如果我们要加载一个css文件,需要安装配置 style-loadercss-loader :

    cnpm i style-loader css-loader -D
    // webpack.config.js
    const path = require('path');
    module.exports = {
    entry: path.join(__dirname, "/src/index.js"), // 入口文件
    output: {
        path: path.join( __dirname, "/dist"), //打包后的文件存放的地方
        filename: "bundle.js" //打包后输出文件的文件名
    },
    devServer: {
        contentBase: "./dist", // 本地服务器所加载文件的目录
        port: "8088",  // 设置端口号为8088
        inline: true, // 文件修改后实时刷新
        historyApiFallback: true, //不跳转
    },
    devtool: 'source-map',  // 会生成对于调试的完整的.map文件,但同时也会减慢打包速度
    module: {
        rules: [
            {
                test: /\.css$/,   // 正则匹配以.css结尾的文件
                use: ['style-loader', 'css-loader']  // 需要用的loader,一定是这个顺序,因为调用loader是从右往左编译的
            }
        ]
    }
    }

    我们在src文件夹下新建 css 文件夹,该文件夹内新建 style.css 文件:

    /* style.css */
    body {
    background: gray;
    }

    index.js 中引用它:

    //index.js 
    import './css/style.css';  //导入css

const hello = require('./hello.js');

document.querySelector("#root").appendChild(hello());

  
这时我们运行`npm run dev`,会发现页面背景变成了灰色。
  
如果是要编译sass文件呢?
    
#### 4.2 配置sass
 

cnpm i sass-loader node-sass -D // 因为sass-loader依赖于node-sass,所以还要安装node-sass

增加sass的rules:
  
```js
// webpack.config.js
const path = require('path');
module.exports = {
    entry: path.join(__dirname, "/src/index.js"), // 入口文件
    output: {
        path: path.join( __dirname, "/dist"), //打包后的文件存放的地方
        filename: "bundle.js" //打包后输出文件的文件名
    },
    devServer: {
        contentBase: "./dist", // 本地服务器所加载文件的目录
        port: "8088",  // 设置端口号为8088
        inline: true, // 文件修改后实时刷新
        historyApiFallback: true, //不跳转
    },
    devtool: 'source-map',  // 会生成对于调试的完整的.map文件,但同时也会减慢打包速度
    module: {
        rules: [
            {
                test: /\.css$/,   // 正则匹配以.css结尾的文件
                use: ['style-loader', 'css-loader']  // 需要用的loader,一定是这个顺序,因为调用loader是从右往左编译的
            },
            {
                test: /\.(scss|sass)$/,   // 正则匹配以.scss和.sass结尾的文件
                use: ['style-loader', 'css-loader', 'sass-loader']  // 需要用的loader,一定是这个顺序,因为调用loader是从右往左编译的
            }
        ]
    }
}

在css文件夹中新建 blue.scss 文件:

/* blue.scss */
$blue: blue;
body{
    color: $blue;
}

index.js 中引入 blue.scss

//index.js 
import './css/style.css';   // 导入css
import './css/blue.scss';   // 导入scss

const hello = require('./hello.js');
document.querySelector("#root").appendChild(hello());

这时 npm run dev 重新启动服务器,应该会出现如下结果:

webpack4.x最详细入门指南

还有诸如图片loader、字体loader等就不一一列出来了,感兴趣的可前往 webpack官网 查看,都是一样的套路。

五、Babel

Babel其实是一个编译JavaScript的平台,它可以编译代码帮你达到以下目的:

  • 让你能使用最新的JavaScript代码(ES6,ES7...),而不用管新标准是否被当前使用的浏览器完全支持;
  • 让你能使用基于JavaScript进行了拓展的语言,比如React的JSX;
       

    5.1 Babel的安装与配置

    Babel其实是几个模块化的包,其核心功能位于称为 babel-core 的npm包中,webpack可以把其不同的包整合在一起使用,对于每一个你需要的功能或拓展,你都需要安装单独的包(用得最多的是解析ES6的 babel-preset-env 包和解析JSX的 babel-preset-react 包)。

    cnpm i babel-core babel-loader babel-preset-env babel-preset-react -D
    // babel-preset-env的env表示是对当前环境的预处理,而不是像以前使用babel-preset-es2015只能针对某个环境
    // webpack.config.js
    const path = require('path');
    module.exports = {
    entry: path.join(__dirname, "/src/index.js"), // 入口文件
    output: {
        path: path.join( __dirname, "/dist"), //打包后的文件存放的地方
        filename: "bundle.js" //打包后输出文件的文件名
    },
    devServer: {
        contentBase: "./dist", // 本地服务器所加载文件的目录
        port: "8088",  // 设置端口号为8088
        inline: true, // 文件修改后实时刷新
        historyApiFallback: true, //不跳转
    },
    devtool: 'source-map',  // 会生成对于调试的完整的.map文件,但同时也会减慢打包速度
    module: {
        rules: [
            {
                test: /\.css$/,   // 正则匹配以.css结尾的文件
                use: ['style-loader', 'css-loader']  // 需要用的loader,一定是这个顺序,因为调用loader是从右往左编译的
            },
            {
                test: /\.(scss|sass)$/,   // 正则匹配以.scss和.sass结尾的文件
                use: ['style-loader', 'css-loader', 'sass-loader']  // 需要用的loader,一定是这个顺序,因为调用loader是从右往左编译的
            },
            {                             // jsx配置
                test: /(\.jsx|\.js)$/,   
                use: {                    // 注意use选择如果有多项配置,可写成这种对象形式
                    loader: "babel-loader",
                    options: {
                        presets: [
                            "env", "react"
                        ]
                    }
                },
                exclude: /node_modules/
            }
        ]
    }
    }

    现在我们已经可以支持ES6及JSX的语法了,我们用react来试试,但使用react还得先安装两个模块 reactreact-dom

    cnpm i react react-dom -D

    接下来我们把 hello.js 文件修改一下:

// hello.js
import React, {Component} from 'react'; // 这两个模块必须引入

let name = Alan;

export default class Hello extends Component{
    render() {
        return (
            <div>
                {name}
            </div>
        );
    }
}

修改 index.js 文件:

//index.js 
import './css/style.css';  // 导入css
import './css/blue.scss';  // 导入scss

import React from 'react';
import {render} from 'react-dom';
import Hello from './hello'; // 可省略.js后缀名

render(<Hello />, document.getElementById('root'));

此时运行 npm run dev 后你可能会发现如下结果:

webpack4.x最详细入门指南

这是因为官方默认 babel-loader | babel 对应的版本需要一致: 即 babel-loader 需要搭配最新版本 babel ,详细可参考 这篇博客

两种解决方案:

  • 回退低版本

    cnpm i babel-loader@7 babel-core babel-preset-env -D
  • 更新到最高版本:
    cnpm i babel-loader @babel/core @babel/preset-env webpack -D

    我这里采取的是第一个方案,回退后,再此运行 npm run dev ,得到如下结果:

webpack4.x最详细入门指南

到这里了是不是感觉很爽,不就是配置嘛,想要使用什么就配置什么。

5.2 优化babel配置

虽然babel完全可以在 webpack.config.js 中进行配置,但现在不是都提倡模块化嘛,也许之后babel膨胀了,增加了更多的配置项呢?

那我们不如把它提取出来,把它放到根目录下的 .babelrc 文件下(webpack会自动调用 .babelrc 里的babel配置选项)。

我们在项目根目录下新建 .babelrc 文件:

webpack4.x最详细入门指南

// webpack.config.js
const path = require('path');
module.exports = {
    entry: path.join(__dirname, "/src/index.js"), // 入口文件
    output: {
        path: path.join( __dirname, "/dist"), //打包后的文件存放的地方
        filename: "bundle.js" //打包后输出文件的文件名
    },
    devServer: {
        contentBase: "./dist", // 本地服务器所加载文件的目录
        port: "8088",  // 设置端口号为8088
        inline: true, // 文件修改后实时刷新
        historyApiFallback: true, //不跳转
    },
    devtool: 'source-map',  // 会生成对于调试的完整的.map文件,但同时也会减慢打包速度
    module: {
        rules: [
            {
                test: /\.css$/,   // 正则匹配以.css结尾的文件
                use: ['style-loader', 'css-loader']  // 需要用的loader,一定是这个顺序,因为调用loader是从右往左编译的
            },
            {
                test: /\.(scss|sass)$/,   // 正则匹配以.scss和.sass结尾的文件
                use: ['style-loader', 'css-loader', 'sass-loader']  // 需要用的loader,一定是这个顺序,因为调用loader是从右往左编译的
            },
            {                             // jsx配置
                test: /(\.jsx|\.js)$/,   
                use: {                    // 注意use选择如果有多项配置,可写成这种对象形式
                    loader: "babel-loader"
                },
                exclude: /node_modules/   // 排除匹配node_modules模块
            }
        ]
    }
}
// .babelrc 使用时把注释删掉,该文件不能添加注释
{
    "presets": ["env", "react"]
}

此时不出问题的话应该一切运行正常,接下来让我们进入强大的插件模块。

六、插件(Plugins)

插件(Plugins)是用来拓展Webpack功能的,它们会在整个构建过程中生效,执行相关的任务。

Loaders和Plugins常常被弄混,但是他们其实是完全不同的东西,可以这么来说,loaders是在打包构建过程中用来处理源文件的(JSX,Scss,Less..),一次处理一个,插件并不直接操作单个文件,它直接对整个构建过程其作用。

6.1 插件如何使用

使用某个插件,需要通过 npm 进行安装,然后在 webpack.config.js 配置文件的 plugins (是一个数组)配置项中添加该插件的实例,下面我们先来使用一个简单的版权声明插件。

// webpack.config.js
const webpack = require('webpack');  // 这个插件不需要安装,是基于webpack的,需要引入webpack模块

module.exports = {
    ...
    module: {
        rules: [
            {
                test: /\.css$/,   // 正则匹配以.css结尾的文件
                use: ['style-loader', 'css-loader']  // 需要用的loader,一定是这个顺序,因为调用loader是从右往左编译的
            },
            {
                test: /\.(scss|sass)$/,   // 正则匹配以.scss和.sass结尾的文件
                use: ['style-loader', 'css-loader', 'sass-loader']  // 需要用的loader,一定是这个顺序,因为调用loader是从右往左编译的
            },
            {                             // jsx配置
                test: /(\.jsx|\.js)$/,   
                use: {                    // 注意use选择如果有多项配置,可写成这种对象形式
                    loader: "babel-loader"
                },
                exclude: /node_modules/   // 排除匹配node_modules模块
            }
        ]
    },
    plugins: [
        new webpack.BannerPlugin('版权所有,翻版必究')  // new一个插件的实例 
    ]
}

运行 npm run build 打包后我们看到 bundle.js 文件显示如下:

webpack4.x最详细入门指南

6.2 自动生成html文件(HtmlWebpackPlugin)

到目前为止我们都是使用一开始建好的 index.html 文件,而且也是手动引入 bundle.js ,要是以后我们引入不止一个js文件,而且更改js文件名的话,也得手动更改 index.html 中的js文件名,所以能不能自动生成 index.html 且自动引用打包后的js呢? HtmlWebpackPlugin 插件就是用来解决这个问题的:

首先安装该插件

cnpm i html-webpack-plugin -D

然后我们对项目结构进行一些更改:

  1. dist 整个文件夹删除;
  2. src 文件夹下新建一个 index.template.html (名称自定义)文件模板(当然这个是可选的,因为就算不设置模板, HtmlWebpackPlugin 插件也会生成默认 html 文件,这里我们设置模块会让我们的开发更加灵活),如下:
     
    <!-- index.template.html -->
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="utf-8">
    <title>Here is Template</title>
    </head>
    <body>
    <div id='root'>
    </div>
    </body>
    </html>

    webpack.config.js 中我们引入了 HtmlWebpackPlugin 插件,并配置了引用了我们设置的模板,如下:

// webpack.config.js
const path = require('path');  // 路径处理模块
const webpack = require('webpack');  // 这个插件不需要安装,是基于webpack的,需要引入webpack模块
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 引入HtmlWebpackPlugin插件

module.exports = {
    entry: path.join(__dirname, "/src/index.js"), // 入口文件
    output: {
        path: path.join( __dirname, "/dist"), //打包后的文件存放的地方
        filename: "bundle.js" //打包后输出文件的文件名
    },
    devServer: {
        contentBase: "./dist", // 本地服务器所加载文件的目录
        port: "8088",  // 设置端口号为8088
        inline: true, // 文件修改后实时刷新
        historyApiFallback: true, //不跳转
    },
    devtool: 'source-map',  // 会生成对于调试的完整的.map文件,但同时也会减慢打包速度
    module: {
        rules: [
            {
                test: /\.css$/,   // 正则匹配以.css结尾的文件
                use: ['style-loader', 'css-loader']  // 需要用的loader,一定是这个顺序,因为调用loader是从右往左编译的
            },
            {
                test: /\.(scss|sass)$/,   // 正则匹配以.scss和.sass结尾的文件
                use: ['style-loader', 'css-loader', 'sass-loader']  // 需要用的loader,一定是这个顺序,因为调用loader是从右往左编译的
            },
            {                             // jsx配置
                test: /(\.jsx|\.js)$/,   
                use: {                    // 注意use选择如果有多项配置,可写成这种对象形式
                    loader: "babel-loader"
                },
                exclude: /node_modules/   // 排除匹配node_modules模块
            }
        ]
    },
    plugins: [
        new webpack.BannerPlugin('版权所有,翻版必究'),  // new一个插件的实例 
        new HtmlWebpackPlugin({
            template: path.join(__dirname, "/src/index.template.html")// new一个这个插件的实例,并传入相关的参数
        })
    ]
}

然后我们使用 npm run build 进行打包,你会发现, dist 文件夹和 html 文件都会自动生成,如下:

webpack4.x最详细入门指南

为什么会自动生成 dist 文件夹呢?因为我们在 output 出口配置项中定义了出口文件所在的位置为 dist 文件夹,且出口文件名为 bundle.js ,所以 HtmlWebpackPlugin 会自动帮你在 index.html 中引用名为 bundle.js 文件,如果你在 webpack.config.js 文件中更改了出口文件名, index.html 中也会自动更改该文件名,这样以后修改起来是不是方便多了?

6.3 清理 /dist 文件夹(CleanWebpackPlugin)

你可能已经注意到,在我们删掉 /dist 文件夹之前,由于前面的代码示例遗留,导致我们的 /dist 文件夹比较杂乱。 webpack 会生成文件,然后将这些文件放置在 /dist 文件夹中,但是 webpack 无法追踪到哪些文件是实际在项目中用到的。

通常,在每次构建前清理 /dist 文件夹,是比较推荐的做法,因此只会生成用到的文件,这时候就用到 CleanWebpackPlugin 插件了。

cnpm i clean-webpack-plugin -D
// webpack.config.js
...
const CleanWebpackPlugin = require('clean-webpack-plugin'); // 引入CleanWebpackPlugin插件

module.exports = {
    ...
    plugins: [
        new webpack.BannerPlugin('版权所有,翻版必究'),  // new一个插件的实例 
        new HtmlWebpackPlugin({
            template: path.join(__dirname, "/src/index.template.html")// new一个这个插件的实例,并传入相关的参数
        }),
        new CleanWebpackPlugin(['dist']),  // 所要清理的文件夹名称
    ]
}
 

插件的使用方法都是一样的,首先引入,然后new一个实例,实例可传入参数。

现在我们运行 npm run build 后就会发现,webpack会先将 /dist 文件夹删除,然后再生产新的 /dist 文件夹。

6.4 热更新(HotModuleReplacementPlugin)

HotModuleReplacementPlugin (HMR)是一个很实用的插件,可以在我们修改代码后自动刷新预览效果。

方法:

  1. devServer 配置项中添加 hot: true 参数。
  2. 因为 HotModuleReplacementPluginwebpack 模块自带的,所以引入 webpack 后,在 plugins 配置项中直接使用即可。 
// webpack.config.js
...
const webpack = require('webpack');  // 这个插件不需要安装,是基于webpack的,需要引入webpack模块

module.exports = {
    ...
    devServer: {
        contentBase: "./dist", // 本地服务器所加载文件的目录
        port: "8088",  // 设置端口号为8088
        inline: true, // 文件修改后实时刷新
        historyApiFallback: true, //不跳转
        hot: true // 热更新
    },
    ...
    plugins: [
        new webpack.BannerPlugin('版权所有,翻版必究'),  // new一个插件的实例 
        new HtmlWebpackPlugin({
            template: path.join(__dirname, "/src/index.template.html")// new一个这个插件的实例,并传入相关的参数
        }),
        new CleanWebpackPlugin(['dist']),  // 传入所要清理的文件夹名称
        new webpack.HotModuleReplacementPlugin() // 热更新插件 
    ]
}

此时我们重新启动项目 npm run dev 后,修改 hello.js 的内容,会发现浏览器预览效果会自动刷新(也许反应会比较慢,因为我们使用了 source-map 和其他配置的影响,后面代码分离的时候我们再处理)。

七、项目优化及拓展

7.1 代码分离

在当前的开发环境都是提倡模块化,webpack自然不例外,我们前面的 webpack.config.js 配置文件,其实也没配置多少东西就这么多了,要是以后增加了更多配置,岂不是看得眼花缭乱,所以最好的方法就是把它拆分,方便管理:

1. 我们在根目录下新建三个文件,分别为 webpack.common.jswebpack.dev.jswebpack.prod.js ,分别代表公共配置文件、开发环境配置文件、生产环境(指项目上线时的环境)配置文件。

2. 安装一个合并模块插件:

cnpm i webpack-merge -D

3. 将 webpack.config.js 的代码拆分到上述新建的三个文件中,然后把 webpack.config.js 文件删除,具体如下:

// webpack.common.js
const path = require('path');  // 路径处理模块
const webpack = require('webpack');  // 这个插件不需要安装,是基于webpack的,需要引入webpack模块
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 引入HtmlWebpackPlugin插件

module.exports = {
    entry: path.join(__dirname, "/src/index.js"), // 入口文件
    output: {
        path: path.join( __dirname, "/dist"), //打包后的文件存放的地方
        filename: "bundle.js" //打包后输出文件的文件名
    },
    module: {
        rules: [
            {
                test: /\.css$/,   // 正则匹配以.css结尾的文件
                use: ['style-loader', 'css-loader']  // 需要用的loader,一定是这个顺序,因为调用loader是从右往左编译的
            },
            {
                test: /\.(scss|sass)$/,   // 正则匹配以.scss和.sass结尾的文件
                use: ['style-loader', 'css-loader', 'sass-loader']  // 需要用的loader,一定是这个顺序,因为调用loader是从右往左编译的
            },
            {                             // jsx配置
                test: /(\.jsx|\.js)$/,   
                use: {                    // 注意use选择如果有多项配置,可写成这种对象形式
                    loader: "babel-loader"
                },
                exclude: /node_modules/   // 排除匹配node_modules模块
            }
        ]
    },
    plugins: [
        new webpack.BannerPlugin('版权所有,翻版必究'),  // new一个插件的实例 
        new HtmlWebpackPlugin({
            template: path.join(__dirname, "/src/index.template.html")// new一个这个插件的实例,并传入相关的参数
        }),
        new webpack.HotModuleReplacementPlugin()
    ]
}
// webpack.dev.js
const merge = require('webpack-merge');  // 引入webpack-merge功能模块
const common = require('./webpack.common.js'); // 引入webpack.common.js

module.exports = merge(common, {   // 将webpack.common.js合并到当前文件
    devServer: {
        contentBase: "./dist",   // 本地服务器所加载文件的目录
        port: "8088",  // 设置端口号为8088
        inline: true,  // 文件修改后实时刷新
        historyApiFallback: true, //不跳转
        hot: true     //热加载
    }
})
// webpack.prod.js
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const CleanWebpackPlugin = require('clean-webpack-plugin'); // 引入CleanWebpackPlugin插件

module.exports = merge(common, { // 将webpack.common.js合并到当前文件
    devtool: 'source-map',  // 会生成对于调试的完整的.map文件,但同时也会减慢打包速度
    plugins: [
        new CleanWebpackPlugin(['dist']),  // 所要清理的文件夹名称
    ]
})

此时我们的项目目录如下:

webpack4.x最详细入门指南

4. 设置 package.jsonscripts 命令:

{
  "name": "webpack-project",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "webpack --config webpack.prod.js",
    "dev": "webpack-dev-server --open --config webpack.dev.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "babel-core": "^6.26.3",
    "babel-loader": "^7.1.5",
    "babel-preset-env": "^1.7.0",
    "babel-preset-react": "^6.24.1",
    "clean-webpack-plugin": "^0.1.19",
    "css-loader": "^1.0.0",
    "html-webpack-plugin": "^3.2.0",
    "node-sass": "^4.9.4",
    "react": "^16.6.0",
    "react-dom": "^16.6.0",
    "sass-loader": "^7.1.0",
    "style-loader": "^0.23.1",
    "webpack": "^4.23.1",
    "webpack-cli": "^3.1.2",
    "webpack-dev-server": "^3.1.10",
    "webpack-merge": "^4.1.4"
  }
}

我们把 build 命令改为了 webpack --config webpack.prod.js ,意思是把打包配置指向 webpack.prod.js 配置文件,而之前我们只需要使用一个 webpack 命令为什么就可以运行了?因为 webpack 命令是默认指向 webpack.config.js 这个文件名称了,现在我们把文件名称改了,所以就需要自定义指向新的文件, dev 命令中的指令也同理。

然后我们运行 npm run buildnpm run dev ,效果应该和我们分离代码前是一样的。

注:说道 package.json 文件,顺便就多提几句,因为也许有些朋友可能对我们安装模块时加的 -D-S-g 命令存在一些疑惑,因为不知道什么时候加什么尾缀。

其实这个 package.json 文件是用于我们安装依赖的,可以把它当成一份依赖安装说明表,就是如果我们把项目上传或者发给其他的开发同事,肯定不会把 /node_modules 文件夹也发送过去,因为这太大了,不现实也没必要。

开发同事只需要有这份 package.json 文件,然后 npm install 就可以把我们所需要的依赖都安装下来,但前提是 package.json 文件上有记录,这就是安装模块时加上 -D , -S 命令的原因。

-D 的全称是 --save-dev 指开发环境时需要用到的依赖,会记录在 package.json 文件中的 devDependencies 选项中,而 -S--save 是指生产环境也就是上线环境中需要用到的依赖,会记录在 package.json 文件中的 dependencies 选项中, -g 的全称是 --global 指安装全局命令,就是我们在本电脑的任何项目中都能使用到的命令,比如安装 cnpm 这个淘宝镜像命令就会用到 -g 命令。

所以我们在安装模块时一定不要忘了加上对应的尾缀命令,让我们的模块有迹可循,否则其他的开发同事接手你的项目的话,会不会下班后(放学后)在门口等你就不知道了。

扯远了,希望不要嫌弃,也是想讲得更详细嘛!

7.2 多入口多出口

到目前为止我们都是一个入口文件和一个出口文件,要是我不止一个入口文件呢?下面我们来试试:

webpack.common.js 中的 entry 入口有三种写法,分别为字符串、数组和对象,平时我们用得比较多的是对象,所以我们把它改为对象的写法,首先我们在 src 文件夹下新建 two.js 文件,名称任意。因为有多个入口,所以肯定得多个出口来进行一一对应了,所以 entryoutput 配置如下:

// webpack.common.js
...
module.exports = {
    entry: {
        index: path.join(__dirname, "/src/index.js"),
        two: path.join(__dirname, "/src/two.js")
    }, 
    output: {
        path: path.join( __dirname, "/dist"), //打包后的文件存放的地方
        filename: "[name].js" //打包后输出文件的文件名
    },
    ...
}
// two.js
function two() {
    let element = document.createElement('div');
    element.innerHTML = '我是第二个入口文件';
    return element;
}

document.getElementById('root').appendChild(two());

然后我们运行 npm run build 打包后发现 /dist 文件夹下会多出 two.js 文件,同时 index.html 也会自动将 two.js 引入,然后我们运行 npm run dev 显示如下:

webpack4.x最详细入门指南

7.3 增加css前缀、分离css、消除冗余css、分离图片

1.增加css前缀

平时我们写css时,一些属性需要手动加上前缀,比如 -webkit-border-radius: 10px; ,在webpack中我们能不能让它自动加上呢?那是必须的,首先肯定得安装模块了:

cnpm i postcss-loader autoprefixer -D

安装好这两个模块后,在项目根目录下新建 postcss.config.js 文件:

// postcss.config.js
module.exports = {
    plugins: [
        require('autoprefixer')  // 引用autoprefixer模块
    ]
}

style.css 中增加以下样式:

/* style.css */
body {
    background: #999;
}

#root div{
    width: 200px;
    margin-top: 50px;
    transform: rotate(45deg); /* 这个属性会产生前缀 */
}

修改 webpack.common.js 文件中的 css-loader 配置:

...
module.exports = {
    ...
    module: {
        rules: [
            {
                test: /\.css$/,   // 正则匹配以.css结尾的文件
                use: [            
                    {loader: 'style-loader'}, // 这里采用的是对象配置loader的写法
                    {loader: 'css-loader'},
                    {loader: 'postcss-loader'} // 使用postcss-loader
                ]  
            },
            ...
        ]
    },
    ...
}

然后我们运行 npm run dev 后css样式中会自动添加前缀,显示如下:

webpack4.x最详细入门指南

2.分离css

虽然webpack的理念是把css、js全都打包到一个文件里,但要是我们想把css分离出来该怎么做呢?

cnpm i extract-text-webpack-plugin@next -D  // 加上@next是为了安装最新的,否则会出错

安装完以上插件后在 webpack.common.js 文件中引入并使用该插件:

// webpack.common.js
...
const ExtractTextPlugin = require('extract-text-webpack-plugin') //引入分离插件

module.exports = {
    ...
    module: {
        rules: [
            {
                test: /\.css$/,   // 正则匹配以.css结尾的文件
                use: ExtractTextPlugin.extract({  // 这里我们需要调用分离插件内的extract方法
                    fallback: 'style-loader',  // 相当于回滚,经postcss-loader和css-loader处理过的css最终再经过style-loader处理
                    use: ['css-loader', 'postcss-loader']
                })
            },
            ...
        ]
    },
    plugins: [
        ...
        new ExtractTextPlugin('css/index.css') // 将css分离到/dist文件夹下的css文件夹中的index.css
    ]
}

运行 npm run build 后会发现 /dist 文件夹内多出了 /css 文件夹及 index.css 文件。

3.消除冗余css

有时候我们css写得多了,可能会不自觉的写重复了一些样式,这就造成了多余的代码,上线前又忘了检查,对于这方面,我们应该尽量去优化它,webpack就有这个功能。

cnpm i purifycss-webpack purify-css glob -D

安装完上述三个模块后,因为正常来说是在生产环境中优化代码,所以我们应该是在 webpack.prod.js 文件中进行配置,引入 clean-webpack-pluginglob 插件并使用它们:

// webpack.prod.js
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const CleanWebpackPlugin = require('clean-webpack-plugin'); // 引入CleanWebpackPlugin插件

const path = require('path');
const PurifyCssWebpack = require('purifycss-webpack'); // 引入PurifyCssWebpack插件
const glob = require('glob');  // 引入glob模块,用于扫描全部html文件中所引用的css

module.exports = merge(common, {   // 将webpack.common.js合并到当前文件
    devtool: 'source-map',  // 会生成对于调试的完整的.map文件,但同时也会减慢打包速度
    plugins: [
        new CleanWebpackPlugin(['dist']),  // 所要清理的文件夹名称
        new PurifyCssWebpack({
            paths: glob.sync(path.join(__dirname, 'src/*.html')) // 同步扫描所有html文件中所引用的css
        })
    ]
})

我们在 style.css 文件中增加一些多余的代码试试:

/* style.css */
body {
    background: #999;
}

#root div{
    width: 200px;
    margin-top: 50px;
    transform: rotate(45deg); /* 这个属性会产生前缀 */
}

.a{                 /* 冗余css */
    color: black;     
}

.b{                 /* 冗余css */
    width: 50px;
    height: 50px;
    background: yellow;
}

然后我们运行 npm run build 后发现打包后的 index.css 中是没有多余的 .a.b 代码的:

/* index.css */

body {
  background: #999;
}

#root div {
  width: 200px;
  margin-top: 50px;
  -webkit-transform: rotate(45deg);
  transform: rotate(45deg);
  /* 这个属性会产生前缀 */
}
/*# sourceMappingURL=index.css.map*/

4.处理图片

到目前为止我们还没讲到图片的问题,如果要使用图片,我们得安装两个loader:

// 虽然我们只需使用url-loader,但url-loader是依赖于file-loader的,所以也要安装
cnpm i url-loader file-loader -D

然后在 webpack.common.js 中配置 url-loader

// webpack.common.js
...
module.exports = {
    ...
    module: {
        rules: [
            {
                test: /\.css$/,   // 正则匹配以.css结尾的文件
                use: ExtractTextPlugin.extract({
                    fallback: 'style-loader',
                    use: ['css-loader', 'postcss-loader']
                })
            },
            {
                test: /\.(png|jpg|svg|gif)$/,  // 正则匹配图片格式名
                use: [
                    {
                        loader: 'url-loader'  // 使用url-loader
                    }
                ]
            },
            ...
        ]
    },
    ...
}

我们修改一下 style.css ,把背景改为图片背景:

/* style.css */
body {
    background: url(../images/coffee.png) top right repeat-y;  /* 设为图片背景 */
}

#root div{
    width: 200px;
    margin-top: 50px;
    transform: rotate(45deg); /* 这个属性会产生前缀 */
}

.a{
    color: black;
}

.b{
    width: 50px;
    height: 50px;
    background: yellow;
}

运行 npm run dev 后显示如下:

webpack4.x最详细入门指南

但是背景图片变成了 base64 ,因为webpack会自动优化图片,减少发送请求,但是如果我想把它变成路径的该怎么做?

我们可以把 webpack.common.js 的loader配置更改一下,增加 options 选项:

// webpack.common.js
...
module.exports = {
    ...
    module: {
        rules: [
            {
                test: /\.css$/,   // 正则匹配以.css结尾的文件
                use: ExtractTextPlugin.extract({
                    fallback: 'style-loader',
                    use: ['css-loader', 'postcss-loader']
                })
            },
            {
                test: /\.(png|jpg|svg|gif)$/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 1000  // 限制只有小于1kb的图片才转为base64,例子图片为1.47kb,所以不会被转化
                        }
                    }
                ]
            },
            ...
        ]
    },
    ...
}

然后我们运行 npm run build 后,再运行 npm run dev ,额,图片是没有转成base64了,但是图片怎么不显示了?

webpack4.x最详细入门指南

问题就出在路径上,我们之前图片的路径是在 ../images 文件夹下,但是打包出来后没有这个路径了,图片直接和文件同级了,所以我们需要在 webpack.common.js 中给它设置一个文件夹:

// webpack.common.js
...
module.exports = {
    ...
    module: {
        rules: [
            ...
            {
                test: /\.(png|jpg|svg|gif)$/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 1000,  // 限制只有小于1kb的图片才转为base64,例子图片为1.47kb,所以不会被转化
                            outputPath: 'images'  // 设置打包后图片存放的文件夹名称
                        }
                    }
                ]
            },
            ...
        ]
    },
    ...
}

继续 npm run build 打包再 npm run dev 运行,我的天!图片还是不显示!

调试工具上看图片路径有 images 文件夹了,但是我的 ../ 呢?

webpack4.x最详细入门指南

这又涉及到配置路径的问题上了,我们还需要在css-loader中给背景图片设置一个公共路径 publicPath: '../' ,如下:

// webpack.common.js
...
module.exports = {
    ...
    module: {
        rules: [
            {
                test: /\.css$/,   // 正则匹配以.css结尾的文件
                use: ExtractTextPlugin.extract({
                    fallback: 'style-loader',
                    use: ['css-loader', 'postcss-loader'],
                    publicPath: '../'  // 给背景图片设置一个公共路径
                })
            },
            {
                test: /\.(png|jpg|svg|gif)$/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 1000,  // 限制只有小于1kb的图片才转为base64,例子图片为1.47kb,所以不会被转化
                            outputPath: 'images'  // 设置打包后图片存放的文件夹名称
                        }
                    }
                ]
            },
            ...
        ]
    },
    ...
}

现在再 npm run build 打包再 npm run dev 启动,OK!没毛病!

webpack4.x最详细入门指南

是不是很热闹?到现在我们不知不觉中也同时解决了图片分离的问题,偷偷高兴一下吧!

7.4 压缩代码

在webpack4.x版本中当你打包时会自动把js压缩了,而且 npm run dev 运行服务器时,当你修改代码时,热更新很慢,这是因为你修改后webpack又自动为你打包,这就导致了在开发环境中效率很慢,所以我们需要把开发环境和生产环境区分开来,这时就体现出我们代码分离的便捷性了, webpack.dev.js 代表开发环境的配置, webpack.prod.js 代表生产环境的配置,这时我们只要在 package.json 文件中配置对应环境的命令即可:

{
  ...
  "scripts": {
    "build": "webpack --config webpack.prod.js --mode production",
    "dev": "webpack-dev-server --open --config webpack.dev.js --mode development"
  },
  ...
  }
}

--mode production 表示打包时是生产环境,会自己将js进行压缩,而 --mode development 表示当前是开发环境,不需要进行压缩。这同时也解决了之前一直遗留的警告问题:

webpack4.x最详细入门指南

总结

好了,到现在我们基本把webapck常用的功能都走了一遍,写得有点长,感谢你能仔细的看到这里,希望能对你有所帮助,如果有发现不对的地方,也请多多指教。其实webpack还有很多功能,这里也没讲述完全,但相信你现在对webpack也有了一定的了解,更多的webpack探索一定难不倒你!

完整代码请前往 github ,如果能有所帮助,希望能给个star(偷笑)


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

服务设计与创新实践

服务设计与创新实践

宝莱恩 (Andy Polaine)、乐维亚 (Lavrans Lovlie)、里森 (Ben Reason) / 王国胜、张盈盈、付美平、赵芳 / 清华大学出版社 / 2015-6-1 / CNY 69.00

产品经济的时代渐行渐远,在以服务为主导的新经济时代,在强调体验和价值的互联网时代,如何才能做到提前想用户之所想?如何比用户想得更周到?如何设计可用、好用和体贴的服务?这些都可以从本书中找到答案。本书撷取以保险业为代表的金融服务、医疗服务、租车及其他种种服务案例,从概念到实践,有理有据地阐述了如何对服务进行重新设计?如何将用户体验和价值提前与产品设计融合在一起? 《服务设计与创新实践》适合产品......一起来看看 《服务设计与创新实践》 这本书的介绍吧!

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具