Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: useRouter add query support #45

Closed
wants to merge 4 commits into from

Conversation

zhuddan
Copy link

@zhuddan zhuddan commented Aug 28, 2024

描述

这个 PR 新增了 vue router 的 query 参数模式。

额外上下文

我明白这个模式不符合 uni-app 的默认跳转方式,但我习惯了 Vue 原生路由的参数传递方式,因此非常需要这个功能。它支持在路由跳转时携带参数,并在页面中获取这些参数。以下是一个使用示例:

import { useRouter } from '@uni-helper/uni-use';
import { computed, ref, watchEffect } from 'vue';

const router = useRouter();

// 路由跳转,携带参数
router.navigate({ url: '/pages/topics/index', query: { foo: 1 } });

// 路由重定向,携带参数
router.navigate({ url: '/pages/auth/login', query: { bar: 'bar' } });

// 获取当前页路由参数
const id = computed(() => router.currentQuery.value.id);

// 使用当前页路由参数获取数据
const data = ref();

watchEffect(async () => {
  const response = await fetch(
    `https://example.com/${id}`
  );
  data.value = await response.json();
});

尽管这个模式与 uni-app 的跳转机制有所冲突,但它极大地提高了我的开发效率。关于测试,由于单元测试不支持 getCurrentPages,目前尚未找到合适的解决方案,因此我只测试了 utils 中新增的函数。文档部分已根据新功能进行了相应修改。

请仔细审查这个 PR 的变更内容,如果可以,希望能够合并,因为这个功能对我的项目非常重要。

Summary by CodeRabbit

  • New Features

    • Enhanced routing functionality with support for query parameters in navigation options.
    • Introduced a new utility for constructing URLs with query parameters.
  • Bug Fixes

    • Improved handling of undefined, null, and falsy values in URL parameter construction.
  • Tests

    • Added a test suite to validate the new query parameter handling functionality.
  • Documentation

    • Updated documentation to reflect new routing capabilities and examples for using query parameters in navigation.

Copy link

coderabbitai bot commented Aug 28, 2024

Walkthrough

The changes enhance the routing functionality of the useRouter module by introducing new interfaces for handling query parameters in navigation options. New functions, handleQuery and setQuery, are added to manage these properties, allowing for improved URL construction. Tests are implemented to validate these functionalities, and the documentation is updated to reflect the new features.

Changes

Files Change Summary
src/useRouter/index.ts Introduced NavigateToOptions and RedirectToOptions interfaces with an optional query property; updated navigation functions accordingly.
src/useRouter/readme.md Enhanced routing examples to illustrate query handling; added reactive data handling with computed for dynamic data fetching.
src/utils.test.ts Newly created tests for the setQuery function to validate query string construction based on various parameter scenarios.
src/utils.ts Introduced a new setQuery function for constructing URLs with query parameters, including handling of empty and falsy values.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Router
    participant Utils
    
    User->>Router: navigateTo({ query: { bar: 'bar' } })
    Router->>Utils: setQuery(baseUrl, query)
    Utils->>Utils: Construct query string
    Utils-->>Router: Return updated URL
    Router-->>User: Navigate to updated URL
Loading

🐰 In the meadow, hop and play,
With new routes, we find our way!
Parameters dance, URLs gleam,
A rabbit's joy, a coder's dream!
Let's explore, so much to see,
With every hop, we're wild and free! 🐇✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share
Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (invoked as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between a1e6fd8 and 4fad2ab.

Files selected for processing (4)
  • src/useRouter/index.ts (4 hunks)
  • src/useRouter/readme.md (2 hunks)
  • src/utils.test.ts (1 hunks)
  • src/utils.ts (1 hunks)
Additional context used
Biome
src/useRouter/index.ts

[error] 110-110: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)

Additional comments not posted (20)
src/utils.test.ts (5)

5-6: LGTM!

The test case correctly checks if the setParams function constructs the URL with valid parameters.

The code changes are approved.


7-7: LGTM!

The test case correctly checks if the setParams function handles undefined parameters.

The code changes are approved.


8-8: LGTM!

The test case correctly checks if the setParams function handles undefined and null parameters.

The code changes are approved.


9-9: LGTM!

The test case correctly checks if the setParams function handles undefined and zero parameters.

The code changes are approved.


10-10: LGTM!

The test case correctly checks if the setParams function handles undefined and boolean parameters.

The code changes are approved.

src/useRouter/readme.md (6)

11-11: LGTM!

The import statements are correctly added to support the new functionality.

The code changes are approved.


35-36: LGTM!

The code correctly demonstrates how to navigate to a specific page while carrying parameters.

The code changes are approved.


