[Vue.js] 不是每個資料操作都是響應式畫面連動,整理一些狀況需要注意(包含jQuery datetimepicker、autocomplete plugin的整合)

Vue.js and jQuery datetimepicker、jQuery autocomplete

前言

把Vue.js真正運用在工作上,其複雜的開發環境就會出現一些地雷需要留意

畢竟現實工作中的專案,同一頁面會用到的JS函式庫不會只有Vue.js,通常還會混搭

地雷事項

1.jQuery的.val("指派值") 不會觸發JS事件,沒有事件觸發,Vue不會探測到資料改變

由於Vue.js的v-model指令預設監聽JS的input事件

所以遇到jQuery日期選擇器或jQuery autocomplete這種會使用到 $("input").val("指派值") 的插件

即使v-model指令雙向綁定到<input>DOM元素,實際上Vue.js是不會獲取到jQuery插件給予的值

有幾種解法:

解法1.修改jQuery插件原始碼,觸發input事件 (治本方法,就算沒使用Vue.js,也不影響其他功能運作)

例如:jQuery autocomplete (jQuery自動完成懶人包)

解法來源:jQuery修改input的val能否触发v-model的更新

但並非每個jQuery插件原始碼都那麼好修改,例如:DateTimePicker

所以下面再提供另一方法

解法2.在<input>的blur事件中,透過$event.target.value取得<input>的值並指派給雙向綁定的對象

※根據我的測試,jQuery插件有的會觸發Vue.js的change事件(jQuery autocomplete),有的不會觸發Vue.js的change事件(上述的jQuery datetimepicker),所以目前我覺得在blur事件中處理比較靠譜

 <!DOCTYPE html>
<html lang="utf-8">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="IE=edge"> 
    <title>測試頁</title> 
    <link href="~/css/bootstrap.css" rel="stylesheet" type="text/css" />
    <!--日期選擇jquery datetimepicker 出處: http://xdsoft.net/jqplugins/datetimepicker/-->
    <link href="~/Content/jQuery_datetimepicker/jquery.datetimepicker.css" rel="stylesheet" />
    <style type="text/css">
        body {
        padding-top:30px;
        }
    </style>
</head>
<body>


    <div class="container" id="mydateApp">
        <div class="row">
            <div class="form-group col-xs-12">
                <!-- 輸入日期-->
                <label>從</label>
                <input type="text" name="mydateFrom" v-model="mydateFrom" v-on:blur="mydateFromBlur" /> ~
                <label>到</label>
                <input type="text" name="mydateTo" v-model="mydateTo" v-on:blur="mydateToBlur" />
            </div>
            <div class="form-group col-xs-12">
                <button type="button" class="btn btn-primary" v-on:click="mySubmit">送出</button>
            </div>
            <div v-text="submitResult">
            </div>
        </div>
    </div>

    <script src="~/Scripts/jquery-3.2.1.min.js"></script>
    <script src="~/Scripts/bootstrap.min.js"></script>
    <!--Vue.js-->
    <script src="~/Scripts/vue.js"></script> 
    <!--日期選擇器-->
    <script src="~/Content/jQuery_datetimepicker/jquery.datetimepicker.js"></script>

    <script type="text/javascript">
        let vue_mydateApp = new Vue({
            el: "#mydateApp",
            data: {
                mydateFrom: "", //輸入的日期
                mydateTo: "",
                submitResult:"" //顯示結果
            },
            methods: { 
                mydateFromBlur: function ($event) {
                    let vm = this;  
                    vm.mydateFrom = $event.target.value;//更新雙向綁定對象的值
                },
                mydateToBlur: function ($event) {
                    let vm = this;
                    vm.mydateTo = $event.target.value;//更新雙向綁定對象的值
                },
                mySubmit: function () {
                    let vm = this;
                    vm.submitResult = `${vm.mydateFrom}~${vm.mydateTo}`;
                 
                }
            }
        });//end Vue app


        //DOM元素啟用jQuery datetimepicker
        $(function () {
            $("input[name='mydateFrom'],input[name='mydateTo']").datetimepicker({
                lang: 'ch',//簡中
                timepicker: false,//不選擇時間
                format: 'Y/m/d',//時間格式
            });

        });
    </script>

