引言
在當今前後端分離的大趨勢下,仍然有一些前後端不分離的需求。因為boss考慮到webpack打包比較麻煩和耗時,使運維有一些問題,但是又無法捨棄vue資料繫結等棒棒噠功能,所以在前後端不分離的情況下,以yii+vue+element技術棧寫專案的任務應運而生,而且這個任務給了我。
具體要求
- 能封裝出各種元件,在php中呼叫
- 能進行一定程度的定製化
- 儘量用php寫
頭腦沙塵暴
- 對於第一個問題,我選擇用yii中的自定義小部件去寫,首先小部件是物件導向方式來重用檢視程式碼,而且符和可配置的需求。
- 對於第二個問題,就是這個部件既可以傳幾個引數進行配置,又可以寫一個完整的函式去覆蓋。(十臉懵逼)
- 對於第三個問題,意思是儘量把程式碼寫在controller裡面,然後賦值給小部件。
第一版
第一版主要解決了一些基礎問題,比如widget目錄結構,vue引入,如何混合寫php和js這樣的問題。
-
涉及小部件的目錄結構
Project └───assets │ └───AppAsset.php // 此處引入element,vue └───components | └───Form │ │ FormView.php // 部件檢視檔案 │ │ FormWidget.php │ └───web └───js └───conponents └─── form.js // 例項化vue 複製程式碼
-
具體程式碼
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'; } } 複製程式碼
-
使用,在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() ' ] ])?> 複製程式碼
-
第一版遇到的問題
- 小部件通訊,比如有樹小部件和表單小部件,我點選樹的節點時,表單小部件需要更新,那麼就要求樹的vue例項中能操縱表單的vue例項
- methods的引數問題,如上面的程式碼,vue_form_method是遍歷form_methods之後賦值給了vue的methods屬性,但是這樣遍歷沒辦法指定引數。比如element的tree有一個事件node-click,預設傳三個引數,這個沒辦法在 form_methods中指定。
- boss的一些新想法
- 廣播,即頁面所有元件都可以監聽事件,事件發生後,對應監聽該事件的元件可以做出相應反應
- 全域性變數,比如兩個元件引入同一變數,這個變數變化的時候,兩個元件的資料也相應變化
-
解決方案
- 把vue例項賦值給一個變數,比如
var Tree = new Vue()
,然後其他元件可以用Tree.example_data來訪問樹的屬性 - 用arguments,
var node = arguments[0]
來訪問引數。當然我個人認為這不是很好的方法,但是沒想出更好的解決辦法 - 廣播的問題,我寫了一個event_bus小部件,這個小部件只是一個單純的vue例項,每個部件用
EventBus.$on('click', function() {})
的方法監聽事件,EventBus.$emit('click')
的方法觸發事件就可以了。 - 當這個全域性變數是一個物件或者是陣列的時候,才可以在變數變化時,資料也變化
- 把vue例項賦值給一個變數,比如
to be continue...