一、vue之分頁組件(含勾選、過濾、ES6寫法) <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>勾選和分頁組件之vue2.6.10版</title> <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script> <style> table{ border-collapse: collapse; border: 1px solid #cbcbcb; width:1000px; } table td,table th { padding: 5px; border: 1px solid #cbcbcb; } table thead { background-color: #e0e0e0; color: #000; text-align: left; } .filter{ width:998px; border:1px solid gray; padding:10px 0px; } .line{ display:flex } .group{ width:330px; } .label{ display: inline-block; width:120px; height: 24px; line-height: 24px; text-align: right; } .input{ display: inline-block; width:180px; height: 24px; line-height: 24px; border-radius: 3px; } .select{ display: inline-block; width:188px; height: 26px; line-height: 26x; border-radius: 3px; } </style> </head> <body> <div id="app"> <div style="padding-bottom:5px;color:red"> <button style="color:red" @click="checkDatasOne.getResultOfCheckAndFilter(divideDatasOne.isShowFilter,divideDatasOne.filterOptions)">獲取勾選和過濾結果</button> <span>{{checkDatasOne.toServerDatas}}</span> </div> <div style="padding-bottom:5px"> <img :src="checkDatasOne.stateAllPages&&checkDatasOne.allExcludedIds.length===0?checkImg.yes:checkImg.no" @click="checkDatasOne.clickAllPages(divideDatasOne.tableDatas)"/> <span>{{checkDatasOne.textAllPages}}</span> </div> <div style="padding-bottom:5px"> <button @click="divideDatasOne.toggleShowFilter()">{{divideDatasOne.isShowFilter?'關閉過濾':'使用過濾'}}</button> <button @click="divideDatasOne.emptyFilterOptions({value5:10})">清空過濾</button> <button @click="divideDatasOne.request(1,divideDatasOne.eachPageItemsNum)">刷新</button> </div> <div style="margin-bottom:5px" class="filter" v-show="divideDatasOne.isShowFilter"> <div class="line"> <div class="group"> <label class="label">標簽</label> <input class="input" type="text" v-model="divideDatasOne.filterOptions.value1" /> </div> <div class="group"> <label class="label">這就是長標簽</label> <input class="input" type="text" v-model="divideDatasOne.filterOptions.value2" /> </div> <div class="group"> <label class="label">標簽</label> <input class="input" type="text" v-model="divideDatasOne.filterOptions.value3" /> </div> </div> <div class="line" style="padding-top: 10px;"> <div class="group"> <label class="label">這就是長標簽</label> <input class="input" type="text" v-model="divideDatasOne.filterOptions.value4" /> </div> <div class="group"> <label class="label">下拉框</label> <select class="select" v-model="divideDatasOne.filterOptions.value5"> <option v-for="item in selectOptions" :value="item.back">{{item.front}}</option> </select> </div> <div class="group"> <label class="label"></label> <button style="width:188px;height:28px" @click="divideDatasOne.request(1,divideDatasOne.eachPageItemsNum)">過濾</button> </div> </div> </div> <div style="width:1000px"> <table> <thead> <tr> <th><img :src="checkDatasOne.stateThisPage?checkImg.yes:checkImg.no" @click="checkDatasOne.clickThisPage(divideDatasOne.tableDatas,divideDatasOne.allItemsNum)"/></th> <th>序號</th> <th>數據1</th> <th>數據2</th> <th>數據3</th> <th>數據4</th> <th>數據5</th> <th>數據6</th> </tr> </thead> <tbody> <tr v-for="(data,index) in divideDatasOne.tableDatas"> <td><img :src="data.state?checkImg.yes:checkImg.no" @click="checkDatasOne.clickSingleItem(data,divideDatasOne.tableDatas,divideDatasOne.allItemsNum)"/></td> <td>{{(divideDatasOne.nowPageNum-1)*divideDatasOne.eachPageItemsNum + (index+1)}} </td> <td>{{ data.key1 }}</td> <td>{{ data.key2 }}</td> <td>{{ data.key3 }}</td> <td>{{ data.key4 }}</td> <td>{{ data.key5 }}</td> <td>{{ data.key6 }}</td> </tr> </tbody> </table> </div> <divide-page :divide-datas="divideDatasOne" :check-datas="checkDatasOne" :fixed-datas="fixedDatas"></divide-page> </div> </body> <script> new Vue({ el: '#app', data(){ return { divideDatasOne:{ nowPageNum:0, allPagesNum:0, allItemsNum:0, eachPageItemsNum:0, tableDatas:[], filterOptions:{value5:10}, isShowFilter:false, otherDatas:{} }, checkDatasOne:{ idKey: 'id',//每條數據的唯一標志 stateThisPage: false,//當前頁所有項是否全選 allIncludedIds: [],//所有被選中數據的ID構成的數組 allExcludedIds: [],//所有沒被選中數據的ID構成的數組 textAllPages: '全選未啟用,沒有選擇任何項!',//復選框被點擊后的提示文字。 stateAllPages: false,//復選框被點擊后的提示文字。 toServerDatas: null, }, } }, methods: { }, created(){ this.fixedDatas = {}; this.selectOptions = [ { back: 10, front: '來' }, { back: 20, front: '來自於' }, { back: 30, front: '來自於國內' }, { back: 40, front: '來自於國內攻擊' }, { back: 50, front: '來自於國內攻擊-2' } ]; this.checkImg = { yes: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAADqADAAQAAAABAAAADgAAAAC98Dn6AAAA+UlEQVQoFZWSMU4DMRBF/584G7QSRcIxuAZKEykNEiUVHVTQRaKh4AIcgAvQpkukVDlBOAYNSGSlXXuwpViyYYFdS9aMZ/6bsezh5HZ3T2KhqkfosEhWqnjkyd1u3xWKdQMsfaEAB0Zilf8swfdU0w0klmpGpz1BvpbHcklbPf8Okts0CfJtWBTz/Yc++Jc8S3PZVQfKGwiuvMD6XYsMzm1dT/1jXKdQ8E0asHRrAzOzbC6UGINWHPQp1UQ/6wjF2LpmJSKfhti4Bi8+lhWP4I+gAqV1uqSi8j9WRuF3m3eMWVUJBeKxzUoYn7bEX7HDyPmB7QEHbRjyL+/+VnuXDUFOAAAAAElFTkSuQmCC', no: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAADqADAAQAAAABAAAADgAAAAC98Dn6AAAAbklEQVQoFWM8c+ZMLQMDQxUQcwAxMeAHUFEbC5CoYmNj02ZmZn5FjK6/f/+K/fr16ypIIwdIk7a29hdiNF69ehWkjIOJGMXY1IxqxBYqULEhFDiglPMDlIygKQKPryBSILUgPSCNbaC0B6RJSuQAbowizhJuOsAAAAAASUVORK5CYII=', } }, components: { dividePage: { props: { divideDatas: { type: Object, default: {} }, checkDatas: { type: Object, default: {} }, fixedDatas: { type: Object, default: {} } }, template: ` <div v-show="divideDatas.allPagesNum>=1" style="display:flex;width:1000px;margin-top:20px;"> <div> <button v-show="divideDatas.allPagesNum>10" @click="clickDividePage('front') " :disabled="divideDatas.nowPageNum===1" >上一頁</button> <button :disabled="number==='...'" v-for="number in divideArray" @click="clickDividePage(number)" :style="{marginRight:'5px',color:number===divideDatas.nowPageNum?'red':'gray'}" >{{ number }}</button> <button v-show="divideDatas.allPagesNum>10" @click="clickDividePage('back')" :disabled="divideDatas.nowPageNum===divideDatas.allPagesNum" >下一頁</button> </div> <div style="display:flex; flex:1; justify-content:flex-end;"> <div style="margin-right:20px;"> <span>轉到第</span> <input type="text" v-model="customString" @keydown="clickDividePage('leap',$event)" style="width:30px;"> <span>頁</span> <button @click="clickDividePage('leap',{which:13})">Go</button> </div> <div> <span>每頁顯示</span> <select v-model="divideDatas.eachPageItemsNum" @change="selectChange(divideDatas.eachPageItemsNum)"> <option v-for="item in numOptions" :value="item.back">{{item.front}}</option> </select> <span>條,</span> </div> <div> <span>{{frontMoreText}}</span> <span>{{totalText}}</span> <span>{{divideDatas.allItemsNum}}</span> <span>{{totalUnit}}</span> <span>{{backMoreText}}</span> </div> </div> </div `, data() { return { customString:'' } }, created(){ var that = this; //1、請求配置 this.url = this.fixedDatas.url || ''; this.method = this.fixedDatas.method || 'post'; this.isShowParams = this.fixedDatas.isShowParams || false;//顯式還是隱式傳參。有時需要在請求發出前手動改變。 //2、響應配置(前端通過這個配置,獲取后台的數據) this.nowPageNum = this.fixedDatas.nowPageNum || 'nowPageNum';//來自服務器的當前頁碼 this.allPagesNum = this.fixedDatas.allPagesNum || 'allPagesNum';//來自服務器的所有頁頁數 this.allItemsNum = this.fixedDatas.allItemsNum || 'allItemsNum';//來自服務器的所有頁數據數 this.eachPageItemsNum = this.fixedDatas.eachPageItemsNum || 'eachPageItemsNum';//來自服務器的每頁最多數據數 this.tableDatas = this.fixedDatas.tableDatas || 'tableDatas';//來自服務器的表格數據 //3、以下配置使用哪種轉圈方式(前端根據需要決定,不受后台影響) this.partCircle = this.fixedDatas.partCircle;//局部是否轉圈。this.fixedDatas.partCircle=$scope.partCircle={isShow =false}。 this.isUsePartCircle = this.fixedDatas.isUsePartCircle;//局部是否轉圈,由當前頁的一個變量控制 this.isUseWholeCircle = this.fixedDatas.isUseWholeCircle;//全局是否轉圈,由本項目的一個服務控制 //4、初始化以下數據,供頁面使用(前端根據需要決定,不受后台影響) this.frontMoreText = this.fixedDatas.frontMoreText || "";//('文字 ')或者("文字 "+result.numOne+" 文字 ") this.totalText = this.fixedDatas.totalText || "";//'共' this.totalUnit = this.fixedDatas.totalUnit || '條';//總數據的單位 this.backMoreText = this.fixedDatas.backMoreText || "";//(' 文字')或者("文字 "+result.numThree+" 文字") this.numOptions = [ { back: 10, front: 10 }, { back: 20, front: 20 }, { back: 30, front: 30 }, { back: 40, front: 40 }, { back: 50, front: 50 } ]; this.request = this.divideDatas.request = function (nowPageNum,eachPageItemsNum) { //此處向后台發送請求, //1、返回正確結果result var data=[]; var allItemsNum = 193; var nowPageNum = nowPageNum||1; var eachPageItemsNum = eachPageItemsNum||10; var allPagesNum = Math.ceil(allItemsNum/eachPageItemsNum); for(var i=1;i<=allItemsNum;i++){ var obj={ id:'id'+i, key1:'數據'+(i+0), key2:'數據'+(i+1), key3:'數據'+(i+2), key4:'數據'+(i+3), key5:'數據'+(i+4), key6:'數據'+(i+5), key7:'數據'+(i+6), }; data.push(obj) } var tableDatas = data.slice((nowPageNum-1)*eachPageItemsNum,nowPageNum*eachPageItemsNum); if(that.divideDatas.trueCb){ that.divideDatas.trueCb() }else{ that.customString = nowPageNum; that.divideDatas.tableDatas = tableDatas; that.divideDatas.nowPageNum = nowPageNum; that.divideDatas.allPagesNum = allPagesNum; that.divideDatas.allItemsNum = allItemsNum; that.divideDatas.eachPageItemsNum = eachPageItemsNum; if(that.checkDatas && that.checkDatas.signCheckbox){ that.checkDatas.signCheckbox(that.divideDatas.tableDatas) } } that.createDividePage(); //2、返回錯誤結果 if(that.divideDatas.errorCb){ that.divideDatas.errorCb() } }; if (!this.divideDatas.isNoInit) { this.request(1,this.divideDatas.eachPageItemsNum); }; this.divideDatas.toggleShowFilter = function () { this.isShowFilter = !this.isShowFilter; if (!this.isShowFilter) { this.request(1,that.divideDatas.eachPageItemsNum); } }; this.divideDatas.emptyFilterOptions = function (extraObject) { //清空選項時,所有值恢復成默認 for(var key in this.filterOptions){ this.filterOptions[key] = undefined; }; if (extraObject) { //小部分選項的默認值不是undefined for(var key in extraObject){ this.filterOptions[key] = extraObject[key]; }; }; this.request(1,that.divideDatas.eachPageItemsNum); }; this.checkDatas.init=function(){//點擊“刷新”、“過濾”、“清除過濾”時執行 this.idKey = idKey ? idKey : 'id'; this.allIncludedIds = []; this.allExcludedIds = []; this.textAllPages = '全選未啟用,沒有選擇任何項!'; this.stateAllPages = false; this.stateThisPage = false; }; this.checkDatas.clickAllPages = function (itemArray) {//所有頁所有條目全選復選框被點擊時執行的函數 if(this.stateAllPages){ if(this.allExcludedIds.length>0){ this.stateAllPages = true; this.stateThisPage = true; this.textAllPages= '全選已啟用,沒有排除任何項!'; itemArray.forEach(function (item) { item.state = true; }); }else if(this.allExcludedIds.length==0){ this.stateAllPages = false; this.stateThisPage = false; this.textAllPages= '全選未啟用,沒有選擇任何項!'; itemArray.forEach(function (item) { item.state = false; }); } }else{ this.stateAllPages = true; this.stateThisPage = true; this.textAllPages= '全選已啟用,沒有排除任何項!'; itemArray.forEach(function (item) { item.state = true; }); } this.allExcludedIds = []; this.allIncludedIds = []; }; this.checkDatas.clickThisPage = function (itemsArray,allItemsNum) {//當前頁所有條目全選復選框被點擊時執行的函數 var that = this; this.stateThisPage = !this.stateThisPage itemsArray.forEach(function (item) { item.state = that.stateThisPage; if (item.state) { that.delID(item[that.idKey], that.allExcludedIds); that.addID(item[that.idKey], that.allIncludedIds); } else { that.delID(item[that.idKey], that.allIncludedIds); that.addID(item[that.idKey], that.allExcludedIds); } }); if(this.stateAllPages){ if(this.stateThisPage && this.allExcludedIds.length === 0){ this.textAllPages = '全選已啟用,沒有排除任何項!'; }else{ this.textAllPages = '全選已啟用,已排除'+ this.allExcludedIds.length + '項!排除項的ID為:' + this.allExcludedIds; } }else{ if(!this.stateThisPage && this.allIncludedIds.length === 0){ this.textAllPages='全選未啟用,沒有選擇任何項!'; }else{ this.textAllPages = '全選未啟用,已選擇' + this.allIncludedIds.length + '項!選擇項的ID為:' + this.allIncludedIds; } } }; this.checkDatas.clickSingleItem = function (item, itemsArray, allItemsNum) {//當前頁單個條目復選框被點擊時執行的函數 var that = this; item.state = !item.state; if (item.state) { this.stateThisPage = true; this.addID(item[this.idKey], this.allIncludedIds); this.delID(item[this.idKey], this.allExcludedIds); itemsArray.forEach( function (item) { if (!item.state) { that.stateThisPage = false; } }); } else { this.stateThisPage = false; this.addID(item[this.idKey], this.allExcludedIds); this.delID(item[this.idKey], this.allIncludedIds); } if(this.stateAllPages){ if(this.stateThisPage && this.allExcludedIds.length === 0){ this.textAllPages = '全選已啟用,沒有排除任何項!'; }else{ this.textAllPages = '全選已啟用,已排除'+ this.allExcludedIds.length + '項!排除項的ID為:' + this.allExcludedIds; } }else{ if(!this.stateThisPage && this.allIncludedIds.length === 0){ this.textAllPages='全選未啟用,沒有選擇任何項!'; }else{ this.textAllPages = '全選未啟用,已選擇' + this.allIncludedIds.length + '項!選擇項的ID為:' + this.allIncludedIds; } } }; this.checkDatas.signCheckbox = function (itemsArray) {//標注當前頁被選中的條目,在翻頁成功后執行。 var that = this; if(this.stateAllPages){ this.stateThisPage = true; itemsArray.forEach(function (item) { var thisID = item[that.idKey]; var index = that.allExcludedIds.indexOf(thisID); if (index > -1) { item.state = false; that.stateThisPage = false; } else { item.state = true; } }); }else{ this.stateThisPage = true; itemsArray.forEach( function (item) { var thisID = item[that.idKey]; var index = that.allIncludedIds.indexOf(thisID); if (index === -1) { item.state = false; that.stateThisPage = false; } }); } }; this.checkDatas.addID = function (id, idArray) { var index = idArray.indexOf(id); if (index === -1) { idArray.push(id);//如果當前頁的單項既有勾選又有非勾選,這時勾選當前頁全選,需要這個判斷,以免重復添加 } }; this.checkDatas.delID = function (id, idArray) { var index = idArray.indexOf(id); if (index > -1) { idArray.splice(index, 1) } }; this.checkDatas.getResultOfCheckAndFilter = function (isShowFilter,filterOptions) {//獲取發送給后台的所有參數。 var toServerDatas; var allIncludedIds = that.deepClone(this.allIncludedIds); var allExcludedIds = that.deepClone(this.allExcludedIds); if (!this.stateAllPages) { if (allIncludedIds.length === 0) { //return 彈窗告知:沒有勾選項 } toServerDatas = { isSelectAll: false, allIncludedIds: allIncludedIds, } }else { toServerDatas = { //exclude isSelectAll: true, allExcludedIds: allExcludedIds, }; } if (isShowFilter) { for(var key in filterOptions){ toServerDatas[key]=filterOptions[key] } } this.toServerDatas=toServerDatas;//這行代碼在實際項目中不需要 return toServerDatas; } }, methods: { deepClone : function (arrayOrObject) { function isArray(value) { return {}.toString.call(value) === "[object Array]"; } function isObject(value) { return {}.toString.call(value) === "[object Object]"; } var target = null; if (isArray(arrayOrObject)) target = []; if (isObject(arrayOrObject)) target = {}; for (var key in arrayOrObject) { var value = arrayOrObject[key]; if (isArray(value) || isObject(value)) { target[key] = deepClone(value); } else { target[key] = value; } } return target; }, selectChange:function(eachPageItemsNum){ this.divideDatas.eachPageItemsNum = eachPageItemsNum; this.request(1,eachPageItemsNum); }, createDividePage : function () { var divideArray = []; var allPagesNum = this.divideDatas.allPagesNum; var nowPageNum = this.divideDatas.nowPageNum; if (allPagesNum >= 1 && allPagesNum <= 10) { for (var i = 1; i <= allPagesNum; i++) { divideArray.push(i); } } else if (allPagesNum >= 11) { if (nowPageNum > 6) { divideArray.push(1); divideArray.push(2); divideArray.push(3); divideArray.push('...'); divideArray.push(nowPageNum - 1); divideArray.push(nowPageNum); } else { for (i = 1; i <= nowPageNum; i++) { divideArray.push(i); } } // 以上當前頁的左邊,以下當前頁的右邊 if (allPagesNum - nowPageNum >= 6) { divideArray.push(nowPageNum + 1); divideArray.push(nowPageNum + 2); divideArray.push('...'); divideArray.push(allPagesNum - 2); divideArray.push(allPagesNum - 1); divideArray.push(allPagesNum); } else { for (var i = nowPageNum + 1; i <= allPagesNum; i++) { divideArray.push(i); } } } this.divideArray = divideArray; }, clickDividePage : function (stringOfNum, event) { var allPagesNum = this.divideDatas.allPagesNum; var nowPageNum = this.divideDatas.nowPageNum; if (stringOfNum === 'front' && nowPageNum != 1) { nowPageNum--; } else if (stringOfNum === 'back' && nowPageNum != allPagesNum) { nowPageNum++; } else if (stringOfNum === 'leap') { if (event.which != 13) return;//不攔截情形:(1)聚焦輸入框、按“Enter”鍵時;(2)點擊“GO”時 var customNum = Math.ceil(parseFloat(this.customString)); if (customNum < 1 || customNum == 'NaN') { nowPageNum = 1;//不給提示 } else if(customNum > allPagesNum) { nowPageNum = allPagesNum;//不給提示 } else { nowPageNum = customNum; } } else { nowPageNum = Math.ceil(parseFloat(stringOfNum)); } this.request(nowPageNum,this.divideDatas.eachPageItemsNum); }, } } }, }) </script> </html> 附:vue之分頁組件(含勾選、過濾、ES6寫法) <template> <div v-show="divideDatas.allPagesNum>=1" style="display:flex;width:1000px;margin-top:20px;"> </div> </template> <script> import comTab from '@/components/ComTab' export default { name: 'dividePage', components: { comTab }, props: { }, data() { return { } }, created(){ }, methods: { } } </script> <style rel="stylesheet/scss" lang="scss" scoped> </style> 二、Vue框架源碼(Observer、Dep和Watcher) 1、自動執行混入 (1)執行initMixin(Vue);stateMixin(Vue);eventsMixin(Vue);lifecycleMixin(Vue);renderMixin(Vue); (2)其中執行initMixin(Vue),產生Vue.prototype._init 2、手動執行類 (1)執行new Vue(options);執行this._init(options);執行initState(vm);initLifecycle(vm);initEvents(vm);initRender(vm);callHook(vm,"beforeCreate");initInjections(vm);initProvide(vm);callHook(vm,"created"); (2)其中執行initState(vm);執行initProps();initData();initComputed();initMethods();initWatch();vm.$mount(vm.$options.el) 3、Vue響應式(Observer、Dep和Watcher) (1)initProps(vm,opts.props)用defineReactive$$1將屬性定義為響應式; (2)initData(vm)用observe將數據定義為響應式,vue數據和Observer實例互相綁定; (3)initComputed(vm,opts.computed)執行new Watcher,vue實例和Watcher實例互相綁定,不執行this.get函數,用vm._computedWatchers[key]存儲Watcher實例,用defineComputed定義計算屬性為只讀(get)響應式,頁面渲染時才會觸發get,此時所用的值都已確定; (4)initWatch或mountComponent執行,new Watcher執行,vue實例和Watcher實例互相綁定,執行this.get函數,給Dep.target賦值,執行this.getter函數,觸發響應式的get函數,獲取value的舊值,通過dep.depend把watcher實例存放到dep.subs里; (5)數據變化,觸發響應式的set函數,通過dep.notify執行dep.subs里watcher實例的cb函數; (6)頁面初次渲染時,用初始值和由初始值計算而來的計算屬性值渲染頁面;更新初始值時,執行watch監聽函數,用更新值和由更新值計算而來的新計算屬性值渲染頁面。 4、給數組方法綁定響應式 var methodsToPatch = ["push","pop","shift","unshift","splice","sort","reverse"]; var arrayProto = Array.prototype; var arrayMethods = Object.create(arrayProto); {}.__proto__ = arrayMethods.__proto__ = arrayProto; methodsToPatch.forEach(function (method) { def(arrayMethods, method, function mutator() {}); }); ['a','b','c'].__proto__ = arrayMethods; function def(obj, key, val, enumerable) { Object.defineProperty(obj, key, { configurable: true, enumerable: !!enumerable, value: val, writable: true, }); } Object.defineProperty(myObj, "key", { configurable: false, enumerable: false, get: function () { console.log(this); return key+2; }, set: function (value) { console.log(this); key = value + 1; }, }); 三、Vue.set應用實例 Vue框架只對數組方法中的'push','pop','shift','unshift','splice','sort','reverse'實現了響應式。通過索引改變數組,沒有執行發布函數,沒法執行訂閱函數,需要通過Vue.set來執行發布函數,實現響應式。 <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app2"> <p v-for="item in items" :key="item.id"> {{item.message}} </p> <button class="btn" @click="btn2Click()">動態賦值</button><br /> <button class="btn" @click="btn3Click()">為data新增屬性</button> </div> </body> </html> <script> var vm2 = new Vue({ el: "#app2", data: { items: [ { message: "Test one", id: "1" }, { message: "Test two", id: "2" }, { message: "Test three", id: "3" } ] }, methods: { btn2Click: function () { Vue.set(this.items, 0, { message: "Change Test", id: '10' }) }, btn3Click: function () { var itemLen = this.items.length; Vue.set(this.items, itemLen, { message: "Test add attr", id: itemLen }); } } }); </script> <!DOCTYPE html> <html> <head> <title></title> <meta charset="utf-8"> <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script> </head> <body> <div id="box">{{msg}}||{{reMsg}}</div> <script type="text/javascript"> var vm = new Vue({ el:'#box', data:{ msg:'12345' }, computed:{ reMsg:function(instance){ console.log(instance===this);//true return this.msg.split('').reverse().join('') } } }); </script> </body> </html> 四、基於ElementUI的vue自定義組件子改父 1、通過屬性傳函數參數來實現 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <title>子改父:通過屬性傳參</title> <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script> <script src="https://cdn.bootcss.com/element-ui/2.10.1/index.js"></script> <link href="https://cdn.bootcss.com/element-ui/2.10.1/theme-chalk/index.css" rel="stylesheet"> <style> #app{ display: flex; justify-content: space-between; } .parent, .child{ width: 45%; } .el-card{ height: 100%; } </style> </head> <body> <div style="margin: 30px 0;">本案例改編自https://www.bbsmax.com/A/kjdwmRaOJN/</div> <div style="margin-bottom: 30px;"> <div>總邏輯 </div> <div>父組件通過屬性傳參,給子組件傳值 </div> <div>父組件通過屬性傳參,給子組件傳屬性函數 </div> <div>觸發子組件的某個事件,執行屬性函數,改變父組件的值 </div> </div> <div id="app"> <div class="parent"> <el-card> <div slot="header"> <span>父組件</span> </div> <el-input v-model="ParentMsg"></el-input> <el-button type="primary" @click="changeChild" style="margin-top: 44px">父組件改變子組件</el-button> </el-card> </div> <div class="child"> <el-card> <div slot="header"> <span>子組件</span> </div> <child :self-msg="childMsg" :fn="changeParent"></child> </el-card> </div> </div> </body> <script> new Vue({ el: '#app', data(){ return { ParentMsg:'父組件的內容', childMsg:'父組件傳給子組件的內容' } }, methods: { changeParent(data){ this.ParentMsg = data, this.childMsg = '子-組件傳給子組件的內容' }, changeChild(){ this.ParentMsg = '父-組件傳給父組件的內容', this.childMsg = '父-組件傳給子組件的內容' } }, components: { child:{ props: { selfMsg: { type: String, default: '' }, fn: { type: Function, default: function(){} } }, template: ` <div> <p>{{selfMsg}}</p> <el-button type='primary' @click='fromChild' style='margin-top: 30px'>子組件改變父組件</el-button> </div> `, data () { return { } }, methods:{ fromChild () { this.fn('子-組件傳給父組件的內容') } } } }, }) </script> </html> 2、通過屬性傳自定義事件來實現 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <title>Title</title> <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script> <script src="https://cdn.bootcss.com/element-ui/2.10.1/index.js"></script> <link href="https://cdn.bootcss.com/element-ui/2.10.1/theme-chalk/index.css" rel="stylesheet"> <style> #app{ display: flex; justify-content: space-between; } .parent, .child{ width: 45%; } .el-card{ height: 100%; } </style> </head> <body> <div style="margin: 30px 0;">本案例改編自https://www.bbsmax.com/A/kjdwmRaOJN/</div> <div style="margin-bottom: 30px;"> <div>總邏輯 </div> <div>父組件通過屬性傳參,給子組件傳值 </div> <div>父組件通過屬性傳參,給子組件傳自定義事件及執行函數 </div> <div>觸發子組件的某個事件,發射自定義事件,並給執行函數傳參,改變父組件的值 </div> </div> <div id="app"> <div class="parent"> <el-card> <div slot="header"> <span>父組件</span> </div> <el-input v-model="ParentMsg"></el-input> <el-button type="primary" @click="changeChild" style="margin-top: 44px">父組件改變子組件</el-button> </el-card> </div> <div class="child"> <el-card> <div slot="header"> <span>子組件</span> </div> <child :self-msg="childMsg" @from-child="changeParent"></child> </el-card> </div> </div> </body> <script> new Vue({ el: '#app', data(){ return { ParentMsg:'父組件的內容', childMsg:'父組件傳給子組件的內容' } }, methods: { changeParent(data){ this.ParentMsg = data, this.childMsg = '子-組件傳給子組件的內容' }, changeChild(){ this.ParentMsg = '父-組件傳給父組件的內容', this.childMsg = '父-組件傳給子組件的內容' } }, components: { child:{ props: { selfMsg: { type: String, default: '' } }, template: ` <div> <p>{{selfMsg}}</p> <el-button type='primary' @click='fromChild' style='margin-top: 30px'>子組件改變父組件</el-button> </div> `, data () { return { } }, methods:{ fromChild () { this.$emit('from-child', '子-組件傳給父組件的內容') } } } }, }) </script> </html> 五、el-dialog三層彈窗 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <title>vue2.6.10組件el-dialog之三層彈窗</title> <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script> <script src="https://cdn.bootcss.com/element-ui/2.10.1/index.js"></script> <link href="https://cdn.bootcss.com/element-ui/2.10.1/theme-chalk/index.css" rel="stylesheet"> <style> #app{ display: flex; justify-content: space-between; } .parent, .child{ width: 45%; } .el-card{ height: 100%; } </style> </head> <body> <div id="app"> <el-button type="text" @click="outerVisible = true">點擊打開外層彈窗</el-button> <!-- 以下是外層 --> <el-dialog width="70%" title="外層" :visible.sync="outerVisible"> 這是外層 <!-- 以下是中層 --> <el-dialog width="50%" title="中層" :visible.sync="middleVisible" append-to-body> 這是中層 <!-- 以下是內層 --> <el-dialog width="30%" title="內層" :visible.sync="innerVisible" append-to-body> 這是內層 <div slot="footer" class="dialog-footer"> <el-button @click="innerVisible = false">關閉內層</el-button> <el-button type="primary" @click="innerVisible = false">關閉內層</el-button> </div> </el-dialog> <!-- 以上是內層 --> <div slot="footer" class="dialog-footer"> <el-button @click="middleVisible = false">關閉中層</el-button> <el-button type="primary" @click="innerVisible = true">打開內層</el-button> </div> </el-dialog> <!-- 以上是中層 --> <div slot="footer" class="dialog-footer"> <el-button @click="outerVisible = false">關閉外層</el-button> <el-button type="primary" @click="middleVisible = true">打開中層</el-button> </div> </el-dialog> <!-- 以上是外層 --> </div> </body> <script> new Vue({ el: '#app', data() { return { outerVisible: false, middleVisible: false, innerVisible: false }; }, methods: { }, components: { }, }) </script> </html> 六、elementUI各彈窗的區別 1、第1組(3秒鍾后自動消失) (1)Message 消息提示,常用於主動操作后的反饋提示。 (2)Notification 通知,常用於系統級通知的被動提醒。 2、第2組(點擊確認后消失) (1)MessageBox 彈窗,模擬系統的消息提示框alert、confirm和prompt而實現的一套模態對話框組件,用於消息提示、確認消息和提交內容。 (2)Dialog 對話框,彈出較為復雜的內容. 3、第3組(非懸停時消失) (1)Tooltip 文字提示,常用於展示鼠標hover時的提示信息。 (2)Popover 彈出框,與Tooltip類似。 七、Vue-CLI 1、Vue CLI一套基於插件的架構,package.json里的依賴都是以@vue/cli-plugin-開頭的。 2、插件可以修改 webpack 的內部配置,也可以向 vue-cli-service 注入命令。 3、在項目創建的過程中,絕大部分列出的特性都是通過插件來實現的。 4、用config.module.rule('svg').exclude.add(resolve('src/icons')).end()對vue-cli-4.0里內置的'svg'模塊規則進行修改 5、用config.module.rule('icons').test(/\.svg$/).include.add(resolve('src/icons')).end()定義並向vue-cli-4.0里注入名為'icons'的模塊規則