es6模块 import, export 知识点小结

ES6模块只支持静态导出,你只可以在模块的最外层作用域使用export,不可在条件语句中使用,也不能在函数作用域中使用。总结了如下几种用法:

exports的几种用法

1. Named exports (导出每个 函数/变量)

名字导出,这种方式导出多个函数,一般使用场景比如 utils、tools、common 之类的工具类函数集,或者全站统一变量等。

只需要在变量或函数前面加 `export` 关键字即可。

//------ lib.js ------
export const sqrt = Math.sqrt;
export function square(x) {
    return x * x;
}
export function diag(x, y) {
    return sqrt(square(x) + square(y));
}


//------ main.js 使用方式1 ------
import { square, diag } from 'lib';
console.log(square(11)); // 121
console.log(diag(4, 3)); // 5

//------ main.js 使用方式2 ------
import * as lib from 'lib';
console.log(lib.square(11)); // 121
console.log(lib.diag(4, 3)); // 5

我们也可以直接导出一个列表,例如上面的lib.js可以改写成:

//------ lib.js ------
const sqrt = Math.sqrt;
function square(x) {
	return x * x;
}
function add (x, y) {
	return x + y;
}
export {sqrt, square, add}

2. Default exports (导出一个默认 函数/类)

这种方式比较简单,一般用于一个类文件,或者功能比较单一的函数文件使用。一个模块中只能有一个export default默认输出。

export default与export的主要区别有2个:

  • 不需要知道导出的具体变量名
  • 导入(import)时不需要{}
//------ myFunc.js ------
export default function () { ... };

//------ main.js ------
import myFunc from 'myFunc';
myFunc();

导出一个类

//------ MyClass.js ------
class MyClass{
  constructor() {}
}
export default MyClass;

//------ Main.js ------
import MyClass from 'MyClass';

注意这里默认导出不需要用{}。

3. Mixed exports (混合导出)

混合导出,也就是 上面第一点和第二点结合在一起的情况。比较常见的比如 Lodash,阿里 Fusion之类的库都是这种组合方式。

//------ lib.js ------
export var myVar = ...;
export let myVar = ...;
export const MY_CONST = ...;

export function myFunc() {
  ...
}
export function* myGeneratorFunc() {
  ...
}
export default class MyClass {
  ...
}

// ------ main.js ------
import MyClass, {myFunc} from 'lib';

再比如lodash例子:

//------ lodash.js ------
export default function (obj) {
    ...
};
export function each(obj, iterator, context) {
    ...
}
export { each as forEach };

//------ main.js ------
import _, { each } from 'lodash';

4. Re-exporting (别名导出)

一般情况下,export输出的变量就是在原文件中定义的名字,但也可以用 as 关键字来指定别名,这样做一般是为了简化或者语义化export的函数名。

//------ lib.js ------
export function getUserName(){
   ...
};
export function setName(){
  ...
};

//输出别名,在import的时候可以同时使用原始函数名和别名
export {
  getName as get, //允许使用不同名字输出两次
  getName as getNameV2,
  setName as set
}


5. Module Redirects (中转模块导出)

有时候为了避免上层模块导入太多的模块,我们可能使用底层模块作为中转,直接导出另一个模块的内容如下:

//------ myFunc.js ------
export default function() {...};
 
//------ lib.js ------
export * from 'myFunc';
export function each() {...};
 
//------ main.js ------
import myFunc,{ each } from 'lib';

错误的export用法

export 只支持在最外层静态导出、只支持导出变量、函数、类,如下的几种用法都是错误的。

//直接输出变量的值
export 'Mark';

//未使用中括号 或 未加default
// 当只有一个导出数,需加default,或者使用中括号
var name = 'Mark';
export name;

//export不要输出块作用域内的变量
function(){
  var name = 'Mark';
  export {name};
}

import的几种用法

import的用法和export是一一对应的,但是import支持静态导入和动态导入两种方式,动态import支持晚一些,兼容性要差一些,目前Chrome浏览器和Safari浏览器支持。

1. Import an entire module’s contents (导入整个模块)

当export有多个函数或变量时,如文中export的第一点,可以使用 * as 关键字来导出所有函数及变量,同时 as 后面跟着的名称做为 该模块的命名空间。

