跨端系列: 使用 Vite 打包 React Native Web 项目

背景

当前, 我们使用 React Native 开发页面, 以支持 Android 和 iOS 平台. 现在我们在探索是否能够让我们的 React Native 项目能够直接运行在 Web 端, 经过调研, 我们计划使用 React Native Web 来达成这一目的.

但是, 由于 React Native 的开发模式, 使得 React Native 项目的开发和构建过程与传统的 Web 开发有很大的不同. 例如, React Native 项目使用 Metro 作为打包工具, 而 Web 项目使用 Webpack 或者 Vite 作为打包工具. 为了提高开发效率, 我们希望能够使用 Vite 来打包 React Native Web 项目.

Tips: 本文暂不关注 React Native 社区诸多原生能力在 Web 端的可能性, 假定你使用的 React Native 的 API 已经被 React Native Web 支持, React Native 开源库亦然.

不过还好, 很多知名且非常重要的库, 都有考虑到 Web 端的支持, 比如:

  • react-navigation
  • react-native-screens
  • react-native-gesture-handler
  • react-native-svg
  • react-native-reanimated
  • react-native-vector-icons

调研

babel-plugin-react-native-web

首先, 我们深入的阅读了 React Native Web 的文档, 并重点分析了其提供的 babel-plugin-react-native-web 插件的源码.

因为 React Native Web 提供了和 React Native 一致的 API , 所以该插件的最主要工作就是将项目代码以及 node_modules 依赖包中的 react-native 依赖替换为 react-native-web .

另外, 建议该插件还可以保证只打包你用到的 react-native-web 包中的组件代码, 以减少打包体积.

集成步骤

为了简单起见, 让我们先抛开 React Native 原生开发所需要的繁琐细节, 从一个干净的 Vite 项目开始

shell 复制代码
pnpm create vite my-react-native-web --template react-ts

安装依赖包

对于 React 18 版本

shell 复制代码
pnpm add react@18 react-dom@18
pnpm add react-native@0.73.6 react-native-web@0.19.10

# 若需要支持 TypeScript
pnpm add -D @types/react@18 @types/react-dom@18

如果你的项目使用的是 React 17 的版本, 需要降低 React Native 和 React Native Web 的版本

shell 复制代码
pnpm add react@17 react-dom@17
pnpm add react-native@0.68.7 react-native-web@0.18.12

# 若需要支持 TypeScript
pnpm add -D @types/react@17 @types/react-dom@17
pnpm add -D @types/react-native@0.68.7

注意 1: 若需要其他版本的 React 或者 React Native, 请自行按照如上逻辑分析并安装对应版本.

注意 2: 新版本的 React Native 内置 TypeScript 类型文件, 老版本需要额外安装对应版本的 @types/react-native .

配置 Vite

首先, 使用 @vitejs/plugin-react 插件, 并将 react-native 映射为 react-native-web .

javascript 复制代码
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

export default defineConfig({
  plugins: [react()],
  alias: {
    "react-native": "react-native-web",
  },
});

但是如果我们想使用社区内为 React Native 或者 React Native Web 提供的开源库, 按照 React Native 的设定, 我们还需要通过如下配置, 实现平台特定文件的查找功能, 以及 JSX 相关支持.

javascript 复制代码
const extensions = [
  ".web.tsx",
  ".tsx",
  ".web.ts",
  ".ts",
  ".web.jsx",
  ".jsx",
  ".web.js",
  ".js",
  ".css",
  ".json",
  ".mjs",
];

export default defineConfig({
  resolve: {
    extensions,
  }
  optimizeDeps: {
    esbuildOptions: {
      loader: { ".js": "jsx" },
      resolveExtensions: extensions,
      jsx: "automatic",
    }
  }
})

另外, 我们还需要将 React Native 中使用的一些变量进行替换:

javascript 复制代码
const development = process.env.NODE_ENV === "development";

export default defineConfig({
  define: {
    global: "window",
    __DEV__: JSON.stringify(development),
    DEV: JSON.stringify(development),
    "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV),
  },
});

详情请参阅

VSCode 配置

安装 React Native Tools 插件

开发

入口代码

index.html

非常简单, 和普通的 Vite 项目没有什么区别.

html 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Demo</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>

src/main.tsx

使用 React Native 的 AppRegistry API .

tsx 复制代码
import React from "react";
// import ReactDOM from 'react-dom/client'
import { AppRegistry } from "react-native";
import App from "./App.tsx";
import "./index.css";

const Root = () => (
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

const rootTag = document.getElementById("root");

// ReactDOM.createRoot(rootTag!).render(<Root />);

AppRegistry.registerComponent("App", () => Root);
AppRegistry.runApplication("App", {
  initialProps: {},
  rootTag,
});

Tips 1: 如何你只是想单独使用一些组件, 可以不使用 AppRegistry 相关的 API, 直接使用 React Dom 即可.

Tips 2: 尽管可以直接使用 ReactDOM.render , 但是会缺少 AppRegistry 提供给组件的全局上下文

src/App.tsx

直接使用 React Native 的组件即可

tsx 复制代码
import React from "react";
import { View, Text } from "react-native";

export default function App() {
  return (
    <View>
      <Text>Hello World</Text>
    </View>
  );
}

src/index.css

根据 React Native Web 的文档, React Native 通常是一个全屏页面, 我们会使用一个 ScrollView 作为根元素.

对于 Web 端, 我们需要在 HTML 文档中添加如下样式:

css 复制代码
/* These styles make the body full-height */
html,
body {
  height: 100%;
}
/* These styles disable body scrolling if you are using <ScrollView> */
body {
  overflow: hidden;
}
/* These styles make the root element full-height */
#root {
  display: flex;
  height: 100%;
}

注意: 如何你只是想单独使用一些组件, 可以不添加如上样式.

功能开发

接下来, 我们就可以按照 React Native 的开发模式, 开发我们的页面. 相关文档请参阅 React NativeReact Native Web.

总结

现在, 我们已经可以使用 Vite 打包 React Native Web 项目了. 但是, 由于 React Native 和 Web 的开发模式不同, 所以在开发过程中, 你可能会遇到一些问题, 例如:

  • React Native 的 API 或者组件在 Web 端不支持
  • React Native 的样式在 Web 端不支持

但是, 我们已经在路上了, 后续会有更多的文章来讨论跨端的一些问题.

参考资料

相关推荐
熊的猫34 分钟前
JS 中的类型 & 类型判断 & 类型转换
前端·javascript·vue.js·chrome·react.js·前端框架·node.js
瑶琴AI前端1 小时前
uniapp组件实现省市区三级联动选择
java·前端·uni-app
会发光的猪。1 小时前
如何在vscode中安装git详细新手教程
前端·ide·git·vscode
我要洋人死2 小时前
导航栏及下拉菜单的实现
前端·css·css3
科技探秘人2 小时前
Chrome与火狐哪个浏览器的隐私追踪功能更好
前端·chrome
科技探秘人2 小时前
Chrome与傲游浏览器性能与功能的深度对比
前端·chrome
JerryXZR3 小时前
前端开发中ES6的技术细节二
前端·javascript·es6
七星静香3 小时前
laravel chunkById 分块查询 使用时的问题
java·前端·laravel
q2498596933 小时前
前端预览word、excel、ppt
前端·word·excel
小华同学ai3 小时前
wflow-web:开源啦 ,高仿钉钉、飞书、企业微信的审批流程设计器,轻松打造属于你的工作流设计器
前端·钉钉·飞书