IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> [React 实战系列] 项目的搭建与配置 -> 正文阅读

[JavaScript知识库][React 实战系列] 项目的搭建与配置

项目开始前的配置,除了新建项目的步骤之外,主要以环境和 Redux 相关为主。

这里主要使用 yarn 去下载与更新依赖,主要是因为使用 npm 的命令数次都无法启动,但是使用 yarn 就没有问题。

项目的分析参见:项目开始前的准备工作

配置完的的代码在:React 实战系列-电商项目的搭建与配置

新建项目

新建项目的步骤还是比较简单的,主要分为三步:

  1. 使用官方提供的 CRA(create-react-app) 脚手架去新建一个 React 项目

    C:> npx create-react-app front-proj --template typescript
    
  2. 安装项目依赖包

    这一步主要也是根据上一篇 [项目开始前的准备工作] 中的技术栈进行的安装。

    C:front-proj> yarn add antd axios moment redux react-redux react-router-dom redux-saga connected-react-router redux-devtools-extension @types/react-redux @types/react-router-dom
    
  3. public/index.html 中引入 antd CSS 的 CDN

    <head>
      <!-- 省略其他的引用 -->
      <link
        rel="stylesheet"
        href="https://cdn.bootcdn.net/ajax/libs/antd/4.8.3/antd.min.css"
      />
    </head>
    

    官方文档的使用方法是在 src/App.css 文件中进行引入 antd 的样式,不过部署环境下最好是使用 CDN。

    官方的引入方式如下:

    @import '~antd/dist/antd.css';
    

环境配置与读取

主要分为在 .env(dotenv)文件 中将配置写入到项目中,与在项目中读取 .env文件 中的配置文件,两个步骤。

环境配置

这个环境主要指的是 .env文件 的配置,该文件处于项目的根目录处,即和 public 与 src 同级。.env 文件内容为:

FAST_REFRESH=false

REACT_APP_PRODUCTION_API_URL=http://someproduction.com/api
REACT_APP_DEVLOPMENT_API_URL=http://localhost/api

其中,FAST_REFRESH=false 是为了让 React 能够成功实现热刷新,只需要在 v17 以上版本进行配置,其原因在 React v17 热刷新 不起作用 中有所简述。

REACT_APP_PRODUCTION_API_URLREACT_APP_DEVLOPMENT_API_URL 是为了导出环境变量。CRA(create-react-app) 脚手架内置了 dotenv,因此开发者可以在项目中自行配制环境变量,但是 CRA 同时规定环境变量的命名规范必须遵从 REACT_APP_APINAME,即以 REACT_APP_ 开头。

读取环境配置

在 src 下新建一个 config.ts 文件去进行配置的导出,对 .env文件 中导出的配置获取方法如下:

// 导出 API 这个变量,其他的项目文件可以直接导入 API 变量即可使用
export let API: string;

// 通过 process.env.NODE_ENV 去判断开发环境
if (process.env.NODE_ENV === 'development') {
  // `!`,即 Non-null assertion operator
  API = process.env.REACT_APP_DEVLOPMENT_API_URL!;
} else if (process.env.NODE_ENV === 'production') {
  API = process.env.REACT_APP_PRODUCTION_API_URL!;
}

确实,还有一种做法是直接在项目中获取 process.env.REACT_APP_DEVLOPMENT_API_URL 的方式去获得 API,不过那会花费很多代码去在不同的地方写判断:

if (process.env.NODE_ENV === 'development') {
  // ...
} else {
  // ...
}

与其这样,不如直接在配置文件中导出全局可用的 API 变量,之后再新增其他的环境:如 QA,也不需要改变项目中的代码。

测试环境配置

主要是通过在 index.tsx 中导入一下 API,使用 console.log() 在终端中输出 API 的值进行确认。

代码实现如下:

import { API } from './config';

console.log(API);

随后运行 yarn start 开启开发环境:

C:front-proj> yarn start

结果如下:

使用 yarn build 去进行生产环境的打包,随后使用在 项目开始前的准备工作 中介绍过的 http-server 去运行生产环境:

C:front-proj> http-server build

结果如下:

可以看到,通过 yarn start 运行的本地开发环境中,API 的值是 http://localhost/api;而通过 http-server 运行打包好的生产环境代码中,API 的值就是 http://someproduction.com/api,正如 .env 文件中配置的那样。

组件实现

目前的核心组件总共分为 3 个:Layout(布局),Home(主页) 与 ShopCart(购物车) 三个页面,并且目前还不涉及核心业务,只是简单渲染一下页面。

Layout

Layout 是一个布局的页面,其主要意义还是对 Header、Footer 之类的可公用组件进行封装,不至重复写同样的代码。

实现如下:

import { FC } from 'react';

interface Props {
  children: React.ReactNode;
}

const Layout: FC<Props> = ({ children }) => {
  return <div>Layout {children}</div>;
};

export default Layout;

注*:这个实现方法使用的是 函数式组件的方法。类的实现方法之前在其他的案例中写过,实现类似于:

import React from 'react';
import Header from '../header/index';

