Webpack 编译在 {Node} 上的 react-native-web 中失败,即流语法说 "You may need an appropriate loader to handle this file type"
Webpack compilation failed in react-native-web on {Node} i.e. Flow Syntax saying "You may need an appropriate loader to handle this file type"
我是 node/npm
、react
和 react-native
的新手,所以对 react-native-web
也是新手。我用了 3 天时间将 react-native-web
集成到使用 npx react-native init
as per the doc 生成的 Hello World App 中。我尝试使用两种模板:有和没有打字稿,但到目前为止没有成功。我得到的最远的是 运行 在 index.web.js
中编写的应用程序代码但是如果我从 ./src/components/
添加任何组件然后我得到错误,主要是 webpack.
我创建了一个测试仓库以便于重新生成错误,所以重现的步骤如下:
- 在您的系统中下载 this repo。
npm install
npm run web
现在您将在终端中看到错误。
版本:
- metro-react-native-babel-预设:0.66.0
- 节点:16.3.0
- npm: 7.8.0
- OS: Windows 10 - 64 位
我遵循了官方文档,但使用了 webpack@^4
并参考了 this article 并以某种方式设法达到了以下情况:
- 如果我的整个代码都在
index.web.js
. 中,则网页正在呈现
- 但是当我在其中导入
App
时,由于加载程序错误导致编译失败。
工作index.web.js
:
import React, {Component} from 'react';
import {AppRegistry, StyleSheet, Text, View} from 'react-native';
class ReactNativeWeb extends Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>Welcome to React Native!</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#142a3d',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
color: '#ffffff',
},
});
AppRegistry.registerComponent('ReactNativeWeb', () => ReactNativeWeb);
AppRegistry.runApplication('ReactNativeWeb', {
rootTag: document.getElementById('react-native-web-app'),
});
新错误index.web.js
:
import React from 'react';
import {AppRegistry} from 'react-native';
import App from './src/components/App';
import {name as appName} from './app.json';
AppRegistry.registerComponent(appName, () => App);
AppRegistry.runApplication(appName, {
rootTag: document.getElementById('react-native-web-app'),
});
src/components/App.jsx
/**
* Sample React Native App
* https://github.com/facebook/react-native
*
* @format
*/
import React from 'react';
// import {Node} from 'react';
import {
SafeAreaView,
ScrollView,
StatusBar,
StyleSheet,
Text,
useColorScheme,
View,
} from 'react-native';
import {
Colors,
DebugInstructions,
Header,
LearnMoreLinks,
ReloadInstructions,
} from 'react-native/Libraries/NewAppScreen';
const Section = ({children, title}) => {
const isDarkMode = useColorScheme() === 'dark';
return (
<View style={styles.sectionContainer}>
<Text
style={[
styles.sectionTitle,
{
color: isDarkMode ? Colors.white : Colors.black,
},
]}>
{title}
</Text>
<Text
style={[
styles.sectionDescription,
{
color: isDarkMode ? Colors.light : Colors.dark,
},
]}>
{children}
</Text>
</View>
);
};
const App = () => {
const isDarkMode = useColorScheme() === 'dark';
const backgroundStyle = {
backgroundColor: isDarkMode ? Colors.darker : Colors.lighter,
};
return (
<SafeAreaView style={backgroundStyle}>
<StatusBar barStyle={isDarkMode ? 'light-content' : 'dark-content'} />
<ScrollView
contentInsetAdjustmentBehavior="automatic"
style={backgroundStyle}>
<Header />
<View
style={{
backgroundColor: isDarkMode ? Colors.black : Colors.white,
}}>
<Section title="Step One">
Edit <Text style={styles.highlight}>App.js</Text> to change this
screen and then come back to see your edits.
</Section>
<Section title="See Your Changes">
<ReloadInstructions />
</Section>
<Section title="Debug">
<DebugInstructions />
</Section>
<Section title="Learn More">
Read the docs to discover what to do next:
</Section>
<LearnMoreLinks />
</View>
</ScrollView>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
sectionContainer: {
marginTop: 32,
paddingHorizontal: 24,
},
sectionTitle: {
fontSize: 24,
fontWeight: '600',
},
sectionDescription: {
marginTop: 8,
fontSize: 18,
fontWeight: '400',
},
highlight: {
fontWeight: '700',
},
});
export default App;
现在我得到以下错误,似乎与 babel 或 flow 相关的 webpack 加载器有关:
ERROR in ./node_modules/react-native/Libraries/NewAppScreen/components/DebugInstructions.js 11:12
Module parse failed: Unexpected token (11:12)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
| */
|
> import type {Node} from 'react';
| import {Platform, StyleSheet, Text} from 'react-native';
| import React from 'react';
@ ./node_modules/react-native/Libraries/NewAppScreen/index.js 17:0-63 20:0-27:2
@ ./src/components/App.jsx
@ ./index.web.js
@ multi ./index.web.js
我所有的设置文件如下:
package.json
{
"name": "awesomeproject2",
"version": "0.0.1",
"private": true,
"scripts": {
"android": "react-native run-android",
"ios": "react-native run-ios",
"start": "react-native start",
"test": "jest",
"lint": "eslint ."
},
"dependencies": {
"react": "17.0.1",
"react-dom": "^17.0.2",
"react-native": "0.64.2",
"react-native-web": "^0.16.5"
},
"devDependencies": {
"@babel/core": "^7.12.9",
"@babel/preset-env": "^7.14.4",
"@babel/preset-react": "^7.13.13",
"@babel/runtime": "^7.12.5",
"@react-native-community/eslint-config": "^2.0.0",
"babel-jest": "^26.6.3",
"babel-loader": "^8.2.2",
"babel-plugin-module-resolver": "^4.1.0",
"babel-plugin-react-native-web": "^0.16.5",
"eslint": "7.14.0",
"jest": "^26.6.3",
"metro-react-native-babel-preset": "^0.64.0",
"react-test-renderer": "17.0.1",
"url-loader": "^4.1.1",
"webpack": "^4.46.0",
"webpack-cli": "^4.7.2",
"webpack-dev-server": "^3.11.2"
},
"jest": {
"preset": "react-native-web"
}
}
web/webpack.config.js
const path = require('path');
const webpack = require('webpack');
const appDirectory = path.resolve(__dirname, '../');
// This is needed for webpack to compile JavaScript.
// Many OSS React Native packages are not compiled to ES5 before being
// published. If you depend on uncompiled packages they may cause webpack build
// errors. To fix this webpack can be configured to compile to the necessary
// `node_module`.
const babelLoaderConfiguration = {
test: /(\.js)|(\.jsx)$/,
// Add every directory that needs to be compiled by Babel during the build.
include: [
path.resolve(appDirectory, 'index.web.js'),
path.resolve(appDirectory, 'src'),
path.resolve(appDirectory, 'node_modules/react-native-uncompiled'),
],
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
// cacheDirectory: true,
// The 'metro-react-native-babel-preset' preset is recommended to match React Native's packager
presets: [
'module:metro-react-native-babel-preset',
'@babel/preset-env',
'@babel/preset-react',
],
// Re-write paths to import only the modules needed by the app
plugins: ['react-native-web'],
},
},
};
// This is needed for webpack to import static images in JavaScript files.
const imageLoaderConfiguration = {
test: /\.(gif|jpe?g|png|svg)$/,
use: {
loader: 'url-loader',
options: {
name: '[name].[ext]',
esModule: false,
},
},
};
module.exports = {
entry: [
// load any web API polyfills
// path.resolve(appDirectory, 'polyfills-web.js'),
// your web-specific entry file
path.resolve(appDirectory, 'index.web.js'),
],
// configures where the build ends up
output: {
filename: 'bundle.web.js',
path: path.resolve(appDirectory, 'dist'),
},
// ...the rest of your config
module: {
rules: [babelLoaderConfiguration, imageLoaderConfiguration],
},
resolve: {
// This will only alias the exact import "react-native"
alias: {
'react-native$': 'react-native-web',
},
// If you're working on a multi-platform React Native app, web-specific
// module implementations should be written in files using the extension
// `.web.js`.
extensions: ['.web.js', '.js', '.jsx', '.web.jsx'],
},
};
.bashrc
{
"plugins": [
[
"module-resolver",
{
"alias": {
"^react-native$": "react-native-web"
}
}
]
]
}
.flowconfig(仅在 [options]
下面添加了部分)
# Alias the package name
module.name_mapper='^react-native$' -> 'react-native-web'
# Point flow to the 'module' field by default
module.system.node.main_field=module
module.system.node.main_field=main
babel.config.js
module.exports = {
presets: [
'module:metro-react-native-babel-preset',
'@babel/preset-env',
'@babel/preset-react',
],
};
终于解决了,我的Hello World搞定了
维护者本人 (@necolas) 在 this discussion 上的这 2 条回复帮助我解决了这个问题。
Reply 1 by @necolas
This line in the stack trace is telling you that you're trying to bundle RN internal code in your web bundle: node_modules/react-native/Libraries/NewAppScreen/index.js.
First, you should not be loading any of the RN package on web, especially not parts that aren't part of the public API . Second, as mentioned in the inline comments of the config you pasted above, you need to explicitly list everything in node_modules that needs compiling.
Reply 2 by @necolas
import {
Colors,
DebugInstructions,
Header,
LearnMoreLinks,
ReloadInstructions,
} from 'react-native/Libraries/NewAppScreen';
All this code of the problem. You should not import from Libraries. This is not part of the public API. And you need to actually read the webpack code comments and follow the instructions when importing code from node_modules.
所以我从 'react-native/Libraries/NewAppScreen'
中删除了导入组件,仅此而已。 this commit.
中有更多详细信息
我添加了an article for setting up React Native for Web from Scratch。
我是 node/npm
、react
和 react-native
的新手,所以对 react-native-web
也是新手。我用了 3 天时间将 react-native-web
集成到使用 npx react-native init
as per the doc 生成的 Hello World App 中。我尝试使用两种模板:有和没有打字稿,但到目前为止没有成功。我得到的最远的是 运行 在 index.web.js
中编写的应用程序代码但是如果我从 ./src/components/
添加任何组件然后我得到错误,主要是 webpack.
我创建了一个测试仓库以便于重新生成错误,所以重现的步骤如下:
- 在您的系统中下载 this repo。
npm install
npm run web
现在您将在终端中看到错误。
版本:
- metro-react-native-babel-预设:0.66.0
- 节点:16.3.0
- npm: 7.8.0
- OS: Windows 10 - 64 位
我遵循了官方文档,但使用了 webpack@^4
并参考了 this article 并以某种方式设法达到了以下情况:
- 如果我的整个代码都在
index.web.js
. 中,则网页正在呈现
- 但是当我在其中导入
App
时,由于加载程序错误导致编译失败。
工作index.web.js
:
import React, {Component} from 'react';
import {AppRegistry, StyleSheet, Text, View} from 'react-native';
class ReactNativeWeb extends Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>Welcome to React Native!</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#142a3d',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
color: '#ffffff',
},
});
AppRegistry.registerComponent('ReactNativeWeb', () => ReactNativeWeb);
AppRegistry.runApplication('ReactNativeWeb', {
rootTag: document.getElementById('react-native-web-app'),
});
新错误index.web.js
:
import React from 'react';
import {AppRegistry} from 'react-native';
import App from './src/components/App';
import {name as appName} from './app.json';
AppRegistry.registerComponent(appName, () => App);
AppRegistry.runApplication(appName, {
rootTag: document.getElementById('react-native-web-app'),
});
src/components/App.jsx
/**
* Sample React Native App
* https://github.com/facebook/react-native
*
* @format
*/
import React from 'react';
// import {Node} from 'react';
import {
SafeAreaView,
ScrollView,
StatusBar,
StyleSheet,
Text,
useColorScheme,
View,
} from 'react-native';
import {
Colors,
DebugInstructions,
Header,
LearnMoreLinks,
ReloadInstructions,
} from 'react-native/Libraries/NewAppScreen';
const Section = ({children, title}) => {
const isDarkMode = useColorScheme() === 'dark';
return (
<View style={styles.sectionContainer}>
<Text
style={[
styles.sectionTitle,
{
color: isDarkMode ? Colors.white : Colors.black,
},
]}>
{title}
</Text>
<Text
style={[
styles.sectionDescription,
{
color: isDarkMode ? Colors.light : Colors.dark,
},
]}>
{children}
</Text>
</View>
);
};
const App = () => {
const isDarkMode = useColorScheme() === 'dark';
const backgroundStyle = {
backgroundColor: isDarkMode ? Colors.darker : Colors.lighter,
};
return (
<SafeAreaView style={backgroundStyle}>
<StatusBar barStyle={isDarkMode ? 'light-content' : 'dark-content'} />
<ScrollView
contentInsetAdjustmentBehavior="automatic"
style={backgroundStyle}>
<Header />
<View
style={{
backgroundColor: isDarkMode ? Colors.black : Colors.white,
}}>
<Section title="Step One">
Edit <Text style={styles.highlight}>App.js</Text> to change this
screen and then come back to see your edits.
</Section>
<Section title="See Your Changes">
<ReloadInstructions />
</Section>
<Section title="Debug">
<DebugInstructions />
</Section>
<Section title="Learn More">
Read the docs to discover what to do next:
</Section>
<LearnMoreLinks />
</View>
</ScrollView>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
sectionContainer: {
marginTop: 32,
paddingHorizontal: 24,
},
sectionTitle: {
fontSize: 24,
fontWeight: '600',
},
sectionDescription: {
marginTop: 8,
fontSize: 18,
fontWeight: '400',
},
highlight: {
fontWeight: '700',
},
});
export default App;
现在我得到以下错误,似乎与 babel 或 flow 相关的 webpack 加载器有关:
ERROR in ./node_modules/react-native/Libraries/NewAppScreen/components/DebugInstructions.js 11:12
Module parse failed: Unexpected token (11:12)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
| */
|
> import type {Node} from 'react';
| import {Platform, StyleSheet, Text} from 'react-native';
| import React from 'react';
@ ./node_modules/react-native/Libraries/NewAppScreen/index.js 17:0-63 20:0-27:2
@ ./src/components/App.jsx
@ ./index.web.js
@ multi ./index.web.js
我所有的设置文件如下:
package.json
{
"name": "awesomeproject2",
"version": "0.0.1",
"private": true,
"scripts": {
"android": "react-native run-android",
"ios": "react-native run-ios",
"start": "react-native start",
"test": "jest",
"lint": "eslint ."
},
"dependencies": {
"react": "17.0.1",
"react-dom": "^17.0.2",
"react-native": "0.64.2",
"react-native-web": "^0.16.5"
},
"devDependencies": {
"@babel/core": "^7.12.9",
"@babel/preset-env": "^7.14.4",
"@babel/preset-react": "^7.13.13",
"@babel/runtime": "^7.12.5",
"@react-native-community/eslint-config": "^2.0.0",
"babel-jest": "^26.6.3",
"babel-loader": "^8.2.2",
"babel-plugin-module-resolver": "^4.1.0",
"babel-plugin-react-native-web": "^0.16.5",
"eslint": "7.14.0",
"jest": "^26.6.3",
"metro-react-native-babel-preset": "^0.64.0",
"react-test-renderer": "17.0.1",
"url-loader": "^4.1.1",
"webpack": "^4.46.0",
"webpack-cli": "^4.7.2",
"webpack-dev-server": "^3.11.2"
},
"jest": {
"preset": "react-native-web"
}
}
web/webpack.config.js
const path = require('path');
const webpack = require('webpack');
const appDirectory = path.resolve(__dirname, '../');
// This is needed for webpack to compile JavaScript.
// Many OSS React Native packages are not compiled to ES5 before being
// published. If you depend on uncompiled packages they may cause webpack build
// errors. To fix this webpack can be configured to compile to the necessary
// `node_module`.
const babelLoaderConfiguration = {
test: /(\.js)|(\.jsx)$/,
// Add every directory that needs to be compiled by Babel during the build.
include: [
path.resolve(appDirectory, 'index.web.js'),
path.resolve(appDirectory, 'src'),
path.resolve(appDirectory, 'node_modules/react-native-uncompiled'),
],
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
// cacheDirectory: true,
// The 'metro-react-native-babel-preset' preset is recommended to match React Native's packager
presets: [
'module:metro-react-native-babel-preset',
'@babel/preset-env',
'@babel/preset-react',
],
// Re-write paths to import only the modules needed by the app
plugins: ['react-native-web'],
},
},
};
// This is needed for webpack to import static images in JavaScript files.
const imageLoaderConfiguration = {
test: /\.(gif|jpe?g|png|svg)$/,
use: {
loader: 'url-loader',
options: {
name: '[name].[ext]',
esModule: false,
},
},
};
module.exports = {
entry: [
// load any web API polyfills
// path.resolve(appDirectory, 'polyfills-web.js'),
// your web-specific entry file
path.resolve(appDirectory, 'index.web.js'),
],
// configures where the build ends up
output: {
filename: 'bundle.web.js',
path: path.resolve(appDirectory, 'dist'),
},
// ...the rest of your config
module: {
rules: [babelLoaderConfiguration, imageLoaderConfiguration],
},
resolve: {
// This will only alias the exact import "react-native"
alias: {
'react-native$': 'react-native-web',
},
// If you're working on a multi-platform React Native app, web-specific
// module implementations should be written in files using the extension
// `.web.js`.
extensions: ['.web.js', '.js', '.jsx', '.web.jsx'],
},
};
.bashrc
{
"plugins": [
[
"module-resolver",
{
"alias": {
"^react-native$": "react-native-web"
}
}
]
]
}
.flowconfig(仅在 [options]
下面添加了部分)
# Alias the package name
module.name_mapper='^react-native$' -> 'react-native-web'
# Point flow to the 'module' field by default
module.system.node.main_field=module
module.system.node.main_field=main
babel.config.js
module.exports = {
presets: [
'module:metro-react-native-babel-preset',
'@babel/preset-env',
'@babel/preset-react',
],
};
终于解决了,我的Hello World搞定了
维护者本人 (@necolas) 在 this discussion 上的这 2 条回复帮助我解决了这个问题。
Reply 1 by @necolas This line in the stack trace is telling you that you're trying to bundle RN internal code in your web bundle: node_modules/react-native/Libraries/NewAppScreen/index.js.
First, you should not be loading any of the RN package on web, especially not parts that aren't part of the public API . Second, as mentioned in the inline comments of the config you pasted above, you need to explicitly list everything in node_modules that needs compiling.
Reply 2 by @necolas
import { Colors, DebugInstructions, Header, LearnMoreLinks, ReloadInstructions, } from 'react-native/Libraries/NewAppScreen';
All this code of the problem. You should not import from Libraries. This is not part of the public API. And you need to actually read the webpack code comments and follow the instructions when importing code from node_modules.
所以我从 'react-native/Libraries/NewAppScreen'
中删除了导入组件,仅此而已。 this commit.
我添加了an article for setting up React Native for Web from Scratch。