跳转到内容

穗腾科技

刀刀

6/20/2025

0 字

0 分钟

一面

Q:讲一下 Vue2 和 Vue3 在 组件通信、响应式、性能优化有什么区别

我的回答

Vue2 响应式原理是 Object.defineProperty,在 Vue2 中如果给对象添加一个新的属性,是不会触发响应式的,因为没有绑定 gettersetter;而数组是通过遍历,给每一项绑定响应式,如果层级多需要深度递归绑定。对于数组的方法如 pushpopshiftunshiftsplicesortreverse 等,Vue2 会对这些方法进行重写,从而实现响应式。

Vue3 响应式原理是 Proxy,它代理是直接代理整个对象,不管是新增还是删除属性,都会触发响应式。

Vue2 想要给对象添加一个新属性,或者通过索引改变数组,需要使用 Vue.setVue.delete,Vue3 不需要。

Vue2 组件通信是通过 props$emit 实现,父组件拿子组件数据通过 this.$refs,子传父通过 this.$emit 触发父组件的事件。Vue3 组件通信是通过 propsemit 实现,父组件想要拿子组件数据需要子组件通过 defineExpose 暴露数据,父组件通过 ref 拿到子组件实例,再拿到子组件数据。

Q:讲一下 Pinia 对比 Vuex 的优势

我的回答

Pinia 是 useHooks 的形式,Vuex 是组合式 API,参数只能定义在 state ,在 mutations 中同步修改值,异步操作只能在 actions 中。Pinia 可以直接用 ref 定义值,定义函数,直接修改值,还能通过插件实现本地存储

Deepseek 回答

特性VuexPinia
Vue 版本支持Vue 2 & 3专为 Vue 3 设计(兼容 Vue 2)
TypeScript 支持需要额外声明完美支持(自动类型推断)
API 设计较复杂(mutations/actions)极简 API(actions 统一操作)
模块化需要 modules 配置自动模块化(文件即模块)
代码分割需动态注册模块开箱即用(按需加载)
DevTools 集成支持 更优支持(时间线/编辑等)
包体积较大(2.6KB gzip)更轻量(1.5KB gzip)
Composition API兼容(但不够自然)原生支持(完美契合)
  1. 极简的 API 设计(核心优势)

    Pinia 移除了 Vuex 中繁琐的 mutations:

    javascript
    // Vuex 的修改操作
    mutations: {
      SET_USER(state, user) {
        state.user = user;
      }
    },
    actions: {
      async fetchUser({ commit }) {
        const user = await api.getUser();
        commit('SET_USER', user); // 需通过 mutation
      }
    }
    
    // Pinia 的修改操作(直接通过 action)
    actions: {
      async fetchUser() {
        this.user = await api.getUser(); // 直接赋值
      }
    }
  2. 完美的 TypeScript 支持

    Pinia 提供全自动类型推断:

    typescript
    // store/user.ts
    export const useUserStore = defineStore('user', {
      state: () => ({
        name: 'Alice',
        age: 28
      }),
      getters: {
        doubleAge(): number { // 自动推断返回类型
          return this.age * 2;
        }
      }
    });
    
    // 组件中使用
    const store = useUserStore();
    store.name // 类型为 string
    store.doubleAge // 类型为 number
  3. 极简的模块化设计

    Pinia 采用文件即模块的零配置方案:

    text
    stores/
      ├─ user.ts       // useUserStore()
      ├─ product.ts    // useProductStore()
      └─ cart.ts       // useCartStore()
  4. 更轻量高效

    • 包体积减少约 40%
    • 无嵌套模块,状态扁平化
    • 更好的性能优化
  5. Composition API 原生支持

    Pinia 与 Composition API 完美融合:

    javascript
    // 在 setup 中使用
    import { useUserStore } from '@/stores/user';
    
    export default {
      setup() {
        const userStore = useUserStore();
        
        // 计算属性自动更新
        const isAdult = computed(() => userStore.age >= 18);
        
        return { userStore, isAdult };
      }
    }
  6. 更强大的 DevTools 支持

    • 时间线追踪:清晰展示状态变化时序
    • Store 热更新:修改代码后保持状态
    • 直接编辑状态:调试时实时修改
    • Action 日志:记录参数和耗时

Q:讲一下 ts 的 常用配置

我的回答

变量: 类型 来声明类型;as 断言;? 可选;! 非空;T 泛型;Pick Omit 等。