export default function HeaderFooterHOC(WrappedComp) {
  class HOC extends React.Component {
    render() {
      return (
        <>
          <Header />
          <WrappedComp />
          <Footer />
        </>
      );
    }
  }
  return HOC;
}

可以看出来,使用 Hook(钩子函数) 比 类函数(class based components) 要简洁很多。

Home

Home 与 ShopCart 的实现方法相对而言都更简单一些,现在只需要渲染一个标识即可。

import Layout from './Layout';

const Home = () => {
  return <Layout>Home</Layout>;
};

export default Home;

渲染效果如下:

home

可以看到,Layout 中的内容和 Home 的内容拼接起来了。

ShopCart

ShopCart 的实现方法也一样:

import Layout from './Layout';

const ShopCart = () => {
  return <Layout>ShopCart</Layout>;
};

export default ShopCart;

渲染结果如下:

shopcart

路由配置

稍微详细一点的笔记在这里:React Router 的基本应用,这里主要就是讲实现了。

因为白屏无法显示页面内容这一问题,这里会使用 HashRouter 去实现。

新增 Route.tsx

用来匹配 Router 和 Component,实现:

import { HashRouter, Route, Switch } from 'react-router-dom';
import Home from './components/core/Home';
import ShopCart from './components/core/ShopCart';

const Routes = () => {
  return (
    <HashRouter>
      <Switch>
        <Route path="/" component={Home} exact />
        <Route path="/shopcart" component={ShopCart} />
      </Switch>
    </HashRouter>
  );
};

export default Routes;

App.tsx 修改路由

依旧只是个人偏好问题,我喜欢把 Application 相关的配置放到 App.tsx,其他的配置放到 index.tsx 中。

import Routes from './Routes';

function App() {
  return <Routes />;
}

export default App;

配置完成后效果如下:

图中使用的是 React 提供的开发工具,用于 Debug。注意被红框圈出来的地方,可以随着页面的变动,组件也从 Home 变更为乐 Shopcart。

使用 React Developer Tools 可以较为直观的展现出当前页面中的嵌套关系,也能够更为直观地展示页面中的组建的 state 和 props。

ShopCart 与 Home 包含的 state 和 props 对比为:

ShopCartHome

ShopCart 下的 Layout 与 Home 下的 Layout 的对比为:

ShopcartHome

仔细判别能够看到,内容结构基本上是相似的,不同之处也在图中显示了出来,如 Home 和 ShopCart 中的 location > pathname,Layout 中的 Rendered by 的内容中,也分别显示了 HomeShopCart。这也是因为 Layout 分别由 Home 和 ShopCart 两个组件渲染了,的确是 被渲染(rendered by) 两个不同的组件。

Redux 配置

暂时不包含 Redux Saga 的配置,现在还没有需要 dispatch 的 action,所以暂时只配置 Reducer。

Redux 的基础学习之前做过笔记:一文快速上手 Redux

这里总结一下 Redux 的特性:Redux 有三个最重要的特性:状态树,Action,Reducer 和 Dispatch。Redux 通过更新 一个 不可变状态树 来进行状态的管理;在 Redux 中,action 是用来描述状态变化;dispatch 用来发送一个 action,触发状态的变化;Reducer 最终实现状态的变化。

基础 Redux 的配置

基本的 Reducer 的结构是这样的:

|- src
|  |- store # redux所在的目录
|  |  |- reducers # 文件夹
|  |  |  |- reducers.ts # 一干reducers
|  |  |  |- index.ts # 最终 combine 一干 reducers,返回一个 root reducer
|  |  |- index.ts # redux的入口文件

所以,基础 Redux 也会按照这个配置实现。

reducers.ts

测试用 reducer,暂时只是导出一个状态作为测试。这里的 reducer 的文件名为 test.reducers.ts。实现为:

export default function testReducer(state: number = 0) {
  return state;
}

store/reducers/index.ts

这个文件负责组合所有的 reducers 中导出的状态,并且会根据 发送(dispatch) 的 actions 去更新 Redux 树。在还没有实现任何的 action,现在只是单纯的结合所有的状态。

实现如下:

import { combineReducers } from 'redux';
import testReducer from './test.reducer';

// 结合所有的reducers,并导出一个状态树
const rootReducer = combineReducers({
  test: testReducer,
});

export default rootReducer;

store/index.ts

这里是用来创建 store,也就是管理的地方。如果要应用 中间件(middleware),就可以在这里实现。

实现如下:

import { createStore } from 'redux';
import rootReducer from './reducers';

const store = createStore(rootReducer);

export default store;

修改 App.tsx 应用 Redux 变化

主要还是在外面包一层由 react-redux 提供的 Provider,并且将 store 作为参数传给 Provier。

实现如下:

import { Provider } from 'react-redux';

import Routes from './Routes';
import store from './store';

function App() {
  return (
    <Provider store={store}>
      <Routes />
    </Provider>
  );
}

export default App;

修改 Home.tsx 查看 Redux 变化