41-42: LGTM!

The code correctly demonstrates how to navigate to a specific page while carrying parameters.

The code changes are approved.


50-51: LGTM!

The computed property correctly extracts the id from currentParams.

The code changes are approved.


54-54: LGTM!

The reactive reference data is correctly initialized.

The code changes are approved.


56-61: LGTM!

The watchEffect function correctly fetches data from an external API using the computed id.

The code changes are approved.

src/useRouter/index.ts (9)

5-6: LGTM!

The imports for tryOnLoad, pathResolve, and setParams are appropriate and necessary for the new functionality.

The code changes are approved.


89-91: LGTM!

The NavigateToOptions interface correctly extends UniNamespace.NavigateToOptions and includes an optional params property.

The code changes are approved.


93-95: LGTM!

The RedirectToOptions interface correctly extends UniNamespace.RedirectToOptions and includes an optional params property.

The code changes are approved.


108-110: LGTM!

The navigateTo function correctly uses the handleParams function to process the params property.

The code changes are approved.

Tools
Biome

[error] 110-110: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)


114-116: LGTM!

The redirectTo function correctly uses the handleParams function to process the params property.

The code changes are approved.


196-197: LGTM!

The currentParams reactive reference is correctly added to store the current parameters.

The code changes are approved.


198-200: LGTM!

The currentParams value is correctly set during the loading phase.

The code changes are approved.


203-208: LGTM!

The navigate and redirect functions correctly accept the new options types and use the updated functions.

The code changes are approved.


221-222: LGTM!

The currentParams is correctly added to the returned object.

The code changes are approved.

src/utils.ts Outdated
Comment on lines 73 to 95
export function setParams(baseUrl: string, params: Record<string, any>): string {
if (!Object.keys(params).length) {
return baseUrl;
}

let parameters = '';

for (const key in params) {
if (params[key] === undefined || params[key] === null || params[key] === '') {
continue;
} // 检查每个参数值是否有效
parameters += `${key}=${encodeURIComponent(params[key])}&`;
}

// 移除末尾多余的 '&' 并构建最终的 URL
parameters = parameters.replace(/&$/, '');
if (!parameters.length) {
return baseUrl;
}
return (/\?$/.test(baseUrl))
? baseUrl + parameters
: baseUrl.replace(/\/?$/, '?') + parameters;
}
Copy link

@coderabbitai coderabbitai bot Aug 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improve readability and efficiency.

The function can be improved by using URLSearchParams for constructing the query string.

Apply this diff to improve the function:

export function setParams(baseUrl: string, params: Record<string, any>): string {
  const url = new URL(baseUrl, 'http://dummy-base');
  const searchParams = new URLSearchParams();

  for (const key in params) {
    if (params[key] !== undefined && params[key] !== null && params[key] !== '') {
      searchParams.append(key, params[key]);
    }
  }

  url.search = searchParams.toString();
  return url.toString().replace('http://dummy-base', '');
}
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function setParams(baseUrl: string, params: Record<string, any>): string {
if (!Object.keys(params).length) {
return baseUrl;
}
let parameters = '';
for (const key in params) {
if (params[key] === undefined || params[key] === null || params[key] === '') {
continue;
} // 检查每个参数值是否有效
parameters += `${key}=${encodeURIComponent(params[key])}&`;
}
// 移除末尾多余的 '&' 并构建最终的 URL
parameters = parameters.replace(/&$/, '');
if (!parameters.length) {
return baseUrl;
}
return (/\?$/.test(baseUrl))
? baseUrl + parameters
: baseUrl.replace(/\/?$/, '?') + parameters;
}
export function setParams(baseUrl: string, params: Record<string, any>): string {
const url = new URL(baseUrl, 'http://dummy-base');
const searchParams = new URLSearchParams();
for (const key in params) {
if (params[key] !== undefined && params[key] !== null && params[key] !== '') {
searchParams.append(key, params[key]);
}
}
url.search = searchParams.toString();
return url.toString().replace('http://dummy-base', '');
}

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

考虑小程序不支持URLSearchParams 故不采用

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zhuddan, that makes perfect sense. Ensuring compatibility with mini-program environments is crucial, and the current implementation is indeed more suitable given the limitations. Thanks for the clarification!

Comment on lines 97 to 107
/**
* 处理 options.params
* @param options
*/
function handleParams<T extends NavigateToOptions | RedirectToOptions>(options: T) {
if (options.params && typeof options.url === 'string') {
options.url = setParams(options.url, options.params);
delete options.params;
}
return options;
}
Copy link

@coderabbitai coderabbitai bot Aug 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid using the delete operator for performance reasons.

