节省你1个小时的烦恼【vue转react的项目搭建】

757 阅读8分钟

写此文的目的:

  1. 整理汇总react的实战经验
  2. 方便技术栈以vue为主,需要从0搭建react项目的人
  3. 把vue2.x的开发习惯合理地搬到react项目

涉及技术栈:

  • react:react官方文档,先全部过一遍对react的写法有点印象。
  • create-react-app:官方推荐的react单页应用脚手架。和vue-cli不同,包括vue-router、vuex、webpack配置等内容,在这个脚手架里都不包含,需要自己单独配置。
  • react-app-rewired: 用于在不需要eject的情况下,给react进行webpack配置。(eject指的是create-react-app的其中一个命令,运行之后会把所有详细的webpack配置全放开出来,有点像vue-cli的2.x版本的webpack配置的文件目录)
  • react-router-dom:用来设置页面路由。
  • mobx:状态管理的库。类似于vuex。

项目搭建:

之前我们在写vue项目的时候,vue-cli这个脚手架已经很贴心地帮我们把所有可配置的东西都搭建好了。比如router、状态管理的库、webpack配置的入口和格式。下文搭建的项目已包括对移动端的适配。

但是react官方的create-react-app装好之后啥都没有。

所以这时候需要我们装好项目之后,再装一堆其他用于补充功能的库。首先先用create-react-app创建基本的目录结构。

1. 创建项目

npm install -g create-react-app 
create-react-app  myProject
yarn start

2. 安装项目需要的库

npm install react-app-rewired customize-cra /1 用来配置webpack配置的,两个库需要搭配使用
            postcss-pxtorem /2 px转rem库,用于移动端适配
            less less-loader /4 less的配置
            react-router-dom /5 路由库
            loadable-components /6 搭配路由,使页面能够按需加载的库
            --save

3.进行webpack配置

  • 创建好目录并安装好上述的库之后就可以进行webpack的配置了。配置主要是用到了上述的react-app-rewired、customize-cra来开放webpack的配置。
  • 首先在根目录下,创建一个文件config-overrides.js,然后按照下面的代码配置。下面的代码已经包括了解决跨域的proxy配置、文件夹引入的别名alias配置、配置lessloader、用于移动端的将px转换为rem的样式适配。
/* config-overrides.js */
// 这里是我要用一些配置信息
const {
  override,
  addPostcssPlugins,
  addWebpackAlias,
  addLessLoader,
  overrideDevServer,
} = require('customize-cra');
const path = require('path');

//解决跨域的peoxy配置
const devServerConfig = () => config => {
  return {
    ...config,
    port: 3000,
    proxy: {
      '/test': {
        target: 'https://www.baidu.com',
        changeOrigin: true,
        pathRewrite: {
          '^/test': '/test',
        }
      },
    }
  };
};


module.exports = {
  webpack: override(
    // 文件夹引入的别名alias配置
    addWebpackAlias({
      ['@']: path.resolve(__dirname, 'src'),
      ['components']: path.resolve(__dirname, 'src/components'),
      ['@pic']: path.resolve(__dirname, 'src/assets/pic'),
      ['@common']: path.resolve(__dirname, 'src/utils/common'),

    }),
    // 配置lessloader
    addLessLoader(),
    //配置pxtorem,对移动端的项目的px转换成rem
    addPostcssPlugins([require('postcss-pxtorem')({
      rootValue: 37.5,
      propWhiteList: [],
      minPixelValue: 2
    })])
  ),
  devServer: overrideDevServer(devServerConfig())
};
  • 设置好之后然后修改package.json文件中的scripts,把之前用react-scripts跑起来的命令改为用react-app-rewired。如下面的代码所示。之后再正常地运行npm run start即可。
 "scripts": {
      -   "start": "react-scripts start",
      +   "start": "react-app-rewired start",
      -   "build": "react-scripts build",
      +   "build": "react-app-rewired build",
      -   "test": "react-scripts test --env=jsdom",
      +   "test": "react-app-rewired test --env=jsdom",
      -   "eject": "react-scripts eject"
    }

