Skip to content

🎯 最佳实践

l-pc-front 项目开发经验总结与技巧


📋 目录


🎨 编码规范

1. JavaScript/ES6+ 最佳实践

javascript
// ✅ 推荐:使用解构赋值
const fetchData = async () => {
  const { data, page, ok } = await $selectList('/api/users');
  if (ok) {
    return { data, page };
  }
};

// ✅ 推荐:使用可选链和空值合并
const userName = user?.profile?.name ?? '匿名';
const pageSize = options?.pagination?.rownumber ?? 20;

// ✅ 推荐:使用模板字符串
const message = `用户 ${name} 的年龄是 ${age} 岁`;

// ❌ 避免:回调地狱
function oldWay(callback) {
  getData(function(res) {
    if (res.ok) {
      process(res.data, function(result) {
        callback(result);
      });
    }
  });
}

2. Vue 组件最佳实践

vue
<template>
  <!-- ✅ 推荐:使用有意义的类名 -->
  <div class="user-management">
    <!-- ✅ 推荐:明确的组件接口 -->
    <data-table
      :data="tableData"
      :loading="loading"
      @row-click="handleRowClick"
    />
  </div>
</template>

<script>
export default {
  // ✅ 推荐:明确的组件名
  name: 'UserManagement',
  
  // ✅ 推荐:Props 验证
  props: {
    userId: {
      type: String,
      required: true,
      default: ''
    }
  },
  
  // ✅ 推荐:响应式数据初始化
  data() {
    return {
      tableData: [],
      loading: false,
      pagination: {
        page: 1,
        rownumber: 20,
        total: 0
      }
    };
  },
  
  // ✅ 推荐:计算属性缓存
  computed: {
    hasData() {
      return this.tableData.length > 0;
    }
  },
  
  // ✅ 推荐:方法命名清晰
  methods: {
    async fetchUserData() {
      this.loading = true;
      try {
        const { data, page, ok } = await $selectList(
          '/api/users',
          { page: this.pagination }
        );
        if (ok) {
          this.tableData = data;
          this.pagination.total = page.total;
        }
      } catch (error) {
        this.handleError(error);
      } finally {
        this.loading = false;
      }
    },
    
    handleError(error) {
      console.error('获取用户数据失败:', error);
      this.$message.error('数据加载失败,请重试');
    }
  },
  
  // ✅ 推荐:生命周期钩子按顺序
  created() {
    this.fetchUserData();
  },
  
  // ✅ 推荐:清理资源
  beforeDestroy() {
    this.tableData = [];
  }
};
</script>

<style scoped>
/* ✅ 推荐:使用 scoped 样式 */
.user-management {
  padding: 20px;
}
</style>

3. 样式最佳实践

scss
// ✅ 推荐:使用 SCSS 变量
$primary-color: #409eff;
$spacing-unit: 16px;
$border-radius: 4px;

// ✅ 推荐:使用 Mixin
@mixin flex-center {
  display: flex;
  align-items: center;
  justify-content: center;
}

// ✅ 推荐:嵌套层次不超过3层
.container {
  padding: $spacing-unit;
  
  .header {
    background: $primary-color;
    
    .title {
      font-size: 20px;
    }
  }
  
  // ❌ 避免:过深嵌套
  // .container .header .nav .menu .item { ... }
}

// ✅ 推荐:使用 CSS 变量实现主题
:root {
  --primary-color: #409eff;
  --bg-color: #f5f7fa;
}

.dark-mode {
  --primary-color: #66b1ff;
  --bg-color: #1a1a1a;
}

🧩 组件开发

1. 组件设计原则

javascript
// ✅ 推荐:单一职责原则
// UserTable.vue - 专注于表格展示
export default {
  name: 'UserTable',
  props: ['data', 'loading'],
  emits: ['row-click', 'sort-change']
}

// ✅ 推荐:可复用组件设计
// DataSelector.vue - 通用选择器
export default {
  name: 'DataSelector',
  props: {
    service: { type: String, required: true },
    value: { type: [String, Array], default: '' },
    multiple: { type: Boolean, default: false },
    filter: { type: Object, default: () => ({}) }
  }
}

2. 组件通信最佳实践

