webpack 升級到4之後還沒好好的同步一個可實用的webpack架子,接下來用webpack4來搭建一個簡單的react的多介面應用,廢話不說 直接擼碼
建立工程
$ mkdir demo && cd demo
$ npm init -y
目錄結構
![工程目錄結構][1]
webpack 配置
安裝react + babel 依賴
$ npm i -S react@16.3.0 react-dom@16.3.0
$ npm i -D webpack@4.4.1 webpack-cli@2.0.13 webpack-dev-server@3.1.1 webpack-merge@4.1.2 babel-cli@6.26.0 babel-preset-env@1.6.1 babel-preset-react@6.24.1 babel-preset-react-hmre@1.1.1 babel-loader@7.1.4 file-loader@1.1.11 url-loader@1.0.1
webpack.base.conf.js(config -> webpack)
const entry = require("./webpack.entry.conf");
const newEntry = {};
for (let name in entry) {
newEntry[name] = entry[name][0]
}
let config = {
entry: newEntry,
resolve: {
extensions: [".js", ".json", ".jsx", ".css", ".pcss"],
}
};
module.exports = config;
webpack.dev.conf.js
const webpack = require(`webpack`);//引入webpack
const opn = require(`opn`);//開啟瀏覽器
const merge = require(`webpack-merge`);//webpack配置檔案合併
const path = require("path");
const baseWebpackConfig = require("./webpack.base.conf");//基礎配置
const webpackFile = require("./webpack.file.conf");//一些路徑配置
const eslintFormatter = require(`react-dev-utils/eslintFormatter`);
let config = merge(baseWebpackConfig, {
/*設定開發環境*/
mode: `development`,
output: {
path: path.resolve(webpackFile.devDirectory),
filename: `js/[name].js`,
chunkFilename: "js/[name].js",
publicPath: ``
},
optimization: {
runtimeChunk: {
name: `manifest`
},
// 包拆分
splitChunks: {
cacheGroups: {
common: { // 專案的公共元件
chunks: "initial",
name: "common",
minChunks: 2,
maxInitialRequests: 5,
minSize: 0
},
vendor: { // 第三方元件
test: /node_modules/,
chunks: "initial",
name: "vendor",
priority: 10,
enforce: true
}
}
}
},
plugins: [
/*設定熱更新*/
new webpack.HotModuleReplacementPlugin(),
],
module: {
rules: [
{
test: /.(js|jsx)$/,
use: [
`babel-loader`,
`cache-loader`,
],
include: [
path.resolve(__dirname, "../../app"),
path.resolve(__dirname, "../../entryBuild")
],
exclude: [
path.resolve(__dirname, "../../node_modules")
],
},
{
test: /.(css|pcss)$/,
loader: `style-loader?sourceMap!css-loader?sourceMap!postcss-loader?sourceMap`,
exclude: /node_modules/
},
{
test: /.(png|jpg|gif|ttf|eot|woff|woff2|svg|swf)$/,
loader: `file-loader?name=[name].[ext]&outputPath=` + webpackFile.resource + `/`
},
{
test: /.(js|jsx)$/,
enforce: `pre`,
use: [
{
options: {
formatter: eslintFormatter,
eslintPath: require.resolve(`eslint`),
// @remove-on-eject-begin
baseConfig: {
extends: [require.resolve(`eslint-config-react-app`)],
},
//ignore: false,
useEslintrc: false,
// @remove-on-eject-end
},
loader: require.resolve(`eslint-loader`),
},
],
include: [
path.resolve(__dirname, "../../app")
],
exclude: [
path.resolve(__dirname, "../../node_modules")
],
}
]
},
/*設定api轉發*/
devServer: {
host: `0.0.0.0`,
port: 8080,
hot: true,
inline: true,
contentBase: path.resolve(webpackFile.devDirectory),
historyApiFallback: true,
disableHostCheck: true,
proxy: [
{
context: [`/api/**`, `/u/**`],
target: `http://10.8.200.69:8080/`,
secure: false
}
],
/*開啟瀏覽器 並開啟本專案網址*/
after() {
opn(`http://localhost:` + this.port);
}
}
});
module.exports = config;
webpack.prod.conf.js
const path = require(`path`);
const merge = require(`webpack-merge`);
const HtmlWebpackPlugin = require(`html-webpack-plugin`);
const CopyWebpackPlugin = require(`copy-webpack-plugin`);
const CleanWebpackPlugin = require(`clean-webpack-plugin`);
const OptimizeCSSPlugin = require(`optimize-css-assets-webpack-plugin`);
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const baseWebpackConfig = require("./webpack.base.conf");
const webpackFile = require(`./webpack.file.conf`);
const entry = require("./webpack.entry.conf");
const webpackCom = require("./webpack.com.conf");
let config = merge(baseWebpackConfig, {
/*設定生產環境*/
mode: `production`,
output: {
path: path.resolve(webpackFile.proDirectory),
filename: `js/[name].[chunkhash:8].js`,
chunkFilename: "js/[name]-[id].[chunkhash:8].js",
},
optimization: {
//包清單
runtimeChunk: {
name: "manifest"
},
//拆分公共包
splitChunks: {
cacheGroups: {
common: { //專案公共元件
chunks: "initial",
name: "common",
minChunks: 2,
maxInitialRequests: 5,
minSize: 0
},
vendor: { //第三方元件
test: /node_modules/,
chunks: "initial",
name: "vendor",
priority: 10,
enforce: true
}
}
}
},
plugins: [
// extract css into its own file
new ExtractTextPlugin(`css/[name].[md5:contenthash:hex:8].css`),
// Compress extracted CSS. We are using this plugin so that possible
// duplicated CSS from different components can be deduped.
new OptimizeCSSPlugin({
assetNameRegExp: /.css$/g,
cssProcessor: require(`cssnano`),
cssProcessorOptions: {
discardComments: {removeAll: true},
// 避免 cssnano 重新計算 z-index
safe: true
},
canPrint: true
}),
],
module: {
rules: [
{
test: /.(js|jsx)$/,
use: [
`babel-loader`,
],
},
{
test: /.(js|jsx)$/,
loader: `babel-loader`,
exclude: /node_modules/,
},
{
test: /.(css|pcss)$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: "css-loader!postcss-loader"
})
},
{
test: /.(png|jpg|gif|ttf|eot|woff|woff2|svg)$/,
loader: `url-loader?limit=8192&name=[name].[hash:8].[ext]&publicPath=` + webpackFile.resourcePrefix + `&outputPath=` + webpackFile.resource + `/`
},
{
test: /.swf$/,
loader: `file?name=js/[name].[ext]`
}
]
}
});
let pages = entry;
for (let chunkName in pages) {
let conf = {
filename: chunkName + `.html`,
template: `index.html`,
inject: true,
title: webpackCom.titleFun(chunkName,pages[chunkName][1]),
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
},
chunks: [`manifest`, `vendor`, `common`, chunkName],
hash: false,
chunksSortMode: `dependency`
};
config.plugins.push(new HtmlWebpackPlugin(conf));
}
/* 清除 dist */
config.plugins.push(new CleanWebpackPlugin([webpackFile.proDirectory], {root: path.resolve(__dirname, `../../`), verbose: true, dry: false}));
/* 拷貝靜態資源 */
copyArr.map(function (data) {
return config.plugins.push(data)
});
module.exports = config;
構建多介面
整體架構搭建起來之後
app -> component
$ mkdir demo && cd demo
$ touch Index.jsx
import React from `react`;
class Index extends React.Component {
render() {
return (
<div className="demo">
寫個demo
</div>
);
}
}
export default Index;
在config -> entry
module.exports = [
{
name: `index`,
path: `index/Index.jsx`,
title: `首頁`,
keywords: `首頁`,
description: `首頁`
},
{
name: `demo`,
path: `demo/Index.jsx`,
title: `demo`,
keywords: `demo`,
description: `demo`
},
{
name: `demo1`,
path: `demo1/Index.jsx`,
title: `demo1`,
keywords: `demo1`,
description: `demo1`
}
];
然後直接執行 npm run create-dev 就會在devBuild 和 entryBuild 中新增一個新的demo.html 和 demo.js
package.json
{
"name": "webpack_es6",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "webpack-dev-server --devtool eval --progress --colors --profile --config config/webpack/webpack.dev.conf.js",
"entry": "node config/entry/entryBuild.js",
"devBuildHtml": "node config/webpack/webpack.devBuildHtml.conf.js",
"create-dev": "npm run entry && npm run devBuildHtml",
"build": "BABEL_ENV=production && webpack --progress --colors --config config/webpack/webpack.prod.conf.js",
"test": "echo "Error: no test specified" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"react": "^16.3.0",
"react-dom": "^16.3.0"
},
"devDependencies": {
"babel-cli": "^6.26.0",
"babel-eslint": "^8.2.2",
"babel-loader": "^7.1.4",
"babel-preset-env": "^1.6.1",
"babel-preset-react": "^6.24.1",
"babel-preset-react-hmre": "^1.1.1",
"cache-loader": "^1.2.2",
"clean-webpack-plugin": "^0.1.19",
"copy-webpack-plugin": "^4.5.1",
"css-loader": "^0.28.11",
"eslint": "^4.19.1",
"eslint-config-react-app": "^2.1.0",
"eslint-loader": "^2.0.0",
"eslint-plugin-flowtype": "^2.46.1",
"eslint-plugin-import": "^2.10.0",
"eslint-plugin-jsx-a11y": "^5.1.1",
"eslint-plugin-react": "^7.7.0",
"extract-text-webpack-plugin": "^4.0.0-beta.0",
"file": "^0.2.2",
"file-loader": "^1.1.11",
"html-webpack-plugin": "^3.1.0",
"optimize-css-assets-webpack-plugin": "^4.0.0",
"postcss-cssnext": "^3.1.0",
"postcss-loader": "^2.1.3",
"precss": "^3.1.2",
"react-dev-utils": "^5.0.0",
"style-loader": "^0.20.3",
"url-loader": "^1.0.1",
"webpack": "^4.4.1",
"webpack-cli": "^2.0.13",
"webpack-dev-server": "^3.1.1",
"webpack-merge": "^4.1.2"
},
"eslintConfig": {
"extends": "react-app",
"rules": {
"import/no-webpack-loader-syntax": 0,
"no-script-url": 0,
"jsx-a11y/href-no-hash": 2
}
}
}
開發環境小技巧
在開發環境新增cache-loader 可以提升在開發環境的編譯速度