多次遇到的问题

109 阅读7分钟

一、div上下左右居中
1、用绝对定位

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .box{
            position:absolute/fixed;
            left:0;
            right:0;
            top:0;
            bottom:0;
            margin:auto;
        }
    </style>
</head>
<body>
    <div class="box"></div>
</body>
</html>

2、用margin负值

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .box{
           width:200px;
           height: 200px;
           position: absolute;
           left:50%;
           top:50%;
           margin-left:-100px;
           margin-top:-100px;
        }
    </style>
</head>
<body>
    <div class="box"></div>
</body>
</html>

3、用动画的方式即:css3 transform

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .box{
           position: absolute;
           left:50%;
           top:50%;
           transform: translate(-50%, -50%);
        }
    </style>
</head>
<body>
    <div class="box"></div>
</body>
</html>

4、用flex布局的方式

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .box{
           display:flex;
           align-items:center;
           justify-content:center;
        }
    </style>
</head>
<body>
    <div class="box"></div>
</body>
</html>

5、用table-cell的方式

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .box{
          display: table-cell;
          vertical-align: middle;
          text-align: center;
        }
    </style>
</head>
<body>
    <div class="box"></div>
</body>
</html>

二、let、const、var的区别

1、var是挂载在window的变量相当于一个全局作用域,无论在什么地方都能够访问到,let和const则不是挂载 window下的变量,用它们定义的变量相当于一个块级作用域,只能在特定的区域内才能访问到。

2、var有变量提升,没有暂时性死区,可以在定义前调用返回的值是undefined,let和const则没有变量提升,有暂时性死区,不能在定义前调用,否则let和const都会会出现这样的报错:Uncaught ReferenceError: Cannot access 'a' before initialization

三、闭包和它的解决的问题

闭包: 就是能够读取其他函数内部变量的函数,即一个函数内部能够使用另外一个函数的局部变量,因为通俗来说就是可以在函数外部调用函数内部的变量。因为正常的情况下函数外部是访问不到函数内部作用域变量的。

它的优点是:可以隔离作用域,不造成全局污染,可以让垃圾回收机制不会回收这个变量,让他能一直被标记为被引用。

缺点: 由于闭包长期驻留内存,则会导致大量内存永远不会被释放。

如何释放内存: 将暴露外部的闭包变量置为 null

四、css优先级

1.ID 选择器, 如 #id{}

2.类选择器, 如 .class{}

3.属性选择器, 如 a[href="segmentfault.com"]{}

4.伪类选择器, 如 :hover{}

5.伪元素选择器, 如 ::before{}

6.标签选择器, 如 span{}

7.通配选择器, 如 *{}

!important>内联样式 > ID 选择器 > 类选择器 = 属性选择器 = 伪类选择器 > 标签选择器 = 伪元素选择器 > 通配选择器

五、flex的常见属性

flex常见的属性有
flex-direction :设置主轴的方向
justify-content :设置主轴上的子元素排列方式
flex-wrap :设置子元素是否换行
align-content :设置侧轴上的子元素排列方式(多行)
align-items :设置侧轴上的子元素排列方式(单行)
flex-flow复合属性,相当于同时设置了flex-direction和flex-wrap

六、常见的js数据类型

js常见的数据类型有:

原始类型即基本数据类型:undefined、null、number、boolean、string、symbol

对象类型也可以说是复杂数据类型即引用数据类型:Object、array、function、data等

七、常见的获取dom的方法
常见获取dom的方法是:
通过ID获取(getElementById)
通过name属性(getElementsByName)
通过标签名(getElementsByTagName)
通过类名(getElementsByClassName)
获取html的方法(document.documentElement)
获取body的方法(document.body)
通过选择器获取一个元素(querySelector)(开发常用)
通过选择器获取一组元素(querySelectorAll)(开发常用)

八、数组去重的方法

我常用的去重方式有三种:

第一种是ES6中的new Set()方法

let arr=[1,2,5,4,9,5,54,56,5,8,52,479,52,45,96,2,8,2,85,2,8]
   console.log(...new Set(arr));//1 2 5 4 9 54 56 8 52 479 45 96 85

2.使用双重for循环方法去重

 //数组去重
    function disTinct(arr) {
        var ans = []
        for (var i = arr.length - 1; i >= 0; i--) {
            var nb = 0 //没重复为0.有重复不为0
            for (var j = i - 1; j >= 0; j--) {
                if (arr[i] == arr[j]) {
                    nb = 1
                    break
                }
            }
            if (nb == 0) {
                ans.unshift(arr[i])
            }
        }
        return ans
    }

3.用for循环+indexOf方法去重

 //数组去重(indexOf)
    function unique(arr) {
        var res = []
        for (var i = 0; i < arr.length; i++) {
            if (res.indexOf(arr[i]) == -1) {
                res.push(arr[i])
            }
        }
        return res
    }

4.filter + indexOf

var arr = [1, 2, 3,4 ,5,6, 4, 3, 8, 1]
    // 数组去重:
    // 方法6 :filter + findIndex
    function newArrFn (arr) {
      // 利用indexOf检测元素在数组中第一次出现的位置是否和元素现在的位置相等,
      // 如果相等,说明数组中没有重复的
      return Array.prototype.filter.call(arr, function (item, index) { 
        return arr.indexOf(item) === index
       })
    }
    console.log(newArrFn(arr));

九、rem和em的区别
rem 和 em 都是相对单位,主要参考的标签不同: rem 是相对于根字号,即相对于html标签的 font-size 实现的,浏览 器默认字号是 font-size:16px em:是相对于父元素标签的字号,和百分比%类似,%也是相对于父级的, 只不过是%相对于父级宽度的,而 em 相对于父级字号的

十、清除浮动的方法

我所知道的清除浮动的方法:

1.使用clear:both清除浮动

2.父元素添加overflow:hidden或overflow:auto

3.使用clear:left清除左浮动

4.使用clear:right清除有浮动

5.为父元素设置高度

十一、vue的生命周期
vue的生命周期可以分为四个阶段
1.创建、2.挂载、3.更新、4.销毁
创建之前: beforeCreate(){},
创建之后:created(){},
挂载之前:beforeMount(){},
挂载之后:mounted(){},
更新之前:beforeUpdate(){},
更新之后:updated(){},
销毁之前:beforeDestroy(){},
销毁之后:destroyed(){},

十二、activated和deactivated

activated被 keep-alive 缓存的组件激活时调用。

deactivated被 keep-alive 缓存的组件失活时调用。

十三、eventloop是什么

eventloop是 js的一个底层运行原理,js是单线程的,但是也有一些耗时任务, 会影响执行效率.代码都在主线程中执行,当遇见你像ajax请求.setTimeout 定时器时候,会 单独开启异步线程.异步线程耗时之后会推入异步队列中等待执行.然后当主线程执行完毕 之后.会到异步队列中取出到主线程中执行.然后再去异步队列中取第二个.这个来回取的过程就是事件循环(eventLoop)

十四、跨域的方式有那些
我知道的有三种:

一、是 jsonp

jsonp 实现原理:主要是利用动态创建 script 标签请求后端接口 地址,然后传递 callback 参数,后端接收 callback,后端经过 数据处理,返回 callback 函数调用的形式,callback 中的参数 就是 json

二、 通过代理的方式(前端代理和后端代理)

前端代理我在 vue 中使用那个 vue.config.js 里面配置一个 proxy,里面有个 target 属性指向跨域链接.修改完重启项目就可以了.实际上就是启动了一个代理服务器.绕开同源策略,在请求的时候,通过代理服务器获取到数据再转给浏览器

三、 是 CORS

CORS 全称叫跨域资源共享,主要是后台工程师设置后端代码来达 到前端跨域请求的

十五、封装一个模态框

