不求甚解 – VUE實踐

kingconan發表於2019-02-18

初衷

之前就是laravel自帶的blade加上jquery,配上boostrap。看前端都上Vue了,新頁面嘗試下,總結下體驗

  • 好處:表單操作變得方便了,簡化了動態渲染頁面的邏輯。
  • 缺點:以前使用的js庫,在Vue的框架下,會有初始化的問題。目前都是在updated回撥了重新初始化,不知道是不是正路。
  • 未知:前端自動化工具及單檔案元件,一直沒用。主要還是隨便寫寫~

讓Vue工作

看到前端各種工具,頭就很大。最簡單使用Vue的方式就是直接引入vue.min.js,像引入其他js庫一樣。然後就可以按照官方的例子來個hello world頁面。
到此,準備工作ok了。
然後就要開始改變觀念,之前一直laravel的,所以前端頁面也比較依賴laravel的相關。用Vue的話,只好硬搞前後端分離,所有資料都暫有js來獲取

QA

符號衝突

laravel預設使用的也是 {{}} 來表示變數,所以想想還是改Vue的吧,變成<% vue_variable %>

var hotelController = new Vue({
    el:`#hotelEl`,
    delimiters: ["<%","%>"],
    ...
})

複製程式碼

載入初始資料

遇到的第一個問題,何時發頁面資料請求,怎樣控制頁面初始時的loading狀態

  1. [v-cloak]來控制Vue初始頁面,否則頁面總會先看到一個Vue的變數在Vue初始化之前
  2. 定義一個loading變數來控制loading狀態。頁面資料請求好後,置loading為false。
  3. 在created回撥裡發起資料請求,使用了axios發發請求

Post請求

這可能是vue最方便我的地方了,直接post vue裡的data,不用再關心form表單裡的欄位設計.
後端直接處理json引數即可

update_data : function(){
    var paras = JSON.parse(JSON.stringify(this.$data));//covert data to json
    axios.post(url,paras.hotel)
        .then(function(response){
            //parse response
        })
        .catch(function(error){
            //console.log(error)
        });
}
複製程式碼

動態表單

以前的思路是,通過Jquery構造表單string,然後append到對應的div裡去。相對來說比較麻煩,調整樣式也蛋疼,現在通過控制Vue data來搞,基本就是arr.splice(index, 0, empty) 或者 arr.splice(index,1) 來改變陣列大小,從而控制表單數目

子控制元件傳遞資料

這個Vue官方有說,父到子通過props定義,子到父通過emit事件傳遞

    <z-float-input-district 
    dom_id="district" 
    placeholder="區域" 
    name="district"
    v-model="hotel.location.district" 
    v-on:district_select="event_district">
    </z-float-input-district>
                                
                                
    Vue.component(`z-float-input-district`,{
        props:["placeholder","name", "value", "dom_id"],
        template: `<label class="form-group has-float-label">` +
        `<input :id="dom_id" class="form-control form_input" :name="name" :placeholder="placeholder" ` +
        `v-bind:value="value"` +
        `v-on:input="update($event.target.value)"/>` +
        `<span><% placeholder %></span>`,
        mounted:function(){
            //...
        },
        data : function(){
            return {
                //...
            }
        },
        methods: {
            update:function(item){
                if(item.hasOwnProperty("district")){
                    this.$emit(`input`, item.district)
                    this.$emit(`district_select`, item)
                }
            }
        }
    });
複製程式碼

Jquery修改的form表單,如果同步到Vue的data中

通常來說,使用者輸入修改的表單,可以通過 v-model 實現雙向繫結,但是由於一些遺留問題,仍然需要通過Jquery或者程式碼的方式修改表單資料,比如使用了一個date-range-picker的第三方控制元件,此時就需要構造原生事件通知Vue了。

helper_js_event : function(obj){
    var evt = document.createEvent(`HTMLEvents`);
    evt.initEvent(`input`, false, true);
    obj.dispatchEvent(evt);
}
複製程式碼

當通過Jquery修改某個input時,再呼叫helper_js_event(input)來同步到Vue中

強制重新整理

不知道為什麼,就是不自動更新頁面,不求甚解的話,直接強制重新整理this.$forceUpdate()

複製資料

需要一個複製功能,但是複製Vue的data裡的資料,object和arr是deep copy,尷尬,需要自己實現一個clone功能

   function clone(obj) {
        var copy;
        if (null == obj || "object" != typeof obj) return obj;

        if (obj instanceof Date) {
            copy = new Date();
            copy.setTime(obj.getTime());
            return copy;
        }
        if (obj instanceof Array) {
            copy = [];
            for (var i = 0, len = obj.length; i < len; i++) {
                copy[i] = clone(obj[i]);
            }
            return copy;
        }
        if (obj instanceof Object) {
            copy = {};
            for (var attr in obj) {
                if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
            }
            return copy;
        }

        throw new Error("Unable to copy obj! Its type isn`t supported.");
    }
複製程式碼

頁面返回時檢查是否儲存

由於表單資料較多,防止內容丟失,做一個詢問是否儲存功能。

至於為啥不自動儲存,怕誤操作自動儲存就回不去了。。。

本來想通過watch來監聽Vue data的變化的,發現有點問題,就是每次Vue data賦值完後會呼叫兩次watch,導致我無法判斷data的初始狀態了,但是我watch一個簡單的變數行為就是對的,可能是data的結構太複雜了麼。。

不求甚解的方案,通過比較json串來判斷

//在每次儲存資料成功後呼叫 last_json = this.helper_hotel_json()
window.addEventListener("beforeunload", function (e) {
        if(last_json != hotelController.helper_hotel_json()){
            var confirmationMessage = `離開之前請確認儲存,否則更改資訊會丟失的!!!`;
            (e || window.event).returnValue = confirmationMessage; //Gecko + IE
            return confirmationMessage; //Gecko + Webkit, Safari, Chrome etc.
        }
    });
複製程式碼

解析路由引數

需要用到vue-router,我用這個主要是要解析url引數,獲取一些配置資訊

var router = new VueRouter({
        mode: `history`,
        routes: []
    });
var hotelList = new Vue({
        router,
        ...,
        created:function () {
            var hotel_id = this.$route.query.id;
        }
    )
}
複製程式碼

for迴圈

//直接渲染
<div v-for="(item, index) in items">
    <% item %>
</div>

//數字,[ 1, 7 ],注意從1開始的。。。
<div v-for="index in 7">
    <% index %>
</div>

//for迴圈不關聯dom元素,可以做更多邏輯
<template v-for="(item, index) in items">
    <div>
        <% item %>
    </div>
    <div v-if="index % 4 == 3"></div>
</template>
複製程式碼

頁面大小適配

好吧,有些控制元件要適配頁面大小。想了想還是直接程式碼搞試試先~

當然,h5頁面還是要單獨寫的,一般在php路由時重定向到h5對應的頁面

mounted:function(){
    window.addEventListener(`resize`, this.handle_resize)
    this.handle_resize(null);
},
beforeDestroy: function () {
    window.removeEventListener(`resize`, this.handle_resize)
},
methods : {
    handle_resize : function(event){
        var height = document.documentElement.clientHeight;
        var width = document.documentElement.clientWidth;
        //然後這裡可以各種操作那些和介面大小相關的data引數
    }
}
複製程式碼

axios跨越問題

好吧,這個太折騰了。還是在後端倒了一次。

使用以前的第三方js

第三方控制元件的初始化時機,目前我這裡是在updated回撥時每次都要檢查一次的。否則就會出現失效的問題。

  • 七牛的js-sdk :上傳圖片
  • affix : 浮動
  • swiper : image gallery
  • blueimp : image gallery & fullscreen
  • google map : 非同步載入,所以回撥時也要嘗試初始化一次

Refs

https://vuejs.org/

相關文章