大家入门react想必都是从这行命令开始的吧!
npx create-react-app my-app
这个命令会生成一个工程骨架,小伙伴就可以愉快的板砖了。搬久了发现工程越来越大,一打包好几兆,严重影响加载速度。这时候就需要多入口打包,将工程分为几个小的SPA。比如登录时一个SPA,内容管理是一个SPA,产品管理是一个SPA,每个SPA加载共同的JS,和自己的JS,无关的不加载。我们就需要配置webpack来改造工程,结果发现项目里根本没有webpack.config.js。
React团队就像你妈一样为你做好了饭,你吃就好了。把webpack进行了封装,把你当傻瓜。
在package.json你会发现有个react-scripts包,打开源码就可以找到相见恨晚的webpack.config.js。但是我们不能直接修改这个文件,可以使用以下两种方法来对webpack进行配置。
第一种方式 eject
执行命令
yarn eject
把封装的配置脚本释放到当前工程里,在config目录下就有webpack.config.js了。此过程是不可逆的,你也将失去react-scripts未来升级能给你带来的好处。
第二种方式 craco
感谢社区提供第二种方案,让你的代码还可以继续保持纯洁的肉体。首先安装一下@craco/craco 这个包。
yarn add @craco/craco
项目根目录下新建craco.config.js,在这个文件中覆盖默认的webpack.config即可。
craco.config.js如何配置请参阅文档:https://www.npmjs.com/package...
我们通过第二种方式实现多入口改造。改造思路:
- 保留react-create-app默认的入口,入口名为main,js为src/index.js,使用模板public/index.html生成index.html
- 为src/entries/下的每个子目录,创建一个同名的入口,入口js为src/entries/{entry-name}/index.js, 使用模板public/{entry-name}.html生成{entry-name}.html
按照这个思路我们需要把webpack config的entry项改为多入口,配置插件HtmlWebpackPlugin为每个entry生成对应的HTML。
其它要点:
- output.filename需要修改为按入口名生成bundle.js
- optimization.runtimeChunk需要修改为single 这样不同的入口会共用同一个runtime.js而不是每个生成一个
废话不多说,直接放码! - 默认webpack runtime代码会内嵌到HTML,可以在.env文件中设置INLINE_RUNTIME_CHUNK=false来禁用,或者通过代码删除InlineChunkHtmlPlugin
devServerConfig.historyApiFallback中disableDotRule设置为true,要不然开发服务器会把/xxx.html的请求重定向到/xxx上,无法打开入口html
const path = require('path'); const fs = require('fs'); const CracoLessPlugin = require('craco-less'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin'); function configureWebpack(webpackConfig, {env, paths}) { const isEnvDevelopment = env === 'development'; const isEnvProduction = env === 'production'; //配置HtmlWebpackPlugin用来产生一个独立的HTML function mkHtmlWebpackPlugin(chunks, filename, template) { return new HtmlWebpackPlugin({ inject: true, template: template || paths.appHtml, chunks, filename, ...isEnvProduction ? { minify: { removeComments: true, collapseWhitespace: true, removeRedundantAttributes: true, useShortDoctype: true, removeEmptyAttributes: true, removeStyleLinkTypeAttributes: true, keepClosingSlash: true, minifyJS: true, minifyCSS: true, minifyURLs: true, } } : undefined }); } //遍历src/entries为所有子目录创建一个webpack入口,并配置对应的HtmlWebpackPlugin const entriesDir = path.join(paths.appSrc, 'entries'); const fileNames = fs.readdirSync(entriesDir); const entries = {}; const htmlWebpackPlugins = []; fileNames.forEach(fileName => { const filePath = path.join(entriesDir, fileName); const file = fs.statSync(filePath); if(file.isDirectory()){ entries[fileName] = path.join(filePath, "index.js"); let template = path.join(paths.appPublic, fileName + ".html"); if (!fs.existsSync(template)) template = undefined; htmlWebpackPlugins.push(mkHtmlWebpackPlugin([fileName], fileName + ".html", template)); } }); //main为create-react-app默认创建的入口,保留下来。这样既可以实现原始的单入口,又可以实现多入口 webpackConfig.entry = { main: webpackConfig.entry, ...entries }; //覆盖默认的plugins配置 const defaultHtmlWebpackPluginIndex = webpackConfig.plugins.findIndex(plugin => plugin instanceof HtmlWebpackPlugin); webpackConfig.plugins.splice(defaultHtmlWebpackPluginIndex, 1, mkHtmlWebpackPlugin(["main"], "index.html"), ...htmlWebpackPlugins); //create-react-app默认用的是一个固定文件名,不适合多入口!改为按入口名生成输出文件名 if (isEnvDevelopment) webpackConfig.output.filename = 'static/js/[name].bundle.js'; //共用runtime bundle webpackConfig.optimization.runtimeChunk = "single"; // react-scripts默认在生产环境会将runtime chunk内嵌到html中 // 禁用该行为,使用独立的js // 也可以在根目录新建.env文件,设置INLINE_RUNTIME_CHUNK=false来禁用 // 不过配置入口太多了,不方便管理,直接这里用代码禁用好了 const inlineChunkHtmlPluginIndex = webpackConfig.plugins.findIndex(plugin => plugin instanceof InlineChunkHtmlPlugin); if (inlineChunkHtmlPluginIndex >= 0) webpackConfig.plugins.slice(inlineChunkHtmlPluginIndex, 1); return webpackConfig; } function configureDevServer(devServerConfig, { env, paths, proxy, allowedHost }) { devServerConfig.historyApiFallback = { disableDotRule: true, //禁用,否则当访问/xxx.html时服务器会自动去掉.html重写url为/xxx index: paths.publicUrlOrPath, verbose: true, }; return devServerConfig; } module.exports = { devServer: configureDevServer, webpack: { configure: configureWebpack, } };
通过上述代码即可完成多入口改造。具体实践代码可以参考
https://github.com/ikeyit/ike...
假设你有入口/src/entries/your-loved-mp4/index.js,访问
http://localhost:3000/your-lo...
即可打开该入口。
执行编译
yarn build
在build文件夹中可以看到生成了好多js,webpack会把共用和不共用的代码分到不同的包里。
执行命令分析Bundle Size
yarn analyze
(完)
欢迎关注作者的github项目,学习微服务:
一个支持多店铺的电商系统,基于Spring Boot的微服务构架
https://ikeyit.xyz
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。