用一个盒子包含另一个盒子即:

 <div class="mtk">
        <div></div>
    </div>

随后用固定定位的方式或者固定定位和flex布局结合的方式完成模态框即:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        *{
            padding: 0;
            margin: 0;
            list-style: none;
            box-sizing: border-box;
        }
        html,body{
            width: 100%;
            height: 100%;
        }
        .mtk{
            width: 100%;
            height: 100%;
            position: fixed;
            left: 0;
            top: 0;
            background: rgba(0, 0, 0, 0.5);
        }
          .box{
            width: 100px;
            height: 100px;
            background: gold;
            position: fixed;
            left: 50%;
            top: 50%;
        }
    </style>
</head>
<body>
    <div class="mtk">
        <div></div>
    </div>
</body>
</html>

或者:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        *{
            padding: 0;
            margin: 0;
            list-style: none;
            box-sizing: border-box;
        }
        html,body{
            width: 100%;
            height: 100%;
        }
        .mtk{
            width: 100%;
            height: 100%;
            position: fixed;
            left: 0;
            top: 0;
            background: rgba(0, 0, 0, 0.5);
            display: flex;
            align-items: center;
            justify-content: center;
        }
        .box{
            width: 100px;
            height: 100px;
            background: gold;
        }
    </style>
</head>
<body>
    <div class="mtk">
        <div class="box"></div>
    </div>
</body>
</html>

十六、this指向的理解

this的指向一般分为四种情况

即:1、在浏览器中,this指向于window

2、在函数中(普通函数),this指向调用他的那个对象

3、在箭头函数中,箭头函数中没有this,使用的this就是箭 头函数父级的this。

4、在构造函数中,this指向构造出来的实例。

十七、vue路由钩子

vue的路由钩子一般分为3中情况,即全局钩子单个路由里面的钩子、和局部钩子

全局钩子:beforeEach、 afterEach、beforeResolve

单个路由里面的钩子:beforeEnter

局部钩子即:组件路由:beforeRouteEnter、 beforeRouteUpdate、 beforeRouteLeave

十八、vue父子组件传值

第一种:父传子:主要通过 props 来实现的

具体实现:父组件通过 import 引入子组件,并注册,在子组件标签上添加要传递的属性,子组件通过 props 接收,接收有两种形式一是通过数组形式[‘要接收的属性’ ],二是通过对象形式{ }来接收,对象形式可以设置要传递的数据类型和默认值,而数组只是简单的接收

第二种:子传父:主要通过$emit 来实现

具体实现:子组件通过通过绑定事件触发函数,在其中设置 this.emit(‘要派发的自定义事件’,要传递的值)emit(‘要派发的自定义事件’,要传递的值),emit 中有两个参数一是要派发的自定义事件,第二个参数是要传递的值然后父组件中,在这个子组件身上@派发的自定义事件,绑定事件触发的methods 中的方法接受的默认值,就是传递过来的参数

十九、数据类型的存储空间

localStorage的存储大小为:5MB

sessionStorage的存储大小为:5MB

cookie的存储大小为:4KB

二十、XMLhttprequest对象的使用

1、创建XMLHttpRequest

2、给定请求方式以及请求地址

3、发送请求

4、获取服务器端给客户端的响应数据

二十一、协商缓存和强制缓存

1.强缓存:不会向服务器发送请求,直接从缓存中读取资源,状态码是200;
2.协商缓存:向服务器发送请求,服务器会根据这个请求的Request headers的一些参数来判断是否命中协商缓存,如果命中,从缓存中读取数据,返回304状态码
两者共同点:都是从客户端缓存中读取资源; 区别:强缓存不会发请求,协商缓存会发请求。

二十二、DNS解析的过程

域名DNS解析完整流程:

第一步:本地客户机提出域名解析请求,查找本地HOST文件后将该请求发送给本地的域名服务器。

第二步:当本地的域名服务器收到请求后,就先查询本地的缓存,如果有该纪录项,则本地的域名服务器就直接把查询的结果返回。

