Răsfoiți Sursa

✨ feat(dependencies): add polyfills for Headers and Response

- 添加abortcontroller-polyfill以支持AbortController
- 创建Headers和Response的polyfill实现,解决小程序环境缺失问题
- 在app.tsx中导入必要的polyfill文件

📝 docs(page): update welcome text on index page

- 修改首页欢迎文本为"欢迎使用小程序444"

♻️ refactor(rpc): optimize rpc client implementation

- 更新rpcClient类型定义,明确要求Hono类型
- 调整taroFetch实现,使用ResponsePolyfill处理响应
- 简化请求头处理逻辑
yourname 4 luni în urmă
părinte
comite
14cf34da46

+ 3 - 2
mini/package.json

@@ -46,6 +46,7 @@
   "author": "",
   "dependencies": {
     "@babel/runtime": "^7.24.4",
+    "@tanstack/react-query": "^5.84.1",
     "@tarojs/components": "4.1.4",
     "@tarojs/helper": "4.1.4",
     "@tarojs/plugin-framework-react": "4.1.4",
@@ -61,9 +62,9 @@
     "@tarojs/runtime": "4.1.4",
     "@tarojs/shared": "4.1.4",
     "@tarojs/taro": "4.1.4",
+    "abortcontroller-polyfill": "^1.7.8",
     "react": "^18.0.0",
-    "react-dom": "^18.0.0",
-    "@tanstack/react-query": "^5.84.1"
+    "react-dom": "^18.0.0"
   },
   "devDependencies": {
     "@babel/core": "^7.24.4",

+ 3 - 0
mini/pnpm-lock.yaml

@@ -59,6 +59,9 @@ importers:
       '@tarojs/taro':
         specifier: 4.1.4
         version: 4.1.4(@tarojs/components@4.1.4(@tarojs/helper@4.1.4)(@types/react@18.3.23)(html-webpack-plugin@5.6.3(webpack@5.91.0(@swc/core@1.3.96)))(postcss@8.5.6)(rollup@3.29.5)(vue@3.5.18(typescript@5.8.3))(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96)))(webpack@5.91.0(@swc/core@1.3.96)))(@tarojs/helper@4.1.4)(@tarojs/shared@4.1.4)(@types/react@18.3.23)(html-webpack-plugin@5.6.3(webpack@5.91.0(@swc/core@1.3.96)))(postcss@8.5.6)(rollup@3.29.5)(vue@3.5.18(typescript@5.8.3))(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96)))(webpack@5.91.0(@swc/core@1.3.96))
+      abortcontroller-polyfill:
+        specifier: ^1.7.8
+        version: 1.7.8
       react:
         specifier: ^18.0.0
         version: 18.3.1

+ 2 - 0
mini/src/app.tsx

@@ -1,3 +1,5 @@
+import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only';
+import '@/utils/headers-polyfill.js'
 import { PropsWithChildren } from 'react'
 import { useLaunch } from '@tarojs/taro'
 import { QueryClientProvider } from '@tanstack/react-query'

+ 1 - 1
mini/src/pages/index/index.tsx

@@ -125,7 +125,7 @@ export default function Index() {
               className="w-32 h-32 rounded-full mx-auto mb-4"
               src="https://images.unsplash.com/photo-1551650975-87deedd944c3?w=192&h=192&fit=crop"
             />
-            <Text className="text-3xl font-bold text-gray-900 mb-2">欢迎使用小程序</Text>
+            <Text className="text-3xl font-bold text-gray-900 mb-2">欢迎使用小程序444</Text>
             <Text className="text-gray-600 text-lg">请先登录以使用完整功能</Text>
           </View>
           

+ 79 - 0
mini/src/utils/headers-polyfill.js

@@ -0,0 +1,79 @@
+class Headers {
+    constructor(init = {}) {
+      this._headers = {};
+  
+      if (init instanceof Headers) {
+        // 如果传入的是另一个 Headers 实例,复制其内容
+        init.forEach((value, name) => {
+          this.append(name, value);
+        });
+      } else if (init) {
+        // 处理普通对象或数组
+        Object.entries(init).forEach(([name, value]) => {
+          if (Array.isArray(value)) {
+            // 处理数组值(如 ['value1', 'value2'])
+            value.forEach(v => this.append(name, v));
+          } else {
+            this.set(name, value);
+          }
+        });
+      }
+    }
+  
+    // 添加头(可重复添加同名头)
+    append(name, value) {
+      const normalizedName = this._normalizeName(name);
+      if (this._headers[normalizedName]) {
+        this._headers[normalizedName] += `, ${value}`;
+      } else {
+        this._headers[normalizedName] = String(value);
+      }
+    }
+  
+    // 设置头(覆盖同名头)
+    set(name, value) {
+      this._headers[this._normalizeName(name)] = String(value);
+    }
+  
+    // 获取头
+    get(name) {
+      return this._headers[this._normalizeName(name)] || null;
+    }
+  
+    // 检查是否存在头
+    has(name) {
+      return this._normalizeName(name) in this._headers;
+    }
+  
+    // 删除头
+    delete(name) {
+      delete this._headers[this._normalizeName(name)];
+    }
+  
+    // 遍历头
+    forEach(callback) {
+      Object.entries(this._headers).forEach(([name, value]) => {
+        callback(value, name, this);
+      });
+    }
+  
+    // 获取所有头(原始对象)
+    raw() {
+      return { ...this._headers };
+    }
+  
+    // 规范化头名称(转为小写)
+    _normalizeName(name) {
+      if (typeof name !== 'string') {
+        throw new TypeError('Header name must be a string');
+      }
+      return name.toLowerCase();
+    }
+  }
+  
+  // 全局注册(如果需要)
+  if (typeof globalThis.Headers === 'undefined') {
+    globalThis.Headers = Headers;
+  }
+  
+  export default Headers;

+ 88 - 0
mini/src/utils/response-polyfill.ts

@@ -0,0 +1,88 @@
+class ResponsePolyfill {
+    constructor(
+      public body: string | ArrayBuffer | null,
+      public init: {
+        status?: number
+        statusText?: string
+        headers?: Record<string, string>
+      } = {}
+    ) {}
+  
+    get ok(): boolean {
+      return this.status >= 200 && this.status < 300
+    }
+  
+    get status(): number {
+      return this.init.status || 200
+    }
+  
+    get statusText(): string {
+      return this.init.statusText || 'OK'
+    }
+  
+    get headers(): Headers {
+      return new Headers(this.init.headers || {})
+    }
+  
+    get bodyUsed(): boolean {
+      return false // 小程序环境简单实现
+    }
+  
+    async arrayBuffer(): Promise<ArrayBuffer> {
+      if (this.body instanceof ArrayBuffer) {
+        return this.body
+      }
+      throw new Error('Not implemented')
+    }
+  
+    async text(): Promise<string> {
+      if (typeof this.body === 'string') {
+        return this.body
+      }
+      throw new Error('Not implemented')
+    }
+  
+    async json<T = any>(): Promise<T> {
+      if (typeof this.body === 'string') {
+        try {
+          return JSON.parse(this.body)
+        } catch (e) {
+          throw new Error('Invalid JSON')
+        }
+      }
+      throw new Error('Not implemented')
+    }
+  
+    clone(): ResponsePolyfill {
+      return new ResponsePolyfill(this.body, { ...this.init })
+    }
+  
+    static json(data: any, init?: ResponseInit): ResponsePolyfill {
+      const headers = new Headers(init && 'headers' in init ? init.headers : undefined)
+      if (!headers.has('Content-Type')) {
+        headers.set('Content-Type', 'application/json')
+      }
+      return new ResponsePolyfill(JSON.stringify(data), {
+        ...init,
+        headers: Object.fromEntries(headers.entries())
+      })
+    }
+  
+    static error(): ResponsePolyfill {
+      return new ResponsePolyfill(null, { status: 0, statusText: 'Network Error' })
+    }
+  
+    static redirect(url: string, status: number): ResponsePolyfill {
+      return new ResponsePolyfill(null, {
+        status,
+        headers: { Location: url }
+      })
+    }
+  }
+  
+  // 全局注册(如果需要)
+  if (typeof globalThis.Response === 'undefined') {
+    globalThis.Response = ResponsePolyfill as any
+  }
+  
+  export default ResponsePolyfill

+ 14 - 17
mini/src/utils/rpc-client.ts

@@ -1,5 +1,7 @@
 import Taro from '@tarojs/taro'
+import type { Hono } from 'hono'
 import { hc } from 'hono/client'
+import ResponsePolyfill from './response-polyfill'
 
 // API配置
 const API_BASE_URL = process.env.TARO_APP_API_BASE_URL || 'http://localhost:3000'
@@ -8,17 +10,11 @@ const API_BASE_URL = process.env.TARO_APP_API_BASE_URL || 'http://localhost:3000
 // const BASE_URL = `${API_BASE_URL}/api/${API_VERSION}`
 
 // 创建自定义fetch函数,适配Taro.request
-const taroFetch: typeof fetch = async (input, init) => {
+const taroFetch: any = async (input, init) => {
   const url = typeof input === 'string' ? input : input.url
   const method = init.method || 'GET'
   
-  const requestHeaders: Record<string, string> = {};
-
-  if (init.headers instanceof Headers) {
-    init.headers.forEach((value, key) => {
-      requestHeaders[key] = value;
-    })
-  }
+  const requestHeaders: Record<string, string> = init.headers;
 
   // 构建Taro请求选项
   const options: Taro.request.Option = {
@@ -38,23 +34,24 @@ const taroFetch: typeof fetch = async (input, init) => {
   }
 
   try {
+    // const response = await Taro.request(options)
     const response = await Taro.request(options)
 
-    const responseHeaders = new Headers();
-    if (response.header) {
-      for (const [key, value] of Object.entries(response.header)) {
-        responseHeaders.set(key, value);
-      }
-    }
+    const responseHeaders = response.header;
+    // if (response.header) {
+    //   for (const [key, value] of Object.entries(response.header)) {
+    //     responseHeaders.set(key, value);
+    //   }
+    // }
 
       // 处理204 No Content响应,不设置body
     const body = response.statusCode === 204
     ? null
-    : responseHeaders.get('content-type')!.includes('application/json')
+    : responseHeaders['content-type']!.includes('application/json')
       ? JSON.stringify(response.data)
       : response.data;
 
-    return new Response(
+    return new ResponsePolyfill(
       body,
       {
         status: response.statusCode,
@@ -73,7 +70,7 @@ const taroFetch: typeof fetch = async (input, init) => {
 }
 
 // 创建Hono RPC客户端
-export const rpcClient = <T>() => {
+export const rpcClient = <T extends Hono>() => {
   return hc<T>(`${API_BASE_URL}`, {
     fetch: taroFetch
   })