Vue.js:在Laravel中实现多页面应用(MPA)的最佳方式

24

我已经寻找了相当长的一段时间,但没有找到什么方便的东西。

在Laravel中实现Vue MPA架构的最佳方法和实践是什么。

已经搜索了很久,但没有找到任何清晰的想法。您的答案将会非常有帮助,请尽量简要回答。

回答以下问题也将有所帮助:

  • 仅使用laravel作为数据API,并将Vue与laravel分开是否是个好主意?
  • 实现SPA和MPA混合的最佳方法。
2个回答

32

我已经尝试过的一些选项:

使用 Laravel 渲染“主视图”+连接 vue.js 应用程序。

基本上,Laravel 将呈现 Vue 应用程序,并且每个请求都经过 API。

  1. 易于设置
  2. 验证用户更容易(您可以使用 Laravel 会话管理器进行验证 - 不需要构建/使用令牌或其他内容。"无需担心应用程序状态")
  3. 如果将来选择分离 SPA 应用程序,则易于与 Laravel "断开连接"。

只将 laravel (或 lumen) 用作 API,在另一个服务器上渲染单页应用程序。

这可能需要更多时间,因为您需要设置额外的服务器、准备跨域等。

  1. 也很容易设置,但比选项 #1 花费更多时间
  2. 您需要创建一些内容来验证用户/状态管理等。
  3. 如果将来决定使用 "只有一个应用程序",则很容易放置到 Laravel 中。
  4. 如果您有前端团队,则可以更轻松地维护/扩展,他们不需要关心 Laravel - 对于您的 "Laravel 团队",他们 "不需要担心" 前端

Laravel + Vue = "一个应用程序"

