# 关于近期小程序代码优化的总结

最近我开发小程序也有三四个月，针对开发过程中遇到的错误调试、代码优化或项目自动化构建进行总结。

### 代码检查

在梳理代码逻辑时发现很多代码不规范的地方，包括简单的js问题以及代码格式化的问题，造成了代码可读性下降，所以项目优化的第一步就是对代码风格做统一化处理。主要用到`eslint`和`prettier`。

#### 关于rpx

`rpx`是小程序官方提供的一种像素单位，可按照屏幕宽度进行自适应，小程序开发过程中建议使用rpx。但对于一些第三方框架，如`vant`，用的是`px`。所以需将对应的`px`换算为`rpx`（按照`750px`的设计稿是`1px:2rpx`）

![](/files/-MLqiNY98IlDWb1g2fMk)

因为用到[miniprogram-ci](https://developers.weixin.qq.com/miniprogram/dev/devtools/ci.html)作为构建工具（下文有提及），所以在自动化构建的过程中需要对`vant`组件库进行转换，转换具体是依赖`gulp`、`gulp-postcss`、`postcss-pxtransform` 对项目中的`px`进行转换。在gulpfile.js中增加以下代码：

```
function formatCSS(cb) {
  const processors = [
    pxtorem({
      platform: 'weapp',
      designWidth: 750
    })
  ]
  gulp
    .src(['./miniprogram_npm/**/*.wxss'])
    .pipe(postcss(processors))
    .pipe(gulp.dest('./miniprogram_npm'))
  gulp
    .src(['./pages/**/*.wxss'])
    .pipe(postcss(processors))
    .pipe(gulp.dest('./pages'))
  gulp
    .src(['./pages/**/*.less'])
    .pipe(postcss(processors))
    .pipe(gulp.dest('./pages'))
  cb()
}
```

注意这里的pxtorem配置有误，因而需要单独引用一份出来修改`deviceRatio`

```
const deviceRatio = {
  640: 2.34,
  750: 2,
  828: 1.81
}
```

#### 如何在提交前触发格式检查

为避免开发时，开发人员的风格各自不同，不符合代码规范，因而需要对代码格式进行eslint检查并自动格式化，这里主要用到`husky、lint-staged、eslint以及prettier`插件

1. 在package.json中增加配置

```
{
  "scripts": {
    "precommit": "lint-staged"
  },
  "lint-staged": {
    "pages/**/*.{js,wxs}": [
      "eslint --fix --ext .js",
      "prettier ./ --write",
      "git add"
    ]
  },
  "devDependencies": {
    "eslint": "^5.0.0",
    "eslint-config-ali": "^2.0.1",
    "eslint-plugin-import": "^2.6.0",
    "eslint-plugin-react": "^7.1.0",
    "husky": "^0.14.2",
    "babel-eslint": "^8.1.1",
    "lint-staged": "^4.0.0",
    "prettier":"^1.16.4",
    "eslint-plugin-prettier":"^3.0.1",
    "eslint-config-prettier":"^4.0.0"
  },
}

```

2\. 在`.eslintrc.js`增加eslint扫描配置

```
module.exports = {
  "extends": ["eslint-config-ali","prettier", "plugin:prettier/recommended"],
  "parser": "babel-eslint",
  "rules": {
    "prettier/prettier": "error",
    "strict": "off",
    "no-console": "off",
    "import/no-dynamic-require": "off",
    "global-require": "off",
    "require-yield": "off",
    "eqeqeq": "off",
    "new-cap": "off",
    "no-nested-ternary": "off",
    "array-callback-return": "off"
  },
  "plugins": ["prettier"],
  "globals": {
    "React": "readable",
    "wx": true,
    "App": true,
    "Page": true,
    "Component": true,
    "getApp": true,
    "toast": true
  }
};
```

3\. 增加`.prettierrc.js`文件，用于在扫描通过后格式化代码

```
module.exports = {
  printWidth: 100,
  semi: false,
  singleQuote: true,
  trailingComma: 'none',
  bracketSpacing: true,
  jsxBracketSameLine: false,
  arrowParens: 'avoid',
  requirePragma: false,
  proseWrap: 'preserve'
};
```

这里如果要忽略掉某一目录，可增加`.prettierignore`文件

```
.vscode
node_modules
miniprogram_npm
helper
pages/api/sdk/wxsdk3.2.0.js
pages/utils/
typings/
```

这样在commit的时候就会自动触发`lint-staged`，如果代码不合规范就会不让提交

![](/files/-MLvFi_TWDPABZCgaDce)

### 自动化构建

之前构建都是在微信开发工具中手动发布，每次都需要手动输入版本号，很不方便，因而需要。而小程序正好提供[miniprogram-ci](https://developers.weixin.qq.com/miniprogram/dev/devtools/ci.html)作为官方的构建工具，我们可使用该工具提供的命令行模式，具体如下：

1. 在package.json中加上

```
 "devDependencies": {
    "miniprogram-ci": "^1.0.79",
    "dayjs": "^1.8.36",
    "execa": "^4.0.3"
 }
```

2\. 因为用到vant组件库，所以需要自动化构建npm

```
async function buildNPM(cb) {
  await ci.packNpm(project, {
    reporter: infos => {
      console.log(infos)
    }
  })
  cb()
}
```

3\. 在CI.js中增加以下代码，用于自动化构建并上传

```
async function uploadCI(cb) {
  await ci.upload({
    project,
    version: getVersion(),
    desc: getGitCommit(),
    setting: {
      es6: true,
      es7: true,
      minify: true
    },
    onProgressUpdate: console.log
  })
  cb()
}
```

4\. 在微信后台生成一个代码上传密钥，重命名为private.key，保存到根目录下

![](/files/-MLqg3o7XyVzPpCFJJ4R)

5\. 在Jenkins中配置自动化构建项目，Jenkins搭建教程很多，这里不再阐述。在这过程中会先构建npm，然后执行rpx转换，最后构建程序并上传，构建脚本如下：

```
node -v
rm -rf node_modules
npm i
npm i gulp -g
gulp default --branch $giteeSourceBranch
```

6\. 等待构建完成后就会自动上传到小程序那边

![](/files/-MLqhWKA9T4hVqWQsqIR)

PS：详细的构建脚本如下（gulpfile.js）:

```
const ci = require('miniprogram-ci')
const dayjs = require('dayjs')
const execa = require('execa')
const gulp = require('gulp')
const postcss = require('gulp-postcss')
const pxtorem = require('./helper/pxtransform')

const app = require('./package.json')
const config = require('./project.config.json')

const project = new ci.Project({
  appid: config.appid, // 小程序的appId
  type: 'miniProgram',
  projectPath: './', // 打包路径
  privateKeyPath: './private.key', // 密钥位置
  ignores: ['node_modules/**/*', 'CI.js', 'private.key'] // 打包时忽略的文件
})

function getVersion() {
  const branch = process.argv[2]
  const date = dayjs().format('YYYYMMDD')
  return `${app.version}.${date}.${branch == 'master' ? 'release' : 'beta'}`
}

function getGitCommit() {
  const commit = execa.commandSync('git log -1')
  return `【${process.argv[2]}】${commit.stdout}`
}

async function buildNPM(cb) {
  await ci.packNpm(project, {
    reporter: infos => {
      console.log(infos)
    }
  })
  cb()
}

function formatCSS(cb) {
  const processors = [
    pxtorem({
      platform: 'weapp',
      designWidth: 750
    })
  ]
  gulp
    .src(['./miniprogram_npm/**/*.wxss'])
    .pipe(postcss(processors))
    .pipe(gulp.dest('./miniprogram_npm'))
  gulp
    .src(['./pages/**/*.wxss'])
    .pipe(postcss(processors))
    .pipe(gulp.dest('./pages'))
  gulp
    .src(['./pages/**/*.less'])
    .pipe(postcss(processors))
    .pipe(gulp.dest('./pages'))
  cb()
}

async function uploadCI(cb) {
  await ci.upload({
    project,
    version: getVersion(),
    desc: getGitCommit(),
    setting: {
      es6: true,
      es7: true,
      minify: true
    },
    onProgressUpdate: console.log
  })
  cb()
}

exports.default = gulp.series(buildNPM, formatCSS, uploadCI)

```

### 真机远程调试

小程序不像webview一样可以通过chrome或safari远程调试，因而需要通过抓包去捕捉真机中的请求错误，一般抓包工具都可以，以下用[charles](https://www.charlesproxy.com/)举例：

1. 安装charles，配置代理：

![](/files/-MLqnw2rt80zS58bU5LX)

2\. 保证手机和pc在同一网络下，在无线局域网-配置代理中输入pc端的无线地址、charles端口

![](/files/-MLqoZQ1sLMdMMsfWN0X)

3\. 这样charles中就会显示对应的请求

![](/files/-MLqolro2bH8rzgz0y6Z)

4\. 对于SSL请求，是看不到内容的，所以需要安装代理证书。

![](/files/-MLqpTMYWMHYtm0Gwsum)

![](/files/-MLqpMboP1XqFNqfHNBJ)

在手机端打开 [chls.pro/ssl](http://chls.pro/ssl) 下载安装证书

![](/files/-MLqpfKuxpGCO4sEUrrS)

需要在 设置→通用→关于本机→证书信任设置里面启用完全信任Charles证书，打开信任按钮

![](/files/-MLqqm3bRE-3deoWL-VM)

在charles中打开Proxy -> SSL Proxying Settings，勾选Enable SSL Proxying并配置域名

![](/files/-MLqqLzyoo5q8t7vk-M5)

### 错误搜集

小程序自身就有一个错误搜集的机制，当线上版本或测试版本出错时，就会传到后台。但这种错误一是对于接口调用的错误没有搜集，二是错误发生时没有提醒。因而需要接入一个第三方的错误监控系统。

![微信的错误搜集页面](/files/-MLq6WQJVbGkVNmUeVeT)

接入其实也很简单，[sentry](https://sentry.io/) 是一个开源的实时错误监控的项目，它支持很多端的配置，包括 web 前端、服务器端、移动端及其游戏端。支持各种语言，例如 python、oc、java、node、javascript 等。对于主流框架vue、angular、react等也支持。安装方式参照docker安装方式即可，网上的[教程](https://blog.csdn.net/kwame211/article/details/104642014)很多，这里就不再阐述。

这里主要说下怎么接入到小程序中，其实已经有有赞开发的插件 [raven-weapp](https://github.com/youzan/raven-weapp)

#### **引入文件**

由于目前小程序不支持从node\_modules中引入文件，因此以npm方式安装的话只能手动将raven-weapp/dist目录下需要的文件拷贝到其他文件中，在app.js中引入，例如：

```
var Raven = require('./utils/raven-weapp/build/raven')
```

#### **初始化Raven**

在sentry中创建一个项目，选择JavaScript类型，创建完会生成对应的服务器链接（<http://36887e0d8a0b48ea9ae3738204a358ef@sentry.nerochen.wang:9000/4>），在app.js中onLaunch时初始化Raven，

```
		Raven.config('http://36887e0d8a0b48ea9ae3738204a358ef@sentry.nerochen.wang:9000/4', {
			release: '1.2.0', // 版本号
			environment: 'production', //指定某个环境下才会上报
			allowDuplicates: true, // 允许相同错误重复上报
			sampleRate: 0.5 // 采样率
		}).install()
```

这样当前端报错时就会上传对应的错误

#### 接口调用的错误

Raven也支持自定义上传错误信息，我们可以在封装request的时候，将接口调用错误信息用Raven上传

```
Raven.captureMessage(url:${url}\nresult:${JSON.stringify(error)}, { level: 'error' });
```

![错误截图](/files/-MLqBZ023Ll1x_kAMcnl)

#### 关于错误通知

除了sentry自带的邮件通知（[配置](https://juejin.im/post/6844904051700842510)），我们使用企业微信的也可以通过配置webhook，触发企业微信机器人通知到我们（<https://dinglingdingling.com/>），具体步骤如下：

1. 在企业微信建一个用于通知的群，然后管理员身份配置群机器人

![](/files/-MLqRU81Pb33BMePG4y8)

2\. 在上述网站中配置一个转发

![](/files/-MLqS-OdgR35fn6KPrRD)

3\. 在sentry中增加webhook插件

![](/files/-MLqSPNSNUWlLPxdNfzu)

4\. 配置dinglingdingling生成的链接

![](/files/-MLqXcHqUcvMnrw-C-nW)

5\. 点击test触发通知

![](/files/-MLqXT2HB3sNtW5K2W1Z)

以上接入同样可以应用在项目构建完成、错误的提醒，定时构建的通知等。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://benjaken.gitbook.io/personal-space/master.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
