【转载】RN组件

React Native生命周期 这篇文章主要讲讲RN的生命周期,并概述一下在每个阶段都干了些什么。 RN的生命周期大致可分为三个阶段,实例化 -> 运行 -> 销毁。 首先讲讲大体的三个阶段做了啥: 实例化阶段:组件第一次绘制。这个阶段完成组件的加载和初始化。 运行阶段:组件进行交互。这个阶段组件可以处理交互,或者接受新的事件更新界面。 销毁阶段:组件卸载消亡。这个阶段做一些组件的清理工作。   下... 阅读详情

作者:意一ineyee
链接:https://www.jianshu.com/p/11bc2bdee5b8
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


目录

一. 什么是组件
二. 组件的props、state属性和普通属性
三. 组件的生命周期
四. 组件之间的通信方式
 1. 界面从前往后和从后往前传值:属性传值、回调函数传值
 2. 父组件给子组件传值、子组件给父组件传值、兄弟组件之间传值:属性传值、回调函数传值
 3. 跨层传值、一对多传值:通知
五. 如何自定义组件
六. 组件的导入与导出


一. 什么是组件


组件(Component)是指一个在UI上具备独立功能的单位,小的组件可以组合使用形成大的组件,最终完成整个App界面的搭建。它通常具备如下三个特征:

  • 可重用:一个组件就是一个具备独立功能的单位,它可以被用在代码的多个地方。
  • 可维护:一个组件就是一个具备独立功能的单位,它只包含它自身的逻辑,更容易被理解和维护。
  • 可组合:一个组件可以与另一个组件同级使用,一个组件也可以嵌套在另一个组件中使用。

一个组件本身的结构可以很简单,唯一必须的就是在render方法中return一个用JSX语法描述的组件,用来表明该组件应该被渲染成什么样子。(所谓JSX语法,是指在可以在JS中嵌入XML结构的语法)


二. 组件的props、state属性和普通属性


我们每定义一个组件,这个组件就会自动拥有props和state属性,因为我们自定义的组件都是继承自系统的Component组件,也就把Component组件的props和state属性给继承下来了。此外我们也可以给一个组件直接添加一些普通属性。

属性有定义、使用(如初始化、赋值、读取等)两个操作。

  • props

props用来存放组件的不可变属性,这些属性一般都是通过外界传进来的。我们不需要专门在组件内定义这些属性,而是在外界使用该组件的地方直接定义并赋值,至于别的使用,你可以在组件内部通过this.props.属性想怎么用怎么用。

// 组件
class CustomComponent extends Component {
    render() {
        return(
            <Text>{this.props.name + this.props.sex + this.props.age}</Text>
        );
    }
}
// 外界
class DetailPage extends Component<Props> {
    render() {
        return (
            <CustomComponent name={'张三'} sex={'男'} age={11}/>
        );
    }
}

注意:

外界在使用某个组件,给它的属性赋值时,外边一定要包一层大括号{},当然我们一敲等号,系统就自动给我们添加上这个大括号。这是因为我们需要把变量嵌入到JSX语法中,所以得用大括号括起来表示大括号里面是一个JS变量或表达式,需要执行后取值,因此我们可以把任意合法的JS表达式放进大括号里嵌入到JSX语法中。

既然说到props用来存放组件的“不可变属性”,这就说明这些属性不是像常量那样只能赋值一次,而是可以重新赋值的,也就是说你外界传进来的值变化了,这些属性的值自然就跟着变化了。所以通常情况下组件的props属性接收的是外界的state属性。

// 组件
class CustomComponent extends Component {
    render() {
        return(
            <Text>{this.props.name + this.props.sex + this.props.age}</Text>
        );
    }
}
// 外界
class DetailPage extends Component<Props> {
    constructor(props) {
        super(props);
        this.state = {
            name: '张三',
            sex: '男',
            age: 11,
        };
    }
    render() {
        return (
            <CustomComponent name={this.state.name} sex={this.state.sex} age={this.state.age}/>
        );
    }
}

这就说到了state属性,我们接下来就看看它。

  • state

state用来存放组件的可变属性,也就是说,state里面存放的属性可以在组件的生命周期内随时修改。而且state里属性的值一旦发生变化,就会触发组件的render方法来重新渲染该组件,换句话说,RN里一切界面的变化都是state里属性值的变化。

通常情况下,我们会在组件的constructor方法中,给this.state赋值来定义并初始化该组件的可变属性,什么地方要使用这个属性就直接this.state.属性使用它,什么地方要修改这个属性就调用this.setState()方法修改它。

constructor(props) {
    super(props);
    this.state = {
        // 定义并初始化一个属性
        color: 'red',
    };
}
// 使用属性
console.log(this.state.color);
// 通过setState()方法来修改属性的值
this.setState({
    color: 'red',
});

