RN-初始化

来学习怎么使用 React Native 吧!

新建项目

项目按运行台分为 ios 和 android,其中 ios 只能在 macOS 上开发,所以对于 windows 玩家,只能玩玩 android 了(

  1. 安装 Android Studio 并配置

    请根据 官方文档 进行该步骤

  2. 创建 rn 项目

    1. 如果你之前全局安装过旧的 react-native-cli 命令行工具,请使用 npm uninstall -g react-native-cli 卸载掉它以避免一些冲突

    2. 在项目父目录,执行

      1
      npx react-native init AwesomeProject

      以新建名为 AwesomeProject 的 rn 项目

      也可以使用 --template 来使用一些社区提供的模板,例如带有TypeScript配置的:

      1
      npx react-native init AwesomeTSProject --template react-native-template-typescript
  3. 在 Android Studio 中打开项目下的 android 目录,然后通过 Tools/AVD Manager 选项,打开虚拟机页面

    如果没有虚拟机,需要点击 Create Virtual Device...,创建新的虚拟设备,并在选择需要的设备类型后,在 Recommended 选项卡中选择 Q API Level 29 image,Target 选择 android 10.0

  4. 确保虚拟机已经启动后,在项目根目录运行命令提示符,然后 yarn android 即可启动 app

注意,命令提示符会打开一个新的 node 命令窗口,在该窗口键入 Ctrl+C 需要连续两次才可退出,或键入 R 以重载应用

基建

RN默认是只有

  • 根目录
    • android
    • ios
    • __tests__
    • 比宇宙更重的 node_modules

这样几个文件夹的,其余文件比如 App.tsx 都是放在根目录下

那我们就要先按 react 编码习惯,给目录做个层级

于是新建一个 src 目录来放置我们的代码

我弄成了如下结构

1
2
3
4
5
6
7
8
9
10
--root
--src
--component // 放置自定义组件
--iconfont // 为了使用 iconfont 的 svg
--interface // 类型定义
--router // 路由配置
--screen // 页面,等效于 umi 中的 pages
--store // redux
--util // 工具类,比如 Swal, request
App.tsx

同时,rn 默认是:

  1. 不支持 less
  2. 不能直接 use svg
  3. 不能直接使用 redux

所以必须要先配置一下

less

使用 react-native-less-transformer 插件

github 地址:https://github.com/kristerkari/react-native-less-transformer

安装依赖

1
yarn add --dev react-native-less-transformer less

打开 metro.config.js,向其中的 transformer 加入 babelTransformerPath: require.resolve("react-native-less-transformer"),向resolver 加入 "less"

文件大致变为如下模样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const {getDefaultConfig} = require('metro-config');

module.exports = (async () => {
const {
resolver: {sourceExts},
} = await getDefaultConfig();
return {
transformer: {
experimentalImportSupport: false,
inlineRequires: true,
babelTransformerPath: require.resolve('react-native-less-transformer'),
},
resolver: {
sourceExts: [...sourceExts, 'less'],
},
};
})();

现在就可以在 tsx 文件中引入 less 文件了

1
import styles from './index.less'

但是,还是不能做到像 umi 中的 less 一样的效果,具体来说,嵌套不能用了,我很难过(

不过可以装 vscode 插件 CSS Modules 来做到对 tsx 中的 style 引用的快速定位

iconfont

使用 react-native-iconfont-cli 插件来解决该问题

github 地址:https://github.com/iconfont-cli/react-native-iconfont-cli

安装依赖

1
2
yarn add react-native-svg
yarn add react-native-iconfont-cli --dev

然后静态链接

1
react-native link react-native-svg

最后生成配置文件

1
npx iconfont-init

此时项目根目录会生成一个 iconfont.json 的文件,内容如下:

1
2
3
4
5
6
7
8
{
"symbol_url": "请参考README.md,复制官网提供的JS链接",
"use_typescript": false,
"save_dir": "./src/iconfont",
"trim_icon_prefix": "icon",
"default_icon_size": 18,
"local_svgs": "./localSvgs"
}

参数意义如下

参数 意义
symbol_url iconfont 官网提供的 [symbol].js 的地址
use_typescript 如果使用 ts 开发,最好设置为 true,但即便是 false,也会提供 .js.d.ts 两种文件,来提供支持
save_dir 存放图标文件的地址,每次生成组件之前,该文件夹都会被清空
trim_icon_prefix 去除统一前缀
default_icon_size 默认大小
local_svgs 本地 svg 的路径,如无可以填空字符串

于是我的 iconfont.json 长这样

1
2
3
4
5
6
7
8
{
"symbol_url": "https://at.alicdn.com/t/font_xxxxxx.js",
"use_typescript": true,
"save_dir": "./src/iconfont",
"trim_icon_prefix": "icon",
"default_icon_size": 28,
"local_svgs": ""
}

配置完成后,开始生成组件

1
npx iconfont-rn

组件生成后,可以通过如下办法使用

1
2
3
4
5
6
7
8
9
10
import Icon from '../src/iconfont';

export const App = () => {
return (
<View>
<Icon name="alipay" size={20} />
<Icon name="wechat" />
</View>
);
};

通过 name 来指定要使用的 icon,并可以通过 size 按需调整大小

当你在 iconfont 上选择了新图标后,要更新配置文件里的 symbol_url 并重新生成图标文件

redux

直接使用 reduxreact-redux

1
yarn add react-redux redux

装完就可以用了

先在 ./src/store 中创建 index.ts

然后声明 actions

1
const actions = ['user/info'] as const;type ActionType = typeof actions[number];

这样使得我们在使用 ActionType 可以使用自动代码补全

之后创建 StateType 用以描述 state

1
2
3
interface StateType {
user: User | null | undefined;
}

再创建 reducer 来进行数据更改的提交

1
2
3
4
5
6
7
8
9
10
11
12
function reducer(
state: StateType = {user: undefined},
action: {type: ActionType; payload: any},
) {
switch (action.type) {
case 'user/info': {
return {...state, user: action.payload};
}
default:
return state;
}
}

reducer 接受两个参数,分别是:

  1. state,表示 redux 当前的状态
  2. action,是一个包含 typepayload 两个字段的对象,表示传入的操作

然后我们通过 switch 来根据传入的不同 type 进行不同的操作

reducer 中直接 return xxx 将会覆盖现在的 state,所以记得先展开原有的 state

最后一步,创建 store 对象,并暴露给外部使用

1
2
const store = createStore(reducer); // import {createStore} from 'redux';
export default store;

然后在 App.tsx 上使用 <Provider> 进行包装

1
2
3
4
5
6
7
8
9
10
11
12
import {Provider} from 'react-redux';
import store from './store';

const App = () => {
return (
<Provider store={store}>
{// 你的内容}
</Provider>
);
};

export default App;

现在可以在外部使用 connect 来连接数据了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import {View, Text} from 'react-native';
import {connect} from 'react-redux';

export default connect(
state => state,
dispatch => ({dispatch}),
)((props: any) => {
const {user} = props;
return (
<View>
<Text>{JSON.stringfy(user)}</Text>
</View>
)
}

connect 接收两个参数

  1. mapStateToProps,是一个函数,该函数接收 redux 的 state 作为参数,并返回一个对象,在向下传递时,会将该对象解构并赋值到组件的 props
  2. mapDispatchToProps,也是一个函数,该函数接收 redux 提供的提交动作 dispatch作为参数,并该函数返回一个对象,在向下传递时,会将该对象解构并赋值到组件的 props

connect 执行完毕后悔返回一个新函数,新函数接收一个函数组件作为参数,并将上述返回值解构并赋值到组件的 props 上。同时,外部传入的组件原本的 props 也会保留

注意,直接引入 store 并使用其 getState 方法来获得 state 的话,实质上并没有订阅 state 的更新,所以 state 更新时并不会触发视图的重新渲染,因此建议在除 <Provider> 外的任何地方,均使用 connect 来完成 state 的订阅

antd

原生的 input 不好用,所以需要 antd

但也只有文本框用 antd 方便了(

文档:https://rn.mobile.ant.design/docs/react/introduce-cn

安装依赖

1
yarn add @ant-design/react-native

链接

1
react-native link @ant-design/icons-react-native

按需加载

在项目根目录创建 .babelrc 文件,并写入如下内容

1
2
3
4
5
{
"plugins": [
["import", { libraryName: "@ant-design/react-native" }] // 与 Web 平台的区别是不需要设置 style
]
}

就可以正常使用 antd 了


感谢阅读

--It's the end.Thanks for your read.--