以前一直用官方的脚手架来建立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解析以及字体、图片、多媒体文件的打包,而且加上代码的分割,提取公共代码等