RN 技术探索:Hermes Engine 初探

自从 Google 的 Flutter 发布之后,Facebook 对 React-Native 的迭代开始快了起来,优化 React-Native 的性能表现,避免被 Flutter 比下去。最近一个比较大的动作是开源了一个 JavaScript 引擎,并将其包含到 React-Native 中。那么这款引擎它有什么不同,相比 V8、JSC 这些 JavaScript 引擎又有什么优势呢,现在本文来为你揭晓。

1.  Hermes 引擎是什么,优势有哪些?

重要的事情提前说:Hermes 引擎是 Facebook 研发,在 React-Native Android 端用于替换 JavaScript Core 的 JavaScript 引擎。Hermes 引擎的优势是适合移动端的轻量级 JavaScript 引擎,使用 aot 编译,可以减少 Android 端内存使用,减小安装包大小,提升执行效率。

2.  什么是 JavaScript 引擎?

JavaScript 引擎是一个专门处理 JavaScript 脚本的虚拟机,一般会附带在网页浏览器之中。

3.  主流 JavaScript 引擎

V8(Google)、JavaScriptCore(Apple)、SpiderMonkey(Firefox)

4.  RN 中的 JavaScript 引擎

Weex,Android:V8,iOS:JavaScriptCore

RN,Android:JavaScriptCore(Hermes、V8),iOS:JavaScriptCore(Apple 要求)

注:Hermes Engine在React-native 0.60.2 版本后支持

5.  Hermes 的特色

  • 预编译字节码(引擎加载二进制代码效率高于运行JS脚本)

  • 无 JIT 编译器(减小了引擎大小,优化内存占用,但直接运行 JS 脚本的性能差于 V8 和 JSC)

  • 针对移动端的垃圾回收策略

继续阅读RN 技术探索:Hermes Engine 初探

年终盘点跨平台技术优劣势对比(Hybrid、RN、Weex、Flutter)

跨平台技术发展的三个阶段

  • 第一阶段是混合开发的web容器时代

    • 为了解决原生开发的高成本、低效率,出现了Hybrid混合开发
    • 原生中嵌入依托于浏览器的WebView
    • Web浏览器中可以实现的需求在WebView中基本都可以实现
    • 但是Web最大的问题是,它的性能和体验与原生开发存在肉眼可感知的差异
    • 因此并不适用于对性能和用户体验要求较高的场景
  • 第二阶段是以RN和Weex为代表的泛web容器时代

    • RN对Web标准进行了功能裁剪
    • 用户体验更接近于原生了
    • 由于进行了功能裁剪,所以RN对业务的支持能力还不到浏览器的5%
    • 因此仅适用于中低复杂度的低交互类页面。面对稍微复杂一点儿的交互和动画需求,都需要通过调用原生代码去扩展才能实现
  • 第三阶段是以Flutter为代表的自绘引擎时代

    • Flutter是构建Google物联网操作系统Fuchsia的SDK
    • 它使用Dart语言开发APP
    • 一套代码可以同时运行在iOS和Android平台上
    • Flutter采用自带的Native渲染引擎渲染视图,它是自己完成了组件渲染的闭环
    • 而RN、Weex之类的框架,只是通过JavaScript虚拟机扩展调用系统组件,最后是由Android或者iOS系统来完成组件的渲染

继续阅读年终盘点跨平台技术优劣势对比(Hybrid、RN、Weex、Flutter)

Centos 设置httpd-2.2 or httpd-2.4 MPM模式

MPM多进程处理模块

MPM分为三种模式:

1,prefork

进程模型,每个进程处理一个请求,模式:父进程——————>多个子进程——————>一个子进程处理一个请求

2,worker

线程模型,每个进程衍生出多个线程,每个线程处理一个请求,模式:父进程——————>多个子进程——————>每个子进程衍生多个线程------->一个线程处理一个请求任务

3,event

事件驱动模型,一个进程处理多个任务,模式:父进程————————>多个子进程-------->一个子进程处理多个请求

centos6 httpd-2.2 MPM设置

修改文件

/etc/sysconfig/httpd

HTTPD=/usr/sbin/httpd.worker

HTTPD=/usr/sbin/httpd.event

默认为prefork模式

配置后重启httpd server生效

centos7 httpd-2.4 MPM设置

修改文件 /etc/httpd/conf.modules.d/00-mpm.conf

LoadModule mpm_prefork_module modules/mod_mpm_prefork.so

LoadModule mpm_worker_module modules/mod_mpm_worker.so

LoadModule mpm_event_module modules/mod_mpm_event.so

模式参数配置详解

<IfModule prefork.c>  //如果加载了这个模块,就实现一下配置,一个条件化模块加载

    StartServers        8  //服务在启动时默认启动几个子进程

    MinSpareServers    5  //最小空闲进程数量

    MaxSpareServers    20  //最大空闲进程数量

    ServerLimit         256 //限制MaxClients

    MaxClients         256 //最大并发量,就是同时访问数量

    MaxRequestsPerChild 4000 //每个子进程最多能处理的请求数量,处理够数量后就被kill然后重新启动