Q:之前用 JS 如何确保健壮性

我的回答

人为用 if 手写校验判断规则,不符合则 return 错误信息。

Deepseek 回答

  1. 严格的输入验证

    • 验证所有外部输入:用户输入、API响应、文件内容等
    • 类型检查:使用 typeof 和 instanceof 确保正确的数据类型
    • 范围验证:检查数字、字符串长度等是否在有效范围内
    • 模式验证:使用正则表达式验证复杂格式(如邮箱、URL)
  2. 全面的错误处理

    • 使用 try/catch:捕获同步代码中的错误
    • Promise.catch():处理异步操作中的错误
    • 全局错误处理:捕获未处理的错误和Promise拒绝
    • 自定义错误类型:创建特定错误类以便更好地区分错误
  3. 防御性编程

    • 空值和未定义检查:使用可选链 ?. 和空值合并 ??
    • 参数默认值:为函数参数提供合理的默认值
    • 边界条件处理:考虑极端情况和边界值
    • 资源清理:使用 finally 块确保资源释放
  4. 代码可维护性

    • 模块化设计:将代码拆分为小的、单一职责的函数
    • 清晰命名:使用有意义的变量和函数名
    • 代码注释:解释复杂的逻辑和决策
    • 一致性:遵循一致的代码风格和模式
  5. 测试策略

    • 单元测试:验证单个函数或模块的行为
    • 集成测试:测试多个组件的交互
    • 端到端测试:模拟真实用户场景
    • 边界测试:测试极端值和边界条件
  6. 异步健壮性

    • Promise处理:正确处理异步操作的成功和失败
    • 超时机制:为异步操作设置合理的超时
    • 取消支持:提供取消异步操作的能力
    • 并发控制:限制同时进行的异步操作数量
  7. 安全实践

    • 防止注入攻击:清理用户输入,避免直接执行
    • 内容安全策略:实施CSP防止XSS攻击
    • 避免敏感信息泄露:不在客户端存储敏感数据
    • HTTPS:始终通过安全连接传输数据

Q:视频分销项目怎么结果多端兼容性问题

我的回答

小差异用注释 #ifdef 处理

Deepseek 回答

  1. 平台检测与适配

    • 使用 uni.getSystemInfoSync() 获取当前平台信息
    • 条件编译:使用 #ifdef#ifndef 处理平台差异
    • 环境变量:process.env.UNI_PLATFORM 获取编译平台
    • 自定义适配层:封装平台适配函数
  2. API兼容性处理

    • 安全调用:检查API是否存在再调用
    • API封装:对平台差异API进行统一封装
    • 降级方案:为不支持的API提供备选方案
    • Promise封装:统一异步API的调用方式
  3. 组件兼容性

    • 条件渲染:使用条件编译渲染不同组件
    • 自定义兼容组件:封装跨平台组件
    • 组件属性适配:处理不同平台的属性差异
    • 组件扩展文件:.h5.vue, .mp.vue, .app.vue
  4. 样式兼容性

    • 使用Flex布局:确保布局的响应式
    • rpx单位:使用Uniapp的响应式单位
    • 条件样式:使用条件编译处理样式差异
    • 平台特定样式文件:style.h5.css, style.mp.css
    • CSS变量:使用CSS变量管理主题和尺寸
  5. 常见兼容性问题及解决方案

    • 问题1:导航栏差异

      解决方案:

      javascript
      // 获取导航栏高度
      function getNavbarHeight() {
          const sysInfo = uni.getSystemInfoSync();
          // #ifdef MP-WEIXIN
          const menuButton = wx.getMenuButtonBoundingClientRect();
          return menuButton.bottom + menuButton.top - sysInfo.statusBarHeight;
          // #endif
          
          // #ifdef H5
          return 44; // H5固定导航栏高度
          // #endif
          
          // #ifdef APP-PLUS
          return sysInfo.statusBarHeight + 44;
          // #endif
          
          return 44; // 默认值
      }
    • 问题2:支付功能差异

      解决方案:

      javascript
      function pay(options) {
          // #ifdef MP-WEIXIN
          return uni.requestPayment({
              provider: 'wxpay',
              ...options
          });
          // #endif
          
          // #ifdef MP-ALIPAY
          return uni.requestPayment({
              provider: 'alipay',
              ...options
          });
          // #endif
          
          // #ifdef APP-PLUS
          // iOS应用内支付
          if (plus.os.name === 'iOS') {
              return uni.requestPayment({
                  provider: 'appleiap',
                  ...options
              });
          }
          // Android支付
          return uni.requestPayment({
              provider: 'paymentService', // 需要配置支付服务
              ...options
          });
          // #endif
          
          // #ifdef H5
          // H5支付跳转
          window.location.href = options.paymentUrl;
          // #endif
      }
    • 问题3:文件系统差异

      解决方案:

      javascript
      async function readFile(filePath) {
          // #ifdef H5
          // 使用File API
          const response = await fetch(filePath);
          return await response.blob();
          // #endif
          
          // #ifdef MP-WEIXIN
          return new Promise((resolve, reject) => {
              wx.getFileSystemManager().readFile({
                  filePath,
                  encoding: 'binary',
                  success: res => resolve(res.data),
                  fail: reject
              });
          });
          // #endif
          
          // #ifdef APP-PLUS
          return new Promise((resolve, reject) => {
              plus.io.resolveLocalFileSystemURL(filePath, entry => {
                  entry.file(file => {
                      const reader = new plus.io.FileReader();
                      reader.onload = e => resolve(e.target.result);
                      reader.onerror = reject;
                      reader.readAsArrayBuffer(file);
                  }, reject);
              }, reject);
          });
          // #endif
      }

