Archives for : 前端

小程序 web-view 内嵌的 h5 调用微信头像和昵称

连踩了好几个大坑,太深,记录一下。

小程序端 (wepy)

const userInfo = this.$parent.globalData.userInfo
// 参数值需要用 encodeURIComponent 进行编码
const nickName = encodeURIComponent(userInfo.nickName)
const avatarUrl = encodeURIComponent(userInfo.avatarUrl)
// 不能使用 # 字符拼接参数,在真机微信上会报错,推荐使用 ?...&...
this.src = `result?nickName=${nickName}&avatarUrl=${avatarUrl}`

h5 端 (vue)

const paramStr = window.location.href.split('?nickName=')[1]
if (paramStr) {
  const param = paramStr.split('&avatarUrl=')
  // 使用 decodeURIComponent 解码
  this.nickName = decodeURIComponent(param[0])
  this.avatarUrl = decodeURIComponent(param[1])
}

最后,使用 <img/> 在真机预览时显示不了图像,就算调用同域名下的远程图像也显示不了,正确的姿势是使用 background .

PS:用 vue + wepy 开发小程序很完美。

next.js 的一些坑

971bf7f8-9ac0-11e6-975c-188defd82df1

这阵子在折腾 next.js,用来做 node 服务端渲染真心不错,然而一路捣腾下来,坑也是不少,有四个比较大的坑。

  1. isomorphic-fetch(解决)
  2. node 端代码打包(未解决)
  3. js 放到 cdn 或者 oss(解决)
  4. static 目录下的图片 hash(临时方案)

这四个坑,1 和 3 是必须解决的,2 和 4 可选。

1

fetch 本来不是什么问题,但是涉及到请求签名,就来了点麻烦,需要区分是服务端还是客户端,客户端用 cookie 存储 token,服务端用变量。

2

node 代码其实不需要打包,因为是运行在服务端,不过为了节省 docker 镜像的大小,尝试了打包,主要是想把 node_modules 也打包进去。先是用 webpack,能打包成功,但是运行失败。后面又用 rollup,同样是打包成功,运行失败。可能是因为 nextjs 本身就包含了 webpack,而用打包工具来打包自己,估计 webpack 和 rollup 都没有想过会有这样的场景出现。于是放弃。

3

这个比较坑,所以重点说一下。

nextjs 提供了 assetPrefix 参数,但是这个参数基本没什么用。需要解决两个问题,一是打包目录要把 hash 带进去,把原来的 bundles/pages/xxx.js 目录结构改成 [hash]/xxx.js,这一步可以在打包脚本里处理,通过 BUILD_ID 和 build-stats.json 拿到 hash,直接上代码:

rm -r -f build
mkdir build

# move pages
buildId=$(cat _next/BUILD_ID)
mkdir build/${buildId}
mv _next/bundles/pages/* build/${buildId}
rm -r _next/bundles

# move app.js
appHash=$(cat _next/build-stats.json)
appHash=${appHash#*hash\":\"}
appHash=${appHash%\"*}
mv _next/app.js build/app.${appHash}.js

其中,_next 是 nextjs 的 distDir,build 是需要上传到 cdn 的目录。

接下来在 server.js 里重写 router,用了 express,参考 https://github.com/zeit/next.js/issues/257

const cdnPrefix = 'https://mycdn.com'

server.use('/_next/**/app.js', (req, res) => {
  const appHash = req.originalUrl.replace('/_next/', '').replace('/app.js', '')
  const newUrl = `${cdnPrefix}/build/app.${appHash}.js`
  res.redirect(newUrl)
})

