中文字幕精品亚洲无线码二区,国产黄a三级三级三级看三级,亚洲七七久久桃花影院,丰满少妇被猛烈进入,国产小视频在线观看网站

React Native 錯誤處理完全指(zhi)南

React Native 錯誤處理完全指南

深入解析跨平臺(tai)應用中的 JS 錯誤、原生(sheng)崩(beng)潰及異常監控方案(an),附實戰(zhan)代(dai)碼與(yu)最佳實踐。

在 React Native 跨平臺(tai)開發(fa)中(zhong),錯(cuo)誤(wu)處理是保障應用(yong)穩(wen)定性與用(yong)戶體驗的核(he)心環節。不同于(yu)純 Web 應用(yong)或原(yuan)生應用(yong),React Native 應用(yong)的錯(cuo)誤(wu)來源更為(wei)復雜——既包含 JavaScript 層(ceng)的邏輯錯(cuo)誤(wu),也涉及(ji)(ji) iOS/Android 雙(shuang)端的原(yuan)生模塊異常,甚(shen)至可能因 JS 與原(yuan)生通信(xin)異常引發(fa)崩潰。本文將系統(tong)梳理 React Native 中(zhong)的錯(cuo)誤(wu)類型(xing)、核(he)心處理工具(ju)、實戰場景解(jie)決方案(an)及(ji)(ji)監(jian)控(kong)策(ce)略(lve),幫助開發(fa)者(zhe)構建(jian)更穩(wen)健(jian)的跨平臺(tai)應用(yong)。

一、React Native 錯誤類型解析

React Native 應用的錯誤主要分(fen)為兩大類:JavaScript 層錯誤與原生層錯誤,二者(zhe)在觸發(fa)場景、表現形(xing)式及處理(li)方式上存(cun)在顯著差異。

(一)JavaScript 層錯誤

這類錯(cuo)誤發生在 React Native 的 JS 運行時(如 Hermes 或 JSC),多由代碼邏輯缺陷(xian)導致,常見場景包括(kuo):變(bian)量未定義、函數調用方式(shi)錯(cuo)誤、數組越界、異步(bu)操(cao)作異常等(deng)。

關鍵特征

  • 開發環境下:會觸發 RedBox(紅色錯誤提示框),顯示錯誤信息、文件路徑及堆棧跟蹤,直接阻斷應用運行。
  • 生產環境下:默認不會顯示錯誤提示,若未處理會導致應用白屏、功能失效,嚴重時引發 JS 線程阻塞。
  • 可通過 React 生態工具或 JS 原生 API 捕獲。

常見示例

// 1. 變量未定義錯誤
function greet() {
  console.log(name); // name 未聲明,觸發 ReferenceError
}

// 2. 異步操作錯誤(未捕獲的 Promise 拒絕)
const fetchData = async () => {
  const response = await fetch('//api.example.com/data');
  const data = await response.json();
  return data.user.name; // 若 data.user 為 undefined,觸發 TypeError
};
fetchData(); // 未添加 catch 處理,導致未捕獲 Promise 錯誤

(二)原生層錯誤

這類(lei)錯誤(wu)發生在(zai) iOS 或 Android 的原(yuan)生代(dai)碼中,常(chang)見于自定義原(yuan)生模塊、第(di)三方原(yuan)生庫(ku)兼容性問題、原(yuan)生 API 調用不當等場景,例如:iOS 中數組越(yue)界、Android 中空指針(zhen)異常(chang)、原(yuan)生模塊向 JS 傳遞非法數據等。

關鍵特征

  • 開發/生產環境下:通常直接導致應用 崩潰,并在原生日志(Xcode 控制臺、Android Logcat)中輸出堆棧跟蹤。
  • 難以通過 JS 層直接捕獲,需借助原生錯誤處理機制或跨層通信工具。
  • 影響范圍更廣,可能破壞應用進程穩定性,甚至導致用戶無法重啟應用。

常見示例

  1. iOS 原生錯誤(Swift)
// 自定義原生模塊中數組越界
@objc func getRandomItem(_ callback: RCTResponseSenderBlock) {
  let items = ["a", "b"]
  let randomIndex = 3 // 超出數組長度(0-1)
  let item = items[randomIndex] // 觸發 IndexOutOfRangeException,導致應用崩潰
  callback([nil, item])
}
  1. Android 原生錯誤(Kotlin)
