Webpack:详解打包和管理JavaScript和CSS依赖
推荐超级课程:
@TOC
Webpack是一个用于管理你的依赖项(css、js等)的构建工具。但为什么我们需要它呢?我们不是可以直接像下面这样将js和css添加到我们的html文件中吗?
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<link rel="stylesheet" href="src/css/main.css">
<link rel="stylesheet" href="src/css/first.css">
<title>Webpack 2 Basics</title>
</head>
<body>
<h1>Webpack basics</h1>
<button id="button1">Press Me!</button>
<script src="src/js/app.js"></script>
<script src="src/js/first.js"></script>
<script src="src/js/second.js"></script>
</body>
</html>
对于一个这样大小的应用程序,我们不需要管理js和css的依赖项,然而随着应用程序的增长,我们需要跟踪许多文件依赖项及其加载顺序。此外,代码压缩尚未实现。所以让我们通过使用webpack来改进我们的依赖管理。
在我们通过npm init初始化项目后,我们安装webpack
$npm init
$npm i -D webpack
让我们在package.json文件中添加一个构建脚本
{
"name": "webpack.basics",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --entry ./src/js/app.js --output-filename ./dist/bundle.js"
},
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^2.3.2",
"webpack-dev-server": "^2.4.2"
}
}
在这里,我们指定了webpack工作的入口文件,以便它通过依赖树进行操作。从app.js开始,webpack将所有代码合并到一个捆绑文件中,这里称为bundle.js。在文件中,我们需要使用“import”和“export”来显示依赖项。首先从app.js文件开始
import first from './first';
document.querySelector("#button1")
.addEventListener("click", function () {
first.handleClick()
});
这表明app.js文件依赖于first.js。让我们来看看first.js文件
export default {
handleClick() {
alert("Hey, I was Pressed!!!");
}
}
该文件导出了一个名为handleClick的函数。我们现在可以执行webpack脚本
$npm run build
正如您所看到的,webpack构建并创建了bundle.js。您应该有以下文件结构
我们现在删除js文件,并用一个捆绑文件替换它们
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<link rel="stylesheet" href="src/css/main.css">
<link rel="stylesheet" href="src/css/first.css">
<title>Webpack 2 Basics</title>
</head>
<body>
<h1>Webpack basics</h1>
<button id="button1">Press Me!</button>
<script src="dist/bundle.js"></script>
</body>
</html>
Webpack-Dev-Server
在上一节中,我们使用了文件协议在浏览器中渲染页面。我们不应该这样做。我们需要一个Web服务器来渲染页面。所以让我们安装webpack-dev-server。什么是webpack-dev-server?它是一个小型的express 服务器,围绕webpack及其所有功能进行封装。让我们安装webpack-dev-server。
@npm i -D webpack-dev-server
然后我们需要更改构建脚本以使用webpack-dev-server。
{
"name": "webpack.basics",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack-dev-server --entry ./src/js/app.js --output-filename ./dist/bundle.js"
},
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^2.3.2",
"webpack-dev-server": "^2.4.2"
}
}
再次运行脚本,注意页面现在托管在http://localhost:8080。此外,我们现在可以删除包含bundle.js文件的dist文件夹,应用程序仍然可以工作,因为webpack-dev-server会构建bundle.js文件并将其放入内存中。这非常方便,因为如果依赖树中的任何文件被更改,webpack-dev-server会自动重新构建捆绑文件并重新加载应用程序。
Webpack配置文件
为了充分利用webpack的功能,我们需要一个配置文件。配置文件将指示webpack如何处理我们的源文件。在项目的根目录中创建一个“webpack.config.js”。
var path = require("path");
module.exports = {
entry: './src/js/app.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: "bundle.js",
publicPath: "/dist" //为webpack-dev-server模拟公共路径
}
};
在运行构建脚本之前,我们需要从构建命令中删除选项,因为构建现在将使用配置文件来指示webpack该做什么。新的package.json应该如下所示:
{
"name": "webpack.basics",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack-dev-server"
},
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^2.3.2",
"webpack-dev-server": "^2.4.2"
}
}
加载器和插件
webpack的真正强大之处在于它提供的加载器和插件。
加载器是针对每个文件应用的任务,在webpack构建捆绑文件时运行。一个典型的任务是将sass文件预处理成css,并将输出加载到html文件的头部。
插件是在捆绑文件发送到输出文件之前应用的过程。一个典型的插件是压缩插件。为了说明加载器和插件,让我们从index.html中移除所有对css文件的引用,并从我们的入口文件(app.js)中导入css文件。为此,我们需要一些加载器和插件来进行转换。在这种情况下,我们需要两个加载器和一个插件:“css-loader”、“style-loader"和"extract-text-webpack-plugin”。
$npm i -D css-loader style-loader extract-text-webpack-plugin
现在我们需要告诉webpack使用这些加载器来分析css文件。
var path = require("path");
var ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
entry: './src/js/app.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: "bundle.js",
publicPath: "/dist" //模拟webpack-dev-server的公共路径
},
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: ['css-loader']
})
}
]
},
plugins: [
new ExtractTextPlugin("bundle.css"),
]
};
这样做的目的是允许webpack将所有导入的css文件合并到一个名为"bundle.css"的包中。
import '../css/main.css';
import '../css/first.css';
import first from './first';
document.querySelector("#button1")
.addEventListener("click", function () {
first.handleClick()
});
现在我们可以从index.html页面中移除css文件链接,并将其替换为对bundle.css文件的单一链接。
现在我们所有的css和js依赖都由webpack管理和处理。
为了实现额外的sass加载器,我们进行以下操作
var path = require("path");
var webpack = require("webpack");
var ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
entry: './src/js/app.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: "js/bundle.js",
publicPath: "dist"
},
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: ['css-loader'],
})
},
{
test: /\.scss$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: ['css-loader', 'sass-loader'],
})
}
]
},
plugins: [
new ExtractTextPlugin("css/bundle.css")
]
};
正如您所看到的,我们已经将包的路径更改为dist/css/bundle.css用于css和dist/js/bundle.js。最后,我们必须相应地调整index.html