# 前端培训资料 - 公众号

## 前期准备

1.node 环境配置在 12 及以上，可以使用 nvm 进行 node 环境管理

2.下载微信开发者工具

3.同步项目仓库

## 启动

1.命令行运行 `npm install` 或 `yarn` 安装依赖

2.运行 `npm run start` 或 `yarn start` 启动项目

3.微信开发者工具打开启动项目后的链接，即可预览项目

## 上传

开发完成之后，同步代码到远程仓库，Jenkins 上面手动点击构建，即可发布新版本

太和公众号：

- QA 环境 [Jenkins](https://jenkins.ywtinfo.com/view/qa/job/qa-fe-nfthwe/)

- 生产环境 [Jenkins](https://jenkins.ywtinfo.com/view/product/job/product-fe-nfthwe-%E5%A4%AA%E5%92%8C%E5%85%AC%E4%BC%97%E5%8F%B7/)

南方医务通公众号：

- QA 环境 [Jenkins](https://jenkins.ywtinfo.com/view/qa/job/qa-fe-nfywtwe/)

- 生产环境 [Jenkins](https://jenkins.ywtinfo.com/view/product/job/product-fe-nfywtwe%E5%8D%97%E6%96%B9%E5%8C%BB%E5%8A%A1%E9%80%9A/)

### 项目结构

```javascript
.
├── .babelrc        // babel配置
├── .editorconfig   // 编辑器配置
├── .eslintignore
├── .eslintrc.js    // eslint配置
├── .gitignore
├── .gitmodules     // git submodule配置
├── .postcssrc.js
├── .prettierrc
├── README.md
├── build             // 构建配置目录
│   ├── build-qa.js   // qa环境变量配置
│   ├── build.js      // prod环境变量配置
│   ├── check-versions.js
│   ├── dev-client.js
│   ├── dev-server.js  // dev环境express启动
│   ├── mock.js        // mock数据
│   ├── utils.js       // 样式相关构建工具库
│   ├── webpack.base.conf.js  // webpack基本构建配置
│   ├── webpack.dev.conf.js   // webpack dev环境构建配置
│   ├── webpack.prod.conf.js  // webpack prod环境构建配置
│   └── webpack.qa.conf.js    // webpack qa环境构建配置
├── config
│   ├── dev.env.js            // mobile端项目配置
│   ├── index.js              // 项目配置汇总
│   ├── prod.env.js           // mobile端prod项目配置
│   ├── qa.env.js             // mobile端qa项目配置
│   └── test.env.js           // 测试项目配置
├── dist
├── index.ejs                 // html模板
├── node_modules
├── package-lock.json
├── package.json
├── src
│   ├── assets                // 项目资源
│   ├── components            // 公共封装组件
|   │   ├── Agreement         // 文本公告弹窗
|   │   ├── AlertTitle        // 提示标语
|   │   ├── App               // APP总容器
|   │   ├── ChatItem          // 咨询聊天组件
|   │   ├── DoctorInfoItem    // 医生信息
|   │   ├── FilterModal       // 信息过滤组件
|   │   ├── GHNotice          // 富文本公告弹窗
|   │   ├── HomePages         // 首页公告
|   │   ├── HospitalInfoItem  // 医院信息
|   │   ├── MySegmentedControl  // 列表展示
|   │   ├── MySwitch          // switch组件
│   |   └── SexSelect         // 性别选择组件
│   ├── const                 // 常量配置
│   ├── containers            // 不同业务页面
|   │   ├── App               // 项目入口/首页
|   │   ├── CheckResult       // 检验检查模块
|   │   ├── Consult           // 咨询模块
|   │   ├── DoctorTeam        // 医生团队模块
|   │   ├── DrugNews          // 快讯模块
|   │   ├── Fl_up             // 随访模块
|   │   ├── Global            // 医生信息
|   │   ├── Guide             // 引导模块
|   │   ├── Home
|   │   ├── Me                // 个人中心
|   │   ├── MedicalRecord     // 诊疗卡中心
|   │   ├── NoMatch           // 404页面
|   │   ├── OnlineOrder       // 线上订单
|   │   ├── OrderCenter       // 订单中心
|   │   ├── Pay               // 支付页面
|   │   ├── PrivateDoctor     // 专职医生介绍页面
|   │   ├── Report            // 远程会诊专家报告
|   │   ├── Tools             // 签名模块
|   │   ├── YuYueGuaHao       // 预约挂号-南方医科大学南方医院
│   |   └── YuYueGuaHao-TaiHe // 预约挂号-太和
│   ├── entry.js              // 项目入口
│   ├── hoc                   // 高阶函数
│   ├── reducers              // redux-reducer
│   ├── routers               // 路由配置
│   ├── saga                  // redux-saga
│   ├── server                // 接口相关api
│   ├── ui_module_components  // git submodule
│   └── utils                 // 工具类
└── static                    // 静态文件夹
    ├── .gitkeep
    ├── images
    └── pdfjs-dist
```

### 生命周期

react 所有生命周期都适用，项目中最常用的有以下两种：

```js
class App extends Component {
	state = {
		name: "",
	};
	// 可以使用所有的 React 生命周期方法，一般在此阶段请求接口获取数据
	componentDidMount() {
		this.setState({
			name: "xxx",
		});
	}

	// 页面状态或者变量更新时的生命周期
	componentDidUpdate() {}

	render() {
		return <div></div>;
	}
}
```

---

### 全局状态管理

公众号使用 redux 相关套件进行全局状态管理

以获取/修改用户信息为例：

#### 设置 reduce 和 state

在 `src/reduce/user.js` 中，创建 reduce，用于同步更改 state

```js
import { combineReducers } from "redux";

const initState = {
	name: "",
};

function userInfo(state = initState, action) {
	switch (action.type) {
		case "SAVE":
			return Object.assign({}, state, action.info.user);
		default:
			return state;
	}
}

export default combineReducers({
	userInfo,
});
```

#### 设置 action

在 `src/saga/user.js` 文件中，我们将创建一个 Saga 来监听所有的 INIT action，并触发一个 API 调用获取用户数据：

```js
import { call, put, takeEvery, takeLatest } from "redux-saga/effects";
import Api from "...";

// worker Saga : 将在 INIT action 被 dispatch 时调用
function* fetchUser(action) {
	const user = yield call(Api.fetchUser);
	yield put({ type: "SAVE", info: { user } });
}

/*
  在每个 `INIT` action 被 dispatch 时调用 fetchUser
  允许并发（译注：即同时处理多个相同的 action）
*/
function* mySaga() {
	yield takeEvery("INIT", fetchUser);
}

/*
  也可以使用 takeLatest

  不允许并发，dispatch 一个 `INIT` action 时，
  如果在这之前已经有一个 `INIT` action 在处理中，
  那么处理中的 action 会被取消，只会执行当前的
*/
function* mySaga() {
	yield takeLatest("INIT", fetchUser);
}

export default mySaga;
```

#### 连接 Redux Store

为了能跑起 Saga，我们需要使用 redux-saga 中间件将 Saga 与 Redux Store 建立连接。

在项目的入口文件 `src/entry.js` 中建立连接：

```js
import { createStore, applyMiddleware } from "redux";
import createSagaMiddleware from "redux-saga";

import mySaga from "./sagas/user.js";
import reducer from "./reduce/user.js";

// create the saga middleware
const sagaMiddleware = createSagaMiddleware();
// mount it on the Store
const store = createStore(reducer, applyMiddleware(sagaMiddleware));

// then run the saga
sagaMiddleware.run(mySaga);
```

#### 页面或者组件接入

```js
import React, { Component } from "react";
import { withRouter } from "react-router-dom";
import { connect } from "react-redux";

class App extends Component {
	state = {
		nickName: "",
	};

	componentDidMount() {
		this.props.onInitUserInfo(); // 调用 props 中的方法获取全局数据

		this.getNickName(); // 直接调用接口获取数据
	}

	async getNickName() {
		const result = await getNickNameApi();
		this.setState({
			nickName: result.nickName,
		});
	}

	render() {
		const { userInfo } = this.props; // 从 props 中获取数据
		return (
			<div onClick={() => this.props.onChangeUserInfo({ name: "xxx" })}>
				{userInfo.name}
			</div>
		);
	}
}

const mapStateToProps = (state) => {
	// 设置所要监听/获取的数据
	return {
		userInfo: state.userInfo,
	};
};

const mapDispatchToProps = (dispatch) => {
	// 设置函数，触发 dispatch，获取/修改用户数据
	return {
		onInitUserInfo() {
			// 初始化数据
			dispatch({ type: "INIT" });
		},
		onChangeUserInfo(user) {
			// 修改数据
			dispatch({
				type: "SAVE",
				info: user,
			});
		},
	};
};

// 通过 redux 提供的 connect 方法，给 APP 组件的 props 中连接所需要的属性和方法
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(App));
```
