聊聊React开发的一些坑(一)

开发 前端
本篇给大家介绍在React开发过程中的一些坑,希望对你有所帮助!

[[404350]]

一、项目生成

  • create-react-app saucxs-web 生成项目
  • cd saucxs-web 进入项目
  • yarn start 启动项目

二、项目部署

  • 本地开发 yarn start
  • 线上部署 yarn build

三、参考文档

  • react 英文文档[1]
  • create-react-app[2]
  • ant UI库的配置参考[3]
  • react-router 英文[4]
  • react-router 中文[5]

四、配置项

1、Ant Design UI库引入

  • yarn add antd 安装UI库
  • yarn add babel-plugin-import 实现按需引入

package.json/babel 中增加如下内容:

  1. "plugins": [ 
  2.     [ 
  3.         "import"
  4.         { 
  5.             "libraryName""antd"
  6.             "libraryDirectory""es"
  7.             "style""css" 
  8.         } 
  9.     ] 

组件使用 如下:

  1. import React, { Component } from 'react'
  2. import { Button } from 'antd' 
  3. import 'antd/dist/antd.css' 
  4. class App extends Component { 
  5.   render() { 
  6.     return ( 
  7.         <Button type="primary">松宝写代码</Button> 
  8.     ); 
  9.   } 
  10. export default App; 

此时可以使用UI库的默认样式

2、自定义Ant Design UI库样式

  • 安装 less 和 less-loader
  • 使用命令 yarn run eject 暴露配置文件
  • 在 webpack.config.dev.js 与 webpack.config.prod.js 中做如下修改:

创建 antModifyVars.js 文件

  1. 'use strict'
  2. const modifyVars = { 
  3.   'primary-color''#E26A6A'
  4. module.exports = modifyVars; 

创建 less 相关变量,参考默认sass的配置:

  1. // style files regexes 
  2. const cssRegex = /\.css$/; 
  3. const cssModuleRegex = /\.module\.css$/; 
  4. const sassRegex = /\.(scss|sass)$/; 
  5. const sassModuleRegex = /\.module\.(scss|sass)$/; 
  6. // 增加less部分 
  7. const lessRegex = /\.less/; 
  8. const lessModuleRegex = /\.module\.less$/; 

在 module.rules 的 oneOf 下, 仿照sass追加一下代码:

  1. // Opt-in support for LESS (using .less extensions). 
  2. // Chains the less-loader with the css-loader and the style-loader 
  3. // to immediately apply all styles to the DOM. 
  4. // By default we support LESS Modules with the 
  5. // extensions .module.scss or .module.sass 
  6.     test: lessRegex, 
  7.     exclude: lessModuleRegex, 
  8.     use: getStyleLoaders({ importLoaders: 2 }, 'less-loader'), 
  9. }, 
  10. // Adds support for CSS Modules, but using SASS 
  11. // using the extension .module.scss or .module.sass 
  12.     test: lessModuleRegex, 
  13.     use: getStyleLoaders( 
  14.     { 
  15.         importLoaders: 2, 
  16.         modules: true
  17.         getLocalIdent: getCSSModuleLocalIdent, 
  18.     }, 
  19.     'less-loader'), 
  20. }, 

在 getStyleLoaders 中,处理 less-loader

  1. // dev下的配置 
  2. if (preProcessor) { 
  3.     let loader = {loader: require.resolve(preProcessor)} 
  4.     if (preProcessor === "less-loader") { 
  5.         loader.options.modifyVars = modifyVars 
  6.         loader.options.javascriptEnabled = true 
  7.     } 
  8.     loaders.push(loader); 
  9. // prod下的配置 
  10. if (preProcessor) { 
  11.     let loader = { 
  12.          loader: require.resolve(preProcessor), 
  13.          options: { 
  14.             sourceMap: shouldUseSourceMap, 
  15.         }, 
  16.     } 
  17.     if (preProcessor === "less-loader") { 
  18.         loader.options.modifyVars = modifyVars 
  19.         loader.options.javascriptEnabled = true 
  20.     } 
  21.     loaders.push(loader); 
  22.   } 

3、ES6 API支持,引入polyfills

增加低版本浏览器、IE浏览器对ES6API的支持,IE9,IE9+

方法一,安装 yarn add react-app-polyfill

  1. // src/index.js中的【第一行】引入对IE9及更高版本的支持 
  2. import 'react-app-polyfill/ie9'
  3. // 其他支持,如:对IE11及更高版本的支持 
  4. import 'react-app-polyfill/ie11'

方法二,安装 yarn add babel-polyfill

  1. // webpack.base.conf.js中引入: 
  2. require("babel-polyfill"
  3. // webpack.base.conf.js中配置: 
  4. entry: { app: ['babel-polyfill''./src/main.js'] } 

4、引入 react-router-dom 路由

  • react-router-dom 依赖 react-router,所以使用npm安装依赖的时候,只需要安装相应环境下的库即可,不用再显式安装react-router。
  • 由于需要权限配置,所以增加AuthorizedRoute.js控制权限

5、配置别名,@ 指向 src 目录

webpack.base.conf.js 与 webpack.base.conf.js的配置一致,如下:

  1. 定义resolvePath方法,新版中resolve名称被占用 
  2. function resolvePath (dir) { 
  3.   return path.join(__dirname, '..', dir) 
  4. // resolve.alias中增加别名配置: 
  5. '@': resolvePath('src'

6、引入 axios

在package.json底部增加以下代码解决跨域问题

  1. // 新版本需要借助http-proxy-middleware,在src下创建setupProxy.js,内容: 
  2. // 会自动引用,不需要额外的配置 
  3. const proxy = require('http-proxy-middleware'
  4. module.exports = function (app) { 
  5.   app.use( 
  6.     proxy( 
  7.       '/api', { 
  8.         target: 'http://**********'
  9.         changeOrigin: true 
  10.       } 
  11.     ) 
  12.   ) 
  13. // 定义多个入口: 
  14. module.exports = function (app) { 
  15.   app.use(proxy('/api', { target: 'http://localhost:7001' })); 
  16.   app.use(proxy('/api2', { target: 'http://localhost:7001' })); 

7、样式处理

使用react-css-modules实现组件内部样式与外部分离,使用方式:

  1. import React from 'react' 
  2. import CSSModules from 'react-css-modules'
  3. import { Button } from 'antd' 
  4. import styles from './Header.module.scss' 
  5. class Header extends React.Component { 
  6.   render () { 
  7.     return ( 
  8.       <div> 
  9.         <Button type="primary" className="nowrap" styleName="test">松宝写代码</Button> 
  10.       </div> 
  11.     ) 
  12.   } 
  13. export default CSSModules(Header, styles) 

 注意:

  • 由于最新版create-react-app已经实现配置,无需再修改webpack配置文件
  • 上述方法可实现,同时使用antd样式、全局样式、组件私有样式
  • 特别注意组件私有样式文件的命名[name].module.scss

8、针对create-react-app 2.1.1之前的版本,引入 stylus

  • 安装 stylus 和 stylus-loader
  • 使用命令 yarn run eject 暴露配置文件
  • 在 webpack.config.dev.js 与webpack.config.prod.js 的 module/rules/oneOf 中修改一下代码:
  1.     test: /\.(css|styl)$/, 
  2.         use: [ 
  3.             require.resolve('style-loader'), 
  4.             { 
  5.                 loader: require.resolve('css-loader'), 
  6.                 options: { 
  7.                   importLoaders: 1, 
  8.                 }, 
  9.             }, 
  10.             { 
  11.                 loader: require.resolve('postcss-loader'), 
  12.                 options: { 
  13.                     // Necessary for external CSS imports to work 
  14.                     // https://github.com/facebookincubator/create-react-app/issues/2677 
  15.                     ident: 'postcss'
  16.                     sourceMap: true
  17.                     plugins: () => [ 
  18.                         require('postcss-flexbugs-fixes'), 
  19.                         autoprefixer({ 
  20.                             browsers: [ 
  21.                             '>1%'
  22.                             'last 4 versions'
  23.                             'Firefox ESR'
  24.                             'not ie < 9', // React doesn't support IE8 anyway 
  25.                         ], 
  26.                         flexbox: 'no-2009'
  27.                     }), 
  28.                 ], 
  29.             }, 
  30.         }, 
  31.         { 
  32.             loader: require.resolve('stylus-loader'), 
  33.             options: { 
  34.                 sourceMap: true
  35.             } 
  36.         }, 
  37.     ], 
  38. }, 

五、开发过程中遇到的问题

1、warning提示:no-op

参考内容[6]

问题描述

warning:Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method

名次解释 no-op

Can only update a mounted or mounting component. This usually means you called setState, replaceState, or forceUpdate on an unmounted component.

也就是不能在卸载的组件中进行状态更新操作,如异步请求、事件、定时器等,在componentWillUnmount生命周期中都应该进行相应的取消处理

对于事件、定时器,只需要在componentWillUnmount方法中,进行取消事件监听或者清除定时器的操作即可

以下方案均针对异步操作带来的问题进行处理。

方案一:使用一个变量[_isMounted]来控制是否更新状态

  1. import React from 'react' 
  2. class newComponent extends React.Component { 
  3.   _isMounted = false 
  4.   componentDidMount() { 
  5.     this._isMounted = true 
  6.     axios 
  7.       .get('https://hn.algolia.com/api/v1/search?query=react'
  8.       .then(result => 
  9.       if (!this._isMounted) return 
  10.         this.setState({ 
  11.           news: result.data.hits, 
  12.         }), 
  13.       ) 
  14.   } 
  15.   componentWillUnmount() { 
  16.     // 取消事件监听、清除定时器、控制异步请求 
  17.     this._isMounted = false 
  18.   } 
  19.   render() { 
  20.     return ( 
  21.       ... 
  22.     ) 
  23.   } 
  24. export default newComponent 

方案二:比较粗暴的方式,直接更改setState函数

  1. import React from 'react' 
  2. class newComponent extends React.Component { 
  3.   componentWillUnmount() { 
  4.     // 取消事件监听、清除定时器、控制异步请求 
  5.     this.setState = () => { 
  6.       return 
  7.     } 
  8.   } 
  9.   render() { 
  10.     return ( 
  11.       ... 
  12.     ) 
  13.   } 
  14. export default newComponent 

方案三,可直接取消接口请求,如axios的CancelToken

2、react-router4.0版本中,不同场景下的路由跳转

参考[7]

方案一,官方推荐,使用withRouter

  1. import React from 'react' 
  2. import { withRouter } from 'react-router-dom' 
  3. class NewComponent extends React.Component { 
  4.   AFunction() { 
  5.     this.props.history.push('/path'
  6.   } 
  7.   render() { 
  8.     return ( 
  9.       ... 
  10.     ) 
  11.   } 
  12. export default withRouter(NewComponent) 

方案二,不推荐,使用context

  1. import React from 'react' 
  2. import PropTypes from 'prop-types' 
  3. import { withRouter } from 'react-router-dom' 
  4. class NewComponent extends React.Component { 
  5.   static contextTypes = { 
  6.     router: PropTypes.object 
  7.   } 
  8.   constructor(props, context) { 
  9.      super(props, context); 
  10.   } 
  11.   AFunction() { 
  12.     this.context.router.history.push('/path'
  13.   } 
  14.   render() { 
  15.     return ( 
  16.       ... 
  17.     ) 
  18.   } 
  19. export default NewComponent 

方案三,使用history

在真实的业务场景中,经常需要在非react组件中使用路由跳转,如:接口统一处理场景下,用户登录失效时,跳转至登录页面。

由于react-router4.0中提供的BrowserRouter组件默认创建了history,并且未暴露出来,在非react中使用history会出现不起作用的情况

因此:可不使用BrowserRouter组件,自行创建一个history,如下:

•创建history

  1. // src/history.js 一定要放在src的根目录下 
  2. import createHistory from 'history/createBrowserHistory' 
  3. const history = createHistory() 
  4. export default history 

•配置router

  1. // src/index.js  注意router上的history 
  2. import { Router, Link, Route } from 'react-router-dom' 
  3. import history from './history' 
  4. ReactDOM.render( 
  5.   <Provider store={store}> 
  6.     <Router history={history}> 
  7.       ... 
  8.     </Router> 
  9.   </Provider>, 
  10.   document.getElementById('root'), 

•其他地方使用,如接口统一处理层

  1. import history from '@/history'
  2. export function addProduct(props) { 
  3.   return dispatch => 
  4.     axios.post('xxx', props, config) 
  5.       .then(response => { 
  6.         history.push('/cart'
  7.       }) 

引用链接

[1] react 英文文档: https://reactjs.org/docs/getting-started.html

[2] create-react-app: https://facebook.github.io/create-react-app/docs/getting-started

[3] ant UI库的配置参考: https://blog.csdn.net/qwe502763576/article/details/83242823

[4] react-router 英文: https://reacttraining.com/react-router/web/example/basic

[5] react-router 中文: http://router.happyfe.com/web/guides/quick-start

[6] 参考内容: https://www.robinwieruch.de/react-warning-cant-call-setstate-on-an-unmounted-component/

[7] 参考: https://github.com/brickspert/blog/issues/3

 

责任编辑:姜华 来源: 松宝写代码
相关推荐

2021-06-30 07:19:35

微服务业务MySQL

2021-10-12 23:10:58

UnsafeJavaJDK

2018-07-30 08:41:48

VueReact区别

2022-08-02 09:55:04

React前端

2021-05-14 09:49:47

React HookReact应用

2021-10-26 13:55:53

搞定系统设计

2020-08-07 08:04:03

数据库MySQL技术

2021-04-16 08:11:24

js前端JavaScript

2015-11-23 10:16:12

2009-07-09 13:16:54

Java Swing开

2018-02-28 16:35:09

开发职位软件

2015-08-27 10:49:43

JavaScript开发框架

2010-11-02 14:11:15

SilverlightWPF微软开发

2018-02-06 11:10:27

iOS开发Xcode快捷键

2015-07-28 14:39:02

IOS技巧

2018-03-26 20:00:32

深度学习

2020-02-06 16:23:00

LinuxWeb前端

2022-06-08 08:03:51

React.jsReactJS 库

2011-06-10 20:02:55

嵌入式开发

2018-02-04 22:29:21

iOS开发
点赞
收藏

51CTO技术栈公众号