javascript
// ✅ 推荐:使用 Props 和 Emit
// 父组件
<template>
  <user-selector
    v-model="selectedUser"
    :filter="{ status: 'active' }"
    @change="handleUserChange"
  />
</template>

// 子组件 (UserSelector.vue)
export default {
  props: ['value', 'filter'],
  emits: ['update:value', 'change'],
  
  methods: {
    onSelect(user) {
      this.$emit('update:value', user.id);
      this.$emit('change', user);
    }
  }
}

// ✅ 推荐:复杂状态使用 Vuex
// store/modules/user.js
export default {
  namespaced: true,
  state: () => ({
    list: [],
    current: null
  }),
  mutations: {
    SET_LIST(state, list) {
      state.list = list;
    }
  },
  actions: {
    async fetchList({ commit }) {
      const { data, ok } = await $selectList('/api/users');
      if (ok) commit('SET_LIST', data);
    }
  }
}

3. 组件性能优化

vue
<script>
export default {
  // ✅ 推荐:使用 computed 缓存复杂计算
  computed: {
    filteredData() {
      return this.tableData.filter(item => {
        return item.status === 'active' && 
               item.name.includes(this.searchKeyword);
      });
    }
  },
  
  // ✅ 推荐:使用 watch 处理异步操作
  watch: {
    'pagination.page': {
      handler: 'fetchData',
      immediate: false
    }
  },
  
  // ✅ 推荐:使用防抖处理频繁操作
  methods: {
    search: debounce(function(keyword) {
      this.searchKeyword = keyword;
      this.fetchData();
    }, 300)
  }
}
</script>

🏪 状态管理

1. Vuex 模块化

javascript
// ✅ 推荐:按功能模块划分
// store/modules/index.js
import user from './user';
import order from './order';
import permission from './permission';

export default {
  user,
  order,
  permission
};

