WebView 中判断键盘是否弹出

1. 背景

最近在开发中遇到这样一个问题:页面中有一个输入框input,一个 Modal 框modal,一个点击打开 Modal 框的按钮btnOpenModal,和一个提交表单的提交按钮submit。当input获得焦点,键盘弹出时,如果直接点击btnOpenModalinput会失去焦点键盘收回,导致页面高度改变,modal弹出后会有一个闪烁。所以需要在点击btnOpenModal时,判断当前键盘是否弹出。

2. 方案

最初,考虑在点击按钮时获取输入框元素,判断它是否处于focus状态,来判断键盘是否已弹出。但点击按钮之后,按钮就成为了document.activeElement。此时输入框必然已失去焦点,导致无法通过该方法判断键盘是否弹出。

考虑监听输入框的focusblur两个事件,当focus触发时,为当前视图容器this.cnt添加一个标识class,假设为KEYBOARD_STATUS;当blur触发时,则移除该class。当点击btnOpenModal时,判断this.cnt是否拥有KEYBOARD_STATUS这个class。如果有,则说明


阅读全文 »

单页视图引擎(SPA)添加视图切换动画

1. 背景

9月的时候,为单页试图引擎 Cyra 2.1.0 版本增加了一个创建视图切换动画的功能,使得 i 版页面之间切换也可以有一个滑入滑出的效果。一直没有时间做一个梳理,现在记录一下。最终的效果如图:

Cyra Animation

补充:悲剧,刚修复了一个 bug,如果想要设置ANIMATION_FUNC,则需要升级到v2.1.4

2. 思路

其实思路比较简单,就是在 Router 中控制。首先,将所有视图容器position都设置为absolute。当切入视图完全渲染出来后(也就是执行完willAppear钩子函数),设置当前展示视图和即将展示的视图两者的 animation CSS 属性,在 animation 中修改视图容器的translateX来实现动画效果。

因为每个视图容器是单例的(Mix 页除外),当项目中出现循环路径,如:

View A -> View B -> View C -> View A

或者当 A 和 C 是同一个视图,只是数据不同。这种情况下,如果视图右滑,也就是用户主动通过点击“返回”按钮进入(返回)前一个视图时,当前视图向右滑出。如果滑出后不修改它


阅读全文 »

Chrome devtools 扩展与模块间通信

1. 背景

为了方便 Cyra 用户查看/管理项目中的视图及视图间的跳转和数据传递,决定开发一个 Chrome extension(扩展)来方便展示。开发中,最关键的问题在于如何解决各模块之间的通信。最终的实现效果如下图:

Cyra devtools

2. 开发

2.1 模块划分

这部分网上已经有很多文章来介绍,在此不再赘述。但网上涉及到devtools类型的插件比较少,所以简单介绍下。首先,在 devtools 中创建的 panel 本质是一个 HTML 页面。代码主要分为以下4部分:

  1. background.js:Chrome 为扩展提供的一个独立的脚本运行环境,在本例中,主要作为 content_script.js 和 devtools.js 之间通信的桥梁,因为 Chrome 并没有提供后面二者直接通信的服务。
  2. content_script.js:用来向打开的页面注入脚本。
  3. devtools.js:用来在 devtools 中创建一个 panel,并实现该 panel 和 background.js 之间的通信。
  4. draw.js:devtools.js 中创建 pan


阅读全文 »

记一次 Nginx URI rewrite 优化

1. 背景

既上一篇文章记录了组内单页引擎升级路由为 History API 方式,考虑到不支持该方式的浏览器/WebView,需要多页降级。如果不对 Nginx 进行配置,就会出现404,因为多数通过pushState得到的 URL 并没有真实对应的资源。

2. URI 形式

采用 History API 方案的 URL 格式如下:

http[s]://hostname/resource/project/page[/view]?arg1=value1

其中 resource 为资源目录,该目录下放置各个不同的项目文件夹,每个项目对应一个 project 目录。一个 project 中可能会有多个单页应用,每个单页应用对应 URL 中的一个 page。一个单页应用下的不同视图分别对应一个 view。因为采用的单页引擎支持默认路由,所以 view 并非必需。

比如 A 项目下有一个 refund 的单页应用,对应的 HTML 资源为 refund.html,为了 URL 的美观,在 URL 中去掉 html 后缀名。refund 管理两个视图 detail 和


阅读全文 »

History API 路由方案 Nginx 配置

1. 背景

之前使用 History API 作为路由方案升级了组内单页视图引擎。完整的实现还需要 Nginx 的辅助配置,因为当页面刷新时,通过pushState到达的路由很可能并不存在对应的资源,所以要使同一个项目下的所有路由匹配到项目对应的单页资源。

2. 方案

其实主要的工作是对3种资源的 uri 配置 rewrite:

  1. HTML 资源
  2. 引用到的项目 JS 资源
  3. 引用到的项目 img 资源

对应的配置如下:

其中/resource/wa/为所有项目所处的统一目录,第二级目录为项目所在目录,第三级对应项目下的某个单页,第四级为可选,且表示某个单页项目下的某个具体 view。

注:转载注明出处并联系作者, 本文链接: https://nodefe.com/history-api-nginx-config/


阅读全文 »

使用 History API 升级 SPA 路由方案

1. 背景

组内现在使用组内自己开发的移动端 SPA 引擎 Cyra,版本为 1.2.x。框架很轻,使用 hash 做路由,然后统一管理各个 view(视图)的状态以及 view 之间跳转。