constructor方法里调用super方法,这个无需多言,前面已经说过了,是为了完成子类继承自父类的一些资源的初始化。但是这里有两个东西需要我们额外关心,那就是constructor方法和super方法都有一个参数props,为什么非要写这个参数,state又为什么要通过this.state = ......这样的方式来编写。

现在假设我们有一个Person类和一个Boy类。

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

可见我们的Boy类继承自Person类,Boy类也就继承了Person类的name属性。因此当我们在编写Boy类的constructor方法时,如果不把name属性传递给super方法,那Boy类的name属性是无法完成赋值的,因为name属性实际上是人家Person类的属性,name属性的赋值操作是在父类里面完成的,除非我们把name的也写在Boy类里面。

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

类推到这个地方,我们自定义的组件都是继承自系统的Component组件,自定义组件的props属性和state属性就是继承自Component组件。所以props属性就类似于上面的name属性,其真正的赋值过程是在父类Component中,所以我们会把自定义组件的props属性传递给super方法,否则很有可能导致自定义组件的props属性完不成赋值。而state就类似于上面的age属性,也许state属性确实也在Component中赋值了,但是我们需要在自定义组件里为组件添加可变属性啊,所以还是在自定义组件里重新写了一下赋值过程。综上,我们知道props属性和state属性在constructor其实可以有多种写法,但最常用的写法还是:

constructor(props) {
    super(props);
    this.state = {
        // 定义并初始化不可变属性
        // ......
    };
}
  • 普通属性

我们已经知道了props属性和state属性的适用场景,但是还有一种情况,例如组件内有个输入框,输入什么,组件的属性就记录什么,那这个属性改用props还是state呢?

很明显这个数据一直在变化,我们不能使用props,但是使用state好像也不对,因为该属性的值一直在变,就一直会触发组件的render()方法重新渲染组件,但是我们并不需要一直重新渲染组件啊,因此还有一种普通属性——直接定义在组件身上的属性。

// 组件
class CustomComponent extends Component {
    render() {
        return(
            <TextInput
                onChangeText={text => {
                    this.searchText = text;
                }}
            />
        );
    }
}

上面代码中searchText就是直接定义在组件身上——而非props或state里——的一个普通属性,它可以随时变化,同时又不会触发render()方法。

可以看到我们也不用专门在组件内部定义它的普通属性,需要时直接.并赋值就行,因为我们知道JS对象新增一个属性的办法就是直接.并赋值,然后什么时候需要别的使用就可以随时使用了。

不过为了代码更加易读,我们会为这个类定义它的普通属性,然后再使用,直接定义在类的头部就可以了。

// 组件
class CustomComponent extends Component {
    searchText;
    render() {
        return(
            <TextInput
                onChangeText={text => {
                    this.searchText = text;
                }}
            />
        );
    }
}


三. 组件的生命周期


iOS里UIViewController提供了有- viewDidLoad、- viewWillAppear:、- viewDidAppear:、- viewWillDisappear:、- viewDidDisappear:、- dealloc等生命周期方法,Android里Activity也有onCreate()、onStart()、onStop()、onDestroy()等生命周期方法,这些生命周期方法展示了一个界面从创建到销毁的一生。RN里组件也有它的生命周期,分为三个阶段。

  • Mounting:加载阶段
  • Updating:更新阶段
  • Unmounting:卸载阶段

每个阶段也都对应着几个生命周期方法,如下。

下面介绍一下我们常用的几个生命周期方法:

  • constructor()方法

组件初始化时调用,类似于iOS里的- init方法,我们一般在这个方法里为组件添加并初始化一些可变的属性。

  • render()方法

组件渲染时调用,该函数必须实现,我们一般在这个方法里返回一个React组件,或者返回null和布尔值代表什么都不渲染。

该方法会在第一次渲染组件时调用,也会在组件state或props里属性值发生变化时调用。

  • componentDidMount()方法

组件加载完毕时调用,类似于iOS里的- viewDidLoad方法,我们一般在这个方法里进行网络请求、写定时器等耗时操作。

  • componentWillUnmount()方法

组件即将卸载时调用,类似于iOS里的- viewWillDisappear:,我们一般在这个方法里移除定时器、移除通知等。

  • componentWillReceiveProps(object nextProps)方法

在当前组件props里的属性值发生变化时,会触发该方法,新的props可以从参数里取到,老的props可以通过this.props获取到。

该方法会在第一次渲染组件时不会被调用。

  • shouldComponentUpdate(object nextProps, object nextState):方法

组件接收到新的props或state后,将要重新渲染之前调用(初次渲染不会调用,仅仅是更新时会调用)。我们可以根据实际情况来重写这个方法,灵活地控制当接收到新的props和state时,组件是否要重新渲染,以此来减少不必要的性能损耗。


