React项目搭建

一.项目准备(此项目由React+TS实现)

1.创建项目,此处我们使用脚手架创建项目

npx create-react-app demo --template typescript
或者
yarn create react-app demo --template typescript

  然后我们把项目进行一定的精简,删除不必要的文件,修改index.tsx为下图的样子

2.进行项目解包

  因为我们使用less进行样式预处理,所以我们必须在webpack.config.js里面进行配置,但是我们发现在项目里面并      没有看到这样的一个文件。原因是我们使用脚手架创建的项目,已经帮我们进行了一些重要的设置,如非必要,脚    手架不希望我们修改配置了。

  所以我们需要进行项目的解包。解包的命令可以在 package.json > scripts 中找到

  但是此时如果我们直接解包,会无法成功。因为解包是一个不可逆的过程,我们最好进行一次版本管理。所以我们    先 使用git先把之前的修改进行保存。

git add .
git commit -m '解包前的备份'

然后我们就可以进行解包了

npm run eject
或者
yarn eject

  解包完成后我们会在根目录看到一个config文件夹,这个config文件夹就是我们要进行配置的地方

3.下载项目所需的依赖

   项目中我们需要使用到的依赖比较多,所以我们先一次性下载好所有的依赖。

   less和less-loader

npm i less less-loader --save--dev
或者
yarn add less less-loader --save--dev

  react-router-dom

npm i react-router-dom@6 --save
或者
yarn add react-router-dom@6 --save

  redux和react-redux

npm i redux react-redux --save
或者
yarn add redux react-redux --save

  axios

npm i axios --save
或者
yarn add axios --save

  mui , mui的官网

npm install @mui/material @emotion/react @emotion/styled
或者
yarn add @mui/material @emotion/react @emotion/styled

  postcss-px-to-viewport

npm i postcss-px-to-viewport --save-dev
或者
yarn add postcss-px-to-viewport --save-dev

4.配置移动端屏幕适配

在前端发展过程中,最认可的屏幕适配方案主要有两个,比较老的是media+rem,比较新的是viewport,这个项目采用viewport. 也就是说我们使用vw+vh来进行屏幕的适配,但是其实我们这个项目中只需要使用vw就够了.

我们在config/webpack.config.js里面进行配置,找到postcss-loader节点,在这个节点的plugins节点的里面添加下面的配置.

require('postcss-px-to-viewport')({
    viewportWidth: 1125, // 视口宽度,根据设计图的大小
    viewportHeight: 1000, // 视口高度,根据设计图的大小
    unitPrecision: 6,// 保留小数点
    viewportUnit: 'vw', // 单位
    selectorBlackList: [], // 排除的类
    minPixelValue: 1, // px的最小单位
    mediaQuery: false, // 排除媒体查询
    landscapeUnit: 'vw', // 横屏单位
    fontViewportUnit: 'vw' // 字体属性单位
})

  值得注意的是 plugins 节点有一个三元判断,需要在两个分支上都添加这段代码。

5.配置less-loader

我们下载好了less-loader后,需要在config/webpack.config.js里面进行配置,找到module/rules节点,在最后一个sass-loader后添加less-loader的配置

{
    test: /\.less$/,
    use: getStyleLoaders({}, 'less-loader')
}

6.配置@指向

首先还是找到config/webpack.config.js里面,找到resolve/alias节点,在最后添加下面代码:

"@": path.join(__dirname, "../src")

 然后还是找到tsconfig.json,在compilerOptions节点的最后节点的最后添加

 

"baseUrl": "./",
"paths": {
    "@/*": ["src/*"]
}

注意:以上修改 webpack.config.js 的所有步骤,都需要重启脚手架方能生效。

7.解决移动端的点击延迟

我们通常会使用fastclick解决移动端的点击延迟问题,在index.htm里面添加如下代码.

<title>IT猿题库</title>
<script src="https://as.alipayobjects.com/g/component/fastclick/1.0.6/fastclick.js"></script>
<script>
  if ('addEventListener' in document) {
    document.addEventListener('DOMContentLoaded', function() {
      FastClick.attach(document.body);
    }, false);
  }
  if(!window.Promise) {
    document.writeln('<script src="https://as.alipayobjects.com/g/component/es6-promise/3.2.2/es6-promise.min.js"'+'>'+'<'+'/'+'script>');
  }
</script>

三.配置路由

我们在src目录下新建一个router文件夹,然后在里面新建一个index.tsx.路由配置如下:

