聊聊React開發(fā)的一些坑(一)
一、項目生成
- create-react-app saucxs-web 生成項目
 - cd saucxs-web 進(jìn)入項目
 - yarn start 啟動項目
 
二、項目部署
- 本地開發(fā) 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 實現(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;
 
此時可以使用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安裝依賴的時候,只需要安裝相應(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)容:
 - // 會自動引用,不需要額外的配置
 - 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' }));
 - }
 
7、樣式處理
使用react-css-modules實現(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)實現(xiàn)配置,無需再修改webpack配置文件
 - 上述方法可實現(xiàn),同時使用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)更新操作,如異步請求、事件、定時器等,在componentWillUnmount生命周期中都應(yīng)該進(jìn)行相應(yīng)的取消處理
對于事件、定時器,只需要在componentWillUnmount方法中,進(jìn)行取消事件監(jiān)聽或者清除定時器的操作即可
以下方案均針對異步操作帶來的問題進(jìn)行處理。
方案一:使用一個變量[_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)聽、清除定時器、控制異步請求
 - this._isMounted = false
 - }
 - render() {
 - return (
 - ...
 - )
 - }
 - }
 - export default newComponent
 
方案二:比較粗暴的方式,直接更改setState函數(shù)
- import React from 'react'
 - class newComponent extends React.Component {
 - componentWillUnmount() {
 - // 取消事件監(jiān)聽、清除定時器、控制異步請求
 - 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
在真實的業(yè)務(wù)場景中,經(jīng)常需要在非react組件中使用路由跳轉(zhuǎn),如:接口統(tǒng)一處理場景下,用戶登錄失效時,跳轉(zhuǎn)至登錄頁面。
由于react-router4.0中提供的BrowserRouter組件默認(rèn)創(chuàng)建了history,并且未暴露出來,在非react中使用history會出現(xiàn)不起作用的情況
因此:可不使用BrowserRouter組件,自行創(chuàng)建一個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















 
 
 




 
 
 
 