文件结构分析
env.js
// Load environment variables from .env* files. Suppress warnings using silent
// if this file is missing. dotenv will never modify any environment variables
// that have already been set. Variable expansion is supported in .env files.
// https://github.com/motdotla/dotenv
// https://github.com/motdotla/dotenv-expand
// 注释其实写的很清楚,用 dotenv 和 dotenv-expand 工具读取所有.env* 文件,获取变量
dotenvFiles.forEach(dotenvFile => {if (fs.existsSync(dotenvFile)) {require('dotenv-expand')(require('dotenv').config({path: dotenvFile,})
);
}
});
// 最后赋值给 process.env 对象
// Stringify all values so we can feed into Webpack DefinePlugin
const stringified = {'process.env': Object.keys(raw).reduce((env, key) => {env[key] = JSON.stringify(raw[key]);
return env;
}, {}),
};
modules.js
其实就两个函数,为了读取 jsconfig.json 或者 tsconfig.json 文件中的配置,两个文件不能同时存在.
paths.js 文件前面说过了
pnpTs.js 这块确实不太清楚,不敢乱讲。看内容是关于 typescript 的,有 ts-pnp 插件配置,有大神帮忙解答下。
webpack.config.js
所有 webpack 基础配置 (除了 devServer) 都在这里面了
暴露一个函数接口,参数就是环境变量
module.exports = function(webpackEnv) {
const isEnvDevelopment = webpackEnv === 'development';
const isEnvProduction = webpackEnv === 'production';
...
}
通过 ’development’ 或者 ’production’ 去加载不同的配置,
return {
mode: isEnvProduction ? 'production' : isEnvDevelopment && 'development',
bail: isEnvProduction,
devtool: isEnvProduction
? shouldUseSourceMap
? 'source-map'
: false
: isEnvDevelopment && 'cheap-module-source-map',
entry: [
isEnvDevelopment &&
require.resolve('react-dev-utils/webpackHotDevClient'),
paths.appIndexJs,
].filter(Boolean),
output: {
path: isEnvProduction ? paths.appBuild : undefined,
pathinfo: isEnvDevelopment,
filename: isEnvProduction
? 'static/js/[name].[contenthash:8].js'
: isEnvDevelopment && 'static/js/bundle.js',
futureEmitAssets: true,
chunkFilename: isEnvProduction
? 'static/js/[name].[contenthash:8].chunk.js'
: isEnvDevelopment && 'static/js/[name].chunk.js',
publicPath: publicPath,
devtoolModuleFilenameTemplate: isEnvProduction
? info =>
path
.relative(paths.appSrc, info.absoluteResourcePath)
.replace(/\\/g, '/')
: isEnvDevelopment &&
(info => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')),
},
optimization: {
minimize: isEnvProduction,
minimizer: [
new TerserPlugin({
terserOptions: {
parse: {ecma: 8,},
compress: {
ecma: 5,
warnings: false,
inline: 2,
},
mangle: {safari10: true,},
output: {
ecma: 5,
comments: false,
ascii_only: true,
},
},
parallel: !isWsl,
cache: true,
sourceMap: shouldUseSourceMap,
}),
new OptimizeCSSAssetsPlugin({
cssProcessorOptions: {
parser: safePostCssParser,
map: shouldUseSourceMap
? {
inline: false,
annotation: true,
}
: false,
},
}),
],
splitChunks: {
chunks: 'all',
name: false,
},
runtimeChunk: true,
},
resolve: {modules: ['node_modules', paths.appNodeModules].concat(modules.additionalModulePaths || []
),
extensions: paths.moduleFileExtensions
.map(ext => `.${ext}`)
.filter(ext => useTypeScript || !ext.includes('ts')),
alias: {'react-native': 'react-native-web',},
plugins: [PnpWebpackPlugin,],
},
resolveLoader: {
plugins: [PnpWebpackPlugin.moduleLoader(module),
],
},
module: {
strictExportPresence: true,
rules: [{ parser: { requireEnsure: false} },
{test: /\.(js|mjs|jsx|ts|tsx)$/,
enforce: 'pre',
use: [
{
options: {formatter: require.resolve('react-dev-utils/eslintFormatter'),
eslintPath: require.resolve('eslint'),
},
loader: require.resolve('eslint-loader'),
},
],
include: paths.appSrc,
},
{
oneOf: [
{test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
loader: require.resolve('url-loader'),
options: {
limit: 10000,
name: 'static/media/[name].[hash:8].[ext]',
},
},
{test: /\.(js|mjs|jsx|ts|tsx)$/,
include: paths.appSrc,
loader: require.resolve('babel-loader'),
options: {
customize: require.resolve('babel-preset-react-app/webpack-overrides'),
plugins: [
[require.resolve('babel-plugin-named-asset-import'),
{
loaderMap: {
svg: {ReactComponent: '@svgr/webpack?-svgo,+ref![path]',
},
},
},
],
],
cacheDirectory: true,
cacheCompression: isEnvProduction,
compact: isEnvProduction,
},
},
{test: /\.(js|mjs)$/,
exclude: /@babel(?:\/|\\{1,2})runtime/,
loader: require.resolve('babel-loader'),
options: {
babelrc: false,
configFile: false,
compact: false,
presets: [
[require.resolve('babel-preset-react-app/dependencies'),
{helpers: true},
],
],
cacheDirectory: true,
cacheCompression: isEnvProduction,
sourceMaps: false,
},
},
{
test: cssRegex,
exclude: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
sourceMap: isEnvProduction && shouldUseSourceMap,
}),
sideEffects: true,
},
{
test: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
sourceMap: isEnvProduction && shouldUseSourceMap,
modules: true,
getLocalIdent: getCSSModuleLocalIdent,
}),
},
{
test: sassRegex,
exclude: sassModuleRegex,
use: getStyleLoaders(
{
importLoaders: 2,
sourceMap: isEnvProduction && shouldUseSourceMap,
},
'sass-loader'
),
sideEffects: true,
},
{
test: sassModuleRegex,
use: getStyleLoaders(
{
importLoaders: 2,
sourceMap: isEnvProduction && shouldUseSourceMap,
modules: true,
getLocalIdent: getCSSModuleLocalIdent,
},
'sass-loader'
),
},
{loader: require.resolve('file-loader'),
exclude: [/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
options: {name: 'static/media/[name].[hash:8].[ext]',
},
},
],
},
],
},
plugins: [new HtmlWebpackPlugin(),
...
]
}
webpackDevServer.config.js
devServer 的配置文件,暴露一个函数
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
const host = process.env.HOST || '0.0.0.0';
module.exports = function(proxy, allowedHost) {
// 参数 proxy 代理配置和 allowedHost
return {
...
contentBase: paths.appPublic,//public 文件夹路径
publicPath: '/',
hot: true,
https: protocol === 'https',
host,
public: allowedHost,
proxy,
before(app,server){...}
}
};