// 原生模塊中空指針異常
@ReactMethod
fun showToast(message: String) {
  val toast = Toast.makeText(null, message, Toast.LENGTH_SHORT) // context 為 null,觸發 NullPointerException
  toast.show()
}

image

層級(ji)結構:應用層 → JavaScript 層(RedBox/白屏(ping))、原生層(iOS/Android 崩潰)→ 底(di)層運行時(Hermes/JSC、原生系統 API)

二、核心錯誤處理工具與 API

針(zhen)對不(bu)同類型的錯誤(wu),React Native 提供了多層(ceng)次的處理工具——從 React 內置的錯誤(wu)邊界(jie),到 JS 運(yun)行時 API,再到原生層(ceng)的崩潰(kui)捕獲機制。

(一)JavaScript 層核心處理工具

1. Error Boundaries(錯誤邊界)

Error Boundaries 是 React 16+ 引入的官方錯誤捕獲機制,專門用于捕獲子組件樹中的 JS 錯誤(包括渲染錯誤、生命周期方法錯誤),并展示降級 UI,避免整個組件樹崩潰。注意:它無法捕獲異步操作(zuo)(如(ru) setTimeout、Promise)、事件處理器(qi)中的錯(cuo)(cuo)誤及(ji)服務器(qi)端渲染錯(cuo)(cuo)誤。

實現方式

需創建一個類組件,實現 getDerivedStateFromError(更新錯誤狀態)和 componentDidCatch(日(ri)志上(shang)報)兩個生命周期方法(fa):

import React, { Component } from 'react';

class ErrorBoundary extends Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false, error: null };
  }

  // 靜態方法:捕獲錯誤并更新組件狀態,用于渲染降級 UI
  static getDerivedStateFromError(error) {
    return { hasError: true, error };
  }

  // 實例方法:捕獲錯誤信息,可用于日志上報
  componentDidCatch(error, errorInfo) {
    // 上報錯誤到監控平臺(如 Sentry)
    console.error('Error Boundary 捕獲錯誤:', error, errorInfo.componentStack);
  }

  render() {
    if (this.state.hasError) {
      // 降級 UI:向用戶展示友好提示
      return (
        <div style={{ padding: 20, textAlign: 'center' }}>
          <h2>頁面加載出錯了</h2>
          <p>{this.state.error?.message}</p>
          <button onClick={() => this.setState({ hasError: false })}>
            刷新頁面
          </button>
        </div>
      );
    }
    // 無錯誤時,渲染子組件樹
    return this.props.children;
  }
}

// 使用方式:包裹可能出錯的組件
export default function App() {
  return (
    <ErrorBoundary>
      <MainComponent /> {/* 可能觸發 JS 錯誤的核心組件 */}
    </ErrorBoundary>
  );
}

2. React Native Error Utils

ErrorUtils 是 React Native 內(nei)置的(de)(de) JS 錯(cuo)(cuo)誤(wu)捕(bu)獲(huo)工(gong)具,可(ke)全局監聽未被錯(cuo)(cuo)誤(wu)邊界捕(bu)獲(huo)的(de)(de) JS 錯(cuo)(cuo)誤(wu)(包括異步操(cao)作錯(cuo)(cuo)誤(wu)),相(xiang)當于(yu) JS 層(ceng)的(de)(de)“最后(hou)一道防(fang)線”。

使用方式

import { ErrorUtils } from 'react-native';

// 保存原始錯誤處理函數(可選,便于后續恢復默認行為)
const originalErrorHandler = ErrorUtils.getGlobalHandler();

// 自定義全局錯誤處理函數
const customErrorHandler = (error, isFatal) => {
  // isFatal:布爾值,標識錯誤是否致命(可能導致應用崩潰)
  console.error(`全局捕獲 JS 錯誤(${isFatal ? '致命' : '非致命'}):`, error);
  
  // 上報錯誤信息(如錯誤消息、堆棧跟蹤、設備信息)
  reportErrorToMonitor({
    message: error.message,
    stack: error.stack,
    isFatal,
    platform: Platform.OS,
  });

  // 若需要保留默認行為(如開發環境顯示 RedBox),可調用原始處理函數
  originalErrorHandler(error, isFatal);
};

