从零开始构建MVC架构的前端项目实战经验分享

菲菲酱~ 框架 阅读 2,288
赞 34 收藏
二维码
手机扫码查看
反馈

我的写法,亲测靠谱

先说说我在用MVC时的一些心得吧。我一般喜欢把控制器(Controller)弄得尽可能轻量,主要负责接收请求和返回响应,具体业务逻辑交给服务层去处理。这样做的好处是代码更容易测试,也方便后期维护。

从零开始构建MVC架构的前端项目实战经验分享

举个例子,这是我常用的控制器写法:

class UserController {
  constructor(userService) {
    this.userService = userService;
  }

  async getUser(req, res) {
    try {
      const userId = req.params.id;
      const user = await this.userService.fetchUser(userId);
      return res.json(user);
    } catch (error) {
      console.error('获取用户信息失败:', error);
      return res.status(500).json({ error: '服务器错误' });
    }
  }
}

这里我把用户相关的业务逻辑都抽到了userService里,控制器只负责调用和返回结果。这样做有几个好处:首先,单元测试更容易写了;其次,如果后面要改业务逻辑,不用动控制器的代码。

这几种错误写法,别再踩坑了

说真的,在用MVC的时候最容易犯的错误就是把所有逻辑都塞到控制器里。我就见过有人这么写的:

class BadController {
  async handleRequest(req, res) {
    // 数据库操作直接写在这里
    const dbResult = await db.query(SELECT * FROM users WHERE id=${req.params.id});
    
    // 业务逻辑也写在这里
    if (dbResult.length > 0) {
      const processedData = someComplexLogic(dbResult);
      
      // 还有视图渲染
      return res.render('template', { data: processedData });
    }
    return res.status(404).send('Not found');
  }
}

这种写法简直就是灾难。我之前接手过一个项目,控制器里几千行代码,各种业务逻辑、数据库操作、格式转换全都搅在一起。改个需求得花好几天理清逻辑,太痛苦了。

实际项目中的坑

在真实项目里,我发现很多人对Model的理解也不太对。有次看到有人这么写Model:

class UserModel {
  constructor(data) {
    this.data = data;
  }

  saveToDatabase() {
    // 直接在这里写数据库操作
    return db.query('INSERT INTO users SET ?', this.data);
  }
}

看着好像没啥问题,但实际上这种写法很危险。我建议把数据访问的逻辑单独抽出来,Model只负责定义数据结构和基本的验证规则:

class User {
  constructor({id, name, email}) {
    this.id = id;
    this.name = name;
    this.email = email;
  }

  validate() {
    if (!this.name || !this.email) {
      throw new Error('缺少必要字段');
    }
  }
}

class UserRepository {
  async save(user) {
    user.validate();
    return db.query('INSERT INTO users SET ?', user);
  }
}
`>
<p>这样分层更清晰,而且Repository可以针对不同的数据源做适配,比如换成MongoDB或者REST API都比较容易。</p>

<h2>关于View层的一些思考</h2>
&lt;p&gt;说到View层,我特别想提醒一下:千万别在模板里写太多逻辑。我遇到过一个项目,ejs模板里写满了if-else,还有复杂的循环嵌套:&lt;/p&gt;</code></pre>html
<% if (user.role === 'admin') { %>
  <% for (let item of data) { %>
    <% if (item.status === 'active') { %>
      <div class="item"><%= item.name %></div>
    <% } else { %>
      <div class="disabled"><%= item.name %></div>
    <% } %>
  <% } %>
<% } else { %>
  <p>无权限查看</p>
<% } %>
<pre class="pure-highlightjs line-numbers language-none"><code class="no-highlight language-none">&lt;p&gt;这种写法看起来就头疼。后来我改成这样:&lt;/p&gt;</code></pre>javascript
// 在控制器里预处理数据
const viewData = data.map(item => ({
  ...item,
  className: item.status === 'active' ? 'item' : 'disabled'
}));
`>
<% if (user.role === 'admin') { %>
  <% viewData.forEach(item => { %>
    <div class="<%= item.className %>"><%= item.name %></div>
  <% }) %>
<% } else { %>
  <p>无权限查看</p>
<% } %>

这样模板就清爽多了,逻辑也更容易维护。

一些小技巧

  • 我习惯在项目根目录放一个config.js文件,专门存放各种配置项,比如API地址:https://jztheme.com/api,这样方便统一管理
  • 用依赖注入的方式来组织各个模块的关系,这样测试起来会轻松很多
  • 记得给每个模块写单元测试,特别是服务层的业务逻辑,这个真的能帮你省下不少调试时间

以上是我总结的最佳实践,有更好的方案欢迎评论区交流。说实话,MVC虽然老套,但确实是个经得起考验的架构模式,只要用得好,开发效率还是很可观的。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论