You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Expo深度链接跳转网站报404,需引导至已安装APP

Expo深度链接唤起APP解决方案

我遇到了Expo深度链接的问题:点击https://monkify.app相关链接时会跳转到网站并抛出404错误,因为该网站仅作为着陆页存在,没有对应路径的页面。我希望用户访问该网站链接时,能直接唤起设备上已安装的APP;若未安装APP,则停留在着陆页。

以下是我的现有代码:

App.js

import React, { useEffect, useState, useCallback, useMemo } from "react";
import { NavigationContainer } from "@react-navigation/native";
import RootNavigator from "./src/navigation/RootNavigation";
import AuthNavigator from "./src/navigation/AuthNavigator";
import Media from "./src/components/Media";
import * as Linking from "expo-linking";
import { AuthContext } from "./src/components/Context";
import AudioProvider from "./src/context/AudioProvider";

export default function App() {
  const linking = {
    prefixes: [Linking.createURL("/"), "https://monkify.app", "monkify.app://"],
    config: {
      screens: {
        Podcast: "Podcast/:id",
        Video: "Video/:id",
      },
    },
  };

  return (
    <AuthContext.Provider value={authContext}>
      <AudioProvider>
        <NavigationContainer
          linking={linking}
          onLayout={onLayoutRootView}
          onStateChange={(state) => {
            setCurrentRouteName(state?.routes[state?.index]?.name);
          }}
        >
          <RootNavigator />
        </NavigationContainer>
      </AudioProvider>
    </AuthContext.Provider>
  );
}

App.json

{
  "expo": {
    "name": "Monkify",
    "slug": "Monkify",
    "owner": "monkify",
    "scheme": "monkify.app",
    "version": "1.0.9",
    "orientation": "default",
    "icon": "./assets/Images/Logo.png",
    "userInterfaceStyle": "light",
    "plugins": [
      "expo-apple-authentication",
      "expo-image-picker",
      "sentry-expo",
      [
        "expo-notifications",
        {
          "icon": "./assets/Images/NotificationLogo.png",
          "color": "#ffffff"
        }
      ],
      [
        "expo-build-properties",
        {
          "ios": {
            "useFrameworks": "static"
          }
        }
      ],
      [
        "expo-screen-orientation",
        {
          "initialOrientation": "DEFAULT"
        }
      ]
    ],
    "updates": {
      "fallbackToCacheTimeout": 0,
      "enabled": false
    },
    "assetBundlePatterns": [
      "**/*"
    ],
    "ios": {
      "requireFullScreen": true,
      "supportsTablet": true,
      "bitcode": false,
      "bundleIdentifier": "Enter your bundle identifier here",
      "buildNumber": "32",
      "associatedDomains": [
        "applinks:monkify.app"
      ],
      "infoPlist": {
        "UIBackgroundModes": [
          "audio"
        ],
        "NSPhotoLibraryUsageDescription": "Monkify needs access to your photos to set your profile picture"
      }
    },
    "android": {
      "package": "Enter your package name here",
      "versionCode": 19,
      "useNextNotificationsApi": true,
      "intentFilters": [
        {
          "action": "VIEW",
          "autoVerify": true,
          "data": [
            {
              "scheme": "https",
              "host": "monkify.app",
              "pathPrefix": "/"
            }
          ],
          "category": [
            "BROWSABLE",
            "DEFAULT"
          ]
        }
      ],
      "permissions": [
        "android.permission.RECORD_AUDIO"
      ]
    },
    "extra": {
      "eas": {
        "projectId": "Enter your project id here"
      }
    }
  },
  "web": {
    "favicon": "./assets/favicon.png"
  }
}

RootNavigator.js

import * as React from "react";
import { useState, useEffect, useContext } from "react";
import Media from "../components/Media";
import AuthNavigator from "./AuthNavigator";
import AudioProvider from "../context/AudioProvider";
import { AuthContext } from "../components/Context";
import * as Linking from "expo-linking";
import { useNavigation } from "@react-navigation/native";
import { isEmptyVar } from "../helper/isEmpty";

