前後端分離探索——MVC 專案升級的一個過渡方案

cnguu發表於2019-11-12

前言

專案環境

  • 後端框架:Phalcon
  • 前端框架:Bootstrap + jQuery

什麼是前後端分離?

傳統專案大多數是 MVC 架構,直接使用 PHP 等後端語言渲染 HTML 模板,返回給瀏覽器

現在,前後端分離不需要後端渲染模板,而是交由瀏覽器 Javascript 渲染,後端只需要返回前端渲染所需要的資料即可

前後端分離的本質:

  • 路由分離
  • 模板分離

前後端偽分離?

傳統 MVC 專案直接升級到前後端分離需要大量的時間與人力,在業務多變的階段並不適合,所以便有了本文的過渡方案探索

  1. 路由先不分離,仍然採用 PHP 提供的路由
  2. 模板部分分離,在原 PHP 模板中,引入 Vue 編譯後的模板,為此需要約定

示例

新建控制器 TestController.php

<?php

namespace App\Controller;

class TestController
{
    public function indexAction() { }
}

新建模板 test/index.volt

<div id="app">
    <!-- 約定 一個頁面對應一個 Vue 元件 -->
    <index-view></index-view>
</div>
<!-- 約定 一個頁面對應一個前端控制器 -->
<script src="/mix/dist/js/test/index.js?v={{ time() }}"></script>

暫時找不到很好解決快取的方案,所以統一不快取

新建前端控制器 public/mix/resources/js/test/index.js

import Vue from 'vue';
import ElementUI from 'element-ui';
import IndexView from '@views/test/index.vue';
import Mixin from '@utils/mixin';

Vue.use(ElementUI);
Vue.use(Mixin); // 全域性元件、方法、計算屬性等

new Vue({
  el: '#app',
  components: { IndexView },
});

新建 Vue 元件 public/mix/resources/views/test/index.vue

<template>
    <div>
        Hello Vue!
    </div>
</template>
<script>
    export default {
        components: {},
        props: {},
        data() {
            return {};
        },
        beforeCreate() {
        },
        created() {
            console.log('Created');
        },
        beforeMount() {
        },
        mounted() {
        },
        beforeUpdate() {
        },
        updated() {
        },
        beforeDestroy() {
        },
        destroyed() {
        },
        watch: {},
        computed: {},
        methods: {},
    };
</script>
<style lang="scss" scoped>
</style>

前後端偽分離

  • 後端框架:Phalcon + Hyperf
  • 前端框架:Bootstrap + jQuery + Vue

前端編譯使用 Laravel Mix 工具,這會節省大量前端配置時間

根目錄新建檔案 webpack.mix.js

const fs = require('fs');
const mix = require('laravel-mix');

const rs_root = 'public/mix/resources';  // 資源 源目錄
const rs_output = 'public/mix/dist';     // 資源 打包目錄
const js_entry = `${ rs_root }/js`;      // js 源目錄
const js_output = `${ rs_output }/js`;   // js 打包目錄
const css_entry = `${ rs_root }/css`;    // css 源目錄
const css_output = `${ rs_output }/css`; // css 打包目錄

mix.webpackConfig({
    resolve: {
        alias: {
            '@': path.resolve(__dirname, rs_root),
            '@api': path.resolve(__dirname, `${ rs_root }/api`),
            '@components': path.resolve(__dirname, `${ rs_root }/components`),
            '@utils': path.resolve(__dirname, `${ rs_root }/utils`),
            '@views': path.resolve(__dirname, `${ rs_root }/views`),
        },
    },
});

// 按照約定,編譯對應的資源
fs.readdirSync(path.resolve(__dirname, js_entry)).forEach(dir => {
    fs.readdirSync(path.resolve(__dirname, `${ js_entry }/${ dir }`)).forEach(file => {
        mix.js(`${ js_entry }/${ dir }/${ file }`, `${ js_output }/${ dir }/${ file }`);
    });
});

mix.sass(`${ css_entry }/app.scss`, `${ css_output }/app.css`); // 公共 CSS
mix.setPublicPath(rs_output);
mix.setResourceRoot('/mix/dist/');

流程

  1. 按照示例配置一個頁面
  2. Yarn 安裝前端依賴
  3. Yarn 前端編譯,此時,PHP 模板中已正確引入 Vue
  4. 訪問路由,PHP 渲染模板,返回給瀏覽器
  5. 瀏覽器載入 Vue,交由 Vue 渲染頁面

侷限

  • 不能做到全域性自動載入元件
  • 編譯後的檔案大小可能會很大

優勢

  • 可以更好地編寫複雜的頁面
  • 更好的維護性

後記

目前仍在不斷地探索中

部落格同步更新

相關文章