// 注冊全局錯誤處理函數
ErrorUtils.setGlobalHandler(customErrorHandler);

3. Promise 錯誤捕獲

React Native 中未捕獲的 Promise 拒絕(如未添加 catch 的異步請(qing)求)會觸發(fa)警告(開發(fa)環境)或靜默失敗(bai)(生產(chan)環境),需(xu)通過以(yi)下(xia)方式統一(yi)處理:

// 監聽未捕獲的 Promise 拒絕
if (YellowBox) {
  // 開發環境:屏蔽特定警告(可選)
  YellowBox.ignoreWarnings(['Possible Unhandled Promise Rejection']);
}

// 全局捕獲未處理的 Promise 錯誤
process.on('unhandledRejection', (reason, promise) => {
  console.error('未處理的 Promise 錯誤:', reason, promise);
  // 上報錯誤信息
  reportErrorToMonitor({
    type: 'UnhandledPromiseRejection',
    message: reason?.message || String(reason),
    stack: reason?.stack,
  });
});

(二)原生層錯誤處理工具

原(yuan)生(sheng)層(ceng)錯誤(崩(beng)潰)無法通過 JS 工具(ju)直接捕獲,需分(fen)別在 iOS 和 Android 端實(shi)現原(yuan)生(sheng)錯誤處理邏輯,或使用第三(san)方(fang)監控庫簡(jian)化(hua)流程。

1. iOS 原生錯誤捕獲(Swift/Objective-C)

iOS 中可通過 NSSetUncaughtExceptionHandler 捕獲未處理的異常,通過 signal 監聽信號量錯誤(如內存訪問錯誤):

// AppDelegate.swift
import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
  var window: UIWindow?

  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // 注冊異常捕獲處理器
    NSSetUncaughtExceptionHandler { exception in
      let name = exception.name.rawValue
      let reason = exception.reason ?? "未知原因"
      let stackTrace = exception.callStackSymbols.joined(separator: "\n")
      
      // 保存錯誤日志到本地或上報
      let errorLog = "iOS 崩潰:\n名稱:\(name)\n原因:\(reason)\n堆棧:\(stackTrace)"
      print(errorLog)
      // 調用自定義上報方法
      ErrorReporter.shared.report(errorLog: errorLog)
    }

    // 監聽信號量錯誤(如 SIGSEGV、SIGABRT)
    let signals = [SIGABRT, SIGILL, SIGSEGV, SIGFPE, SIGBUS, SIGPIPE]
    for signal in signals {
      signal(signal) { sig in
        let errorLog = "iOS 信號量錯誤:信號 \(sig)"
        print(errorLog)
        ErrorReporter.shared.report(errorLog: errorLog)
        // 退出應用(避免僵尸進程)
        exit(sig)
      }
    }

    return true
  }
}

2. Android 原生錯誤捕獲(Kotlin/Java)

Android 中可通過實現 Thread.UncaughtExceptionHandler 捕獲線程未(wei)處(chu)理的異常(chang):

// CrashHandler.kt
import android.content.Context
import java.io.PrintWriter
import java.io.StringWriter

class CrashHandler(private val context: Context) : Thread.UncaughtExceptionHandler {
  // 保存默認異常處理器
  private val defaultHandler = Thread.getDefaultUncaughtExceptionHandler()

  override fun uncaughtException(t: Thread, e: Throwable) {
    // 收集錯誤信息
    val errorLog = StringBuilder()
    errorLog.append("Android 崩潰:\n線程:${t.name}\n")
    // 獲取堆棧跟蹤
    val sw = StringWriter()
    val pw = PrintWriter(sw)
    e.printStackTrace(pw)
    errorLog.append("堆棧:${sw.toString()}")

    // 保存日志或上報
    print(errorLog.toString())
    ErrorReporter.report(context, errorLog.toString())

    // 調用默認處理器(觸發系統崩潰提示)
    defaultHandler?.uncaughtException(t, e)
  }

