企业微信开发(一期)

历时一个多月的企业微信,在这一个多月的连续冲刺之下终于暂时告一段落,在这版本迭代的空档期,我还是静下心来好好的整理一下关于这个由腾讯提供平台,第三方服务商接入的产品。企业微信的开发过程中,我们遇到了很多问题,在处理这些问题的同时,我们也在飞速的成长着,在这种平台开发之上积累了更多的开发经验。同时也在这类开发过程中深刻了解到了文档的重要性,无论是接入别人的平台开发阅读别人的开发文档,还是我们自己的内部文档,文档的可读性、完整性和存在性是非常的重要,它可以有效的帮助我们集成API,了解开发原理、框架结构,帮助我们在开发过程中减少不必要的时间开销,当然要是一些含糊不清,可读性差的文档也会导致我们花费更多的时间原地踏步,所以只要是个开发者,你的文档就必须伴随着你的开发,同时你也得保证它的可读性与完整性。

框架环境

这里简单介绍一下关于企业微信的开发环境,我们用react脚手架和webpack构建工具,再配合一系列的其他插件搭建了这个项目的开发环境,环境的搭建过程这里就不详细介绍了。主要讲一下在搭建过程中遇到的一些问题:

1
css-module

webpack的css-loader插件可以帮助我们进行css的差异化处理

1
2
3
4
options: {
modules: true,
localIdentName: "[path][name]--[local]--[hash:base64:5]"
}

将css-loader的options配置之后,我们在webpack打包的代码之中就可以看到每一个模块的css都会被加上一段哈希值作为css的差异化。这里有两个踩点:
1、只有将css文件引入到js文件中,并且以类似对象的点语法操作引用之后css-module才会生效;
2、css-module的哈希化产生的问题,我们的项目引入了两个UI库:weui和antd,恰好我们的项目也采用的是less的css预处理器,在entry文件中引入这两个库的css文件之后,导致这两个库的css文件类名在打包之后被哈希化了,最后呈现的就是没有样式的页面,在一番斟酌之后,我们决定对所有css文件采用非css哈希化,对所有的less文件进行css哈希化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
{
loader: "css-loader"
},
{
loader: "postcss-loader"
}
]
})
},
{
test: /\.less$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
{
loader: "css-loader",
options: {
modules: true,
localIdentName: "[path][name]--[local]--[hash:base64:5]"
}
},
{
loader: "postcss-loader"
},
{
loader: "less-loader"
}
]
})
}

1
"'plugins': ['import', { 'libraryName': 'antd-mobile', 'style': 'css'}]"

由于antd是支持less和css文件的引入,所以最后我们就引入css文件避免与项目内的自定义css文件产生冲突。

1
2
url-loader
file-loader

这两个插件是用于webpack打包时打包图片和文件的,我遇到这个问题是在加入favicon.ico文件的时候,这个文件加入进来直接在entry的html中调用link,我本以为在调用link的时候输入的路径会引用到这个文件,结果证明不行,还需要我们在js文件中单独引入一次,这样这个文件才会被打包。

屏幕适配

webapp的项目永远是不可能脱离屏幕适配这一步的,我们的项目采用过两套适配方案:
1、flexiable
flexiable的主要作用是根据设备像素比也就是dpr将视觉稿中的px转换成rem,这里简单介绍一下像素的概念,这里采用直接加载阿里CDN的方式

1
<script src="http://g.tbcdn.cn/mtb/lib-flexible/0.3.4/??flexible_css.js,flexible.js"></script>

物理像素:物理像素又被称为设备像素,他是显示设备中一个最微小的物理部件。每个像素可以根据操作系统设置自己的颜色和亮度。

设备独立像素:设备独立像素也称为密度无关像素,可以认为是计算机坐标系统中的一个点,这个点代表一个可以由程序使用的虚拟像素(比如说CSS像素),然后由相关系统转换为物理像素。

CSS像素(设备逻辑像素):CSS像素是一个抽像的单位,主要使用在浏览器上,用来精确度量Web页面上的内容。一般情况之下,CSS像素称为与设备无关的像素(device-independent pixel),简称DIP。

屏幕密度:屏幕密度是指一个设备表面上存在的像素数量,它通常以每英寸有多少像素来计算(PPI)。

设备像素比:设备像素比简称为dpr,其定义了物理像素和设备独立像素的对应关系。

1
设备像素比 = 物理像素 / 设备独立像素

rem:rem就是相对于根元素的font-size来做计算
所以针对我们的设计稿750 x 1334 的比例计算出 1rem = 75px

采用了flexiable的适配方案在屏幕适配上面能够适应大部分手机屏幕,我在最初采用的时候也没有问题,但是问题出在每一次html页面的网页在屏幕进行缩放之后都会重新计算dpr。导致每一次拖动屏幕或者放大缩小屏幕都会出现因重新计算dpr导致的屏幕闪烁问题,当然我们可以再微信开发的屏幕上禁止掉它的缩放,但是这始终是一个隐患问题,所以我们果断弃用了这套方案,而采用更新的viewport的方案。

2、viewport
viewport(视窗):即设备屏幕上用来显示网页内容的区域
移动设备上计算一个css像素对应的物理像素数可以由设备像素比(devicePixelRatio)来计算,Flexible方案是通过JavaScript来模拟vw的特性,vw已经得到了众多浏览器的支持,也就是说,可以直接考虑将vw单位运用于我们的适配布局中,vw是基于Viewport视窗的长度单位:

1vw等于window.innerWidth的1%;
1vh等于window.innerHeihgt的1%;
vmin的值是当前vw和vh中较小的值;
vmax的值是当前vw和vh中较大的值。

