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'的模块规则
 
原文地址:https://www.cnblogs.com/gushixianqiancheng/p/13392540.html