react- scripts 中的一系列技术栈配置都 在使用create-react-app构建项目时并未开放,需要使用以下命令弹射到应用的顶层以供个性化,不过特别注意,整个过程是不可逆的 ,eject后就可以看见熟悉的webpack配置目录config。
npm run eject
webpack 是什么?归根到底,webpack 就是一个.js 配置文件,你的架构好或坏 都体现在webpack.config.js这个配置里。
webpack配置中最重要也是必选的两项是入口(Entry)和出口(Output)。入口的作用是告诉webpack从哪里开始寻找依赖,并且编译,出口则用来配置编译后的文件储存位置和文件名。
通过观察发现 entry 处 只配置了一个入口 paths.appIndexJs ,点进去可以看见指向index.js。
entry: [
// Include an alternative client for WebpackDevServer. A client's job is to
// connect to WebpackDevServer by a socket and get notified about changes.
// When you save a file, the client will either apply hot updates (in case
// of CSS changes), or refresh the page (in case of JS changes). When you
// make a syntax error, this client will display a syntax error overlay.
// Note: instead of the default WebpackDevServer client, we use a custom one
// to bring better experience for Create React App users. You can replace
// the line below with these two lines if you prefer the stock client:
// require.resolve('webpack-dev-server/client') + '?/',
// require.resolve('webpack/hot/dev-server'),
isEnvDevelopment &&
require.resolve('react-dev-utils/webpackHotDevClient'),
// Finally, this is your app's code:
paths.appIndexJs,
// We include the app code last so that if there is a runtime error during
// initialization, it doesn't blow up the WebpackDevServer client, and
// changing JS code would still trigger a refresh.
].filter(Boolean),
webpack官方文档有介绍 入口起点(entry points)的多种书写形式如下:
entry: './path/to/my/entry/file.js' //单个入口(简写)语法
entry: {
main: './path/to/my/entry/file.js' //对象语法
}
entry: { //多页面应用程序
pageOne: './src/pageOne/index.js',
pageTwo: './src/pageTwo/index.js',
pageThree: './src/pageThree/index.js'
}
这里可以看出将 单个入口 改写为 对象语法 的多页面应用程序就可以达到多入口的目的。
工程化 可以用到 globby 的帮助 搜寻 src 目录下的多页面应用:
// 引入 globby 模块
const globby = require('globby');
// 入口文件路径
const entriesPath = globby.sync([resolveApp('src') + '/*/index.tsx']);
通过遍历 globby 获得的目录数组 获得 多页面 路径入口文件:
function getEntries(){
const entries = {};
const files = paths.entriesPath;
files.forEach(filePath => {
let tmpArr = filePath.split('/');
let name = tmpArr[tmpArr.length - 2];
entries[name] = [
filePath,
];
isEnvDevelopment && entries[name].push(require.resolve('react-dev-utils/webpackHotDevClient'));
//这份代码是由react官方的create-react-app提供的热构建插件
});
return entries;
}
入口 boundle 如何插入对应的 html 中?
我们通常需要这个插件HtmlWebpackPlugin
自动处理,目前代码是这样:
new HtmlWebpackPlugin(
Object.assign(
{},
{
inject: true,
template: paths.appHtml,
},
isEnvProduction
? {
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}
: undefined
)
),
配置 HtmlWebpackPlugin 插件, 指定入口生成对应的 html 文件,有多少个页面就需要 new 多少个 HtmllWebpackPlugin webpack配置多入口后,只是编译出多个入口的JS,同时入口的HTML文件由HtmlWebpackPlugin生成,也需做配置。 chunks,指明哪些 webpack入口的JS会被注入到这个HTML页面。如果不配置,则将所有entry的JS文件都注入HTML。 filename,指明生成的HTML路径,如果不配置就是build/index.html,需要 配置新的filename,避免与第一个入口的index.html相互覆盖。
我们已经 通过 globby 的帮助拿到多入口对象 entries,这里可以遍历处理:
const htmlPlugin = Object.keys(entries).map(item => {
return new HtmlWebpackPlugin(
Object.assign(
{},
{
inject: true,
template: paths.appHtml,
filename: item + '.html',
chunks: [item]
},
isEnvProduction
? {
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}
: undefined
)
);
});
将 处理完成的 htmlPlugin 放入对应的 plugins,这里可以使用扩展运算符( spread ):
plugins: [
...htmlPlugin,
以下代码省略...
如此能正常的打包出正确的 多页面应用了,但是开发环境会受到静态资源名字没有Hash 而出现白屏,需要将 出口(Output)开发环境静态资源加上 Hash值:
output: {
// The build folder.
path: isEnvProduction ? paths.appBuild : undefined,
// Add /* filename */ comments to generated require()s in the output.
pathinfo: isEnvDevelopment,
// There will be one main bundle, and one file per asynchronous chunk.
// In development, it does not produce real files.
filename: isEnvProduction
? 'static/js/[name].[contenthash:8].js'
: isEnvDevelopment && 'static/js/[name].bundle.js',
// TODO: remove this when upgrading to webpack 5
futureEmitAssets: true,
// There are also additional JS chunk files if you use code splitting.
chunkFilename: isEnvProduction
? 'static/js/[name].[contenthash:8].chunk.js'
: isEnvDevelopment && 'static/js/[name].chunk.js',
以下代码省略...