The handleParams function correctly processes the params property. However, the delete operator can impact performance. Consider setting the property to undefined instead.

Apply this diff to avoid using the delete operator:

function handleParams<T extends NavigateToOptions | RedirectToOptions>(options: T) {
  if (options.params && typeof options.url === 'string') {
    options.url = setParams(options.url, options.params);
-    delete options.params;
+    options.params = undefined;
  }
  return options;
}
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/**
* 处理 options.params
* @param options
*/
function handleParams<T extends NavigateToOptions | RedirectToOptions>(options: T) {
if (options.params && typeof options.url === 'string') {
options.url = setParams(options.url, options.params);
delete options.params;
}
return options;
}
/**
* 处理 options.params
* @param options
*/
function handleParams<T extends NavigateToOptions | RedirectToOptions>(options: T) {
if (options.params && typeof options.url === 'string') {
options.url = setParams(options.url, options.params);
options.params = undefined;
}
return options;
}

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已根据建议更新了代码

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zhuddan 感谢你根据建议更新了代码!一切看起来都很棒。😊

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 4fad2ab and dd4d57a.

Files selected for processing (1)
  • src/useRouter/index.ts (4 hunks)
Files skipped from review as they are similar to previous changes (1)
  • src/useRouter/index.ts

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between dd4d57a and 96bfc97.

Files selected for processing (4)
  • src/useRouter/index.ts (4 hunks)
  • src/useRouter/readme.md (2 hunks)
  • src/utils.test.ts (1 hunks)
  • src/utils.ts (1 hunks)
Files skipped from review as they are similar to previous changes (2)
  • src/useRouter/readme.md
  • src/utils.test.ts
Additional comments not posted (4)
src/utils.ts (1)

66-66: No changes detected.

The function isThenable remains unchanged and is correctly implemented.

src/useRouter/index.ts (3)

89-95: Interface extensions are well-implemented.

The extensions to NavigateToOptions and RedirectToOptions to include a query property are correctly implemented and align with the new routing functionality.


101-107: Function handleQuery is correctly implemented.

The function processes the query property effectively, appending query parameters to the URL and then cleaning up by setting query to undefined.


196-201: Reactive reference currentQuery is a useful addition.

The introduction of currentQuery to manage and store the current query parameters enhances the state management within the router.