四. 组件之间的通信方式


自上而下的通信,采用属性;自下而上的通信,采用回调函数;跨层通信、一对多通信,采用通知。

1. 界面从前往后和从后往前传值:属性传值、回调函数传值

到目前为止,我们还没有学习界面的跳转与返回,下一篇文章将学习。这里先把界面从前往后和从后往前传值的方法列在这里,学习后可返回来查看。

其实RN里界面从前往后和从后往前传值和我们iOS里是一样的,我们先回想一下:

  • iOS里是如何从前往后传值的:属性传值,后一个界面定义一个接收数据的属性,前一个界面在跳转界面时把需要传递的数据传递给后一个界面的那个属性就可以了。

  • iOS里是如何从后往前传值的:使用block作为回调,后一个界面负责定义一个block属性,并在需要的地方调用block;前一个界面负责block的具体实现,并在跳转界面时把这个具体实现传递给后一个界面的block属性。

// 前一个界面
export default class TrendingPage extends Component<Props> {
    render() {
        return (
            <View>
                <Button
                    title={'跳转到详情页'}
                    onPress={() => {
                        NavigationUtil.navigate('DetailPage', {
                            data: '哈哈',
                            callback: (text) => this._callback(text)
                        });
                    }}
                />
            </View>
        );
    }
    _callback(text) {
        console.log(text);
    }
}
// 后一个界面
export default class DetailPage extends Component<Props> {
    render() {
        const {data, callback} = this.props.navigation.state.params;
        return (
            <View style={styles.container}>
                <Text style={styles.welcome}>{data}</Text>
                <Button
                    title={'返回'}
                    onPress={() => {
                        callback('嘻嘻');
                        NavigationUtil.goBack();
                    }}
                />
            </View>
        );
    }
}
2. 父组件给子组件传值、子组件给父组件传值、兄弟组件之间传值:属性传值、回调函数传值
  • 父组件给子组件传值

这个和我们iOS里是一样的,我们iOS里父视图给子视图传值采用从前往后的传值法,我们先回想一下iOS里是如何从前往后传值的:属性传值,后一个界面定义一个接收数据的属性,前一个界面在跳转界面时把需要传递的数据传递给后一个界面的那个属性就可以了。

对应到RN里就是:子组件定义一个接收数据的属性,父组件在调用子组件时把需要传递的数据传递给子组件的那个属性就可以了。

// 子组件
class SubComponent extends Component {
    render() {
        return (
            null
        );
    }
    componentDidMount() {
        console.log(this.props.data);
    }
}
// 父组件
export default class ParentComponent extends Component {
    render() {
        return (
            <View style={styles.container}>
                <SubComponent
                    // 子组件定义一个接收数据的属性
                    // 父组件在调用子组件时把需要传递的数据传递给子组件的那个属性
                    data={'我是传递的数据哦'}
                />
            </View>
        );
    }
}
  • 子组件给父组件传值

这个和我们iOS里是一样的,我们iOS里子视图给父视图传值采用从后往前的传值法,我们先回想一下iOS里是如何从后往前传值的:使用block作为回调,后一个界面负责定义一个block属性,并在需要的地方调用block;前一个界面负责block的具体实现,并在跳转界面时把这个具体实现传递给后一个界面的block属性。

对应到RN里就是:子组件负责定义一个回调函数属性(定义在props里,因为它也是不可变的,外面传什么它就是什么),并在需要的地方调用回调函数;父组件负责回调函数的具体实现,并在调用子组件时把这个具体实现传递给子组件的回调函数属性。

// 子组件
class SubComponent extends Component {
    render() {
        return (
            <View style={styles.subComponent}>
                <TextInput
                    style={styles.textInput}
                    onChangeText={(text) => {
                        // 子组件在需要的地方调用回调函数
                        this.props.textDidChange(text);
                    }}
                />
            </View>
        );
    }
}
// 父组件
export default class ParentComponent extends Component {
    render() {
        return (
            <View style={styles.container}>
                <SubComponent
                    // 子组件负责定义一个回调函数属性
                    // 父组件负责调用子组件时把这个具体实现传递给子组件的回调函数属性
                    textDidChange={(text) => this._textDidChange(text)}
                />
            </View>
        );
    }
    // 父组件负责回调函数的具体实现
    _textDidChange(text) {
        console.log(text);
    }
}
  • 兄弟组件之间传值

兄弟组件之间传值的本质其实是:兄弟组件共享父组件的state + 子组件给父组件传值 + 父组件.setState()重新触发render() + 父组件给子组件传值。