//导出lib的所有函数及变量
import * as lib from 'lib';

//以 lib 做为命名空间进行调用,类似于object的方式
console.log(lib.square(11)); // 121

2. Import a single/multiple export from a module

从模块文件中导入单个或多个函数,与 * as namepage 方式不同,这个是按需导入。如下例子:

//导入square和 diag 两个函数
import {square, diag} from 'lib';

// 只导入square 一个函数
import {square} from 'lib';

// 导入默认模块
import _ from 'lodash';

// 导入默认模块和单个函数,这样做主要是简化单个函数的调用
import _, { each } from 'lodash';

3. Rename multiple exports during import

和 export 一样,也可以用 as 关键字来设置别名,当import的2个类的名字一样时,可以使用 as 来重设导入模块的名字,也可以用as 来简化名称。如下例子:

// 用as 来 简化函数名称
import {
  reallyReallyLongModuleExportName as shortName,
  anotherLongModuleName as short
} from '/modules/my-module.js';

// 避免重名
import { lib as UserLib} from "ulib";
import { lib as GlobalLib } from "glib";

4. Import a module for its side effects only

有时候我们只想import进来,不需要调用,很常见的,比如在webpack构建时,我们经常import css 进来,或者import一个类库进来。

// 导入css
import './mystyle.css';

// 导入类库
import 'axios';

5. Dynamic Imports

静态import在首次加载时候会把全部模块资源都下载下来,但是,我们实际开发时候,有时候需要动态import(dynamic import),例如点击某个选项卡,才去加载某些新的模块,这个动态import特性浏览器也是支持的。

// 当动态import时,返回的是一个promise
import('/modules/my-module.js')
  .then((module) => {
    // Do something with the module.
  });

// 上面这句实际等同于
let module = await import('/modules/my-module.js');

es7的新用法:

async function main() {
    const myModule = await import('./myModule.js');

    const {export1, export2} = await import('./myModule.js');

    const [module1, module2, module3] =
        await Promise.all([
            import('./module1.js'),
            import('./module2.js'),
            import('./module3.js'),
        ]);
}
main();

参考资料:

博客恢复了

今年年中,本博所在的阿里云服务器忘记续费,导致程序和源码被清空删除了,写了9年的博客就这样没了,甚是可惜。咨询了在阿里云的同事,均表示未续费的服务器被格式化后数据是无法找回的。无奈只能通过其他方式将散落在网络上的各种转载博客的文章重新收集起来。9月开始断断续续的进行博客恢复之旅,目前部分文章已恢复,还有一些没法恢复的就算了。

记录一下恢复的过程:

从备份中恢复

依稀记得,在很久以前做WordPress的时候,曾经备份过一次博客的数据库到邮箱里,于是在qq邮箱里搜关键字把博客的数据库搜出来了,里面有70多篇文章,但没法直接恢复,因为数据库太旧了,最新的WordPress修改了一些字段并新增了一些关联表。

于是先在阿里云重新购买并初始化一个Nginx + php 环境,搭个WordPress + phpmyadmin 上去。

搭建完之后,将旧数据库的wp_postswp_commentswp_term_relationshipswp_term_taxonomywp_terms等几个核心表的数据拼成新库所需的sql导入到WordPress。至此12年之前的文章算是恢复了,但回头看这些文章,实际参考意义不大了,前端技术变化太快,那个时候还是在讨论jquery、讨论ie6、7的兼容性问题,权当是纪念自己的职业生涯吧。

从网络搜集

虽然博客更新频率也不算太高,保持一个月一篇的速度,但这几年下来也有几十篇了。先根据邮箱搜索一下博客里以前的评论(因为博客文章有评论的话,会邮件通知)。

从评论的邮件里可以翻出博客的文章标题,基于标题再去百度上搜索对应的文章,所幸博客还是有蛮多人关注的,网上也有一些人转载了我的文章,这个时候发挥百度和google的作用了。

经过一番搜索,发现有几个网站还专门爬了我的博客,比如这个:

http://ju.outofmemory.cn/feed/2494/

把我15年和16年的博客每篇文章给转载了,真是庆幸,于是从这个网站上把相关的3页文章给再次搬回网站,文章发表时间根据该网站转载的时间,稍微调前一点(毕竟是原创,总不能比转载的时间更晚是吧)。

那么剩下17年和12到15年这几年的文章了,只能一篇一篇的去找了,也再找回几篇质量比较高的,剩下一些因为标题太普遍了,搜不到与我发表的文章内容相关的。

感叹

经过这件事情,说明数据备份是多么重要啊。也说明自己懒了,很久定的计划,一个月至少一篇,但是近2年没有坚持下来,后面还要坚持更新下去。

这次博客重新恢复,虽然损失了很多网友的评论和一些无法恢复的文章,但大部分也算找回了。以前的文章代表过去,后续继续产出高质量文章,加油!

requestAnimationFrame最佳实践

requestAnimationFrame ,这个方法应该都不陌生,字面含义是请求动画帧。从API命名来看,和动画有着密切的关系。其用法跟setTimeout差不多,与setTimeout相比,最大的优势是 由浏览器来决定函数的执行时机 。形象一点的解释就是:告诉浏览器说 “我这里有一个函数要执行,你有空了帮忙执行一下”,然后浏览器相对比较空闲的时候就给执行了。

用法一:动画

raf api本身的设计就是用来解决js动画的性能问题。那么,为什么raf做动画性能会更好呢?主要原因在于 raf更加智能,它并非加快执行速度,而是适当时候降帧,防止并解决丢帧问题 。当它发现无法维持60fps的频率时,它会把频率降低到30fps来保持帧数的稳定。也就是说如果上一次raf的回调执行时间过长,那么触发下一次raf回调的时间就会缩短,反之亦然,这也是为什么说由浏览器来决定执行时机性能会更好。

(function animloop(){
  window.requestAnimFrame(animloop);
  render();
})();

DEMO:

See the Pen bErOLz by hugo ( @baofen14787 ) on CodePen .

用法二: 函数节流

在高频率事件中,为了防止16ms内发生多次函数执行,使用raf可保证16ms内只触发一次,这既能保证流畅性也能更好的节省函数执行的开销。 16ms内函数执行多次没有意义,因为显示器16ms刷新一次,多次执行并不会在界面上有任何显示。

举个例子:

var $box = $('#J_num2'),
      $point = $box.find('i');
$box.on('mousemove',function(e){
  requestAnimationFrame(function(){
      $point.css({
          top : e.pageY,
          left : e.pageX
      })
  })

})

用法三:CPU节能

raf的另一个特性是:如果页面不是激活状态下的话,函数会自动暂停,有效节省了CPU开销。在移动端,如果页面中有自动播放的轮播图、倒计时或使用 setTimeout/setInterval 来执行任务的定时器。那么 当app进到后台或是锁屏后,webviewcorethread仍然持续占用CPU,导致耗电 。而使用raf可以很简单的解决此类问题。

PS:复杂情况下,可配合 window.onblur & window.focus 、 document.onvisibilitychange 、 document.onpause &document.onresume 等相关的方法进行实现,但这篇文章里不做深入探讨。

(function(){
        var timer;
        var $txt = $('#J_num2'),
                num = 0;
        function play(){
            timer = setTimeout(function(){
                  //使用raf实现非激活状态下不运行
                requestAnimationFrame(function(){
                    stop();
                    next();
                });
            },1000)
        }

        function stop(){
            clearTimeout(timer)
        }

        function next(){
            $txt.text(num++);
            play();
        }
        play();
    })();

DEMO:

See the Pen OMxodB by hugo ( @baofen14787 ) on CodePen .

用法四:分帧初始化

都知道,raf的执行时间约为16.7ms,即为一帧。那么可以使用raf将页面初始化的函数进行打散到每一帧里,这样 可以在初始化时降低CPU及内存开销 

很多页面,初始化加载时,CPU都会有很明显的波动,就是因为大量的操作都集中到了一点上。

举个例子:

页面中有4个模块,A、B、C、D,在页面加载时进行实例化,一般的写法类似于:

$(function(){
    new A();
    new B();
    new C();
    new D();
})

而使用raf可将每个模块分别初始化,即 每个模块都有16ms的初始化时间

    $(function(){
        var lazyLoadList = [A,B,C,D];
        $.each(lazyloadList, function(index, module){
        window.requestAnimationFrame(function(){new module()});
        }); 
        
    })