这里会用到 react-redux 提供的一个钩子函数:useSelector,去获取状态,并且在页面上输出状态。写入的状态是 state: number = 0,并且在 combineReducers 中声明的是 test: testReducer,输出的结果应该就是 {test: 0}

实现如下:

import { useSelector } from 'react-redux';
import Layout from './Layout';

const Home = () => {
  const state = useSelector((state) => state);

  // state是一个对象,直接是无法输出结果的,所以需要用 SON.stringify() 转化成人类可读的数据
  return <Layout>Home {JSON.stringify(state)}</Layout>;
};

export default Home;

页面效果如下:

可以看到状态这部分,除了 props 之外,还多了一个 hooks,hooks 下面也多了一个 Selector 的对象,其中获取的就是导出的状态;同时,页面上也输出了 Selector 中获取的值。

至此可以证明基础的 Redux 已经配置好了,接下来就需要配置 Redux 的其他中间件/工具,为后面的开发提供便利。

connected-react-router 的配置

使用 connected-react-router 主要是为了能够更方便的使用 history 相关属性。react-router 有一个奇怪的特性,那就是只有被配置到 Routes.tsx 中的组件可以直接访问 history 的属性:

ShoprCartShoprCart > Layout

而子组件想要访问 props > history 只能通过应用 withRouther 包裹住组件。当层叠的组件多了之后,一层层套用 withRouter 就会变得非常的麻烦。

使用 connected-react-router 就可以直接在子组件访问 history 属性,应用起来也就更加的方便。

这里的配置都是跟着 npmjs 上的步骤实现的,官方已经停手把手的了。

修改 root reducer 文件

store/reducers/index.ts 中的内容,原本是一个对象对象,现在将其修改为一个可以接受 history 作为参数的函数。

实现如下:

import { connectRouter } from 'connected-react-router';
import { History } from 'history';
import { combineReducers } from 'redux';
import testReducer from './test.reducer';

const createRootReducer = (history: History) =>
  combineReducers({
    test: testReducer,
    // 不能修改,键必须为 router
    router: connectRouter(history),
  });

export default createRootReducer;

修改 store 文件

store/index.ts,本来函数中接受的是一个对象,现在需要接受一个参数,并传一个 history 作为参数。

实现如下:

import { routerMiddleware } from 'connected-react-router';
import { createHashHistory } from 'history';
import { createStore } from 'redux';

import createRootReducer from './reducers';

// 这是 history 自己提供的,直接调用即可
export const history = createHashHistory();

// 这里稍作修改即可,将 history 传入 createRootReducer
const store = createStore(createRootReducer(history));

export default store;

修改 App.tsx 应用 connected router

import { ConnectedRouter } from 'connected-react-router';
import { Provider } from 'react-redux';

import Routes from './Routes';
import store, { history } from './store';

function App() {
  return (
    <Provider store={store}>
      <ConnectedRouter history={history}>
        <Routes />
      </ConnectedRouter>
    </Provider>
  );
}

export default App;

新旧 Layout 的对比:

原本的 Layout新的 Layout

可以看到对比起原本的 props > children,引用了 connected-react-router 的对象中多出了一个包含 history 属性的对象。

页面的输出结果也变了:

能够看到 JSON.stringify(state) 中的值,新增了 history 属性。

Redux DevTools 的配置

现在的问题就在于,一旦想要开始 Debug,很难直接获得 Redux 中的值。直接在页面中输出也不是不行,只是测试起来还是相对比较麻烦,也很难追踪由状态变化引起 Redux 的变化。

而 Redux DevTools 也是需要配制的,这里修改的东西不是很多,只有 store/index.ts 下的内容。本质上来说,添加 Redux DevTools 就是增添一个 中间件(middleware)。

实现方式如下:

import { routerMiddleware } from 'connected-react-router';
import { createHashHistory } from 'history';
import { applyMiddleware, createStore } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';

import createRootReducer from './reducers';

export const history = createHashHistory();

const store = createStore(
  createRootReducer(history),
  composeWithDevTools(applyMiddleware(routerMiddleware(history)))
);

export default store;

之后,就能在浏览器提供的 Redux DevTools 中看到结果:

Redux DevTools 的优势在适配完 Action 后,会有更显著的效果。通过下面的方向键可以查看状态在不同时间的变化。

Redux Logger

Redux Logger 是我私人挺喜欢的一个中间件,主要可以把 Redux 的状态输出到命令行中,属于一个在开发状态中使用还挺友好的中间件。

个人觉得,Redux DevTools 跟 Redux Logger 可以二选一,而 Redux DevTools 相对而言应用范围更加的广泛。

安装方式如下:

C:front-proj> yarn add --dev @types/redux-logger

JavaScript 上次更新还是很久之前的事情了,TypeScript 的版本更新还是挺勤快的,使用方式也很简单,依旧只需要配置 store/index.ts

// 导入logger
import logger from 'redux-logger';

const store = createStore(
  // ...
  // 只需要修改这一行
  composeWithDevTools(applyMiddleware(routerMiddleware(history), logger))
);

渲染结果:

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-08-12 16:30:39  更:2021-08-12 16:31:18 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 9:41:24-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码