</body>
</html>

這種情況已經是工程師自行決定何時更新數據,所以在<input>使用v-model雙向綁定 或 v-bind:value 單向綁定 value屬性 兩種寫法已經都沒差

<!--輸入日期-->
<label>從</label> <!--也可以單向綁定value屬性-->
<input type="text" name="mydateFrom" v-bind:value="mydateFrom"  v-on:blur="mydateFromBlur"  /> ~
<label>到</label>
<input type="text" name="mydateTo" v-bind:value="mydateTo"  v-on:blur="mydateToBlur"  />

採用第二種解法需留意,對於有些jQuery插件(jQuery autocomplete),需要等待一點時間讓<input>填完值後才能更新雙向綁定的對象,所以寫法要加上setTimeout↓

     your_blur_event: function ($event) {
            let vm = this;
            setTimeout(function () { 
                vm.yourdata = $event.target.value; 
            }, 200);//等待值確實填入input
          
     } 

※ ↑ 如果用戶操作速度超快,有可能雙向綁定的對象還沒更新時,用戶就點擊、執行其他功能導致前端邏輯不正確,setTimeout會是效能瓶頸,若有使用,請謹慎思考前端操作流程

解法3.找Vue.js的第三方插件以取代jQuery 插件

※2019.01.18追記:jQuery叫用 $("input[type='checkbox'] , input[type='radio']").prop("checked",true);,也必須留意不會觸發input事件 

※2019.1.21追記:個人使用的jQuery DateTimePicker 還有另一方法可以給予Vue.js data,利用jQuery監聽<input>的change事件,當change事件觸發時,再用.val()抓DOM元素的值給予Vue.js的data值

<!--日期選擇jquery datetimepicker 出處: http://xdsoft.net/jqplugins/datetimepicker/-->
<link href="~/Content/jQuery_datetimepicker/jquery.datetimepicker.css" rel="stylesheet" />
<div id="myApp">
    <input type="text" name="myDate" v-model="myDate" />
</div>
<!--引用jQuery-->
<script src="~/Scripts/jquery-3.2.1.min.js"></script>
<!--引用 Vue.js-->
<script src="~/Scripts/vue.js"></script>
<!--日期選擇jquery datetimepicker 出處: http://xdsoft.net/jqplugins/datetimepicker/-->
<script src="~/Content/jQuery_datetimepicker/jquery.datetimepicker.js"></script>
 
<script type="text/javascript">
    let vueApp = new Vue({ //實例化Vue App
        el: "#myApp",
        data: {
            myDate: ""
        }
    });


    /*以下是jQuery的操作*/
    $(function () {
        $("input[name='myDate']").datetimepicker({//啟用jQuery datetimepicker
            lang: 'ch',//簡中
            timepicker: false,//不選擇時間
            format: 'Y/m/d',//時間格式
        });
        /*解法↓*/
        $("input[name='myDate']").change(function () {
            //vueApp為Vue.js的全域實體變數
            vueApp.myDate = $(this).val();//從jQuery抓DOM的值指派給Vue.js的data
        });

    });
</script>

↑ 但不是每個jQuery plugin都可以這樣做,我使用的 jQuery autocomplete (jQuery自動完成懶人包) ,就算在jQuery監聽的change、blur事件中使用.val() 抓DOM元素的值

.val() 抓到值仍然不是 autocomplete 下拉清單中的用戶選定值,所以要使用什麼解法真的都要試過一遍Orz

 

2.雖然同樣是表單輸入欄位,但<input type="hidden" />隱藏欄位必須用v-bind:value的指令才能正確存取DOM元素的值

隱藏欄位注定偵測不到用戶的input事件,所以用v-model指令無意義,只能綁定DOM元素的value屬性,存取才會正常,例如:表單提交

解法來源:v-model do not work with hidden input ?

↓ ASP.net MVC的View畫面

 <!DOCTYPE html>
<html lang="utf-8">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="IE=edge"> 
    <title>測試頁</title> 
    <link href="~/css/bootstrap.css" rel="stylesheet" type="text/css" /> 
    <style type="text/css">
        body {
        padding-top:30px;
        }
    </style>
