2018-02-28 16:35:00 zsp765098084 阅读数 3972
  • 征服ECMAScript6(JavaScript)视频教程

    本课程包含了几乎所有的ECMAScript6核心内容,包括let、const、变量的解构赋值,字符串扩展、正则扩展、数值扩展、数组扩展、函数扩展、对象扩展、Symbol、Proxy和Reflect、二进制数组、Set和Map数据解构、Generator函数、Promise对象、异步操作和async函数、Class、修饰器、Module等内容。

    23234 人正在学习 去看看 李宁

前言

前文ReactNative学习笔记——调用原生模块(Android)简单说了下ReactNative调用Android原生模块的基本用法,下面讲解下调用原生模块经常会用到的CallbackPromise
很多时候我们可能不仅仅是调用Native的方法,还要通过原生方法获取它的返回值,但是,前面说过,要导出一个方法给JavaScript使用,Java方法的返回类型必须为void,React Native的跨语言访问是异步进行的,我们无法像Java中常规方法一样直接通过return方式获取返回值。想要给JavaScript返回一个值的唯一方法是使用回调函数(Callback)或者发送事件(Promise

1.使用回调函数Callback

这里首先讲解CallBack的使用方法,它就是提供了一个回调函数来把返回值回传给JavaScript。
比如,我们通过原生模块获取手机型号:

package com.getnativemodule;
import android.os.Build;
import com.facebook.react.bridge.Callback;
...

public class SplashScreenModule extends ReactContextBaseJavaModule {

    public SplashScreenModule(ReactApplicationContext reactContext) {
        super(reactContext);
    }

    ...

    /**
     * 获取手机型号
     */
    @ReactMethod
    public void getSystemModel(Callback result){
        String sysModel= Build.MODEL;
        result.invoke(sysModel);
    }
}

这里,我们将CallBack作为参数传入方法中,然后调用invoke()方法,并将我们最终需要获取的返回值传入invoke方法的参数列表中(比如这里的sysModel),这样,我们就可以在RN的js中获取该返回值。
我们看下Callback的源码:

package com.facebook.react.bridge;

public interface Callback {

  /**
   * Schedule javascript function execution represented by this {@link Callback} instance
   *
   * @param args arguments passed to javascript callback method via bridge
   */
  public void invoke(Object... args);

}

就是一个接口,里面就一个invoke方法,可以说,这就是一个约定俗成的规则,没啥好研究的。

接下来,在RN中调用获取该返回值,写法如下:

...
import SplashScreen from "./jscode/SplashScreen";

export default class App extends Component<{}> {

    constructor(props){
        super(props);
        this.state={
            systemModel:'',
        }
    }
    ...

    componentDidMount() {
        ...
        SplashScreen.getSystemModel((sysModel)=>{
            this.setState({systemModel:sysModel});
        });
    }

    render() {
        return (
            <View style={styles.container}>
                ...
                <Text style={styles.welcome}>
                    {this.state.systemModel}
                </Text>
            </View>
        );
    }

}

之前忘了把SplashScreen.js代码添加进来,现在补上(也就两行代码)

import {NativeModules} from 'react-native'
export default NativeModules.SplashScreen;

在前面一文我们知道,Callback映射到JavaScript中是function,在ES6中使用箭头函数就如上面写法,这里的sysModel就是我们最终要获取的返回值。
我们运行查看效果:
这里写图片描述

当然,我们还可以传多个参数,比如,我们还要获取系统版本号,在Java中写法如下:

@ReactMethod
    public void getSystemModel(Callback result){
        String sysModel= Build.MODEL;
        String sysVersion=Build.VERSION.RELEASE;//获取系统版本号
        result.invoke(sysModel,sysVersion);     //将值传入invoke
    }

前面我们知道,invoke方法的参数是可变长度参数,所以你可以传任意多个参数值。
相应的,我们在JavaScript中修改代码如下:

...
import SplashScreen from "./jscode/SplashScreen";

export default class App extends Component<{}> {

    constructor(props){
        super(props);
        this.state={
            systemModel:'',
            systemVersion:'',
        }
    }
    ...

    componentDidMount() {
        ...
        SplashScreen.getSystemModel((sysModel,sysVersion)=>{//增加传参sysVersion
            this.setState({systemModel:sysModel,systemVersion:sysVersion});
        });
    }

    render() {
        return (
            <View style={styles.container}>
                ...
                <Text style={styles.welcome}>
                    {this.state.systemModel}
                </Text>
                <Text style={styles.welcome}>
                    {this.state.systemVersion}
                </Text>
            </View>
        );
    }

}

运行查看效果:
这里写图片描述
由此我们知道,JavaScript中传入的参数就是对应Java中invoke传入的参数。

2.使用Promise机制

原生模块还可以使用promise来简化代码,搭配ES2016(ES7)标准的async/await语法则效果更佳。如果桥接原生方法的最后一个参数是一个Promise,则对应的JS方法就会返回一个Promise对象。
它同样可以实现Callback的功能,我们先简单介绍一下Promise。
Promise是ES6中提供给原生的一个对象,它可以传递异步操作的消息,代表了某个未来才会知道结果的事件。

Promise有两个特点
  1. Promise对象的状态不会受到外界操作的影响

    Promise有三种状态:Pending、Resolved和 Rejected,只有等异步操作结束,得到操作结果后,由Promise根据结果决定自身是什么状态。

  2. 一旦状态改变,就不会再变,任何时候都可以得到这个结果

    Promise 对象的状态改变,只有两种可能:从 Pending 变为 Resolved 和从 Pending 变为 Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对 Promise 对象添加回调函数,也会立即得到这个结果。

Promise的三种状态

  • pending:进行时,无法知道处理进度,只知道目前正在处理异步操作;
  • resolve:已完成,表示异步操作正确返回结果回调方法;
  • reject:已失败,表示处理中途抛出异常,或者处理结果不正确的回调方法。

关于Promise还有很多内容,这里不做详解,下面我们就用Promise机制来实现刚刚Callback实现的功能。

首先在Java中实现使用PromiseReactMethod方法:

...
    @ReactMethod
    public void getSystemModelByPromise(Promise promise){
        String sysModel= Build.MODEL;
        promise.resolve(sysModel);
    }

    @ReactMethod
    public void getSystemVersionByPromise(Promise promise){
        String sysVersion=Build.VERSION.RELEASE;
        promise.resolve(sysVersion);
    }

对应在JavaScript中实现方法如下:

...
import SplashScreen from "./jscode/SplashScreen";

export default class App extends Component<{}> {

    constructor(props){
        super(props);
        this.state={
            systemModel:'',
            systemVersion:'',
        }
    }
    ...

    async componentDidMount(){ //注意此时要声明为`async`表示该方法为异步方法
        ...
        this.setState({
            systemModel:await SplashScreen.getSystemModelByPromise(), //声明await表示等待异步操作结果
            systemVersion:await SplashScreen.getSystemVersionByPromise()
        });
    }

    render() {
        return (
            <View style={styles.container}>
                ...
                <Text style={styles.welcome}>
                    {this.state.systemModel}
                </Text>
                <Text style={styles.welcome}>
                    {this.state.systemVersion}
                </Text>
            </View>
        );
    }

}

运行,同样可以得到我们想要的效果(效果图就不贴了)

通过上面可知,Promise异步操作结果一次只能传递一个值,而无法像Callback一样可以通过可变长度参数一次性传递多个值,必须写多个方法来获取。当然,我们也可以用封装的方式,将我们所需要的值封装成一个对象传给resolve方法。
当然,Promise的使用是为了简化Callback的层层回调,只是在这个例子中没有体现出来,可以说各有利弊。

Promise的缺点

这里也简单说下Promise的缺点:

  1. 无法取消 Promise,一旦新建它就会立即执行,无法中途取消。
  2. 如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。
  3. 当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

好了,今天的文章先到这

参考文献
【React Native】Promise的简单使用
ReactNative中文网——原生模块

2019-02-23 17:38:10 u013003052 阅读数 741
  • 征服ECMAScript6(JavaScript)视频教程

    本课程包含了几乎所有的ECMAScript6核心内容,包括let、const、变量的解构赋值,字符串扩展、正则扩展、数值扩展、数组扩展、函数扩展、对象扩展、Symbol、Proxy和Reflect、二进制数组、Set和Map数据解构、Generator函数、Promise对象、异步操作和async函数、Class、修饰器、Module等内容。

    23234 人正在学习 去看看 李宁


ReactNative系列-文章

Promise 详解

前言

Js代码是单线程运行,这就意味着类似于网络请求/数据库查询等耗时操作,需要放在异步进行,返回结果通过回调函数实现。在js回调函数是这样子:

$jquery.get(‘http://www.baidu.com/,  function(data, status) {
    
});

如果有这样的一个需求,在请求登陆接口成功后,再请求获取当前的用户信息,那么网络请求可能会写成这样:

$jquery.post(/login’, function(data, status) {
  if(status === 200) {
    $jquery.get(/memberInfo?id=1, function(data, status) {
      ……
    })
  }
})

上述代码中,我们需要在第一个请求的回调中,判断请求结果,然后再进行第二请求,如果还有需求需要更多的请求,那么上述代码可能会变成这样子:

$jquery.post(.., function(){
  $jquery.post(.., function(){
    $jquery.post(.., function(){
      $jquery.post(.., function(){
        ……
      })
    })
  })
})

很多的callback嵌套,我们简称回调地狱。Promise的出现,可以解决这个问题。使用promise后,回调地狱可以变为这样子:

$jquery.post(‘address1’)
  .then(function(data, status) {
    // 业务处理
    return $jquery.post(‘address2’);
  })
  .then(function(data, status) {
    return $jquery.post(‘address3’);
  })
  .then(function(data, status) {
    return $jquery.post(‘addres4’);
  })
  .catch(function(err) {

  })

定义

Promise是一个代表了异步操作最终完成或者失败的结果对象。它本质上是一个绑定了回调的对象,而不是将回调传进函数内部。常见的promise调用方式,以axios调用举例:

axios.get(‘http://www.baidu.com’)
  .then(function(res) {
    console.log(res);
  })
  .catch(function(err) {
    console.log(err);
  })

上述get方法在axios库的内部,其实已经将promise封装好了,所以看不到promise相关的单词。那么我们如何创建promise函数,可以如同上述代码那样调用呢?

创建和使用

以封装一个ajax的get请求为例:

function get(address) {
  return new Promise(function(resolve, reject){
    var request = new XMLHttpRequest();
    request.onreadystatechange = function() {
      if(request.readyState === 4) {
        if(request.status === 200) {
          resolve(request.responseText);
        } else {
          reject(request.status);
        }
      }
    };
    request.open('GET', address, true);
    request.send();
  });
}

首先需要在函数体里面创建Promise对象并返回,一般来说是这样的形式: new Promise(function (resolve, reject) {}); 回调函数中有两个参数:resolve, reject。resolve表示程序运行完成,得到正确结果;reject表示程序运行过程中出现错误。然后上述ajax请求promise封装,调用方式:

get('http://www.baidu.com/)
  .then(function(res){
    // 响应的字符串 request.responseText
  })
  .catch(function(e){
    // 错误 request.status
  });

分析:在ajax的异步请求回调里,我们先对请求响应进行判断,如果成功,使用resolve返回结果,它将在.then()函数回调中得到这个结果,如果失败,使用reject返回失败原因,它将在.catch()函数回调中得到原因。

resolve

resolve和reject函数,实际上都是返回promise对象。但是它们内部的状态不一样,分别是:resolve promise和reject promise。而第一个.then能够执行的条件是,“前面”的promise对象中执行了resolve方法。我们可以验证一下:

function get(address) {
  return new Promise(function(resolve, reject){
    var request = new XMLHttpRequest();
    request.onreadystatechange = function() {
      if(request.readyState === 4) {
        if(request.status === 200) {
          // 此处将resolve方法注释
          // resolve(request.responseText);
        } else {
          reject(request.status);
        }
      }
    };
    request.open('GET', address, true);
    request.send();
  });
}
get('http://www.baidu.com/')
  .then(function(res){
    console.log('then1', res);
  })
  .then(function(res){
    console.log('then2', res);
  })
  .catch(function(e){
    console.log('reject', e);
  });

结果:

// 无

结果没有任何输出,程序无法进入then里,我们把上面的函数改一下:

function get(address) {
  return new Promise(function(resolve, reject){
    var request = new XMLHttpRequest();
    request.onreadystatechange = function() {
      if(request.readyState === 4) {
        if(request.status === 200) {
          resolve(request.responseText);
        } else {
          reject(request.status);
        }
      }
    };
    request.open('GET', address, true);
    request.send();
  });
}
get('http://www.baidu.com/')
  .then(function(res){
    console.log('then1', res);
  })
  .then(function(res){
    console.log('then2', res);
  })
  .catch(function(e){
    console.log('reject', e);
  });

结果:

then1  <!DOCTYPE html>。。。。。。
then2  undefined

可以看到第一个then里有回调结果,而第二个then也执行了,但是回调结果是undefined。**通过 .then 形式添加的回调函数,甚至都在异步操作完成之后才被添加的函数,都会被调用,如上所示。**可想而之,.then方法会一直执行下去。如果需要第二个.then拥有回调结果,需要前面的.then返回一个对象或者值。可以验证一下:

get('http://www.baidu.com/')
  .then(function(res){
    console.log('then1', res);
    get('http://www.bing.com/'); // 不使用return返回
  })
  .then(function(res){
    console.log('then2', res);
  })
  .catch(function(e){
    console.log('reject', e);
  });

结果:

then1  <!DOCTYPE html>。。。。。。
then2  undefined

将resolve promise对象使用return返回:

get('http://www.baidu.com/')
  .then(function(res){
    console.log('then1', res);
    return get('http://www.bing.com/'); // 不使用return返回
  })
  .then(function(res){
    console.log('then2', res);
  })
  .catch(function(e){
    console.log('reject', e);
  });

结果:

then1  <!DOCTYPE html>。。。。。。
then2  <!DOCTYPE html>。。。。。。

reject

当程序运行错误,需要返回reject promise,当程序reject的时候,会直接被.catch()函数捕获错误,而不会进入到.then()函数。

get('http://www.baidu.com/')
  .then(function(res){
    console.log('then1', res);
    return Promise.reject('1');
  })
  .then(function(res){
    console.log('then2', res);
  })
  .catch(function(e){
    console.log('reject', e);
  });

结果

then1  <!DOCTYPE html>。。。。。。
reject 1

catch的后续链

当reject进入.catch()函数后,可以继续使用链式操作,类似于Java的try catch后的finally,例如:

get('http://www.baidu.com/')
  .then(function(res){
    console.log('then1', res);
    return Promise.reject('1');
  })
  .then(function(res){
    console.log('then2', res);
  })
  .catch(function(e){
    console.log('reject', e);
  })
  .then(function(){
    console.log('finally');
  });

结果:

then1  <!DOCTYPE html>。。。。。。
reject 1
finally

Promise.all()

Promise.all() 和 Promise.race()是并行运行异步操作的两个组合式工具。

Promise.all()接受Promise对象组成的数组作为参数,它的返回参数也是数组。当promise的全部为resolve后,它才会进入.then()方法,只要有一个promise为reject,它就会进入.catch()。实现如下:

function get(address) {
  return new Promise(function(resolve, reject){
    var request = new XMLHttpRequest();
    request.onreadystatechange = function() {
      if(request.readyState === 4) {
        if(request.status === 200) {
          resolve('resolve');
        } else {
          reject(request.status);
        }
      }
    };
    request.open('GET', address, true);
    request.send();
  });
}
function r() {
  return new Promise(function(resolve,reject){
    reject('reject');
  })
}
Promise.all([get('http://www.baidu.com/'), get('http://www.bing.com/'), r()])
  .then(function(res){
    console.log(JSON.stringify(res));
  })
  .catch(function(e){
    console.log('catch');
  })

结果:

catch

将reject去掉:

Promise.all([get('http://www.baidu.com/'), get('http://www.bing.com/')])
  .then(function(res){
    console.log(JSON.stringify(res));
  })
  .catch(function(e){
    console.log('catch');
  })

结果:

["resolve", "resolve"]

Promise.race()

Promise.race()接受的参数与Promise.all()一样,不同的是,它会辨别最快达到resolve或者reject的promise对象,如果这个最快是resolve,则进入.then()方法,如果是reject,则进入.catch()方法。

function get(address) {
  return new Promise(function(resolve, reject){
    var request = new XMLHttpRequest();
    request.onreadystatechange = function() {
      if(request.readyState === 4) {
        if(request.status === 200) {
          resolve('resolve');
        } else {
          reject(request.status);
        }
      }
    };
    request.open('GET', address, true);
    request.send();
  });
}
function r() {
  return new Promise(function(resolve,reject){
    setTimeout(function() {
      reject('reject');
    }, 5000);
  })
}
Promise.race([get('http://www.baidu.com/'), get('http://www.bing.com/'), r()])
  .then(function(res){
    console.log(JSON.stringify(res));
  })
  .catch(function(e){
    console.log('catch');
  })

结果:

"resolve"

将reject延时函数改为立即:

function r() {
  return new Promise(function(resolve,reject){
    setTimeout(function() {
      reject('reject');
    }, 0);
  })
}
Promise.race([get('http://www.baidu.com/'), get('http://www.bing.com/'), r()])
  .then(function(res){
    console.log(JSON.stringify(res));
  })
  .catch(function(e){
    console.log('catch');
  })

结果:

catch

与async await配合使用

async函数实际是返回一个promise对象,如果在async函数return某值,实际会被包装为promise的_value值,状态为resolve。

await可以让JavaScript进行等待,它会等待一个promise执行并返回它的结果,JavaScript才会继续往下执行。

如上面的get()请求方法,我们可以这样写:

async function asy() {
  try {
    let respond = await get('http://www.baidu.com/');
  } catch(e) {
    console.log('当get方法中执行reject时')
  }
}

respond的赋值会等待get方法中执行resolve,如果get方法里执行了reject,则会直接被catch函数捕捉。

知识细节

  1. resolve和reject函数,实际上都是返回promise对象,它们只是_state状态不同。
  2. 函数.then能够继续执行的条件是,“前面”的promise对象执行resolve方法。
  3. .then()能够一直顺序执行,直到产生reject,它才会中断,进入.catch()。
  4. .then()方法要获得回调参数,必须要前面有返回值。
  5. .catch()的执行条件是,“前面”的promise对象执行reject方法。
  6. async函数实际是返回一个promise对象,return的值会被包装为promise的_value。
  7. await等待的是一个promise对象。
2019-04-11 21:06:38 qczg_wxg 阅读数 53
  • 征服ECMAScript6(JavaScript)视频教程

    本课程包含了几乎所有的ECMAScript6核心内容,包括let、const、变量的解构赋值,字符串扩展、正则扩展、数值扩展、数组扩展、函数扩展、对象扩展、Symbol、Proxy和Reflect、二进制数组、Set和Map数据解构、Generator函数、Promise对象、异步操作和async函数、Class、修饰器、Module等内容。

    23234 人正在学习 去看看 李宁

第一离线缓存技术与promise机制、
promise机制:
1,解决回调监狱、
2,promise有两种结果:做得到(resolve)与做不到(reject)
首先访问本地的缓存,本地缓存为空就访问网络;访问网络要在本地做缓存;下面我们实现这些逻辑

/**
 * Created by penn on 2016/12/21.
 */

import {
    AsyncStorage,
} from 'react-native';

export default class DataRepository {
    constructor() {
    }
    saveRepository(url, data, callback) {
        //如果data或者url不为空
        if (!data || !url)return;
        //把请求得到的数据缓存到本地,并记录缓存时间
        let wrapData = {data: data, update_date: new Date().getTime()};
        // 1,缓存到本地
        // 2,JSON.stringify(wrapData):把对象转换为字符串;
        AsyncStorage.setItem(url, JSON.stringify(wrapData), callback);
    }

    fetchRepository(url) {
        return new Promise((resolve, reject)=> {
            //1,可以理解为在子线程中运行这些代码
            //2,先从本地获取
            //3,只要返回一个Promise对象,那后面肯定跟着then方法;fetchLocalRepository返回
            //的结果,最终传给了后面的then方法。
            this.fetchLocalRepository(url).then((wrapData)=> {
                 //1,wrapData是从本地取到的,如果不为空就执行 resolve(wrapData.data, true);
                if (wrapData) {
                    //1,如果wrapData不为空,就说明本地有数据,本地有数据就从本地获取数据
                    //2, {data: data, update_date: new Date().getTime()};缓存的时候我们把数据丢到data这个键值对里,
                    // 所以这里要取数据的话也要写wrapData.data
                    resolve(wrapData.data, true);
                } else {//为空就是本地没有数据,没数据就去请求网络
                    //凡是返回Promise,后面肯定要加一个then()方法;
                    this.fetchNetRepository(url).then((data)=> {
                        resolve(data);
                    }).catch((error)=> {
                        reject(error);
                    })
                }
                //承诺失败
            }).catch((error)=> {
                this.fetchNetRepository(url).then((data)=> {
                    resolve(data);
                }).catch((error=> {
                    reject(error);
                }))
            })
        })
    }
    fetchLocalRepository(url) {
        return new Promise((resolve, reject)=> {
            //1,使用AsyncStorage做缓存,可以把AsyncStorage当做android中的SharedPrefrece;
            //2,获取缓存数据、
            //3,键使用url地址
            AsyncStorage.getItem(url, (error, result)=> {
                //1,如果没有发生异常
                //2,一般情况下error是为空的。发生错误的时候才不为空
                if (!error) {
                    try {
                        //在这里给Promise赋值
                        resolve(JSON.parse(result));
                    } catch (e) {
                        reject(e);
                        console.error(e);
                    }
                } else {
                    reject(error);
                    console.error(error);
                }
            })
        })
    }
    fetchNetRepository(url) {
        return new Promise((resolve, reject)=> {
                fetch(url)
                    //把response转化为json字符串
                    .then((response)=>response.json())
                    //如果这个时候没有网络,就会回调catch方法。
                    .catch((error)=> {
                        reject(error);
                    }).then((responseData)=> {//如果成功了
                    if (!responseData) {
                        //如果responseData没有取到数据,我们就认为它失败了
                        reject(new Error('responseData is null'));
                        return;
                    }
                    resolve(responseData);
                    //缓存到本地
                    this.saveRepository(url,responseData)
                }).done();
        })
    }
    checkDate(longTime) {
        let currentDate = new Date();
        let targetDate = new Date();
        targetDate.setTime(longTime);
        if (currentDate.getMonth() !== targetDate.getMonth())return false;
        if (currentDate.getDate() !== targetDate.getDate())return false;
        if (currentDate.getHours() - targetDate.getHours() > 4)return false;
        if (currentDate.getMinutes() - targetDate.getMinutes() > 1)return false;
        return true;
    }
}

调用:

var dataRepository = new DataRepository();
  //获取网络数据
        //承诺无论成功还是失败都会走到then方法,
        dataRepository.fetchRepository(`http://192.168.1.111:8090/weixinapp/studentinfo/reactlist`)
        //result就是承诺包含里面的东西,result就是返回的东西
            .then(result=>{
                this.setState({
                    //我们拿到了result对其进行赋值
                    dataList:result,
                    content:result[0].mainTitle,
                    isLoading:false
                })

完整项目代码下载
https://download.csdn.net/download/qczg_wxg/11107525

2018-05-30 23:09:53 pang_gua 阅读数 1883
  • 征服ECMAScript6(JavaScript)视频教程

    本课程包含了几乎所有的ECMAScript6核心内容,包括let、const、变量的解构赋值,字符串扩展、正则扩展、数值扩展、数组扩展、函数扩展、对象扩展、Symbol、Proxy和Reflect、二进制数组、Set和Map数据解构、Generator函数、Promise对象、异步操作和async函数、Class、修饰器、Module等内容。

    23234 人正在学习 去看看 李宁

ReactNative实现键值对的同步存取

官方 AsyncStorage
  • AsyncStorage 是一个简单的、异步的、持久化的 Key-Value 存储系统。
  • 由于是异步的,每个方法都会返回一个 Promise,不满足同步存取需求。
  • 它的使用请参考官方实例
实现同步存取的两种方案
使用静态对象持有
  • 思路分析:

    • 在App启动时,从 AsyncStorage 中异步读取所有键值对,并赋值给静态变量;
    • 调用 setValue(key) 时先将其加入到静态变量中,之后再异步存入 AsyncStorage;
    • 调用 getValue(key) 时直接从静态变量中读取,实现同步存取;
  • 优缺点分析:

    • 优点:代码实现量少,不需要引入其他第三方库做支撑,存储任务实际上仍旧由系统提供的 AsyncStorage 完成。
    • 缺点:由于第一步的 init 读取所有键值对仍然是异步操作,所以需要在 App 初始化时完成这一操作,并且在初始化时不能执行同步读取,因为此时可能静态变量可能并未赋值成功。
  • 代码实现:

    	
    export default class SyncStorage {
    
    	static cache: { [key: string]: string } = {}
    
    	// 初始化需要在App启动时执行
    	static async init() {
        	let keys = await AsyncStorage.getAllKeys()
        	let items = await AsyncStorage.multiGet(keys).then()
        	items.map(([key, value]) => {
            	this.cache[key] = value
        	})
    	}
    
    	static getValue(key: string) {
        	return this.cache[key]
    	}
    
    	static setValue(key: string, value: string) {
        	if (this.cache[key] === value) return
        	this.cache[key] = value
        	AsyncStorage.setItem(key, value)
    	}
    
    	static removeKey(key: string) {
        	delete this.cache[key]
        	AsyncStorage.removeItem(key)
    	}
    }
    
使用realm.js自行封装
  • 思路分析:

    • 由于我们经常有一些需求是在 App 初始化时就要进行 Key-Value 的读取,从而选择UI上的展示内容,所以上面的静态变量持有的方案,并不能满足这种需求。
    • 通过阅读 react-native AsyncStorage 部分的Android实现源码可以发现,facebook 使用了 SQLiteDatabase 来实现 Key-Value 的存取,那么我们也可以自己使用数据库实现一套简单的键值对存取方案。
    • 由于 realm 提供了 ReactNative 版本,集成更加简单高效,所以这里使用 realm 来完成这套方案。
  • 代码实现:

    • 使用 realm 实现存取操作

      /**
       * 定义Key-Value类
       */
      class Config {
      
      	private _key: string
      	private _value: string
      
      	constructor(key: string, value: string) {
      		this._key = key
      		this._value = value
      	}
      
      	get key(): string {
      		return this._key
      	}
      
      	get value(): string {
      		return this._value
      	}
      }
      
      /**
       * 定义Realm-Schema
       */
      const ConfigSchema = {
      	name: Config.name,
      	primaryKey: 'key',
      	properties: {
      	key: 'string',
      	value: 'string'
      	}
      } 
      
      // 获取 realm 存取对象
      const realm = new Realm({schema: [ConfigSchema, MediaSchema]})
      
      /**
       * 使用 realm 实现 Key-Value 存取
       */
      class DBManager {
      
      	static insertConfig(config: Config) {
      		realm.write(() => {
      			realm.create(Config.name, config)
      		})
      	}
      
      	static updateConfig(config: Config) {
      		realm.write(() => {
      			realm.create(Config.name, config, true)
      		})
      	}
      
      	static queryConfig(key: string): Config {
      		return realm.objectForPrimaryKey<Config>(Config.name, key)!
      	}
      }
      
      /**
       * 封装存取 Util,供外界调用
       */
      export default class SyncStorage {
      
      	static putValue(key: string, value: string) {
      		if (DBManager.queryConfig(key) == undefined)
      			DBManager.insertConfig(new Config(key, value))
      		else
      			DBManager.updateConfig(new Config(key, value))
      		}
      
      	static getValue(key: string): string | undefined {
      		let config: Config = DBManager.queryConfig(key)
      		return config == undefined ? undefined : config.value
      	}
      }
      
2019-03-31 22:14:37 qq_30379689 阅读数 334
  • 征服ECMAScript6(JavaScript)视频教程

    本课程包含了几乎所有的ECMAScript6核心内容,包括let、const、变量的解构赋值,字符串扩展、正则扩展、数值扩展、数组扩展、函数扩展、对象扩展、Symbol、Proxy和Reflect、二进制数组、Set和Map数据解构、Generator函数、Promise对象、异步操作和async函数、Class、修饰器、Module等内容。

    23234 人正在学习 去看看 李宁

ES6特性

  • 模块化
  • 箭头函数
  • 函数参数默认值
  • 模板字符串
  • 解构赋值
  • 延展操作符
  • 对象属性简写
  • Promise
  • Let与Const

1、类

引入类的概念,让其具有面向对象的开发

class Person {
    constructor(name,age) {
        this.name = name;
        this.age = age;
    }
}

2、模块化

模块之间的相互调用关系是通过export来规定模块对外暴露的接口,通过import来引用其它模块提供的接口

export var name = 'Rainbow';                        //导出变量
export const sqrt = Math.sqrt;                      //导出常量

var name = 'Rainbow';                               //导出多个变量
var age = '24';
export {name, age};

export function myModule(someArg) {                 //导出函数
  return someArg;
}

export default class MyComponent extends Componet{  //导出组件
    render(){
        <Text>自定义组件</Text>
    }
}

定义好模块的输出以后就可以在另外一个模块通过import引用

import {myModule} from 'myModule';    //导入函数
import {name,age} from 'test';        //导入变量
import MyComponent from 'MyComponent' //导入组件

3、箭头函数

箭头函数与包围它的代码共享同一个this,能帮你很好的解决this的指向问题

()=>{
    alert("foo");
}

错误示范

class PauseMenu extends React.Component{
    componentWillMount(){
        AppStateIOS.addEventListener('change', this.onAppPaused.bind(this));
    }
    componentWillUnmount(){
        AppStateIOS.removeEventListener('change', this.onAppPaused.bind(this));
    }
    onAppPaused(event){
    
    }
}

正确示范

class PauseMenu extends React.Component{
    componentWillMount(){
        AppStateIOS.addEventListener('change', this.onAppPaused);
    }
    componentWillUnmount(){
        AppStateIOS.removeEventListener('change', this.onAppPaused);
    }
    onAppPaused = (event) => {
    
    }
}

4、函数参数默认值

function foo(height = 50, color = 'red'){}

5、模板字符串

var name = `Your name is ${firstname} ${lastname}`

6、解构赋值

从数组中获取值

[a, b] = [1, 2];
console.log(a); // 1
console.log(b); // 2

[a=5, b=7] = [1];
console.log(a); // 1
console.log(b); // 7

交换两个变量的值

var a = 1;
var b = 3;

[a, b] = [b, a];
console.log(a); // 3
console.log(b); // 1

获取对象中的值

const student = {
  name:'Ming',
  age:'18',
  city:'Shanghai'
};

const {name,age,city} = student;
console.log(name); // "Ming"
console.log(age);  // "18"
console.log(city); // "Shanghai"

7、延展操作符

延展操作符...,可以对数组、对象、string进行展开操作

myFunction(...iterableObj);             //对函数展开
[...iterableObj, '4', ...'hello', 6];   //对数组展开
let objClone = { ...obj };              //对对象展开

对数组展开

var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
var arr3 = [...arr1, ...arr2]; // 将 arr2 中所有元素附加到 arr1 后面并返回

对对象展开

var params = {
	name: '123',
	title: '456',
	type: 'aaa'
}

var { type, ...other } = params;

<CustomComponent type='normal' number={2} {...other} />
//等同于
<CustomComponent type='normal' number={2} name='123' title='456' />

8、对象属性简写

简写前

const name='Ming',age='18',city='Shanghai';

const student = {
    name:name,
    age:age,
    city:city
};
console.log(student);

简写后

const name='Ming',age='18',city='Shanghai';

const student = {
    name,
    age,
    city
};
console.log(student);

9、Promise

Promise是异步编程的一种解决方案,在不使用Promise的时候需要嵌套多层代码

setTimeout(function()
{
    console.log('Hello'); // 1秒后输出"Hello"
    setTimeout(function()
    {
        console.log('Hi'); // 2秒后输出"Hi"
    }, 1000);
}, 1000);

使用Promise后,只需要通过then操作符进行操作

var waitSecond = new Promise(function(resolve, reject)
{
    setTimeout(resolve, 1000);
});

waitSecond
    .then(function()
    {
        console.log("Hello"); // 1秒后输出"Hello"
        return waitSecond;
    })
    .then(function()
    {
        console.log("Hi"); // 2秒后输出"Hi"
    });

10、Let与Const

const与let都是块级作用域

{
  var a = 10;   // 全局作用域
}
console.log(a); // 输出10

{
  let a = 10;   // const或let,块级作用域
}
console.log(a); //-1 or Error“ReferenceError: a is not defined”

ES7特性

  • includes()
  • 指数操作符

1、includes()

includes()函数用来判断一个数组是否包含一个指定的值

arr.includes(x)
//等同于
arr.indexOf(x) >= 0   

2、指数操作符

引入了指数运算符****具有与Math.pow(..)等效的计算结果

console.log(Math.pow(2, 10)); // 输出1024
console.log(2**10);           // 输出1024

ES8特性

  • async/await
  • Object.values()
  • Object.entries()
  • String padding
  • 函数参数列表结尾允许逗号
  • Object.getOwnPropertyDescriptors()

1、async/await

async/await是异步函数,结合Promise,在使用上使整个代码看起来很简洁

//登陆用户
login(userName) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('1001');
        }, 600);
    });
}
//获取用户数据
getData(userId) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if (userId === '1001') {
                resolve('Success');
            } else {
                reject('Fail');
            }
        }, 600);
    });
}

// 不使用async/await
doLogin(userName) {
    this.login(userName)
        .then(this.getData)
        .then(result => {
            console.log(result)
        })
}

// 使用async/await
async doLogin(userName) {
    const userId=await this.login(userName);
    const result=await this.getData(userId);
}
doLogin('Hensen').then(console.log); //通过then获取异步函数的返回值

async/await支持并发操作,我们通过Promise.all来实现await的并发调用

async function charCountAdd(data1, data2) {
    const [d1,d2]=await Promise.all([charCount(data1),charCount(data2)]);
    return d1+d2;
}

charCountAdd('Hello','Hi')
    .then(console.log)
    .catch(console.log); //捕捉整个async/await函数的错误

function charCount(data) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(data.length);
        }, 1000);
    });
}

2、Object.values()

遍历对象的所有值

const obj = {a: 1, b: 2, c: 3};

const vals=Object.keys(obj).map(key=>obj[key]);
console.log(vals);//[1, 2, 3]

const values=Object.values(obj1);
console.log(values);//[1, 2, 3]

3、Object.entries()

遍历对象的所有key和value

Object.keys(obj).forEach(key=>{
	console.log('key:'+key+' value:'+obj[key]);
})

for(let [key,value] of Object.entries(obj1)){
	console.log(`key: ${key} value:${value}`)
}

4、String padding

PadStartPadEnd函数可向左、右填充字符串(如空格),如果目标长度太短则不填充,如果目标长度有多余的空位,则填补参数padString的值

// String.prototype.padStart(targetLength [, padString])
'hello'.padStart(10); // '     hello'
'hello'.padStart(10, '0'); // '00000hello'
'hello'.padStart(); // 'hello'
'hello'.padStart(6, '123'); // '1hello'
'hello'.padStart(3); // 'hello'
'hello'.padStart(3, '123'); // 'hello';

// String.prototype.padEnd(targetLength [, padString])
'hello'.padEnd(10); // 'hello     '
'hello'.padEnd(10, '0'); // 'hello00000'
'hello'.padEnd(); // 'hello'
'hello'.padEnd(6, '123'); // 'hello1'
'hello'.padEnd(3); // 'hello'
'hello'.padEnd(3, '123'); // 'hello';

5、函数参数列表结尾允许逗号

var f = function(a,b,c,d,) {}

6、Object.getOwnPropertyDescriptors()

Object.getOwnPropertyDescriptors()获取一个对象的所有自身属性的描述符

const obj2 = {
	name: 'Jine',
	get age() { return '18' }
};
Object.getOwnPropertyDescriptors(obj2)

Prop

Prop可以理解为组件中的属性,它可以通过外界传递进来的参数,类似于构造函数的参数

1、属性参数

使用自定义组件的时候,传递参数到自定义组件中

<View>
    <PropsText
        name = "小明",
        age = 18
    />
</View>

2、默认属性

在React中可以提供默认参数defaultProps属性

class PropsText extends Component{
    
    static defaultProps = {
        name: "小俊",
        age: 18
    }
    
    render(){
        <Text>Hello{this.props.name}</Text>
    }
}

3、属性检测

在React中的参数中可以增加类型判断propTypes属性,如果类型不准确,则会报错通知开发者

class PropsText extends Component{
    
    static defaultProps = {
        name: "小俊",
        age: 18
    }
    
    static propTypes = {
        name:PropTypes.string,
        number:PropTypes.number.isRequired
    }
    
    render(){
        <Text>Hello{this.props.name}</Text>
    }
}

State

State可以理解为组件中的成员变量,通过改变成员变量的值去更新组件

1、初始化State

通过getInitialState()初始化state,在组件的生命周期中仅执行一次

getInitialState(){
    return {
        favorite:false
    };
}

2、更新State

通过this.setState()方法来更新state,组件就会重新渲染,执行render()

handleClick:function(event){
    this.setState({
        favorite:!this.state.favorite
    });
},

render(){
    var text=this.state.favorite? 'favorite':'un favorite';
    return (
      <div type='button' onClick={this.handleClick}>
        You {text} this. Click to toggle.
      </div>
    );
}

ref

ref可以作为Dom节点的标识,可以获取到某个Dom节点

1、获取组件实例

<View>
    <Text onPress={()=>{
        this.refs.refText  //获取组件实例
        this.refs[refText] //获取组件实例
    }}
    <Text ref="refText"/>
</View>

2、获取组件方法

<View>
    <Text onPress={()=>{
        this.refs.refText.getSize();  //获取组件方法
        this.refs[refText].getSize(); //获取组件方法
    }}
    <Text ref="refText"/>
</View>

this

在方法中使用this对象,会报错找不到,原因是这里的this指的是当前_onPressItem方法中的对象

_onPressItem() {
    let navigator = this.props.navigator;
}

解决方法是在构造函数中将当前的方法的this对象进行绑定

constructor(props) {
    super(props);

    this.state = {}
    this._onPressItem = this._onPressItem.bind(this);
}

或者在使用该方法的时候直接使用箭头函数,能自动将this对象进行绑定

<TouchableOpacity onPress={()=>{
    let navigator = this.props.navigator
}}>

</TouchableOpacity>

组件方法

1、render()

该方法表示组件创建的时候进行渲染的回调函数。它会检测this.propsthis.state,并返回一个单子级组件

2、getInitialState()

该方法表示初始化组件状态,在组件挂载之前调用一次

3、getDefaultProps()

该方法表示设置组件属性的默认值,在组件类创建的时候调用一次

4、propTypes

该对象用于验证传入到组件的props的类型

5、statics

该对象允许你定义静态的方法,这些静态的方法可以在组件类上调用。这些方法不能获取组件的propsstate。如果你想在静态方法中检查props的值,在调用处把props作为参数传入到静态方法

class MyComponent extends Componet{
  
  statics: {
    customMethod: function(foo) {
      return foo === 'bar';
    }
  }
  
  render: function() {
  
  }
}

6、displayName

该字符串用于输出调试信息

7、isMounted()

该方法通常用于异步任务完成后修改state前的检查,以避免修改一个没有被渲染的组件的state。当组件被渲染到DOM,该方法返回true,否则返回false

组件生命周期

1、Mounting(装载)

  • getInitialState(): 在组件挂载之前调用一次。返回值将会作为this.state的初始值
  • componentWillMount():服务器端和客户端都只调用一次,在初始化渲染执行之前立刻调用
  • componentDidMount():在初始化渲染执行之后立刻调用一次,仅客户端有效

2、Updating(更新)

  • componentWillReceiveProps():在组件接收到新的props的时候调用
  • shouldComponentUpdate():在接收到新的props或者state
  • componentWillUpdate():在接收到新的 props 或者 state 之前立刻调用
  • componentDidUpdate():在组件的更新已经同步到DOM中之后立刻被调用

3、Unmounting(移除)

  • componentWillUnmount():在组件从DOM中移除的时候立刻被调用

4、完整生命周期

在这里插入图片描述

持久化存储

1、基础使用

ReactNative提供AsyncStorage用于持久化保存key-value

  • static getItem(key: string, callback:(error, result)):根据键来获取值,获取的结果会在回调函数中
  • static setItem(key: string, value: string, callback:(error)):根据键来设置值
  • static removeItem(key: string, callback:(error)):根据键来移除项
  • static mergeItem(key: string, value: string, callback:(error)):合并现有值和输入值
  • static clear(callback:(error)):清除所有的项目
  • static getAllKeys(callback:(error, keys)):获取所有的键
  • static flushGetRequests():清除所有进行中的查询操作
  • static multiGet(keys, callback:(errors, result)):获取多项数据,keys是字符串数组
  • static multiSet(keyValuePairs, callback:(errors)):设置多项,keyValuePairs是字符串的二维数组
  • static multiRemove(keys, callback:(errors)):删除多项,keys是字符串数组
  • static multiMerge(keyValuePairs, callback:(errors)):多个键值合并,keyValuePairs是字符串的二维数组

2、封装使用

import {
   AsyncStorage
}from 'react-native';

export default class StorageUtils{

	static get(key) {
       	return AsyncStorage.getItem(key).then((value) => {
         	const jsonValue = JSON.parse(value);
         	return jsonValue;
        });
 	}
 	
 	static save(key, value) {
 		return AsyncStorage.setItem(key, JSON.stringify(value));
 	}
 	
 	static update(key, value) {
 		return DeviceStorage.get(key).then((item) => {
         	value = typeof value === 'string' ? value : Object.assign({}, item, value);
            return AsyncStorage.setItem(key, JSON.stringify(value));
        });
 	}
 	
 	static delete(key) {
 		return AsyncStorage.removeItem(key);
 	}
}

布局

1、像素

在React Native中尺寸是没有单位的,它代表了设备独立像素。运行在Android上时,长和宽的尺寸单位被解释为dp,字体的尺寸单位被解释为sp,运行在iOS上时,尺寸单位被解释为pt

2、flexBox

约束父视图的属性

  • flexDirection:属性定义了父视图中的子元素沿横轴或侧轴方片的排列方式
  • flexWrap:属性定义了子元素在父视图内是否允许多行排列,默认为nowrap
  • justifyContent:属性定义了子元素的主轴的对齐方式
  • alignItems:属性定义了子元素的侧轴的对齐方式

约束子视图的属性

  • alignSelf:属性定义了flex容器内被选中项目的对齐方式
  • flex:属性当前元素的位置的占比权重,默认为0

其他属性

  • 尺寸:可以定义容器的宽高
  • 边框:可以定义容器的上下左右的边框和颜色
  • 内边距:可以定义容器的上下左右的内边距
  • 外边距:可以定义容器的上下左右的外边距
  • 定位:可以定义容器的绝对位置或相对位置

调试

1、日志调试

可以通过不同级别的日志就行输出调试

console.warn()
console.error()

2、Chrome调试

在开发中遇到最大的问题是Window下的Chrome调试,尝试了很久后终于找到可调试的办法

  1. 摇一摇打开调试菜单,选择Debug JS Remotely
  2. 在弹出来的Chrome中将ip地址改为localhost,刷新Chrome
  3. 摇一摇打开调试菜单,选择Reload
  4. F12打开开发者界面,进行断点调试

ReactNative await和async

阅读数 1753

没有更多推荐了,返回首页