React踩坑 - junruchen/junruchen.github.io GitHub Wiki
-
create-react-app monkey-web
生成项目 -
cd monkey-web
进入项目 -
yarn start
启动项目
- 本地开发
yarn start
- 线上部署
yarn build
-
yarn add antd
安装UI库 -
yarn add babel-plugin-import
实现按需引入
package.json/babel 中增加如下内容:
"plugins": [
[
"import",
{
"libraryName": "antd",
"libraryDirectory": "es",
"style": "css"
}
]
]
组件使用 如下:
import React, { Component } from 'react';
import { Button } from 'antd'
import 'antd/dist/antd.css'
class App extends Component {
render() {
return (
<Button type="primary">测试</Button>
);
}
}
export default App;
此时可以使用UI库的默认样式
- 安装
less
和less-loader
- 使用命令
yarn run eject
暴露配置文件 - 在
webpack.config.dev.js
与webpack.config.prod.js
中做如下修改:
a) 创建 antModifyVars.js
文件
'use strict';
const modifyVars = {
'primary-color': '#E26A6A',
}
module.exports = modifyVars;
b) 创建 less
相关变量,参考默认sass的配置:
// style files regexes
const cssRegex = /\.css$/;
const cssModuleRegex = /\.module\.css$/;
const sassRegex = /\.(scss|sass)$/;
const sassModuleRegex = /\.module\.(scss|sass)$/;
// 增加less部分
const lessRegex = /\.less/;
const lessModuleRegex = /\.module\.less$/;
c) 在 module.rules
的 oneOf
下, 仿照sass追加一下代码:
// Opt-in support for LESS (using .less extensions).
// Chains the less-loader with the css-loader and the style-loader
// to immediately apply all styles to the DOM.
// By default we support LESS Modules with the
// extensions .module.scss or .module.sass
{
test: lessRegex,
exclude: lessModuleRegex,
use: getStyleLoaders({ importLoaders: 2 }, 'less-loader'),
},
// Adds support for CSS Modules, but using SASS
// using the extension .module.scss or .module.sass
{
test: lessModuleRegex,
use: getStyleLoaders(
{
importLoaders: 2,
modules: true,
getLocalIdent: getCSSModuleLocalIdent,
},
'less-loader'),
},
d) 在 getStyleLoaders
中,处理 less-loader
// dev下的配置
if (preProcessor) {
let loader = {loader: require.resolve(preProcessor)}
if (preProcessor === "less-loader") {
loader.options.modifyVars = modifyVars
loader.options.javascriptEnabled = true
}
loaders.push(loader);
}
// prod下的配置
if (preProcessor) {
let loader = {
loader: require.resolve(preProcessor),
options: {
sourceMap: shouldUseSourceMap,
},
}
if (preProcessor === "less-loader") {
loader.options.modifyVars = modifyVars
loader.options.javascriptEnabled = true
}
loaders.push(loader);
}
增加低版本浏览器、IE浏览器对ES6API的支持,IE9,IE9+
方法一,安装 yarn add react-app-polyfill
// src/index.js中的【第一行】引入对IE9及更高版本的支持
import 'react-app-polyfill/ie9';
// 其他支持,如:对IE11及更高版本的支持
import 'react-app-polyfill/ie11';
方法二,安装 yarn add babel-polyfill
// webpack.base.conf.js中引入:
require("babel-polyfill")
// webpack.base.conf.js中配置:
entry: { app: ['babel-polyfill', './src/main.js'] }
-
react-router-dom
依赖react-router
,所以使用npm安装依赖的时候,只需要安装相应环境下的库即可,不用再显式安装react-router。 - 由于需要权限配置,所以增加
AuthorizedRoute.js
控制权限
webpack.base.conf.js 与 webpack.base.conf.js的配置一致,如下:
定义resolvePath方法,新版中resolve名称被占用
function resolvePath (dir) {
return path.join(__dirname, '..', dir)
}
// resolve.alias中增加别名配置:
'@': resolvePath('src')
在package.json
底部增加以下代码解决跨域问题
// 新版本需要借助http-proxy-middleware,在src下创建setupProxy.js,内容:
// 会自动引用,不需要额外的配置
const proxy = require('http-proxy-middleware')
module.exports = function (app) {
app.use(
proxy(
'/api', {
target: 'http://**********',
changeOrigin: true
}
)
)
}
// 定义多个入口:
module.exports = function (app) {
app.use(proxy('/api', { target: 'http://localhost:7001' }));
app.use(proxy('/api2', { target: 'http://localhost:7001' }));
}
使用react-css-modules实现组件内部样式与外部分离,使用方式:
import React from 'react'
import CSSModules from 'react-css-modules';
import { Button } from 'antd'
import styles from './Header.module.scss'
class Header extends React.Component {
render () {
return (
<div>
<Button type="primary" className="nowrap" styleName="test">测试</Button>
</div>
)
}
}
export default CSSModules(Header, styles)
注意:
- 由于最新版create-react-app已经实现配置,无需再修改webpack配置文件
- 上述方法可实现,同时使用antd样式、全局样式、组件私有样式
- 特别注意组件私有样式文件的命名
[name].module.scss
- 安装
stylus
和stylus-loader
- 使用命令
yarn run eject
暴露配置文件 - 在
webpack.config.dev.js
与webpack.config.prod.js
的module/rules/oneOf
中修改一下代码:
{
test: /\.(css|styl)$/,
use: [
require.resolve('style-loader'),
{
loader: require.resolve('css-loader'),
options: {
importLoaders: 1,
},
},
{
loader: require.resolve('postcss-loader'),
options: {
// Necessary for external CSS imports to work
// https://github.com/facebookincubator/create-react-app/issues/2677
ident: 'postcss',
sourceMap: true,
plugins: () => [
require('postcss-flexbugs-fixes'),
autoprefixer({
browsers: [
'>1%',
'last 4 versions',
'Firefox ESR',
'not ie < 9', // React doesn't support IE8 anyway
],
flexbox: 'no-2009',
}),
],
},
},
{
loader: require.resolve('stylus-loader'),
options: {
sourceMap: true,
}
},
],
},
问题描述
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]来控制是否更新状态
import React from 'react'
class newComponent extends React.Component {
_isMounted = false
componentDidMount() {
this._isMounted = true
axios
.get('https://hn.algolia.com/api/v1/search?query=react')
.then(result =>
if (!this._isMounted) return
this.setState({
news: result.data.hits,
}),
)
}
componentWillUnmount() {
// 取消事件监听、清除定时器、控制异步请求
this._isMounted = false
}
render() {
return (
...
)
}
}
export default newComponent
方案二:比较粗暴的方式,直接更改setState函数
import React from 'react'
class newComponent extends React.Component {
componentWillUnmount() {
// 取消事件监听、清除定时器、控制异步请求
this.setState = () => {
return
}
}
render() {
return (
...
)
}
}
export default newComponent
方案三,可直接取消接口请求,如axios的CancelToken
方案一,官方推荐,使用withRouter
import React from 'react'
import { withRouter } from 'react-router-dom'
class NewComponent extends React.Component {
AFunction() {
this.props.history.push('/path')
}
render() {
return (
...
)
}
}
export default withRouter(NewComponent)
方案二,不推荐,使用context
import React from 'react'
import PropTypes from 'prop-types'
import { withRouter } from 'react-router-dom'
class NewComponent extends React.Component {
static contextTypes = {
router: PropTypes.object
}
constructor(props, context) {
super(props, context);
}
AFunction() {
this.context.router.history.push('/path')
}
render() {
return (
...
)
}
}
export default NewComponent
方案三,使用history
在真实的业务场景中,经常需要在非react组件中使用路由跳转,如:接口统一处理场景下,用户登录失效时,跳转至登录页面。
由于react-router4.0中提供的BrowserRouter组件默认创建了history,并且未暴露出来,在非react中使用history会出现不起作用的情况
因此:可不使用BrowserRouter组件,自行创建一个history,如下:
a. 创建history
// src/history.js 一定要放在src的根目录下
import createHistory from 'history/createBrowserHistory'
const history = createHistory()
export default history
b. 配置router
// src/index.js 注意router上的history
import { Router, Link, Route } from 'react-router-dom'
import history from './history'
ReactDOM.render(
<Provider store={store}>
<Router history={history}>
...
</Router>
</Provider>,
document.getElementById('root'),
)
c. 其他地方使用,如接口统一处理层
import history from '@/history';
export function addProduct(props) {
return dispatch =>
axios.post('xxx', props, config)
.then(response => {
history.push('/cart')
})
}