用法五:异步化

raf实际是一种异步化的操作,曾经 setTimeout(function(){},0) 一度成为解决了很多前端疑难杂症的法宝。而现在,可以用raf来代替。

React Native 性能优化建议

1. 异步逐层渲染。

React Native 虽然一直标榜媲美Native的体验,但实际使用下来,其渲染性还是非常低效,基于ScrollView和ListView两大容器,在渲染上,相当于web端的table布局,需要等整个大table渲染完成才显示页面,也就是说,当容器内有大量的子元素,其白屏时间会非常长。

如何让React Native做到像web端边渲染边加载?可以采用异步渲染的方式,使用requestAnimationFrame 或 setTimeout 定时将单个组件push进ScrollView容器。

基于这个原理,写了个逐层渲染的组件: react-progressive

2. 实现shouldComponentUpdate方法

如上第一点,逐层渲染提升打开时间,但是也会导致component重复渲染,也就是执行了大量无用的diff算法。虽然React里引以为傲的diff算法非常高效,但是执行数量达到一定程度后,也会带来非常大的影响。那么可使用 shouldComponentUpdate 来控制component的渲染次数。

如何做?

  • 如果确定该组件渲染完后无需再次更新,即这个组件是一个静态组件,那么可以直接return false。

shouldComponentUpdate(){
    return false
  • 如果组件比较复杂,自己对RN的更新机制不太熟,可以直接Minxi一下React提供的 PureRenderMixin 组件
mixins: [React.addons.PureRenderMixin]
  • 手动实现或使用第三方组件库,比如 Immutable-js

说白了,就是要确定组件内的不可变数据,让其不再执行diff及render。

3. 使用setNativeProps方法

setNativeProps 方法可以理解为web的直接修改dom。使用该方法修改 View 、 Text 等 RN自带的组件 ,则不会触发组件的componentWillReceiveProps 、 shouldComponentUpdate 、 componentWillUpdate 等组件生命周期中的方法。

建议频繁更新的操作,如slider、tabs切换等拖曳操作时,使用 setNativeProps 来更新属性,会获得意想不到的性能体验。

代码片段:

me.refs.tabView.setNativeProps({
                style : {
                    height : 0,
                    opacity : 0
                }
            });

性能分析工具: React.addons.Perf

4. 不要使用阴影效果

React Native 里面的 shadow 相关的样式,是非常耗性能的css属性。这在web上,以前android 2.0年代,也是一样耗性能的css属性之一。如果需要使用阴影效果,建议使用图片来代替反而性能更好一些。

5. 最小化DOM

React Native里虚拟dom结构越复杂,则越低效。感觉RN的渲染性能,和以前android2.x时代没多大区别,如果层级结构大于5级,则要考虑下优化了。这没啥技巧,纯靠经验及硬实力。

6.组件粒度化

如何更好的划分组件粒度,这需要合理的对组件进行更细粒度的划分,区分出静态组件及动态组件。

Chrome渲染分析之Rendering工具使用(3)

在前面2篇文章中介绍了 Show paint rectangles 和 show composited layer borders 两个选项的作用。今天接着讲下一个: show FPS meter

show FPS meter

show fps meter 可以理解为显示FPS帧频/帧数。开启这个选项后,右上角会实时显示当前页面的FPS。

先简单科普一下啥是FPS。FPS全称叫 Frames Per Second (每秒帧数)。帧数越高,动画显示的越流畅。一般的液晶显示器的刷新频率也就是 60HZ。也就是说, 要想页面上的交互效果及动画流畅。那么FPS稳定在60左右,是最佳的体验。 。据悉 ios上的交互效果都是60FPS呢。

记得以前做Flash游戏的时候,FPS帧数是游戏流畅度的一个重要指标。在web端,道理也是一样。

还记得我之前的文章提到 《web移动端性能调优及16ms优化》 这里的16毫秒,实际就是 1000ms/60FPS = 16.6ms。 也就是一帧所花费的时间约是16毫秒。

科普完毕,回到正题。chrome提供的 show FPS meter 选项,在我们制作测试页面交互及动画性能时非常有用。同时它也提供了当前页面的GPU占有率给我们。

React Native 自定义事件机制

React Native 刚出来不久,写过一篇 react-native 组件间通信 的文章,里面介绍了组件间三种关系的相互通信。用了一段时间下来,感觉React Native 这套组件间传递数据的方法,实施起来特别麻烦,并且不利于解耦。

翻看React Native的文档,并没有看到实现组件间消息机制的方法。但在源码里,找到了相关的实现。

其中 EventEmitter 实现了事件机制,基于EventEmitter可以实现在一个component里注册一个自定义事件,在另一个component监听该事件,从而实现简单的 观察者模式 

RCTDeviceEventEmitter 从源码里可以看到,只是实例化了一个 EventEmitter 而已。在 PushNotificationIOS 组件中有相关的使用。

而 Subscribable 也是对 EventEmitter 做了一层封装,实现了一个较为安全的 mixin 方法来处理事件。

具体API请直接查看源码,比较简单,就不一一介绍了。

DEMO3


/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 */
'use strict';

var React = require('react-native');

var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');

var {
  StyleSheet,
  Text,
  View,
  TextInput
} = React;


var styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
});