// 子组件1
class SubComponent1 extends Component {
    render() {
        return (
            <View style={styles.subComponent}>
                <Text>{this.props.data}</Text>
                <TextInput
                    style={styles.textInput}
                    onChangeText={(text) => {
                        this.props.textDidChange(text);
                    }}
                />
            </View>
        );
    }
}
// 子组件2
class SubComponent2 extends Component {
    render() {
        return (
            <View style={styles.subComponent}>
                <Text>{this.props.data}</Text>
                <TextInput
                    style={styles.textInput}
                    onChangeText={(text) => {
                        this.props.textDidChange(text);
                    }}
                />
            </View>
        );
    }
}
// 父组件
export default class ParentComponent extends Component {
    constructor(props) {
        super(props);
        this.state = {
            subComponent1Data: null,
            subComponent2Data: null,
        }
    }
    render() {
        return (
            <View style={styles.container}>
                <SubComponent1
                    data={this.state.subComponent1Data}
                    textDidChange={(data) => this._textDidChange1(data)}
                />
                <SubComponent2
                    data={this.state.subComponent2Data}
                    textDidChange={(data) => this._textDidChange2(data)}
                />
            </View>
        );
    }
    _textDidChange1(data) {
        this.setState({
            subComponent2Data: data,
        });
    }
    _textDidChange2(data) {
        this.setState({
            subComponent1Data: data,
        });
    }
}
3. 跨层传值、一对多传值:通知

上面讲的几种情况都是组件之间有关联的传值,我们在开发中当然还可能遇见无关联组件之间传值——即我们通常所说的跨层传值,也有可能遇到一对多传值,那该怎么做呢?

回想iOS中类似的情况,我们是通过可以通过单例和通知来做的,RN中好像用通知比较多一些。RN也提供了一个专门负责通知的组件DeviceEventEmitter,它类似于我们iOS里的通知中心[NSNotificationCenter defaultCenter],需要接收和发送通知的双方都要导入它。

通知的使用其实也很简单,我们按着iOS写通知的思路来就没问题,不具体分析了,下面只简单写个例子。

// 界面二,想要接收界面一发出的某个通知
import {DeviceEventEmitter} from 'react-native';

class MyPage extends Component<Props> {
render() {
// …
}

componentDidMount() {
    // 在通知中心注册观察者
    this.listener = DeviceEventEmitter.addListener(
        // 通知的名字
        'FavoritePageDidTouch',
        // 通知的回调
        (notificationData) =&gt; {
            console.log(notificationData);
        });
}

componentWillUnmount() {
    // 移除通知的观察者
    if (this.listener) {
        this.listener.remove();
    }
}

}

// 界面一,发出某个通知
import {DeviceEventEmitter} from 'react-native';

class FavoritePage extends Component<Props> {
    render() {
        return (
            <View style={styles.container}>
                <Button
                    title={'发出通知'}
                    onPress={() =>
                        // 发出通知,以及通知携带的参数
                        DeviceEventEmitter.emit('FavoritePageDidTouch', {
                            'hi': '11'
                        })
                    }
                />
            </View>
        );
    }
}


五. 如何自定义组件


接下来我们以自定义一个导航栏组件为例,来看一下在RN中如何自定义一个组件。

-----------ProjectNavigationBar.js----------
/**
 * 自定义导航栏
 *
 * 至于为什么要自定义导航栏,详见DynamicBottomNavigator.js,DynamicBottomNavigator.navigationOptions = ......那里有提到
 */
import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View, StatusBar, ViewPropTypes} from 'react-native';
import {PropTypes} from 'prop-types';
import Color from "../../Const/Color";
// 高度
const STATUS_BAR_HEIGHT = (Platform.OS === 'ios' ? 20 : 0);// 安卓自己保留了StatusBar的高度,我们不设置了
const NAVIGATION_BAR_HEIGHT = (Platform.OS === 'ios' ? 44 : 50);
// StatusBar其实是系统提供的组件,这里我们在StatusBarShape里写的属性,其实和StatusBar的属性一模一样,方便我们传递给它
const StatusBarShape = {// 注意整个项目的StatusBar只可能有一种配置,后设置的会覆盖先设置的
    barStyle: PropTypes.oneOf(['default', 'light-content', 'dark-content']),
    hidden: PropTypes.bool,
};
export default class ProjectNavigationBar extends Component {
    // 为该组件的属性设置类型检查:一个组件有很多属性,有些属性我们想给它指定为特定的类型,就是通过这种办法。设置之后,外界在使用该组件时,如果给某个属性赋的值的类型,跟我们这里类型检查设置的类型不一样,编译器就会报警告。
    // 注意:这里仅仅是对组件的部分属性做了类型检查,而不是添加属性(添加属性还是在外界使用组件的时候,那里写什么就是添加了什么属性),也就是一个组件的属性是要做类型检查属性的父集
    static propTypes = {
        // 每个组件不是都有个style属性嘛,它也必须是某种特定的类型————ViewPropTypes.style
        style: ViewPropTypes.style,
        // 我们封装的导航栏组件也包含了状态栏,它们统称为导航栏,这里这个属性其实只是用来配置statusBar的,而不是直接接收一个statusBar组件
        statusBarProps: PropTypes.shape(StatusBarShape),
        // 导航栏标题
        title: PropTypes.string,
        // 导航栏标题位置的自定义组件
        titleView: PropTypes.element,
        // titleView的style
        titleViewStyle: ViewPropTypes.style,
        // 左item
        leftButton: PropTypes.element,
        // 右item
        rightButton: PropTypes.element,
        // 导航栏是否隐藏
        hide: PropTypes.bool,
    };
    // 为该组件的属性设置默认值:一个组件有很多属性,有些属性我们想给它设置默认值,就是通过这种办法。
    // 注意:static defaultProps和static propTypes一样,它们是兄弟关系,各自负责不同的功能,不存在谁包含谁,这里仅仅是为该组件的部分属性设置了默认值而已,也不是添加属性(添加属性还是在外界使用组件的时候,那里写什么就是添加了什么属性),也就是说一个组件的属性是要设置默认值的属性的父集
    static defaultProps = {
        // 这里我们只设置一下statusBarProps的默认值,如果用户不设置statusBar的属性的话,我们让项目的statusBar的属性有个默认值
        statusBarProps: {
            barStyle: 'light-content',
            hidden: false,
        },
    };
    render() {
        // 创建状态栏
        let statusBar = this.props.statusBarProps.hidden ? null :
            <View style={styles.statusBar}>
                <StatusBar {...this.props.statusBarProps}/>
            </View>;
        // 创建title或titleView
        let titleView = this.props.titleView ? this.props.titleView :
            <Text
                style={styles.title}
                numberOfLines={1}
                // 一行显示不下时,省略号的模式,'head'前省略,'tail'后省略,'middle'中间省略,'clip'裁切能显示的部分显示、而不显示省略号
                ellipsizeMode={'tail'}
            >
                {this.props.title}
            </Text>;
        // 创建导航栏
        let navigationBar = this.props.hide ? null :
            <View style={styles.navigationBar}>
                {/* 创建左item */}
                {this.getButtonElement(this.props.leftButton)}
                {/* 中间title或titleView */}
                <View style={[styles.navigationBarTitleContainer, this.props.titleViewStyle]}>
                    {titleView}
                </View>
                {/* 创建右item */}
                {this.getButtonElement(this.props.rightButton)}
            </View>;
        return (
            // 注意:this.props.style是外界传进来的style,一定要放在styles.container我们内部定义的style后面,否则外面设置的覆盖不了前面的,用户设置的就没效果了
            <View style={[styles.container, this.props.style]}>
                {/* 状态栏 */}
                {statusBar}
                {/* 导航栏 */}
                {navigationBar}
            </View>
        );
    }
    getButtonElement(element) {
        // 外界已经写好leftButton和rightButton传进来了,这里我们为了好布局它们俩,给它俩外面包一层View来做它俩的style
        return (
            <View style={styles.navigationBarButton}>
                {element ? element : null}
            </View>
        );
    }
    }
    const styles = StyleSheet.create({
    container: {
        backgroundColor: Color.THEME_COLOR,
    },
    statusBar: {
        height: STATUS_BAR_HEIGHT,
    },
    navigationBar: {
        flexDirection: 'row',
        alignItems: 'center',
        justifyContent: 'space-between',
        height: NAVIGATION_BAR_HEIGHT,
    },
    navigationBarTitleContainer: {
        alignItems: 'center',
        justifyContent: 'center',
        // 为了避免leftButton和rightButton大小不一样,而导致我们的titleView没法横向居中,这里我们用一下绝对布局
        position: 'absolute',
        top: 0,
        right: 40,
        bottom: 0,
        left: 40,
    },
    title: {
        fontSize: 17,
        color: 'white',
    },
    navigationBarButton: {
        alignItems: 'center',
    },
    });
附注:属性的类型检查

先在项目中导入prop-types库:yarn add prop-types。

属性是某个JS数据类型
  • PropTypes.bool
  • PropTypes.number
  • PropTypes.string
  • PropTypes.array
  • PropTypes.object
  • PropTypes.func
属性是某个RN组件
  • PropTypes.element
属性是某几个特定值中的一个(枚举值)
  • PropTypes.oneOf(['value1', 'value2'])
属性是一个包含指定形状属性的JS对象

即该属性是一个JS对象,它只能包含shape(obj)方法的参数obj里指定的属性,也就是说属性要和obj长得差不多,而且属性里的属性要少于等于obj里的属性。

  • PropTypes.shape(obj)