// ✅ 推荐:使用命名空间
export default {
  namespaced: true,
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

// ✅ 推荐:使用 Getter 派生状态
getters: {
  activeUsers: state => state.list.filter(u => u.status === 'active'),
  getUserById: state => id => state.list.find(u => u.id === id)
}

2. 状态更新最佳实践

javascript
// ✅ 正确:使用 Mutation 更新状态
mutations: {
  UPDATE_USER(state, payload) {
    // 使用扩展运算符保持响应式
    state.user = { ...state.user, ...payload };
  },
  
  ADD_ITEM(state, item) {
    // 数组操作保持响应式
    state.list.push(item);
  }
}

// ❌ 错误:直接修改状态
mutations: {
  BAD_UPDATE(state, payload) {
    state.user.name = payload.name;  // 可能丢失响应式
  }
}

// ✅ 推荐:使用 Action 处理异步
actions: {
  async updateUser({ commit }, payload) {
    const res = await $http.post('/api/user/update', payload);
    if (res.data.state === 'SUCCESS') {
      commit('UPDATE_USER', payload);
      return { ok: true };
    }
    return { ok: false, msg: res.data.resultMessage };
  }
}

3. 路由栈管理最佳实践

javascript
// ✅ 推荐:使用路由栈实现智能返回
export default {
  methods: {
    async handleSave() {
      await this.saveData();
      // 智能返回:优先使用栈内路由
      this.$routeStack.smartGoBack();
    },
    
    handleCancel() {
      // 直接返回上一页
      this.$routeStack.goBack();
    },
    
    handleViewDetail(id) {
      // 前往详情页(自动加入栈)
      this.$router.push(`/detail/${id}`);
    }
  }
}

🛣️ 路由管理

1. 路由配置最佳实践

javascript
// ✅ 推荐:模块化路由配置
// router/modules/user.js
export default [
  {
    path: '/user/list',
    name: 'UserList',
    component: () => import('@/pages/user/list.vue'),
    meta: { requiresAuth: true, title: '用户列表' }
  },
  {
    path: '/user/detail/:id',
    name: 'UserDetail',
    component: () => import('@/pages/user/detail.vue'),
    meta: { requiresAuth: true }
  }
];

// router/index.js
import userRoutes from './modules/user';
import orderRoutes from './modules/order';

const routes = [
  ...userRoutes,
  ...orderRoutes,
  // ... 其他路由
];

2. 路由守卫最佳实践

javascript
// ✅ 推荐:统一的路由守卫逻辑
router.beforeEach((to, from, next) => {
  // 1. 检查路由栈启用状态
  const store = require('@/store').default;
  
  if (store.getters['routeStack/isEnabled']) {
    // 避免重复路由
    const stack = store.getters['routeStack/routeStack'];
    const lastRoute = stack[stack.length - 1];
    
    if (lastRoute && lastRoute.fullPath === to.fullPath) {
      next();  // 已在栈顶,跳过
      return;
    }
    
    // 记录路由到栈
    store.dispatch('routeStack/pushRoute', to);
  }
  
  // 2. 检查权限
  if (to.meta.requiresAuth && !checkAuth()) {
    next('/login');
    return;
  }
  
  next();
});

🔌 API调用

1. 请求封装最佳实践

javascript
// ✅ 推荐:使用封装的快捷方法
// 好的写法
const fetchData = async () => {
  const { data, page, ok } = await $selectList(
    '/business/select/srvuser_list_select',
    {
      condition: [{ colName: 'status', ruleType: 'eq', value: 'active' }],
      page: { pageNo: 1, rownumber: 20 }
    }
  );
  
  if (ok) {
    this.tableData = data;
    this.pagination.total = page.total;
  }
};

// ❌ 避免:直接使用 $http,重复编写请求格式
const badFetch = async () => {
  const res = await $http.post('/business/select/srvuser_list_select', {
    serviceName: 'srvuser_list_select',
    colNames: ['*'],
    condition: [{ colName: 'status', ruleType: 'eq', value: 'active' }],
    page: { pageNo: 1, rownumber: 20 }
  });
  
  if (res.data.state === 'SUCCESS') {
    this.tableData = res.data.data;
    this.pagination.total = res.data.page.total;
  }
};

2. 错误处理最佳实践

javascript
// ✅ 推荐:统一的错误处理
export default {
  methods: {
    async fetchData() {
      try {
        this.loading = true;
        
        const result = await $selectList('/api/data', {
          page: this.pagination
        });
        
        if (result.ok) {
          this.data = result.data;
        } else {
          this.$message.error(result.msg || '获取数据失败');
        }
      } catch (error) {
        console.error('API调用失败:', error);
        this.$message.error('网络错误,请稍后重试');
        
        // 上报错误
        this.reportError(error);
      } finally {
        this.loading = false;
      }
    },
    
    reportError(error) {
      if (process.env.NODE_ENV === 'production') {
        fetch('/api/error-collect', {
          method: 'POST',
          body: JSON.stringify({
            type: 'api-error',
            message: error.message,
            stack: error.stack,
            timestamp: Date.now()
          })
        }).catch(() => {});
      }
    }
  }
}

3. 批量操作最佳实践

javascript
// ✅ 推荐:使用 Promise.all 处理批量请求
const batchUpdate = async (ids, updateData) => {
  const requests = ids.map(id => 
    $http.post('/api/user/update', {
      serviceName: 'srvuser_list_update',
      condition: [{ colName: 'id', ruleType: 'eq', value: id }],
      data: [updateData]
    })
  );
  
  const results = await Promise.allSettled(requests);
  
  const successCount = results.filter(r => 
    r.status === 'fulfilled' && r.value.data.state === 'SUCCESS'
  ).length;
  
  return {
    success: successCount,
    failed: ids.length - successCount
  };
};

⚡ 性能优化

1. 构建优化

javascript
// vue.config.js
module.exports = {
  // ✅ 推荐:代码分割
  configureWebpack: {
    optimization: {
      splitChunks: {
        cacheGroups: {
          vendor: {
            test: /[\\/]node_modules[\\/]/,
            name: 'vendors',
            chunks: 'all',
            priority: 10
          },
          elementUI: {
            test: /[\\/]node_modules[\\/]element-ui[\\/]/,
            name: 'element-ui',
            chunks: 'all',
            priority: 20
          }
        }
      }
    }
  },
  
  // ✅ 推荐:Gzip压缩
  chainWebpack: config => {
    if (process.env.NODE_ENV === 'production') {
      config.plugin('compression-webpack-plugin')
        .use(require('compression-webpack-plugin'), [{
          algorithm: 'gzip',
          test: /\.(js|css)$/,
          threshold: 8192
        }]);
    }
  }
}

2. 运行时优化

javascript
// ✅ 推荐:路由懒加载
const routes = [
  {
    path: '/heavy',
    component: () => import(/* webpackChunkName: "heavy" */ './Heavy.vue')
  },
  {
    path: '/admin',
    component: () => import(/* webpackChunkName: "admin" */ './Admin.vue')
  }
];

// ✅ 推荐:组件按需加载
export default {
  components: {
    HeavyChart: () => import('./HeavyChart.vue'),
    RichText: () => import('./RichText.vue')
  }
};

// ✅ 推荐:大数据列表使用虚拟滚动
import Vue from 'vue';
import { VirtualList } from 'vue-virtual-scroll-list';

export default {
  components: {
    VirtualList
  },
  data() {
    return {
      listData: []  // 可能有上万条数据
    };
  }
};

3. 内存管理

javascript
// ✅ 推荐:组件卸载时清理资源
export default {
  data() {
    return {
      timer: null,
      ws: null,
      chart: null
    };
  },
  
  methods: {
    initChart() {
      this.chart = echarts.init(this.$refs.chart);
    }
  },
  
  beforeDestroy() {
    // 清理定时器
    if (this.timer) {
      clearInterval(this.timer);
    }
    
    // 关闭WebSocket
    if (this.ws) {
      this.ws.close();
    }
    
    // 销毁图表实例
    if (this.chart) {
      this.chart.dispose();
      this.chart = null;
    }
    
    // 清理数据引用
    this.listData = [];
  }
}

🚨 错误处理

1. 全局错误处理

javascript
// main.js
// ✅ 推荐:Vue错误边界
Vue.config.errorHandler = function (err, vm, info) {
  console.error('Vue错误:', err);
  console.error('组件:', vm.$options.name);
  console.error('信息:', info);
  
  if (process.env.NODE_ENV === 'production') {
    // 上报错误
    fetch('/api/error-collect', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        type: 'vue-error',
        message: err.message,
        stack: err.stack,
        component: vm.$options.name,
        info: info,
        timestamp: new Date().toISOString()
      })
    }).catch(() => {});
  }
};