import {BrowserRouter,Routes,Route} from "react-router-dom"
import Login from "../views/Login"
import App from "../App"
import Home from "../views/Home"
import Fast from "../views/Fast"
import Mine from "../views/Mine"
import SelectPractice from "../views/Select"
import Practice from "../views/Practice"
const router = (
    <BrowserRouter>
      <Routes>
        <Route path="/login" element={<Login />}></Route>
        <Route path="/" element={<App />}>
          <Route index element={<Home />}></Route>
          <Route path="home" element={<Home />}></Route>
          <Route path="fast" element={<Fast />}></Route>
          <Route path="mine" element={<Mine />}></Route>
          <Route path="select" element={<SelectPractice />}></Route>
          <Route path="practice" element={<Practice />}></Route>
        </Route>
      </Routes>
    </BrowserRouter>
  );
  export default router;

然后自己在views目录下新建路由对应的组件,在入口的index.tsx中渲染我们的路由。同时记得要在App组件中添加<Outlet/>

import router from "./router";

const rootEl = document.getElementById("root");
const root = ReactDOM.createRoot(rootEl!);
root.render(router);

至此项目目录快照如下:

 

5.配置Store

  项目中肯定会有部分数据需要共享的,所以我们需要使用react-redux进行全局数据共享。

  先在src目录上新建store目录,在里面新建 index.ts 文件,写入下面代码。

/* 
    使用TS的方式创建store
        1.createStore
        2.需要定义state和action的类型
*/

import {createStore} from "redux"

const defState:IRootState={
    count:100
}
const reducer=(state:IRootState=defState,action:IStateAction)=>{
state = JSON.parse(JSON.stringify(state));
return state
}
const store=createStore(reducer) export default store;

其中 IStoreStateIStoreAction 是两个interface ,我们需要在 src/types 目录下新建一个 store.d.ts 进行声明。

.d.ts 文件用于声明一些全局可用的类型数据。

// xxx.d.ts是ts里面用于全局地声明类型的文件
interface IRootState{
    count:number
}

interface IStateAction{
    type:string;
    payload:any
}

最后在入口的index.tsx里面使用 Provider 组件进行数据共享。

import router from "./router";
import { Provider } from "react-redux";
import store from "./store";

const rootEl = document.getElementById("root");
const root = ReactDOM.createRoot(rootEl!);
root.render(<Provider store={store}>{router}</Provider>);

示例:在Fast组件中使用store里面数据

import { useSelector } from "react-redux";
// 快速刷题页面
export default function Fast() {
  const count = useSelector((state: IRootState) => state.count);
  return <div>Fast{count}</div>;
}

6.请求配置

  在 src/api 目录里面新建 request.ts

import axios from "axios";

const request = axios.create({
  baseURL: "/api",
  timeout: 5000,
});

request.interceptors.request.use(
  (config) => {
    return config;
  },
  (err) => Promise.reject(err)
);
request.interceptors.response.use(
  (res) => {
    return res.data;
  },
  (err) => Promise.reject(err)
);

export default request;

然后我们需要解决请求跨域的问题,推荐使用wepack的代理。我们直接修改 config/webpackDevServer.config.js 里面的配置. 【参考地址】

module.exports = function (proxy, allowedHost) {
  const disableFirewall =
    !proxy || process.env.DANGEROUSLY_DISABLE_HOST_CHECK === 'true';

  proxy = {
    ...proxy,
    '/api': {
      target: 'https://www.ahsj.link/rambo', // 后台服务地址以及端口号
      changeOrigin: true, //是否跨域
      pathRewrite: { '^/api': '/' },
      secure: false
    }
  }

然后我们在 src/api 目录下新建 index.ts ,用来导出请求用的 api。

import request from "./request";

export const LoginApi = (params: ILoginParams) =>  request.post("/1024/login", params);

其中 ILoginParams 是一接口,我们在 src/types 目录下新建一个 api.d.ts 声明。

interface ILoginParams {
  username: string;
  password: string;
}

但是我们如果在请求的地方想要得到后端返回的数据,ts会检测不到,所以我们需要给所有API都设置对应的返回值。

已经知道我们所有的请求返回的数据都有 errCode / message / data 三个属性。所以我们需要定义一个用于接收请求返回值的 interface,在 api.d.ts 里面定义它。

因为 errCodemessage 我们都知道是固定的,但是对于 data 来说,不同的接口返回的数据不同,我们就先给个泛型,以便在需要的时候再给一个指定的返回值。

interface IAxiosResponseData<T> {
  data: T;
  errCode: number;
  message: string;
}

但是我们需要真正需要使用的类型是需要在请求的 then 方法中接收到的数据,而 then 方法需要的是一个 Promise包着的数据,所以我们还定义一个类型别名以方便我们在请求时规定返回值.

type IResonseData<T> = Promise<IAxiosResponseData<T>>;

至此我们就可以把 api.ts里面的数据接口方法进行修改

export const LoginApi = (params: ILoginParams): IResonseData<string> => request.post("/1024/login", params);

这样我们就能在调用 LoginApi 这个方法的时候从 then 中知道 data 是一个 string;

 

 

 

 

posted @ 2022-04-13 12:27  李云蹊  阅读(211)  评论(1编辑  收藏  举报