|
|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
Next.js作为一个基于React的强大框架,为开发者提供了服务器渲染、静态网站生成和客户端应用开发的全面解决方案。然而,在开发过程中,我们难免会遇到各种挑战和问题。本文将深入探讨Next.js的调试与故障排除技巧,帮助你解决开发难题,优化应用性能,提升开发体验。
1. Next.js开发环境设置与调试工具
开发环境配置
在开始调试之前,确保你的开发环境正确配置:
- # 创建Next.js项目
- npx create-next-app@latest my-next-app
- cd my-next-app
- # 启动开发服务器
- npm run dev
复制代码
VS Code调试配置
在VS Code中调试Next.js应用,首先创建.vscode/launch.json文件:
- {
- "version": "0.2.0",
- "configurations": [
- {
- "name": "Next.js: debug server-side",
- "type": "node-terminal",
- "request": "launch",
- "command": "npm run dev",
- "serverReadyAction": {
- "pattern": "started server on .+, url: (https?://.+)",
- "uriFormat": "%s",
- "action": "debugWithChrome"
- }
- },
- {
- "name": "Next.js: debug client-side",
- "type": "chrome",
- "request": "launch",
- "url": "http://localhost:3000"
- },
- {
- "name": "Next.js: debug full stack",
- "type": "node-terminal",
- "request": "launch",
- "command": "npm run dev",
- "serverReadyAction": {
- "pattern": "started server on .+, url: (https?://.+)",
- "uriFormat": "%s",
- "action": "debugWithChrome"
- },
- "presentation": {
- "hidden": true
- }
- }
- ],
- "compounds": [
- {
- "name": "Next.js: debug full stack",
- "configurations": [
- "Next.js: debug server-side",
- "Next.js: debug client-side"
- ]
- }
- ]
- }
复制代码
调试工具
1. React Developer Tools:安装浏览器扩展,检查组件层次结构、props、state和性能。
2. Next.js内置分析器:帮助你分析应用性能:
React Developer Tools:安装浏览器扩展,检查组件层次结构、props、state和性能。
Next.js内置分析器:帮助你分析应用性能:
- // next.config.js
- module.exports = {
- experimental: {
- // 启用分析器
- analyze: true,
- },
- }
复制代码
然后运行:
- ANALYZE=true npm run build
复制代码
2. 常见Next.js问题及解决方案
构建和部署问题
错误信息:Module not found: Can't resolve 'some-module'
原因:依赖项未安装或路径错误。
解决方案:
- # 检查并安装缺失的依赖
- npm install some-module
- # 或者使用yarn
- yarn add some-module
复制代码
确保导入路径正确:
- // 错误示例
- import MyComponent from '../components/MyComponent'; // 路径错误
- // 正确示例
- import MyComponent from '../components/MyComponent'; // 确保路径正确
复制代码
错误信息:JavaScript heap out of memory
原因:Node.js默认内存限制不足。
解决方案:
在package.json中增加内存限制:
- {
- "scripts": {
- "build": "NODE_OPTIONS=--max-old-space-size=4096 next build"
- }
- }
复制代码
或者使用环境变量:
- # Linux/Mac
- export NODE_OPTIONS=--max-old-space-size=4096
- npm run build
- # Windows
- set NODE_OPTIONS=--max-old-space-size=4096
- npm run build
复制代码
路由问题
原因:文件命名不正确或getStaticPaths未正确实现。
解决方案:
确保文件命名正确,例如pages/posts/[id].js。
实现getStaticPaths:
- // pages/posts/[id].js
- export async function getStaticPaths() {
- // 获取所有可能的id
- const posts = await getPosts();
- const paths = posts.map(post => ({
- params: { id: post.id.toString() },
- }));
- return {
- paths,
- fallback: true, // 或 'blocking'
- };
- }
- export async function getStaticProps({ params }) {
- // 获取单个post数据
- const post = await getPost(params.id);
-
- return {
- props: {
- post,
- },
- };
- }
复制代码
问题:使用next/link或next/router进行路由跳转时出现问题。
解决方案:
正确使用next/link:
- import Link from 'next/link';
- function Navigation() {
- return (
- <nav>
- <Link href="/about">
- <a>About</a>
- </Link>
- <Link href="/posts/[id]" as="/posts/1">
- <a>Post 1</a>
- </Link>
- </nav>
- );
- }
复制代码
使用next/router进行编程式导航:
- import { useRouter } from 'next/router';
- function MyComponent() {
- const router = useRouter();
-
- const handleClick = () => {
- // 基本导航
- router.push('/about');
-
- // 带查询参数的导航
- router.push({
- pathname: '/posts/[id]',
- query: { id: '1' },
- });
-
- // 动态路由
- router.push('/posts/[id]', '/posts/1');
- };
-
- return <button onClick={handleClick}>Go to post</button>;
- }
复制代码
数据获取问题
原因:默认情况下,Next.js会缓存getStaticProps的结果。
解决方案:
使用revalidate选项设置重新验证时间:
- export async function getStaticProps() {
- const posts = await getPosts();
-
- return {
- props: {
- posts,
- },
- revalidate: 60, // 每60秒重新生成页面
- };
- }
复制代码
或者使用On-Demand Revalidation(按需重新验证):
- // pages/api/revalidate.js
- export default async function handler(req, res) {
- try {
- await res.revalidate('/posts');
- return res.json({ revalidated: true });
- } catch (err) {
- return res.status(500).send('Error revalidating');
- }
- }
复制代码
然后从客户端调用此API:
- async function triggerRevalidation() {
- const response = await fetch('/api/revalidate');
- const data = await response.json();
- console.log(data.revalidated); // true
- }
复制代码
原因:在getServerSideProps中,请求对象(如cookies、headers等)可能未正确传递。
解决方案:
确保正确访问请求对象:
- export async function getServerSideProps(context) {
- const { req, res, query } = context;
-
- // 获取cookies
- const cookies = req.headers.cookie;
-
- // 获取headers
- const userAgent = req.headers['user-agent'];
-
- // 获取查询参数
- const { id } = query;
-
- // 获取数据
- const data = await fetchData(id, cookies);
-
- return {
- props: {
- data,
- userAgent,
- },
- };
- }
复制代码
性能问题
原因:未优化的资源、未实现代码分割或未使用SSR/SSG。
解决方案:
1. 使用动态导入实现代码分割:
- import dynamic from 'next/dynamic';
- // 动态导入组件,不进行SSR
- const DynamicComponent = dynamic(() => import('../components/hello'), {
- ssr: false,
- });
- // 带加载状态的动态导入
- const DynamicComponentWithLoading = dynamic(
- () => import('../components/hello'),
- { loading: () => <p>Loading...</p> }
- );
- function Home() {
- return (
- <div>
- <h1>Home</h1>
- <DynamicComponent />
- <DynamicComponentWithLoading />
- </div>
- );
- }
复制代码
1. 优化图片:
- import Image from 'next/image';
- function MyComponent() {
- return (
- <div>
- <Image
- src="/hero.jpg"
- alt="Hero image"
- width={800}
- height={600}
- priority // 对于首屏图片使用priority
- />
- </div>
- );
- }
复制代码
1. 使用next/head优化资源加载:
- import Head from 'next/head';
- function MyPage() {
- return (
- <div>
- <Head>
- <title>My Page</title>
- <link rel="preload" href="/fonts/my-font.woff2" as="font" type="font/woff2" crossOrigin="anonymous" />
- </Head>
- <p>Page content</p>
- </div>
- );
- }
复制代码
原因:未预取页面或组件过大。
解决方案:
1. 使用next/link的预取功能:
- import Link from 'next/link';
- function Navigation() {
- return (
- <nav>
- {/* 默认情况下,Link会预取页面 */}
- <Link href="/about">
- <a>About</a>
- </Link>
-
- {/* 禁用预取 */}
- <Link href="/contact" prefetch={false}>
- <a>Contact</a>
- </Link>
- </nav>
- );
- }
复制代码
1. 优化组件大小:
- // 使用React.memo避免不必要的重新渲染
- const MyComponent = React.memo(function MyComponent({ data }) {
- return <div>{data.map(item => <Item key={item.id} item={item} />)}</div>;
- });
- // 使用useMemo和useCallback优化性能
- function ParentComponent({ items }) {
- const processedItems = React.useMemo(() => {
- return items.map(item => ({ ...item, processed: true }));
- }, [items]);
-
- const handleClick = React.useCallback((id) => {
- console.log(`Item ${id} clicked`);
- }, []);
-
- return <MyComponent data={processedItems} onClick={handleClick} />;
- }
复制代码
SSR/SSG问题
错误信息:Warning: Text content did not match. Server: "..." Client: "..."
原因:服务器渲染的HTML与客户端首次渲染不匹配。
解决方案:
1. 确保在useEffect中执行客户端特定代码:
- function MyComponent() {
- const [isClient, setIsClient] = React.useState(false);
-
- React.useEffect(() => {
- setIsClient(true);
- }, []);
-
- return (
- <div>
- {isClient ? (
- <ClientOnlyComponent />
- ) : (
- <div>Loading...</div>
- )}
- </div>
- );
- }
复制代码
1. 使用动态导入禁用SSR:
- import dynamic from 'next/dynamic';
- const ClientOnlyComponent = dynamic(() => import('../components/ClientOnlyComponent'), {
- ssr: false,
- });
复制代码
1. 处理日期和时区问题:
- function DateComponent({ date }) {
- const [formattedDate, setFormattedDate] = React.useState('');
-
- React.useEffect(() => {
- // 在客户端格式化日期
- setFormattedDate(new Date(date).toLocaleString());
- }, [date]);
-
- return <div>{formattedDate}</div>;
- }
复制代码
原因:构建时无法访问外部API或数据源。
解决方案:
1. 使用fallback模式:
- export async function getStaticPaths() {
- // 只获取部分路径用于构建
- const posts = await getFeaturedPosts();
- const paths = posts.map(post => ({
- params: { id: post.id.toString() },
- }));
- return {
- paths,
- fallback: 'blocking', // 或 true
- };
- }
- export async function getStaticProps({ params }) {
- try {
- const post = await getPost(params.id);
-
- if (!post) {
- return {
- notFound: true,
- };
- }
-
- return {
- props: {
- post,
- },
- revalidate: 60,
- };
- } catch (error) {
- console.error('Error fetching post:', error);
- return {
- props: {
- error: 'Failed to load post',
- },
- };
- }
- }
复制代码
1. 在组件中处理加载和错误状态:
- function PostPage({ post, error }) {
- const router = useRouter();
-
- // 如果页面仍在生成中
- if (router.isFallback) {
- return <div>Loading...</div>;
- }
-
- // 如果发生错误
- if (error) {
- return <div>Error: {error}</div>;
- }
-
- // 如果找不到post
- if (!post) {
- return <div>Post not found</div>;
- }
-
- return (
- <div>
- <h1>{post.title}</h1>
- <p>{post.content}</p>
- </div>
- );
- }
复制代码
3. 性能优化技巧
代码分割和懒加载
使用动态导入实现组件级别的代码分割:
- import dynamic from 'next/dynamic';
- // 简单的动态导入
- const DynamicComponent = dynamic(() => import('../components/DynamicComponent'));
- // 带自定义加载组件
- const DynamicComponentWithLoading = dynamic(
- () => import('../components/DynamicComponent'),
- {
- loading: () => <p>Loading...</p>,
- ssr: false // 禁用SSR
- }
- );
- // 带错误处理的动态导入
- const DynamicComponentWithError = dynamic(
- () => import('../components/DynamicComponent').catch(err => {
- console.error('Failed to load component:', err);
- return () => <div>Failed to load component</div>;
- })
- );
复制代码
图片优化
使用Next.js的Image组件优化图片:
- import Image from 'next/image';
- function MyComponent() {
- return (
- <div>
- {/* 基本用法 */}
- <Image
- src="/hero.jpg"
- alt="Hero image"
- width={800}
- height={600}
- />
-
- {/* 响应式图片 */}
- <Image
- src="/hero.jpg"
- alt="Hero image"
- width={800}
- height={600}
- sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
- />
-
- {/* 优先加载首屏图片 */}
- <Image
- src="/hero.jpg"
- alt="Hero image"
- width={800}
- height={600}
- priority
- />
-
- {/* 带占位符 */}
- <Image
- src="/hero.jpg"
- alt="Hero image"
- width={800}
- height={600}
- placeholder="blur"
- blurDataURL="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAv/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAX/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCdABmX/9k="
- />
- </div>
- );
- }
复制代码
字体优化
使用Next.js的字体优化:
- import { Inter } from 'next/font/google';
- // 配置字体
- const inter = Inter({
- subsets: ['latin'],
- variable: '--font-inter',
- display: 'swap', // 或 'block', 'fallback', 'optional'
- });
- function MyApp({ Component, pageProps }) {
- return (
- <main className={inter.className}>
- <Component {...pageProps} />
- </main>
- );
- }
复制代码
脚本优化
使用Next.js的Script组件优化第三方脚本加载:
- import Script from 'next/script';
- function MyComponent() {
- return (
- <div>
- {/* 在页面加载前加载 */}
- <Script
- src="https://example.com/script.js"
- strategy="beforeInteractive"
- />
-
- {/* 在页面交互后加载 */}
- <Script
- src="https://example.com/analytics.js"
- strategy="afterInteractive"
- />
-
- {/* 延迟加载,直到空闲时 */}
- <Script
- src="https://example.com/widget.js"
- strategy="lazyOnload"
- />
-
- {/* 手动控制加载 */}
- <Script
- id="custom-script"
- src="https://example.com/custom.js"
- onLoad={() => {
- console.log('Script loaded');
- }}
- />
- </div>
- );
- }
复制代码
缓存策略
配置适当的缓存策略:
- // next.config.js
- module.exports = {
- async headers() {
- return [
- {
- source: '/(.*)',
- headers: [
- {
- key: 'Cache-Control',
- value: 'public, max-age=31536000, immutable',
- },
- ],
- },
- {
- source: '/api/(.*)',
- headers: [
- {
- key: 'Cache-Control',
- value: 'no-cache, no-store, must-revalidate',
- },
- ],
- },
- ];
- },
- };
复制代码
优化构建过程
优化Webpack配置:
- // next.config.js
- module.exports = {
- webpack: (config, { dev, isServer }) => {
- // 生产环境优化
- if (!dev && !isServer) {
- Object.assign(config.resolve.alias, {
- 'react/jsx-runtime.js': 'preact/compat/jsx-runtime',
- react: 'preact/compat',
- 'react-dom/test-utils': 'preact/test-utils',
- 'react-dom': 'preact/compat',
- });
- }
-
- // 优化构建性能
- if (!isServer) {
- config.optimization.splitChunks = {
- chunks: 'all',
- cacheGroups: {
- vendor: {
- test: /[\\/]node_modules[\\/]/,
- name: 'vendors',
- chunks: 'all',
- priority: 10,
- },
- common: {
- name: 'common',
- minChunks: 2,
- chunks: 'all',
- priority: 5,
- },
- },
- };
- }
-
- return config;
- },
- };
复制代码
4. 调试工具和技术
使用Next.js内置的调试功能
Next.js提供了内置的错误覆盖层,可以在开发时显示错误:
- // 自定义错误页面
- // pages/_error.js
- function Error({ statusCode }) {
- return (
- <p>
- {statusCode
- ? `An error ${statusCode} occurred on server`
- : 'An error occurred on client'}
- </p>
- );
- }
- Error.getInitialProps = ({ res, err }) => {
- const statusCode = res ? res.statusCode : err ? err.statusCode : 404;
- return { statusCode };
- };
- export default Error;
复制代码
Next.js在开发模式下自动提供源映射,帮助调试:
- // next.config.js
- module.exports = {
- // 生产环境也启用源映射
- productionBrowserSourceMaps: true,
- };
复制代码
使用浏览器开发者工具
使用React Developer Tools检查组件:
- // 使用displayName帮助调试
- function MyComponent() {
- return <div>Hello</div>;
- }
- MyComponent.displayName = 'MyComponent';
- export default MyComponent;
复制代码
使用Chrome DevTools的Performance面板分析性能:
- // 使用React Profiler API
- import { Profiler } from 'react';
- function onRenderCallback(
- id, // 组件的标识
- phase, // "mount"(挂载)或 "update"(更新)
- actualDuration, // 本次更新组件渲染的耗时
- baseDuration, // 不使用memoization的情况下组件渲染的耗时
- startTime, // 本次更新React开始渲染的时间
- commitTime, // 本次更新React提交到DOM的时间
- interactions // 属于本次更新的interactions集合
- ) {
- console.log(`${id} ${phase} took ${actualDuration}ms`);
- }
- function App() {
- return (
- <Profiler id="App" onRender={onRenderCallback}>
- <MyComponent />
- </Profiler>
- );
- }
复制代码
使用日志和断点
使用适当的日志级别:
- function MyComponent({ data }) {
- // 调试日志
- console.debug('Component rendering with data:', data);
-
- // 信息日志
- console.info('Component mounted');
-
- // 警告日志
- if (!data) {
- console.warn('No data provided to component');
- }
-
- // 错误日志
- try {
- // 可能出错的操作
- } catch (error) {
- console.error('Error in component:', error);
- }
-
- return <div>{/* ... */}</div>;
- }
复制代码
在VS Code中设置断点:
- function fetchData() {
- // 在这里设置断点
- debugger;
-
- return fetch('/api/data')
- .then(response => response.json())
- .then(data => {
- // 在这里设置断点
- debugger;
- return data;
- });
- }
复制代码
使用第三方调试工具
- // lib/logger.js
- const winston = require('winston');
- const logger = winston.createLogger({
- level: 'info',
- format: winston.format.json(),
- defaultMeta: { service: 'next-app' },
- transports: [
- new winston.transports.File({ filename: 'error.log', level: 'error' }),
- new winston.transports.File({ filename: 'combined.log' }),
- ],
- });
- if (process.env.NODE_ENV !== 'production') {
- logger.add(new winston.transports.Console({
- format: winston.format.simple(),
- }));
- }
- export default logger;
复制代码
在API路由中使用:
- // pages/api/data.js
- import logger from '../../lib/logger';
- export default function handler(req, res) {
- logger.info('API request received', { method: req.method, url: req.url });
-
- try {
- // 处理请求
- res.status(200).json({ data: 'example' });
- } catch (error) {
- logger.error('API error', error);
- res.status(500).json({ error: 'Internal server error' });
- }
- }
复制代码- // lib/sentry.js
- import * as Sentry from '@sentry/nextjs';
- Sentry.init({
- dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
- tracesSampleRate: 1.0,
- });
- // pages/_app.js
- import '../lib/sentry';
- function MyApp({ Component, pageProps }) {
- return <Component {...pageProps} />;
- }
- export default MyApp;
复制代码
捕获错误:
- import * as Sentry from '@sentry/nextjs';
- function MyComponent() {
- const handleClick = () => {
- try {
- // 可能出错的操作
- throw new Error('Something went wrong');
- } catch (error) {
- Sentry.captureException(error);
- }
- };
-
- return <button onClick={handleClick}>Click me</button>;
- }
复制代码
5. 最佳实践和预防措施
代码组织和结构
保持良好的代码组织:
- my-next-app/
- ├── components/
- │ ├── ui/
- │ │ ├── Button.js
- │ │ ├── Input.js
- │ │ └── ...
- │ ├── layout/
- │ │ ├── Header.js
- │ │ ├── Footer.js
- │ │ └── ...
- │ └── ...
- ├── lib/
- │ ├── api.js
- │ ├── utils.js
- │ └── ...
- ├── pages/
- │ ├── api/
- │ │ ├── users.js
- │ │ └── ...
- │ ├── _app.js
- │ ├── _document.js
- │ ├── index.js
- │ └── ...
- ├── public/
- │ ├── images/
- │ ├── fonts/
- │ └── ...
- ├── styles/
- │ ├── globals.css
- │ └── ...
- ├── .eslintrc.js
- ├── .prettierrc.js
- ├── next.config.js
- ├── package.json
- └── README.md
复制代码
错误处理
实现全面的错误处理:
- // components/ErrorBoundary.js
- import React from 'react';
- class ErrorBoundary extends React.Component {
- constructor(props) {
- super(props);
- this.state = { hasError: false, error: null };
- }
- static getDerivedStateFromError(error) {
- return { hasError: true, error };
- }
- componentDidCatch(error, errorInfo) {
- console.error('Error caught by boundary:', error, errorInfo);
- // 可以在这里将错误报告给错误跟踪服务
- }
- render() {
- if (this.state.hasError) {
- return this.props.fallback || <div>Something went wrong.</div>;
- }
- return this.props.children;
- }
- }
- export default ErrorBoundary;
- // 使用ErrorBoundary
- function MyApp({ Component, pageProps }) {
- return (
- <ErrorBoundary>
- <Component {...pageProps} />
- </ErrorBoundary>
- );
- }
复制代码
测试
编写测试来预防错误:
- // __tests__/components/MyComponent.test.js
- import { render, screen } from '@testing-library/react';
- import MyComponent from '../../components/MyComponent';
- test('renders correctly with data', () => {
- const mockData = { title: 'Test Title', content: 'Test Content' };
- render(<MyComponent data={mockData} />);
-
- expect(screen.getByText('Test Title')).toBeInTheDocument();
- expect(screen.getByText('Test Content')).toBeInTheDocument();
- });
- test('handles empty data', () => {
- render(<MyComponent data={null} />);
-
- expect(screen.getByText('No data available')).toBeInTheDocument();
- });
复制代码
类型检查
使用TypeScript或PropTypes进行类型检查:
- // 使用TypeScript
- interface MyComponentProps {
- title: string;
- content?: string;
- count: number;
- }
- const MyComponent: React.FC<MyComponentProps> = ({ title, content, count }) => {
- return (
- <div>
- <h1>{title}</h1>
- {content && <p>{content}</p>}
- <p>Count: {count}</p>
- </div>
- );
- };
- export default MyComponent;
- // 使用PropTypes
- import PropTypes from 'prop-types';
- function MyComponent({ title, content, count }) {
- return (
- <div>
- <h1>{title}</h1>
- {content && <p>{content}</p>}
- <p>Count: {count}</p>
- </div>
- );
- }
- MyComponent.propTypes = {
- title: PropTypes.string.isRequired,
- content: PropTypes.string,
- count: PropTypes.number.isRequired,
- };
- export default MyComponent;
复制代码
性能监控
使用性能监控工具:
- // lib/analytics.js
- export function measurePerformance(name, fn) {
- if (process.env.NODE_ENV === 'development') {
- const start = performance.now();
- const result = fn();
- const end = performance.now();
- console.log(`${name} took ${end - start}ms`);
- return result;
- }
- return fn();
- }
- // 使用示例
- const data = measurePerformance('fetchData', () => fetchData());
复制代码
6. 结论
Next.js是一个强大的框架,但在开发过程中可能会遇到各种挑战。通过掌握调试技巧、了解常见问题的解决方案、实施性能优化策略和遵循最佳实践,开发者可以显著提高开发效率、优化应用性能并提升开发体验。
记住,有效的调试不仅仅是解决问题,更是预防问题的过程。通过良好的代码组织、全面的错误处理、适当的测试和持续的性能监控,可以构建出高质量、高性能的Next.js应用。
随着Next.js的不断发展,保持学习新特性和最佳实践同样重要。希望这本实战手册能够帮助你在Next.js开发旅程中更加顺利和高效。
版权声明
1、转载或引用本网站内容(Next.js调试与故障排除实战手册解决开发难题优化应用性能提升开发体验)须注明原网址及作者(威震华夏关云长),并标明本网站网址(https://pixtech.cc/)。
2、对于不当转载或引用本网站内容而引起的民事纷争、行政处理或其他损失,本网站不承担责任。
3、对不遵守本声明或其他违法、恶意使用本网站内容者,本网站保留追究其法律责任的权利。
本文地址: https://pixtech.cc/thread-40648-1-1.html
|
|