server.use('/_next/**/page/**', (req, res) => {
  const buildHash = req.originalUrl.replace('/_next/', '').replace(/\/page\/.*/, '')
  const pageName = req.originalUrl.replace(/.*page\//, '') || 'index'
  const newUrl = `${cdnPrefix}/build/${buildHash}/${pageName}.js`
  res.redirect(newUrl)
})

4

static 目录下的图片 hash,这个问题也比较次要,暂时可以通过 githooks 来处理图片修改的问题,对于图片只允许增量提交。

.git/hooks/pre-commit

#!/bin/sh
STAGED_IMAGES=$(git diff --cached --name-only --diff-filter=M | grep -E ".(jpg|jpeg|png|gif)$")

if [[ $STAGED_IMAGES ]]; then
  echo $STAGED_IMAGES
  exit 1
fi

还有一些小坑,比如 process.env.NODE_ENV 的问题(如果有三种以上的环境),比如 scss 里面因不同环境插入不同 cdn 地址图片的问题,等等,就不列了。

生产项目:https://www.isspu.com

webpack 的 DllPlugin 很厉害

webpack.dllplugin.powerful

我们的打包机比较烂,所以想尽办法优化打包速度,这次使用了 DllPluginhappypack 进行优化(主要的功臣是 DllPlugin),速度明显提升,效果显著。

生产环境

  • 优化前 ≈ 125 秒
  • 优化后 ≈ 55 秒
  • 提升 ≈ 56%

大约是 DllPlugin 减了 50 秒,happypack 减了 20 秒。

开发环境

  • 优化前 ≈ 40 秒
  • 优化后 ≈ 15 秒
  • 提升 ≈ 62.5%
  • 热构建从 4 秒缩短到 2 秒

小程序审核越来越快了

IMG_4476

(图:等待审核只用了 41 分钟)

回想第一次审核通过,等了三天,春节过后的审核速度一次比一次快,今天又刷新纪录,只用了 41 分钟,不知道是好事还是坏事。往往看起来是个好事的事,不见得就是啥好事。

如今写本前端书真不容易

WechatIMG30

(图:《Node.js 硬实战》)

新书到手,啪啪啪翻了下,看到一段,说当时写这本书时 grunt 正流行,然后提到了 gulp。心想,如今写本技术书真心不容易,等你写出来的时候,已经过时了,尤其是前端书。

sublime 文件比对

IMG_1163

(图:Diffy)

之前一直用 sublimerge 这个比对插件,但是最近发现这货居然收费了,从以前的弹出窗口变成了 90 天试用,卖得还不便宜,35 刀。

于是下午找了两款插件配合起来用,效果也还不错。

第一个插件叫 diffy,这货超级简单,甚至可以用简陋来形容。

使用方法:

  1. 开两个窗口
  2. 在任意一个窗口点右键选择 Diffy 的 Compare

但是这样不能同步滚动两个窗口,所以要再装一个插件。

第二个插件叫 syncViewScroll,支持多个窗口同步滚动,但是刚开始用起来会有点不适应,有点小窍门。

比如开 A 和 B 两个窗口,使用方法:

  1. 先在 A 窗口,下拉菜单 -> View -> 勾选 Sync Scroll
  2. 然后在 B 窗口,执行相同操作
  3. 滚动 B 窗口,会同步滚动 A 窗口,但是滚动 A 窗口不会带动 B 窗口
  4. 如果有多个窗口,那就要在最后一个窗口滚动

这两个插件都可以定义快捷键来辅助使用,附上我的快捷键:

// syncViewScroll
{
  "keys": ["super+shift+ctrl+s"],
  "command": "toggle_sync_scroll"
},

// diffy
{
  "keys": ["super+shift+option+d"],
  "command": "diffy"
},

// diffy clear
{
  "keys": ["super+shift+option+c"],
  "command": "diffy", "args": {"action": "clear"}
},

微信小程序本地开发(https 解决方案)

IMG_0961

(图:吃完午饭遛遛)

微信小程序强制使用 https 请求,本地开发调试可以用以下两种方案。

  1. 如果自己没有域名,可以用 natapp,免费版不支持 https,购买最低档的 vip 是¥5/月。
  2. 如果自己有域名且已备案,可以注册一个腾讯云账户,然后免费申请 SSL 证书

附:wdcp 开启 https( nginx+apache 双引擎,或者 nginx 引擎)

  1. 后台防火墙 iptables 增加 443 端口
  2. 后台文件管理 /www/wdlinux/nginx/conf/vhost
  3. 编辑你的站点配置文件,加入以下代码(记得修改证书路径)
    listen 443 ssl;
    ssl_certificate /www/ssl/zhugao.net_bundle.crt;
    ssl_certificate_key /www/ssl/zhugao.net.key;
    ssl_session_timeout 5m;
  4. 后台重启服务 httpd 和 nginxd

Mac 上使用 Genymotion 模拟器调试 Android

IMG_0179

(图:怀念童年的成年人们)

Mac 上使用 Genymotion 模拟器调试 Android,安装过程有点麻烦,不过对于调试频率不高,前期懒得用真机调试的同学可以试试。

安装

  1. 下载安装 VirtualBox(速度很慢,我是在 新浪科技 下的)
  2. 进入 Genymotion 官网,注册一个帐号,然后下载,速度比较慢(个人版免费,企业版收费)
  3. 安装完成,打开 Genymotion,点击 Add 按钮(用之前注册的帐号登录),选择一个设备,点 Next 下载,通常都会下载失败
  4. 如果下载失败(其实不用等到下载失败,有一点点进度时就可以取消了),然后打开终端输入命令
    open ~/.Genymobile
  5. 找到 genymotion.log 并打开,搜索字符 .ova
  6. 找到类似这样的地址: http://dl.genymotion.com/dists/7.0.0/ova/genymotion_vbox86p_7.0_160912_085006.ova
  7. 这个就是虚拟设备文件,用迅雷或者其他工具下载下来
  8. 打开 VirtualBox,菜单 -> 管理 -> 导入虚拟电脑,选择下载下来的文件进行导入
  9. 关闭 Genymotion 再重新打开,这时可以看到设备了
  10. 单击设备,右侧有一个设置按钮可以设置分辨率等信息
  11. 双击设备就可以进入系统了

使用

安装 App 很简单,直接把 apk 文件拖进去就行。

在 Android Studio 安装 Genymobile 插件,可以参考 这里

问题

React Native 项目修改后,Genymobile 无法刷新,网上有说可以双击 R,或者 cmd + m(拖动分隔线),然而并没有用,只能在 Android Studio 里重新 Run。

如果 Genymotion 模拟器已经运行,但是 Android Studio 在 Run 的时候检测不到,尝试以下操作:

  1. 进入 Genymotion 的 Settings,在 ADB 标签下选择 Use custom Android SDK tools,输入 Android SDK 路径,例如
    /Users/你的用户名/Library/Android/sdk
  2. 进入 Android Studio,菜单 Tools -> Android,勾选 Enable ADB Integration
  3. 重启  Android Studio
  4. 如果上述方法无效,打开 VirtualBox,删除虚拟设备,重新导入设备
  5. 重启  Android Studio

如何在手机上玩代码、操作服务器

G20 放假那几天,在家捣鼓如何在手机上调试代码,初衷是如何在手机上调试 node。

找到了serverauditor 这款神器,可以用它来登录服务器,爱干啥干啥,比如今天上午在小区里遛小朋友的时候,我用它来作了一次代码发布,想想就醉。

Serverauditor is a desktop and mobile SSH terminal
Everywhere. Handy. Functional.

第2892天:h5页面下拉加载就这么简单

Q(window).scroll(function() {
    if ((Q(window).scrollTop() + Q(window).height() > Q(document).height() - 40)) {
        // do something
    }
});