# WebPack
# WebPack 是什么?
从本质上来说,webpack 是一个静态模块打包工具。
要想让我们写好的模块化代码在各式各样的浏览器上能做到兼容,就必须借助于其他工具;
而 webpack 的其中一个核心就是让我们可以进行模块化开发,并帮我们处理模块间的依赖关系。
不仅仅是 Javascript 文件,我们的 css、图片、json 文件等在 webpack 中都可以当作模块来使用,这就是 webpack 的模块化概念。
当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。
# 学习资料
Webpack 中文文档 (opens new window)
# 快速上手
# 简单的例子
假设我们有这样两个文件,一个 index.html 一个 src/index.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>
<script src="./dist/main.js"></script>
</head>
<body>
<div id="app"></div>
</body>
</html>
document.getElementById("app").innerText = "Hello, Webpack";
# 安装依赖
# 本地安装
npm install webpack webpack-cli -D
// webpack.config.js
const path = require("path");
module.exports = {
mode: "development",
entry: path.join(__dirname, "./src/index.js"), // dirname代表索引到文件所在目录
output: {
path: path.join(__dirname, "./dist"),
filename: "main.js"
}
};
在上面的示例中,我们通过
output.filename
和output.path
属性,来告诉 webpack 打包出 bundle 的名称,以及我们想要把 bundle 生成到哪里
{
"scripts": {
"build": "webpack --config webpack.config.js"
}
}
npm run build
# 热更新
npm install webpack-dev-server -D
npm install html-webpack-plugin -D
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
mode: "development", // development-开发环境。 production-生产环境
entry: path.join(__dirname, "./index.js"),
output: {
path: path.join(__dirname, "./dist"),
filename: "main.js"
},
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname, "./index.html")
})
]
};
{
"scripts": {
"dev": "webpack-dev-server --open --port 3002 --hot",
"build": "webpack --config webpack.config.js"
}
}
npm run dev
# 处理 CSS
# 处理 ES6
# 处理图片资源
# HTML 文件的热更新
# WebPack 核心概念
# 什么 Webpack 模块 ?
- 在模块化编程中,开发者将程序分解成离散功能块(discrete chunks of functionality),并称之为模块。
- 每个模块具有比完整程序更小的接触面,使得校验、调试、测试轻而易举。 精心编写的模块提供了可靠的抽象和封装界限,使得应用程序中每个模块都具有条理清楚的设计和明确的目的。
- 在 web 存在多种支持 JavaScript 模块化的工具,这些工具各有优势和限制。webpack 基于从这些系统获得的经验教训,并将模块的概念应用于项目中的任何文件。
# entry
entry 的基本配置是一个指向文件的地址字符串
const config = {
entry: {
main: "./path/to/my/entry/file.js"
}
};
// 简写
const config = {
entry: "./path/to/my/entry/file.js"
};
// 数组
const config = {
entry: ["./path/to/my/entry/file1.js", "./path/to/my/entry/file2.js"]
};
// 对象
const config = {
entry: {
app: "./src/app.js",
vendors: "./src/vendors.js"
}
};
// 多页面应用程序
const config = {
entry: {
pageOne: "./src/pageOne/index.js",
pageTwo: "./src/pageTwo/index.js",
pageThree: "./src/pageThree/index.js"
}
};
对象语法会比较繁琐。然而,这是应用程序中定义入口的最可扩展的方式。
# output
outout 的基本配置是一个对象,至少包含以下两点:
- filename 输出的文件名
- path 输出路径
// 基本配置
const config = {
output: {
filename: "bundle.js",
path: "/home/proj/public/assets"
}
};
// 使用占位符来对应多个入口
const config = {
entry: {
app: "./src/app.js",
search: "./src/search.js"
},
output: {
filename: "[name].js",
path: __dirname + "/dist"
}
};
// 上面将会输出 ./dist/app.js ./dist/search.js
// 在进一步,我们使用哈希值来命名打包后的文件
const config = {
entry: {
app: "./src/app.js"
},
output: {
filename: "[name].[hash].js",
path: __dirname + "/dist"
}
};
// 上面将会输出 ./dist/app.sdf4whq.sj 这里的哈希值却决于真实的文件内容
# mode
提供 mode 配置选项,会告知 webpack 使用相应模式的内置优化,默认支持两种模式分别是development
和production
。
// config中指定
const config = {
mode: "production"
};
# 也可以从CLI参数中传递
webpack --mode=production
当指定
mode
的时候会将process.evn.NODE_ENV
设置为相同值,反之值设置NODE_ENV
则不会自动设置mode
# loader
webpack 自身只能处理 JavaScript,loader 让 webpack 能够处理非 JavaScript 文件
本质上,webpack loader 将所有类型的文件,转换为应用程序的依赖图(和最终的 bundle)可以直接引用的模块。
在 webpack 中配置 loader 有两个目标:
- test,正则表达式,用于标识出对应的 loader 进行转换的某个或某些文件
- use,字符串,表示进行转换时,应该使用那个 loader
注意在 webpack 中定义 loader 时,要定义在 module.rules 中,而不是 rules
// webpack.config.js
const path = require("path");
const config = {
ouput: {
filename: "bundle.js"
},
module: {
rules: [
{ text: "/.txt$/", use: "raw-loader" },
{ test: "/.css$/", use: ["css-loader"] },
{ test: "/.ts$/", use: ["ts-loader"] },
{ test: "/.less$/", use: ["less-loader"] },
{ test: "/.sass$/", use: ["sass-loader"] },
{ test: "/.(mov|mp4)$/", use: ["file-loader"] }
]
}
};
常用 loader 如下:
- raw-loader val-loader url-loader file-loader
- json-loader json5-loader cson-loader
- script-loader babe-loader ts-loader coffee-loader
- html-loader markdown-loader react-markdown-loader
- style-loader css-loader less-loader sass-loader postcss-loader
- eslint-loader mocha-loader jslint-loader
- vue-loader angular-template-loader
module.rules
允许指定多个 loader,这是展示 loader 的一种简明方式,并且有助于使代码变得简洁。
const config = {
module: {
rules: [
{
test: /\.css$/,
use: [
{ loader: "style-loader" },
{
loader: "css-loader",
options: {
modules: true
}
}
]
}
]
}
};
# plugins
插件是 webpack 的支柱功能。webpack 自身也是构建于,你在 webpack 配置中用到的相同的插件系统之上! 插件目的在于解决 loader 无法实现的其他事情
webpack 插件是一个具有 apply
属性的 JavaScript 对象。apply 属性会被 webpack compiler 调用,并且 compiler 对象可在整个编译生命周期访问。
由于插件可以携带参数/选项,你必须在 webpack 配置中,向 plugins 属性传入 new 实例。
下面是一个 html-webpack-plugin 的使用示例:
const HtmlWebpackPlugin = require("html-webpack-plugin"); //通过 npm 安装
const webpack = require("webpack"); //访问内置的插件
const path = require("path");
const config = {
entry: "./path/to/my/entry/file.js",
output: {
filename: "my-first-webpack.bundle.js",
path: path.resolve(__dirname, "dist")
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
use: "babel-loader"
}
]
},
plugins: [
new webpack.optimize.UglifyJsPlugin(),
new HtmlWebpackPlugin({
template: "./src/index.html",
scriptLoad: "blocking"
})
]
};
module.exports = config;
# Webpack 基础配置
# 配置对象
webpack 需要传入一个配置对象,那么我们就可以朴素
的传入一个对象
var path = require("path");
module.exports = {
mode: "development",
entry: "./foo.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "foo.bundle.js"
}
};
除了导出单个配置对象,还有一些方式满足其他需求
比如导出为一个函数
module.exports = function (env, argv) {
return {
mode: env.production ? "production" : "development",
devtool: env.production ? "source-maps" : "eval",
plugins: [
new webpack.optimize.UglifyJsPlugin({
compress: argv["optimize-minimize"] // 只有传入 -p 或 --optimize-minimize
})
]
};
};
或者导出一个 Promise
module.exports = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
entry: "./app.js"
/* ... */
});
}, 5000);
});
};
# Webpack 常用的加载器
# file-loader
# url-loader
# css-loader
# sass-loader
# vue-loader
# Webpack 常用的插件
# JSON 合并
merge-jsons-webpack-plugin (opens new window)
# 全局样式
style-resources-loader (opens new window)
# 依赖分析
webpack-bundle-analyzer (opens new window)1
# 条件编译
安装相关依赖
npm i js-conditional-compile-loader -D
npm i cross-env -D
修改打包配置
// webpack.base.conf.js
const conditionalCompiler = {
loader: "js-conditional-compile-loader",
options: {
primary: process.env.version == "primary",
senior: process.env.version == "senior",
normal: process.env.oem == "", // undefined
}
};
// webpack.base.conf.js
rules: [
{
test: /\.vue$/,
// loader: "vue-loader",
// options: vueLoaderConfig
use: [
{
loader: "vue-loader",
options: vueLoaderConfig
},
conditionalCompiler
]
},
{
test: /\.js$/,
// loader: "babel-loader",
use: ["babel-loader", conditionalCompiler],
include: [resolve("src"), resolve("test"), resolve("node_modules/webpack-dev-server/client")]
}
];
package.json
通过命令行传入参数
{
"dev:primary": "cross-env version=primary npm run dev",
"dev:senior": "cross-env version=senior npm run dev"
}
在 JS 和 Vue 文件中使用
<template>
<div id="app">
<!-- /* IFTRUE_normal */ -->
<img src="./assets/logo.png" />
<!-- /* FIDEBUG_normal */ -->
<!-- /* IFTRUE_youku */ -->
<img src="./assets/logo_senior.png" />
<!-- /* FITRUE_youku */ -->
<router-view />
</div>
</template>
<script>
export default {
name: "App",
created() {
/* IFTRUE_primary */
console.log("primary");
/* FITRUE_primary */
}
};
</script>