开发过程中,遇到很多坑都和使用 hash 做路由有关。当然,问题并不在 hash 本身,而是和客户端以及后端 RD 配合中遇到的问题。比如:

  1. 验签问题,客户端没有将 hash 计算进去;
  2. 客户端会在 WebView 中访问的 URL 上拼接一些参数,作为客户端和前端之间通信的一种手段。由于拼接算法有问题,导致部分参数拼接到了 hash 后面,使得 Cyra 多个 view 之间通过 URL 传递的参数格式被破坏;
  3. iOS 版 APP 的 WebView 中通过 jsbridge 来修改 title,但是 hash 的修改无法修改。

综上,考虑升级框架的路由实现方案,使用 History API 代替 hash,并配合 Session Storage 做多页降级,以此来解决上述问题。

2. 方案

为兼容 Cyra 1.x 开发的项目,此次升级仅修改路由的内部实现方式,


阅读全文 »

iOS 8.1/8.2 WebView 第三方输入法无法响应 keyup 事件

这是前两天遇到的一个坑。场景是一个包含一个输入框和一个“提交”按钮(默认 disabled)的 H5 页面,当在输入框中输入合法数据后,“提交”按钮会变得可用,然后用户可以点击提交数据。

之前的实现是监听输入框的keyup事件,当输入合法数据后,修改按钮样式变为可用。

但部分用户反馈无论输入什么样内容,按钮始终不可点击,所以猜测可能是keyup事件没有响应。通过整理出问题的 OS,发现主要是 iOS 8.1/8.2两个版本。测试后发现的确是这两个版本的 WebView 下第三方输入法对keyup事件无响应。

最终的解决方案是修改为监听input事件,问题解决。先写这么多,留坑总结下两个事件的不同。

注:转载注明出处并联系作者, 本文链接: https://nodefe.com/keyup-dont-work-under-ios8_1-and-ios8_2/


阅读全文 »

stringify 实现及 JSON 数据类型思考

背景

记之前遇见的一道面试题,让现场写出 JavaScript 中stringify函数的实现。首先写一下自己最开始的思路,然后针对里面的一些问题进行逐步修改,并且引出对 JSON 这种轻量级数据传输格式所拥有数据类型的学习与思考。

一、最初实现

首先给定题目

将一个 JSON 格式对象转换为字符串,转换后的结果可以通过JSON.parse()方法将该字符串重新转换为一个 JSON 对象。

先简化问题,将数据类型简单划分(并不正确,下文会给描述)为:对象(Object)、数组(Array)和字符串(String)这三种类型。对于 String,只需要简单的对它进行toString()调用,并包裹在"中处理,对于前两者则需要采取不同操作。考虑到他们的子元素也是 JSON 对象,定义具有递归性,所以代码也采用递归来实现。最初代码如下:

代码的思路就是传入一个对象,然后判断它的类型:1. 如果非数组和对象类型,就直接返回它的字符串形式;2. 如果是对象类型,则遍历每个键值对,判断每个键对应值的类型,如果是


阅读全文 »

HTTP 缓存机制学习实践

1. 背景

这两天研究了下 HTTP 的缓存机制,其中版本是HTTP 1.0/1.1。现在将几个和 HTTP cache 有关的 Header 的用法做一个总结。

2. 缓存机制

2.1 Header 取值

服务器在返回的 response 中主要使用两个 Header 来控制浏览器的缓存行为:

  1. Expires:在HTTP 1.0版本中定义,为了兼容老版本 UA 常常也会加上该 Header,后面跟一个绝对时间字符串,表示过期时间。
  2. Cache-Control:在HTTP 1.1版本中定义,除了提供了同Expires相同并更精确的缓存功能,还提供了验证机制,它可以取以下这些值:
    • max-age:功能和Expires类似,但是后面跟一个以“秒”为单位的相对时间,来供浏览器计算过期时间。
    • no-cache:提供了过期验证机制,下文会再着重介绍。
    • no-store:表示当前请求资源禁用缓存。
    • private:指示只有用户客户端可以缓存,而 CDN 等不可。
    • public:指示用户客户端和 CDN 都可以缓存当前资源。
2.2 直接缓存


阅读全文 »

React Native 下拉菜单容器实现

背景

本周算是把一个 react native 版的商家管理工具(iOS 版花花内嵌商家后台)完成,所以现在花一点时间来把其中一些值得记录下的东西整理一下写出来。 根据 UI 设计,很多页面都采下图的展示方式:

截图

可以看到,两个页面的结构相似:顶部都是若干个PickerIOS组件的集合(红色框部分),然后下面是不同的组件(蓝色框组件)。例如左图有3个PickerIOS而右图有4个PickerIOS,并且分别代表不同的筛选条件。左图中的内容组件包括一个图表以及一个列表,而右图只有一个列表。所以考虑把公共部分提取出一个公共组件,该组件可以实现:

  1. 红色框内的PickerIOS数量以及内容可以定制。
  2. 蓝色框内的内容组件可以作为一个属性传递给该组件,用来展示信息。
  3. 切换筛选条件,能够向后端请求数据,并更新内容组件。

功能实现

顶部的Picker集合采用配置的方式实现,即单独创建一个配置文件模块DropdownViewConfig.js,文件内容大致如下:

每个 key(如 platform 和 date 等)对


阅读全文 »