关于React版本升级可行性的考察
项目背景:项目初期为了兼容IE8,采用react版本较低(0.14),本文针对兼容性要求调整到IE9及以上,后对React升级可行性的考察
一、React浏览器支持
官方说明
https://react-1251415695.cos-website.ap-chengdu.myqcloud.com/docs/react-dom.html#browser-support
1.浏览器支持
React 支持所有的现代浏览器,包括 IE9 及以上版本,但是需要为旧版浏览器比如 IE9 和 IE10 引入相关的 polyfills 依赖。
注意:
我们不支持那些不兼容 ES5 方法的旧版浏览器(IE8基本不支持ES5),但如果你的应用包含了 polyfill,例如 es5-shim 和 es5-sham 你可能会发现你的应用仍然可以在这些浏览器中正常运行。但是如果你选择这种方法,你便需要孤军奋战了。
JavaScript 环境要求
React 16 依赖集合类型 Map 和 Set 。如果你要支持无法原生提供这些能力(例如 IE < 11)或实现不规范(例如 IE 11)的旧浏览器与设备,考虑在你的应用库中包含一个全局的 polyfill ,例如 core-js 或 babel-polyfill 。
React 同时还依赖于 requestAnimationFrame
(甚至包括测试环境)。 你可以使用 raf 的 package 增添 requestAnimationFrame
的 shim
参考:
ES6 + Webpack + React + Babel 如何在低版本浏览器上愉快的玩耍(下) https://segmentfault.com/a/1190000006930013
ES6 + Webpack + React + Babel 如何在低版本浏览器上愉快的玩耍(上) https://segmentfault.com/a/1190000006929961
二、升级环境准备
1.babel-loader使用7.0版本
2.安装npm-check-updates 模块升级插件
npm install -g npm-check-updates // 或者 cnpm install -g npm-check-updates // 查看所有更新 ncu // 更新package.json文件 ncu –u // 安装更新 npm install
npm update,只能按照package.json中标注的版本号进行更新,升级后不会修改package.json中的版本号,需要自己手动修改,比较麻烦。
npm-check-updates 升级插件升级后会自动修改package.json里的版本号,简单方便。
官网:https://www.npmjs.com/package/npm-check-updates
3.IE低版本支持相关包去掉
export-from-ie8
es3ify-loader
三、升级修改
1.webpack升级修改
官方文档:
https://www.webpackjs.com/guides/migrating/#resolve-root-resolve-fallback-resolve-modulesdirectories
主要内容:
(1) 安装webpack-cli
(2) module.loaders 改为 module.rules
(3) webpack v4 中extract-text-webpack-plugin 改为mini-css-extract-plugin instead
2.react升级变更
(1) React. PropTypes 改为 import PropTypes from 'prop-types';
量很大
(2) componentWillReceiveProps 更名
报错内容:
Warning: componentWillReceiveProps has been renamed, and is not recommended for use. See https://fb.me/react-async-component-lifecycle-hooks for details.
修改原则:
- 如果你需要执行副作用(例如,数据提取或动画)以响应 props 中的更改,请改用 componentDidUpdate 生命周期。
- 如果你使用 componentWillReceiveProps 仅在 prop 更改时重新计算某些数据,请使用 memoization helper 代替。
- 如果你使用 componentWillReceiveProps 是为了在 prop 更改时“重置”某些 state,请考虑使组件完全受控或使用 key 使组件完全不受控 代替。
说明:
有使用该方法处理逻辑的地方,需要详细处理方案
可以使用 rename-unsafe-lifecycles自动更新组件。具体修改方式见官网说明:
(3) componentWillMount更名
报错内容:
react-dom.development.js:11494 Warning: componentWillMount has been renamed, and is not recommended for use. See https://fb.me/react-async-component-lifecycle-hooks for details.
修改原则:
- 使用 constructor() 来初始化 state。
- 避免在此方法中引入任何副作用或订阅。如遇此种情况,请改用 componentDidMount()。
现状:
有接口调用获取数据等处理
具体修改方式见官网说明:
(4) 自定义的事件被忽略
Warning: Unknown event handler property `onInputChange`. It will be ignored.
(5) 不要将组件的属性, 传给DOM节点
Warning: React does not recognize the `errorInfos` prop on a DOM element. If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercase `errorinfos` instead. If you accidentally passed it from a parent component, remove it from the DOM element.
本条改完,(4)中大部分可同时消除
3.antd升级
(1) 全局公共样式
比如,下列样式去掉
article, aside, blockquote, body, button, code, dd, details, div, dl, dt, fieldset, figcaption, figure, footer, form, h1, h2, h3, h4, h5, h6, header, hgroup, hr, input, legend, li, menu, nav, ol, p, pre, section, td, textarea, th, ul{ margin: 0; padding: 0; }
4.参考文章
react技术栈升级的过程https://segmentfault.com/a/1190000016357380
第一阶段:
react15+react-router2+redux3+webpack1
升级到
react16+react-router3+redux4+webpack4
1.react15升级到16 遇到的坑:
在react16中去除contextTypes ,导致this.context.router.push('/*') 需要替换成this.props.router.push('/*') 。
2.webpack1升级到4遇到的坑:
(1)webpack4 中建议使用min-css-extract-plugin 分离css,sass等文件,取代插件extract-text-webpack-plugin 效率更高
(2)html-webpack-plugin 要升级到2.22.0及以上
(3)webpack4将webpack.optimize.CommonsChunkPlugin移除,使用和entry平级的optimization里的属性splitChunks来把提取出来的样式和common.js会自动添加进发布模式的html文件中,原来的html文件中没有,前提必须是mode=prodution 才生效。
(4)webpack4中把内置的webpack.DefinePlugin({'process.env':{NODE_ENV:JSON.stringify("development")}})去掉,添加了和entry平级的mode属性,来区分环境。mode的value有none/development/production 这3中属性,若要在系统中使用,则用"process.env.NODE_ENV"变量来获取,比较奇葩。
(5)entry的路径原来path.resolve(path.resolve(path.resolve(path.resolve(__dirname)),'src'),'app')应替换为相对路径的'./src/App.jsx'。
(6)output的路径原来的path.resolve(path.resolve(__dirname),"dist")应替换为path.join(path.join(__dirname),"dist")。
四、 兼容
1.IE11
a) babel-polyfill
安装
npm i babel-polyfill --save-dev
引用
在./src/index.js中加入
import 'babel-polyfill';
webpack配置
entry: {
app: [
'babel-polyfill',
path.join(__dirname, '../src/index.js')
]
},
2.IE9
特别注意文件上传等功能
React16和Antd如何在IE9环境下忍辱偷生 https://blog.csdn.net/Napoleonxxx/article/details/80426678
IE9引发的血案-如何处理webpack打包后体积依然过大的css文件 https://blog.csdn.net/napoleonxxx/article/details/80292006
IE10、IE9在线兼容工具 https://www.browserling.com/
五、项目关联
公共组件需要升级版本
1.webpack4中的mode
公共组件始终使用production
六、组件文档
1.react-docgen
基础库,将组件注释转化为json数据 https://github.com/reactjs/react-docgen
2.react-styleguidist
带样式和界面的文档工具 https://github.com/styleguidist/react-styleguidist
七、组件开发测试工具
1.Carte Blanche
只有beta版本,2016年后停止维护
官网:https://github.com/carteb/carte-blanche
2.react-storybook
官网:https://storybook.js.org/docs/guides/guide-react/
https://storybook.js.org/docs/basics/introduction/
b) 添加依赖项
npm install @storybook/react --save-dev
注意:安装前需要确保项目包含react
,react-dom
,@babel/core
,和babel-loader
依赖,如果项目中没有,需要额外安装
c) 添加npm脚本
将以下NPM脚本添加到package.json,以便启动storybook:
{ "scripts": { "storybook": "start-storybook" } }
d) 配置文件
创建一个.storybook/config.js(windows下输入”.storybook.”创建名为.storybook的目录)包含以下内容的文件:
import { configure } from '@storybook/react';
configure(require.context('../src', true, /\.stories\.js$/), module);
这将加载../src目录下与模式匹配的所有故事*.stories.js。我们建议您将故事与源文件放在同一位置,但是您可以将它们放置在任意位置。
e) 写用户故事
创建./src/index.stories.js文件,并写下您的第一个故事:
import React from 'react'; import { Button } from '@storybook/react/demo'; export default { title: 'Button' }; export const withText = () => <Button>Hello Button</Button>; export const withEmoji = () => ( <Button><span role="img" aria-label="so cool">aaaaa</span></Button> );
f) 运行
执行以下命令启动storybook
npm run storybook
g) 问题
组件中文件引入路径使用webpack中alias的无法识别
八、附录
1. 组件化思考
前端 UI组件化的一些思考https://zhuanlan.zhihu.com/p/25820838
2. ucWeb中package.json
{ "name": "glodon-jf", "version": "1.0.0", "description": "glodon-jf", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "dev-build": "webpack --config build/webpack.dev.config.js", "start": "webpack-dev-server --config build/webpack.dev.config.js --color --progress --hot", "test-build": "webpack --config build/webpack.test.config.js --progress", "release-build": "webpack --config build/webpack.release.config.js --progress", "product-build": "webpack --config build/webpack.product.config.js --progress" }, "keywords": [ "react", "react-router", "webpack", "es6", "react-family", "ie8" ], "browserslist": [ "last 2 versions", "> 0.01%", "ie > 7" ], "author": "brickspert", "license": "ISC", "devDependencies": { "autoprefixer": "^9.6.1", "babel-core": "^6.26.3", "babel-eslint": "^10.0.3", "babel-loader": "^7.0.0", "babel-plugin-transform-runtime": "^6.23.0", "babel-polyfill": "^6.26.0", "babel-preset-es2015": "^6.24.1", "babel-preset-react": "^6.24.1", "babel-preset-stage-0": "^6.24.1", "bower-webpack-plugin": "^0.1.9", "bundle-loader": "^0.5.6", "clean-webpack-plugin": "^3.0.0", "console-polyfill": "^0.3.0", "css-loader": "^3.2.0", "eslint": "^6.4.0", "eslint-loader": "^3.0.0", "eslint-plugin-react": "^7.14.3", "eventsource-polyfill": "^0.9.6", "extract-text-webpack-plugin": "3.0.2", "fetch-ie8": "^1.5.0", "file-loader": "^4.2.0", "html-webpack-plugin": "^3.2.0", "html-withimg-loader": "^0.1.16", "less": "^3.10.3", "less-loader": "^5.0.0", "node-sass": "^4.12.0", "open-browser-webpack-plugin": "^0.0.5", "postcss-cssnext": "^3.1.0", "postcss-loader": "^3.0.0", "react-hot-loader": "^4.12.13", "redux-devtools": "^3.5.0", "redux-logger": "^3.0.6", "redux-thunk": "^2.3.0", "sass-loader": "^8.0.0", "style-loader": "^1.0.0", "styled-jsx": "^3.2.2", "uglifyjs-webpack-plugin": "^2.2.0", "url-loader": "^2.1.0", "webpack": "^4.40.2", "webpack-cli": "^3.3.8", "webpack-dev-server": "^3.8.0", "webpack-merge": "^4.2.2" }, "dependencies": { "antd": "3.23.2", "axios": "^0.19.0", "babel-plugin-import": "^1.12.1", "es3ify-webpack-plugin": "^0.1.0", "http-proxy-middleware": "^0.20.0", "js-cookie": "^2.2.1", "moment": "*", "prop-types": "^15.7.2", "react": "16.9.0", "react-dom": "16.x.x", "react-redux": "^7.1.1", "react-router-dom": "^5.0.1", "redux": "*" } }
3. commonUI中package.json
{ "name": "gcf-common-component", "version": "1.1.0", "description": "A common component library of gcf", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build-func": "webpack --config build/webpack.func.config.js", "build-comp": "webpack --config build/webpack.comp.config.js" }, "author": "zhangs", "license": "ISC", "dependencies": {}, "peerDependencies": { "react": "16.9.0", "react-dom": "16.x.x" }, "devDependencies": { "antd": "3.23.2", "assets-webpack-plugin": "^3.9.10", "babel-cli": "^6.26.0", "babel-core": "^6.26.3", "babel-eslint": "^10.0.3", "babel-loader": "7.0.0", "babel-plugin-import": "^1.12.1", "babel-plugin-transform-runtime": "^6.23.0", "babel-preset-es2015": "^6.24.1", "babel-preset-react": "^6.24.1", "babel-preset-stage-0": "^6.24.1", "css-loader": "^3.2.0", "eslint": "^6.4.0", "glob": "^7.1.4", "mini-css-extract-plugin": "^0.8.0", "style-loader": "^1.0.0", "styled-jsx": "^3.2.2", "uglifyjs-webpack-plugin": "^2.2.0", "url-loader": "^2.1.0", "webpack": "^4.40.2", "webpack-cli": "^3.3.8" } }