美文网首页react native
react-native技术栈总结

react-native技术栈总结

作者: 王逵_e9b3 | 来源:发表于2019-07-10 11:14 被阅读0次

    React-Native基本原理

    1. 整体架构

    RN框架让js开发者可以大部分使用js代码构建跨平台APP,Facebook官方说法是learn once, run everywhere, 即在Android 、 IOS、 Browser各个平台,程序画UI和写逻辑的方式都大致相同。因为JS 可以动态加载,从而理论上可以做到write once, run everywhere, 当然要做额外的适配处理。如图:

    1.jpeg

    RN需要一个js的运行环境,在ios上直接使用内置的javascriptcore,在android上使用webkit.org官方开源的jsc.so。此外还集成了其他开源组件,如fresco图片组件,okhttp网络组件等。

    RN会把应用的js代码(包括依赖的framework)编译成一个js文件(一般命名为index.android.bundle)。RN的整体框架目标就是为了解释运行这个js脚本文件,如果是js扩展的api,则直接通过bridge调用native方法;如果是ui界面,则映射到virtual Dom这个虚拟的js数据结构中,通过bridge传递到native,然后根据数据属性设置各个对应的真实native的view。bridge是一种js和java代码通信的机制,用bridge函数传入对方module和method即可达到异步回调的结果。

    对于js开发者来说,画ui只画到virtual DOM中,不需要特别关心具体的平台,还是原来的单线程开发,还是原来的html组装ui(jsx),还是原来的样式模型(部分兼容)。RN的界面处理除了实现View增删改查的接口之外,还自定义一套样式表达CSSLayout,这套CSSLayout也是跨平台实现。RN拥有画ui的跨平台能力,主要是加入了virtual DOM编程模型,该方法一面可以照顾js开发者在htmlDOM的部分传承,另一方面可以让virtualDOM适配实现到各个平台,实现跨平台的能力,并且为未来增加更多的想象空间,比如react-cavas,react-openGL。而实际上react-native也是从reactjs演变而来

    2.jpg

    2. 组件代码

    
    import React, { Component } from "react";
    import { View, Text, StyleSheet, ActivityIndicator } from "react-native";
    
    export default class Main extends Component {
      constructor(props) {
        super(props);
      }
      static defaultProps = {
        title: "处理中..."
      };
      render() {
        let { title } = this.props;
        return (
          <View
            style={{
              padding: 20,
              backgroundColor: "rgba(0, 0, 0, 0.7)",
              borderRadius: 10
            }}
          >
            <ActivityIndicator animating={true} size="large" color="#fff" />
            <Text style={{ color: "#fff", marginTop: 10 }}>{title}</Text>
          </View>
        );
      }
      componentDidMount = async () => {};
    }
    const style = StyleSheet.create({});
    
    

    对于js开发者来说,整个RN APP就只有一个js文件,而开发者需要编写的就只有如上部分。主要是四个部分:

    1. import所有依赖的组件

    2. class Main extends Component创建app,并且在reader函数中返回ui界面结构,实际经过编译,都会变成js代码。

    3. const style = StyleSheet.create({}) 创建css样式,实际上会直接当作参数反馈到上面的app

    4. AppRegistry.registerComponent('AwesomeProject', () => AwesomeProject); 以上三个更像是参数,这个才是JS 程序的入口。即把当前APP的对象注册到AppRegistry组件中, AppRegistry组件是js module

    接着就等待Native事件驱动渲染JS端定义的APP组件

    四小强之路由的设计封装

    3.png

    1、简单的路由页面配置

    import Home from "home/Home";
    import NewList from "home/NewList";
    import Product from "home/Product";
    import NewDetail from "home/NewDetail";
    import Icon from "react-native-vector-icons/FontAwesome";
    const HomeRoute = {
      home: {
        screen: Home,
        navigationOptions: {
          header: null
        }
      }
    };
    export default HomeRoute;
    
    

    2、利用react-navigation对配置文件进行封装

    - tabbar的封装
    /** @format */
    
    import React, { Component } from "react";
    import {
      createBottomTabNavigator,
      createStackNavigator,
      createAppContainer,
      getActiveChildNavigationOptions
    } from "react-navigation";
    import { Icon, Touchable, Button } from "ui";
    import { StyleSheet, Platform, View } from "react-native";
    
    import { transformSize } from "@/utils";
    import lang from "@/assets/lang";
    import home from "./home";
    import service from "./service";
    import my from "./my";
    import common from "./public";
    import login from "./login";
    
    let tabNavRouteConfig = {
      home: {
        screen: home.home.screen,
        navigationOptions: ({ screenProps }) => ({
          tabBarLabel: screenProps.tabLabel.home,
          tabBarIcon: ({ tintColor, focused }) => (
            <Icon name={focused ? "home2" : "home1"} size={25} color={tintColor} />
          ),
          header: null
        })
      },
      service: {
        screen: service.service.screen,
        navigationOptions: ({ screenProps }) => ({
          tabBarLabel: screenProps.tabLabel.service,
          tabBarIcon: ({ tintColor, focused }) => (
            <Icon
              name={focused ? "service2" : "service1"}
              size={25}
              color={tintColor}
            />
          )
        })
      },
      my: {
        screen: my.my.screen,
        navigationOptions: ({ screenProps }) => ({
          tabBarLabel: screenProps.tabLabel.my,
          tabBarIcon: ({ tintColor, focused }) => (
            <Icon name={focused ? "wode2" : "wode1"} size={25} color={tintColor} />
          )
        })
      }
    };
    
    const TabNav = createBottomTabNavigator(tabNavRouteConfig, {
      animationEnabled: false,
      swipeEnabled: false,
      lazy: true,
      tabBarPosition: "bottom",
      tabBarOptions: {
        showIcon: true,
        activeTintColor: "#3fc375",
        inactiveTintColor: "#494949",
        labelStyle: {
          fontSize: 12
        },
        style: {
          height: transformSize(110)
        }
      }
    });
    
    TabNav.navigationOptions = ({ navigation, screenProps }) => {
      return getActiveChildNavigationOptions(navigation, screenProps);
    };
    
    - 整体路由的封装
    // const AppContainer = createAppContainer(TabNav);
    const AppNavigator = createStackNavigator(
      {
        App: TabNav,
        ...home,
        ...service,
        ...my,
        ...common,
        ...login
      },
      {
        defaultNavigationOptions: ({ navigation }) => StackOptions({ navigation }),
        cardShadowEnabled: false,
        cardOverlayEnabled: true
      }
    );
    
    const AppContainer = createAppContainer(AppNavigator);
    
    export default AppContainer;
    
    const StackOptions = ({ navigation }) => {
      const tabBarVisible = false;
      const headerBackTitle = false;
    
      const headerStyle = {
        backgroundColor: "#fff",
        borderBottomWidth: 1,
        borderBottomColor: "#edebed",
        elevation: 0
      };
    
      const headerTitleStyle = {
        flex: 1,
        textAlign: "center",
        fontWeight: "600",
        color: "#333",
        fontSize: transformSize(36)
      };
      const headerTintColor = "#3fc375";
      const headerLeft = (
        <Button
          style={s.backButton}
          onPress={() => {
            navigation.goBack();
          }}
          icon="back"
          iconSize={20}
          iconColor="#666"
          title={lang.t("common.back")}
          textStyle={{
            color: "#666",
            marginLeft: transformSize(10),
            fontSize: transformSize(28)
          }}
        />
      );
      const headerRight = <View style={s.rightView} />;
      return {
        tabBarVisible,
        headerBackTitle,
        headerStyle,
        headerTitleStyle,
        headerTintColor,
        headerLeft,
        headerRight
      };
    };
    
    

    四小强之axios的封装结合

    4.png

    1、封装页面和axios调用的类

    /** @format */
    
    import axios from "./axios";
    import Qs from "qs";
    import _pick from "lodash/pick";
    import _assign from "lodash/assign";
    import _merge from "lodash/merge";
    import _isEmpty from "lodash/isEmpty";
    import _isArray from "lodash/isArray";
    
    import { assert } from "@/utils";
    import { API_DEFAULT_CONFIG, AXIOS_DEFAULT_CONFIG } from "@/config";
    import API_CONFIG from "@/api";
    
    /**
     * 生成api接口类
     */
    class Api {
      constructor(options) {
        this.api = {};
        this.apiBuilder(options);
      }
    
      /**
       * 创建工程接口
       * @param sep 分隔符
       * @param config 接口配置对象
       * @param mock 是否开启mock
       * @param debug 是否开启debug模式
       * @param mockBaseURL mock接口地址
       */
      apiBuilder({
        sep = "/",
        config = {},
        mock = false,
        debug = false,
        mockBaseURL = ""
      }) {
        Object.keys(config).map(namespace => {
          this._apiSingleBuilder({
            namespace,
            mock,
            mockBaseURL,
            sep,
            debug,
            config: config[namespace]
          });
        });
      }
    
      /**
       * 创建单个接口
       * @param sep 分隔符
       * @param config 接口配置对象
       * @param mock 是否开启mock
       * @param debug 是否开启debug模式
       * @param mockBaseURL mock接口地址
       */
      _apiSingleBuilder({
        namespace,
        sep = "/",
        config = {},
        mock = false,
        debug = false,
        mockBaseURL = ""
      }) {
        config.forEach(api => {
          const { name, desc, params, method, path, cache = true, headers } = api;
          let apiname = `${namespace}${sep}${name}`; // 接口调用名称 this.$api['apiname']()
          let url = path; // 接口地址
          const baseURL = mock ? mockBaseURL : AXIOS_DEFAULT_CONFIG.baseURL; // 接口base地址
    
          debug && assert(name, `${url} :接口name属性不能为空`);
          debug &&
            assert(url.indexOf("/") === 0, `${url} :接口路径path,首字符应为/`);
    
          Object.defineProperty(this.api, `${apiname}`, {
            value(outerParams, outerOptions) {
              // let _data = _isEmpty(outerParams) ? params : _pick(_assign({}, params, outerParams), Object.keys(params));
              let _data =
                _isArray(outerParams) || outerParams instanceof FormData
                  ? outerParams
                  : _merge({}, params, outerParams);
    
              /*特殊页面,需要对数据做处理*/
              if (
                (method.toUpperCase() === "POST" ||
                  method.toUpperCase() === "PUT") &&
                (!headers || !headers.hasOwnProperty("Content-Type"))
              ) {
                _data = Qs.stringify(_data);
              }
    
              return axios(
                _normoalize(
                  _assign(
                    {
                      name,
                      url,
                      desc,
                      baseURL,
                      method,
                      headers: headers || null,
                      cache
                    },
                    outerOptions
                  ),
                  _data
                )
              );
            }
          });
        });
      }
    }
    
    /**
     * 根据请求类型处理axios参数
     * @param options
     * @param data
     * @returns {*}
     * @private
     */
    function _normoalize(options, data) {
      //处理IE下请求缓存
      if (!options.cache) {
        options.url = `${options.url}${
          options.url.includes("?") ? "&" : "?"
        }_=${new Date().getTime()}`;
      }
    
      if (options.method === "POST" || options.method === "PUT") {
        options.data = data;
      } else if (options.method === "GET") {
        options.params = data;
      }
      return options;
    }
    
    /**
     * 导出接口
     */
    export default new Api({
      config: API_CONFIG,
      ...API_DEFAULT_CONFIG
    })["api"];
    
    

    2、 使用axios接口拦截器

    import axios from 'axios';
    
    import { AXIOS_DEFAULT_CONFIG } from '@/config';
    import {
      requestSuccessFunc,
      requestFailFunc,
      responseSuccessFunc,
      responseFailFunc
    } from '@/config/interceptor/axios';
    
    let axiosInstance = {};
    axiosInstance = axios.create(AXIOS_DEFAULT_CONFIG);
    
    // 注入请求拦截
    axiosInstance.interceptors.request.use(requestSuccessFunc, requestFailFunc);
    
    // 注入返回拦截
    axiosInstance.interceptors.response.use(responseSuccessFunc, responseFailFunc);
    
    export default axiosInstance;
    

    3、 接口通过一下方式配置

    export default [
      {
        name: "slide",
        method: "GET",
        desc: "轮播图",
        path: "/method/frappe.apis.apps.home.slides"
      }
    ];
    

    四小强之redux的封装

    5.png

    1、reducer的配置

    const DEFAULT_STATE = {
      homeData: {},
      slideData: [],
      newsData: [],
      newListData: [],
      newDetailData: {},
      productGroupData: [],
      productData: []
    };
    const USERDATA = {};
    export default function(state = DEFAULT_STATE, action = {}) {
      switch (action.type) {
        case home.HOME_DATA:
          return { ...state, ...action.payload };
        case home.SLIDE_DATA:
          return { ...state, ...action.payload };
        case home.NEWS_DATA:
          return { ...state, ...action.payload };
        case home.NEW_LIST_DATA:
          return { ...state, ...action.payload };
        case home.NEW_DETAIL_DATA:
          return { ...state, ...action.payload };
        case home.PRODUCT_GROUP_DATA:
          return { ...state, ...action.payload };
        case home.PRODUCT_DATA:
          return { ...state, ...action.payload };
    
        default:
          return state;
      }
    }
    

    2、actions的配置

    import $api from "@/plugins/api";
    import { doAction } from "./index";
    
    export const HOME_DATA = "HOME_DATA";
    
    export const NEW_DETAIL_DATA = "NEW_DETAIL_DATA";
    
    
    export const getHomeData = params =>
      doAction(params, "user/userInfo", "HOME_DATA", "homeData");
    
    export const getNewDetailData = (params, url) =>
      doAction(
        params,
        "home/newsDetail",
        "NEW_DETAIL_DATA",
        "newDetailData",
        null,
        url
      );
    
    

    同时封装调用接口的逻辑doAction

    export function doAction(
      params,
      url,
      dispatchType,
      stateData,
      model,
      replaceUrl
    ) {
      console.log("e", params, url, dispatchType, stateData, model, replaceUrl);
      return dispatch => {
        return new Promise((resolve, reject) => {
          $api[url](params, replaceUrl)
            .then(res => {
              let newRes;
              if (model) {
                newRes = model(res.data.display);
              } else {
                newRes = res.data.display;
              }
              let reducer = {
                type: "",
                payload: {}
              };
              reducer.type = dispatchType;
    
              reducer.payload[stateData] = newRes;
    
              dispatch(reducer);
              resolve(newRes);
            })
            .catch(err => {
              reject(err);
            });
        });
      };
    }
    

    中间加一层model层,处理后台不规范数据

    export function serviceModel(data) {
      return data.map((item, index) => {
        return {
          title: item.plan_name || item.service_project_name,
          ...item
        };
      });
    }
    
    export function spDetailModel(data) {
      let tasks = data.tasks.map((item, index) => {
        return {
          ...item,
          description: item.description || ""
        };
      });
      return {
        ...data,
        tasks
      };
    }
    

    四小强之公共方法的封装

    6.png
    1. 封装在utils文件夹中

    相关文章

      网友评论

        本文标题:react-native技术栈总结

        本文链接:https://www.haomeiwen.com/subject/nimckctx.html