您可以使用 Laravel 渲染所有视图 + vuejs 用于页面中的组件/元素。

  1. 易于设置。您拥有 Laravel 和 Vue.js,它们已准备好一起使用。 https://laravel.com/docs/5.5/frontend#writing-vue-components
  2. 不太容易分离。在这种情况下,您需要为 Vue.js 创建相同的视图。这可能需要时间。
  • 这是“传统的Web开发”(在我的看法中)。如果我今天要开始一个这样的项目,我不会创建所有页面在Vue.js + Laravel(控制器+新路由)中来呈现此视图。如果您这样做(再次 - 我的意见),这只是多余的工作。如果您担心SEO,可以有“回退”/额外选项。
  • --

    所有选项都可测试和扩展。

    这也取决于您如何开始(我应该担心将来如何分离应用程序吗?Laravel + Vue是否长期使用?),您的团队将如何工作(前端团队真的需要设置Laravel还是他们只需要关注前端代码?)等等。

    如果我没有回答您的问题,请留下评论。


    1
    很棒的答案,写得很好。但是如何再次实现MPA呢?如果您看第一个答案,那个回答有点解释了。请稍微解释一下技术方面。谢谢。 - Gammer
    1
    您能解释一下在我的使用情况下第一种选项和第三种选项哪个更好吗?我正在重建SaaS遗留应用程序。当前系统使用cakephp和jQuery(依赖jQuery较重),我很擅长laravel,但只是对Vue.js有些了解。 - Parth Patel

    14

    你还没有找到明确的答案,因为除了“符合你的理解和项目需求”的内容之外,真的没有什么可谈的了。如果你发现自己非常不确定,可以随意尝试做任何有意义的事情,然后在获得更多经验后重新调整结构。

    此外,阅读关于系统架构的书籍会很有帮助。


    这是一个关于使用Laravel作为数据API并将Vue与Laravel分离的好主意吗?我理解你是指单页应用程序(SPA)?老实说,如果您的应用程序很小,那么我认为这是可以的。但是对于更大的应用程序,如果它们是SPA,则通常难以维护。如果最终使用Laravel作为API端点,请使用其简化版Lumen,因为它没有Blade和其他一些东西。Lumen是简化版,用作API端点。阅读:https://medium.com/@NeotericEU/single-page-application-vs-multiple-page-application-2591588efe58

    实现SPA和MPA混合的最佳方法。

    根据我的经验,我曾尝试构建4个以上的混合项目,以下是我发现的最优结构:

    我的示例将涉及一个保存“帖子”的应用程序。

    1. 使用仓储设计模式。

    这将在维护代码和保持DRY(不要重复自己)概念方面为您节省很多麻烦。

    • 创建一个目录App\Repositories\

    创建一个新类PostsRepository。这将是与数据库通信并包含大部分逻辑的类。

    • 创建目录App\Services\

    创建一个新类PostsService。这将在其构造函数中具有PostsRepository

    服务类将处理用户的输入,无论是来自Web控制器还是API控制器。

    <?php
    
    namespace App\Service;
    
    use App\Repositories\PostsRepository;
    
    class PostsService;
    {
        protected $repository;
    
        public function __construct(PostsRepository $repository)
        {
            $this->repository = $repository;
        }
    }
    
    • 将Web和API控制器分开。

    对于Web控制器,您可以像往常一样创建控制器:

    php artisan make:controller PostsController

    对于API控制器,您需要将控制器创建在Api文件夹中。

    php artisan make:controller Api\PostsController

    最后一个命令将创建目录App\Http\Controllers\Api并将控制器放置其中。

    回顾

    现在我们有了不同的控制器,以返回适合起点(Web / API)的结果。

    我们有服务,这些(Web / API)控制器都将其数据发送以进行验证(并由存储库执行操作)。

    例如:

    <?php
    
    namespace App\Http\Controllers;
    
    use App\Service\PostsService;
    
    class PostsController extends Controller
    {
      protected $service;
    
      public function __construct(PostsService $service)
      {
          $this->service = $service;
      }
    
      public function index()
      {
         /**
         * Instead of returning a Blade view and
         * sending the data to it like:
         *
         *          $posts = $this->service->all();
         *          return views('posts.index', compact('posts'));
         *
         * We go ahead and just return the boilerplate of 
         * our posts page (Blade).
         */
         return view('posts.index');
      }
    }
    

    ...

    <?php
    
    namespace App\Http\Controllers\Api;
    
    use App\Service\PostsService;
    
    class PostsController extends Controller
    {
      protected $service;
    
      public function __construct(PostsService $service)
      {
          $this->service = $service;
      }
    
      /**
      * Returns all posts.
      *
      * A vue component calls this action via a route.
      */
      public function index()
      {
         $posts = $this->service->all();
    
         return $posts;
      }
    
      /**
      * Notice we don't have a store() in our
      * Web controller.
      */
      public function store()
      {
         return $this->service->store();
      }
    }
    

    ...

    <?php
    
    namespace App\Services;
    
    use App\Repositories\PostsRepository;
    
    class PostsService extends Controller
    {
      protected $repository;
    
      public function __construct(PostsRepository $repository)
      {
          $this->repository = $repository;
      }
    
      public function all()
      {
         $posts = $this->repository->all();
    
         return $posts;
      }
    
      public function store()
      {
         $request = request()->except('_token');
    
         $this->validation($request)->validate();
    
         return $this->repository->store($request);
      }
    
      public function validation(array $data)
      {
          return Validator::make($data, [
              'content' => 'required|string|max:255',
              //
          ]);
      }
    }
    

    在我们的PostsRepository中,实际上调用保存数据的方法。例如:Post::insert($request);

    2. 专门为API分组

    Route::prefix('api/v1')->middleware('auth')->group(function() {
    
        Route::post('posts/store', 'Api\PostsController@store')->name('api.posts.store');
    
    });
    

    给API路由加上->name() 在进行phpunit测试时非常有帮助。

    3. Blade视图

    这些应该是简化的简单视图。

    views/posts/index.blade.php:

    @extends('layouts.app', ['title' => trans('words.posts')])
    
    @section('content')
      <!-- Your usual grid columns and stuff -->
      <div class="columns">
         <div class="column is-6">
             <!-- This comp. can have a modal included. -->
             <new-post-button></new-post-button>
         <div class="column is-6">
              <posts-index-page></posts-index-page>
         </div>
      </div>
    @endsection
    

    4. Vue结构。

    https://github.com/pablohpsilva/vuejs-component-style-guide

    那么这些Vue组件可能会存储在resources/assets/js/components/posts/中,其中/posts/内部会有文件夹,例如IndexPageCreateModalEditModal,每个文件夹都有其自己的.vueREADME.md
    我将在index.blade.php中使用<posts-index-page>,并在需要时放置<post-create-modal><edit-post-modal>
    所有Vue组件将使用我们在路由文件中指定的API端点。

    README.md 是用来做什么的? - common sense
    @commonsense README.md 的作用是编写自己的 README.md。你需要指定组件的目的、它所接受的 props 及其类型(如果有的话)、如何使用它等等。基本上是为了维护自己的代码而编写 README.md。 - Shafiq al-Shaar
    @Spacemudd 好多了。 - Gammer
    编辑/更新功能怎么样?如果你没有使用Modal,你如何在<edit-post-page>组件中传递post_id参数?你是否会接受从App\Http\Controllers\PostController@edit传递的:post_id属性到组件中? - Chris Landeza
    如果<edit-post-page>比较简单,那么是的,我会传递一个:post_id属性。但是,在更复杂的情况下,如果多个组件都需要:post_id,我更喜欢使用Vuex来管理我的状态,这样我可以在任何组件中访问我的状态的post_id而无需传递props。 - Shafiq al-Shaar
    这应该在官方文档中! - user3485442

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接