我的第一个开源项目:SpringCloud电商前端Vue实战

作为一名刚接触企业级开发的程序员,我始终记得第一次提交代码时的紧张——凌晨两点的屏幕前,手指悬在回车键上迟迟不敢落下。而我的第一个开源项目——基于SpringCloud的电商管理系统前端,就藏着无数个这样的瞬间。从环境搭建时的手足无措,到功能跑通时的狂喜,每一行代码都刻着成长的痕迹。

项目缘起:为什么选择电商系统?

选择电商系统作为第一个开源项目,源于两个考量:一是电商场景涵盖了企业开发的核心需求(用户管理、商品管理、权限控制等),能全面锻炼技术能力;二是前端作为用户直接接触的层面,视觉与交互的成就感更直观,适合作为开源入门的切入点。

我们的目标是开发一个完整的电商管理后台,包含品牌管理、商品分类、SKU配置、用户权限等模块,前端采用Vue+Element UI实现,后端基于SpringCloud微服务架构。而我主要负责前端部分的开发,这也是我第一次系统性地使用Vue生态。

踩坑第一步:环境搭建的"连环坑"

环境搭建是开源项目的第一道门槛,而我差点栽在这一步。

最初按照文档安装Node.js时,误将安装路径改成了中文目录,导致后续cnpm始终报错。排查两小时后才发现,Node.js对中文路径的兼容性问题会导致依赖包解析失败。重新安装到默认路径(C:\Program Files\nodejs)后,才算解决了第一个问题。

# 全局安装cnpm时的正确命令(阿里镜像加速)
npm install cnpm -g --registry=https://registry.npmmirror.com

安装Vue CLI时,又遇到了版本兼容问题。最初安装的4.x版本与项目依赖冲突,报错"vue-cli-service: command not found"。查了无数issue后才知道,项目文档中推荐的3.10.0版本是经过验证的"无坑版",最终通过指定版本解决:

# 安装指定版本的Vue CLI
cnpm install -g @vue/cli@3.10.0

创建项目时,我犯了一个低级错误——给项目名加了大写字母(如"ShopAdmin"),导致Vue CLI直接报错"Project name can’t contain uppercase letters"。原来Vue项目名必须小写,这是为了兼容跨平台文件系统的命名规范。

核心功能开发:从"能跑"到"好用"

路由设计:单页应用的"导航图"

Vue的路由设计是前端架构的核心。最初我直接使用默认路由配置,导致所有页面都挤在一个窗口里。后来才理解,电商后台需要"主页面+子页面"的层级结构:左侧导航栏固定,右侧内容区动态加载组件。

通过嵌套路由实现这一效果:主路由加载布局组件(包含导航栏和内容区),子路由对应具体功能页面(如品牌管理、商品列表)。

// src/router/index.js
const routes = [
  {
    path: '/',
    name: 'Index',
    component: () => import('@/views'), // 主布局组件
    children: [
      {
        path: '/brand', // 品牌管理子路由
        name: 'BrandIndex',
        component: () => import('@/views/brand')
      },
      {
        path: '/category', // 分类管理子路由
        name: 'CategoryIndex',
        component: () => import('@/views/category')
      }
    ]
  }
]

异步请求:Axios封装的"血泪史"

最初直接在组件中写Axios请求,导致代码重复率极高。比如品牌列表的查询,每个组件都要写一遍axios.get(...),一旦接口地址变更,所有组件都要修改。

后来参考开源项目的最佳实践,封装了全局请求方法:

// src/plugins/axios.js
Vue.prototype.request = (url, method, params, callback) => {
  // 请求前显示加载动画
  const txLoading = Loading.service({ text: '拼命加载中' });
  
  _axios.request({
    url: url,
    method: method,
    // get请求用params,post用formData
    [method === 'get' ? 'params' : 'data']: params
  }).then(response => {
    callback(response.data);
  }).finally(() => {
    txLoading.close(); // 无论成功失败都关闭加载动画
  });
};

// 封装get和post简化调用
Vue.prototype.get = (url, params, callback) => {
  request(url, 'get', params, callback);
};
Vue.prototype.post = (url, params, callback) => {
  request(url, 'post', params, callback);
};

封装后,组件中调用变得简洁:

// 品牌列表查询
this.get('/pms-brand/list', { pageNo: 1 }, response => {
  this.tableData = response.records;
});

但新的问题来了:跨域。前端80端口访问后端8081端口时,浏览器报"CORS policy"错误。最终在后端配置跨域过滤器解决:

// 后端跨域配置
@Configuration
public class CorsConfig {
  @Bean
  public CorsFilter corsFilter() {
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    CorsConfiguration config = new CorsConfiguration();
    config.addAllowedOrigin("*"); // 允许所有域名
    config.addAllowedMethod("*"); // 允许所有请求方法
    source.registerCorsConfiguration("/**", config);
    return new CorsFilter(source);
  }
}

品牌管理模块:增删改查的"闭环实践"

品牌管理是第一个完整实现的模块,包含列表查询、新增、修改、删除功能,也是我第一次体会到"功能跑通"的成就感。

列表页使用Element UI的el-table组件,通过v-for循环渲染数据。但最初图片无法显示,排查后发现是图片路径拼接问题——后端返回的是图片名称,需要拼接MinIO服务器地址:

<!-- 品牌列表中的图片显示 -->
<el-table-column label="图片" width="260px">
  <template slot-scope="scope">
    <el-image 
      :src="img(scope.row.img)" 
      style="width: 100px; height: 80px"
      fit="contain">
    </el-image>
  </template>
</el-table-column>
// main.js中定义全局图片路径拼接方法
Vue.prototype.img = (url) => {
  return 'http://192.168.149.131:9000/images/' + url;
};

新增功能的弹窗表单中,我遇到了"表单验证不生效"的问题。原来Element UI的el-form需要通过prop属性与rules对应:

<el-form :model="form" :rules="rules" ref="txform">
  <el-form-item label="品牌名" prop="name">
    <el-input v-model="form.name"></el-input>
  </el-form-item>
</el-form>

<script>
export default {
  data() {
    return {
      rules: {
        name: [
          { required: true, message: '请输入品牌名', trigger: 'blur' }
        ]
      }
    }
  }
}
</script>

商品SKU配置:最复杂的"动态组合"

SKU(最小库存单位)是电商系统的核心难点——用户需要选择颜色、尺码等属性,系统自动生成组合并配置库存。

最初直接用el-checkbox-group实现,但属性值动态添加时出现了"勾选状态不更新"的问题。最终通过给每个属性值绑定唯一key,并在数据更新时强制刷新组件解决:

<el-form-item label="颜色">
  <el-checkbox-group v-model="item.value">
    <el-checkbox 
      v-for="color in item.inputList.split(',')" 
      :key="color" 
      :label="color">
    </el-checkbox>
  </el-checkbox-group>
</el-form-item>

为了实现"动态添加属性值"(如新增一种颜色),我在组件中添加了输入框和"添加"按钮,点击后将新值追加到inputList

addSku(item) {
  if (item.addvalue) {
    item.inputList += ',' + item.addvalue; // 追加新值
    item.addvalue = ''; // 清空输入框
  }
}

上线与开源:从"本地跑通"到"公开可用"

项目开发到第3个月,终于完成了核心功能。但上线前的部署又给了我"致命一击"——本地运行正常的代码,打包后图片全部失效。

排查发现,Vue打包时会将静态资源路径转为相对路径,而图片路径是硬编码的绝对路径。通过修改vue.config.js中的publicPath配置解决:

// vue.config.js
module.exports = {
  publicPath: './' // 打包时使用相对路径
};

最终,我在GitCode上创建了仓库,提交了第一版代码。看着git log里的提交记录,从"fix: 跨域问题"到"feat: 完成SKU模块",突然意识到:开源的意义不仅是分享代码,更是记录成长的轨迹。

结语:开源给我的三个礼物

这个项目让我收获了远超技术本身的成长:

  1. 解决问题的耐心:从环境搭建到跨域调试,每个问题都是"百度3小时,改码5分钟",但正是这些经历让我学会了拆解问题。
  2. 代码的洁癖:为了让开源代码可读,我强迫自己写注释、拆组件,代码风格逐渐规范。
  3. 分享的勇气:最初怕代码写得烂被嘲笑,但看到有人Star并提Issue时,才明白开源的本质是互助。

如今再看这个项目,代码里满是"新手痕迹":冗余的判断、未优化的渲染。但我舍不得重构——这些不完美,才是最真实的成长印记。

如果你也是开源新手,不妨从一个小模块开始。毕竟,每个大神都曾是"提交代码会手抖"的萌新。

SpringCloud电商项目开发完整流程

(项目地址:https://gitee.com/xiao-yezi/spring-cloud-project,欢迎Star和PR!)

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