// ✅ 推荐:Promise错误处理
window.addEventListener('unhandledrejection', (event) => {
  console.error('未处理的Promise拒绝:', event.reason);
  
  // 统一错误上报
  if (process.env.NODE_ENV === 'production') {
    fetch('/api/error-collect', {
      method: 'POST',
      body: JSON.stringify({
        type: 'promise-rejection',
        message: event.reason?.message || String(event.reason),
        timestamp: Date.now()
      })
    });
  }
});

2. 网络错误处理

javascript
// ✅ 推荐:HTTP请求错误处理
instance.interceptors.response.use(
  response => response,
  error => {
    if (error.response) {
      const { status, data } = error.response;
      
      switch (status) {
        case 401:
          // 认证失败
          handleAuthFailure();
          break;
        case 403:
          // 权限不足
          Message.warning('权限不足,无法访问');
          break;
        case 500:
          // 服务器错误
          Message.error('服务器内部错误');
          break;
        default:
          Message.error(`请求失败: ${data?.resultMessage || '未知错误'}`);
      }
    } else if (error.code === 'ECONNABORTED') {
      Message.error('请求超时,请稍后重试');
    } else {
      Message.error('网络错误,请检查连接');
    }
    
    return Promise.reject(error);
  }
);

3. 数据验证

javascript
// ✅ 推荐:输入验证
const validateUser = (user) => {
  const errors = [];
  
  if (!user.name || user.name.trim().length < 2) {
    errors.push('姓名至少需要2个字符');
  }
  
  if (user.age && (user.age < 1 || user.age > 150)) {
    errors.push('年龄必须在1-150之间');
  }
  
  if (user.email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(user.email)) {
    errors.push('邮箱格式不正确');
  }
  
  return {
    valid: errors.length === 0,
    errors
  };
};

// 使用示例
const result = validateUser(formData);
if (!result.valid) {
  this.$message.error(result.errors[0]);
  return;
}

🔍 调试技巧

1. 浏览器调试

javascript
// ✅ 推荐:使用 Vue DevTools
// 在浏览器控制台调试
console.log('当前路由:', window.app.$route);
console.log('Store状态:', window.app.$store.state);
console.log('路由栈:', window.app.$routeStack.getStack());

