# 前端培训资料 - 小程序

## 前期准备

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

2.下载微信开发者工具

3.同步项目仓库

## 启动

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

2.因为患者端小程序里面包含多个项目，所以一定要使用 `yarn replace:by` 命令，把项目配置切换到白云（其他项目同理，具体命令看 `package.json`）

3.命令行运行 `npm run dev:weapp-by` 或 `yarn dev:weapp-by` 运行项目（其他项目同理，具体命令看 `package.json`）

4.运行后，在项目根目录 `dist_by/weapp` 下会生成打包产物（其他项目同理，打包产物以 `dist_` 开头，加以特殊的项目名称后缀）

5.微信开发者工具导入打包产物 `dist_by/weapp`，即可预览项目

## 上传

1.1.使用 `yarn build-qa:weapp-by` 打包编译白云小程序 QA 环境（其他项目同理，具体命令看 `package.json`）

1.2.使用 `yarn build:weapp-by` 打包编译白云小程序生产环境（其他项目同理，具体命令看 `package.json`）

2.打包后，在项目根目录 `dist_by/weapp` 下会生成打包产物（其他项目同理，打包产物以 `dist_` 开头，加以特殊的项目名称后缀）

3.点击微信开发者工具右上方上传按钮，填写新版本好和基本信息，即可把打包产物上传到体验版（需微信后台管理员设置上传权限）

## react 项目与 taro

### 项目结构

```js
├── dist                        编译结果目录
|
├── config                      项目编译配置目录
|   ├── index.js                默认配置
|   ├── dev.js                  开发环境配置
|   └── prod.js                 生产环境配置
|
├── src                         源码目录
|   ├── pages                   页面文件目录
|   |   └── index               index 页面目录
|   |       ├── index.js        index 页面逻辑
|   |       ├── index.css       index 页面样式
|   |       └── index.config.js index 页面配置
|   |
|   ├── app.js                  项目入口文件
|   ├── app.css                 项目总通用样式
|   └── app.config.js           项目入口配置
|
├── project.config.json         微信小程序项目配置 project.config.json
|
├── babel.config.js             Babel 配置
├── tsconfig.json               TypeScript 配置
├── .eslintrc                   ESLint 配置
|
└── package.json
```

### 生命周期

入口组件生命周期，项目入口使用，针对全局的请求或设置可在此阶段运行

```js
class App extends Component {
	// 对应 onLaunch
	// 在小程序环境中对应 app 的 onLaunch
	// 在此生命周期中通过访问 options 参数或调用 getCurrentInstance().router，可以访问到程序初始化参数
	onLaunch() {}

	// 可以使用所有的 React 生命周期方法
	componentDidMount() {}

	// 对应 onShow，程序启动，或切前台时触发。
	componentDidShow() {}

	// 对应 onHide
	// 程序切后台时触发。
	componentDidHide() {}

	render() {
		// 在入口组件不会渲染任何内容，但我们可以在这里做类似于状态管理的事情
		return (
			<Provider store={store}>
				{/* this.props.children 是将要被渲染的页面 */}
				{this.props.children}
			</Provider>
		);
	}
}
```

页面组件生命周期，页面中使用

```js
class Index extends Component {
	// 可以使用所有的 React 生命周期方法
	componentDidMount() {} // 一般接口请求会放在此阶段进行

	// onLoad
	onLoad() {}

	// onReady
	onReady() {}

	// 对应 onShow
	componentDidShow() {}

	// 对应 onHide
	componentDidHide() {}

	render() {
		return <View className="index" />;
	}
}
```

---

### 全局状态管理

小程序使用 mobx 进行全局状态管理（如个人信息，授权信息等），以下以简化后的个人信息为例：

#### 设置初始值

在项目目录 `src/store/index.js` 中，设置初始值

```JS
import { observable } from "mobx";

const userInfo = observable({ // 此对象就是可以监听的对象
  nickname: "",
  initData(res) { // 初始化时调用
    this.nickname = res.nickname;
  },
  bindUser(res) { // 变更状态时调用
    this.nickname = res.nickname;
  }
});
export default userInfo;
```

#### 全局设置

必须在项目目录入口文件 `src/app.js` 中设置全局 store

```JS
import userInfo from "./store/index.js";
import { Provider } from "mobx-react";

const store = {
  userInfo,
};

class App extends Component {

  render() {
    return <Provider {...store}>{this.props.children}</Provider>;
  }
}
```

#### 局部监听

在需要监听的页面或者组件中，将 Provider 中设置的 store 提取到组件的 props 中，下面以白云小程序个人中心页面为例：

在 `src/pages/users/userPage` 中：

```js
import { observer, inject } from "mobx-react";

@inject("userInfo")
@observer
class ByUser extends Component {
	render() {
		const { nickname } = this.props.userInfo || {};
		return <View>{nickname}</View>;
	}
}
```

#### 更新状态

在 `src/pages/users/userPage` 中，使用已经经过初始化的 `bindUser` 函数进行状态变更，重新渲染页面

```js
import { observer, inject } from "mobx-react";

@inject("userInfo")
@observer
class ByUser extends Component {
	handleChangeName = () => {
		const userInfo = this.props.userInfo || {};
		userInfo.bindUser({ nickname: "xxx" });
	};

	render() {
		const { nickname } = this.props.userInfo || {};
		return <View onClick={() => handleChangeName()}>{nickname}</View>;
	}
}
```

### 标准页面结构

```js
import { observer, inject } from "mobx-react";

@inject("userInfo") // 注入需要监听的全局状态
@observer
class ByUser extends Component {

  //初始化页面所需的变量
  state = {
    name: ''
  }

  // 在此生命周期中获取接口数据，设置初始值
  componentDidMount(){
    this.initData()
  }

  initData = async () => {
    const result = await getName()
    this.setState({
      name: result.name
    })
  }

  // 修改state
  handleChangeNickName = () => {
    this.setState({
      name: 'yyy'
    })
  }

  // 修改全局状态
  handleChangeName = () => {
    const userInfo = this.props.userInfo || {};
    userInfo.bindUser({ nickname: "xxx" });
  };

  render() {
    const { nickname } = this.props.userInfo || {};
    const { name } = this.state
    return (
      <View>
        <View onClick={() => handleChangeNickName()}>{nickname}</View>
        <View onClick={() => handleChangeName()}>{name}</View>
      </View>;
    )
  }
}
```

## 项目运行流程

1. 入口文件 `src/app.js`，统一入口，注入全局状态 store

2. `src/index/index`，微信小程序的唯一入口，在此获取 token/鉴权/路由判断/二维码判断，跳转到指定的路由

- 2.1 通过 `handleToken` 获取 token 和个人信息

- 2.2 通过判断路由参数 `q` 和 `pathname`，加上解析参数 `query`，跳转到具体业务页面，适用于固定链接(不需要关注或通过公众号)，如 `https://m-qa.ywtinfo.com/thnethospwe/prescriptionOrder?bizNo=P201811011958115320228626&orderId=20991`

- 2.3 通过判断路由参数 `entry`，跳转到具体业务页面，使用于动态链接(从公众号通过链接跳转到小程序的某个页面)，如 `pages/index/index?entry=by/medical_record_mailing/newborn?mailingOrderId=mailingOrderId`

- 2.4 跳转到默认首页 `/pages/index/home`

3. 具体页面渲染

- 3.1 生命周期，接口获取数据

- 3.2 注入全局状态，获取全局状态数据
