React Native填坑之旅--多平臺支援之Web

小紅星閃啊閃發表於2021-11-04

如果你使用react native開發了app,會不會想有一個站點呢。如果你想,那麼react-native-web就有用武之地了。只要不是平臺相關的元件基本都可以複用,包括js樣式。更不用說內建的accessibility。

react-native-web比較傾向於推薦expo的,更多的介紹是關於create-react-app生成的程式碼的。但是這篇文章是對於已經能存在的mobile的專案而言的。這是一種更急普遍,或者更需要這篇文章的情況。

程式碼在這裡

新增依賴

要是使用react-native-web首先要新增必須的依賴。首先跳轉到專案的根目錄

cd path-to-your-project

先把react-native-web加上

yarn add react-native-web

然後相關依賴

yarn add -D babel-plugin-react-native-web webpack webpack-cli webpack-dev-server html-webpack-plugin react-dom babel-loader url-loader @svgr/webpack

如果你有前端的開發經驗的話,看看我們現在專案的結構你會發現少了很多。作為一個單頁應用,單頁都還沒有呢。dev server和打包的老大哥webpack也沒有。這些我們也都要一一加上。
index.html

<!DOCTYPE html>
<html>
  <!-- 略 -->
  <body>
    <div id="app-root"></div>
  </body>
</html>

這個就是單頁應用的那個單頁。是作為整個app的承載頁。所有的js都執行在這個頁面裡。

webpack.config.js

// ...
const compileNodeModules = [
  // 新增需要編譯的react-native包
].map((moduleName) => path.resolve(appDirectory, `node_modules/${moduleName}`));
// ...

webpack是打包工具。開發的時候跑dev server,發生產的時候打生產包。這裡需要制定webpack把依賴到的包都編譯了。

如果你不這麼做的話,大概率你會遇到這個麼一個報錯。比如你用了react-native-gesture-handler

ERROR in ./node_modules/react-native-gesture-handler
Module parse failed: Unexpected token
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders

然後我們也會新增所有需要編譯的程式碼。比如index.web.jsApp.web.tsx。還有src目錄下的所有程式碼,以及上文提到的所有的庫。

// ...
const babelLoaderConfiguration = {
  test: /\.js$|tsx?$/,
  // Add every directory that needs to be compiled by Babel during the build.
  include: [
    path.resolve(__dirname, 'index.web.js'), <-- Entry to web project
    path.resolve(__dirname, 'App.web.tsx'), // <-- or App.js
    path.resolve(__dirname, 'src'), // <-- Main source folder
    ...compileNodeModules, // <-- Modules we compiles above
  ],
// ...

還有一個很重要的檔案:index.web.js

這個檔案是給上文說的index.html掛載react-native-web的元件的。就如同在index.js看到的要AppRegistry.registerComponent一樣。在web開發中也需要註冊元件,同時還要掛載元件。
index.web.js

import { AppRegistry } from 'react-native';
import { name as appName } from './app.json';
import App from './App';
if (module.hot) {
  module.hot.accept();
}
AppRegistry.registerComponent(appName, () => App); // 1
AppRegistry.runApplication(appName, { // 2
  initialProps: {},
  rootTag: document.getElementById('app-root'),
});

// 1. 和index.js一樣,註冊元件
// 2. 掛載元件。細節沒看,估計是用來呼叫react-dom來實現的。

不同平臺指定程式碼。比如這裡的index.web.js。
就是特指給web平臺執行的程式碼。在ios和android也可以分別指定在某個平臺執行的程式碼。
比如只在ios執行,可以命名為SomeComp.ios.js。在android的就可以叫做SomeComp.android.js。

另外需要新增一個App.web.tsx

//...
      <TouchableOpacity
        onPress={() => setCount(count + 1)}
        style={styles.button}>
        <Text>Click me!</Text>
      </TouchableOpacity>
      <WebSection title="web title">
        <Text>Hello Web</Text>
      </WebSection>
      <Text>You clicked {count} times!</Text>
//...

明顯可以看到處理點選的TouchableOpacity和顯示文字的Text元件都可以正常使用。

複用程式碼

YouKnowApp/src/WebSection.tsx這個檔案是直接複用的js/components/Section.tsx這個檔案。直接用會出問題。

Module parse failed: Unexpected token (11:12)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file

只要註釋掉這一行就好了。

// import { Colors } from 'react-native/Libraries/NewAppScreen';

估計這行程式碼使用了flow導致的。

Flow是FB開發的一個js的型別增強工具。現在用的已經很少了,主要是FB自己的很多程式碼還包含了這部分。

解決的辦法可能要查閱一下是否存在一個webpack的loader可以專門處理flow程式碼。

配置Scripts

最後在package.json檔案新增指令碼執行的命令

    "build": "rm -rf dist/ && webpack --mode=production --config webpack.config.js",
    "web": "webpack serve --mode=development --config webpack.config.js"

執行

yarn web

檢視http://localhost:8080就可以看到效果了。
image.png

後續遇到多平臺程式碼共享的問題再繼續更新。

相關文章