Q:接到新需求时开发前会不会先考虑差异性问题

我的回答

比如开发蓝牙功能,会先去看 uniapp 有没有适配多端,没有的话就去官方看看有没有人有相关问题解决。

Q:怎么优化地图echart图渲染

我的回答

echart 图在左右两侧,通过 IntersectionObserver 监听滚动,当滚动到指定位置时,再去渲染那些 echart 图。

Deepseek 回答

一、数据层优化(核心瓶颈)

  1. 压缩 GeoJSON 数据

    使用 mapshaper 简化地理数据(减少 50%-90% 体积):

    bash
    mapshaper input.geojson -simplify 10% -o output.geojson
  2. 精度控制:保留小数点后 4 位(经纬度)

    案例:某省级地图从 2.3MB → 280KB,加载速度提升 5 倍

  3. 按需加载矢量边界

    javascript
    // 动态注册地图(避免全局加载)
    import simplifiedChina from './china_simplified.json'; 
    
    echarts.registerMap('china', simplifiedChina); 
    const option = { series: [{ type: 'map', map: 'china' }] };
  4. 离散点数据聚合

    10万+点位时使用 Grid 聚类:

    javascript
    series: [{
      type: 'scatter',
      coordinateSystem: 'geo',
      cluster: true,
      data: pointsArray  // 原始经纬度数组
    }]

二、渲染策略优化

  1. 渐进渲染(大数据场景)

    javascript
    chart.setOption({
      series: [{
        progressive: 2000,  // 每次渲染2000个点
        progressiveThreshold: 5000 // 超过5000点启用分片渲染
      }]
    });
  2. 分层绘制复杂元素

    javascript
    // 基础轮廓层(静态)
    { type: 'map', map: 'china', silent: true, itemStyle: { ... } }
    
    // 动态数据层(单独控制)
    { type: 'scatter', zlevel: 2, symbolSize: 8, data: [...] }
  3. WebWorker 异步计算

    将路径计算/聚类等 CPU 密集型操作移至 Worker:

    javascript
    const worker = new Worker('./geoCluster.js');
    worker.postMessage(rawData);
    worker.onmessage = (e) => chart.setOption({ series: [{ data: e.data }] });

三、配置关键参数

参数推荐值作用
hoverLayerThreshold5000超过该点数禁用鼠标事件
animationfalse 大数据关闭动画
blendMode'lighter'叠加效果更轻量
largetrue开启大数据模式
useDirtyRecttrue (v5.4.1+)局部渲染(增量更新)
四、内存泄漏防御
javascript
// 销毁时释放资源(SPA 必备)
beforeUnmount() {
  chart.dispose();  // 销毁实例
  echarts.unregisterMap('china'); // 释放地图内存
}

Q:如果有多个地图扎点,如何优化

我的回答

分别渲染,每次渲染20个,分多次渲染

Q:还有其他方法么

我的回答

暂时没想到

Q:如何封装echart图

我的回答