var Input = React.createClass({

    handleUpdateChange(text) {
        RCTDeviceEventEmitter.emit('change',text);
    },


  render() {

    return (
      <View style={{justifyContent: 'center',alignItems: 'center',backgroundColor: '#F5FCFF'}}>
        <TextInput onChangeText={(text) => this.handleUpdateChange(text)} style={{ width : 200, height: 40, borderColor: 'gray', borderWidth: 1}} />
      </View>
    );
  }
});




var ShowText = React.createClass({

    getInitialState(){
      return {
        text : ''
      }
    },

    componentDidMount(){
      var me = this;
      RCTDeviceEventEmitter.addListener('change',function(text){
         me.setState({
          text : text
         })
      })
    },


    render() {
        return (
          <View style={{justifyContent: 'center',alignItems: 'center',backgroundColor: '#F5FCFF'}}>
            <Text>{this.state.text}</Text>
          </View>
        );
    }
});

module.exports = React.createClass({


  render() {
    return (
      <View style={styles.container}>
        <Input />
        <ShowText />
      </View>
    );
  }
});

另一个使用Subscribable实现的DEMO: https://github.com/colinramsay/react-native-event-emitters/blob/master/index.ios.js

Chrome渲染分析之Rendering工具使用(2)

距离这个专题的上一篇 《Chrome渲染分析之Rendering工具使用(1)》 已经隔了一年多了,迟迟没有下笔,囧!

2.show composited layer borders

中文可翻译为:显示层的组合边界。

我们知道,在页面最终是由多个“图层”渲染而成。勾上这个选项,页面上的“layer(层)”会加上一个黄色的边框显示出来,如下图的 天猫首页 头部所示:

其中:

  • 黄色边框:用于显示页面上的layer
  • 蓝色栅格线:表示的是分块,这些分块可以看作是比层更低一级的单位

当然,还有其他颜色的边框线,比如图片如果单独有个layer的话,边框线是 蓝色的 

使用这个工具,可以查看当前页面的layer情况,更好的发现页面不需要的layer将之清除。

layer存在的意义

在弄明白这个问题之前,我们需要先了解一个dom元素最终是如何转变为我们屏幕上可视的图像。在概念上讲,可简单的分为四个步骤:

  1. 获取 DOM 并将其分割为多个层
  2. 将每个层独立的绘制进位图中
  3. 将层作为纹理上传至 GPU
  4. 复合多个层来生成最终的屏幕图像。

可以将这个过程理解为设计师的Photoshop文件。在ps源文件里,一个图像是由若干个图层相互叠加而展示出来的。分成多个图层的好处就是每个图层相对独立,修改方便,对单个图层的修改不会影响到页面上的其他图层。

基于photoshop的图层理念来理解web端的层,那么就很容易理解了。layer存在的意义在于: 用最小的代价来改变某个页面元素 

我们可以将某个css动画或某个js交互效果把它抽离到一个单独的渲染层,这样可以加快渲染的效率。

如何创建layer

  • 3D 或透视变换(perspective transform) CSS 属性
  • 使用加速视频解码的 <video> 元素
  • 拥有 3D (WebGL) 上下文或加速的 2D 上下文的 <canvas> 元素
  • 混合插件(如 Flash)
  • 对自己的 opacity 做 CSS 动画或使用一个动画 webkit 变换的元素
  • 拥有加速 CSS 过滤器的元素
  • 元素有一个包含复合层的后代节点(换句话说,就是一个元素拥有一个子元素,该子元素在自己的层里)
  • 元素有一个 z-index 较低且包含一个复合层的兄弟元素(换句话说就是该元素在复合层上面渲染),关于这点,更详细的实践可查看 该文章

在webkit内核的浏览器中,如果有上述情况,则会创建一个独立的layer。

其中第一点是最常用的手段,比如我们有时候给一个css效果加上 transform: translateZ(0); ,目的就是为了创建一个独立的layer。

另外还有另外一个css属性: will-change 也能实现同样的效果。

layer 过多带来的问题

还是拿photoshop来做比喻,一个ps文件如果有非常多的图层,那么这个文件肯定是非常大的。那对于web端也是一样,创建一个新的渲染层,它得消耗额外的内存和管理资源。当在内存资源有限的设备,比如手机上,由于过多的渲染层来带的开销而对页面渲染性能产生的影响,甚至远远超过了它在性能改善上带来的好处。

举个栗子,我们在天猫首页上加入css: * {-webkit-transform: translateZ(0);} 。 然后使用 timeline 可以看到,天猫的渲染耗时非常严重。

其影响的是页面渲染的最后一个环节: Composite Layers 

那么,一个合理的策略是: 当且仅当需要的时候才为元素创建渲染层。

更直观的查看页面layer

除了rendering 里提供的 show composited layer borders 选项外,还有一个更为直观的3d图像展示:

先选中 timeline 的某一帧,然后选择下面的 layer 标签tab,在右侧的区域就可以看到整个页面的3d图层了。

在这个视图中,你可以对这一帧中的所有渲染层进行扫描、缩放等操作,同时还能看到每个渲染层被创建的原因。

扩展阅读

若需要更细致的资料,可以查看下面这些文章。(可能需要翻墙)

wordpress 使用七牛镜像插件与SyntaxHighlighter插件冲突的解决方法

今天本博的阿里云服务器过期了。早上起来续费,发现要2k大洋。想想心疼啊,博客日访问量不超200UV,搞个大宽带,着实浪费。

续费时,就选了流量按需收费,0.8元/G。为了节省带宽,想到了用七牛云存储来保存及加速图片。

于是,在七牛上注册了账户,并安装了水煮鱼的 七牛镜像存储插件

整个过程一切顺利,图片成功转换为七牛图片了。但发现文章里的代码片段无法显示了。而我使用的是 SyntaxHighlighter 插件来高亮代码,于是我判断应该是 七牛镜像存储插件 与 SyntaxHighlighter插件 冲突。

google了一番,发现没有解决方案,莫非同时安装这2个插件的人太少?泥煤啊。

于是,只有放弃使用七牛镜像插件了。看了下七牛加速的原理,实际就是一个url替换,比如我的这张图片: http://www.ghugo.com/wp-content/uploads/2015/08/52.png 只需要替换域名后就可以直接使用 http://7qnca0.com1.z0.glb.clouddn.com/wp-content/uploads/2015/08/52.png

那么简单的一个功能,也不需要专门使用插件来处理了,直接写代码替换下即可。于是继续google一下,找到一段代码,将如下代码贴到你主题里的 functions.php 文件最末尾即可。


if ( !is_admin() ) {
    add_action('wp_loaded','c7sky_ob_start');
    
    function c7sky_ob_start() {
        ob_start('c7sky_qiniu_cdn_replace');
    }
    
    //修改自七牛镜像存储 WordPress 插件
function c7sky_qiniu_cdn_replace($html){
    $local_host = 'http://www.ghugo.com'; //博客域名
    $qiniu_host = 'http://7qnca0.com1.z0.glb.clouddn.com'; //七牛域名
    $cdn_exts   = 'css|png|jpg|jpeg|gif|ico'; //扩展名(使用|分隔)
    $cdn_dirs   = 'wp-content|wp-includes'; //目录(使用|分隔)
    
    $cdn_dirs   = str_replace('-', '\-', $cdn_dirs);

    if ($cdn_dirs) {
        $regex  =  '/' . str_replace('/', '\/', $local_host) . '\/((' . $cdn_dirs . ')\/[^\s\?\\\'\"\;\>\<]{1,}.(' . $cdn_exts . '))([\"\\\'\s\?]{1})/'; $html = preg_replace($regex, $qiniu_host . '/$1$4', $html); } else { $regex = '/' . str_replace('/', '\/', $local_host) . '\/([^\s\?\\\'\"\;\>\<]{1,}.(' . $cdn_exts . '))([\"\\\'\s\?]{1})/';
        $html =  preg_replace($regex, $qiniu_host . '/$1$3', $html);
    }
    return $html;
}
}

七牛云加速就是那么简单!

React Native ListView sticky效果实现

React Native中,ScrollView组件可以使用 stickyHeaderIndices 轻松实现 sticky 效果。

而使用ListView组件时,使用 stickyHeaderIndices 则不生效。

在IOS中的ListView的内部结构,实际是由多个Section组成,最典型的案例就是iphone手机的通讯录,滚动时每个section header会吸顶。

而在web端,使用 position : -weblit-sticky 实现的吸顶效果,也是类似的原理。具体可以看下之前的文章: 《position:sticky 使用条件分析》 .

好了,废话不多。在ListView中实现 sticky ,需要使用 cloneWithRowsAndSections 方法,将 dataBlob (object), sectionIDs (array),rowIDs (array) 三个值传进去即可。

dataBlob

dataBlob 包含ListView所需的所有数据(section header 和 rows),在ListView渲染数据时,使用 getSectionData 和 getRowData 来渲染每一行数据。 dataBlob 的 key 值包含 sectionID rowId

sectionIDs

sectionIDs 用于标识每组section。

rowIDs

rowIDs 用于描述每个 section 里的每行数据的位置及是否需要渲染。在ListView渲染时,会先遍历 rowIDs 获取到对应的 dataBlob 数据。

根据上面3个数据的定义,模拟出对应的数据结构如下:


var dataBlob = {
     'sectionID1' : { ...section1 data },
     'sectionID1:rowID1' : { ...row1 data },
     'sectionID1:rowID2' : { ..row2 data },
     'sectionID2' : { ...section2 data },
     'sectionID2:rowID1' : { ...row1 data },
     'sectionID2:rowID2' : { ..row2 data },
     ...
}

var sectionIDs = [ 'sectionID1', 'sectionID2', ... ]

var rowIDs = [ [ 'rowID1', 'rowID2' ], [ 'rowID1', 'rowID2' ], ... ]

在 DataSource 中,告诉ListView获取row和section的方法。


var getSectionData = (dataBlob, sectionID) => {
      return dataBlob[sectionID];
 }
var getRowData = (dataBlob, sectionID, rowID) => {
      return dataBlob[sectionID + ':' + rowID];
}
this.ds = new ListView.DataSource({
      getSectionData: getSectionData,
      getRowData: getRowData,
      rowHasChanged: (r1, r2) => r1 !== r2,
      sectionHeaderHasChanged: (s1, s2) => s1 !== s2
})

最后将数据传进ListView

 this.dataSource.cloneWithRowsAndSections(dataBlob, sectionIDs, rowIDs) 

最终效果如图:

1

完整代码示例可查看: https://github.com/hugohua/rn-listview-example

参考文章:

http://moduscreate.com/react-native-listview-with-section-headers/

React Native 之flex布局

react native 的flex布局,是web的阉割版本,目前还不支持 flex-shrink 、 flex-basis 、 order 几种flex属性,同时现有支持的flex属性值也不全。

alignItems

调整伸缩项目在侧轴上的定位方式

可选值: flex-start flex-end center stretch

alignSelf

alignSelf 属性会覆盖容器的 alignItems 属性,取值和用法 alignItems 一样。

可选值: auto flex-start flex-end center stretch

justifyContent

与 alignItems 相呼应,表示元素在主轴(横轴)方向上的对齐方式

可选值: flex-start flex-end center space-between space-around

flexDirection

子元素在父容器中的排列位置,相比于web,少了 row-reverse 和 column-reverse 两个值

可选值: row column

flexWrap

子元素超出父容器时是否换行

可选值: wrap nowrap

本文参考至: