问题描述
我正在尝试在我的 React 通用应用程序中添加用于代码拆分的可加载组件库。我的项目中有 CSS 模块,它曾经运行良好。但是后来我添加了用于代码拆分的可加载组件库。现在服务器正在工作,但 CSS 没有工作,并且页面在没有 CSS 的情况下加载。我检查了 stats.json 文件,但它缺少 CSS 文件。
webpack.config.js:
'use strict';
const fs = require('fs');
const path = require('path');
const webpack = require('webpack');
const resolve = require('resolve');
const PnpWebpackPlugin = require('pnp-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin');
const TerserPlugin = require('terser-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const safePostCssparser = require('postcss-safe-parser');
const ManifestPlugin = require('webpack-manifest-plugin');
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
const WorkBoxWebpackPlugin = require('workBox-webpack-plugin');
const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent');
// const ESLintPlugin = require('eslint-webpack-plugin');
const paths = require('./paths');
const modules = require('./modules');
const getClientEnvironment = require('./env');
const ModuleNotFoundplugin = require('react-dev-utils/ModuleNotFoundplugin');
const ForkTsCheckerWebpackPlugin = require('react-dev-utils/ForkTsCheckerWebpackPlugin');
const typescriptFormatter = require('react-dev-utils/typescriptFormatter');
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
const LoadablePlugin = require('@loadable/webpack-plugin')
const postcssnormalize = require('postcss-normalize');
const appPackageJson = require(paths.appPackageJson);
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false';
const webpackDevClientEntry = require.resolve(
'react-dev-utils/webpackHotDevClient'
);
const reactRefreshOverlayEntry = require.resolve(
'react-dev-utils/refreshOverlayInterop'
);
const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== 'false';
const imageInlinesizeLimit = parseInt(
process.env.IMAGE_INLINE_SIZE_LIMIT || '10000'
);
const useTypeScript = fs.existsSync(paths.appTsConfig);
const swSrc = paths.swSrc;
const cssRegex = /\.css$/;
const cssModuleRegex = /\.css$/;
const sassRegex = /\.(scss|sass)$/;
const sassModuleRegex = /\.module\.(scss|sass)$/;
const hasJsxRuntime = (() => {
if (process.env.disABLE_NEW_JSX_TRANSFORM === 'true') {
return false;
}
try {
require.resolve('react/jsx-runtime');
return true;
} catch (e) {
return false;
}
})();
module.exports = function (webpackEnv) {
const isEnvDevelopment = webpackEnv === 'development';
const isEnvProduction = webpackEnv === 'production';
const isEnvProductionProfile =
isEnvProduction && process.argv.includes('--profile');
const env = getClientEnvironment(paths.publicUrlOrPath.slice(0,-1));
const shouldUseReactRefresh = env.raw.FAST_REFRESH;
const getStyleLoaders = (cssOptions,preProcessor) => {
const loaders = [
isEnvDevelopment && require.resolve('style-loader'),isEnvProduction && {
loader: MiniCssExtractPlugin.loader,index.html folder
// in production `paths.publicUrlOrPath` can be a relative path
options: paths.publicUrlOrPath.startsWith('.')
? { publicPath: '../../' }
: {},},{
loader: require.resolve('css-loader'),options: cssOptions,{
loader: require.resolve('postcss-loader'),options: {
ident: 'postcss',plugins: () => [
require('postcss-flexbugs-fixes'),require('postcss-preset-env')({
autoprefixer: {
flexBox: 'no-2009',stage: 3,}),postcssnormalize(),],sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,].filter(Boolean);
if (preProcessor) {
loaders.push(
{
loader: require.resolve('resolve-url-loader'),options: {
sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,root: paths.appSrc,{
loader: require.resolve(preProcessor),options: {
sourceMap: true,}
);
}
return loaders;
};
return {
mode: isEnvProduction ? 'production' : isEnvDevelopment && 'development',// Stop compilation early in production
bail: isEnvProduction,devtool: isEnvProduction
? shouldUseSourceMap
? 'source-map'
: false
: isEnvDevelopment && 'cheap-module-source-map',entry:
isEnvDevelopment && !shouldUseReactRefresh
? [
webpackDevClientEntry,paths.appIndexJs,]
: paths.appIndexJs,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',// We inferred the "public path" (such as / or /my-project) from homepage.
publicPath: paths.publicUrlOrPath,devtoolmodulefilenameTemplate: isEnvProduction
? info =>
path
.relative(paths.appSrc,info.absoluteResourcePath)
.replace(/\\/g,'/')
: isEnvDevelopment &&
(info => path.resolve(info.absoluteResourcePath).replace(/\\/g,'/')),globalObject: 'this',optimization: {
minimize: isEnvProduction,minimizer: [
new TerserPlugin({
terserOptions: {
parse: {
ecma: 8,compress: {
ecma: 5,warnings: false,inline: 2,mangle: {
safari10: true,// Added for profiling in devtools
keep_classnames: isEnvProductionProfile,keep_fnames: isEnvProductionProfile,output: {
ecma: 5,comments: false,ascii_only: true,sourceMap: shouldUseSourceMap,new OptimizeCSSAssetsPlugin({
cssprocessorOptions: {
parser: safePostCssparser,map: shouldUseSourceMap
? {
inline: false,annotation: true,}
: false,cssprocessorPluginoptions: {
preset: ['default',{ minifyFontValues: { removeQuotes: false } }],splitChunks: {
chunks: 'all',name: false,runtimeChunk: {
name: entrypoint => `runtime-${entrypoint.name}`,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',...(isEnvProductionProfile && {
'react-dom$': 'react-dom/profiling','scheduler/tracing': 'scheduler/tracing-profiling',...(modules.webpackAliases || {}),plugins: [
new ModuleScopePlugin(paths.appSrc,[
paths.appPackageJson,reactRefreshOverlayEntry,]),resolveLoader: {
plugins: [
PnpWebpackPlugin.moduleLoader(module),module: {
strictExportPresence: true,rules: [
{ parser: { requireEnsure: false } },{
oneOf: [
{
test: [/\.avif$/],loader: require.resolve('url-loader'),options: {
limit: imageInlinesizeLimit,mimetype: 'image/avif',name: 'static/media/[name].[hash:8].[ext]',{
test: [/\.bmp$/,/\.gif$/,/\.jpe?g$/,/\.png$/],{
test: /\.(js|mjs|jsx|ts|tsx)$/,include: paths.appSrc,loader: require.resolve('babel-loader'),options: {
customize: require.resolve(
'babel-preset-react-app/webpack-overrides'
),presets: [
[
require.resolve('babel-preset-react-app'),{
runtime: hasJsxRuntime ? 'automatic' : 'classic',plugins: [
[
require.resolve('babel-plugin-named-asset-import'),{
loaderMap: {
svg: {
ReactComponent:
'@svgr/webpack?-svgo,+titleProp,+ref![path]',isEnvDevelopment &&
shouldUseReactRefresh &&
require.resolve('react-refresh/babel'),].filter(Boolean),cacheCompression: false,compact: isEnvProduction,{
test: /\.(js|mjs)$/,exclude: /@babel(?:\/|\\{1,2})runtime/,options: {
babelrc: false,configFile: false,compact: false,presets: [
[
require.resolve('babel-preset-react-app/dependencies'),{ helpers: true },cacheDirectory: true,sourceMaps: shouldUseSourceMap,inputSourceMap: shouldUseSourceMap,{
test: cssRegex,exclude: cssModuleRegex,use: getStyleLoaders({
importLoaders: 1,sourceMap: isEnvProduction
? shouldUseSourceMap
: isEnvDevelopment,sideEffects: true,{
test: cssModuleRegex,modules: {
getLocalIdent: getCSSModuleLocalIdent,{
loader: require.resolve('file-loader'),exclude: [/\.(js|mjs|jsx|ts|tsx)$/,/\.html$/,/\.json$/],options: {
name: 'static/media/[name].[hash:8].[ext]',plugins: [
new LoadablePlugin({filename:'../dist/loadable-stats.json',writetodisk:true}),// Generates an `index.html` file with the <script> injected.
new LoadablePlugin(),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
)
),isEnvDevelopment &&
shouldUseReactRefresh &&
new ReactRefreshWebpackPlugin({
overlay: {
entry: webpackDevClientEntry,sockIntegration: false,isEnvDevelopment && new CaseSensitivePathsPlugin(),isEnvDevelopment &&
new WatchMissingNodeModulesPlugin(paths.appNodeModules),isEnvProduction &&
new MiniCssExtractPlugin({
filename: 'static/css/[name].[contenthash:8].css',chunkFilename: 'static/css/[name].[contenthash:8].chunk.css',new ManifestPlugin({
fileName: 'asset-manifest.json',publicPath: paths.publicUrlOrPath,generate: (seed,files,entrypoints) => {
const manifestFiles = files.reduce((manifest,file) => {
manifest[file.name] = file.path;
return manifest;
},seed);
const entrypointFiles = entrypoints.main.filter(
fileName => !fileName.endsWith('.map')
);
return {
files: manifestFiles,entrypoints: entrypointFiles,};
},new webpack.IgnorePlugin(/^\.\/locale$/,/moment$/),isEnvProduction &&
fs.existsSync(swSrc) &&
new WorkBoxWebpackPlugin.InjectManifest({
swSrc,dontCacheBustURLsMatching: /\.[0-9a-f]{8}\./,exclude: [/\.map$/,/asset-manifest\.json$/,/LICENSE/],template/pwa/issues/13#issuecomment-722667270
maximumFileSizetoCacheInBytes: 5 * 1024 * 1024,// TypeScript type checking
useTypeScript &&
new ForkTsCheckerWebpackPlugin({
typescript: resolve.sync('typescript',{
basedir: paths.appNodeModules,async: isEnvDevelopment,checkSyntacticErrors: true,resolveModuleNameModule: process.versions.pnp
? `${__dirname}/pnpTs.js`
: undefined,resolveTypeReferenceDirectiveModule: process.versions.pnp
? `${__dirname}/pnpTs.js`
: undefined,tsconfig: paths.appTsConfig,reportFiles: [
'../**/src/**/*.{ts,tsx}','**/src/**/*.{ts,'!**/src/**/__tests__/**','!**/src/**/?(*.)(spec|test).*','!**/src/setupProxy.*','!**/src/setupTests.*',silent: true,formatter: isEnvProduction ? typescriptFormatter : undefined,node: {
module: 'empty',dgram: 'empty',dns: 'mock',fs: 'empty',http2: 'empty',net: 'empty',tls: 'empty',child_process: 'empty',performance: false,};
};
server.js:
// import thunk from 'redux-thunk';
import { createReactAppExpress } from '@cra-express/core';
import { getinitialData } from '@cra-express/router-prefetcher';
import { HelmetProvider } from 'react-helmet-async';
// import Cookies from 'cookies';
import { getStoredState,persistCombineReducers } from 'redux-persist';
// import { CookieStorage,NodeCookiesWrapper } from 'redux-persist-cookie-storage';
import autoMergeLevel1 from 'redux-persist/lib/stateReconciler/autoMergeLevel1';
import storage from "redux-persist/lib/storage";
// import StyleContext from 'isomorphic-style-loader/StyleContext'
import routes from '../src/routes';
import {store} from '../src/index'
import {ChunkExtractor,ChunkExtractorManager} from '@loadable/server'
const path = require('path');
const React = require('react');
const { Provider } = require('react-redux');
const { StaticRouter } = require('react-router-dom');
const { createStore,applyMiddleware,compose} = require('redux');
const { default: App } = require('../src/App');
const { default: reducer } = require('../src/redux/reducers');
const clientBuildpath = path.resolve(__dirname,'../client');
const statsFile=path.resolve(__dirname,'../dist/loadable-stats.json')
let tag = '';
//let store;
let AppClass = App;
let serverData;
let helmetCtx;
// console.log("REDUCERS",reducer)
const app = createReactAppExpress({
clientBuildpath,universalRender: handleUniversalRender,onFinish(req,res,html) {
const { helmet } = helmetCtx;
const helmetTitle = helmet.title.toString();
const helmetMeta = helmet.Meta.toString();
const newHtml = html
.replace('{{HELMET_TITLE}}',helmetTitle)
.replace('{{HELMET_Meta}}',helmetMeta);
res.send(newHtml);
},onEndReplace(html) {
const state = store.getState();
//console.log("----SERVER getState----",store.getState());
return html.replace(
'{{SCRIPT}}',`${tag}<script>
window.__PRELOADED_STATE__ = ${JSON.stringify(state).replace(
/</g,'\\u003c'
)};
window.__INITIAL_DATA__ = ${JSON.stringify(serverData).replace(
/</g,'\\u003c'
)};
</script>`
);
}
});
function handleUniversalRender(req,res) {
const context = {};
helmetCtx = {};
// const cookieJar = new NodeCookiesWrapper(new Cookies(req,res));
const persistConfig = {
key: 'root',storage: storage,// storage: new CookieStorage(cookieJar),stateReconciler: autoMergeLevel1,};
let preloadedState;
getStoredState(persistConfig)
.then(preloadedState => {
//console.log("SERVER Preloded State",preloadedState);
})
try {
preloadedState = {
test: 'presisited Data'
};
} catch (e) {
preloadedState = {};
}
const rootReducer = persistCombineReducers(persistConfig,reducer);
/* store = createStore(
rootReducer,preloadedState,applyMiddleware(thunk)
);*/
return getinitialData(req,routes)
.then(data => {
const css = new Set();
const insertCss = (...styles) => styles.forEach(style => css.add(style._getCss()));
const extractor=new ChunkExtractor({statsFile})
const scriptTags = extractor.getScriptTags()
serverData = data;
// console.log("CSS FILES",scriptTags);
const app = (
<HelmetProvider context={helmetCtx}>
<StaticRouter location={req.url} context={context}>
<Provider store={store}>
{/* <StyleContext.Provider value={{ insertCss }}> */}
<ChunkExtractorManager extractor={extractor}>
<AppClass routes={routes} initialData={data} store={store}/>
</ChunkExtractorManager>
{/* </StyleContext.Provider> */}
</Provider>
</StaticRouter>
</HelmetProvider>
);
return app;
})
.catch(err => {
console.error(err);
res.send(500);
});
}
if (module.hot) {
module.hot.accept('../src/App',() => {
const { default: App } = require('../src/App');
AppClass = App;
console.log('✅ Server hot reloaded App');
});
module.hot.accept('../src/routes',() => {
console.log('✅ Server hot reloaded routes');
});
}
export default app;
css modules works fine with client side and worked fine without code splitting in server side but not working with loadable components. i am using cra-universal library for server side rendering.
can someone please help me with this issue and it will be greatly appreciated.
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)