16、Vue之分頁組件(含勾選、過濾、ES6寫法),Vue框架源碼(Observer、Dep和Watcher),Vue.set應用實例,基於ElementUI的vue自定義組件、el-dialog三層彈窗、各彈窗的區別、Vue-CLI


一、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'的模塊規則
 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM