聊聊React開發(fā)的一些坑(一)
一、項(xiàng)目生成
- create-react-app saucxs-web 生成項(xiàng)目
- cd saucxs-web 進(jìn)入項(xiàng)目
- yarn start 啟動(dòng)項(xiàng)目
二、項(xiàng)目部署
- 本地開發(fā) yarn start
- 線上部署 yarn build
三、參考文檔
- react 英文文檔[1]
- create-react-app[2]
- ant UI庫的配置參考[3]
- react-router 英文[4]
- react-router 中文[5]
四、配置項(xiàng)
1、Ant Design UI庫引入
- yarn add antd 安裝UI庫
- yarn add babel-plugin-import 實(shí)現(xiàn)按需引入
package.json/babel 中增加如下內(nèi)容:
- "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;
此時(shí)可以使用UI庫的默認(rèn)樣式
2、自定義Ant Design UI庫樣式
- 安裝 less 和 less-loader
- 使用命令 yarn run eject 暴露配置文件
- 在 webpack.config.dev.js 與 webpack.config.prod.js 中做如下修改:
創(chuàng)建 antModifyVars.js 文件
- 'use strict';
- const modifyVars = {
- 'primary-color': '#E26A6A',
- }
- module.exports = modifyVars;
創(chuàng)建 less 相關(guān)變量,參考默認(rèn)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$/;
在 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'),
- },
在 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);
- }
3、ES6 API支持,引入polyfills
增加低版本瀏覽器、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'] }
4、引入 react-router-dom 路由
- react-router-dom 依賴 react-router,所以使用npm安裝依賴的時(shí)候,只需要安裝相應(yīng)環(huán)境下的庫即可,不用再顯式安裝react-router。
- 由于需要權(quán)限配置,所以增加AuthorizedRoute.js控制權(quán)限
5、配置別名,@ 指向 src 目錄
webpack.base.conf.js 與 webpack.base.conf.js的配置一致,如下:
- 定義resolvePath方法,新版中resolve名稱被占用
- function resolvePath (dir) {
- return path.join(__dirname, '..', dir)
- }
- // resolve.alias中增加別名配置:
- '@': resolvePath('src')
6、引入 axios
在package.json底部增加以下代碼解決跨域問題
- // 新版本需要借助http-proxy-middleware,在src下創(chuàng)建setupProxy.js,內(nèi)容:
- // 會(huì)自動(dòng)引用,不需要額外的配置
- const proxy = require('http-proxy-middleware')
- module.exports = function (app) {
- app.use(
- proxy(
- '/api', {
- target: 'http://**********',
- changeOrigin: true
- }
- )
- )
- }
- // 定義多個(gè)入口:
- module.exports = function (app) {
- app.use(proxy('/api', { target: 'http://localhost:7001' }));
- app.use(proxy('/api2', { target: 'http://localhost:7001' }));
- }
7、樣式處理
使用react-css-modules實(shí)現(xiàn)組件內(nèi)部樣式與外部分離,使用方式:
- 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已經(jīng)實(shí)現(xiàn)配置,無需再修改webpack配置文件
- 上述方法可實(shí)現(xiàn),同時(shí)使用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 中修改一下代碼:
- {
- 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,
- }
- },
- ],
- },
五、開發(fā)過程中遇到的問題
1、warning提示:no-op
參考內(nèi)容[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.
也就是不能在卸載的組件中進(jìn)行狀態(tài)更新操作,如異步請求、事件、定時(shí)器等,在componentWillUnmount生命周期中都應(yīng)該進(jìn)行相應(yīng)的取消處理
對于事件、定時(shí)器,只需要在componentWillUnmount方法中,進(jìn)行取消事件監(jiān)聽或者清除定時(shí)器的操作即可
以下方案均針對異步操作帶來的問題進(jìn)行處理。
方案一:使用一個(gè)變量[_isMounted]來控制是否更新狀態(tài)
- 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() {
- // 取消事件監(jiān)聽、清除定時(shí)器、控制異步請求
- this._isMounted = false
- }
- render() {
- return (
- ...
- )
- }
- }
- export default newComponent
方案二:比較粗暴的方式,直接更改setState函數(shù)
- import React from 'react'
- class newComponent extends React.Component {
- componentWillUnmount() {
- // 取消事件監(jiān)聽、清除定時(shí)器、控制異步請求
- this.setState = () => {
- return
- }
- }
- render() {
- return (
- ...
- )
- }
- }
- export default newComponent
方案三,可直接取消接口請求,如axios的CancelToken
2、react-router4.0版本中,不同場景下的路由跳轉(zhuǎn)
參考[7]
方案一,官方推薦,使用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
在真實(shí)的業(yè)務(wù)場景中,經(jīng)常需要在非react組件中使用路由跳轉(zhuǎn),如:接口統(tǒng)一處理場景下,用戶登錄失效時(shí),跳轉(zhuǎn)至登錄頁面。
由于react-router4.0中提供的BrowserRouter組件默認(rèn)創(chuàng)建了history,并且未暴露出來,在非react中使用history會(huì)出現(xiàn)不起作用的情況
因此:可不使用BrowserRouter組件,自行創(chuàng)建一個(gè)history,如下:
•創(chuàng)建history
- // src/history.js 一定要放在src的根目錄下
- import createHistory from 'history/createBrowserHistory'
- const history = createHistory()
- export default history
•配置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'),
- )
•其他地方使用,如接口統(tǒng)一處理層
- import history from '@/history';
- export function addProduct(props) {
- return dispatch =>
- axios.post('xxx', props, config)
- .then(response => {
- history.push('/cart')
- })
- }
引用鏈接
[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] 參考內(nèi)容: https://www.robinwieruch.de/react-warning-cant-call-setstate-on-an-unmounted-component/
[7] 參考: https://github.com/brickspert/blog/issues/3