  companion object {
    // 在 Application 中初始化
    fun init(context: Context) {
      Thread.setDefaultUncaughtExceptionHandler(CrashHandler(context))
    }
  }
}

// 初始化(在自定義 Application 類中)
class MyApp : Application() {
  override fun onCreate() {
    super.onCreate()
    CrashHandler.init(this)
  }
}

三、實戰場景:關鍵業務錯誤處理方案

結合 React Native 開發中(zhong)的(de)高頻場景,以下是針對性的(de)錯誤處(chu)理實踐方案(an),涵蓋網(wang)絡(luo)請求、異步操作、原生模(mo)塊(kuai)調(diao)用等核心環節。

(一)網絡請求錯誤處理

網絡請(qing)(qing)求(qiu)(qiu)是(shi)錯誤高發場景,需處理請(qing)(qing)求(qiu)(qiu)失敗(bai)、響(xiang)應異(yi)常、數據解析(xi)錯誤等問(wen)題,建議(yi)封裝統(tong)一的請(qing)(qing)求(qiu)(qiu)工(gong)具:

import axios from 'axios';
import { Alert } from 'react-native';

// 創建 axios 實例
const api = axios.create({
  baseURL: '//api.example.com',
  timeout: 10000,
});

// 請求攔截器:添加請求頭(如 Token)
api.interceptors.request.use(
  (config) => {
    const token = localStorage.getItem('token');
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
  },
  (error) => Promise.reject(error)
);

// 響應攔截器:統一處理錯誤
api.interceptors.response.use(
  (response) => response.data, // 成功時直接返回數據
  (error) => {
    let errorMessage = '網絡請求失敗,請稍后重試';
    
    // 分類處理錯誤
    if (error.response) {
      // 服務器返回錯誤(4xx/5xx)
      const status = error.response.status;
      const data = error.response.data;
      errorMessage = data?.message || `請求錯誤(${status})`;
      
      // 特殊狀態碼處理(如 401 未授權)
      if (status === 401) {
        // 觸發登出邏輯
        logout();
        errorMessage = '登錄已過期,請重新登錄';
      }
    } else if (error.request) {
      // 無響應(網絡錯誤、超時)
      errorMessage = error.code === 'ECONNABORTED' ? '請求超時' : '網絡異常,請檢查網絡連接';
    } else {
      // 請求配置錯誤(如參數錯誤)
      errorMessage = `請求配置錯誤:${error.message}`;
    }

    // 上報錯誤信息
    reportErrorToMonitor({
      type: 'NetworkError',
      message: errorMessage,
      stack: error.stack,
      requestConfig: error.config,
    });

    // 向用戶展示錯誤提示
    Alert.alert('提示', errorMessage);
    
    return Promise.reject(error);
  }
);

// 使用示例:獲取用戶數據
const fetchUser = async (userId) => {
  try {
    const data = await api.get(`/users/${userId}`);
    return data;
  } catch (error) {
    // 業務層可額外處理(如重試、降級)
    console.error('獲取用戶數據失敗:', error);
    throw error; // 向上傳遞錯誤,供組件處理
  }
};

(二)原生模塊調用錯誤處理

React Native 調用自(zi)定義原(yuan)生模塊時,需處理參數校驗、原(yuan)生邏輯異(yi)常等問題,建議通過 Promise 封(feng)裝原(yuan)生方法,便于捕(bu)獲錯(cuo)誤:

1. 原生模塊封裝(以 Android 為例)

// CustomModule.kt
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
import com.facebook.react.bridge.Promise

class CustomModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
  override fun getName() = "CustomModule"

  // 用 Promise 封裝原生方法,便于 JS 捕獲錯誤
  @ReactMethod
  fun processData(input: String, promise: Promise) {
    try {
      // 校驗參數
      if (input.isEmpty()) {
        throw IllegalArgumentException("輸入參數不能為空")
      }
      // 業務邏輯
      val result = "處理后的結果:$input"
      promise.resolve(result) // 成功回調
    } catch (e: Exception) {
      // 錯誤回調:傳遞錯誤信息到 JS 層
      promise.reject("PROCESS_ERROR", e.message, e)
    }
  }
}

2. JS 層調用與錯誤處理

import { NativeModules } from 'react-native';
const { CustomModule } = NativeModules;

// 調用原生模塊方法
const processNativeData = async (input) => {
  try {
    const result = await CustomModule.processData(input);
    return result;
  } catch (error) {
    // 捕獲原生模塊拋出的錯誤
    console.error('原生模塊調用失敗:', error.code, error.message);
    // 上報錯誤
    reportErrorToMonitor({
      type: 'NativeModuleError',
      module: 'CustomModule',
      method: 'processData',
      code: error.code,
      message: error.message,
    });
    // 向用戶提示
    Alert.alert('錯誤', `處理失敗:${error.message}`);
    throw error;
  }
};

// 使用示例
processNativeData('測試輸入')
  .then((result) => console.log(result))
  .catch((error) => console.error(error));

(三)異步操作錯誤處理(如文件讀寫、存儲)

React Native 中的異步操作(如 AsyncStorage、文件系統操作)需通過 try/catch 捕獲錯誤,并提供降級方案:

import AsyncStorage from '@react-native-async-storage/async-storage';
import { Alert } from 'react-native';

// 封裝 AsyncStorage 操作,統一處理錯誤
const StorageService = {
  async setItem(key, value) {
    try {
      const jsonValue = JSON.stringify(value);
      await AsyncStorage.setItem(key, jsonValue);
    } catch (error) {
      console.error(`存儲 ${key} 失敗:`, error);
      // 上報錯誤
      reportErrorToMonitor({
        type: 'StorageError',
        operation: 'setItem',
        key,
        message: error.message,
      });
      // 提示用戶
      Alert.alert('存儲錯誤', '數據保存失敗,請檢查存儲空間');
      throw error;
    }
  },

  async getItem(key) {
    try {
      const jsonValue = await AsyncStorage.getItem(key);
      return jsonValue != null ? JSON.parse(jsonValue) : null;
    } catch (error) {
      console.error(`獲取 ${key} 失敗:`, error);
      reportErrorToMonitor({
        type: 'StorageError',
        operation: 'getItem',
        key,
        message: error.message,
      });
      // 降級處理:返回默認值
      return null;
    }
  },
};

[圖例插(cha)入標識:React Native 異步(bu)操作錯誤處(chu)理(li)流程示意圖] 流程節點:發起異步(bu)操作(setItem/getItem)→ try 塊執行操作 → 成(cheng)功(gong):返回(hui)結果 / 失敗(bai):catch 捕獲(huo) → 錯誤上報 → 用戶提示/降(jiang)級處(chu)理(li)

四、錯誤監控與日志上報

僅在應用(yong)內處(chu)理錯(cuo)(cuo)誤不(bu)夠,還需建立完善的監控體系,實時收集錯(cuo)(cuo)誤信息,以便定位問題(ti)并優化。常用(yong)方案(an)分為(wei)“自(zi)建監控”和“第(di)三方監控”兩類。

(一)第三方監控工具(推薦)

第(di)三方工具已封裝好 JS 層與原(yuan)生層的(de)錯誤(wu)捕獲邏輯,支持(chi)崩潰分析、用戶(hu)行為追蹤、設(she)備信息收集等功(gong)能,主流工具包括:

1. Sentry

  • 支持 React Native 全平臺錯誤捕獲(JS 錯誤、原生崩潰)。
  • 提供詳細的堆棧跟蹤、錯誤上下文(用戶信息、設備信息、應用版本)。
  • 支持錯誤分組、告警通知(郵件、Slack)。

集成示例

// 安裝依賴:npm install @sentry/react-native && npx pod-install ios
import * as Sentry from '@sentry/react-native';

// 初始化 Sentry(在 App 入口處)
Sentry.init({
  dsn: '你的 Sentry DSN',
  environment: __DEV__ ? 'development' : 'production',
  tracesSampleRate: 1.0, // 性能監控采樣率
});

// 手動上報錯誤(可選)
try {
  // 可能出錯的邏輯
} catch (error) {
  Sentry.captureException(error, {
    extra: { customInfo: '額外上下文信息' },
    tags: { module: 'user', action: 'login' },
  });
}

2. Bugsnag

  • 專注于移動應用崩潰監控,支持 React Native 雙端原生崩潰捕獲。
  • 提供錯誤優先級分級、用戶會話跟蹤、版本趨勢分析。

