MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Qwik文件系统路由入门指南

2024-03-242.3k 阅读

Qwik 文件系统路由基础

理解 Qwik 路由概念

在前端开发中,路由是指根据不同的 URL 地址,展示不同的用户界面。Qwik 采用文件系统路由,这意味着路由的定义直接与项目的文件结构相关联。这种设计使得路由配置更加直观,易于维护。

例如,在传统的路由框架中,你可能需要在一个单独的配置文件中定义每个路由及其对应的组件。而在 Qwik 中,你只需要在项目的 src/routes 目录下创建文件和目录,Qwik 会自动根据这些文件结构生成路由。

Qwik 路由的优势

  1. 简单直观:开发人员无需编写复杂的路由配置代码,通过文件系统结构就能自然地映射出路由。这对于新手来说非常友好,降低了学习成本。
  2. 易于维护:当项目规模扩大时,修改或添加路由只需要在文件系统中进行相应的文件和目录操作,而不需要在庞大的配置文件中查找和修改特定的路由规则。
  3. 自动代码拆分:Qwik 会根据路由结构自动进行代码拆分,只有在需要的时候才加载相应的代码,提高了应用程序的性能。

创建基本路由

创建项目与目录结构

首先,确保你已经安装了 Qwik CLI。你可以使用以下命令创建一个新的 Qwik 项目:

npm create qwik@latest my - app
cd my - app

在项目的 src/routes 目录下,这就是定义路由的地方。例如,创建一个名为 home.tsx 的文件,这个文件将对应网站的首页路由。

编写首页路由组件

打开 src/routes/home.tsx 文件,编写如下代码:

import { component$, useContext } from '@builder.io/qwik';
import type { PageContext } from './$types';

export const Home = component$(() => {
  const { url } = useContext<PageContext>();
  return (
    <div>
      <h1>Welcome to My Qwik App</h1>
      <p>The current URL is: {url.pathname}</p>
    </div>
  );
});

在这段代码中,我们定义了一个名为 Home 的组件。通过 useContext 我们获取到了当前页面的上下文 PageContext,从中提取出了当前的 URL 信息,并在页面上显示出来。

嵌套路由

嵌套路由的概念

嵌套路由允许在一个路由组件内部再包含其他路由组件。这在构建复杂的用户界面时非常有用,例如一个有侧边栏和内容区域的页面,侧边栏的不同选项可以对应不同的嵌套路由。

创建嵌套路由结构

src/routes 目录下,创建一个新的目录,例如 products。在 products 目录下,创建 index.tsx 文件,这将是 products 路由的默认组件。然后,在 products 目录下再创建一个子目录 product - detail,并在其中创建 [productId].tsx 文件。[productId] 是一个动态路由参数,表示产品的 ID。

编写嵌套路由组件

src/routes/products/index.tsx 代码如下:

import { component$, useContext } from '@builder.io/qwik';
import type { PageContext } from './$types';

export const Products = component$(() => {
  const { url } = useContext<PageContext>();
  return (
    <div>
      <h1>Products Page</h1>
      <p>The current URL is: {url.pathname}</p>
    </div>
  );
});

src/routes/products/product - detail/[productId].tsx 代码如下:

import { component$, useContext } from '@builder.io/qwik';
import type { PageContext } from './$types';

export const ProductDetail = component$(() => {
  const { url } = useContext<PageContext>();
  const productId = url.searchParams.get('productId');
  return (
    <div>
      <h1>Product Detail Page</h1>
      <p>The product ID is: {productId}</p>
      <p>The current URL is: {url.pathname}</p>
    </div>
  );
});

ProductDetail 组件中,我们通过 url.searchParams.get('productId') 获取了动态路由参数 productId,并在页面上显示出来。

动态路由

动态路由的用途

动态路由允许根据不同的参数展示不同的内容。在实际应用中,这常用于展示用户个人资料、商品详情等页面,每个页面的内容取决于特定的参数值。

定义动态路由

如前面在嵌套路由中提到的 [productId].tsx 文件就是一个动态路由的例子。文件名中的方括号 [] 表示这是一个动态路由参数。你可以根据实际需求定义不同的动态路由参数名。

