記一次痛苦的yii+vue+element 前後端不分離開發經歷

Day10發表於2018-07-12

引言

在當今前後端分離的大趨勢下,仍然有一些前後端不分離的需求。因為boss考慮到webpack打包比較麻煩和耗時,使運維有一些問題,但是又無法捨棄vue資料繫結等棒棒噠功能,所以在前後端不分離的情況下,以yii+vue+element技術棧寫專案的任務應運而生,而且這個任務給了我。

具體要求

  1. 能封裝出各種元件,在php中呼叫
  2. 能進行一定程度的定製化
  3. 儘量用php寫

頭腦沙塵暴

  1. 對於第一個問題,我選擇用yii中的自定義小部件去寫,首先小部件是物件導向方式來重用檢視程式碼,而且符和可配置的需求。
  2. 對於第二個問題,就是這個部件既可以傳幾個引數進行配置,又可以寫一個完整的函式去覆蓋。(十臉懵逼)
  3. 對於第三個問題,意思是儘量把程式碼寫在controller裡面,然後賦值給小部件。

第一版

第一版主要解決了一些基礎問題,比如widget目錄結構,vue引入,如何混合寫php和js這樣的問題。

  1. 涉及小部件的目錄結構

    Project
    └───assets
    │       └───AppAsset.php // 此處引入element,vue
    └───components
    |    └───Form
    │         │   FormView.php    // 部件檢視檔案
    │         │   FormWidget.php 
    │   
    └───web
        └───js
            └───conponents
                └─── form.js // 例項化vue
    複製程式碼
  2. 具體程式碼

    AppAsset.php

        <?php
        namespace app\assets;
        use yii\web\AssetBundle;
        class AppAsset extends AssetBundle
        {
            public $basePath = '@webroot';
            public $baseUrl = '@web';
            public $css = [
                'css/site.css',
                'css/element-ui/index.css',
            ];
            public $js = [
                'js/vue/vue.js',
                'js/element-ui/index.js',
            ];
            public $depends = [
                'yii\web\YiiAsset',
                'yii\bootstrap\BootstrapAsset',
            ];
        
        }
    複製程式碼

    FormView.php

    <?php
    use app\components\form\FormAsset;
    FormAsset::register($this);
    
    $this->registerJsFile('@web/js/components/form.js', ['position'=> \yii\web\View::POS_END, 'depends' => @app\assets\AppAsset::ClassName()]);  
    ?>
    <div id="form">
       
    </div>
    <script type='text/javascript'>
    var vue_form_template = <?=json_encode($form_template)?>;
    var vue_form_data = <?=json_encode($form_data)?>;
    var vue_form_mounted= <?=json_encode($form_mounted)?>
    
    var form_temp_methods= <?=json_encode($form_methods)?>;
    var vue_form_method = {};
    
    function generateMethods() {
        for (let key in form_temp_methods) {
            vue_form_method[key] = function() {
                // 引數預設用arguments
                console.log(arguments);
                eval(form_temp_methods[key])
            }
        };
    }
    generateMethods()
    </script>
    複製程式碼

    FormWidget

        <?php
        namespace app\components\form;
        use yii\base\Widget;
        use yii\helpers\Html;
        use app\assets\AppAsset;
        
        
        class FormWidget extends Widget
        {
            public $form_template = '';
        
            public $form_data = [];
        
            public $form_mounted;
        
            public $form_methods;
        
            public function init()
            {
                
            }
        
            public function run()
            {   
                return $this->render('FormView',[
                    'form_template'=>$this->form_template,
                    'form_data'=>$this->form_data,
                    'form_mounted'=>$this->form_mounted,
                    'form_methods'=>$this->form_methods,
                ]);
            }
        
            public function getViewPath() // 這個函式可以定義部件view的路徑
            {
                return '@app/components/form'; 
            }
        }
    複製程式碼
  3. 使用,在index.php

        <?=FormWidget::widget([
            // controller中傳入的html,類似<el-button></el-button>
            'form_template' => $form_template, 
            'form_data' => $form_data,
            'form_mounted' => $form_mounted,
            'form_methods' => [
                'closeForm' => '
                    Table.table_span = 20;
                    this.form_data.form_is_show = false;
                ',
                'saveForm' => '
                    this.closeForm()
                '
            ]
        ])?>
    複製程式碼
  4. 第一版遇到的問題

    • 小部件通訊,比如有樹小部件和表單小部件,我點選樹的節點時,表單小部件需要更新,那麼就要求樹的vue例項中能操縱表單的vue例項
    • methods的引數問題,如上面的程式碼,vue_form_method是遍歷form_methods之後賦值給了vue的methods屬性,但是這樣遍歷沒辦法指定引數。比如element的tree有一個事件node-click,預設傳三個引數,這個沒辦法在 form_methods中指定。
    • boss的一些新想法
      • 廣播,即頁面所有元件都可以監聽事件,事件發生後,對應監聽該事件的元件可以做出相應反應
      • 全域性變數,比如兩個元件引入同一變數,這個變數變化的時候,兩個元件的資料也相應變化
  5. 解決方案

    • 把vue例項賦值給一個變數,比如var Tree = new Vue(),然後其他元件可以用Tree.example_data來訪問樹的屬性
    • 用arguments,var node = arguments[0]來訪問引數。當然我個人認為這不是很好的方法,但是沒想出更好的解決辦法
    • 廣播的問題,我寫了一個event_bus小部件,這個小部件只是一個單純的vue例項,每個部件用EventBus.$on('click', function() {})的方法監聽事件,EventBus.$emit('click')的方法觸發事件就可以了。
    • 當這個全域性變數是一個物件或者是陣列的時候,才可以在變數變化時,資料也變化

to be continue...

相關文章