第三步:如果本地DNS缓存中没有该纪录,则本地域名服务器就直接把请求发给根域名服务器,然后根域名服务器再返回给本地域名服务器一个所查询域(根的子域)的主域名服务器的地址。

第四步:本地服务器再向上一步返回的域名服务器发送请求,然后接受请求的服务器查询自己的缓存,如果没有该纪录,则返回相关的下级的域名服务器的地址。

第五步:重复第四步,直到找到正确的纪录。

第六步:本地域名服务器把返回的结果保存到缓存,以备下一次使用,同时还将结果返回给客户机。

递归查询:在该模式下DNS服务器接收到客户机请求,必须使用一个准确的查询结果回复客户机。如果DNS服务器本地没有存储查询DNS信息,那么该服务器会询问其他服务器,并将返回的查询结果提交给客户机。

迭代查询:DNS所在服务器若没有可以响应的结果,会向客户机提供其他能够解析查询请求的DNS服务器地址,当客户机发送查询请求时,DNS服务器并不直接回复查询结果,而是告诉客户机另一台DNS服务器地址,客户机再向这台DNS服务器提交请求,依次循环直到返回查询的结果为止。

二十三、js的原型链

原型链是理解 JS 面向对象很重要的一点,这里主要涉及到两个点,一是_proto_ ,二是 prototype,举个例子吧,这样还好说点,例如:我用 function创建一个 Person 类,然后用 new Person 创建一个对象的实例假如叫 p1 吧,在Person 类的原型 prototype 添加一个方法,例如:play 方法,那对象实例 p1如何查找到 play 这个方法呢,有一个查找过程,具体流程是这样的:首先在 p1 对象实例上查找是否有 play 方法,如果有则调用执行,如果没有则用p1._proto_往创建p1 的类的原型上查找,也就是说往 Person.prototype 上查找,如果在Person.prototype 找 到 play 方 法 则 执 行 , 否 则 继 续 往 上 查 找 , 则 用Person.prototye.__proto__继续往上查找,找到 Object.prototype,如果Object.prototype有play方法则执行之,否则用Object.prototype.__proto__继续再往上查找,但 Object.prototpye.__proto__上一级是 null,也就是原型 链的顶级,结束原型链的查找。

二十四、页面性能优化

我知道的方法有:
1、资源压缩和合并:资源压缩可以从文件中去掉多余的字符,比如回车、空格。

2.利用浏览器缓存:对于web应用来说,缓存是提升页面性能同时减少服务器压力的利器。

3、静态资源加载优化:运用路由懒加载和图片懒加载的方式

4、使用异步加载方式:对于非核心代码使用异步加载能提高页面的性能

5、减少不必要的重新渲染:修改元素的样式后,再读取它的以下属性,会立刻强制触发页面重新计算 Layout,修改样式之后,尽量避免去访问这些需要重新布局才能计算出来属性,可以降低 layout 次数,也就降低了 js 执行耗时,进而提升页面性能。

6、动画优化.

7、减少http请求

二十五、table布局的弊端

1、Table要比其它html标记占更多的字节。(延迟下载时间,占用服务器更多的流量资源。)

2、Table会阻挡浏览器渲染引擎的渲染顺序。(会延迟页面的生成速度,让用户等待更久的时间。)

3、Table里显示图片时需要你把单个、有逻辑性的图片切成多个图。(增加设计的复杂度,增加页面加载时间,增加HTTP会话数。)

4、在某些浏览器中Table里的文字的拷贝会出现问题。(这会让用户不悦。)

5、Table会影响其内部的某些布局属性的生效(比如里的元素的height:100%)(这会限制你页面设计的自由性。)

6、一旦学了CSS知识,你会发现使用table做页面布局会变得更麻烦。(先花时间学一些CSS知识,会省去你以后大量的时间。)

7、table对于页面布局来说,从语义上看是不正确的。(它描述的是表现,而不是内容。)