获取动态路由参数

在动态路由组件中,可以通过 PageContext 中的 url 对象来获取动态路由参数。例如在 src/routes/products/product - detail/[productId].tsx 中:

import { component$, useContext } from '@builder.io/qwik';
import type { PageContext } from './$types';

export const ProductDetail = component$(() => {
  const { url } = useContext<PageContext>();
  const productId = url.searchParams.get('productId');
  return (
    <div>
      <h1>Product Detail Page</h1>
      <p>The product ID is: {productId}</p>
      <p>The current URL is: {url.pathname}</p>
    </div>
  );
});

这里通过 url.searchParams.get('productId') 获取到了 productId 参数的值。

路由导航

使用 Qwik 内置导航

Qwik 提供了内置的导航函数和组件来实现页面之间的跳转。例如,你可以使用 useNavigate 钩子来实现编程式导航。

在一个组件中,引入 useNavigate 并使用它来实现导航:

import { component$, useNavigate } from '@builder.io/qwik';

export const NavigateComponent = component$(() => {
  const navigate = useNavigate();
  const handleClick = () => {
    navigate('/products');
  };
  return (
    <button onClick={handleClick}>Go to Products Page</button>
  );
});

在这段代码中,当按钮被点击时,handleClick 函数会调用 navigate('/products'),将用户导航到 /products 路由对应的页面。

基于锚点的导航

除了编程式导航,你还可以使用传统的 HTML 锚点标签 <a> 来进行导航。例如:

<a href="/home">Go to Home Page</a>

Qwik 会自动处理这些锚点导航,确保在导航过程中进行必要的路由匹配和页面渲染。

路由配置与选项

路由元数据

Qwik 允许你为路由定义元数据。在路由组件文件中,可以通过 export const routeMeta 来定义元数据。例如,在 src/routes/home.tsx 中:

import { component$, useContext } from '@builder.io/qwik';
import type { PageContext } from './$types';

export const Home = component$(() => {
  const { url } = useContext<PageContext>();
  return (
    <div>
      <h1>Welcome to My Qwik App</h1>
      <p>The current URL is: {url.pathname}</p>
    </div>
  );
});

export const routeMeta = {
  title: 'Home Page',
  description: 'This is the home page of our Qwik app'
};

这些元数据可以用于设置页面的标题、描述等信息,在 SEO 优化等方面非常有用。

路由加载策略

Qwik 支持不同的路由加载策略,例如懒加载。通过配置,可以控制路由组件何时加载。默认情况下,Qwik 会自动进行代码拆分和懒加载,只有在需要的时候才加载相应的路由组件代码。

你可以在 qwik.config.ts 文件中进一步配置路由加载策略等选项。例如:

import { defineConfig } from '@builder.io/qwik/optimizer';

export default defineConfig({
  routes: {
    '/products': {
      lazy: true
    }
  }
});

在这个例子中,我们明确指定了 /products 路由采用懒加载策略。

处理 404 页面

创建 404 页面组件

src/routes 目录下创建 404.tsx 文件,这将是 Qwik 应用的 404 页面组件。编写如下代码:

import { component$ } from '@builder.io/qwik';

export const NotFound = component$(() => {
  return (
    <div>
      <h1>404 - Page Not Found</h1>
      <p>The page you are looking for does not exist.</p>
    </div>
  );
});

当用户访问一个不存在的路由时,Qwik 会自动渲染这个 404.tsx 组件。

自定义 404 处理逻辑

除了简单的页面展示,你还可以在 404 页面组件中添加自定义的处理逻辑。例如,记录 404 错误日志,或者提供一些引导用户回到正确页面的功能。

import { component$, useContext } from '@builder.io/qwik';
import type { PageContext } from './$types';

export const NotFound = component$(() => {
  const { url } = useContext<PageContext>();
  const log404Error = () => {
    console.error(`404 error: ${url.href}`);
  };
  return (
    <div>
      <h1>404 - Page Not Found</h1>
      <p>The page you are looking for does not exist: {url.href}</p>
      <button onClick={log404Error}>Log 404 Error</button>
    </div>
  );
});

在这个例子中,我们添加了一个按钮,点击按钮可以将 404 错误信息记录到控制台。

路由与数据获取

在路由组件中获取数据

在路由组件中,通常需要获取数据来展示。Qwik 提供了多种方式来进行数据获取,例如使用 fetch 直接在组件中获取数据。

src/routes/products/index.tsx 中,我们可以获取产品列表数据:

import { component$, useContext } from '@builder.io/qwik';
import type { PageContext } from './$types';

export const Products = component$(() => {
  const { url } = useContext<PageContext>();
  const [products, setProducts] = useState<Product[]>([]);
  const fetchProducts = async () => {
    const response = await fetch('/api/products');
    const data = await response.json();
    setProducts(data);
  };
  onMount(() => {
    fetchProducts();
  });
  return (
    <div>
      <h1>Products Page</h1>
      <p>The current URL is: {url.pathname}</p>
      <ul>
        {products.map(product => (
          <li key={product.id}>{product.name}</li>
        ))}
      </ul>
    </div>
  );
});

interface Product {
  id: number;
  name: string;
}

在这个例子中,我们在组件挂载时通过 fetch 获取产品数据,并将其展示在页面上。

数据预加载

为了提高用户体验,Qwik 支持数据预加载。你可以在路由组件的 export const onBeforeRender 函数中进行数据预加载。

src/routes/products/index.tsx 中:

import { component$, useContext } from '@builder.io/qwik';
import type { PageContext } from './$types';

export const Products = component$(() => {
  const { url } = useContext<PageContext>();
  const [products, setProducts] = useState<Product[]>([]);
  return (
    <div>
      <h1>Products Page</h1>
      <p>The current URL is: {url.pathname}</p>
      <ul>
        {products.map(product => (
          <li key={product.id}>{product.name}</li>
        ))}
      </ul>
    </div>
  );
});

interface Product {
  id: number;
  name: string;
}

export const onBeforeRender = async (pageContext: PageContext) => {
  const response = await fetch('/api/products');
  const data = await response.json();
  pageContext.props.products = data;
};

onBeforeRender 函数中,我们提前获取了产品数据,并将其设置到 pageContext.props 中,这样在组件渲染时就可以直接使用这些数据,减少了用户等待时间。

路由过渡效果

实现路由过渡动画

Qwik 支持为路由切换添加过渡效果,以提升用户体验。你可以使用 Qwik 的动画库来实现这一功能。

首先,安装动画库,例如 @builder.io/qwik - animate

npm install @builder.io/qwik - animate

src/routes/home.tsx 中,添加路由过渡效果:

import { component$, useContext } from '@builder.io/qwik';
import { fade } from '@builder.io/qwik - animate';
import type { PageContext } from './$types';

export const Home = component$(fade(() => {
  const { url } = useContext<PageContext>();
  return (
    <div>
      <h1>Welcome to My Qwik App</h1>
      <p>The current URL is: {url.pathname}</p>
    </div>
  );
}));

这里我们使用了 fade 动画,当进入或离开 Home 路由时,会有淡入淡出的过渡效果。

自定义过渡效果

除了使用内置的过渡效果,你还可以自定义过渡效果。例如,创建一个自定义的滑动过渡效果:

import { createAnimation } from '@builder.io/qwik - animate';

const slide = createAnimation({
  initial: {
    opacity: 0,
    transform: 'translateX(100%)'
  },
  enter: {
    opacity: 1,
    transform: 'translateX(0)'
  },
  leave: {
    opacity: 0,
    transform: 'translateX(-100%)'
  },
  duration: 300
});

然后在路由组件中使用这个自定义动画:

import { component$, useContext } from '@builder.io/qwik';
import { slide } from './customAnimation';
import type { PageContext } from './$types';

export const Home = component$(slide(() => {
  const { url } = useContext<PageContext>();
  return (
    <div>
      <h1>Welcome to My Qwik App</h1>
      <p>The current URL is: {url.pathname}</p>
    </div>
  );
}));