所以,根据上面的关系得到在750x1334的视觉设计稿中1vw = 7.5px,这里的计算计算我们引入了一个插件postcss-px-to-viewport,通过这个插件我们在项目中直接写入px值,在打包编译后该插件会自动计算出vw或者vh值,这里有个小细节根据在插件中设置好的视觉稿宽高,可以直接写入UI给出的px值,其他情况下一律按照1px乘以dpr的值输入。

vw适配的适用场景:
1、容器适配;
2、文本的适配;
3、大于1px的边框、圆角、阴影;
4、内距和外距

在vw适配中还有一个问题,那就是容器的长宽比缩放,这里采用postcss-aspect-ratio-mini

处理1px问题时我们用postcss-write-svg,具体用法可以直接看它的官方文档。

整体来说我们采用第二套方案也是因为适配稳定,同时兼容性好,这里我只是把这些需要用到的插件列出来作为引导,具体怎么使用,大家可以直接查看官方文档,这样对你自己而言也才是真正的学习。

调试模式

移动端开发涉及到手机设备上的开发调试,这里推荐一篇关于移动设备调试的文章作为参考。
企业微信的调试是在企业微信内嵌webView中的调试,简单来说就是把网页房在企业微信的原生页面里面作为网页展示出来,我们不能直接进行调试修改。所以,我们采用了一些方案:

1、本地调试服务器
要展示一个完整的网站应用,我们需要打包我们的代码进行压缩编译生成html、css、js和图片资源文件,这些文件生成后要在网页上展示出来,电脑端通过localhost可以直接本地加载,但是在企业微信开发这种移动端内嵌网页式的开发中要展示出来就需要我们把这些静态资源部署到公网上去,然后通过内嵌webView加载公网链接进行显示,大致的过程确实是这样,看起来也不是很复杂,但是要实现这个过程,需要做的事就多了。首先我们得有一个托管资源的CDN服务器,目前我们采用的是beego的CDN静态资源的服务器,beego的服务器搭建由后端完成,并部署。因为它有一套成型的CDN托管方案,所以我们能更方便快捷的进行CDN的搭建,对我开发初期可以带来很乐观的效率。但是不足之处便是在于,每一个资源文件的映射都需要手动去实现。所以后期我们还是会更换成node.js的CDN托管服务器,同时由前端自行管理部署等等。好了,有了CDN托管服务器接下来就是部署静态资源,正常来说,直接将其发布到公网上即可,但是这样每一次发布到公网上部署会对调试产生很大一部分时间开销。所以,我们加入的ngrok的反向代理,将我们本地的环境穿透到公网环境上去,然后再通过企业微信内嵌页面加载出来,这里有个问题,反向代理的公网穿透加载比较慢,所以打包的代码要想进行快速调试就必须完全压缩,这样每一次加载静态资源就会快上许多(大约10s左右)。我们在本地构建了一个静态资源服务器在每一次启动加载本地指定目录的资源文件进行方向穿透,这样我们在本地更改代码后就不需要发布到公网上,而可以直接查看。其中最耗时的还是编译打包,因为每一次调试都需要进行重新打包编译,这其中的过程耗时约30s左右。虽然比不上直接通过本地加载,但是也能很大提升我们的开发效率了。

2、vConsole
vConsole是腾讯团队提供的一种移动端webapp开发的log工具,它可以将页面中的log信息在设备上通过弹窗的形式展示出来。使用方法也很方便,直接通过npm安装vconsole,然后在代码中初始化即可。这个工具对我们在查看某些数据或者报错信息的时候很方便。

3、weinre
weinre通过引入链接文件的方式进行加载,可以在插入脚本的页面进行简单的CSS样式修改和newwork查看,但是这种方式不足之处在每一个需要调试的页面都得加入script链接脚本。

4、whistle
为了更方便的使用vConsole和weinre,我们引入了whistle,他可以不需要你安装vConsole,也不需要引入weinre即脚本链接,只需要我们在安装之后启动它,然后通过它的调试平台安装它的语法注入我们的页面即可加载vConsole和weinre,whistle也拥有绑定转发、req、res、debugging和插件开发等其他功能。

JSSDK

企业微信开发这种平台类的内嵌网页开发,需要接入jssdk,页面加载初始化时,我们需要先获取token,获取token后需要签名验证,验证成功后才能调用jssdk。企业微信要求每一个页面在url变化之后都进行一次jssdk的调用,这里有两种实现方案:

签名验证:

1
2
3
4
5
6
7
8
9
10
11
wx.config({
beta: true,
debug: false,
appId: info.appId,
timestamp: info.timestamp,
nonceStr: info.nonceStr,
signature: info.signature,
jsApiList: ['getLocation', 'chooseImage', 'previewImage', 'uploadImage', 'onHistoryBack', 'hideAllNonBaseMenuItem', 'showAllNonBaseMenuItem',
'onMenuShareAppMessage', 'onMenuShareWechat', 'onMenuShareTimeline', 'shareAppMessage', 'shareWechatMessage', 'openEnterpriseChat', 'startRecord',
'stopRecord', 'onVoiceRecordEnd', 'uploadVoice', 'setClipboardData', 'getClipboardData', 'closeWindow']
});

1、封装签名体,在每一个页面进行调用;
2、在react-router的onEnter方法里面进行调用;

这里我们采用的第一种方式,优点是签名调用灵活,在每一个页面组件化的情况下也可以进行签名,缺点是方法代码臃肿,第二种方案代码简洁,但是调用不灵活,一些子组件url不变的情况下也需要签名的情况下不能直接调用。当然,我们完全可以两种方案都采用,页面转变可以通过路由的生命周期调用,同时在个别子组件单独调用。这也是我们需要完善的优化点。