封装成组件,以柱状图为例,x轴y轴等固定的参数直接封装,是否需要单位、数据、柱体颜色宽度、tooltip 统一 props 传入。

Deepseek 回答

echart组件代码示例
vue
// EChartWrapper.vue
<template>
  <div ref="chartDom" class="echart-container" :style="{ width, height }"></div>
</template>

<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, watch, shallowRef } from 'vue';
import * as echarts from 'echarts/core';
import {
  TooltipComponent,
  GridComponent,
  LegendComponent
} from 'echarts/components';
import { BarChart, LineChart, PieChart } from 'echarts/charts';
import { CanvasRenderer } from 'echarts/renderers';

// 按需注册组件
echarts.use([
  TooltipComponent,
  GridComponent,
  LegendComponent,
  BarChart,
  LineChart,
  PieChart,
  CanvasRenderer
]);

// TypeScript 类型定义
interface Props {
  width?: string;
  height?: string;
  option: echarts.EChartsCoreOption;
  theme?: string | object;
  autoResize?: boolean;
}

const props = withDefaults(defineProps<Props>(), {
  width: '100%',
  height: '400px',
  autoResize: true,
  theme: 'default'
});

const chartDom = ref<HTMLDivElement | null>(null);
const chartInstance = shallowRef<echarts.ECharts | null>(null);

// 初始化图表
const initChart = () => {
  if (!chartDom.value) return;
  
  // 销毁旧实例
  if (chartInstance.value) {
    chartInstance.value.dispose();
  }
  
  // 创建新实例
  chartInstance.value = echarts.init(
    chartDom.value, 
    props.theme,
    { renderer: 'canvas' } // 强制使用Canvas
  );
  
  // 设置配置项
  chartInstance.value.setOption(props.option);
  
  // 自动响应式
  if (props.autoResize) {
    window.addEventListener('resize', handleResize);
  }
};

// 响应式调整
const handleResize = () => {
  chartInstance.value?.resize();
};

// 监听配置变化
watch(
  () => props.option,
  (newOption) => {
    if (!chartInstance.value) return;
    chartInstance.value.setOption(newOption, true); // 第二个参数表示不合并配置
  },
  { deep: true }
);

// 生命周期
onMounted(initChart);
onBeforeUnmount(() => {
  if (chartInstance.value) {
    if (props.autoResize) {
      window.removeEventListener('resize', handleResize);
    }
    chartInstance.value.dispose();
  }
});

// 暴露实例方法
defineExpose({
  getInstance: () => chartInstance.value,
  resize: handleResize
});
</script>

<style scoped>
.echart-container {
  background: white;
  overflow: hidden;
}
</style>

事件统一处理:

事件统一处理代码
js
// 事件映射表
const eventMap = {
  click: 'click',
  mouseover: 'mouseover',
  // ...其他事件
};

// 自动绑定事件
const bindEvents = () => {
  if (!chartInstance.value) return;
  
  Object.entries(eventMap).forEach(([emitName, echartsEvent]) => {
    chartInstance.value?.on(echartsEvent, (params) => {
      emit(emitName, params);
    });
  });
};

// 在initChart中调用
initChart();
bindEvents();

按需加载模块:

按需加载模块代码
js
// 动态加载图表类型
const loadChartModule = async (type: string) => {
  switch (type) {
    case 'map':
      const [map] = await Promise.all([
        import('echarts/charts').then(m => m.MapChart),
        import('echarts/components').then(m => m.GeoComponent)
      ]);
      echarts.use([map]);
      break;
    // 其他类型处理
  }
};

// 根据option自动检测
watch(() => props.option, (newOption) => {
  const seriesTypes = newOption.series?.map(s => s.type) || [];
  seriesTypes.forEach(type => loadChartModule(type));
});

Q:音果项目SKU组件封装的思路,为什么要封装

我的回答

封装成组件,方便快速落地,组件高内聚低耦合

Q:如何迁移项目 Vue2 到 Vue3

我的回答

没做过,想法是先升级到 Vue2.7,可适配 2 和 3,然后逐步迁移。

Deepseek 回答

  1. 组合式 API 优先迁移
    javascript
    // Before (Vue2 Options API)
    export default {
      data() {
        return { count: 0 }
      },
      methods: {
        increment() {
          this.count++
        }
      }
    }
    
    // After (Vue3 Composition API)
    import { ref } from 'vue'
    
    export default {
      setup() {
        const count = ref(0)
        
        const increment = () => {
          count.value++
        }
        
        return { count, increment }
      }
    }
  2. 组件迁移路线图 组件迁移路线图
  3. 关键变更点处理
    • 生命周期映射
      Vue2Vue3说明
      beforeCreatesetup()使用 setup 替代
      createdsetup()使用 setup 替代
      beforeMountonBeforeMount
      mountedonMounted
      beforeUpdateonBeforeUpdate
      updatedonUpdated
      beforeDestroyonBeforeUnmount
      destroyedonUnmounted
    • API 变更处理
      js
      // 事件总线替代方案(Vue2)
      // Vue.prototype.$bus = new Vue()
      
      // Vue3 使用 mitt 库
      import mitt from 'mitt'
      const emitter = mitt()
      
      // 发送事件
      emitter.emit('event-name', data)
      
      // 监听事件
      emitter.on('event-name', (data) => {
        // 处理事件
      })
    • 全局 API 变更
      js
      // Vue2
      Vue.component('comp', Component)
      Vue.directive('focus', FocusDirective)
      
      // Vue3
      const app = createApp(App)
      app.component('comp', Component)
      app.directive('focus', FocusDirective)
    • v-model 升级
      vue
      <!-- Vue2 -->
      <ChildComponent v-model="pageTitle" />
      
      <!-- 等价于 -->
      <ChildComponent :value="pageTitle" @input="pageTitle = $event" />
      
      <!-- Vue3 -->
      <ChildComponent v-model:title="pageTitle" />
      
      <!-- 等价于 -->
      <ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />
  4. 依赖迁移
    • 库迁移
      库名称Vue3 支持状态替代方案
      Vuex兼容 (v4.x)Pinia (推荐)
      Vue Router兼容 (v4.x)
      Element UI不兼容Element Plus
    • 依赖迁移
      bash
      # 更新 Vue 全家桶
      npm uninstall vue vue-router vuex
      npm install vue@next vue-router@4 vuex@4
      
      # UI 库迁移
      npm uninstall element-ui
      npm install element-plus
      
      # 状态管理迁移
      npm uninstall vuex
      npm install pinia

Q:有没有了解后台管理框架

我的回答

没怎么接触过

Q:拉一个新项目下来,如何设置权限

我的回答

后端返回权限列表,前端通过动态路由添加。组件权限用自定义事件

Deepseek 回答

  1. 路由级权限控制(Vue示例)
    javascript
    // router.js
    import { createRouter } from 'vue-router'
    
    const routes = [
      {
        path: '/admin',
        component: () => import('./Admin.vue'),
        meta: { requiresAuth: true, permissions: ['admin:access'] }
      },
      {
        path: '/reports',
        component: () => import('./Reports.vue'),
        meta: { requiresAuth: true, permissions: ['report:view'] }
      }
    ]
    
    router.beforeEach((to, from, next) => {
      const authStore = useAuthStore()
      
      // 检查登录状态
      if (to.meta.requiresAuth && !authStore.isAuthenticated) {
        return next('/login')
      }
      
      // 检查权限
      if (to.meta.permissions) {
        const hasPermission = authStore.hasPermissions(to.meta.permissions)
        if (!hasPermission) return next('/forbidden')
      }
      
      next()
    })
  2. 组件级权限控制
    vue
    <template>
      <div>
        <button v-if="hasPermission('user:create')">添加用户</button>
        <button v-if="hasPermission('user:delete')">删除用户</button>
      </div>
    </template>
    
    <script>
    import { useAuthStore } from '@/stores/auth'
    
    export default {
      setup() {
        const authStore = useAuthStore()
        
        const hasPermission = (permission) => {
          return authStore.hasPermission(permission)
        }
        
        return { hasPermission }
      }
    }
    </script>
  3. 权限指令(高级用法)
    javascript
    // directives/permission.js
    export default {
      mounted(el, binding) {
        const authStore = useAuthStore()
        const { value } = binding
        
        if (!authStore.hasPermission(value)) {
          el.parentNode?.removeChild(el)
        }
      }
    }
    
    // 使用
    <button v-permission="'user:delete'">删除</button>

Q:echart图x轴实现滚动

我的回答

dataZoom

Q:vite等构建工具的配置

我的回答

base、publicPath、代理、别名、build打包等

