目录
一. 什么是组件
二. 组件的
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) => {
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
导入时,不需要也不能加大括号{}
。