</head>
<body>


    <div class="container" id="myApp">
        <div class="row">
            <form action="@Url.Action("Test","Common")" method="post">
                <input type="hidden" name="myHidden" v-bind:value="myHidden"  /> <!--如果這裡的value預設值和Vue不一樣的話,則以Vue為主-->
                
                <div class="form-group col-xs-12">
                    <button type="button" class="btn btn-info" v-on:click="modifyHidden">修改hidden的值</button>
                    <button type="button" class="btn btn-info" id="btnQueryHidden">jQuery抓hidden的值確認看看</button>
                    <button type="submit" class="btn btn-primary">送出表單</button>
                </div>
                <div>
                    <!--剛剛提交的結果-->
                    @ViewData["myHidden"]
                </div>
            </form>
        </div>
    </div>

    <script src="~/Scripts/jquery-3.2.1.min.js"></script>
    <script src="~/Scripts/bootstrap.min.js"></script>
    <!--Vue.js-->
    <script src="~/Scripts/vue.js"></script> 
     

    <script type="text/javascript">
        let vue_mydateApp = new Vue({
            el: "#myApp",
            data: {
                myHidden: "vue_init", //hidden的值 
            },
            methods: { 
                modifyHidden: function ($event) {
                    let vm = this; 
                    vm.myHidden =  Date.now();//修改hidden值
                }  
            }
        });//end Vue app

        //以下是jQuery世界
        $(function () {
            $("#btnQueryHidden").on("click", function () {

               alert($("input[type='hidden']").val());

            });
        });
    </script>

</body>
</html>

3.創建Vue實例後,才針對資料物件追加屬性的寫法

Javascript的物件可以動態追加屬性(鍵值),但有些地雷需要留意

↓以下是錯誤寫法

 <div class="container" id="myApp">
        <div class="row"> 
            <div class="form-group col-xs-12">
                <button type="button" class="btn btn-primary" v-on:click="mySubmit">追加屬性</button>
            </div>
            <div v-text="myObj.newKey"></div> <!--顯示新追加屬性的值-->
        </div>
    </div>

    <script src="~/Scripts/jquery-3.2.1.min.js"></script>
    <script src="~/Scripts/bootstrap.min.js"></script>
    <!--Vue.js-->
    <script src="~/Scripts/vue.js"></script>
     

    <script type="text/javascript">
        let vue_myApp = new Vue({
            el: "#myApp",
            data: {
                myObj: {
                    name: "高級打字員" 
                }  
            },
            methods: { 
                mySubmit: function () {
                    let vm = this;
                    vm.myObj.newKey = "newValue"; //追加新屬性,不會響應式 
                }
            }
        });//end Vue app

         
    </script>

↓這樣寫才正確(適用追加一個屬性)

<body>


    <div class="container" id="myApp">
        <div class="row">
            <div class="form-group col-xs-12">
                <button type="button" class="btn btn-primary" v-on:click="mySubmit">追加屬性</button>
            </div>
            <div v-text="myObj.newKey"></div><!--顯示新追加屬性的值-->
        </div>
    </div>

    <script src="~/Scripts/jquery-3.2.1.min.js"></script>
    <script src="~/Scripts/bootstrap.min.js"></script>
    <!--Vue.js-->
    <script src="~/Scripts/vue.js"></script>
     

    <script type="text/javascript">
        let vue_myApp = new Vue({
            el: "#myApp",
            data: {
                myObj: {
                    name: "高級打字員" 
                }  
            },
            methods: { 
                mySubmit: function () {
                    let vm = this;
                    //第二個參數是字串,第三個參數則任意型別
                    Vue.set(vm.myObj, "newKey", "New Hello World!!");//追加新屬性,響應式的正確寫法 
                     
                }
            }
        });//end Vue app

         
    </script>

</body>

↓ 追加多個屬性的正確寫法