Deepseek 回答

  1. 基础配置

    javascript
    import { defineConfig } from 'vite';
    import vue from '@vitejs/plugin-vue'; // Vue 项目
    // import react from '@vitejs/plugin-react'; // React 项目
    
    export default defineConfig({
      // 项目根目录(默认 process.cwd())
      root: './src',
      
      // 公共基础路径(部署子目录时使用)
      base: '/my-app/', 
      
      // 插件配置
      plugins: [vue()],
      
      // 开发服务器配置
      server: {
        port: 3000,              // 端口号
        open: true,               // 自动打开浏览器
        proxy: {                  // 代理配置
          '/api': {
            target: 'http://localhost:8080',
            changeOrigin: true,
            rewrite: path => path.replace(/^\/api/, '')
          }
        }
      },
      
      // 构建配置
      build: {
        outDir: '../dist',       // 输出目录
        assetsDir: 'assets',     // 静态资源目录
        sourcemap: true,         // 生成 sourcemap
        minify: 'terser',        // 代码压缩工具
      },
      
      // 解析配置
      resolve: {
        alias: {                 // 路径别名
          '@': path.resolve(__dirname, './src'),
          'components': '/src/components'
        }
      }
    });
  2. 常用插件配置

    javascript
    // 示例插件(需先安装)
    import legacy from '@vitejs/plugin-legacy';          // 传统浏览器支持
    import svgr from 'vite-plugin-svgr';                 // SVG 组件
    import checker from 'vite-plugin-checker';           // TypeScript 检查
    
    plugins: [
      vue(),
      legacy({ targets: ['defaults', 'not IE 11'] }),
      svgr(),
      checker({ typescript: true }) // TS 类型检查
    ]
  3. 环境变量配置

    创建 .env 文件:

    bash
    .env         # 所有环境
    .env.local   # 本地覆盖(gitignore)
    .env.development # 开发环境
    .env.production  # 生产环境

    内容格式(以 VITE_ 开头):

    env
    VITE_API_URL=https://api.example.com
    VITE_DEBUG=true

    使用变量:

    javascript
    console.log(import.meta.env.VITE_API_URL);

Q:项目上线跨域问题怎么解决

我的回答

前端设置 ngnix 代理

Deepseek 回答

  1. 方案 1:Nginx 反向代理(推荐)

    js
    server {
        listen 80;
        server_name your-domain.com;
    
        # 前端静态文件
        location / {
            root /usr/share/nginx/html;
            index index.html;
            try_files $uri $uri/ /index.html;
        }
    
        # API 代理
        location /api/ {
            proxy_pass http://backend-server:8080/; # 后端服务器
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            
            # CORS 头(可选,也可在后端设置)
            add_header 'Access-Control-Allow-Origin' '$http_origin' always;
            add_header 'Access-Control-Allow-Credentials' 'true' always;
            add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' >     always;
            add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type' >     always;
            
            # 处理 OPTIONS 预检请求
            if ($request_method = 'OPTIONS') {
                return 204;
            }
        }
    }
  2. 方案 2:后端配置 CORS 头

    以 Node.js (Express) 为例:

    javascript
    // server.js
    const express = require('express');
    const cors = require('cors');
    
    const app = express();
    
    // 基本 CORS 配置
    app.use(cors());
    
    // 高级配置
    app.use(cors({
      origin: ['https://your-frontend.com', 'https://another-domain.com'],
      methods: ['GET', 'POST', 'PUT', 'DELETE'],
      allowedHeaders: ['Content-Type', 'Authorization'],
      credentials: true,
      maxAge: 86400
    }));
    
    // 手动设置 CORS 头
    app.use((req, res, next) => {
      res.header('Access-Control-Allow-Origin', 'https://your-frontend.com');
      res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
      res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
      res.header('Access-Control-Allow-Credentials', 'true');
      
      if (req.method === 'OPTIONS') {
        return res.sendStatus(200);
      }
      
      next();
    });
  3. 方案 3:JSONP(仅限 GET 请求)

    javascript
    // 前端
    function jsonpRequest(url, callbackName) {
      const script = document.createElement('script');
      script.src = `${url}?callback=${callbackName}`;
      document.body.appendChild(script);
    }
    
    // 全局回调函数
    window.handleResponse = (data) => {
      console.log('Received:', data);
    };
    
    // 发起请求
    jsonpRequest('https://api.example.com/data', 'handleResponse');

二面