属性可以是任意类型
  • PropTypes.any
属性为必需填写

上述语法,都可以通过在后面加上isRequired来表明该属性是必需填写的。如果将属性声明为必需的,如果使用时没有给该属性传递数据,手机上会弹出相关的警告信息。

  • PropTypes.array.isRequired

更多类型检查,详见使用 PropTypes 进行类型检查。


六. 组件的导入与导出


组件主要有两个命令:export和import。export关键字用来导出当前组件的接口供外界使用,import关键字用来导入外界组件的接口。

1. export关键字

一个组件就是一个独立的文件,也就是说JS会把每一个.js文件看做是一个组件。而该文件内部所有的变量,外部都无法获取,如果你希望外部能够读取该组件内部的某个变量,就必须使用export关键字导出该变量。

比如下面是一个JS文件,里面就使用了export命令导出变量。

// profile.js

let firstName = ‘三’;
let lastName = ‘张’;
let year = 2000;

export {firstName, lastName, year};

export关键字除了可以导出变量,还可以导出一个JS文件里的方法和类。(其实一个JS文件里,也就只能直接定义变量、方法和类这三个东西了)

function hello() {}
// export hello;// 报错
export {hello};// 正确

class Person {}
class Son extends Person {}
export {Person, Son};

需要注意的是,使用export关键字导出东西的时候,一定要记得后面加上大括号{},即便只导出一个东西也要加,否则会报错。

2. import关键字

当我们使用export关键字将某个组件的接口导出之后,其他的JS文件就可以通过import关键字去导入那个组件导出的接口了。

比如下面是main.js文件从profile.js文件里导入数据。

// main.js
import {firstName, lastName, year} from './profile'
function printFullName() {
    console.log(firstName + ' ' + lastName);
}

需要注意的是:

  • 使用import关键字导入东西的时候,最好也是在后面加上一个大括号{}。
  • import导入的数据都是只读的,不允许修改,你一修改就报错,但如果导入的变量是个对象,我们不能直接修改对象,但是修改对象的属性是不会报错的。
  • from后面是组件文件的位置,可以是相对路径,也可以是绝对路径,.js后缀可以省略。
3. export default命令

export default命令用来指定某个组件的默认输出,一个组件内只能有一个默认输出,所以一个组件内只能使用一次export default命令。

下面我们看下为什么要搞这个export default命令。

经过上面第1、2小节,我们看到使用import命令的时候,需要知道另一个组件里导出了哪些东西,也就是说需要知道这些东西的变量名、函数名或类名,否则就没办法无法导入了。但是,用户肯定希望快速上手,未必愿意阅读文档,去了解那个组件导出了哪些变量、函数或类。

因此为了给用户提供方便,让他们不用阅读文档就能加载组件,就可以用到export default命令,为某个组件指定默认输出。

// export-default.js
export default function () {
  console.log('foo');
}

上面代码是一个组件文件export-default.js,它的默认输出就是一个函数,其他组件加载该组件时就不需要知道它具体导出了什么,可以使用import命令为该匿名函数指定任意名字。

// import-default.js
import customName from './export-default';
customName(); // 'foo'

需要注意的是: 使用export default导出的东西,在被import导入时,不需要也不能加大括号。

RN 生命周期方法,Android校招面试指南 生命周期 |调用次数 |能否使用 setSate() |1(全局调用一次)| 否|constructor / getInitialState |1 |否是|render |>=1 |否是自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前! 阅读详情

相关推荐

RN组件库 - Button 组件

从零构建 React Native 组件库,作为一个前端er 谁不想拥有一个自己的组件库

kiku 1148

(十二)React-Native-生命周期详解

一.React-Native生命周期 说到生命周期,大家大概也能想到就是创建、销毁、状态改变。RN的组件就是一个状态机。它接收两个输入参数:props和state,返回一个Virtual DOM。和Native一样,RN也为我们提供相应的钩子函数。RN的状态变化取决于props和state。我们先来看一张经典图。 这张图涵盖了一个组件从创建、运行到销毁的整个过程。大家可以看到,初始

袁梦龙的博客 2449

避坑指南:Qt无边框窗口拖动+阴影效果这些细节要注意

本文深入探讨了Qt无边框窗口开发中实现流畅拖动与阴影效果的关键细节与常见陷阱。针对高DPI屏幕适配、阴影闪烁、拖动卡顿及多显示器兼容性等核心难题,提供了从QGraphicsDropShadowEffect原理剖析到手动绘制优化、坐标系统精准处理及平台原生事件拦截的实战解决方案,帮助开发者构建稳定、美观的商业级应用界面。

weixin_27943395的博客 474

React Native 生命周期