Comment on lines +73 to +95
export function setQuery(baseUrl: string, query: Record<string, any>): string {
if (!Object.keys(query).length) {
return baseUrl;
}

let parameters = '';

for (const key in query) {
if (query[key] === undefined || query[key] === null || query[key] === '') {
continue;
} // 检查每个参数值是否有效
parameters += `${key}=${encodeURIComponent(query[key])}&`;
}

// 移除末尾多余的 '&' 并构建最终的 URL
parameters = parameters.replace(/&$/, '');
if (!parameters.length) {
return baseUrl;
}
return (/\?$/.test(baseUrl))
? baseUrl + parameters
: baseUrl.replace(/\/?$/, '?') + parameters;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider handling array parameters.

The function setQuery is well-implemented for basic key-value pairs. However, consider supporting array parameters, which are common in query strings. This can be done by checking if the value is an array and then iterating over it to append each value with the same key.

Here's a suggested implementation for handling array parameters:

for (const key in query) {
  if (query[key] === undefined || query[key] === null || query[key] === '') {
    continue;
  }
+ if (Array.isArray(query[key])) {
+   query[key].forEach(value => {
+     parameters += `${key}[]=${encodeURIComponent(value)}&`;
+   });
+ } else {
    parameters += `${key}=${encodeURIComponent(query[key])}&`;
+ }
}
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function setQuery(baseUrl: string, query: Record<string, any>): string {
if (!Object.keys(query).length) {
return baseUrl;
}
let parameters = '';
for (const key in query) {
if (query[key] === undefined || query[key] === null || query[key] === '') {
continue;
} // 检查每个参数值是否有效
parameters += `${key}=${encodeURIComponent(query[key])}&`;
}
// 移除末尾多余的 '&' 并构建最终的 URL
parameters = parameters.replace(/&$/, '');
if (!parameters.length) {
return baseUrl;
}
return (/\?$/.test(baseUrl))
? baseUrl + parameters
: baseUrl.replace(/\/?$/, '?') + parameters;
}
export function setQuery(baseUrl: string, query: Record<string, any>): string {
if (!Object.keys(query).length) {
return baseUrl;
}
let parameters = '';
for (const key in query) {
if (query[key] === undefined || query[key] === null || query[key] === '') {
continue;
} // 检查每个参数值是否有效
if (Array.isArray(query[key])) {
query[key].forEach(value => {
parameters += `${key}[]=${encodeURIComponent(value)}&`;
});
} else {
parameters += `${key}=${encodeURIComponent(query[key])}&`;
}
}
// 移除末尾多余的 '&' 并构建最终的 URL
parameters = parameters.replace(/&$/, '');
if (!parameters.length) {
return baseUrl;
}
return (/\?$/.test(baseUrl))
? baseUrl + parameters
: baseUrl.replace(/\/?$/, '?') + parameters;
}

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 96bfc97 and 4fcc99d.

Files selected for processing (1)
  • src/useRouter/index.ts (4 hunks)
Additional comments not posted (6)
src/useRouter/index.ts (6)

89-91: Interface Extension Approved: NavigateToOptions

The addition of the optional query property enhances the flexibility of navigation options by supporting query parameters.

The changes to the interface are approved.


93-95: Interface Extension Approved: RedirectToOptions

The addition of the optional query property to RedirectToOptions is consistent with the changes made to NavigateToOptions, enhancing the redirection capabilities with query parameters.

The changes to the interface are approved.


101-108: Effective Query Handling in handleQuery Function

The function handleQuery effectively integrates query parameters into the URL and cleans up the options object by setting the query property to undefined after use. This approach ensures that the URL is correctly formatted with query parameters and that the options object remains clean.

The implementation of handleQuery is approved.


110-112: Integration of Query Handling in navigateTo Function

The function navigateTo effectively integrates the handleQuery function to process query parameters before navigating. This ensures that the navigation is performed with the correct URL and query parameters.

The changes to navigateTo are approved.


116-118: Consistent Query Handling in redirectTo Function

The function redirectTo now consistently handles query parameters, similar to navigateTo, ensuring that redirections are performed with the correct URL and query parameters.

The changes to redirectTo are approved.


198-202: Addition of Reactive Reference currentQuery Approved

The new reactive reference currentQuery enhances state management within the router by allowing current query parameters to be stored and accessed reactively. This is a valuable addition, especially for pages that need to react to changes in query parameters dynamically.

The addition of currentQuery is approved.

@zhuddan zhuddan changed the title feat: useRouter add params support feat: useRouter add query support Aug 28, 2024
@edwinhuish
Copy link
Collaborator

暂无此应用场景,不过既然 query 最终还是得拼接到 url 上,那么就没有必要额外多加一个 query 选项,而是让用户自己去拼接URL。 仅需要监听 onload 的query参数即可,如你的下面这段。

  const currentQuery = ref<Record<string, any>>({});

  tryOnLoad((options) => {
    currentQuery.value = options ?? {};
  });

移除 query 参数后,关于你的 setQuery 函数,由于此库是 uni-use,不宜加入此处。建议可自用、另发包、或写入readme。

此函数建议可以改进成这样:

/**
 * 封装带有查询参数的 URL
 * @param baseUrl - 基础 URL
 * @param query - 要附加到 URL 的查询参数对象
 * @returns 返回附加了查询参数的完整 URL
 */
export function buildUrl(baseUrl: string, query: Record<string, any>): string {
  if (!Object.keys(query).length) {
    return baseUrl;
  }

  const pieces: string[] = [];

  for (const key in query) {
    if (query[key] === undefined || query[key] === null || query[key] === '') {
      // 用户传入了 key,理应附加此 key,而非移除
      pieces.push(`${key}=`);
    } else {
      pieces.push(`${key}=${encodeURIComponent(query[key])}`);
    }
  }

  // 使用数组 join 方法,而不是使用字符串拼接,以提高性能
  const parameters = pieces.join('&');

  if (!parameters.length) {
    return baseUrl;
  }

  return baseUrl.includes('?')  // 可不用正则解决的需求尽量不要用正则。
    ? `${baseUrl}&${parameters}`
    : `${baseUrl}?${parameters}`;
}

@zhuddan
Copy link
Author

zhuddan commented Aug 29, 2024

暂无此应用场景,不过既然 query 最终还是得拼接到 url 上,那么就没有必要额外多加一个 query 选项,而是让用户自己去拼接URL。 仅需要监听 onload 的query参数即可,如你的下面这段。

感谢您仔细审阅并提供反馈。我理解现有应用场景下,直接让用户拼接 URL 确实更为简洁高效,而不需要额外增加 query 选项。我会遵循您的建议,仅在 onload 时监听 query 参数。既然如此,我会关闭这个PR。非常感谢您的指导,今后我会更加注意确保方案与仓库的目标一致。

@zhuddan zhuddan closed this Aug 29, 2024
@zhuddan zhuddan deleted the useRouter-params branch August 29, 2024 01:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants