• 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;
          1000px;
        }
        table td,table th {
          padding: 5px;
          border: 1px solid #cbcbcb;
        }
        table thead {
          background-color: #e0e0e0;
          color: #000;
          text-align: left;
        }
        .filter{
          998px;
          border:1px solid gray;
          padding:10px 0px;
        }
        .line{
          display:flex
        }
        .group{
          330px;
        }
        .label{
          display: inline-block;
          120px;
          height: 24px;
          line-height: 24px;
          text-align: right;
        }
        .input{
          display: inline-block;
          180px;
          height: 24px;
          line-height: 24px;
          border-radius: 3px;
        }
        .select{
          display: inline-block;
          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="188px;height:28px" @click="divideDatasOne.request(1,divideDatasOne.eachPageItemsNum)">过滤</button>
          </div>
        </div>
      </div>
      <div style="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;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="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;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{
           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{
           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{
           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'的模块规则
     
  • 相关阅读:
    mysql常见的优化方法
    Mac 怎么通过自带终端连接linux服务器
    基于 appium 的 UI 自动化测试
    sourcetree在mac上的使用
    mac下git安装和使用
    mac 上更改环境变量
    Mac环境下svn的使用
    jira常用配置
    influxDB基本操作
    Collectd 和 InfluxDB 的部署和使用
  • 原文地址:https://www.cnblogs.com/gushixianqiancheng/p/13392540.html
Copyright © 2020-2023  润新知