Auto-height Webview of ReactNative

April 24, 2018

自动高的 Webview 实现方式其实跟 iframe 无二,无非是计算其内容高度后再赋值给容器样式。但是普通的办法实际上用起来差强人意,其问题主要体现在页面加载过慢,需要整个页面(包括图片)加载完成后才能计算出高度。而实际想要的效果往往是跟普通“网页”的表现一致,即:先加载文字,图片等内容异步加载、显示。在尝试了多款开源解决方案后,问题均没有得到解决,因此有了自己动手的想法。

不过本方案目前也只适用于自己拼接的 HTML,不适用于直接打开链接的 Webview,应用场景主要是在 ReactNative 应用内打开由 CMS 编辑的类新闻页面。

主要思路为:通过 Webview 提供的 postMessage 交互方式,不断地从 HTML 页面把自己计算好的高度抛送给 APP 端。但是这里其实有个问题,ReactNative Webview 的 postMessage 必须在页面加载完成以后才会注入,因此可以先加载一个空白页,待 postMessage 注入完成以后,再将实际文章内容插入到 body 中。

但是这么做有一个问题就是,页面将无法知道真正的内容“是否已加载完”,因为 window.onload 事件在加载开始之前就已经结束了。因此它只能不停地抛送高度信息,直到页面被销毁。

核心代码(HTML):

<html>
<head>
    <link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <meta name="viewport"
          content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
    <script>
      var inserted = false;
      var interval = setInterval(function () {
        var body = document.body, html = document.documentElement;
        var height = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight);
        if (window.postMessage) {
          if (!inserted) {
            document.body.innerHTML = '${valueParsed}';
            inserted = true;
          }
          window.postMessage(height + '');
        }
        if (document.readyState === 'complete') {
          //clearInterval(interval)
        }
      }, 200);
    </script>
</head>
<body></body>
</html>

核心代码(App):

export default class AutoHeightWebview extends PureComponent {
  constructor (props) {
    super(props);
    this.state = {
      webviewHeight: 0
    };
  }

  assembleHTML = (value) => {
    // 组装HTML,略
  };

  onMessage = (event) => {
    const webviewHeight = parseFloat(event.nativeEvent.data);
    if (!isNaN(webviewHeight) && this.state.webviewHeight !== webviewHeight) {
      this.setState({webviewHeight});
    }
  };

  render () {
    const HTML = this.assembleHTML(this.props.html);
    const onLoadEnd = this.props.onLoadEnd || function () {};
    // 防止 postMessage 与页面原有方法冲突
    const patchPostMessageFunction = function () {
      var originalPostMessage = window.postMessage;
      var patchedPostMessage = function (message, targetOrigin, transfer) {
        originalPostMessage(message, targetOrigin, transfer);
      };
      patchedPostMessage.toString = function () {
        return String(Object.hasOwnProperty).replace('hasOwnProperty', 'postMessage');
      };
      window.postMessage = patchedPostMessage;
    };

    const patchPostMessageJsCode = '(' + String(patchPostMessageFunction) + ')();';

    return (
      <WebView
        injectedJavaScript={patchPostMessageJsCode}
        source={{html: HTML, baseUrl: 'http:'}}
        scalesPageToFit={Platform.OS !== 'ios'}
        bounces={false}
        scrollEnabled={false}
        startInLoadingState={false}
        automaticallyAdjustContentInsets={true}
        onMessage={this.onMessage}
        onLoadEnd={onLoadEnd}
      />
    );
  }
}

关于 Moment.js 的一些思考

January 24, 2018

Moment.js 是一个流行的基于 JavaScript 的时间处理工具库。应该是一个从 2011 年开始启动的项目,至今它的 Github repo 也有了 3w+ 的星星,可以说在前端界人尽皆知了。反正我自从用了它基本上就没再接触过其它的相关库。

但最近我却对它的看法却产生了些许改变。原因是,它的 API 设计给使用者埋下了巨大无比的坑,简单来说:“名不副实”。

Read more »

Linux Setup for Work

January 9, 2018