const RootNavigator = () => {
  const { token } = useContext(AuthContext);
  const navigation = useNavigation();

  const handleURL = async (event) => {
    const url = await Linking.getInitialURL();
    if (!isEmptyVar(url) || !isEmptyVar(event)) {
      let { path, queryParams } = Linking.parse(
        !isEmptyVar(event) ? event.url : url
      );
      if (path === "Podcast") {
        navigation.navigate("Podcast", {
          id: queryParams.id,
        });
      } else if (path === "Video") {
        navigation.navigate("Video", {
          id: queryParams.id,
        });
      }
    }
  };

  useEffect(() => {
    handleURL();
  }, []);

  useEffect(() => {
    let subscribe = Linking.addEventListener("url", handleURL);
    return () => {
      subscribe.remove();
    };
  }, []);

  return <AudioProvider>{token ? <Media /> : <AuthNavigator />}</AudioProvider>;
};

export default RootNavigator;

一、网站端配置(核心解决404+唤起APP)

你的网站仅作为着陆页,没有对应子路径页面,需做以下配置:

  1. 配置平台验证文件

    • iOS:在网站根目录放置apple-app-site-association(无后缀)文件,内容替换为:
      {
        "applinks": {
          "apps": [],
          "details": [
            {
              "appID": "你的TeamID.你的BundleIdentifier",
              "paths": ["*"]
            }
          ]
        }
      }
      
      注:TeamID可在苹果开发者后台查看,BundleIdentifier需与App.json中配置一致。
    • Android:在网站.well-known目录下放置assetlinks.json文件,内容替换为:
      [
        {
          "relation": ["delegate_permission/common.handle_all_urls"],
          "target": {
            "namespace": "android_app",
            "package_name": "你的AndroidPackageName",
            "sha256_cert_fingerprints": ["你的APP签名SHA256指纹"]
          }
        }
      ]
      
      注:SHA256指纹可通过expo credentials:manager命令查看。
  2. 添加网页唤起脚本
    在网站所有页面(尤其是404页面)插入以下脚本,优先尝试唤起APP,失败则跳转回着陆页:

    (function() {
      const appUrl = "monkify://" + window.location.pathname + window.location.search;
      const timeout = 1000;
      const startTime = Date.now();
    
      // 尝试唤起APP
      window.location.href = appUrl;
    
      // 唤起失败则跳转回着陆页
      setTimeout(() => {
        if (Date.now() - startTime < timeout * 2) {
          window.location.href = "/";
        }
      }, timeout);
    })();
    

二、Expo配置优化

  1. 修正Scheme字段
    Scheme仅需填自定义协议前缀,去掉.app后缀:

    "scheme": "monkify"
    

    自定义协议变为monkify://,https://monkify.app作为Universal Links使用。

  2. 保持关联域名配置
    iOS的associatedDomains和Android的intentFilters配置正确,无需修改。

三、React Navigation链接逻辑简化

React Navigation的linking配置可自动处理路由跳转,无需手动解析:

  1. 完善App.js的linking配置
    确保路由名称与导航器内的屏幕名称完全匹配:

    const linking = {
      prefixes: ["https://monkify.app", "monkify://"],
      config: {
        screens: {
          Media: {
            screens: {
              Podcast: "Podcast/:id",
              Video: "Video/:id",
            },
          },
        },
      },
    };
    
  2. 移除RootNavigator中的手动监听
    删除handleURL函数及对应的useEffect,React Navigation会自动处理冷启动、后台唤起的链接跳转逻辑。

验证步骤

  1. 用EAS Build重新构建并发布APP
  2. 确认验证文件可通过网站域名访问(如https://monkify.app/apple-app-site-association)
  3. 测试链接:打开https://monkify.app/Podcast/123,验证已安装APP时能否唤起并跳转对应页面,未安装时能否回到着陆页

内容的提问来源于stack exchange,提问作者Komal Parulekar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 02:52:05