需要注意的点有:

1.上面的addLessLoader必须要写在addPostcssPlugins之前,否则less中的css样式将不会将px值转换为rem

2.如果需要其他更多的webpack配置可以在customize-cra的github官方仓库找找,或者google、百度搜索现成的配置参照一下

3.上面配置好lessloader之后,之后可以直接在jsx文件中引入less文件,就可以添加组件的样式了

4.postcss-pxtorem的库引入之后,你还设置一段配置当前页面的html的fontsize的代码,直接在全局引入即可

4.router实现

- 路由及嵌套路由配置

路由这边就比较麻烦了,首先得去看看react-router官网看看,大致了解一遍路由的格式和写法。

然后我们这边仿照vue-router的配置文件,先把路由地址的配置大致先写好,方便我们之后的代码理解。需要注意的点有:

  1. loadable-components是用于实现页面按需加载的库。
  2. 下面代码中的二级路由childrens的path需要把之前页面的path也加上,和vue-router的配置文件有点不一样。

下面是参照vue-router把react路由单独提取到一个文件index.js的写法。

import loadable from "loadable-components";  //按需加载的库


export const routes = [{
    path: '/home',
    component: loadable(() => import("../views/home/index.jsx")),
    childrens:[
      {
        path: '/home/mypage', //注意这里的写法要把父路径也带上
        component: loadable(() => import("../views/myPage/index.jsx")),
      },
      {
        path: '/home/search',
        component: loadable(() => import("../views/search/index.jsx")),
      }
    ]
  },
  {
    path: '/mypage',
    component: loadable(() => import("../views/myPage/index.jsx")),
  },
  {
    path: '/search',
    component: loadable(() => import("../views/search/index.jsx")),
  },
  {
    path: '/createsearch',
    component: loadable(() => import("../views/createSearch/index.jsx")),
  },
  {
    path: '/stockresult',
    component: loadable(() => import("../views/stockResult/index.jsx")),
  }
]

上面的路由文件,如果是在vue的话,只要在main.js里将引入上面文件导出的router,然后再使用<router-view />来展示内容。但是在react这边的代码就有点不一样了。整个逻辑都得用react的方式实现一遍。

读取路由配置的main.js代码如下:

//以下代码已实现基础路由以及嵌套的子路由

import React from "react";
import { HashRouter as Router, Route, Switch } from "react-router-dom";  
//react的路由库,这里引入HashRouter就是使用hash模式的路由,history模式的话是BrowserRouter
import { routes } from './index.js'
const renderRoutes = (routeData = [], extraProps = {}) =>
  routeData ? (
    <Switch>
      {routeData.map((route, i) => (
        <Route
          key={route.key || i}
          path={route.path}
          exact={route.exact}
          strict={route.strict}
          render={props => {
            //判断是不是还有子路由
            if (route.childrens) {
              return <route.component {...props} {...extraProps} route={route}>
                {renderRoutes(route.childrens)}
              </route.component>
            }
            else {
              return <route.component {...props} {...extraProps} route={route} />
            }
          }}
        />
      ))}
    </Switch>
  ) : null;

function AppRouter() {
  return (
    <Router>
      {
        renderRoutes(routes)
      }
    </Router>
  )
}

export default AppRouter;

最后再把写好的router/main.jsx文件,在全局的入口文件引入即可。

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './router/main.jsx';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

接下来如果是在父页面中需要嵌套展示children页面的话,比如当前的url为http://localhost:3000/#/home/search。在vue中的写法是直接在home组件中写入<router-view/>,子路由的页面内容就会显示在对应的空间。

但是在react中,如果根据根据上面的index.js文件的写法一样的话。需要在home这个组件的render中写入下面的代码,然后router的path匹配的话,下面的代码就能渲染出../views/search/index.jsx组件的内容。

{
  this.props.children.props.children
}
- 路由跳转

接下来还有很重要的替代vue中常用的this.$router.push(),路由跳转的方法。在react中d有两种方法:

  • 第一种就是使用react-router提供的hook,能够获取url参数和进行路由跳转