3. Firebase Crashlytics

  • 與 Firebase 生態集成,適合使用 Firebase 的項目。
  • 免費版功能足夠滿足中小項目需求,支持崩潰統計與過濾。

(二)自建監控系統

若需定制化監控邏輯,可通過以下方式實(shi)現:

  1. 日志收集:在 JS 層和原生層捕獲錯誤后,將錯誤日志(含錯誤信息、堆棧、設備信息、用戶 ID)保存到本地。
  2. 日志上報:在應用下次啟動時,檢查本地日志,將未上報的錯誤通過網絡請求發送到自建服務器。
  3. 后臺管理:搭建后臺系統,對錯誤日志進行分類、統計、搜索,設置告警規則(如某類錯誤發生率超過 5% 時觸發告警)。

五、錯誤處理最佳實踐

(一)開發階段最佳實踐

  1. 啟用嚴格模式:在 App.js 中啟用 React 嚴格模式,提前發現潛在問題:
import { StrictMode } from 'react';
import { AppRegistry } from 'react-native';
import App from './App';
import { name as appName } from './app.json';

AppRegistry.registerComponent(appName, () => () => <StrictMode><App /></StrictMode>);
  1. 禁用生產環境的 RedBox/YellowBox:避免向用戶暴露錯誤細節,保護敏感信息。
  2. 編寫錯誤處理測試:使用 Jest 測試錯誤邊界、異常捕獲邏輯,確保其能正常工作。

(二)生產階段最佳實踐

  1. 避免靜默失敗:所有異步操作、原生模塊調用必須添加錯誤處理,禁止忽略 catch 塊。
  2. 提供友好的用戶提示:避免向用戶展示技術術語(如“NullPointerException”),用通俗語言說明問題(如“數據加載失敗,請檢查網絡”)。
  3. 實現錯誤降級:核心功能出錯時,提供替代方案(如網絡請求失敗時展示緩存數據)。
  4. 定期分析錯誤日志:優先修復高頻錯誤、嚴重崩潰(如啟動時崩潰),持續優化應用穩定性。
  5. 版本控制錯誤處理邏輯:記錄錯誤處理代碼的變更,便于回溯問題。

(三)跨平臺兼容性注意事項

  1. 原生模塊錯誤適配:針對 iOS 和 Android 原生模塊的差異,分別處理平臺專屬錯誤(如 iOS 的權限錯誤、Android 的存儲權限錯誤)。
  2. Hermes 引擎兼容:使用 Hermes 引擎時,部分 JS 錯誤的堆棧跟蹤格式會變化,需確保監控工具支持 Hermes 日志解析。
  3. 第三方庫版本控制:避免因第三方原生庫版本更新導致的兼容性崩潰,建議鎖定關鍵依賴版本。

六、結語

React Native 錯(cuo)(cuo)誤(wu)(wu)處理的(de)核心在于“分層(ceng)捕獲、全面監控(kong)、友(you)好(hao)降級(ji)”——JS 層(ceng)通過(guo)(guo)錯(cuo)(cuo)誤(wu)(wu)邊(bian)界和全局處理器覆(fu)蓋(gai)邏(luo)輯錯(cuo)(cuo)誤(wu)(wu),原(yuan)生層(ceng)通過(guo)(guo)平(ping)臺專屬機制捕獲崩潰,再結合監控(kong)工具(ju)實(shi)現問(wen)題的(de)實(shi)時感知與(yu)快速(su)定(ding)位。

開(kai)發(fa)者(zhe)需根據應用(yong)場景選擇合適(shi)的(de)處理方案:小型應用(yong)可使用(yong) React 內置工具+簡易日志上報;中(zhong)大型應用(yong)建議集成(cheng) Sentry 等第(di)三方監控工具,同時定制化(hua)錯誤處理邏輯。通過系(xi)統化(hua)的(de)錯誤處理策略,能(neng)顯著提升應用(yong)穩定性,改善用(yong)戶體驗,降低運維成(cheng)本。

posted @ 2025-10-30 09:55  葡萄城技術團隊  閱讀(129)  評論(0)    收藏  舉報