以前一直用官方的腳手架來建立react應用,用eject指令後查看weback的一些配置後發現就是基於webpack的多層優化,但裏面用到的東西太多也比較雜,每次都不知道本身的react應用到底用了哪些技術棧,因此打算本身從零搭建一個相似於官方的腳手架,方便個性化處理
https://github.com/jianjiache...
內部配置大部分作了詳細說明
執行方式:
cnpm install
cnpm run start
cnpm run buildcss
建立一個WebpackReact項目,使用cnpm init -y
指令
-y: 避免一直確認輸入yeshtml
npm install --save-dev webpack webpack-cli
node
建立一個公共打包的配置文件名爲 webpack.common.config.js
react
WebpackReact ├── config │ ├── webpack.common.config.js
const path = require('path'); module.exports = { entry: { app: './src/app.js', }, output: { filename: 'js/bundle.js', path: path.resolve(__dirname, '../dist') } }
寫入console.log('hello world')
webpack
WebpackReact ├── src │ ├── app.js
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", + "start": "webpack --config ./config/webpack.common.config.js" },
npm run start
在dist/js ,其中有一個js文件: bundle.js
git
!(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('hello world');// 在這裏能夠看到咱們的代碼 } ]);
咱們須要一個公共的配置,而後基於這個公共的配置上,生成生產環境的配置和開發環境的配置github
npm install --save-dev webpack-merge
web
WebpackReact-----------------------------------根目錄 ├── config │ ├── webpack.common.config.js---------------公共的配置 │ ├── webpack.dev.config.js------------------生產環境的配置 │ └── webpack.prod.config.js-----------------開發環境的配置
在生產環境webpack.prod.config.js
就能夠這樣使用merge
chrome
const merge = require('webpack-merge'); const common = require('./webpack.common.config.js'); module.exports = merge(common, { mode: 'production', });
而後修改package.json
文件,添加指令npm
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", - "start": "webpack --config ./config/webpack.common.config.js", + "build": "webpack --config ./config/webpack.prod.config.js" },
修改app.js
代碼:
var root =document.getElementById('root'); root.innerHTML = 'hello, webpack!';
運行代碼npm run build
就能夠看到dist文件下打包的js
咱們建立一個public
來測試打包運行的結果,後面會用插件來幹這件事
WebpackReact ├── config │ ├── webpack.common.config.js │ ├── webpack.dev.config.js │ └── webpack.prod.config.js ├── dist ├── package.json ├── public │ └── index.html ------------------建立來測試打包的JS是否可用 ├── readme.md ├── src │ ├── app.js
在public
裏建立一個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>搭建Webpack4+React腳手架</title> </head> <body> <div id="root"></div> <script src="../dist/js/bundle.js"></script> </body> </html>
直接在瀏覽器裏運行這個文件
npm install --save react react-dom
在 src 文件夾下新建一個js文件, index.js ,用於渲染根組件。
WebpackReact ├── config │ ├── webpack.common.config.js │ ├── webpack.dev.config.js │ └── webpack.prod.config.js ├── dist ├── package.json ├── public │ └── index.html ------------------建立來測試打包的JS是否可用 ├── readme.md ├── src │ ├── app.js + |——index.js
import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; ReactDOM.render(<App />, document.getElementById('root'));
並用jsx語法重寫 app.js :
import React from 'react'; function App() { return ( <div className="App">Hello World</div> ); } export default App;
修改webpack入口文件配置
const path = require('path'); module.exports = { entry: { index: './src/index.js', }, output: { filename: 'js/bundle.js', path: path.resolve(__dirname, '../dist') } }
直接從新運行npm run build
,會發現打包失敗,緣由是webpack沒法識別你的react語法啊,須要預先編譯
安裝相關的bable依賴
npm install --save-dev babel-loader @babel/preset-react @babel/preset-env @babel/core
根目錄新建一個配置文件.babelrc
配置相關的"presets"
{ "presets": [// presets 就是 plugins 的組合 [ "@babel/preset-env", { "targets": { // 大於相關瀏覽器版本無需用到 preset-env "edge": 17, "firefox": 60, "chrome": 67, "safari": 11.1 }, // 根據代碼邏輯中用到的 ES6+語法進行方法的導入,而不是所有導入 "useBuiltIns": "usage" //useBuiltIns就是是否開啓自動支持 polyfill,它能自動給每一個文件添加其須要的poly-fill。 } ], "@babel/preset-react" ], "plugins": [ ["@babel/plugin-proposal-decorators", { "legacy": true }]// 轉義ES7的修飾器@ ] }
再修改webpack.common.config.js
,添加以下代碼:
const path = require('path'); module.exports = { entry: { index: './src/index.js', }, output: { filename: 'js/bundle.js', path: path.resolve(__dirname, '../dist') }, module: { rules: [ { test: /\.(js|jsx)$/, use: 'babel-loader', exclude: /node_modules/,//不須要去轉譯"node\_modules"這裏面的文件。 } ] } }
如今從新打包應該能成功了,別且運行咱們手動建立的HTML文件能看到一個hello world
html-webpack-plugin
用來生成HTML文件自動引用打包好的JS文件
npm install --save-dev html-webpack-plugin
const merge = require('webpack-merge'); const common = require('./webpack.common.config.js'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = merge(common, { mode: 'production', plugins: [ new HtmlWebpackPlugin({ filename: 'index.html', template: 'public/index.html',// 定義的html爲模板生成 從根路徑開始查找 inject: 'body', minify: {// 壓縮HTML文件 removeComments: true,//去除註釋 collapseWhitespace: true,//去除空格 }, }), ] });
在咱們的/src
目錄下,新建一個文件名爲app.css
,並輸入如下代碼:
.App { height: 200px; display: flex; justify-content: center; align-items: center; background-color: lightcoral; } h1 { font-size: 16px; color: #fff; }
在app.js中引入css
import './app.css';
配置loader
wbpack只能編譯js文件,css文件是沒法被識別並編譯的,咱們須要loader加載器來進行預處理。 首先安裝style-loader
和css-loader
:
npm install --save-dev style-loader css-loader
在module的rules裏添加配置
{ test: /\.css$/, use: [ 'style-loader',// 最後計算完的css,將會使用style-loader生成一個內容爲最終解析完的css代碼的style標籤,放到head標籤裏 'css-loader' // css-loader加載器去解析這個文件,遇到「@import」等語句就將相應樣式文件引入 ] }
能夠看到CSS已經內嵌到了咱們頁面,咱們能夠優化一下讓它單獨提出來,下一章會詳細說明
css編譯器順序可能會致使了Webpack編譯報錯
Webpack選擇了compose方式,而不是pipe的方式而已,從右往左的函數式,因此loader的順序編程了從右往左
const compose = (...fns) => x => fns.reduceRight((v, f) => f(v), x); const add1 = n => n + 1; //加1 const double = n => n * 2; // 乘2 const add1ThenDouble = compose( double, add1 ); add1ThenDouble(2); // 6 // ((2 + 1 = 3) * 2 = 6)
由於執行順序必須是(先用加載器解析,再最終解析到style標籤)css-loader->style-loader->
因此style-loader放在css-loader的左邊,也就是上面
WebpackReact ├── config │ ├── webpack.common.config.js ------------------------公共的配置 │ ├── webpack.dev.config.js ---------------------------開發環境配置(還未配置) │ └── webpack.prod.config.js -------------------------生產環境配置 ├── dist -----------------------------------------------打包的文件 ├── package.json ---------------------------------------配置文件 ├── public │ └── index.html -------------------------------------手動建立的入口後面會用插件打入包裏 ├── readme.md ------------------------------------------說明文檔 ├── src │ ├── app.js ----------------------------------------組件 │ ├── app.css --------------------------------------樣式文件 │ └── index.js --------------------------------------入口文件
下次會在這個目錄的基礎上加上less,sass解析以及字體、圖片、多媒體文件的打包,而且加上代碼的分割,提取公共代碼等