本章提要:
│── build
│ │── base.js // 公共部分
│ │── build.js
│ └── dev.js
│── config
│ │── base.js // 基础配置
│ │── css.js // css 配置
│ │── HtmlWebpackPlugin.js // html 配置
│ └── MiniCssExtractPlugin.js // 提取css
│── public // 公共资源
│ └── index.html // html 模版
└── src // 开发目录
│── style
│ └── index.css
└── main.js // 主入口
package.json
{
"scripts": {
"dev": "cross-env NODE_ENV=development node build/dev.js",
"build": "cross-env NODE_ENV=production node build/build.js"
},
"dependencies": {
"cross-env": "^6.0.3",
"css-loader": "^3.2.0",
"cssnano": "^4.1.10",
"ora": "^4.0.3",
"rimraf": "^3.0.0",
"webpack": "^4.41.2"
},
"devDependencies": {
"extract-text-webpack-plugin": "^3.0.2",
"html-webpack-plugin": "^3.2.0",
"mini-css-extract-plugin": "^0.8.0",
"vue-cli-plugin-commitlint": "^1.0.4",
"webpack-chain": "^6.0.0",
"webpack-cli": "^3.3.10",
"webpack-dev-server": "^3.9.0"
}
}
build/base.js
const { findSync } = require("../lib");
const Config = require("webpack-chain");
const config = new Config();
const files = findSync("config");
const path = require("path");
const resolve = p => {
return path.join(process.cwd(), p);
};
module.exports = () => {
const map = new Map();
files.map(_ => {
const name = _.split("/")
.pop()
.replace(".js", "");
return map.set(name, require(_)(config, resolve));
});
map.forEach(v => v());
return config;
};
build/build.js
const rimraf = require("rimraf");
const ora = require("ora");
const chalk = require("chalk");
const path = require("path");
// 删除 dist 目录
rimraf.sync(path.join(process.cwd(), "dist"));
const config = require("./base")();
const webpack = require("webpack");
const spinner = ora("开始构建项目...");
spinner.start();
webpack(config.toConfig(), function(err, stats) {
spinner.stop();
if (err) throw err;
process.stdout.write(
stats.toString({
colors: true,
modules: false,
children: false,
chunks: false,
chunkModules: false
}) + "\n\n"
);
if (stats.hasErrors()) {
console.log(chalk.red("构建失败\n"));
process.exit(1);
}
console.log(chalk.cyan("build完成\n"));
});
build/dev.js
const config = require("./base")();
const webpack = require("webpack");
const chalk = require("chalk");
const WebpackDevServer = require("webpack-dev-server");
const port = 8080;
const publicPath = "/common/";
config.devServer
.quiet(true)
.hot(true)
.https(false)
.disableHostCheck(true)
.publicPath(publicPath)
.clientLogLevel("none");
const compiler = webpack(config.toConfig());
// 拿到 devServer 参数
const chainDevServer = compiler.options.devServer;
const server = new WebpackDevServer(
compiler,
Object.assign(chainDevServer, {})
);
["SIGINT", "SIGTERM"].forEach(signal => {
process.on(signal, () => {
server.close(() => {
process.exit(0);
});
});
});
// 监听端口
server.listen(port);
new Promise(() => {
compiler.hooks.done.tap("dev", stats => {
const empty = " ";
const common = `App running at:
- Local: http://127.0.0.1:${port}${publicPath}\n`;
console.log(chalk.cyan("\n" + empty + common));
});
});
config/css.js
module.exports = (config, resolve) => {
return (lang, test) => {
const baseRule = config.module.rule(lang).test(test);
const normalRule = baseRule.oneOf("normal");
applyLoaders(normalRule);
function applyLoaders(rule) {
rule
.use("extract-css-loader")
.loader(require("mini-css-extract-plugin").loader)
.options({
publicPath: "./"
});
rule
.use("css-loader")
.loader("css-loader")
.options({});
}
};
};
config/MiniCssExtractPlugin.js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = (config, resolve) => {
return () => {
config
.oneOf("normal")
.plugin("mini-css-extract")
.use(MiniCssExtractPlugin);
};
};
config/HtmlWebpackPlugin.js
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = (config, resolve) => {
return () => {
config.plugin("html").use(HtmlWebpackPlugin, [
{
template: "public/index.html"
}
]);
};
};
public/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>learn_webpack</title>
<body></body>
</html>
src/style/index.css
.test {
width: 200px;
height: 200px;
color: red;
background-color: orange;
}
src/main.js
require("./style/index.css");
const h2 = document.createElement("h2");
h2.className = "test";
h2.innerText = "test";
document.body.append(h2);