因为各种烦人的原因,公司搬家后到新办公室第一件事先把老电脑格了。犹豫了一下,最终还是放弃了重装 Windows,支持我做出选择的原因有几:

  • 不需要进行(纯)MS 系开发
  • 没有必须使用的 Windows 软件
  • Windows 上跑 Android emulator 卡得头疼
  • NVIDIA 已有支持 Linux 的官方显卡驱动
  • Linux 开发效率更高
  • Linux 学习价值更高

本文是办公室适用(对我来说)的安装记录。

Read more »

2017

December 29, 2017

今天是 2017 年的最后一个(法定)工作日。做个简单的总结。

先对比一下去年的自己与目标:

  • 关于工作,年初就换了。现在到了一个游戏公司(西山居)上班。对于去年吐槽最多的「业务」问题来说,如今算是彻底解决了。
  • 关于学习,感觉自己从某些方面来说,是有一点进步的。
  • 关于生活,今年入了两台主要设备,一台 RMBP,以及一台游戏主机。感觉都很值。

大概就这些。

Read more »

《芳华》

December 27, 2017

我个人非常喜欢冯导的这部电影。

我的理解,这部电影的内容、主题,就跟它的名字一样,芳华。虽然我不是生活在那个年代的人,但是我也许可以理解那些都是什么。电影把一代人最美的形象,最好的年华,最真的梦想,展示给了我们看。相信这一点没有争议,不用过多解释。

至于其它的,我觉得都不重要。

有些人在这个故事里看到的更多是人的「恶」。如林丁丁,如红二代,如政委。认为所谓的「战友情」不过是镜花水月。但是,生活不就是这样的吗?

在电影里面,最终没有任何事情被追究,就连「迫害」了刘峰的林丁丁,最后也能被拿来给受害人打趣,然而我并没有觉得有任何反感之处。

人不就是这样的吗?当你对形势做出了错误的判断,就理应承担造成的后果。认真就输了,可谓一语成谶。既然是自己酿成的错,有什么好追究的呢?

百年以后,没有人会记得这些人当年的那些点点滴滴的琐事,善也好,恶也罢,大概都已经如萧穗子散落的情书一般,仿佛从来就没有存在过。即使是残酷至极的战争,也终究会被人遗忘。

也许能留下来的,也不过存在于现实与记忆中的,一代又一代人的最美的芳华吧。

Upgrade Projects Built by vue-cli

December 18, 2017

使用 vue-cli 创建的脚手架项目,目前最大的问题是创建后无法自动地进行升级。虽然 3.0 版本已经计划将其作为头等大事来进行改善 (#589),但是现行的版本依然要面对它。以下基于 webpack template 来进行升级时的一些要点解析。

Read more »

JSX in Vue.js

December 13, 2017

在基于 Webpack 的 Vue 项目中添加 JSX 支持:

$ yarn add babel-plugin-syntax-jsx babel-plugin-transform-vue-jsx babel-helper-vue-jsx-merge-props --dev

各依赖的作用:

  • babel-plugin-syntax-jsx 提供基础的 JSX 语法转换
  • babel-plugin-transform-vue-jsx 提供基于 Vue 的 JSX 特殊语法
  • babel-helper-vue-jsx-merge-props 是可选的,提供对类似 <comp {...props}/> 写法的支持

然后在 .babelrc 中,增加:

{
  ...
  "plugins": [
    "transform-vue-jsx",
    ...
  ]
  ...
}

注意如果有其它 env 也要如此加上 transform-vue-jsx 插件。

Read more »

Serve static with PM2

December 5, 2017

Command (2.4.0+):

$ pm2 serve <path> <port>

For example:

$ pm2 serve /dist 80

By default, it displays 404.html from the serving directory when that happens (NOT configurable).

Common-used Commands

November 8, 2017

Personal common-used commands list, including windows, osx, git, etc.

Read more »

Vue-Router Note

November 6, 2017

Vue Router (https://github.com/vuejs/vue-router) 使用笔记。虽然官方文档比较详尽,但实际用起来依然有些地方需要特别注意的(其实主要是我的个人需求)。

Read more »