//useHistory
//useHistory可以让组件内部直接访问history,无须通过props获取。路由跳转可用history.push()实现。

import { useHistory } from "react-router-dom";

const Contact = () => {
  const history = useHistory();
  return (
    <Fragment>
      <h1>Contact</h1>
      <button onClick={() => history.push("/")}>Go to home</button>
    </Fragment>
  );
};

//useParams
//获取url的参数

const About = () => {
  const { name } = useParams();
  return (
    // props.match.params.name
    <Fragment>
      {name !== "John Doe" ? <Redirect to="/" /> : null}
      <h1>About {name}</h1>
      <Route component={Contact} />
    </Fragment>
  );
};
//useLocation
//useLocation 会返回当前 URL的 location对象

import { useLocation } from "react-router-dom";

const Contact = () => {
  const { pathname } = useLocation();

  return (
    <Fragment>
      <h1>Contact</h1>
      <p>Current URL: {pathname}</p >
    </Fragment>
  );
};
  • 第二种的大致思路就是引入withRouter高阶函数,然后使用props中的hitroy.push来进行跳转。 示例代码如下:
import React from 'react'

import { withRouter } from "react-router-dom";

function Main(props) {
  let jumpResultPage = (query) => {
    props.history.push({
      pathname: '/stockresult',    //pathname是跳转的路径
      state: { from: props.location.pathname }     
      //state是需要传递的数据,在跳转过去的页面中也可以用props.location.state进行获取。
    })
  }
  return (
    <div className="hotMain" onClick={jumpResultPage}></div>
  );
}

export default withRouter(Main)

history具体其他的跳转参数可以参照官方文档

5.配置mobx

mobx是和vuex概念相似的状态管理库。基本使用方法如下:

import { makeAutoObservable } from 'mobx'

// 构造响应对象
export const store = makeAutoObservable({
  resultData: '',        //类似于vuex中的state
  changeResultData(data) {
    this.resultData = data    //类似于vuex中的mutation
  }
})

你可以把上述的代码当做是vuecli项目中vuex创建的store/index.js文件,vue中的state和mutation直接定义为makeAutoObservable参数。

然后在需要使用的地方引入刚刚封装好的store

import { store } from './mobx/index.js'  //这个就是上面写的代码的文件

//改变值的方法
store.changeResultData('你需要改变的值')

//获取值的方法
let value = store.resultData

其他笔记

  • react的class名字不能用保留字,如icon
  • 有时候页面报错,然后改过来之后,react进行热更新了,但页面还是会停留在上一次的报错页面,需要手动刷新页面

image.png

  • BrowserRouter是history模式的路由。HashRouter是hash模式的路由。直接import使用就可以了。这点和vue区别挺大的。
import {
    BrowserRouter as Router,
    Route,
    Link
} from 'react-router-dom';

#改为
import {
    HashRouter as Router,
    Route,
    Link
} from 'react-router-dom';
  • 在上面提到webpack配置中的config-overrides.js,需要注意先引入lessloader然后再引入addPostcssPlugins对当前px值的样式转换为rem。这两个的先后顺序很重要。如果调换过来的话会出现less文件的样式都没有被转换成rem的情况。
  addLessLoader(),
  addPostcssPlugins([require('postcss-pxtorem')({
    rootValue: 37.5,
    propWhiteList: [],
    minPixelValue: 2
    // propWhiteList: [],
    // selectorBlackList: ['weui-']
  })])
  • 注意用withRouter包装过之后入参变为了一个对象,所以需要确保withRouter包装的组件的最外层的元素只有一个。如果出现多个的话会报错。

image.png

  • react的17版本不能用react-loadable,会报错。在npm官方仓库中有新版本的库。

感悟

  • 个人感觉react其实学习难度很大一部分在于官方文档没有vue清晰、其他工具库种类过多,每个库的文档质量参差不齐。但其实都定好一套流程之后,就清晰很多了。用习惯之后其实用vue和react效率都很高。
  • 如果有需要的我再弄一个github仓库把上面的代码弄个demo

参考及收藏文章: