react-native_react-native-vector-icons - CSDN
  • 完全征服React Native

    2018-10-22 21:38:05
    React Native是Facebook于2015年推出的跨平台开发...本课程采用新的ES6开发,主要内容包括ReactNative的基础知识,ReactNative的布局,组件,API,封装本地API和组件,发布ReactNative App,本地与ReactNative深度结合
  • 刚刚度过了繁忙的一个月,连续不断的需求让自己有点招架不住了。写的代码质量有些堪忧,又导致不断的修改bug,陷入了恶性循环中了,不过随着最近最后一个需求即将完结,终于抽空写写rn相关的内容了。...

    刚刚度过了繁忙的一个月,连续不断的需求让自己有点招架不住了。写的代码质量有些堪忧,又导致不断的修改bug,陷入了恶性循环中了,不过随着最近最后一个需求即将完结,终于抽空写写rn相关的内容了。这个标题略有写浮夸,主要是为了吸引眼球,也有自己感受的原因。

    一、rn历史简介

    大家都知道rn是facebook开源的一个框架,不过关于rn的历史大家可能不太清楚。facebook在客户端2.0版本的时候,将大部分页面使用web技术实现,当时大概是2011年,android还在2.3版本、ios还在5.0版本。可想而知,结果当然是扑街了,在当时网页在手机上的体验相当的糟糕,用户吐槽声一片,facebook只能迅速的换成原生的实现。不得不说虽然是一次失败的尝试,但是facebook算是混合应用的先驱者和探索者,这也为后来facebook开发rn打下了基础。
    rn的idea是在2013年的一个极客大会上提出的,2014年7月facebook内部开始尝试使用这项技术,到了2015年3月,rn的ios版本正式开源,到了同年9月,rn的android版本也开源了。大概的发展历程如下:
    这里写图片描述
    rn虽然开源了接近了3年的时间了,但依然还没有到达1.0的正式版本,目前到了0.54版本,期待2021年1.0版本上线的时候…….不得不说,rn是历史上第一个没到正式版本,github却有6w+星星的项目,大写的服气。

    二、rn原理简介

    这里写图片描述
    盗了一张别人的android端架构图(侵删),rn的架构分为3层,java/oc层、c++层以及js层。这个结构和app内嵌h5基本一致,同样涉及到js和native通信等,只是将webview层替换为了c++层。

    三、前端上手rn

    1、rn对前端意味着什么

    我个人的理解是rn能帮助前端开发者使用熟悉的方式开发出一款接近原生体验的App,前提是你对react比较熟悉。接近原生体验要从两个方面来说这个问题,一个方面是rn的实现方式就决定了rn开发出的app只能是接近原生的体验,另一方面是前端同学一般对客户端开发不熟悉,在某些需要客户端方面进行支持优化的地方很难去处理。

    2、前端上手难度

    会React开发就能很快上手RN,还可以使用React全家桶哦。rn直接和react开发有很多相似的地方,同时也可以用是redux等react常用的第三方库,上手难度不大。

    3、前端遇到的小问题

    • 元素超过一屏不滚动
      web端的页面天生会滚动,如果元素超过了一屏就会出现滚动条,但是客户端上元素超过一屏是不会滚动的,需要在最外层套上一个能滚动的元素,比如scrollview或则listview等等。
    • ios和android的平台差异
      虽然rn尽量处理了android和ios两端之前的差异,但是还是两端还是有一些不一致的地方,比如有些控件的属性只在某一端能够使用,这个时候我们尽量避免使用这样的属性,采用两端效果能一致的实现方式来处理
    • 长列表的选择
      rn目前已经提供了listview和flatList两种长列表的方式,listview因为性能问题基本被放弃使用了,所以在选择长列表的时候尽量选择flatlist或者其他开源的列表组件。

    四、rn打开的正确方式

    这里写图片描述
    这是看到别人的博客上写的一段话,看了之后挺有感触的,因为自己在写rn的时候也觉得这是一个需要多方合作的一种开发模式。之前了解到携程好像将客户端和前端合并为大前端团队了,客户端和前端的同学一起协作开发。客户端的同学在app容器处理上肯定是最好的选择,但是一般在react开发上面不是很熟悉,前端同学明显在react的开发上比客户端同学更熟悉和了解,但是一般对客户端的开发比较陌生。双方可以完成自己更擅长的部分,同时可以相互学习各自领域的内容,这是一种极好的提升的方式。

    五、存在的问题

    1、不断更新的版本

    这里写图片描述
    这是rn版本的截图,3年时间已经更新了四十几个版本,不同版本之间差距还是挺大的,所以如何做好rn版本管理以及升级是一个非常重要的工作,主要是兼容性问题。

    2、问题排查困难

    这里写图片描述
    这是做rn经常会看到的一张图,大红的背景里提示你出现了错误,虽然说有调试工具可以帮助你定位错误,但是和web开发不同,rn报错有可能是前端爆的错误,也可能是客户端的错误,问题排查起来会比较苦难。

    3、长列表及动画性能不佳

    • 长列表快速滚动时会出现白屏的情况
      长列表快速向上滚动时,列表会出现白屏的情况。这主要是由于滚动时native和js频繁通信,导致客户端渲染不及时所导致的问题。目前还没有特别好的解决办法,有些公司会尽量避免在rn上使用长列表来规避这样的问题,如果列表内容比较简单的话,白屏出现的可能性会比较小。
    • 动画效果出现卡顿(特别是android手机上)
      rn的实现出来的动画会比较卡顿,原因和上一个问题基本相同。可能得解决方法是将动画完全由原生实现,js端直接使用原生动画,这样就可以避免动画卡顿的问题。

    4、首页白屏问题

    打包成Bundle包后一般会有几M,甚至十几M,导致首次渲染时白屏时间很长。
    在最新款的手机上,白屏的时间很短,然而在低版本的android手机上白屏时间缺非常的长。当然首页白屏问题现在已经有很多方案进行处理,比如拆包和预加载,这两种都是比较常见的处理方案了。

    5、优秀的热更新方案

    • 自搭还是第三方?
      一般公司都会选择自己搭建热更新框架,比较很少有公司愿意把这种更新的业务交由其他公司来完成,小公司的话可以尝试使用微软的热更新方案。
    • 如何做好兼容性处理?
      热更新也会导致兼容性问题,比如在之前的bundle包里没有这个功能,或者功能的结构发生变化,导致老版本无法正常使用新版本的内容。可能得解决方案有:
      1、控制版本,不兼容的版本就不进行热更新
      2、对客户端开方的接口进行封装,屏蔽客户端内部的实现
      3、不使用或者尽量少使用热更新方案,将rn作为一个类似原生模块嵌入到app中,每次发布版本才更新一次rn包
    • 傲娇的苹果
      苹果之前封杀过JSPatch等热跟新方案,没准苹果有出台什么规则不允许rn之类的热更新,所以也得做好心理准备。

    到这就写的差不用多了,最近rn的尝试也是挺有意义的,虽然开发流程等并没有太多新的东西,但是对rn开发流程、整体的结构等有了更深入的了解,之后准备再写写有关rn框架相关的东西,更深入的去了解这个框架。目前来说,rn还不是一个比较成熟的框架,在大型项目中的应用还需要更加谨慎的看待,所以最后点题,rn从入门到放弃,期待1.0版本的到来。

    over…

    展开全文
  • 移动端应用高速发展, 本教程便是使用ReactNative开发的高性能基于原生应用跨Android与iOS两大平台的大型综合App。 本实战项目使用react native 开发招聘,房产,点餐,商城,二手车,本地商务的大型综合门户性...
  • 从2016年7月份开始,坚持写ReactNative系列博客,记录工作中遇到的点滴。今天把博客汇如下:《React-Native系列》1、初探React-Native《React-Native系列》2、RN与native交互与数据传递《React-Native系列》3、RN与...

    从2016年7月份开始,坚持写ReactNative系列博客,记录工作中遇到的点滴。

    今天把博客汇总如下:

    1. 《React-Native系列》1、初探React-Native
    2. 《React-Native系列》2、RN与native交互与数据传递
    3. 《React-Native系列》3、RN与native交互之Callback、Promise
    4. 《React-Native系列》4、表单界面代码编写
    5. 《React-Native系列》5、RN实现弹出选择界面与动画效果
    6. 《React-Native系列》6、Navigator语法介绍及经典应用
    7. 《React-Native系列》7、bundle文件的加载和维护
    8. 《React-Native系列》8、RN如何打离线包
    9. 《React-Native系列》9、 Networking之fetch
    10. 《React-Native系列》10、 RN组件之Text和TextInput以及注意要点
    11. 《React-Native系列》11、 图解RN布局之FlexBox,三分钟上手写RN界面
    12. 《React-Native系列》12、 API模块之PixelRatio和Dimensions
    13. 《React-Native系列》13、 组件封装之Dialog(iOS和Android通用)
    14. 《React-Native系列》14、 RN学习之NodeJS
    15. 《React-Native系列》15、 RN之可触摸组件
    16. 《React-Native系列》16、 RN组件之ListView
    17. 《React-Native系列》17、 RN中this所引起的undefined is not an object错误
    18. 《React-Native系列》18、 RN之定时器Timer
    19. 《React-Native系列》19、 ListView组件之上拉刷新(iOS和Android通用)
    20. 《React-Native系列》20、 RN数据流之Flux概览
    21. 《React-Native系列》21、 解决RN在Android下不支持gif问题
    22. 《React-Native系列》22、 Flux框架Demo详解
    23. 《React-Native系列》23、 js实现下拉刷新效果(Android和iOS通用)
    24. 《React-Native系列》24、 结合Demo学习Redux框架
    25. 《React-Native系列》25、 详解Redux的connect方法
    26. 《React-Native系列》26、 ReactNative实现图片上传功能
    27. 《React-Native系列》27、 Redux的异步数据流
    28. 《React-Native系列》28、 RN之AsyncStorage
    29. 《React-Native系列》29、 RN组件之WebView
    30. 《React-Native系列》30、 RN组件间通信
    31. 《React-Native系列》31、 Fetch发送POST请求的坑与解决方案
    32. 《React-Native系列》32、 基于Fetch封装HTTPUtil工具类
    33. 《React-Native系列》33、 键盘遮挡问题处理
    34. 《React-Native系列》34、 ReactNative的那些坑
    35. 《React-Native系列》35、 RN在Android下支持gif的另一种方案
    36. 《React-Native系列》36、 ReactNative地图组件
    37. 《React-Native系列》37、 ReactNative百度地图开源组件使用
    38. 《React-Native系列》38、 ReactNative混合组件封装
    39. 《React-Native系列》39、 ReactNative之键盘Keyboard
    40. 《React-Native系列》40、 ReactNative之bundle文件瘦身
    41. 《React-Native系列》41、刨根问底Picker组件
    42. 《React-Native系列》42、键盘遮挡问题官方处理方法KeyboardAvoidingView
    43. 《React-Native系列》43、通用容器和导航设计方案
    44. 《React-Native系列》44、基于多个TextInput的键盘遮挡处理方案优化
    45. 《React-Native系列》45、踩坑记录

    展开全文
  • React Native 之Android混合开发,及遇到的各种坑
    展开全文
  • React源码 React16源码之React Fiber架构 从源码看React异常处理 从源码看React.PureComponent ...web移动端布局的那些...React Native转web方案:react-native-web React Native Icon方案:react-native-svg ...

    原文地址:github.com/HuJiaoHJ/bl…

    React源码

    React16源码之React Fiber架构

    从源码看React异常处理

    从源码看React.PureComponent

    实践总结

    web移动端布局的那些事儿

    React Native转web方案:react-native-web

    React Native Icon方案:react-native-svg

    React Native 异常处理

    工具开发

    【Node】简单快捷的图片压缩脚本

    Chrome Devtools 开发之消息通信

    时间

    2018年9月

    从源码看React.PureComponent

    React Native 异常处理

    从源码看React异常处理

    【Node】简单快捷的图片压缩脚本

    React Native Icon方案:react-native-svg

    2018年8月

    react native转web方案:react-native-web

    React16源码之React Fiber架构

    chrome devtools 开发之消息通信

    web移动端布局的那些事儿

    本文将从三个方面分享 react native 转 web 方案:react-native-web

    • react-native-web 的使用
    • react-native-web 源码分析
    • react-native-web 实践

    react-native-web:github.com/necolas/rea…

    使用

    安装

    yarn add react react-dom react-native-web
    复制代码
    

    如果使用了 ART,需要安装 react-art(比如,使用了 react-native-svg 来做RN端icon方案,这就是基于 react-art)

    yarn add react-art
    复制代码
    

    安装好之后,使用主要分一下两步:

    • webpack配置
    • 入口处新增配置

    webpack配置

    webpack配置就跟普通 React web 应用配置一致即可,然后新增alias配置,如下:

    // webpack.config.js
    module.exports = {
      // ...the rest of your config
      resolve: {
        alias: {
          'react-native$': 'react-native-web'
        }
      }
    }
    复制代码
    

    入口处新增配置

    有两种方式:

    • 使用 AppRegistry API
    • 使用 render 方法

    使用 AppRegistry API

    在新增配置之前,首先看看RN的入口文件:

    // index.js
    import { AppRegistry } from 'react-native';
    import App from './App';
    
    AppRegistry.registerComponent('rn_web', () => App);
    复制代码
    

    新增配置之后,如下:

    // index.web.js
    import { AppRegistry } from 'react-native';
    import App from './App';
    
    AppRegistry.registerComponent('rn_web', () => App);
    
    AppRegistry.runApplication('rn_web', {
        rootTag: document.getElementById('react-root')
    });
    复制代码
    

    使用 render 方法

    使用 render 方法如下:

    import { render } from 'react-native';
    import App from './App';
    
    render(<App/>, rootTag: document.getElementById('react-root'));
    复制代码
    

    可以看到,AppRegistry API 更贴近RN的写法,render 方法跟 ReactDOM.render 是一个意思。

    以上,就能够将现有RN页面转成web页面了

    接下来,以 AppRegistry API 为入口,看看 react-native-web 做了什么

    react-native-web 源码分析

    从三部分来对源码进行分析:

    • 入口,即 AppRegistry API
    • API,即对 RN API 实现
    • 组件,即对 RN 组件实现

    入口:AppRegistry API

    入口文件代码:

    // index.web.js
    import { AppRegistry } from 'react-native';
    import App from './App';
    
    AppRegistry.registerComponent('rn_web', () => App);
    
    AppRegistry.runApplication('rn_web', {
        rootTag: document.getElementById('react-root')
    });
    复制代码
    

    那我们来来看看这两个 API 都做了什么

    AppRegistry.registerComponent

    const runnables = {};
    static registerComponent(appKey: string, componentProvider: ComponentProvider): string {
        runnables[appKey] = {
            getApplication: appParameters => getApplication(componentProviderInstrumentationHook(componentProvider), appParameters ? appParameters.initialProps : emptyObject, wrapperComponentProvider && wrapperComponentProvider(appParameters)),
            run: appParameters => renderApplication(componentProviderInstrumentationHook(componentProvider), appParameters.initialProps || emptyObject, appParameters.rootTag, wrapperComponentProvider && wrapperComponentProvider(appParameters), appParameters.callback)
        };
        return appKey;
    }
    复制代码
    

    以例子代码为例,此方法就是定义了 runnables['rn_web'] 对象,此对象有 getApplication、run 两个方法

    AppRegistry.runApplication

    static runApplication(appKey: string, appParameters: Object): void {
        runnables[appKey].run(appParameters);
    }
    复制代码
    

    以例子代码为例,此方法就是调用了

    runnables['rn_web'].run({
        rootTag: document.getElementById('react-root')
    })
    复制代码
    

    这里的 appParameters 数据结构如下:

    {
        initialProps, // 初始props
        rootTag, // root DOM节点
        callback, // 回调函数
    }
    复制代码
    

    renderApplication

    import { render } from 'react-dom';
    const renderFn = render;
    function renderApplication<Props: Object>(RootComponent: ComponentType<Props>, initialProps: Props, rootTag: any, WrapperComponent?: ?ComponentType<*>, callback?: () => void) {
        renderFn(
            <AppContainer WrapperComponent={WrapperComponent} rootTag={rootTag}>
                <RootComponent {...initialProps} />
            </AppContainer>,
            rootTag,
            callback
        );
    }
    复制代码
    

    实际调用的是:

    ReactDOM.render(
        <AppContainer WrapperComponent={WrapperComponent} rootTag={rootTag}>
            <App {...initialProps} />
        </AppContainer>,
        rootTag,
        callback
    );
    复制代码
    

    AppContainer

    export default class AppContainer extends Component<Props, State> {
      state = { mainKey: 1 };
    
      static childContextTypes = {
        rootTag: any
      };
    
      static propTypes = {
        WrapperComponent: any,
        children: node,
        rootTag: any.isRequired
      };
    
      getChildContext(): Context {
        return {
          rootTag: this.props.rootTag
        };
      }
    
      render() {
        const { children, WrapperComponent } = this.props;
        let innerView = (
          <View
            children={children}
            key={this.state.mainKey}
            pointerEvents="box-none"
            style={styles.appContainer}
          />
        );
    
        if (WrapperComponent) {
          innerView = <WrapperComponent>{innerView}</WrapperComponent>;
        }
        return (
          <View pointerEvents="box-none" style={styles.appContainer}>
            {innerView}
          </View>
        );
      }
    }
    
    const styles = StyleSheet.create({
      appContainer: {
        flex: 1
      }
    });
    复制代码
    

    API

    以 StyleSheet 为例,分析 react-native-web API 源码

    我们都知道,RN中使用的样式表是CSS的子集,我们来看看 react-native-web 对样式表的处理

    StyleSheet

    const StyleSheet = {
      absoluteFill,
      absoluteFillObject,
      compose(style1, style2) {
        ...
      },
      create(styles) {
        ...
      },
      flatten: flattenStyle,
      hairlineWidth: 1
    };
    复制代码
    

    RN的StyleSheet模块有以下几个方法和常量:

    1、方法:

    • setStyleAttributePreprocessor(此方法存在风险)
    • create
    • flatten

    2、常量:

    • hairlineWidth
    • absoluteFill
    • absoluteFillObject

    可以发现,react-native-web 中 StyleSheet 定义了除 setStyleAttributePreprocessor(此方法存在风险)方法之外的所有方法和常量。此外,还新增了 compose 方法,此方法在 react-native-web 的组件中使用

    首先来看看 StyleSheet.create 方法

    StyleSheet.create

    create(styles) {
      const result = {};
      Object.keys(styles).forEach(key => {
        const id = styles[key] && ReactNativePropRegistry.register(styles[key]);
        result[key] = id;
      });
      return result;
    }
    复制代码
    

    代码比较简单,主要就是遍历styles,对所有styles调用 ReactNativePropRegistry.register 获取对应的id,返回对应 key-id 的对象。我们先看个例子:

    const 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,
      },
      ellipsis: {
        width: 200,
      }
    });
    
    console.log(styles);
    复制代码
    

    我们来看看打印出来的styles是什么?

    {container: 78, welcome: 79, instructions: 80, ellipsis: 81}
    复制代码
    

    接着来看看 ReactNativePropRegistry.register 做了什么

    ReactNativePropRegistry

    const emptyObject = {};
    const objects = {};
    const prefix = 'r';
    let uniqueID = 1;
    
    const createKey = id => `${prefix}-${id}`;
    
    export default class ReactNativePropRegistry {
      static register(object: Object): number {
        const id = uniqueID++;
        if (process.env.NODE_ENV !== 'production') {
          Object.freeze(object);
        }
        const key = createKey(id);
        objects[key] = object;
        return id;
      }
    
      static getByID(id: number): Object {
        if (!id) {
          return emptyObject;
        }
        const key = createKey(id);
        const object = objects[key];
        if (!object) {
          return emptyObject;
        }
        return object;
      }
    }
    复制代码
    

    这个模块,定义了两个方法:register、getByID,register 是将样式对象存入 objects 对象中,并返回对应的 id;getByID 则是通过 id 获取对应的样式对象

    在react-native-web整个样式转换过程中,除了StyleSheet.create,还需要关注一下 StyleSheet.flatten 方法,即 flattenStyle

    flattenStyle

    function getStyle(style) {
      if (typeof style === 'number') {
        return ReactNativePropRegistry.getByID(style);
      }
      return style;
    }
    
    function flattenStyle(style: ?StyleObj): ?Object {
      if (!style) {
        return undefined;
      }
    
      if (!Array.isArray(style)) {
        return getStyle(style);
      }
    
      const result = {};
      for (let i = 0, styleLength = style.length; i < styleLength; ++i) {
        const computedStyle = flattenStyle(style[i]);
        if (computedStyle) {
          for (const key in computedStyle) {
            const value = computedStyle[key];
            result[key] = value;
          }
        }
      }
      return result;
    }
    复制代码
    

    flattenStyle 方法接受的 styles 参数是存有样式表id的数组或变量,通过递归遍历 styles,调用上一部分提到的 ReactNativePropRegistry.getByID 方法,通过id获取对应的样式对象,并返回。

    以上,我们以 StyleSheet 为例分析了 react-native-web 实现 RN API 的源码。

    组件

    以 View 组件为例,分析 react-native-web 组件的源码

    const calculateHitSlopStyle = hitSlop => {
      const hitStyle = {};
      for (const prop in hitSlop) {
        if (hitSlop.hasOwnProperty(prop)) {
          const value = hitSlop[prop];
          hitStyle[prop] = value > 0 ? -1 * value : 0;
        }
      }
      return hitStyle;
    };
    
    class View extends Component<ViewProps> {
      static displayName = 'View';
    
      static contextTypes = {
        isInAParentText: bool
      };
    
      static propTypes = ViewPropTypes;
    
      render() {
        const hitSlop = this.props.hitSlop;
        const supportedProps = filterSupportedProps(this.props);
    
        const { isInAParentText } = this.context;
    
        supportedProps.style = StyleSheet.compose(
          styles.initial,
          StyleSheet.compose(isInAParentText && styles.inline, this.props.style)
        );
    
        if (hitSlop) {
          const hitSlopStyle = calculateHitSlopStyle(hitSlop);
          const hitSlopChild = createElement('span', { style: [styles.hitSlop, hitSlopStyle] });
          supportedProps.children = React.Children.toArray([hitSlopChild, supportedProps.children]);
        }
    
        return createElement('div', supportedProps);
      }
    }
    
    const styles = StyleSheet.create({
      // https://github.com/facebook/css-layout#default-values
      initial: {
        alignItems: 'stretch',
        borderWidth: 0,
        borderStyle: 'solid',
        boxSizing: 'border-box',
        display: 'flex',
        flexDirection: 'column',
        margin: 0,
        padding: 0,
        position: 'relative',
        zIndex: 0,
        // fix flexbox bugs
        minHeight: 0,
        minWidth: 0
      },
      inline: {
        display: 'inline-flex'
      },
      // this zIndex-ordering positions the hitSlop above the View but behind
      // its children
      hitSlop: {
        ...StyleSheet.absoluteFillObject,
        zIndex: -1
      }
    });
    
    export default applyLayout(applyNativeMethods(View));
    复制代码
    

    View 组件就是一个简单的React组件,首先关注一下:

    export default applyLayout(applyNativeMethods(View));
    复制代码
    

    其中,applyNativeMethods 方法是将native的方法转换为对应的DOM方法;applyLayout 方法是对组件的生命周期函数进行重写。这部分感兴趣的小伙伴自行了解~

    接下来关注一下 View 组件的 render 方法,主要是对组件的 props 做些处理,包括校验 props 是否支持、style 处理,最后调用 createElement 方法

    createElement

    const createElement = (component, props, ...children) => {
      // use equivalent platform elements where possible
      let accessibilityComponent;
      if (component && component.constructor === String) {
        accessibilityComponent = AccessibilityUtil.propsToAccessibilityComponent(props);
      }
      const Component = accessibilityComponent || component;
      const domProps = createDOMProps(Component, props);
      adjustProps(domProps);
      return React.createElement(Component, domProps, ...children);
    };
    复制代码
    

    最终是调用了 React.createElement 方法创建 React Element,在此之前,主要做的事情就是调用 createDOMProps 方法,得到 domProps

    createDOMProps

    const createDOMProps = (component, props, styleResolver) => {
      ...
      const {
        ...
        ...domProps
      } = props;
    
      // GENERAL ACCESSIBILITY
      ...
    
      // DISABLED
      ...
    
      // FOCUS
      // Assume that 'link' is focusable by default (uses <a>).
      // Assume that 'button' is not (uses <div role='button'>) but must be treated as such.
      ...
    
      // STYLE
      // Resolve React Native styles to optimized browser equivalent
      const reactNativeStyle = [
        component === 'a' && resetStyles.link,
        component === 'button' && resetStyles.button,
        role === 'heading' && resetStyles.heading,
        component === 'ul' && resetStyles.list,
        role === 'button' && !disabled && resetStyles.ariaButton,
        pointerEvents && pointerEventsStyles[pointerEvents],
        providedStyle,
        placeholderTextColor && { placeholderTextColor }
      ];
      const { className, style } = styleResolver(reactNativeStyle);
      if (className && className.constructor === String) {
        domProps.className = props.className ? `${props.className} ${className}` : className;
      }
      if (style) {
        domProps.style = style;
      }
    
      // OTHER
      // Link security and automation test ids
      ...
      return domProps;
    };
    复制代码
    

    createDOMProps 方法代码较长,这里就不全部粘贴,从几个注释可以知道,此方法主要是将各 props 转换成对应的 web 端的props,这里我们以 style 为例,看看是如何做转换的。

    样式转换工作量主要在 styleResolver 方法,即调用 ReactNativeStyleResolver 实例的 resolve 方法。此方法最后会返回 className 和 style,最后会赋值到 domProps 中

    styleResolver

    resolve(style) {
      // fast and cachable
      // style: id
      if (typeof style === 'number') {
        this._injectRegisteredStyle(style);
        const key = createCacheKey(style);
        return this._resolveStyleIfNeeded(style, key);
      }
      // resolve a plain RN style object
      // style: 样式对象
      if (!Array.isArray(style)) {
        return this._resolveStyleIfNeeded(style);
      }
      // flatten the style array
      // cache resolved props when all styles are registered
      // otherwise fallback to resolving
      // style: 存储id的数组
      const flatArray = flattenArray(style);
      let isArrayOfNumbers = true;
      for (let i = 0; i < flatArray.length; i++) {
        const id = flatArray[i];
        if (typeof id !== 'number') {
          isArrayOfNumbers = false;
        } else {
          this._injectRegisteredStyle(id);
        }
      }
      const key = isArrayOfNumbers ? createCacheKey(flatArray.join('-')) : null;
      return this._resolveStyleIfNeeded(flatArray, key);
    }
    复制代码
    

    接下来看看 _injectRegisteredStyle 和 _resolveStyleIfNeeded

    _injectRegisteredStyle

    _injectRegisteredStyle(id) {
      const { doLeftAndRightSwapInRTL, isRTL } = I18nManager;
      const dir = isRTL ? (doLeftAndRightSwapInRTL ? 'rtl' : 'rtlNoSwap') : 'ltr';
      if (!this.injectedCache[dir][id]) {
        // 根据id获取对应的样式对象
        const style = flattenStyle(id);
        // 对样式对象格式化:各样式属性排序;添加长度单位;颜色值处理;特定属性处理;返回格式化之后的样式对象
        const domStyle = createReactDOMStyle(i18nStyle(style));
        Object.keys(domStyle).forEach(styleProp => {
          const value = domStyle[styleProp];
          if (value != null) {
            // 将样式插入 WebStyleSheet(domStyleElement.sheet)中
            this.styleSheetManager.injectDeclaration(styleProp, value);
          }
        });
        // 将此样式标记为已插入
        this.injectedCache[dir][id] = true;
      }
    }
    复制代码
    

    其中,styleSheetManager.injectDeclaration 是基于 domStyleElement.sheet 对页面样式进行插入操作,我们可以看看转出来的web页面的样式:

    [图片上传中...(image-5f1e13-1542030461141-0)]

    _resolveStyleIfNeeded

    _resolveStyleIfNeeded 方法即是调用 _resolveStyle 方法,源码如下:

    _resolveStyle(style) {
      // 获取对应id的样式对象
      const flatStyle = flattenStyle(style);
      // 对样式对象格式化:各样式属性排序;添加长度单位;颜色值处理;特定属性处理;返回格式化之后的样式对象
      const domStyle = createReactDOMStyle(i18nStyle(flatStyle));
    
      const props = Object.keys(domStyle).reduce(
        (props, styleProp) => {
          const value = domStyle[styleProp];
          if (value != null) {
            // 获取 WebStyleSheet 中特定样式属性及值对应的className
            // 通过 StyleSheet.create 创建的样式,会插入到 WebStyleSheet
            const className = this.styleSheetManager.getClassName(styleProp, value);
            if (className) {
              // 将此className放入props.classList中
              props.classList.push(className);
            } else {
              // Certain properties and values are not transformed by 'createReactDOMStyle' as they
              // require more complex transforms into multiple CSS rules. Here we assume that StyleManager
              // can bind these styles to a className, and prevent them becoming invalid inline-styles.
              // 单条样式属性,如果不是特殊属性,则直接放进props.style中
              // 单条样式属性是指未通过 StyleSheet.create 创建的样式
              if (
                styleProp === 'pointerEvents' ||
                styleProp === 'placeholderTextColor' ||
                styleProp === 'animationName'
              ) {
                const className = this.styleSheetManager.injectDeclaration(styleProp, value);
                if (className) {
                  props.classList.push(className);
                }
              } else {
                if (!props.style) {
                  props.style = {};
                }
                // 4x slower render
                props.style[styleProp] = value;
              }
            }
          }
          return props;
        },
        { classList: [] }
      );
    
      props.className = classListToString(props.classList);
      if (props.style) {
        props.style = prefixInlineStyles(props.style);
      }
      return props;
    }
    复制代码
    

    此方法主要是获取所有样式对应的 className 或者 style,并存入props中返回

    以上,我们以 View 组件为例分析了 react-native-web 实现 RN 组件的源码。

    我们做完源码分析之后,我们看看如何基于 react-native-web 做一些修改

    实践

    以 Text 组件为例,RN Text组件可以设置 numberOfLines,来实现单行或多行省略,但是react-native-web只实现了单行省略,所以我们要把多行省略的功能加上,代码如下:

    class Text extends Component<*> {
      ...
      render() {
        ...
        // allow browsers to automatically infer the language writing direction
        otherProps.dir = dir !== undefined ? dir : 'auto';
        otherProps.style = [
          styles.initial,
          this.context.isInAParentText === true && styles.isInAParentText,
          style,
          selectable === false && styles.notSelectable,
          numberOfLines === 1 && styles.singleLineStyle,
          onPress && styles.pressable
        ];
        // 支持多行省略
        if (numberOfLines > 1) {
          otherProps.style.push({
            display: '-webkit-box',
            WebkitBoxOrient: 'vertical',
            WebkitLineClamp: numberOfLines,
            overflow: 'hidden',
            textOverflow: 'ellipsis',
          });
        }
        const component = isInAParentText ? 'span' : 'div';
        return createElement(component, otherProps);
      }
      ...
    }
    复制代码
    

    举的这个例子比较简单,想表达的是我们通过看react-native-web源码,在开发过程中,遇到了转换web的问题,我们可以通过修改源码、或者使用它提供的API来解决

    具体代码可以参考示例项目:rn_web,包括源码注释和实例代码

    写在最后

    以上就是我对 react-native-web 源码的分享,希望能对有需要的小伙伴有帮助~~~

    作者:hujiao
    链接:https://juejin.im/post/5b79397be51d45389153b060
    来源:掘金
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    展开全文
  • Cordova 和 React-Native 是使用 Web 开发移动端的两大框架, Cordova 是 Apache 旗下的,React-Native 是 Facebook 旗下的在2013年发布的一个前端框架,两者皆开源。下面的内容主要记录了这两大框架的优劣,以及...
  • (1)jpush-react-native 插件的集成与使用 Android 篇 https://www.jianshu.com/p/6721a0360af9https://github.com/qn9301/react-native-web-canvas(1)react native极光推送全程教程android和ios ...
  • 低版本号为:"react-native-baidu-map": "^0.6.0" ,前往1.0.6版 在 Xcode 9.4上运行,10.1运行编译有问题 GitHub:https://github.com/lovebing/react-native-baidu-map 先到百度地图创建应用: 申请开发版S...
  • React-Native遇到那些坑

    2018-10-31 11:37:10
    目录 搭建环境 1、在模拟器上都安装不上package 2、安装JDK竟然有问题 3、Unable to load script from ...4、ReactNative:The development server returned response error code: 500 5、Unable to resolve mo...
  • react native实现登录...demo下载:react-native 完整实现登录功能1.完整目录2.实现的界面3.主界面的代码实现import React, { Component } from 'react'; import { ToolbarAndroid, AppRegistry, StyleSheet, Tex
  • 写一个用reactnative 的项目 可以用于查看GitHub最受欢迎与最热项目的App。 项目开始 HomePage.js PS 可以先拖动到底部看下效果图 新建一个reactnative项目 ,底部因为有四个tab 选项卡 那么 我使用一个第三方组件...
  • 使用react-native-cli安装版本不能低于0.60.0 error Cannot use React Native CLI to initialize project with version lower than 0.60.0.如何安装低版本 启动命令: react-native run-android 安卓模拟器总是...
  • react-native技术的优劣

    2018-02-27 23:35:18
    从2017年初开始到现在,使用React-Native做项目已经一年了。我们做的是一款IM软件,嵌入在一个手机游戏平台的工程内部。之所以要采用react-native(后文简称RN)框架重构它,是因为现在游戏大厅上的所有游戏都是热...
  • 这两个问题主要是由于reactnative和vue,react等框架不一样,这个是跨平台的,写的app不能直接在网页上即看即所得[意味着不能”直接地”console.log打印..]只能在真机调试或者在模拟器中查看,所以关于调试就显得...
  • 由于前段时间写了一个React-Native的项目Mung,里面有一个图片浏览的功能,一般图片浏览都需要放大缩小和左右滑动,后来找了两个这方面比较好的控件react-native-gallery和react-native-image-viewer,但他们都有些...
  • 一、初识React-Native对于初学者常见的困惑是弄不清ReactReact.js、React-Native这三者之间的关系,有必要先说明一下:React是基础框架,是一套基础设计实现理念,开发者不能直接使用它来开发网页或移动应用。...
  • 然后找到了react-native-amap3d这个第三方组件,很好用的一个地图组件,文档还比较细,GitHub上面Issuse也很活跃。 初次使用react-native开发APP还是有一些困难,这个组件是需要链接原生库的。就是说要使用react-...
  • 今天遇到了一个问题:下载了react-native-baidu-map的开源组件,可是在ios中遇到了问题,编译不通过,我想删除这个组件,然后写其他的功能,以后再添加这个地图功能,可是发现无法删除干净,android和ios原生都编译...
  • 在网上查找相关组件,开始使用的是react-native-qrcode组件,截图功能使用的是react-native-view-shot组件,保存图片则是使用的react-native自带的CameraRoll模块。以为这样就可以万事大吉了,没想到还是出了不明...
1 2 3 4 5 ... 20
收藏数 59,906
精华内容 23,962
关键字:

react-native