初涉webpack

2018-10-09  本文已影响16人  DHFE

什么是webpack?

本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。

为什么是webpack?

市面上已经存在的模块管理和打包工具并不适合大型的项目,尤其单页面 Web 应用程序。最紧迫的原因是如何在一个大规模的代码库中,维护各种模块资源的分割和存放,维护它们之间的依赖关系,并且无缝的将它们整合到一起生成适合浏览器端请求加载的静态资源。

这些已有的模块化工具并不能很好的完成如下的目标:

webpack特点
webpack与其他模块化工具有什么区别?

———— 赵达《webpack中文指南》


开始

安装

npm install webpack -g

并作为开发依赖安装到本地。(确认有package.json)

npm install webpack --save-dev

之后打印webpack -h看看是否安装成功。

需要注意的是,在webpack 3中,webpack本身和它的CLI以前都是在同一个包中,但在第4版中,他们已经将两者分开来更好地管理它们。尝试全局安装webpack-cli。

npm install -g webpack-cli

之后webpack -h检验是否安装成功。


使用
在webpack4.0之前,可以不进行配置文件,直接使用,由于这里使用的是webpack4.0以上的版本,需要先配置文件。
新建一个webpack.config.js文件,这是webpack的默认配置文件名。

const path = require('path');

module.exports = {
    mode: "production",
    entry: './a.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js'
    }
};

其中path为node.js的核心模块,path.resolve,根据给定的路径片段构造一个绝对路径,供webpack出口使用。

path.resolve(__dirname, 'dist')表示打包后的文件将在当前目录下的dist文件下被输出,文件名为bundle.js

现在新建一个a.js文件。

// a.js
console.log("OK!");

运行webpack命令。


打包成功了,打包过程中会显示日志,包含了版本号,时间等信息。并且会看到目录中多了一个dist文件夹,里面就是我们需要的打包输出文件。
!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t){console.log("OK!")}]);

我们使用生产模式打包,所以代码被压缩了。

现在新建一个index.html文件,引入bundle.js,在浏览器中打开。

这就是最简单的一次webpack尝试。


打包CSS和图片

webpack本身不支持任何除JS文件之外的任何加载,我们需要下载对应的loader。

loader 让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块,然后你就可以利用 webpack 的打包能力,对它们进行处理。
本质上,webpack loader 将所有类型的文件,转换为应用程序的依赖图(和最终的 bundle)可以直接引用的模块。

对于CSS文件,需要下载:

npm install  css-loader style-loader --save-dev

对于图片文件,需要下载:

npm install file-loader url-loader --save-dev

首先在项目目录下创建一个css文件。

/*
./src/cstyle.css
*/
body {
    background: red;
}

但是下载了loader还不行,还差一步配置loader使其生效。

const path = require('path');

module.exports = {
    mode: "development",// "production" | "development" | "none"
    entry: './src/js/entry.js',
    output: {
        path: path.resolve(__dirname, 'dist/js'),
        filename: 'bundle.js'
    },
    module: {
        rules: [{
            test: /\.css$/,
            use: ["style-loader","css-loader"]
        }]
    }
};

以上配置中,对一个单独的 module 对象定义了 rules 属性,里面包含两个必须属性:testuse。这告诉 webpack 编译器(compiler) 如下信息:

“嘿,webpack 编译器,当你碰到「在 require()或import 语句中被解析为'.css' 的路径」时,在你对它打包之前,先使用 css-loader 转换一下。”

同样,对于图片文件,我们一样可以配置。

const path = require('path');

module.exports = {
    mode: "development", // "production" | "development" | "none"
    entry: './src/js/entry.js',
    output: {
        path: path.resolve(__dirname, 'dist/js'),
        filename: 'bundle.js'
    },
    module: {
        rules: [{
                test: /\.css$/,
                use: ["style-loader", "css-loader"]
            },
            {
                test:/\.(png|jpg|gif)\$/,
                use: [{
                    loader: "url-loader",
                    options: {limit: 8192}
                }]
            }
        ]
    }
};

url-loader:url-loader 功能类似于 file-loader,但是在文件大小(单位 byte)低于指定的限制(这里是8kb)时,可以返回一个 DataURL。

推荐文章:data url简介及data url的利弊

现在我们来准备一次CSS和图片打包。

src文件夹为开发目录,img文件内中jerry.jpg为小于8k的jpg文件,观察如果文件大于8k会做何处理。

<!-- 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>Document</title>
</head>
<body>
    <h1>webpack 4</h1>
    <div id="tom"></div>
    <br>
    <div id="jerry"></div>
    <script type="text/javascript" src="./js/bundle.js"></script>
</body>
</html>
/* style.css */

body {
    background: red;
}
#jerry {
    width: 100px;
    height: 100px;
    background-image: url("../img/jerry.jpg");
    background-repeat: no-repeat;
}
#tom {
    width: 100px;
    height: 100px;
    background-image: url("../img/tom.jpg");
    background-repeat: no-repeat;
}

webpack配置文件如下:

const path = require('path');

module.exports = {
    mode: "development", // "production" | "development" | "none"
    entry: './src/js/entry.js',
    output: {
        path: path.resolve(__dirname, 'dist/js'),
        filename: 'bundle.js'
    },
    module: {
        rules: [{
                test: /\.css$/,
                use: ["style-loader", "css-loader"]
            },
            {
                test:/\.(png|jpg|gif)\$/,
                use: [{
                    loader: "url-loader",
                    options: {limit: 8192}
                }]
            }
        ]
    }
};

执行webpack

可以输入webpack --display-modules来显示打包过程中隐藏的信息。

此时,dist目录下多了一些文件。


先不管是什么,我们引入bundle.js看看效果。


可以看到,CSS文件成功打包并生效了,Jerry也出现了,并且是以Base64的形式存在在bundle.js中。

但是,tom去哪了?

打开F12检查。


404,找不到这个文件,并且发现,这个jpg文件名字变得一长串不明字串。还记得之前打包后多出来的那些文件中,也有一个jpg文件吗,这其实就是tom。


这其实也是webpack内部做的一些工作,当打包完成后,tom的文件指向不再是原来的tom.jpg,而是打包后重命名的这个长字串文件,但是问题是,index.html显然没有找到它,于是回了404。


f12也显示了,是/dist下的jpg,而文件是在/dist/js下。

这在使用url-loader时可能会出现的一个问题。

解决问题

  1. 既然如此,我们将index.html移动到/dist/js下,使jpg与html处于同层目录。

当然别忘记修改<script type="text/javascript" src="./bundle.js"></script>中的目录。

手动调了size

tom就成功显示了,这是第一种也是最简单最常用的方法。

  1. 另一种方法,是在webpack.config.js输出中配置pulicPath属性
    output: {
        path: path.resolve(__dirname, 'dist/js'),
        filename: 'bundle.js',
        publicPath: './js',
    },

显示指明了资源获取目录。

重新编译打包后也能正常显示。


但是不推荐使用第二种方法。

在设置index.html提供资源服务时具有强制性,只要设置了字串,index.html所有的资源入口都会从这里开始,这个机制会影响之后要提到的新功能热加载工具。


热加载(自动编译打包)

利用webpack开发服务器工具:webpack-dev-server

webpack-dev-server

webpack-dev-server 为你提供了一个简单的 web 服务器,并且能够实时重新加载(live reloading)。

首先:npm install --save-dev webpack-dev-server

修改配置文件,告诉开发服务器(dev server),在哪里查找文件:

const path = require('path');