前言:          在面向对象编程中,任何对象的存在都会存在生命周期。类似我们iOS 的View,就会有LoadView,ViewWillAppear,ViewDidLoad等等生命周期。RN也不例外,这篇主要学习RN的生命周期,在开发中如果掌握了并熟练的运用生命周期函数的话,往往开发能事半功倍。 React Native生命周期简介

星宇大前端 2141

ASP.NET Core Identity + Mysql(实战一)——注册登陆

在进行实战之前,我们先来了解一下Identity。 ASP.NET Core Identity是用于构建ASP.NET Core Web应用程序的成员资格系统,包括成员资格、登录和用户数据存储。简单的来讲包括: 1.用户数据存储(使用任意你喜欢的关系型数据库,从sqllite到mysql、sqlserver等等,由Entity Framwork 支持) 2.登陆、注册外加身份认证(基于coo...

weixin_40359716的博客 1395

TP4054充电电路发热严重?手把手教你调整电阻值降低温度(附实测数据)

本文针对TP4054锂电池充电电路发热严重的问题,提供了详细的排错与解决方案。通过分析发热原理,指出核心在于PROG引脚电阻值设定,并手把手指导如何计算和选择正确的电阻以降低充电电流与温度。文末附有不同电阻下的实测温度与性能数据,为电子爱好者和工程师提供了实用的调试参考。

p4q5r6s7t的博客 399

uniapp实战:5分钟搞定PDA激光扫码功能(附完整代码)

本文详细介绍了在Uniapp应用中快速集成工业PDA激光扫码功能的实战方案。通过解析PDA硬件扫码与手机摄像头扫码的核心机制差异,提供了完整的广播监听封装类代码,并演示了在Vue页面中的集成使用方法,帮助开发者高效解决仓储、零售等行业的条码扫描需求。

weixin_29239661的博客 452

转载【React Native代码】手写验证码倒计时组件

  实例代码: import React, { Component , PropTypes} from 'react'; import { AppRegistry, StyleSheet, Text, View, TextInput, TouchableOpacity } from 'react-native'; const DownButto...

Landen2011的博客 186

【Matlab/Unity】跨平台UDP通信实战:从数据发送到实时可视化

本文详细介绍了Matlab与Unity之间通过UDP协议实现跨平台通信的实战方法,涵盖数据发送、接收和实时可视化全流程。针对科研仿真和工业设计场景,提供优化代码示例和性能对比数据,帮助开发者解决低延迟传输、线程安全等关键问题,显著提升跨平台协作效率。

weixin_29044713的博客 148

告别CAPL编程!用Matlab/Simulink+CANoe实现复杂ECU仿真的5个技巧

本文介绍了如何利用Matlab/Simulink与CANoe进行联合仿真,以替代复杂的CAPL编程,高效实现复杂ECU仿真。文章分享了5个核心技巧,涵盖信号映射、模型编译集成、实时参数调整、性能优化及数据分析,帮助算法工程师构建稳定强大的仿真环境,显著提升开发与测试效率。

rgv234567的博客 292

[RN] React Native 自定义 底部 弹出 选择框 实现

React Native 自定义 底部选择框 实现 效果如图所示: 实现方法: 一、组件封装 CustomAlertDialog.js import React, {Component} from 'react'; import {Dimensions, Modal, StyleSheet, Text, TouchableOpacity, View} from ...

weixin_30889885的博客 3036

开发具有情感分析能力的AI Agent

本文将深入探讨开发具有情感分析能力的AI Agent的各个方面。我们将首先介绍情感分析的基础知识,包括定义、背景和应用领域。接着,我们将详细讲解情感分析的核心概念、数据处理方法和算法原理。随后,文章将展示如何设计情感分析系统架构,并进行项目实战,包括环境安装、源代码实现和案例分析。最后,我们将总结最佳实践和注意事项,为读者提供进一步学习的资源。情感分析(Sentiment Analysis),也称为意见挖掘,是自然语言处理(NLP)的一个分支。

SuperAGI2025 1138

[RN] React Native 好用的时间线 组件

React Native 好用的时间线 组件 效果如下: 实现方法: 一、组件封装 CustomTimeLine.js "use strict"; import React, {Component} from "react"; import { StyleSheet, ListView, Image, ...

weixin_30828379的博客 806

大数据Alluxio面试题及参考答案

Alluxio 在大数据生态里的定位是作为数据编排层,能在不同计算框架和存储系统之间起到桥梁的作用。它的核心功能在于加速数据访问,让大数据应用能够更高效地使用数据。Alluxio 把数据存储和数据处理分离开来,使计算框架可以在不考虑底层存储细节的情况下访问数据,这样就提高了系统的灵活性和可扩展性。Alluxio 和 HDFS 既存在关联又有区别。从关联方面来看,HDFS 是一种广泛使用的分布式文件系统,很多大数据应用都将其作为主要的数据存储系统。Alluxio 可以和 HDFS 集成,把 HDFS 当作底层

大模型大数据攻城狮的专栏 856

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

[RN] React Native 头部 滑动吸顶效果的实现

React Native 头部 滑动吸顶效果的实现 效果如下图所示: 实现方法: 一、吸顶组件封装 StickyHeader .js import * as React from 'react'; import {StyleSheet, Animated} from "react-native"; /** * 滑动吸顶效果组件 * @expor...

weixin_30412167的博客 2334

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

React Native

React Native 中文网 http://reactnative.cn/ 零基础用react-native开发android app https://segmentfault.com/a/1190000003915315 ...

chongjubao8359的博客 65

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

Tips——RN如何动态地添加删除组件

一、需求 点击时动态地创建或删除组件 二、问题 RN是通过state更新页面,没有dom那一套直接添加节点的api 三、解决 通过数组的增删改查对动态地改变state,以达到创建或删除组件的效果 添加image: //add imgItem imgArr.push( <ImgItem key={ imgNum } componentId={...

weixin_34289744的博客 523

ReactNative 踩坑之 iOS 原生组件

iOS 中Undefined symbols for architecture x86_64 在原生 iOS 开发中,这个错误信息,应该或多或少的会碰到过: Undefined symbols for architecture x86_64: "_OBJC_CLASS_$_XXX", referenced from: __XXX in TestClass.o ld: symbol(...

weixin_34399060的博客 368

01、RN 系列之 什么是 ReactNative

版权声明:本文为博主原创文章,未经博主允许不得转载。 PS:转载请注明出处 作者: TigerChain 地址: www.jianshu.com/p/717ccdd7c… 本文出自 TigerChain 简书 ReactNative 系列 教程简介 1、阅读对象 本篇教程适合新手阅读,老手直接略过 2、教程难度 初级 一、手机 APP 分类 1、手机 APP 现状 从目前来说...

weixin_34014277的博客 164

【转载记录】rn工程启动流程

一、React Native工程结构分析 1) 初始化RN工程 react-native init LKDemo2 2) 工程目录结构图 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZUf5wHzh-1585552299962)(/Users/a123/Desktop/Flutter/LearnFlutterNodes/images/img16.png)] 3)...

hello917的博客 399

《ReactNative系列讲义》高级篇---03.编写npm组件

| 版权声明:本文为博主原创文章,未经博主允许不得转载。 一、简介 ReactNative Android版的发布已有两年多了,RN官网的迭代速度也是相当之快,给广大开发者提供的API也相当丰富了,但是官方给出的API也只能是共有功能或者是常用功能。但实际中,每款APP都会根据自己的产品需求开发一些特有功能。这篇文章就通过一个支持RTSP协议的摄像头功能组件,给大家介绍一下如何制作个性化的...

fsf_snail的专栏 922

ReactNative是怎么在iOS上跑起来的

ReactNative是怎么在iOS上跑起来的 本文所有观点并不权威,都是平时看文档和实践中概括的,可能有错的地方,如有发现麻烦指出wayshongg@gmail.com谢谢。 初始化流程: 创建根Controller:RCTRootView 创建桥接对象:RCTBridge,用来处理js的jsccore,js、c++和OC交互。 加载JS:[RCTBatchedBridge loa...

weixin_30249203的博客 122

ReactNative

React Native可伸缩列表封装与快速实现方案React Native快速封装一个类似qq好友列表的可伸缩列表,一点经验分享 ReactNative 学习资源大汇集React Native 着力于提高多平台开发的开发效率 —— 仅需学习一次,编写任何平台。(Learn once, write anywhere)。 本文汇集了 React-Native 各类学习资源,方便大家学习,同时还有 [

379

RN的第一个API-----注册组件Appregistry

首先解释下AppRegistry是JS运行所有React Native应用的入口 什么是入口? 1.在我们初始化一个react native项目的时候 默认的index.ios.js/index.ios.js里面的内容是这这样的 (这里我们简化一下代码) import React,...

295

RN如何固定底部的按钮

如上图的底部新增按钮,要是放在web里那是相当简单,直接是用固定定位就行,但是在RN里是没有固定定位可言的。 方案一: 采用绝对定位,相对于最外层的定位在底部位置。(在部分安卓机上有问题,动态计算的高度,定位底部的高度有问题,导致底部按钮定位脱离了视野) 正确方案:还是flex布局好用。 大致如上图,头部导航<HeaderView> 是固定的,剩下的就是 <Segme...

weixin_34166472的博客 4246
上一篇: FPGA学习心得--01
下一篇: 微带阻抗匹配网络(一)
-Willing-
博客等级 码龄7年 129粉丝 49原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值