u
This commit is contained in:
1 parent
52cef511d6
commit
68f08d921a
184 files changed
+19499
-1
No files matched your search
@@ -0,0 +1,9 @@
|
||||
[React Native](https://reactnative.dev/) 是一个开源框架,用于构建跨平台的移动应用。它允许开发者使用 React 来构建原生的 iOS 和 Android 应用。
|
||||
|
||||
主要特点:
|
||||
- 跨平台开发:通过一次编写代码,React Native 可以在 iOS 和 Android 上运行,大大减少开发时间和成本。
|
||||
- 原生性能:React Native 使用原生组件(如按钮、文本框等),提供接近原生应用的性能和用户体验。
|
||||
- 热重载:开发者可以实时查看代码修改的效果,而无需重新加载整个应用。
|
||||
- 组件化开发:通过 React 的组件化理念,开发者可以更高效地构建和维护应用界面。
|
||||
- 丰富的生态系统:React Native 拥有大量的第三方库和工具,帮助开发者快速实现功能。
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
[Expo](https://docs.expo.dev/) 是一个基于 React Native 的框架,旨在简化移动应用开发。它提供了一套工具和服务,让开发者可以更快速地构建、测试和发布 React Native 应用。
|
||||
|
||||
主要特点:
|
||||
- 快速上手:无需复杂配置,直接在手机上查看代码效果。
|
||||
- 内置功能:相机、位置服务、推送通知等常用功能,免去手动集成。
|
||||
- 跨平台支持:同一套代码可在 iOS 和 Android 上运行。
|
||||
- 热重载:修改代码后,应用会即时更新,提升开发效率。
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
创建项目命令:
|
||||
|
||||
```bash
|
||||
pnpm create expo-app@latest --template blank-typescript
|
||||
```
|
||||
|
||||
[官网模板](https://docs.expo.dev/more/create-expo/) 如下:
|
||||
|
||||
| 模板名称 | 描述 |
|
||||
| ------------------------------------------------------------ | ------------------------------------------------------------ |
|
||||
| [`default`](https://github.com/expo/expo/tree/main/templates/expo-template-default) | 默认模板,用于构建多页面应用。包含推荐工具,如 **Expo CLI**、**Expo Router** 路由库,并启用 **TypeScript** 配置。适合大多数应用。 |
|
||||
| [`blank`](https://github.com/expo/expo/tree/main/templates/expo-template-blank) | 最简模板,仅安装最基本依赖,不包含导航配置。 |
|
||||
| [`blank-typescript`](https://github.com/expo/expo/tree/main/templates/expo-template-blank-typescript) | 空白模板,但启用了 **TypeScript** 支持。 |
|
||||
| [`tabs`](https://github.com/expo/expo/tree/main/templates/expo-template-tabs) | 启用基于文件的路由系统(Expo Router),并默认使用 **TypeScript**,适合多标签页结构的应用。 |
|
||||
| [`bare-minimum`](https://github.com/expo/expo/tree/main/templates/expo-template-bare-minimum) | 带有原生目录(`android` 与 `ios`)的空白模板,创建时会自动运行 [`npx expo prebuild`](https://docs.expo.dev/workflow/prebuild)。适合需要直接修改原生代码的项目。 |
|
||||
|
||||
**启动项目:**
|
||||
|
||||
```bash
|
||||
npx expo start
|
||||
```
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
**生成基础文件**,后面要用到:
|
||||
|
||||
```bash
|
||||
npx expo customize babel.config.js
|
||||
npx expo customize metro.config.js
|
||||
```
|
||||
|
||||
## 配置eslint格式化
|
||||
|
||||
可以使用快捷命令(推荐):
|
||||
|
||||
```bash
|
||||
pnpm dlx @antfu/eslint-config@latest
|
||||
```
|
||||
|
||||
安装:
|
||||
|
||||
```bash
|
||||
pnpm i -D eslint @antfu/eslint-config
|
||||
```
|
||||
|
||||
新建 `eslint.config.mjs` 文件:
|
||||
|
||||
```js
|
||||
import antfu from '@antfu/eslint-config'
|
||||
|
||||
export default antfu({
|
||||
stylistic: {
|
||||
indent: 2, // 缩进为 2 个空格(可选 4 或 'tab')
|
||||
quotes: 'single', // 使用单引号(可选 'double')
|
||||
},
|
||||
rules: {
|
||||
'unused-imports/no-unused-imports': 'warn',
|
||||
'unused-imports/no-unused-vars': 'warn',
|
||||
'@typescript-eslint/no-use-before-define': 'off', // 关闭 TS 的“定义前使用”警告
|
||||
'no-console': 'off', // 关闭 console 报错
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
配置 vscode 模板,修改 `.vscode/settings.json` :
|
||||
|
||||
> 注意安装 eslint 插件
|
||||
|
||||
```json
|
||||
{
|
||||
// Disable the default formatter, use eslint instead
|
||||
"prettier.enable": false,
|
||||
"editor.formatOnSave": false,
|
||||
|
||||
// Auto fix
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit",
|
||||
"source.organizeImports": "never"
|
||||
},
|
||||
|
||||
// Silent the stylistic rules in you IDE, but still auto fix them
|
||||
"eslint.rules.customizations": [
|
||||
{ "rule": "style/*", "severity": "off", "fixable": true },
|
||||
{ "rule": "format/*", "severity": "off", "fixable": true },
|
||||
{ "rule": "*-indent", "severity": "off", "fixable": true },
|
||||
{ "rule": "*-spacing", "severity": "off", "fixable": true },
|
||||
{ "rule": "*-spaces", "severity": "off", "fixable": true },
|
||||
{ "rule": "*-order", "severity": "off", "fixable": true },
|
||||
{ "rule": "*-dangle", "severity": "off", "fixable": true },
|
||||
{ "rule": "*-newline", "severity": "off", "fixable": true },
|
||||
{ "rule": "*quotes", "severity": "off", "fixable": true },
|
||||
{ "rule": "*semi", "severity": "off", "fixable": true }
|
||||
],
|
||||
|
||||
// Enable eslint for all supported languages
|
||||
"eslint.validate": [
|
||||
"javascript",
|
||||
"javascriptreact",
|
||||
"typescript",
|
||||
"typescriptreact",
|
||||
"vue",
|
||||
"html",
|
||||
"markdown",
|
||||
"json",
|
||||
"jsonc",
|
||||
"yaml",
|
||||
"toml",
|
||||
"xml",
|
||||
"gql",
|
||||
"graphql",
|
||||
"astro",
|
||||
"svelte",
|
||||
"css",
|
||||
"less",
|
||||
"scss",
|
||||
"pcss",
|
||||
"postcss"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 配置深度链接
|
||||
|
||||
作用:通过链接就可以跳转到该app。
|
||||
|
||||
在 `app.json` 文件下进行配置:
|
||||
|
||||
```json
|
||||
{
|
||||
"expo": {
|
||||
"scheme": "oneao"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
然后就可以通过这个配置 从别的应用或者浏览器跳转到你的app:
|
||||
|
||||
```
|
||||
oneao://courses/react-native
|
||||
```
|
||||
|
||||
## 定义 src 路径别名 @
|
||||
|
||||
> 参考:https://docs.expo.dev/guides/typescript/
|
||||
|
||||
修改 `tsconfig.json` 文件:
|
||||
|
||||
```json
|
||||
{
|
||||
"extends": "expo/tsconfig.base",
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"@/*": ["src/*"]
|
||||
},
|
||||
"strict": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
安装 `module-resolver` :
|
||||
|
||||
```
|
||||
pnpm add -D babel-plugin-module-resolver
|
||||
```
|
||||
|
||||
修改 `babel.config.js` :
|
||||
|
||||
```js
|
||||
module.exports = function (api) {
|
||||
api.cache(true)
|
||||
return {
|
||||
presets: [
|
||||
['babel-preset-expo', { jsxImportSource: 'nativewind' }],
|
||||
'nativewind/babel',
|
||||
],
|
||||
plugins: [
|
||||
[
|
||||
'module-resolver',
|
||||
{
|
||||
root: ['./src'],
|
||||
alias: {
|
||||
'@': './src',
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,359 @@
|
||||
## 云打包
|
||||
1. 安装 EAS CLI
|
||||
|
||||
```bash
|
||||
npm install -g eas-cli
|
||||
```
|
||||
|
||||
2. 注册并登录 Expo 账户
|
||||
|
||||
- 访问 https://expo.dev/ 注册账户,然后命令行登录:
|
||||
|
||||
```bash
|
||||
expo login
|
||||
```
|
||||
|
||||
3. 配置项目构建
|
||||
|
||||
- 输入以下命令,选择 Android 平台即可。
|
||||
|
||||
```bash
|
||||
eas build:configure
|
||||
```
|
||||
|
||||
4. 构建 Android 包
|
||||
|
||||
- 默认构建 AAB 包(上传 Google Play):
|
||||
|
||||
```bash
|
||||
eas build --platform android
|
||||
```
|
||||
|
||||
- 构建 APK(可直接安装):
|
||||
|
||||
```bash
|
||||
eas build -p android --profile preview
|
||||
```
|
||||
|
||||
## 本地打包(EAS方式)
|
||||
|
||||
## 本地打包(原生方式)
|
||||
### 1. 安装 [Android Studio]( https://developer.android.com/?hl=zh-cn)
|
||||
|
||||
1. 下载安装
|
||||
|
||||
- 按照官网提示依次安装 SDK 工具和必要组件。
|
||||
|
||||
- 安装完成后,记住 SDK 的安装路径(例如:`D:\data\sdk`,默认位置是:`C:\Users\<你的用户名>\AppData\Local\Android\Sdk`),后续需要用到。
|
||||
|
||||
2. 配置环境变量
|
||||
|
||||
- 打开 **系统属性 → 高级 → 环境变量**。
|
||||
|
||||
- 在 **系统变量** 中新增:
|
||||
|
||||
```
|
||||
变量名:ANDROID_HOME
|
||||
变量值:D:\data\sdk
|
||||
```
|
||||
|
||||
- 在 **Path** 中新增:
|
||||
|
||||
```bash
|
||||
%ANDROID_HOME%\platform-tools
|
||||
%ANDROID_HOME%\tools
|
||||
%ANDROID_HOME%\tools\bin
|
||||
```
|
||||
|
||||
- 保存设置。
|
||||
|
||||
3. 验证安装
|
||||
|
||||
- 打开命令行,执行:
|
||||
|
||||
```
|
||||
echo %ANDROID_HOME%
|
||||
```
|
||||
|
||||
- 如果输出 SDK 路径,则说明配置成功。
|
||||
|
||||
|
||||
|
||||
### 2. 安装 [jdk17](https://adoptium.net/zh-CN/temurin/releases?version=8&os=any&arch=any)
|
||||
|
||||
1. 下载安装
|
||||
|
||||
- 下载对应版本解压即可。
|
||||
- 安装成功后,记住 jdk 的安装路径,后续需要用到。
|
||||
|
||||
2. 配置环境变量
|
||||
|
||||
- 打开 **系统属性 → 高级 → 环境变量**。
|
||||
|
||||
- 在 **系统变量** 中新增:
|
||||
|
||||
```bash
|
||||
变量名:JAVA_HOME
|
||||
变量值:D:\devtool\jdk\jdk-17.0.16+8
|
||||
```
|
||||
|
||||
- 在 **Path** 中新增:
|
||||
|
||||
```bash
|
||||
%JAVA_HOME%\bin
|
||||
```
|
||||
|
||||
- 保存设置。
|
||||
|
||||
3. 验证安装
|
||||
|
||||
- 打开命令行,执行:
|
||||
|
||||
```bash
|
||||
java --version
|
||||
```
|
||||
|
||||
- 如果输出 jdk 的信息,则说明配置成功。
|
||||
|
||||
|
||||
|
||||
### 3. Expo 项目启用 [Prebuild](https://docs.expo.dev/workflow/prebuild/)
|
||||
|
||||
1. 生成原生项目
|
||||
|
||||
```bash
|
||||
npx expo prebuild
|
||||
```
|
||||
|
||||
然后执行完毕后,会生成 `android` 目录。
|
||||
|
||||
2. 清除并重新生成原生项目
|
||||
|
||||
```bash
|
||||
npx expo prebuild --clean
|
||||
```
|
||||
|
||||
会删除已有的 `android` 和 `ios` 目录,然后重新生成。
|
||||
|
||||
### 4. 配置 Keystore
|
||||
#### 4.1 生成密钥
|
||||
|
||||
```bash
|
||||
keytool -genkey -v -keystore android-release.keystore -alias my-key-alias -keyalg RSA -keysize 2048 -validity 10000
|
||||
```
|
||||
说明:
|
||||
|
||||
- `my-release-key.keystore`:生成的密钥文件名,可自定义路径
|
||||
- `my-key-alias`:密钥别名
|
||||
- `RSA`:加密算法
|
||||
- `2048`:密钥长度
|
||||
- `10000`:有效期(天)
|
||||
|
||||
#### 4.2 手动配置
|
||||
|
||||
修改 `android/app/build.gradle` 文件
|
||||
|
||||
1. 修改 `signingConfigs`
|
||||
|
||||
```bash
|
||||
signingConfigs {
|
||||
release {
|
||||
storeFile file('android-release.keystore') // keystore 文件路径
|
||||
storePassword '你的Keystore密码' // keystore 密码
|
||||
keyAlias '你的KeyAlias' // 密钥别名
|
||||
keyPassword '你的Key密码' // key 密码
|
||||
}
|
||||
|
||||
debug {
|
||||
storeFile file('debug.keystore')
|
||||
storePassword 'android'
|
||||
keyAlias 'androiddebugkey'
|
||||
keyPassword 'android'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. 在 `buildTypes` 中设置 release 使用 release 签名
|
||||
|
||||
```bash
|
||||
buildTypes {
|
||||
release {
|
||||
signingConfig signingConfigs.release
|
||||
}
|
||||
|
||||
debug {
|
||||
signingConfig signingConfigs.debug
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 4.3 脚本配置(推荐)
|
||||
|
||||
1. 在 expo 项目根目录下新增 `plugins\withAndroidSignature.js` 文件,内容如下:
|
||||
|
||||
```js
|
||||
// plugins/withAndroidSignature.js
|
||||
|
||||
const { withAppBuildGradle } = require("@expo/config-plugins");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
module.exports = function withAndroidSignature(config) {
|
||||
return withAppBuildGradle(config, config => {
|
||||
if (config.modResults.language === "groovy") {
|
||||
config.modResults.contents = setAndroidSignature(config.modResults.contents);
|
||||
} else {
|
||||
throw new Error("如果不是 groovy,则无法在 app/build.gradle 中设置 signingConfigs");
|
||||
}
|
||||
return config;
|
||||
});
|
||||
};
|
||||
|
||||
function setAndroidSignature(appBuildGradle) {
|
||||
if (!fs.existsSync(path.resolve(__dirname, "../credentials.json"))) {
|
||||
console.warn("警告:没有设置正式版本的 Android Keystore 文件,因为 credentials.json 不存在。");
|
||||
return appBuildGradle;
|
||||
}
|
||||
const info = JSON.parse(fs.readFileSync(path.resolve(__dirname, "../credentials/credentials.json"), { encoding: "utf8" }));
|
||||
|
||||
// 使用正则表达式插入签名信息
|
||||
let output = appBuildGradle.replace(
|
||||
/(signingConfigs\s*\{)/,
|
||||
`$1
|
||||
release {
|
||||
storeFile file(${JSON.stringify(path.resolve(__dirname, "../credentials/android-release.keystore"))})
|
||||
storePassword ${JSON.stringify(info.android.keystore.keystorePassword)}
|
||||
keyAlias ${JSON.stringify(info.android.keystore.keyAlias)}
|
||||
keyPassword ${JSON.stringify(info.android.keystore.keyPassword)}
|
||||
}`,
|
||||
);
|
||||
|
||||
// 使用正则表达式替换 signingConfig
|
||||
output = output.replace(
|
||||
/(release\s*\{)[^}]*?signingConfig\s+signingConfigs\.debug/s,
|
||||
`$1
|
||||
signingConfig signingConfigs.release
|
||||
`,
|
||||
);
|
||||
|
||||
return output;
|
||||
}
|
||||
```
|
||||
|
||||
2. 在 expo 项目根目录下新增 `/credentials/credentials.json` 文件(该文件主要是 Keystore 的信息):
|
||||
|
||||
```json
|
||||
{
|
||||
"android": {
|
||||
"keystore": {
|
||||
"keystorePath": "credentials/android-release.keystore", // Android keystore 文件路径
|
||||
"keystorePassword": "123456", // keystore 密码
|
||||
"keyAlias": "my-key-alias", // Key 别名
|
||||
"keyPassword": "123456" // Key 密码
|
||||
}
|
||||
},
|
||||
"ios": {
|
||||
"provisioningProfilePath": "ios/certs/profile.mobileprovision", // iOS 描述文件路径
|
||||
"distributionCertificate": {
|
||||
"path": "ios/certs/dist-cert.p12", // iOS 分发证书路径
|
||||
"password": "password" // 证书密码
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. 在 `app.json` 中指定这个项目级插件:
|
||||
|
||||
```json
|
||||
{
|
||||
"expo": {
|
||||
"plugins": [
|
||||
"./plugins/withAndroidSignature"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 打包
|
||||
|
||||
- 编译成 Google Play 商店用的 .aab 文件
|
||||
|
||||
```bash
|
||||
./gradlew bundleRelease
|
||||
```
|
||||
|
||||
- 直接安装的 .apk 文件
|
||||
|
||||
```bash
|
||||
./gradlew assembleRelease
|
||||
```
|
||||
|
||||
**注意**:这个时候可能会下载 `gradle` 、`apk` 等资源,网络速度可能过慢。
|
||||
|
||||
**解决方法**:在 `android/gradle.properties` 添加网络代理:
|
||||
|
||||
```bash
|
||||
systemProp.http.proxyHost=127.0.0.1
|
||||
systemProp.http.proxyPort=7890
|
||||
systemProp.https.proxyHost=127.0.0.1
|
||||
systemProp.https.proxyPort=7890
|
||||
```
|
||||
|
||||
打包结果在 `android/app/build/outputs/apt/release` 包下。
|
||||
|
||||
|
||||
## 打包体积优化
|
||||
#### 方法1(简单)
|
||||
找到 `android/gradle.properties` 文件,找到 `reactNativeArchitectures` ,根据需要删除即可
|
||||
|
||||
```properties
|
||||
reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
|
||||
```
|
||||
|
||||
#### 方法2(原生方法 选择更多)
|
||||
在 `android/app/build.gradle` 文件中添加或修改以下配置:
|
||||
|
||||
```
|
||||
android {
|
||||
// 其他配置...
|
||||
|
||||
splits {
|
||||
abi {
|
||||
enable true // 启用按 CPU 架构拆分
|
||||
reset() // 重置默认架构列表
|
||||
include 'arm64-v8a', 'armeabi-v7a'
|
||||
universalApk false // 是否生成包含所有架构的通用 APK(false 表示只生成分架构的独立包)
|
||||
// 还有一个 exclude 配置是排除指定架构的
|
||||
}
|
||||
}
|
||||
|
||||
// 其他配置...
|
||||
}
|
||||
```
|
||||
|
||||
**说明:**
|
||||
|
||||
- `enable true`:启用架构拆分功能。
|
||||
- `reset()`:清空默认的架构配置,避免重复。
|
||||
- `include`:指定要生成的架构包,比如只打 `arm64-v8a` 和 `armeabi-v7a`。
|
||||
- `universalApk false`:不生成通用包(即包含所有架构的 APK)。
|
||||
|
||||
**举例:**
|
||||
|
||||
执行打包命令后,将生成多个包,例如:
|
||||
|
||||
```
|
||||
app-arm64-v8a-release.apk
|
||||
app-armeabi-v7a-release.apk
|
||||
```
|
||||
|
||||
每个 APK 只包含对应架构的二进制文件,体积更小。
|
||||
|
||||
如果把 `universalApk` 设置为 `true`,则只会生成一个:
|
||||
|
||||
```
|
||||
app-universal-release.apk
|
||||
```
|
||||
|
||||
这个包能在 `arm64-v8a` 和 `armeabi-v7a` 架构的设备上运行,但体积最大。
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"label": "expo",
|
||||
"position": 1,
|
||||
"link": {
|
||||
"type": "generated-index"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
有很多地方下载依赖使用的命令是:
|
||||
```bash
|
||||
npx expo add xxx
|
||||
```
|
||||
|
||||
使用这种的方式只是为了兼容 expo 项目,如果没用 expo 的话,将命令修改为现在使用的 `pnpm add xxx`,`npm i xxx` 等都可以
|
||||
@@ -0,0 +1,109 @@
|
||||
NativeWind 原子化 CSS 库
|
||||
> 官网:https://www.nativewind.dev/docs/getting-started/installation
|
||||
|
||||
1. 安装依赖
|
||||
|
||||
```bash
|
||||
npx expo add nativewind react-native-reanimated react-native-safe-area-context
|
||||
npx expo add tailwindcss@^3.4.17 -- --save-dev
|
||||
```
|
||||
|
||||
2. 运行 `npx tailwindcss init` 生成 `tailwind.config.js` 文件,添加配置:
|
||||
|
||||
```js
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
// NOTE: Update this to include the paths to all files that contain Nativewind classes.
|
||||
content: ['./App.tsx', './components/**/*.{js,jsx,ts,tsx}'],
|
||||
presets: [require('nativewind/preset')],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
```
|
||||
|
||||
3. 创建 `global.css` 文件:
|
||||
|
||||
```css
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
```
|
||||
|
||||
|
||||
注意在全局引入
|
||||
|
||||
4. 修改 `babel.config.js` 文件:
|
||||
|
||||
> 如果没有该文件,执行 `npx expo customize babel.config.js` 命令创建。
|
||||
|
||||
```js
|
||||
module.exports = function (api) {
|
||||
api.cache(true)
|
||||
return {
|
||||
presets: [
|
||||
['babel-preset-expo', { jsxImportSource: 'nativewind' }],
|
||||
'nativewind/babel',
|
||||
],
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
5. 修改 `metro.config.js` 文件:
|
||||
|
||||
> 如果没有该文件,执行 `npx expo customize metro.config.js` 命令创建。
|
||||
|
||||
```js
|
||||
const { getDefaultConfig } = require('expo/metro-config')
|
||||
const { withNativeWind } = require('nativewind/metro')
|
||||
|
||||
const config = getDefaultConfig(__dirname)
|
||||
|
||||
module.exports = withNativeWind(config, { input: './global.css' })
|
||||
```
|
||||
|
||||
6. 新建 `src/types/nativewind-env.d.ts` 文件:
|
||||
|
||||
```ts
|
||||
/// <reference types="nativewind/types" />
|
||||
```
|
||||
|
||||
7. 安装 ` react-native-css-interop` :
|
||||
|
||||
```bash
|
||||
npx expo install react-native-css-interop
|
||||
```
|
||||
|
||||
8. 检验是否生效
|
||||
|
||||
```tsx
|
||||
import { Text, View } from 'react-native'
|
||||
import './global.css'
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<View className="flex-1 items-center justify-center bg-white">
|
||||
<Text className="text-xl font-bold text-blue-500">
|
||||
Welcome to Nativewind!
|
||||
</Text>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
如果不生效,记得修改 `tailwind.config.js` 的目标路径:
|
||||
|
||||
```js
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
// NOTE: Update this to include the paths to all files that contain Nativewind classes.
|
||||
content: ['./src/app/_layout.tsx', './src/components/**/*.{js,jsx,ts,tsx}'],
|
||||
presets: [require('nativewind/preset')],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,290 @@
|
||||
Gluestack UI UI 组件库
|
||||
> 官网:https://gluestack.io/ui/docs/home/getting-started/installation
|
||||
|
||||
## 方式一:CLI
|
||||
|
||||
```bash
|
||||
npx gluestack-ui init
|
||||
```
|
||||
|
||||
## 方式二:Manual(推荐)
|
||||
|
||||
**前置条件:** 需要安装 `nativewind`
|
||||
|
||||
1. 安装依赖:
|
||||
|
||||
```bash
|
||||
npx expo add @gluestack-ui/core @gluestack-ui/utils react-native-svg @gluestack/ui-next-adapter
|
||||
```
|
||||
|
||||
2. 从 [github](https://github.com/gluestack/gluestack-ui/tree/main/src/components/ui/gluestack-ui-provider) 复制必须的组件到 `@/components/ui/gluestack-ui-provider`。
|
||||
|
||||
3. 修改 `tailwind.config.js` 文件:
|
||||
|
||||
```bash
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
darkMode: process.env.DARK_MODE ? process.env.DARK_MODE : 'class',
|
||||
content: [
|
||||
'./app/**/*.{html,js,jsx,ts,tsx,mdx}',
|
||||
'./components/**/*.{html,js,jsx,ts,tsx,mdx}',
|
||||
'./utils/**/*.{html,js,jsx,ts,tsx,mdx}',
|
||||
'./*.{html,js,jsx,ts,tsx,mdx}',
|
||||
'./src/**/*.{html,js,jsx,ts,tsx,mdx}',
|
||||
],
|
||||
presets: [require('nativewind/preset')],
|
||||
important: 'html',
|
||||
safelist: [
|
||||
{
|
||||
pattern:
|
||||
// eslint-disable-next-line regexp/no-dupe-disjunctions
|
||||
/(bg|border|text|stroke|fill)-(primary|secondary|tertiary|error|success|warning|info|typography|outline|background|indicator)-(0|50|100|200|300|400|500|600|700|800|900|950|white|gray|black|error|warning|muted|success|info|light|dark|primary)/,
|
||||
},
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: {
|
||||
0: 'rgb(var(--color-primary-0)/<alpha-value>)',
|
||||
50: 'rgb(var(--color-primary-50)/<alpha-value>)',
|
||||
100: 'rgb(var(--color-primary-100)/<alpha-value>)',
|
||||
200: 'rgb(var(--color-primary-200)/<alpha-value>)',
|
||||
300: 'rgb(var(--color-primary-300)/<alpha-value>)',
|
||||
400: 'rgb(var(--color-primary-400)/<alpha-value>)',
|
||||
500: 'rgb(var(--color-primary-500)/<alpha-value>)',
|
||||
600: 'rgb(var(--color-primary-600)/<alpha-value>)',
|
||||
700: 'rgb(var(--color-primary-700)/<alpha-value>)',
|
||||
800: 'rgb(var(--color-primary-800)/<alpha-value>)',
|
||||
900: 'rgb(var(--color-primary-900)/<alpha-value>)',
|
||||
950: 'rgb(var(--color-primary-950)/<alpha-value>)',
|
||||
},
|
||||
secondary: {
|
||||
0: 'rgb(var(--color-secondary-0)/<alpha-value>)',
|
||||
50: 'rgb(var(--color-secondary-50)/<alpha-value>)',
|
||||
100: 'rgb(var(--color-secondary-100)/<alpha-value>)',
|
||||
200: 'rgb(var(--color-secondary-200)/<alpha-value>)',
|
||||
300: 'rgb(var(--color-secondary-300)/<alpha-value>)',
|
||||
400: 'rgb(var(--color-secondary-400)/<alpha-value>)',
|
||||
500: 'rgb(var(--color-secondary-500)/<alpha-value>)',
|
||||
600: 'rgb(var(--color-secondary-600)/<alpha-value>)',
|
||||
700: 'rgb(var(--color-secondary-700)/<alpha-value>)',
|
||||
800: 'rgb(var(--color-secondary-800)/<alpha-value>)',
|
||||
900: 'rgb(var(--color-secondary-900)/<alpha-value>)',
|
||||
950: 'rgb(var(--color-secondary-950)/<alpha-value>)',
|
||||
},
|
||||
tertiary: {
|
||||
50: 'rgb(var(--color-tertiary-50)/<alpha-value>)',
|
||||
100: 'rgb(var(--color-tertiary-100)/<alpha-value>)',
|
||||
200: 'rgb(var(--color-tertiary-200)/<alpha-value>)',
|
||||
300: 'rgb(var(--color-tertiary-300)/<alpha-value>)',
|
||||
400: 'rgb(var(--color-tertiary-400)/<alpha-value>)',
|
||||
500: 'rgb(var(--color-tertiary-500)/<alpha-value>)',
|
||||
600: 'rgb(var(--color-tertiary-600)/<alpha-value>)',
|
||||
700: 'rgb(var(--color-tertiary-700)/<alpha-value>)',
|
||||
800: 'rgb(var(--color-tertiary-800)/<alpha-value>)',
|
||||
900: 'rgb(var(--color-tertiary-900)/<alpha-value>)',
|
||||
950: 'rgb(var(--color-tertiary-950)/<alpha-value>)',
|
||||
},
|
||||
error: {
|
||||
0: 'rgb(var(--color-error-0)/<alpha-value>)',
|
||||
50: 'rgb(var(--color-error-50)/<alpha-value>)',
|
||||
100: 'rgb(var(--color-error-100)/<alpha-value>)',
|
||||
200: 'rgb(var(--color-error-200)/<alpha-value>)',
|
||||
300: 'rgb(var(--color-error-300)/<alpha-value>)',
|
||||
400: 'rgb(var(--color-error-400)/<alpha-value>)',
|
||||
500: 'rgb(var(--color-error-500)/<alpha-value>)',
|
||||
600: 'rgb(var(--color-error-600)/<alpha-value>)',
|
||||
700: 'rgb(var(--color-error-700)/<alpha-value>)',
|
||||
800: 'rgb(var(--color-error-800)/<alpha-value>)',
|
||||
900: 'rgb(var(--color-error-900)/<alpha-value>)',
|
||||
950: 'rgb(var(--color-error-950)/<alpha-value>)',
|
||||
},
|
||||
success: {
|
||||
0: 'rgb(var(--color-success-0)/<alpha-value>)',
|
||||
50: 'rgb(var(--color-success-50)/<alpha-value>)',
|
||||
100: 'rgb(var(--color-success-100)/<alpha-value>)',
|
||||
200: 'rgb(var(--color-success-200)/<alpha-value>)',
|
||||
300: 'rgb(var(--color-success-300)/<alpha-value>)',
|
||||
400: 'rgb(var(--color-success-400)/<alpha-value>)',
|
||||
500: 'rgb(var(--color-success-500)/<alpha-value>)',
|
||||
600: 'rgb(var(--color-success-600)/<alpha-value>)',
|
||||
700: 'rgb(var(--color-success-700)/<alpha-value>)',
|
||||
800: 'rgb(var(--color-success-800)/<alpha-value>)',
|
||||
900: 'rgb(var(--color-success-900)/<alpha-value>)',
|
||||
950: 'rgb(var(--color-success-950)/<alpha-value>)',
|
||||
},
|
||||
warning: {
|
||||
0: 'rgb(var(--color-warning-0)/<alpha-value>)',
|
||||
50: 'rgb(var(--color-warning-50)/<alpha-value>)',
|
||||
100: 'rgb(var(--color-warning-100)/<alpha-value>)',
|
||||
200: 'rgb(var(--color-warning-200)/<alpha-value>)',
|
||||
300: 'rgb(var(--color-warning-300)/<alpha-value>)',
|
||||
400: 'rgb(var(--color-warning-400)/<alpha-value>)',
|
||||
500: 'rgb(var(--color-warning-500)/<alpha-value>)',
|
||||
600: 'rgb(var(--color-warning-600)/<alpha-value>)',
|
||||
700: 'rgb(var(--color-warning-700)/<alpha-value>)',
|
||||
800: 'rgb(var(--color-warning-800)/<alpha-value>)',
|
||||
900: 'rgb(var(--color-warning-900)/<alpha-value>)',
|
||||
950: 'rgb(var(--color-warning-950)/<alpha-value>)',
|
||||
},
|
||||
info: {
|
||||
0: 'rgb(var(--color-info-0)/<alpha-value>)',
|
||||
50: 'rgb(var(--color-info-50)/<alpha-value>)',
|
||||
100: 'rgb(var(--color-info-100)/<alpha-value>)',
|
||||
200: 'rgb(var(--color-info-200)/<alpha-value>)',
|
||||
300: 'rgb(var(--color-info-300)/<alpha-value>)',
|
||||
400: 'rgb(var(--color-info-400)/<alpha-value>)',
|
||||
500: 'rgb(var(--color-info-500)/<alpha-value>)',
|
||||
600: 'rgb(var(--color-info-600)/<alpha-value>)',
|
||||
700: 'rgb(var(--color-info-700)/<alpha-value>)',
|
||||
800: 'rgb(var(--color-info-800)/<alpha-value>)',
|
||||
900: 'rgb(var(--color-info-900)/<alpha-value>)',
|
||||
950: 'rgb(var(--color-info-950)/<alpha-value>)',
|
||||
},
|
||||
typography: {
|
||||
0: 'rgb(var(--color-typography-0)/<alpha-value>)',
|
||||
50: 'rgb(var(--color-typography-50)/<alpha-value>)',
|
||||
100: 'rgb(var(--color-typography-100)/<alpha-value>)',
|
||||
200: 'rgb(var(--color-typography-200)/<alpha-value>)',
|
||||
300: 'rgb(var(--color-typography-300)/<alpha-value>)',
|
||||
400: 'rgb(var(--color-typography-400)/<alpha-value>)',
|
||||
500: 'rgb(var(--color-typography-500)/<alpha-value>)',
|
||||
600: 'rgb(var(--color-typography-600)/<alpha-value>)',
|
||||
700: 'rgb(var(--color-typography-700)/<alpha-value>)',
|
||||
800: 'rgb(var(--color-typography-800)/<alpha-value>)',
|
||||
900: 'rgb(var(--color-typography-900)/<alpha-value>)',
|
||||
950: 'rgb(var(--color-typography-950)/<alpha-value>)',
|
||||
white: '#FFFFFF',
|
||||
gray: '#D4D4D4',
|
||||
black: '#181718',
|
||||
},
|
||||
outline: {
|
||||
0: 'rgb(var(--color-outline-0)/<alpha-value>)',
|
||||
50: 'rgb(var(--color-outline-50)/<alpha-value>)',
|
||||
100: 'rgb(var(--color-outline-100)/<alpha-value>)',
|
||||
200: 'rgb(var(--color-outline-200)/<alpha-value>)',
|
||||
300: 'rgb(var(--color-outline-300)/<alpha-value>)',
|
||||
400: 'rgb(var(--color-outline-400)/<alpha-value>)',
|
||||
500: 'rgb(var(--color-outline-500)/<alpha-value>)',
|
||||
600: 'rgb(var(--color-outline-600)/<alpha-value>)',
|
||||
700: 'rgb(var(--color-outline-700)/<alpha-value>)',
|
||||
800: 'rgb(var(--color-outline-800)/<alpha-value>)',
|
||||
900: 'rgb(var(--color-outline-900)/<alpha-value>)',
|
||||
950: 'rgb(var(--color-outline-950)/<alpha-value>)',
|
||||
},
|
||||
background: {
|
||||
0: 'rgb(var(--color-background-0)/<alpha-value>)',
|
||||
50: 'rgb(var(--color-background-50)/<alpha-value>)',
|
||||
100: 'rgb(var(--color-background-100)/<alpha-value>)',
|
||||
200: 'rgb(var(--color-background-200)/<alpha-value>)',
|
||||
300: 'rgb(var(--color-background-300)/<alpha-value>)',
|
||||
400: 'rgb(var(--color-background-400)/<alpha-value>)',
|
||||
500: 'rgb(var(--color-background-500)/<alpha-value>)',
|
||||
600: 'rgb(var(--color-background-600)/<alpha-value>)',
|
||||
700: 'rgb(var(--color-background-700)/<alpha-value>)',
|
||||
800: 'rgb(var(--color-background-800)/<alpha-value>)',
|
||||
900: 'rgb(var(--color-background-900)/<alpha-value>)',
|
||||
950: 'rgb(var(--color-background-950)/<alpha-value>)',
|
||||
error: 'rgb(var(--color-background-error)/<alpha-value>)',
|
||||
warning: 'rgb(var(--color-background-warning)/<alpha-value>)',
|
||||
muted: 'rgb(var(--color-background-muted)/<alpha-value>)',
|
||||
success: 'rgb(var(--color-background-success)/<alpha-value>)',
|
||||
info: 'rgb(var(--color-background-info)/<alpha-value>)',
|
||||
light: '#FBFBFB',
|
||||
dark: '#181719',
|
||||
},
|
||||
indicator: {
|
||||
primary: 'rgb(var(--color-indicator-primary)/<alpha-value>)',
|
||||
info: 'rgb(var(--color-indicator-info)/<alpha-value>)',
|
||||
error: 'rgb(var(--color-indicator-error)/<alpha-value>)',
|
||||
},
|
||||
},
|
||||
fontFamily: {
|
||||
'heading': undefined,
|
||||
'body': undefined,
|
||||
'mono': undefined,
|
||||
'jakarta': ['var(--font-plus-jakarta-sans)'],
|
||||
'roboto': ['var(--font-roboto)'],
|
||||
'code': ['var(--font-source-code-pro)'],
|
||||
'inter': ['var(--font-inter)'],
|
||||
'space-mono': ['var(--font-space-mono)'],
|
||||
},
|
||||
fontWeight: {
|
||||
extrablack: '950',
|
||||
},
|
||||
fontSize: {
|
||||
'2xs': '10px',
|
||||
},
|
||||
boxShadow: {
|
||||
'hard-1': '-2px 2px 8px 0px rgba(38, 38, 38, 0.20)',
|
||||
'hard-2': '0px 3px 10px 0px rgba(38, 38, 38, 0.20)',
|
||||
'hard-3': '2px 2px 8px 0px rgba(38, 38, 38, 0.20)',
|
||||
'hard-4': '0px -3px 10px 0px rgba(38, 38, 38, 0.20)',
|
||||
'hard-5': '0px 2px 10px 0px rgba(38, 38, 38, 0.10)',
|
||||
'soft-1': '0px 0px 10px rgba(38, 38, 38, 0.1)',
|
||||
'soft-2': '0px 0px 20px rgba(38, 38, 38, 0.2)',
|
||||
'soft-3': '0px 0px 30px rgba(38, 38, 38, 0.1)',
|
||||
'soft-4': '0px 0px 40px rgba(38, 38, 38, 0.1)',
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
4. 使用 `GluestackUIProvider` 包括着整个组件:
|
||||
|
||||
```tsx
|
||||
// App.tsx
|
||||
import { GluestackUIProvider } from '@/components/ui/gluestack-ui-provider'
|
||||
import '@/global.css' // Make sure this path is correct
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<GluestackUIProvider>{/* Add your app code here */}</GluestackUIProvider>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## 测试
|
||||
|
||||
下载对应组件:
|
||||
|
||||
```bash
|
||||
npx gluestack-ui add button
|
||||
```
|
||||
|
||||
引入使用:
|
||||
|
||||
```tsx
|
||||
import { StyleSheet, Text, View } from 'react-native'
|
||||
import { SafeAreaView } from 'react-native-safe-area-context'
|
||||
import { Button, ButtonText } from '@/components/ui/button'
|
||||
import { GluestackUIProvider } from '@/components/ui/gluestack-ui-provider'
|
||||
import '@/styles/global.css'
|
||||
|
||||
export default function Layout() {
|
||||
return (
|
||||
<GluestackUIProvider>
|
||||
<SafeAreaView style={styles.container}>
|
||||
<View className="flex-1 items-center justify-center bg-white">
|
||||
<Text className="text-xl font-bold text-blue-500">
|
||||
Welcome to Nativewind!
|
||||
</Text>
|
||||
<Button>
|
||||
<ButtonText>你好</ButtonText>
|
||||
</Button>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
</GluestackUIProvider>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: '#f5f5f5',
|
||||
paddingHorizontal: 16,
|
||||
}
|
||||
})
|
||||
```
|
||||
@@ -0,0 +1,6 @@
|
||||
Lucide 是一个轻量级、开源的图标库
|
||||
> 官网:https://lucide.dev/icons/
|
||||
|
||||
```bash
|
||||
npx expo add lucide-react-native
|
||||
```
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"label": "第三方库",
|
||||
"position": 2,
|
||||
"link": {
|
||||
"type": "generated-index"
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user