https://necolas.github.io/react-native-web/docs/rendering/
在阅读了文档中的SSR示例之后,我仍然不知道如何实现SSR
我不想在其他框架(如NextJS )中应用SSR
有人能给我举个例子或者给我一些建议吗?
发布于 2022-12-01 18:03:54
我发布这篇文章,并不是为了直接回答最初的问题,因为它是直接针对NextJS的,OP需要独立于NextJS之类的框架。然而,用NextJS理解它可以让任何人更接近事物,因为它们依赖于Webpack配置,NextJS在封装配置中也使用这种配置。
首先要知道的是,一旦为Reacti原住民编写了一个包,它需要首先被转换,以便能够在网络上使用,与webpack config.externals一起使用。
let modulesToTranspile = [
'react-native',
'react-native-dotenv',
'react-native-linear-gradient',
'react-native-media-query',
'react-native-paper',
'react-native-view-more-text',
// 'react-native-vector-icons',
];然后,您需要将一些react本地包化名为react本机web等效于让包使用web版本的模块,如:
config.resolve.alias = {
...(config.resolve.alias || {}),
// Transform all direct `react-native` imports to `react-native-web`
'react-native$': 'react-native-web',
'react-native-linear-gradient': 'react-native-web-linear-gradient',
};在这一点上,你几乎得到了本质。其余的是正常Webpack配置的正常应用程序。此外,它还需要本机配置文件中的一些附加配置。我会张贴所有的吐露内容。
对于NextJS:next.config.js:
const path = require('path');
let modulesToTranspile = [
'react-native',
'react-native-dotenv',
'react-native-linear-gradient',
'react-native-media-query',
'react-native-paper',
'react-native-view-more-text',
// 'react-native-vector-icons',
];
// console.log('modules to transpile', modulesToTranspile);
// import ntm = from 'next-transpile-modules';
// const withTM = ntm(modulesToTranspile);
// logic below for externals has been extracted from 'next-transpile-modules'
// we won't use this modules as they don't allow package without 'main' field...
// https://github.com/martpie/next-transpile-modules/issues/170
const getPackageRootDirectory = m =>
path.resolve(path.join(__dirname, 'node_modules', m));
const modulesPaths = modulesToTranspile.map(getPackageRootDirectory);
const hasInclude = (context, request) => {
return modulesPaths.some(mod => {
// If we the code requires/import an absolute path
if (!request.startsWith('.')) {
try {
const moduleDirectory = getPackageRootDirectory(request);
if (!moduleDirectory) {
return false;
}
return moduleDirectory.includes(mod);
} catch (err) {
return false;
}
}
// Otherwise, for relative imports
return path.resolve(context, request).includes(mod);
});
};
const configuration = {
node: {
global: true,
},
env: {
ENV: process.env.NODE_ENV,
},
// optimizeFonts: false,
// target: 'serverless',
// bs-platform
// pageExtensions: ['jsx', 'js', 'bs.js'],
// options: { buildId, dev, isServer, defaultLoaders, webpack }
webpack: (config, options) => {
// config.experimental.forceSwcTransforms = true;
// console.log('fallback', config.resolve.fallback);
if (!options.isServer) {
// We shim fs for things like the blog slugs component
// where we need fs access in the server-side part
config.resolve.fallback.fs = false;
} else {
// SSR
// provide plugin
config.plugins.push(
new options.webpack.ProvidePlugin({
requestAnimationFrame: path.resolve(__dirname, './polyfills/raf.js'),
}),
);
}
// react-native-web
config.resolve.alias = {
...(config.resolve.alias || {}),
// Transform all direct `react-native` imports to `react-native-web`
'react-native$': 'react-native-web',
'react-native-linear-gradient': 'react-native-web-linear-gradient',
};
config.resolve.extensions = [
'.web.js',
'.web.ts',
'.web.tsx',
...config.resolve.extensions,
];
config.externals = config.externals.map(external => {
if (typeof external !== 'function') {
return external;
}
return async ({ context, request, getResolve }) => {
if (hasInclude(context, request)) {
return;
}
return external({ context, request, getResolve });
};
});
const babelLoaderConfiguration = {
test: /\.jsx?$/,
use: options.defaultLoaders.babel,
include: modulesPaths,
// exclude: /node_modules[/\\](?!react-native-vector-icons)/,
};
babelLoaderConfiguration.use.options = {
...babelLoaderConfiguration.use.options,
cacheDirectory: false,
// For Next JS transpile
presets: ['next/babel'],
plugins: [
['react-native-web', { commonjs: true }],
['@babel/plugin-proposal-class-properties'],
// ['@babel/plugin-proposal-object-rest-spread'],
],
};
config.module.rules.push(babelLoaderConfiguration);
return config;
},
};
// module.exports = withTM(config);
module.exports = configuration;当服务器端缺少一些功能时,SSR将无法构建。React最受欢迎的是requestAnimationFrame。我添加了一个Webpack插件来模仿它。它可以是一个空函数或多填充:
文件'polyfills/raf.js(I just put it assetImmediate`):
const polys = { requestAnimationFrame: setImmediate };
module.exports = polys.requestAnimationFrame;Babel配置对于它的最后一部分是必要的,无法在下一个配置中直接工作。babel.config.js:
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
plugins: [['module:react-native-dotenv'], 'react-native-reanimated/plugin'],
};最后,我在package.json中列出的包列表
{
"name": "my-app",
"version": "0.1.0",
"private": true,
"scripts": {
"android": "react-native run-android",
"android:dev": "adb reverse tcp:8081 tcp:8081 && react-native run-android",
"ios": "react-native run-ios",
"start": "react-native start",
"test": "jest",
"lint": "eslint .",
"web": "webpack serve -d source-map --mode development --config \"./web/webpack.config.js\" --inline --color --hot",
"build:web": "webpack --mode production --config \"./web/webpack.config.js\" --hot",
"next:dev": "next",
"next:build": "next build",
"next:start": "next start",
"next:analyze": "ANALYZE=true next build"
},
"dependencies": {
"@material-ui/core": "^4.12.4",
"@react-native-async-storage/async-storage": "^1.17.3",
"@react-navigation/drawer": "^6.4.1",
"@react-navigation/native": "^6.0.10",
"@react-navigation/stack": "^6.2.1",
"@reduxjs/toolkit": "^1.8.1",
"axios": "^0.21.1",
"local-storage": "^2.0.0",
"lottie-ios": "^3.2.3",
"lottie-react-native": "^5.1.3",
"lottie-web": "^5.9.4",
"moment": "^2.29.1",
"next": "^12.1.6",
"nookies": "^2.5.2",
"numeral": "^2.0.6",
"raf": "^3.4.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-native": "0.68.1",
"react-native-dotenv": "^2.5.5",
"react-native-gesture-handler": "^2.4.2",
"react-native-keyboard-aware-scroll-view": "^0.9.5",
"react-native-linear-gradient": "^2.5.6",
"react-native-media-query": "^1.0.9",
"react-native-paper": "^4.12.1",
"react-native-progress": "^5.0.0",
"react-native-read-more-text": "^1.1.2",
"react-native-reanimated": "^2.8.0",
"react-native-safe-area-context": "^4.2.5",
"react-native-screens": "^3.13.1",
"react-native-share-menu": "^6.0.0",
"react-native-svg": "^12.3.0",
"react-native-svg-transformer": "^1.0.0",
"react-native-vector-icons": "^9.1.0",
"react-native-view-more-text": "^2.1.0",
"react-native-web": "^0.17.7",
"react-native-web-linear-gradient": "^1.1.2",
"react-redux": "^8.0.1"
},
"devDependencies": {
"@babel/plugin-proposal-class-properties": "^7.14.5",
"@next/bundle-analyzer": "^12.2.2",
"@react-native-community/eslint-config": "^2.0.0",
"@swc/cli": "^0.1.57",
"@swc/core": "^1.2.179",
"eslint": "^7.28.0",
"metro-react-native-babel-preset": "^0.66.0",
"url-loader": "^4.1.1",
"webpack": "^5.39.1",
"webpack-cli": "^4.7.2"
},
"jest": {
"preset": "react-native-web"
},
"sideEffects": false
}注意:只有反应-本地包也使用在网络上必须转移。有些反应-本机包只能在本机中使用,因此将它们转到Web上会增加不必要的大量代码块,这是不好的。本机-本机- Web / Web本机已经比直接为Web制作的普通包更重。
与NextJS保持凉爽的技巧
Platform.OS === 'web',在这些组件中,您计划使用React本机模块或Web模块,这会导致所有组件在web代码上加载不必要的本机包。如果大小不重要,那么您可以忽略它。在末尾添加扩展.web.js和.native.js,并分离小代码。例如,我为以下内容编写了单独的函数和组件:Storage.web.js、Storage.native.js、CustomLink.web.js、CustomLink.native.js和hooks useCustomNavigation.web.js、useCustomNavigation.native.js,因此我调用CustomLink代替NextJS链接/路由器并作出反应--导航CustomLink.web.js使用react-native-media-query包作为高级媒体查询的生命保护程序,用于所有SSR/CSR和本机响应显示。该应用程序可以像普通桌面网站一样在大屏幕上进行重组,然后缩成移动视图,就像NextJS.上的Material
https://stackoverflow.com/questions/72040787
复制相似问题