module.exports = {
    mode: "development", // "production" | "development" | "none"
    entry: './src/js/entry.js',
    output: {
        path: path.resolve(__dirname, 'dist/js'),
        filename: 'bundle.js',
    },
    module: {
        rules: [{
                test: /\.css$/,
                use: ["style-loader", "css-loader"]
            },
            {
                test: /\.(png|jpg|gif)$/,
                use: [{
                    loader: "url-loader",
                    options: {
                        limit: 8192
                    }
                }]
            }
        ]
    },
    devServer: {
        contentBase: "./dist"
    }
};

如果是全局安装,那么直接运行webpack-dev-server就可以使用了,本地安装需要在script字段添加脚本。

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "webpack-dev-server --open",
  },

运行npm start,不出意外,浏览器会打开一个tag,展现打包好的html。

并且,此时对原文件的任何修改,都会使webpack重新编译,重新启动服务器展现在你面前。

body {
    background: greenyellow;
}

并且使用webpack-dev-server打包在目录下输出文件,所有的编译,打包,引用都是在服务器中进行,完成。

另外,对于contentBase字段,默认情况下,它服务于根目录下的index.html(必须是这个文件名)。也就是说,不填写contentBase字段,默认会寻找的根目录下的index.html。

    devServer: {
        contentBase: "./dist"
    }

使用插件
HtmlWebpackPlugin为例

HtmlWebpackPlugin

安装:

npm install --save-dev html-webpack-plugin
npm install --save-dev clean-webpack-plugin

引入:

// webpack.config.js
const HtmlWebpackPlugin = require("html-webpack-plugin");
const CleanWebpackPlugin = require("clean-webpack-plugin");

配置:

const path = require('path');

const HtmlWebpackPlugin = require("html-webpack-plugin");
const CleanWebpackPlugin = require("clean-webpack-plugin");

module.exports = {
    mode: "development", // "production" | "development" | "none"
    entry: './src/js/entry.js',
    output: {
        path: path.resolve(__dirname, 'dist/js'),
        filename: 'bundle.js',
    },
    module: {
        rules: [{
                test: /\.css$/,
                use: ["style-loader", "css-loader"]
            },
            {
                test: /\.(png|jpg|gif)$/,
                use: [{
                    loader: "url-loader",
                    options: {
                        limit: 8192
                    }
                }]
            }
        ]
    },
    devServer: {
        // defalut
    },
    plugins: [
        new HtmlWebpackPlugin({template: "./index.html"}),
        new CleanWebpackPlugin(["dist"])
    ]
};

HtmlWebpackPlugin插件将为你生成一个 HTML5 文件, 其中包括使用 script 标签的 body 中的所有 webpack 包

template字段表示指定的模版存放目录(也就是index.html的目录),在模板html中,没有引入任何js文件。

<!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>Document</title>
</head>
<body>
    <h1>webpack 4</h1>
    <div id="tom"></div>
    <br>
    <div id="jerry"></div>
</body>
</html>

运行webpack命令。

打包完成后发现,dist目录多了一个index.html文件,这个文件就是插件根据模版文件生成的新的html文件,并且该文件自引入了bundle.js,其他的和模板文件完全相同。

<!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>Document</title>
</head>
<body>
    <h1>webpack 4</h1>
    <div id="tom"></div>
    <br>
    <div id="jerry"></div>
<script type="text/javascript" src="bundle.js"></script></body>
</html>

优点是,所有的静态资源与index.html都在一起,防止了一些因为打包后资源目录不正确(设置冲突)引起的资源Not found问题。

而对于CleanWebpackPlugin插件。

    plugins: [
        new HtmlWebpackPlugin({template: "./index.html"}),
        new CleanWebpackPlugin(["dist"])
    ]

功能在于每次打包输出文件前,会事先将dist目录下的所有文件删除一次后在输出此次打包的文件,因为会有一些冗余文件存在而造成影响。


阮一峰webpack教程集
webpack快速入门——如何安装webpack及注意事项

webpack 3 零基础入门教程 #16 - 使用 ProvidePlugin 插件来处理像 jQuery 这样的第三方包
Webpack飞行手册

上一篇 下一篇

猜你喜欢

热点阅读