这样就实现了一个自定义的滑动过渡效果。

路由与服务器端渲染(SSR)

Qwik 的 SSR 支持

Qwik 对服务器端渲染有很好的支持。在使用文件系统路由时,SSR 可以确保页面在服务器端就渲染完成,然后将渲染好的页面发送到客户端,提高了页面的初始加载速度。

配置 SSR 与路由

qwik.config.ts 文件中,可以配置 SSR 相关选项。例如:

import { defineConfig } from '@builder.io/qwik/optimizer';

export default defineConfig({
  ssr: true,
  routes: {
    // 路由相关配置
  }
});

通过设置 ssr: true,开启了 SSR 功能。在路由组件中,不需要额外的复杂配置就可以享受 SSR 的好处。

SSR 与数据获取

在 SSR 场景下,数据获取也有所不同。通常,你需要在服务器端获取数据,然后将数据传递给客户端。

src/routes/products/index.tsx 中:

import { component$, useContext } from '@builder.io/qwik';
import type { PageContext } from './$types';

export const Products = component$(() => {
  const { url, props } = useContext<PageContext>();
  const products = props.products as Product[];
  return (
    <div>
      <h1>Products Page</h1>
      <p>The current URL is: {url.pathname}</p>
      <ul>
        {products.map(product => (
          <li key={product.id}>{product.name}</li>
        ))}
      </ul>
    </div>
  );
});

interface Product {
  id: number;
  name: string;
}

export const onBeforeRender = async (pageContext: PageContext) => {
  if (pageContext.isServer) {
    const response = await fetch('/api/products');
    const data = await response.json();
    pageContext.props.products = data;
  }
};

onBeforeRender 函数中,通过 pageContext.isServer 判断是否在服务器端,如果是,则在服务器端获取产品数据,并设置到 pageContext.props 中,这样客户端就可以直接使用这些数据。

优化路由性能

代码拆分与懒加载优化

虽然 Qwik 已经默认进行了代码拆分和懒加载,但你可以进一步优化。例如,对于一些较大的路由组件,可以手动设置更细粒度的懒加载策略。

qwik.config.ts 中:

import { defineConfig } from '@builder.io/qwik/optimizer';

export default defineConfig({
  routes: {
    '/big - component - route': {
      lazy: true,
      chunkSize: 1000 // 限制拆分后的代码块大小
    }
  }
});

通过设置 chunkSize,可以控制拆分后的代码块大小,避免加载过大的代码块影响性能。

预加载资源

除了数据预加载,你还可以预加载一些关键资源,例如图片、样式文件等。在路由组件的 export const onBeforeRender 中,可以使用 fetch 等方式预加载资源。

export const onBeforeRender = async (pageContext: PageContext) => {
  const imageResponse = await fetch('/assets/banner.jpg');
  const styleResponse = await fetch('/styles/main.css');
  // 可以进一步处理资源,例如缓存等
};

这样在路由切换到该组件时,相关资源已经提前加载,提高了页面的渲染速度。

优化动态路由参数处理

在动态路由中,频繁获取和处理动态路由参数可能会影响性能。可以通过缓存参数值等方式进行优化。

例如,在动态路由组件中:

import { component$, useContext } from '@builder.io/qwik';
import type { PageContext } from './$types';

let cachedProductId: string | null = null;

export const ProductDetail = component$(() => {
  const { url } = useContext<PageContext>();
  const productId = cachedProductId || url.searchParams.get('productId');
  if (!cachedProductId) {
    cachedProductId = productId;
  }
  return (
    <div>
      <h1>Product Detail Page</h1>
      <p>The product ID is: {productId}</p>
      <p>The current URL is: {url.pathname}</p>
    </div>
  );
});

这里通过缓存 productId,避免了每次组件渲染时都重新获取动态路由参数。

通过以上对 Qwik 文件系统路由的深入介绍,从基础概念到高级优化,你应该对如何在 Qwik 项目中使用和优化路由有了全面的了解。希望这些知识能帮助你构建出更高效、更具用户体验的前端应用程序。