<body>


    <div class="container" id="myApp">
        <div class="row">
            <div class="form-group col-xs-12">
                <button type="button" class="btn btn-primary" v-on:click="mySubmit">追加屬性</button>
            </div>
            <ul>
                <li v-for="(value , key) in myObj" v-text="( key + ' '+ value)"></li>
                </ul><!--顯示新追加屬性的值-->
        </div>
    </div>

    <script src="~/Scripts/jquery-3.2.1.min.js"></script>
    <script src="~/Scripts/bootstrap.min.js"></script>
    <!--Vue.js-->
    <script src="~/Scripts/vue.js"></script>
     

    <script type="text/javascript">
        let vue_myApp = new Vue({
            el: "#myApp",
            data: {
                myObj: {
                    name: "高級打字員" 
                }  
            },
            methods: { 
                mySubmit: function () {
                    let vm = this;
                    vm.myObj = Object.assign({}, vm.myObj, {
                        age: 27,
                        favoriteColor: 'Vue Green',
                        threeKey:"threeKeyValue"
                    })
                     
                }
            }
        });//end Vue app

         
    </script>

</body>

↓這種的,就沒救了

4.留意修改、刪除Array的幾個限制

※附送上移、下移的功能實作

<!doctype html>
<html>
<head> 
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css" integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS" crossorigin="anonymous">
    <title>Hello, Vue.js!</title>
    <style type="text/css">
        body {
            padding-top: 30px;
        }  
    </style>
</head>
<body> 
    <div class="container" id="myApp">
        <div class="row">
            <div class="form-group col-12">
                <button type="button" class="btn btn-warning" v-on:click="EditMyArray">把myArray的b替換成z</button>
                <button type="button" class="btn btn-danger" v-on:click="ClearMyArray">清除myArray</button>
                <button type="button" class="btn btn-success" v-on:click="RestoreMyArray">恢復myArray</button>
            </div>
            <div class="form-group col-12">
                顯示myArray資料↓
                <ul v-if="myArray.length>0">
                    <li v-for="(item , index) in myArray" v-text="item"></li>
                </ul>

            </div> <!--end .form-group-->
            <div class="form-group col-12">
                <button type="button" class="btn btn-warning" v-on:click="Editinit_users">把init_users的username Allen替換成ALLEN</button>
                <button type="button" class="btn btn-danger" v-on:click="Clearinit_users">清除init_users</button>
                <button type="button" class="btn btn-success" v-on:click="Restoreinit_users">恢復init_users</button>
            </div>
            <div class="form-group col-12">
                顯示init_users資料↓
                <ul v-if="init_users.length>0">
                    <li v-for="(user , index) in init_users">
                        id:{{user.id}}
                        <b> username:{{user.username}} </b>
                        email:{{user.email}} age:{{ user.age}}
                        sort:{{user.sort}}
                    </li>
                </ul>

            </div> <!--end .form-group-->
            <div class="form-group col-12">
                輸入字串並上移、下移資料↓
                <ul v-if="init_users.length >0" style="list-style:none;">
    <!--此範例會改變陣列順序,由於出現沒被綁定的表單欄位,故必須事先v-bind:key才能跟著變換順序-->
                    <li v-for="(user , index) in init_users" v-bind:key="user.id" style="border:1px solid #000;"  >
                        id:{{user.id}}  username:{{user.username}}  age:{{ user.age}}
                        <br />
                        email: 
<input type="text" v-model="user.email" placeholder="輸入email" 
class="form-control" style="display:inline-block;width:25%;" />
		<input type="text" placeholder="我沒被綁定"  />
                        <br />
                        <button type="button" class="btn btn-info" v-on:click="Up(user,index);">上移</button>
                        <button type="button" class="btn btn-info" v-on:click="Down(user,index);">下移</button>
                    </li>
                </ul>

            </div> <!--end .form-group-->
        </div> <!--end .row-->
        </div> <!--end .container-->


    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://code.jquery.com/jquery-3.3.1.min.js" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js" integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js" integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k" crossorigin="anonymous"></script>
    <!-- 引用Vue.js -->
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.js"></script>
    <script type="text/javascript"> 
        //模擬這是後端預先準備的Json資料
        let init_users = [
            {
                id: 1,
                "username": "Allen",
                "email": "Allen@april.biz",
                 age: 32,
                 sort:1 //sort通常會存在後端的DB
            },
            {
                id: 2,
                "username": "Brench",
                "email": "Brench@melissa.tv",
                age: 18,
                sort: 2
             
            },
            {
                id: 3,
                "username": "Cherry",
                "email": "Cherry@miniasp.com",
                age: 25,
                sort: 3
            },
            {
                id: 4,
                "username": "David",
                "email": "David@miniasp.com",
                age: 44,
                sort: 4
            }
        ];
        let myArray = ["a", "b", "c"];
       
        let app = new Vue({
            el: '#myApp',/*定義Vue作用範圍,el指的是element*/
            data: {//data是關鍵字
                myArray: myArray,
                init_users: init_users,//模擬把後端資料指派給init_users data  
                //計算屬性放在data沒有作用
                // init_users_indexObj: function () {
                //    let vm = this;
                //    let firstIndex = 0;
                //    let lastIndex = 0;

                //    if (vm.init_users.length > 0) {//有資料時
                //        lastIndex = vm.init_users.length - 1;
                //    }
                //    return { firstIndex: firstIndex, lastIndex: lastIndex };
                //}
            },
             computed: {
                //計算屬性一定要有回傳值
                init_users_indexObj: function () {
                    let vm = this; 
                    let firstIndex = -1;
                    let lastIndex = -1;

                    if (vm.init_users.length > 0)
                    {//有資料時
                        firstIndex = 0;
                        lastIndex = vm.init_users.length - 1;
                    }
                    return { firstIndex: firstIndex, lastIndex: lastIndex };
                }
            } ,
            methods: { //methods是關鍵字,通常處理和用戶的互動事件
                EditMyArray: function () {
                    let vm = this;
                    
                    let source = "b";
                    let target = "z"; 
                    let delIndex =  vm.myArray.findIndex(function (item, index) {
                        return item === source;  //找不到index就回傳-1
                    }); 

                    //錯誤寫法,不是響應式
                     //vm.myArray[delIndex] = target ;
                     
                    //正確寫法
                    //if (delIndex !== -1) 
                    //{
                    //Vue.set給index -1不會有事,不必防呆
                      Vue.set(vm.myArray, delIndex, target);//替換陣列中的值,會連同源資料 myArray都一起替換
                    //}
                    
                },
                ClearMyArray: function () {//清除陣列 
                    //this.myArray.length = 0;//資料清除,但不是響應式的錯誤寫法
                   //this.myArray = []; //或 指向new Array(); 是響應式寫法,但只是把指標指向其它位址,源資料仍存在
									 this.myArray.splice(0,this.myArray.length); //連同源資料也刪除
									 
									 
                },
                RestoreMyArray: function () {
                    this.myArray = myArray;
                },
                Editinit_users: function () {
                    let vm = this;//把username Allen改成ALLEN

                    let source = "Allen";//要找的username
                    let target = "ALLEN";//要替換的username

                    let delIndex = vm.init_users.findIndex(function (userObj, index) {
                        return userObj.username === source; //找不到,index回傳-1
                    }); 
                     
                    if (delIndex !== -1)
                    {//有找到
                        //集合物件可以正確這樣處理
                        vm.init_users[delIndex].username = target;
                        // Vue.set(vm.init_users[delIndex], "username", target);//替換集合物件中,某個物件屬性的值
                        //↑上面兩種寫法都會連同 源資料init_users一起修改
                     }

                },
                Clearinit_users: function () {//清除陣列 
                   // this.init_users.length = 0;//資料清除,但不是響應式的錯誤寫法
                    //this.init_users = []; //或 指向new Array(); 是響應式寫法,但只是把指標指向其它位址,源資料仍存在
								 this.init_users.splice(0,this.init_users.length); //連同源資料也刪除
									 
                },
                Restoreinit_users: function () {
                    this.init_users = init_users;
                }, 
                Up: function (user,index) {
                    let vm = this;
                    //找出前一筆資料交換Sort

                    //如果已是第一筆就不執行上移
                    let currentSort = user.sort;
                    //let currentIndex = vm.init_users.findIndex(function (item, idx) {
                    //  return item.id === user.id;
                    //});
                      let currentIndex=index;
                    //往上面的資料走訪
                    for (let idx = currentIndex; idx >= vm.init_users_indexObj.firstIndex; idx--)
                    {
                          if (vm.init_users[idx].sort < currentSort) {//交換Sort資料
                            user.sort = vm.init_users[idx].sort;
                              vm.init_users[idx].sort = currentSort;

                            //交換陣列位置,以變更View
                            let tempObj = vm.init_users[idx];//target object先暫存
                            vm.init_users[idx] = user;//source放到正確位置
                            vm.init_users[currentIndex] = tempObj;//最後處理 
                            break;//僅處理一筆即可
                        }//end if 
                    }//end 
                     
                },
                Down: function (user,index) {
                    let vm = this;
                    //找出下一筆資料交換Sort
                    
                    //如果已是最後一筆就不執行下移
                    let currentSort = user.sort;
                   // let currentIndex =  vm.init_users.findIndex(function (item,idx) {
                    //    return item.id === user.id; 
                   // });
                    let currentIndex=index ;
                    for (let idx = currentIndex; idx <= vm.init_users_indexObj.lastIndex; idx++)//往下尋找資料
                    {
                        if (vm.init_users[idx].sort > currentSort)
                        {//交換Sort資料
                            user.sort = vm.init_users[idx].sort;  
                            vm.init_users[idx].sort = currentSort; 
                            //交換陣列位置,以變更View
                            let tempObj = vm.init_users[idx];//target object先暫存
                            vm.init_users[idx] = user;//source放到正確位置
                            vm.init_users[currentIndex] = tempObj;//最後處理 
                            break;
                        }
                    }//end for


                }
            }  
             
        }); 
    </script>
</body>
</html>

線上Demo:https://jsfiddle.net/ShadowKao/j834vkas/

↑ 此範例會改變元素在陣列的位置,如果剛好畫面有未綁定的表單欄位<input>、<select>、<textarea>,記得v-for迴圈要 v-bind:key 綁定一個唯一鍵值讓Vue識別追蹤

官網 Vue.set 說明

 

5.留意修改、刪除Object資料集的幾個限制

※附送上移、下移功能的實現

<!doctype html>
<html>
<head> 
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css" integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS" crossorigin="anonymous">
    <title>Hello, Vue.js!</title>
    <style type="text/css">
        body {
            padding-top: 30px;
        }  
    </style>
</head>
<body> 
    <div class="container" id="myApp">
        <div class="row">
     
            
            <div class="form-group col-12">
                <button type="button" class="btn btn-warning" v-on:click="Editinit_users">把init_users的username Allen替換成ALLEN</button>
                <button type="button" class="btn btn-danger" v-on:click="Clearinit_users">清除init_users</button>
                <button type="button" class="btn btn-success" v-on:click="Restoreinit_users">恢復init_users</button>
            </div>
            <div class="form-group col-12">
                顯示init_users資料↓
                <ul v-if="Object.keys(init_users).length>0">
                    <li v-for="(user , key) in init_users">
                        userid:{{user.userid}}
                        <b> username:{{user.username}} </b>
                        email:{{user.email}} age:{{ user.age}}
                        sort:{{user.sort}}
                    </li>
                </ul>

            </div> <!--end .form-group-->
            <div class="form-group col-12">
                輸入字串並上移、下移資料↓
                <ul v-if="Object.keys(init_users).length >0" style="list-style:none;">
                    <li v-for="(user , key) in init_users" style="border:1px solid #000;"  >
                        userid:{{user.userid}}  username:{{user.username}}  age:{{ user.age}}
                        <br />
                        email: <input type="text" v-model="user.email" placeholder="輸入email" class="form-control" style="display:inline-block;width:50%;" />
                        <br />
                        <button type="button" class="btn btn-info" v-on:click="Up(user,key);">上移</button>
                        <button type="button" class="btn btn-info" v-on:click="Down(user,key);">下移</button>
                    </li>
                </ul>

            </div> <!--end .form-group-->
        </div> <!--end .row-->
        </div> <!--end .container-->


    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://code.jquery.com/jquery-3.3.1.min.js" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js" integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js" integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k" crossorigin="anonymous"></script>
    <!-- 引用Vue.js -->
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.js"></script>
    <script type="text/javascript"> 
        let init_users = //.net的Dictionary<string,Object>可以序例化為這樣的Json格式
        {   
            "1": {
                userid:"1",
                username: "Bret",
                email: "Bret@april.biz",
                age: 32,
                sort: 10
            },
            "2":
            {
                userid: "2",
                username: "Antonette",
                email: "Antonette@melissa.tv",
                age: 18,
                sort: 20
            },
           "3":
            {
               userid: "3",
                username: "Allen",
                email: "Allen@miniasp.com",
                age: 25,
                sort: 30
            },
            "4":
            { 
                userid: "4",
                username: "Jack",
                email: "Jack@miniasp.com",
                age: 44,
                sort: 40
            }
        };
        let app = new Vue({
            el: '#myApp',/*定義Vue作用範圍,el指的是element*/
            data: { 
                init_users: init_users 
            }, 
            methods: { //methods通常處理和用戶的互動事件 
                Editinit_users: function () {
                    let vm = this;//把username Allen改成ALLEN

                    let source = "Allen";//要找的username
                    let target = "ALLEN";//要替換的username
                    for(let key in vm.init_users)
										{
										 if(vm.init_users[key].username===source)
										 {
										    vm.init_users[key].username=target;
											//Vue.set(vm.init_users[key], "username", target);//替換集合物件中,某個物件屬性的值
											//↑兩種寫法都可以
										 }
										
										}
                   

                },
                Clearinit_users: function () {//清除物件群 
                    let vm = this;
                   //this.init_users = { }; //或 指向new Object(); 是響應式寫法,但只是把指標指向其它位址,源資料仍存在
								 
                Object.keys(vm.init_users).forEach(function (key) {
									 // delete vm.init_users[key]; //不是響應式寫法
									   Vue.delete(vm.init_users, key);//連同 源資料也被刪除
                });
								    
									 
                },
                Restoreinit_users: function () 
								{
                    this.init_users = init_users;
                }, 
                Up: function (user,currentKey) {
                    let vm = this;
                    //找出前一筆資料交換Sort 
                    //如果已是第一筆就不執行上移
                    let currentSort = user.sort;
                   
									  let lastIndex = Object.keys(vm.init_users).length - 1;
                    //由下往上面的資料走訪
                    for (let idx = lastIndex; idx >= 0; idx--)
                    {
										    //將索引換算成對應的key字串
										    let key = Object.keys(vm.init_users)[idx];
                          if (vm.init_users[key].sort < currentSort) 
													{//交換Sort資料
                            user.sort = vm.init_users[key].sort;
                            vm.init_users[key].sort = currentSort;

                            //掛到不同的key,以變更View
                            let tempObj = vm.init_users[key];//target object先暫存
                            vm.init_users[key] = user;//source放到正確位置,上移
                            vm.init_users[currentKey] = tempObj;//最後處理 
                            break;//僅處理一筆即可
                        }//end if 
                    }//end 
                     
                },
                Down: function (user,currentKey) {
                    let vm = this;
                    //找出下一筆資料交換Sort
                    
                    //如果已是最後一筆就不執行下移
                    let currentSort = user.sort;
                   
                    for (let key in vm.init_users)//往下尋找資料
                    {
                        if (vm.init_users[key].sort > currentSort)
                        {//交換Sort資料
                            user.sort = vm.init_users[key].sort;  
                            vm.init_users[key].sort = currentSort; 
                            //交換物件的key,以變更View
                            let tempObj = vm.init_users[key];//target object先暫存
                            vm.init_users[key] = user;//source放到正確位置 
                            vm.init_users[currentKey] = tempObj;//最後處理  
                            break;
                        }
                    }//end for


                }
            }  
             
        }); 
    </script>
</body>
</html>

線上Demo:https://jsfiddle.net/ShadowKao/r7vz6nqm/

刪除物件資料集可使用迴圈搭配 Vue.delete( target, key )

但如果對陣列使用迴圈搭配 Vue.delete,例如↓

let vm = this;
for(let idx in vm.myArray)
{
 Vue.delete(vm.myArray,idx);

}

這種情況就會如同在迴圈中呼叫.splice(index,1); 不完全刪除資料,因為每刪除一筆陣列中的元素,陣列大小就會改變


let vm =this;
for(let idx=0;idx<vm.myArray.length;idx++)
{
 vm.myArray.splice(idx,1);  
}

結論

陣列刪除↓

 let deleteIndex = 0;//先找出要刪除的陣列索引
 let vm =this; 
 vm.myArray.splice(deleteIndex,1);//刪除一筆 

 //全部刪除陣列中的元素
 vm.myArray.splice(0,vm.myArray.length); 
 

物件刪除↓

let vm = this;
Vue.delete(vm.myObject, key);//刪除一筆

for(let key in vm.myObject)//刪除多筆
{
  Vue.delete(vm.myObject, key);
}

詳見官網說明:注意事项

進階議題官網說明:深入响应式原理

6.使用 v-if 切換畫面,如果內容有表單欄位時,最好使用v-model指令雙向綁定那些表單欄位

要不然改使用 v-show (顯示或隱藏display:none;)來切換畫面

<!DOCTYPE html>
<html lang="utf-8">

  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>[Vue.js] 切換畫面可能遇到的問題</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <style type="text/css">
      body {
        padding-top: 30px;
      }

    </style>
  </head>

  <body>
    <div class="container" id="myApp">
      <div class="row">
        <!--v-if會重新渲染畫面-->
        <div class="form-group col-xs-12">
          <input type="checkbox" v-model="isChecked" /> <label>請勾我</label>
        </div>


       
        <div v-if="isChecked" class="form-group col-xs-12">
          v-if 沒綁定: <input type="text" placeholder="沒綁定" />
        </div>
        <div v-else class="form-group col-xs-12">
          v-else 沒綁定 <input type="text" placeholder="沒綁定" />
        </div>
				
				
				 
        <div class="form-group col-xs-12">
          使用v-bind:key (維持不綁定,不完美的解法,輸入的值在切換後消失)↓
        </div>
        <div v-if="isChecked" class="form-group col-xs-12">
          v-if 沒綁定但有key: <input type="text" placeholder="沒綁定" v-bind:key="1" />
        </div>
        <div v-else class="form-group col-xs-12">
          v-else 沒綁定但有key <input type="text" placeholder="沒綁定" v-bind:key="2" />
        </div>






        <div class="form-group col-xs-12">
          使用v-model解法(雙向綁定)↓
        </div>
        <div v-if="isChecked" class="form-group col-xs-12">
          v-if 有綁定<input type="text" placeholder="有綁定" v-model="myIf" />
        </div>
        <div v-else class="form-group col-xs-12"> 
          v-else 有綁定<input type="text" placeholder="有綁定" v-model="myElse" />
        </div>

        <div class="form-group col-xs-12">
          改用v-show解法(不綁定)↓
        </div> 
        <div v-show="isChecked" class="form-group col-xs-12">
          v-show: <input type="text" placeholder="沒綁定" />
        </div>
        <div v-show="isChecked===false" class="form-group col-xs-12">
          v-show isChecked===false <input type="text" placeholder="沒綁定" />
        </div>
				 
        <div class="form-group col-xs-12">
          改用v-show解法(有綁定)↓
        </div>
        <div v-show="isChecked" class="form-group col-xs-12">
          v-show: <input type="text" placeholder="有綁定" v-model="myShow" />
        </div>
        <div v-show="isChecked===false" class="form-group col-xs-12">
          v-show isChecked===false <input type="text" placeholder="有綁定" v-model="myShowFalse" />
        </div>

      </div>
    </div>

    <script src="https://code.jquery.com/jquery-3.3.1.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
    <!--Vue.js-->
    <script src="https://vuejs.org/js/vue.js"></script>
    <script type="text/javascript">
      let myApp = new Vue({
        el: "#myApp",
        data: {
          isChecked: true, //預設打勾
          myIf: "",
          myElse: "",
          myShow: "",
          myShowFalse: ""
        } 
      });

    </script>
  </body>

</html>

線上Demo:https://jsfiddle.net/ShadowKao/u8oyb1ch/

7. 使用 v-if 切換畫面,搭配 animate.css 、wow.js 插件使用的話...

由於 v-if 會重新渲染DOM元素,要小心會重新觸發CSS動畫的執行,如果不想讓CSS動畫重新執行的話,請改用 v-show

線上Demo:https://jsfiddle.net/ShadowKao/gzr1t8be/

 

 

 

資源參考

表单输入绑定

HTML Event Attributes