React 16组件无法渲染,仅兼容0.14.7版本,需如何适配?
哎,这个问题我之前碰过好多次,React从0.14.7跳到16版本跨度确实不小,不管是构建配置还是组件代码写法都有不少不兼容的地方,我给你拆成配置修改和代码调整两部分来梳理,你跟着一步步来改就行:
React 16需要配合较新的Babel版本(Babel 7+),旧的babel-loader和预设已经完全不兼容了,得先把这部分更新:
1. 升级Babel相关依赖
先卸载旧的Babel包,再装新的:
# 卸载旧的Babel工具链 npm uninstall babel-loader babel-preset-react babel-preset-es2015 # 安装Babel 7+的核心依赖和预设 npm install --save-dev @babel/core @babel/preset-env @babel/preset-react babel-loader@8.x
另外,如果你的Webpack版本低于2.x,也得升级到Webpack 4+或者5+(旧Webpack和新babel-loader不兼容),执行这个命令升级:
npm install --save-dev webpack webpack-cli
2. 更新.babelrc配置
你原来的.babelrc只写了"pre...",应该是用了旧的babel-preset-react这类预设,现在替换成适配React 16的新预设:
{ "presets": [ "@babel/preset-env", // 负责转译ES6+语法到目标环境兼容的代码 "@babel/preset-react" // 处理React JSX语法,包括React 16的新特性 ] }
3. 调整Webpack配置
Webpack 2+之后把loaders改成了rules,还要添加对JSX文件的支持,修改后的webpack.config.js应该是这样:
module.exports = { entry: "./app-client.js", output: { filename: "public/bundle.js" }, module: { rules: [ { test: /\.(js|jsx)$/, // 匹配所有.js和.jsx文件 exclude: /node_modules/, // 跳过node_modules目录 use: { loader: 'babel-loader' } } ] }, resolve: { extensions: ['.js', '.jsx'] // 让Webpack自动识别这些扩展名,导入组件时不用写后缀 } };
React 16废弃了不少旧API,也新增了一些规则,以下是最常见的需要修改的地方:
1. 渲染入口的变化
React 0.14用的是React.render(),但React 16开始渲染逻辑被抽到了react-dom包中,必须单独导入并使用ReactDOM.render():
// 旧写法(React 0.14) import React from 'react'; React.render(<App />, document.getElementById('root')); // 新写法(React 16) import React from 'react'; import ReactDOM from 'react-dom'; // 必须单独导入ReactDOM ReactDOM.render(<App />, document.getElementById('root'));
2. PropTypes的导入方式变了
React 0.14中PropTypes是React的内置属性,但React 16开始把它单独抽成了prop-types包,得先安装:
npm install --save prop-types
然后在组件中修改导入:
// 旧写法 import React from 'react'; MyComponent.propTypes = { name: React.PropTypes.string.isRequired }; // 新写法 import React from 'react'; import PropTypes from 'prop-types'; MyComponent.propTypes = { name: PropTypes.string.isRequired };
3. 废弃的生命周期钩子
React 16废弃了三个旧的生命周期钩子:componentWillMount、componentWillReceiveProps、componentWillUpdate,如果你的组件用了这些,得换成新的替代方案:
componentWillMount:直接把初始化逻辑放到constructor或者componentDidMount里就行componentWillReceiveProps:替换成static getDerivedStateFromProps+componentDidUpdatecomponentWillUpdate:替换成getSnapshotBeforeUpdate
举个componentWillReceiveProps的替换例子:
// 旧写法 componentWillReceiveProps(nextProps) { if (nextProps.id !== this.props.id) { this.setState({ data: null }); this.fetchData(nextProps.id); } } // React 16+的写法 static getDerivedStateFromProps(nextProps, prevState) { if (nextProps.id !== prevState.prevId) { return { data: null, prevId: nextProps.id }; } return null; // 不需要更新状态就返回null } componentDidUpdate(prevProps) { if (prevProps.id !== this.props.id) { this.fetchData(this.props.id); } }
4. 组件返回值的规则
React 16之前,组件的render方法必须返回单个元素(得用div之类的包裹),React 16支持返回数组或者Fragment,但如果你的旧组件返回了undefined或者多个未包裹的元素,会直接导致渲染失败,得确保render方法总是返回有效的React元素或者null。
比如原来的错误写法:
// 旧写法(React 0.14会报错,React 16也会报错) render() { return ( <h1>Hello</h1> <p>World</p> ); }
改成Fragment包裹:
// React 16+写法 render() { return ( <> <h1>Hello</h1> <p>World</p> </> ); }
- 先确保react和react-dom是16.x版本:
npm install --save react@16.x react-dom@16.x - 按上面的步骤更新配置和代码
- 运行Webpack打包,打开控制台看报错信息(比如某个生命周期未定义、PropTypes找不到),针对性修复就行
内容的提问来源于stack exchange,提问作者trogne