8、table代码会让阅读者抓狂。(不但无法利用CSS,而且不容易理解)

9、table一旦设计完成就变成死的,很难通过CSS让它展现新的面貌。

二十六、vuex的理解

vuex是一个专为vue.js应用程序开发的状态管理模式。
它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。

Vue采用集中式存储管理应用的所有组件的状态。这里的关键在于集中式存储管理。这意味着本来需要共享状态的更新是需要组件之间的通讯,而现在有了vuex,组件就都和store通讯了。这也是为什么官网再次会提到Vuex构建大型应用的价值,如果你不打算开大大型的单页应用,使用Vuex可能会变得很繁琐,对于大型项目,可以使用Vuex作为不同组件之间的状态管理,而对于小型的项目,推荐使用HTML5特有的属性,localStroage和sessionStroage作为数据之间的传递。

vuex有这么几个核心概念:State、Getter、Mutation、Action、Module。

state里面就是存放的我们上面所提到的状态

mutations就是存放如何更改状态

getters就是从state中派生出状态,比如将state中的某个状态进行过滤然后获取新的状态。

actions就是mutation的加强版,它可以通过commit mutations中的方法来改变状态,最重要的是它可以进行异步操作。

modules顾名思义,就是当用这个容器来装这些状态还是显得混乱的时候,我们就可以把容器分成几块,把状态和管理规则分类来装。这和我们创建js模块是一个目的,让代码结构更清晰。

二十七、bfc的理解

bfc是一个 block format content 块级格式化上下文.是一个布局里面的概念.把一个盒子 设置成 bfc 之后,里面无论怎么布局.都不会影响外面的变动.另外如果是一个 bfc 盒子.浮 动的元素也会参数计算.用它可以解决一些布局方面的问题吧.比方说.margin 重叠.高度塌 陷等. overflow:hidden.float. display:inline-block 方法一:给父级元素设置绝对定位:position:absolute 方法二:给父级元素设置 overflow:hidden; 方法三:通过伪对象来实现

.
clearfix:after {
content:
" "
;
display: block;
clear: both;
height: 0;
}

二十八、v-html是什么用的

  • v-html 指令的作用:设置元素的innerHTML

  • 内容中有html结构会被解析为标签

  • v-text指令无论内容是什么,只会解析为文本

  • 解析文本使用v-text,需要解析html结构使用v-html

二十九、cookie、sessionStorage和localStroage的区别

cookie 可以设置失效时间,但没有自己的存取取的方法,需要时封装,每次请求时跟随请求发送,而 localStorage和sessionStorage 可以有自己存取的方法例:setItem(),getItem(),removeItem(),clear() 如: localStorage.setItem(‘属性’,值) 而且cookie一般由服务器生成,可设置失效时间,如果在浏览器端生成cookie,默认是关闭浏览器端后失效,它的存放数据的大小为4k每次都会携带在http头中,如果使用cookie保存过多数据会带来性能问题,还需要程序员自己封装,源生的cookie接口不友好
localStorage在存入浏览器中后除非被消除,否则的话就永久保存,sessionStorage则与其相反仅在当前会话下有效,关闭页面或浏览器后会被清除,localStorage和sessionStorage的存放数据大小一般为5MB,都仅在客户端(即浏览器端)中保存,不参与和服务器的通信,并且他们都可以接受源生接口,亦可以再次封装来对Object和Array有更好的支持

三十、解析地址栏参数

解析地址栏参数我知道的有两种
第一种:字符串拆分法
window.location.href 或者 location.href 或者 window.location 获得地址栏中的所有内容 decodeURI() 可以解码地址栏中的数据 恢复中文数据 window.location.search 获得地址栏中问号及问号之后的数据

//获取地址栏里(URL)传递的参数 

function GetRequest(value) {  
    //url例子:www.bicycle.com?id="123456"&Name="bicycle";  
    var url = decodeURI(location.search); //?id="123456"&Name="bicycle";
    var object = {};
    if(url.indexOf("?") != -1)//url中存在问号,也就说有参数。  
    {   
      var str = url.substr(1);  //得到?后面的字符串
      var strs = str.split("&");  //将得到的参数分隔成数组[id="123456",Name="bicycle"];
      for(var i = 0; i < strs.length; i ++)  
        {   
        object[strs[i].split("=")[0]]=strs[i].split("=")[1]
      }
  }
    return object[value];  
}  

第二种:正则匹配法

这种方法其实原理和上一种方法类似,都是从URL中提取,只是提取的方法不同而已。

function GetQueryString(name) {  
    var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");  
    var r = window.location.search.substr(1).match(reg);  
    if (r != null) {   
        return unescape(r[2]);  
    }  
    return null;  
} 

在vue中可以通过this.$route获取路由对象然后根据具体需要取对象内容

this.$route.path 当前页面路由
this.$route.params 路由参数
this.$route.query 查询路由参数

三十一、v-if和show的区别
v-if 和 v-show 都可以显示和隐藏一个元素,但有本质区别 v-if 是惰性的,只是值为 false 就不会加载对应元素,为 true 才动态加 载对应元素 v-show:是无论为 true 和为 false 都会加载对应 html 代码,但为 false 时用 display:none 隐藏不在页面显示,但为 true 时页面上用 display:block 显示其效果 适用场景:切换频繁的场合用 v-show,切换不频繁的场合用 v-if
三十二、vue按钮的权限控制

三十三、vue路由模式
我知道的有两种:History和hash模式

三十四、两种路由模式的区别
1、hash:模式(vue-router默认模式URL后⾯带#)使⽤URL的hash值来作为路由,⽀持所有浏览器,缺点:只能改变#后⾯的来实现路由跳转

2、history:history模式(通过mode: 'history’来改变为history模式)HTML5 (BOM)History API 和服务器配置缺点:怕刷新如果后端没有处理这个情况的时候前端刷新就是实实在在的请求服务器这样消耗的时间很多还很慢。

三十五、computed和watch的区别
1、功能上:computed是计算属性,watch是监听一个值的变化,然后执行对应的回调。
2、是否调用缓存:computed中的函数所依赖的属性没有发生变化,那么调用当前的函数的时候会从缓存中读取,而watch在每次监听的值发生变化的时候都会执行回调。
3、是否调用return:computed中的函数必须要用return返回,watch中的函数不是必须要用return。
4、computed默认第一次加载的时候就开始监听;watch默认第一次加载不做监听,如果需要第一次加载做监听,添加immediate属性,设置为true(immediate:true)
5、使用场景:computed----当一个属性受多个属性影响的时候,使用computed-----购物车商品结算。watch–当一条数据影响多条数据的时候,使用watch-----搜索框.

三十六、axios做过那些封装
1.一般情况,axios的封装我们一般会在src文件夹下创建utils文件夹下的的request.js文件中

2.在request.js文件中引入axios,进行封装,baseURL代表后端即服务器的地址,timeout表示接口的响应时间,超时退出

在页面登录的时候我们通常情况下会随机生产token,为保障服务器接口的安全性,会要求在进行axios的封装时携带token调用每个接口

3.创建拦截器,确保能正确的接收接口返回的值

4.创建api文件夹 ,在api文件夹下创建接口文件colllect.js,文件中引入src中的request.js文件,创建接口函数,区分get方法和post方法所传入参数的不同

5.在子页面中引入你所需要的接口。

import { getDictValue } from '@/api/collect'

在methods中调用接口

methods:{

    getList('参数').then(res=>{
            //接收后台返回的数据
            console.log(res)
    })

}

三十七、$nextTick的作用

nextTick的作用是在下次DOM更新循环结束之后执行延迟回调,在修改数据之后使用nextTick的作用是在下次 DOM 更新循环结束之后执行延迟回调,在修改数据之后使用 nextTick,则可以在回调中获取更新后的 DOM