</IfModule>

参考链接


Centos 设置httpd-2.2 or httpd-2.4 MPM模式

Android自动化测试--Espresso使用

相比上一篇文章所讲的Instrumented Unit Tests,本文所讲的自动化测试Espresso最显著的特点就是,可以与UI相交互。

使用

首先我们在Android Studio中新建一个项目,取名为EspressoTests。同时删除自动生成的一些文件,最终目录结构如下:

继续阅读Android自动化测试--Espresso使用

Espresso单元测试等待界面上某个元素显示出来/某个操作完成

在使用 Espresso 进行单元测试的时候,我们需要等待被测试界面上某个元素显示出来,这个时候需要进行等待,等待的时间需要我们自行控制。

继续阅读Espresso单元测试等待界面上某个元素显示出来/某个操作完成

WindowManager中removeView(View v)与removeViewImmediate(View v)的区别

问题及原因:

   我们在做UI相关的代码时有时候会碰到WindowLeak,也就是所谓的窗体泄露,泄露的原因是因为Android UI操作在主线程中操作,但是我们会需要在一些线程或者异步任务中操作UI界面元素的需求,那么这个时候可能会出现类似问题。我在做浮动窗口的时候碰到了这个问题,浮动窗口需要用到WindowManager, WindowManger又是一个Activity的一个变量,它依存于Activity,当横竖屏切换或者Activity销毁的时候这个变量会销毁。销毁的时候导致WindowManager通过AddView()方法添加的View没有依存,导致窗体泄露。那么问题来了,为什么这里会泄露了?

2.解决方法:我在onDestroy()里面调用了removeView方法,想要避免窗体泄露,但是这个方法并不管用,后来换成removeViewImmediate()就解决了这个问题,原因就是两个方法设计到线程同步问题,removeViewImmediate()是通知View立刻调用View.onDetachWindow(),这说明这个方法是通过一个监听或者观察者来实现的,因为线程的同步跟异步问题导致Activity销毁了,但View还没有被remove完,于是就产生了所谓的窗体泄露。说到这里,我想大家也能明白这两个方法的区别了。

参考链接


android WindowManager中removeView(View v)与removeViewImmediate(View v)的区别

NDK is missing a "platforms" directory.unset the NDK variable from ANDROID_NDK_HOME o...

NDK is missing a "platforms" directory.
If you are using NDK, verify the ndk.dir is set to a valid NDK directory.  It is currently set to D:\Android\Sdk\ndk-bundle.
If you are not using NDK, unset the NDK variable from ANDROID_NDK_HOME or local.properties to remove this warning. 

首先这个错误之前没有的,可能是NDK升级到最新导致的编译失败。

尝试按照网上搜索的解决方法解决:

  • 1 删除NDK重新下载,最后删除重新下载后还下载失败,让我自己去看看错误的logAndroid Studio - Help - Show Log In Explorer打开查看log
  • 2 自己去官网下载NDK解压设置路径,都无济于事
  • 3 想到之前看到一篇类似错误的文章说过错误提示缺少哪个文件如果没有就新建一个空文件放在目录下,于是我看了解压后的android-ndk-r22,确实没有错误里提到的platforms,就新建了一个空的platforms文件夹,修改了项目的新NDK路径,再执行编译,奇迹发生了,编译成功!!!app按钮终于是绿色不带叉的了。

继续阅读NDK is missing a "platforms" directory.unset the NDK variable from ANDROID_NDK_HOME o...

ubuntu编译weex_js_engine

weex早期在Android上使用的是V8,但经过测试,JavaScriptCore的性能比V8的性能更好,所以便用JavaScriptCore全面替代了V8

主分支默认使用由 UC 提供的 V8 引擎,默认只提供 armeabi-v7ax86 两个版本的引擎。

系统只能是ubuntu 16.04,因为编译的时候需要gcc-4.7的头文件。

继续阅读ubuntu编译weex_js_engine

vue中router-view与父组件之间的通信

在项目当中,遇到一个问题。当父页面的某个属性变化时,需要router-view中的页面根据不同的值进行不同的操作。

仔细想一下,其实类似父子组件之间的传值。

实现过程如下:

  1. 父组件绑定属性和事件
<template>
   <router-view v-on:test="testP" v-bind:msg="msg"></router-view>
</template>
<script type="text/javascript">
   export default {
      data() {
         return {
            msg: "把我带给router-view吧!"
         }
      },
      methods: {
        testP: function (data) {
          // 从router-view返回来的数据
          console.log(data)
          // 打印出来就是
          // 把我带给父组件吧!第一次!
          // 把我带给父组件吧!第二次!
        }
      }
   }
</script>
  1. router-view关联的属性和监听动作
<template>
   <div>{{msg}}</div>
</template>
<script type="text/javascript">
   export default {
      props:['msg'],
      data() {
         return {}
      },
      watch: {
        // 监听父组件的msg的变化
        msg: function() {
          console.log(this.msg)
          // 打印出来就是
          // 把我带给router-view吧!
        }
      },
      mounted() {
        // this.init()
      },
      methods: {
        init() {
          // 第一次向父组件传值
          this.$emit("test", "把我带给父组件吧!第一次!")
          // 第二次向父组件传值
          this.$emit("test", "把我带给父组件吧!第二次!")
        }
      }
   }

注意:

<router-view v-on:test="testP" v-bind:msg="msg"></router-view>

v-on 绑定的函数名 `test`,尽量不要出现大写字母(驼峰命名)(比如 v-on:Aplus_clicked="testP"),否则在某些特殊使用方式的情况下,可能会出现无法触发的问题

驼峰命名,可能会发生如下报错:

[Vue tip]: Event "aplus_clicked" is emitted in component <Anonymous> but the handler is registered for "Aplus_clicked". Note that HTML attributes are case-insensitive and you cannot use v-on to listen to camelCase events when using in-DOM templates. You should probably use "aplus_clicked" instead of "Aplus_clicked".

参考链接


Evaluating JavaScript code via import()

The `import()` operator lets us dynamically load ECMAScript modules. But they can also be used to evaluate JavaScript code (as Andrea Giammarchi recently pointed out to me), as an alternative to `eval()`. This blog post explains how that works.

`eval()` does not support `export` and `import`

A significant limitation of `eval()` is that it doesn’t support module syntax such as `export` and `import`.

If we use `import()` instead of `eval()`, we can actually evaluate module code, as we will see later in this blog post.

In the future, we may get Realms which are, roughly, a more powerful `eval()` with support for modules.

Evaluating simple code via `import()`

Let’s start by evaluating a `console.log()` via `import()`:

const js = `console.log('Hello everyone!');`;
const encodedJs = encodeURIComponent(js);
const dataUri = 'data:text/javascript;charset=utf-8,'
  + encodedJs;
import(dataUri);

// Output:
// 'Hello everyone!'

What is going on here?

  • First we create a so-called data URI. The protocol of this kind of URI is `data:`. The remainder of the URI encodes the full resource instead pointing to it. In this case, the data URI contains a complete ECMAScript module – whose content type is `text/javascript`.
  • Then we dynamically import this module and therefore execute it.

Warning: This code only works in web browsers. On Node.js, `import()` does not support data URIs.

Accessing an export of an evaluated module  

The fulfillment value of the Promise returned by `import()` is a module namespace object. That gives us access to the default export and the named exports of the module. In the following example, we access the default export:

const js = `export default 'Returned value'`;
const dataUri = 'data:text/javascript;charset=utf-8,'
  + encodeURIComponent(js);
import(dataUri)
  .then((namespaceObject) => {
    assert.equal(namespaceObject.default, 'Returned value');
  })
  .catch((err) => {
     console.log(err.message); // "Importing a module script failed."
     // apply some logic, e.g. show a feedback for the user
   });

Creating data URIs via tagged templates

With an appropriate function `esm` (whose implementation we’ll see later), we can rewrite the previous example and create the data URI via a tagged template:

const dataUri = esm`export default 'Returned value'`;
import(dataUri)
  .then((namespaceObject) => {
    assert.equal(namespaceObject.default, 'Returned value');
  })
  .catch((err) => {
     console.log(err.message); // "Importing a module script failed."
     // apply some logic, e.g. show a feedback for the user
   });

The implementation of `esm` looks as follows:

function esm(templateStrings, ...substitutions) {
  let js = templateStrings.raw[0];
  for (let i=0; i<substitutions.length; i++) {
    js += substitutions[i] + templateStrings.raw[i+1];
  }
  return 'da

For the encoding, we have switched from `charset=utf-8` to `base64`. Compare:

  • Source code: `'a' < 'b'`
  • Data URI 1: `data:text/javascript;charset=utf-8,'a'%20%3C%20'b'`
  • Data URI 2: `data:text/javascript;base64,J2EnIDwgJ2In`

Each of the two ways of encoding has different pros and cons:

  • Benefits of charset=utf-8 (percent-encoding):
    • Much of the source code is still readable.
  • Benefits of base64:
    • The URIs are usually shorter.
    • Easier to nest because it doesn’t contain special characters such as apostrophes. We’ll see an example of nesting in the next section.

`btoa()` is a global utility function that encodes a string via base 64. Caveats:

  • It is not available on Node.js.
  • It should only be used for characters whose Unicode code points range from 0 to 255.

Evaluating a module that imports another module  

With tagged templates, we can nest data URIs and encode a module `m2` that imports another module `m1`:

const m1 = esm`export function f() { return 'Hello!' }`;
const m2 = esm`import {f} from '${m1}'; export default f()+f();`;
import(m2)
  .then(ns => assert.equal(ns.default, 'Hello!Hello!'))
  .catch((err) => {
     console.log(err.message); // "Importing a module script failed."
     // apply some logic, e.g. show a feedback for the user
   });

Further reading  

参考链接