// ✅ 推荐:状态变化监听
this.$store.subscribe((mutation, state) => {
  console.log('Mutation:', mutation.type, mutation.payload);
});

// ✅ 推荐:网络请求监控
// 在 Network 面板查看请求详情
// 右键请求 -> Break on -> XMLHttpRequest/fetch

2. 性能分析

javascript
// ✅ 推荐:测量组件渲染时间
export default {
  mounted() {
    this.$nextTick(() => {
      const duration = performance.now() - performance.timing.navigationStart;
      console.log(`组件渲染时间: ${duration.toFixed(2)}ms`);
      
      if (duration > 1000) {
        console.warn('组件渲染过慢,考虑优化');
      }
    });
  }
}

// ✅ 推荐:监控API响应时间
instance.interceptors.response.use(response => {
  const duration = Date.now() - response.config.metadata.startTime;
  
  if (duration > 1000) {
    console.warn(`慢API: ${response.config.url} (${duration}ms)`);
  }
  
  return response;
});

3. 数据调试

javascript
// ✅ 推荐:使用 Vue.set 解决响应式问题
// ❌ 错误
this.list.push(newItem);  // 可能不触发更新

// ✅ 正确
this.$set(this.list, this.list.length, newItem);
// 或
this.list = [...this.list, newItem];

// ✅ 推荐:深度监听数据变化
watch: {
  'formData': {
    handler: 'validateForm',
    deep: true  // 深度监听
  }
}

🛠️ 项目维护

1. 代码审查清单

markdown
### 代码审查清单

- [ ] **功能完整性**
  - [ ] 需求是否全部实现?
  - [ ] 边界情况是否考虑?

- [ ] **代码质量**
  - [ ] 命名是否清晰?
  - [ ] 注释是否充分?
  - [ ] 是否有重复代码?

- [ ] **性能优化**
  - [ ] 是否有性能瓶颈?
  - [ ] 是否使用懒加载?
  - [ ] 是否有内存泄漏?

- [ ] **安全性**
  - [ ] 输入是否验证?
  - [ ] 敏感信息是否泄露?
  - [ ] 权限是否正确?

- [ ] **测试覆盖**
  - [ ] 单元测试是否通过?
  - [ ] E2E测试是否通过?

- [ ] **文档更新**
  - [ ] API文档是否更新?
  - [ ] 组件文档是否更新?
  - [ ] 部署说明是否更新?

2. Git 工作流

bash
# ✅ 推荐:功能分支工作流
git checkout main
git pull origin main
git checkout -b feature/user-auth

# 开发完成后
git add .
git commit -m "feat: 添加用户认证功能

- 实现JWT登录
- 添加权限验证
- 支持记住登录"

git push origin feature/user-auth
# 创建 PR,等待 Code Review

# ✅ 推荐:提交信息规范
# 类型(范围): 简短描述
# 空一行
# 详细描述(可选)
# 空一行
# 页脚(可选)

# 示例
feat(auth): 添加OAuth2登录支持

添加了Google和GitHub第三方登录
- 集成OAuth2流程
- 添加登录按钮组件
- 支持绑定已有账号

Closes #123

3. 依赖管理

bash
# ✅ 推荐:定期检查依赖
yarn outdated          # 查看过时依赖
yarn upgrade-interactive --latest  # 交互式更新

# ✅ 推荐:安全审计
yarn audit             # 检查安全漏洞
yarn audit --level high  # 只看高危漏洞

# ✅ 推荐:清理无用依赖
yarn install --production  # 只安装dependencies

🎓 经验总结

1. 开发效率提升

  • 善用代码片段 - 创建常用代码模板
  • 使用 IDE 插件 - Vetur, ESLint 等
  • 保持代码整洁 - 定期重构
  • 写好注释 - 特别是复杂逻辑

2. 团队协作

  • 统一代码风格 - 使用 Prettier
  • 定期 Code Review - 互相学习
  • 文档先行 - 先写文档再开发
  • 及时沟通 - 遇到问题及时讨论

3. 持续改进

  • 收集反馈 - 用户和团队反馈
  • 监控指标 - 性能、错误率等
  • 学习新技术 - 保持技术更新
  • 总结经验 - 写博客或文档

文档维护: l-pc-front 开发组
最后更新: 2025-12-19

最近更新