[TOC]

image.png无论从事什么行业,只要做好两件事就够了,一个是你的专业、一个是你的人品,专业决定了你的存在,人品决定了你的人脉,剩下的就是坚持,用善良专业和真诚赢取更多的信任。

HTML、CSS

  • 谈谈你对UTF8字符集的了解
  • 文档流
    • 一、什么是文档流?
      • 那么所谓的文档流(normal flow,也被称为“普通流”),指的是就是元素排版布局过程中,元素会自动从左往右,从上往下地遵守这种流式排列方式。
      • 当浏览器渲染html文档时,从顶部开始渲染,为元素分配所需要的空间,每一个块级元素单独占一行,行内元素则按照顺序被水平渲染直到在当前行遇到了边界,然后换到下一行的起点继续渲染。那么此时就不得不说一下块级元素和行内元素。

二、块级元素与行内元素

     - 块级元素:它就应该有自己的宽度和高度。而且它比较霸道,每个块级元素默认占一行高度,一行内添加一个块级元素后一般无法添加其他元素(float浮动后除外),一般作为容器使用,常见的块级元素有:from、select、 textarea、h1-h6 、table 、button 、hr 、p 、ol 、ul等。
     - 结合以上内容,块级元素拥有以下特点:
     - 1.每个块级元素都是独自占一行。
     - 2.元素的高度、宽度、行高和边距都是可以设置的。
     - 3.元素的宽度如果不设置的话,默认为父元素的宽度。
     - 行内元素:显然,这种元素存在于一行内,且能与别的行内元素共同享有一行。常见的行内元素有:span、input、a、em、strong、b、br、img、select、button等。
     - 那么行内元素拥有的特点如下:
     - 1.每一个行内元素可以和别的行内元素共享一行,相邻的行内元素会排列在同一行里,直到一行排不下了,才会换行。
     - 2.行内元素设置width, height无效(此处有坑,请往下看),宽度随元素的内容而变化。
     - 3.行内水平方向的padding-left和padding-right都会产生边距效果,但是竖直方向上的padding-top和padding-bottom都不会产生边距效果。
  - **三、替换元素和非替换元素**
     - 细心的大家肯定发现了,像<img>、<input>、<select>、<textarea>等,它们也是行内元素呀,明明就可以设置宽高啊,那这里就有问题了。其实并不是所有的行内元素都不能设置宽高的。
     - 行内元素也分为两种:替换元素和非替换元素。
        - 替换元素:
           - 浏览器根据元素的标签和属性,来决定其的具体显示内容的元素,常见的有:<img>、<input>、<select>、<textarea>、<object>。比如浏览器根据<img >标签的src属性显示图片。根据<input>的type属性决定显示输入框还是按钮,它们的宽度和高度是可以设置的。
        - 非替换元素:
           - 内容直接表现给用户端的元素称为成为非替换元素,常见的有:<span>、<p>、<label>等。例如<span>,它会将开始和结束标签中的内容直接在浏览器上展示出来。
  - **四、脱离文档流**
     - 所谓脱离文档流,即将元素从普通的布局排版(普通文档流)中脱离出来,其他盒子在定位的时候,会当做没看到它(余生你不必再指教了),两者位置重叠都是可以的,但是依然在DOM树中存在。
     - 那么会使元素脱离文档流的情况有哪些呢?
        - 1.float产生的浮动
           - 使用float脱离文档流时,虽然其他盒子会无视这个元素,但其他盒子内的文本依然会为这个元素让出位置,环绕在该元素的周围。
           - 下面是代码:<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .demo1{ width: 200px; height: 200px; border: 5px solid red; float: left; } .demo2{ width: 200px; height: 100px; border: 5px solid green; } </style> </head> <body> <div class="demo1">这是demo1的文本</div> <div class="demo2">这是demo2的文本</div> </body> </html>
           - 代码运行效果:
              - ![image.png](https://cdn.nlark.com/yuque/0/2022/png/1776080/1651337218428-29ccd030-8395-4e50-a7e9-48daf8685e19.png#clientId=u0f4792af-9b9b-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u74438aa2&margin=%5Bobject%20Object%5D&name=image.png&originHeight=249&originWidth=255&originalType=url&ratio=1&rotation=0&showTitle=false&size=30392&status=done&style=none&taskId=ued59f740-cf52-41e5-9937-d31709a92f2&title=)
        - 2.position:absolute;
           - absolute是绝对定位,绝对定位的元素以第一个非static父元素为参照。如果没有非static的父元素,则以body为参照。
           - 下面是例子:<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .demo1{ width: 200px; height: 200px; border: 5px solid red; } .demo2{ width: 200px; height: 100px; border: 5px solid green; position: absolute; top: 50px;/*给demo2一个绝对定位,并且距离body顶部 50px*/ } .demo3{ width: 200px; height: 200px; border: 5px solid black; } body{ border: 2px solid blue; } </style> </head> <body> <div class="demo1">这是demo1的文本</div> <div class="demo2">这是demo2的文本</div> <div class="demo3">这是demo3的文本</div> </body> </html>
           - 代码效果如图:
              - ![image.png](https://cdn.nlark.com/yuque/0/2022/png/1776080/1651337218436-044aea64-249c-4bf8-9bf5-6de8a408f41a.png#clientId=u0f4792af-9b9b-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=ue58ac33e&margin=%5Bobject%20Object%5D&name=image.png&originHeight=474&originWidth=455&originalType=url&ratio=1&rotation=0&showTitle=false&size=52823&status=done&style=none&taskId=udd3fe896-f790-407a-82d2-23f67d92830&title=)
           - 我们可以看到第二个div它相对body顶部向下移动了50px;
        - 3.position:fixed;
           - 完全脱离文档流,相对于浏览器窗口进行定位,也就是这个div固定在浏览器窗口上了,不论我们怎么拖动滚动条都无法改变它在浏览器窗口的位置。
           - 下面是代码:<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .demo1{ width: 200px; height: 200px; border: 5px solid red; } .demo2{ width: 200px; height: 100px; border: 5px solid green; position: fixed; right: 50px; } .demo3{ width: 200px; height: 200px; border: 5px solid black; } body{ border: 2px solid blue; } </style> </head> <body> <div class="demo1">这是demo1的文本</div> <div class="demo2">这是demo2的文本</div> <div class="demo3">这是demo3的文本</div> </body> </html>
              - ![image.png](https://cdn.nlark.com/yuque/0/2022/png/1776080/1651337218482-7f014bfd-3e1a-4078-b869-c731269f03a6.png#clientId=u0f4792af-9b9b-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u27102a59&margin=%5Bobject%20Object%5D&name=image.png&originHeight=456&originWidth=1434&originalType=url&ratio=1&rotation=0&showTitle=false&size=73878&status=done&style=none&taskId=ue1737792-afd9-403c-9479-cd356d0519d&title=)
     - 脱离标准文档流的两种方式<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">所谓脱离标准文档流</span><span style="background-color: rgb(255, 255, 255); font-family: SimSun; color: rgb(51, 51, 51); font-size: 16px; white-space: pre-wrap;">就是将元素从普通的布局排版中拿走,其他盒子在定位的时候,会当做脱离文档流的元素不存在而进行定位</span><span style="background-color: rgb(255, 255, 255); color: rgb(51, 51, 51); font-size: 16px; white-space: pre-wrap; font-family: "Microsoft YaHei", arial, "courier new", courier, 宋体, monospace;">。</span>
        - 不浮动的盒子会无视浮动的盒子,假使现有两个盒子,一个浮动一个不浮动,则浮动的盒子会覆盖不浮动的盒子。如下代码的结果所示:<!DOCTYPE html> <html lang="en"> <head>     <meta charset="UTF-8">     <title>浮动</title>     <style type="text/css">     body{         margin:0px;     }     .first     {         background-color: #ccc;         float:left;         width:200px;         height:200px;     }     .second     {         background-color: blue;         width:250px;         height:250px;     }     </style> </head> <body>     <div class="first"></div>          <div class="second"></div> </body> </html>
        - 结果如图:
           - ![image.png](https://cdn.nlark.com/yuque/0/2022/png/1776080/1651337218761-b671a905-3e39-485d-9114-3b591a438c3a.png#clientId=u0f4792af-9b9b-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=uad3e46f4&margin=%5Bobject%20Object%5D&name=image.png&originHeight=259&originWidth=256&originalType=url&ratio=1&rotation=0&showTitle=false&size=12932&status=done&style=none&taskId=ud548f957-e5bb-458d-b085-b7ce354dc34&title=)
        - 盒子元素会无视浮动的元素,但是盒子元素里面的文字并不会无视浮动元素,如下图所示:
           - ![image.png](https://cdn.nlark.com/yuque/0/2022/png/1776080/1651337220428-ae2e90ae-ae3e-4c3c-8371-d869500f3514.png#clientId=u0f4792af-9b9b-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u2c1ffbda&margin=%5Bobject%20Object%5D&name=image.png&originHeight=334&originWidth=245&originalType=url&ratio=1&rotation=0&showTitle=false&size=47411&status=done&style=none&taskId=u399e0452-db6c-4e2f-852b-ccfd3dd7774&title=)
        - 可以看到蓝色背景色盒子里的文本注意到了这个浮动元素于是在盒子里右推的形式围绕在浮动盒子的周围。
        - 此外,一旦一个元素浮动了,那么他就可以设置宽高,可以并排,无论原来他是块级元素还是行内元素。当浮动元素里面有文字时,浮动元素不会覆盖文字,文字会围绕浮动元素显示。
        - 关于清除浮动,为什么要清除浮动呢?举一个自己遇到的例子,代码如下:<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "[http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd](http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd)"> <html xmlns="[http://www.w3.org/1999/xhtml](http://www.w3.org/1999/xhtml)"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>混合布局</title> <style> body{ margin:0; padding:0; font-size:10px; font-weight:bold} div{ text-align:center; line-height:50px} .head,.main{ width:200px;margin:0 auto;} .head{ height:100px; background:#F90} .left{ width:80px; height:60px; background:#ccc;float:left;} .right{ width:120px; height:60px;background:#FCC; float:right} .r_sub_left{ width:60px; height:60px; background:#9C3; float:left} .r_sub_right{ width:60px; height:60px; background:#9FC; float:right;} .footer{width:400px; height:50px; background:#9F9;margin:0 auto;} </style> </head> <body> <div class="head">head</div> <div class="main"> <div class="left">left</div> <div class="right">     <div class="r_sub_left">sub_left </div> <div class=" r_sub_right">sub_right </div> </div> </div> <div class="footer">footer</div> </body> </html>
        - 运行后看到如下结果:
           - ![image.png](https://cdn.nlark.com/yuque/0/2022/png/1776080/1651337220911-d8a0c305-7bd4-449a-b373-22ec5ce3622e.png#clientId=u0f4792af-9b9b-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u5a53331b&margin=%5Bobject%20Object%5D&name=image.png&originHeight=213&originWidth=441&originalType=url&ratio=1&rotation=0&showTitle=false&size=36409&status=done&style=none&taskId=u38e9a72d-5fe7-4213-9e94-21f82c14975&title=)
        - 尴尬了,注意到footer的盒子飘到上面去了,只剩下文字孤零零的在下面,这是为什么呢?因为left和right均设置为浮动的状态,而main并没有设置高度,可以想象为一条线在head的下面,这样footer自然无视left和right两个浮动元素然后飘到上面去了,这是我们需要进行float清除,清除的方式有一下几种,如下:清除浮动的方法综合一下答案:
           -  一、clear:both(/left/right);
           -  二、overflow:hidden;width:100%;
           - 三 、:after
           -  四、给main设置高度:.main{width:960px; {height:600px};margin:0 auto;}
           -  五、:margin:600px 0 0 0;
           - 详见:[https://my.oschina.net/leipeng/blog/221125](https://my.oschina.net/leipeng/blog/221125)
        -  推荐使用方法一和方法二,在给footer使用overflow的时候,千万不要忘记设置它的宽度。
           - 其中clear:both清除浮动 值描述
           -  left 在左侧不允许浮动元素。
           -  right 在右侧不允许浮动元素。
           -  both 在左右两侧均不允许浮动元素。
           -  none 默认值。允许浮动元素出现在两侧。
           -  inherit 规定应该从父元素继承 clear 属性的值。
        - overflow 属性规定当内容溢出元素框时发生的事情。
           -  值描述
              -  visible 默认值。内容不会被修剪,会呈现在元素框之外。
              -  hidden 内容会被修剪,并且其余内容是不可见的。
              -  scroll 内容会被修剪,但是浏览器会显示滚动条以便查看其余的内容。
              -  auto 如果内容被修剪,则浏览器会显示滚动条以便查看其余的内容。
              -  inherit 规定应该从父元素继承 overflow 属性的值。
        - 脱离文本的第二种方式是绝对定位(position:absolute):相比于float,position:absolute不管是文本还是盒子都会直接无视掉浮动元素,将float:left换为position:absolute后可以看到如下结果:
           - ![image.png](https://cdn.nlark.com/yuque/0/2022/png/1776080/1651337220980-d164e147-8ec4-4b54-bd30-70c2c804b54b.png#clientId=u0f4792af-9b9b-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u930601fd&margin=%5Bobject%20Object%5D&name=image.png&originHeight=248&originWidth=249&originalType=url&ratio=1&rotation=0&showTitle=false&size=24393&status=done&style=none&taskId=u6067ab21-2449-4f83-a544-50397d48a4a&title=)
     - 理解脱离文档流:
        - 一个帅帅的男生(div1)和一个可爱的女孩纸(div2),他们喜欢着对方,有一天,他们在一起了,从此过上了幸福的生活…..
        - 故事怎么可能是这样子的(皮一下)
        - 随着时间的推移,在某些事情的影响下(float、fixed、absolute),他们发现对方有很多的缺点,并不是完美的,有一天终于无法忍受对方,“咱们分手吧!”div1说到,balabala,他们分手了(div1脱离了文档流)。“你就当我死了吧”,div1说到。虽然男孩纸div1和女孩纸div2都还在这个世界上过着自己的生活(div1和div2都还存在于DOM树中),但是他们让对方死在了自己的心中(盒子在定位的时候,会当做没看到它)。狗血的故事讲完了。
  - 所谓的文档流,就好比如一块块的正方形组成的一个整体,而这些正方形就代表着每个div。当某个div脱离了这个整体,也就代表他脱离了文档流。然后下一个div就会来填补脱离的div的位置。
  - 下面是流程图。
     - 有四个小朋友在买小卖部排队买糖吃~
        - ![image.png](https://cdn.nlark.com/yuque/0/2022/png/1776080/1651337220983-7c4b945e-9a59-425c-8f88-58c6eb133877.png#clientId=u0f4792af-9b9b-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u4e6d122f&margin=%5Bobject%20Object%5D&name=image.png&originHeight=337&originWidth=362&originalType=url&ratio=1&rotation=0&showTitle=false&size=14402&status=done&style=none&taskId=u35367741-8d19-4d8b-9355-937ff9a132c&title=)
     - 第一个买完了糖的小朋友脱离了排队的队伍开心的吃糖去。
        - ![image.png](https://cdn.nlark.com/yuque/0/2022/png/1776080/1651337220981-1b64e40a-6866-406f-9624-834eb6da678b.png#clientId=u0f4792af-9b9b-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u10b4ade7&margin=%5Bobject%20Object%5D&name=image.png&originHeight=305&originWidth=406&originalType=url&ratio=1&rotation=0&showTitle=false&size=16447&status=done&style=none&taskId=uca446a6c-d782-408d-8d71-ac1d3df2435&title=)
     - 后面的小朋友看前面的小朋友走了,连忙补上防止别人插队。
        - ![image.png](https://cdn.nlark.com/yuque/0/2022/png/1776080/1651337222559-ae9873b1-3782-4321-86d3-bc62c442ab2b.png#clientId=u0f4792af-9b9b-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u376e4bd0&margin=%5Bobject%20Object%5D&name=image.png&originHeight=296&originWidth=364&originalType=url&ratio=1&rotation=0&showTitle=false&size=12794&status=done&style=none&taskId=u889043b8-3ff8-4db7-ba5b-705911840ef&title=)
        - ![image.png](https://cdn.nlark.com/yuque/0/2022/png/1776080/1651337223102-4642a647-7fd0-4636-815c-96a5caf79475.png#clientId=u0f4792af-9b9b-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u3d8e1330&margin=%5Bobject%20Object%5D&name=image.png&originHeight=220&originWidth=382&originalType=url&ratio=1&rotation=0&showTitle=false&size=10225&status=done&style=none&taskId=u054e3704-e589-4818-8764-a22c8bc9d9c&title=)
  - 实际上,在html页面中,我们看到的会是这样。
     - ![image.png](https://cdn.nlark.com/yuque/0/2022/png/1776080/1651337223225-0de203e0-a5ed-46ed-9e7a-63f644a81591.png#clientId=u0f4792af-9b9b-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=uc09cb73d&margin=%5Bobject%20Object%5D&name=image.png&originHeight=212&originWidth=176&originalType=url&ratio=1&rotation=0&showTitle=false&size=6501&status=done&style=none&taskId=u5cad87d9-3743-4995-a99a-bfd2696d83a&title=)
  - div2被div1给覆盖了!因为脱离文档流的div1不占据页面的空间了,所以才会留有空间给后面的div补上,当然这也导致了div2给div1覆盖了!
  - 目前常见的会影响元素脱离文档流的css属性有:
     - ①float浮动。
     - ②position的absolute和fixed定位。
  - 最后,想知道要怎么解决这种覆盖问题
     - 当我们看到div相互层叠覆盖的时候,首先我们想到的是否有div脱离了文档流?其次我们在分析他们是通过什么方法脱离文档流的?
     - 目前,常见的脱离文档流的方法有position定位和float浮动两种!
        - 1、如果这个div是通过float导致的脱离文档流的话,可以通过上面的div和下面的div之间插入清除浮动。 .clean{ clear:both; } //我是上面的div //我是下面的div 
        - 2、如果是position绝对定位(absolute)导致某个div脱离了文档流,从而使下面的div(即div2)和上面的div(div1)相互层叠了。那么,该如何解决呢?
           - 首先,切忌对下一个div使用position定位来解决问题,不然这就是一个坑!
           - ①“替死鬼”法
              - 制作多一个div(即div3)来代替div2,使得这个新制作出来的div3来填补这个文档流的缺漏(为脱离文档流的div1填补)。
              - 不单单制作出来div3出来后就算了,还要设置他的高度height为div1的高度。这样子就好像恢复如初,回到最初的未改变文档流一样!(嘻嘻~,障眼法~)
  • input框
    • input框的23种类型
      • type 属性
        • 总述
          • 常用的并且能为大多数浏览器所识别的类型大概有:text、password、number、button、reset、submit、hidden、radio、checkbox、file、image、color、range、date、month、week、time、datetime-local。
          • 另外还有一些类型:tel、email、url、datetime、search。这些类型部分浏览器不支持识别或校验。
          1. text:文本
            • 效果:
              • image.png
            • 注意:当input没有填写类型时,默认为文本类型。
          1. password:密码
            • 效果:
              • image.png
          1. number:数字
            • 效果:
              • image.png
          1. button:按钮
            • 效果:
              • image.png
          1. tel:电话
            • 效果:
              • image.png
            • 注意:tel类型似乎没有什么实际作用。
          1. email:邮件
            • 效果:
              • image.png
            • 注意:火狐对email类型有校验,360浏览器无校验。
          1. file:文件
            • 效果:
              • image.png
          1. range:滑动条
            • 效果:
              • image.png
          1. date:日期
            • 效果:
              • image.png
          1. month:月份
            • 效果:
            • image.png
          1. week:周
            • 效果:
            • image.png
          1. time:时间
            • 效果:
            • image.png
          1. datetime:时间、日、月、年(UTC时间)
            • 效果:
            • image.png
            • 注意:火狐、360浏览器都对datetime不支持,会按照text类型处理。
          1. datetime-local:时间、日、月、年(本地时间)
            • 效果:
            • image.png
          1. radio:单选框
            • 效果:
            • image.png
              1. checkbox:复选框
            • 效果:
              • image.png
              1. image:图片
            • 效果:
              • image.png
              1. color:颜色
            • 效果:
              • image.png
              1. search:搜索框
            • 效果:
              • image.png
            • 注意:search似乎与text的效果没有什么区别。。。
              1. reset:重置按钮
            • 效果:
              • image.png
            • 注意:reset按钮一般用于form表单中。
              1. submit:提交按钮
            • 效果:
              • image.png
            • 注意:submit按钮一般用于form表单中。
              1. hidden:隐藏
            • 效果:
            • 注意:hidden类型会将input隐藏,所以什么都看不到,而且被隐藏的input框也不会占用空间。
              1. url:路径
            • 效果:
              • image.png
            • 注意:火狐对url类型有校验,360浏览器无校验。
      • value属性
        • input 标签的 value 属性的作用是由 input 标签的 type 属性的值决定的
        • 当 type 的取值为 button、reset、submit 中的其中一个时,此时 value 属性的值表示的是按钮上显示的文本
        • 当 type 的取值为 text、password、hidden 中的其中一个时,此时 value 属性的值表示的是输入框中显示的初始值,此初始值可以更改,并且在提交表单时,value 属性的值会发送给服务器(既是初始值,也是提交给服务器的值)
        • 当 type 的取值为 checkbox、radio 中的其中一个时,此时 value 属性的值表示的是提交给服务器的值
        • 当 type 的取值为 image 时,点击它提交表单后,会将用户的点击位置相对于图像左上角的 x 坐标和 y 坐标提交给服务器
        • type=”checkbox” 时,其 value 属性的值表示在复选框呈勾选状态时提交给服务器的数据值,默认为 on
        • type=”image” 定义图像形式的提交按钮,此时必须把 src 属性 和 alt 属性 与 结合使用(alt 属性表示图片未正常显示时,用于替换图片的提示;如果不写这个属性,当图片未正常显示时,会默认显示提交这两个字)
        • <!DOCTYPE html>
          value的值是按钮上的文本




          value的值是输入框中的初始值




          value的值在提交表单时会发送给服务器



        • 在谷歌浏览器中的呈现效果如下:
          • image.png
        • 注】:
          • checkbox 型的 input 标签的不足之处在于:提交表单时,只有处于勾选状态的复选框的数据值才会发送给服务器。也就是说,如果没有任何一个复选框被选中,那么服务器就不会收到与其相关的数据项
          • 当设置 input 标签的 type 属性值为checkbox 或者 radio 时,必须同时设置 input 标签的 value 属性
          • 当 type=”file” 时,不能使用 value 属性
    • 3大类选择器的作用:
      • 第一类:控制系(Input Control States)
        • 选择器 | 作用
        • :enabled | 选择可使用状态的 元素
        • :disabled | 选择不可使用状态的 元素
        • :read-only | 选择不可编辑状态的元素(不仅仅是 )
        • :read-write | 选择可编辑状态的元素(不仅仅是 )
        • :placeholder-shown | 选择 placeholder text 显示时的元素
        • :default | 选择在 ,, , 以及 上的默认状态

      • 第二类:输出系(Input Value States)
        • 选择器 | 作用
        • :checked | 选择处于选中状态的
        • :indeterminate | 选择状态不确定的表单元素与

      • 第三类:侦查系(Input Value-checking)
        -  选择器     | 作用
        -  :blank     | 选择处于空值时的 ,暂未被浏览器支持
        -  :valid     | 选择验证通过的表单元素
        -  :invalid     | 选择验证不通过的表单元素
        -  :in-range     | 选择处于指定范围内的
        -  :out-of-range     | 选择不处于指定范围内的
        -  :required     | 选择必填的表单元素
        -  :optional     | 选择选填的表单元素
        -  :user-invalid     | 选择用户输入但值非法时的 ,暂未被浏览器支持
        

      • 可怕,除了选择器,居然还跟这些属性有关系
      • 除了有很多相关的选择器,结合不同的type还有不同的属性可以供使用。他们的作用如下:
        -  属性     | 作用
        -  maxlength     | 可输入的最大长度
        -  minlength     | 可输入的最小长度
        -  size     | 输入框的长度
        -  readonly     | 输入框是否只读
        -  required     | 输入框是否必填
        -  multiple     | 输入框是否可以多选
        -  pattern     | 输入框验证规则
        -  min     | 可输入的最小值
        -  max     | 可输入的最大值
        -  step     | 输入框每次的增量
        -  list     | 输入框绑定的可选值数据
        -  placeholder     | 输入框预选文字
        

      • 纯CSS实现表单提交功能
        • 首先我们来看个效果图
          • image.png
        • 上面的效果就是一个纯CSS实现的表单提交功能,这是怎么实现的呢?下面我们直接看源码,然后一步一步地来分拆(不想看的可以直接CV下面的源码自己做测试~) :root { —error-color: red; } .form > input { margin-bottom: 10px; } .form > .f-tips { color: var(—error-color); display: none; } input[type=”text”]:invalid ~ input[type=”submit”], input[type=”password”]:invalid ~ input[type=”submit”] { display: none; } input[required]:focus:invalid + span { display: inline; } input[required]:empty + span { display: none; } input[required]:invalid:not(:placeholder-shown) + span { display: inline; } 账号: 请输入正确的账号 密码: 请输入正确的密码
        • 第一步:写好基础结构
        • 第二步:重点功能input[type=”text”]:invalid ~ input[type=”submit”], input[type=”password”]:invalid ~ input[type=”submit”] { display: none; } input[required]:focus:invalid + span { display: inline; } input[required]:empty + span { display: none; } input[required]:invalid:not(:placeholder-shown) + span { display: inline; }
          • 上面便是核心交互的实现。
          • 首先第一个class就是保证了在两个输入框不通过的时候隐藏,就是当输入框值为空或者不符合验证规则,则隐藏提交按钮。
          • 第二个,第三个class则是控制当用户在输入框输入内容时,如果不符合验证规则,则显示错误信息,否则则隐藏。
          • 第四个class则是用过 placeholder 是否存在来控制错误信息的显隐,如果 placeholder 不显示,则证明用户正在输入,错误信息则根据用户输入的值来判断是否显隐,否则则隐藏。
        • 状态切换
          • 面我们有提到一个选择器 :indeterminate ,这个是用于选择状态不确定的表单元素与 ,玩过扫雷的人都知道,右击除了可以选择红旗,还可以选择问号,就是选中,但不确定;又跟 promise 的 pending 状态类型,介于 resolve 与 reject 之间。
          • 多了 :indeterminate 会给我们带来很多很有趣的体验。
          • 首先我们来看看它的使用案例。
          • 基础使用法
          • 先看效果
            • image.png
          • 代码如下: body { background: #333; color: #fff; padding: 20px; text-align: center; } input { margin-right: .25em; width: 30px; height: 30px; } label { position: relative; top: 1px; font-size: 30px; } 点击左边 ‘use strict’; checkbox.addEventListener(‘click’, ev => { if (ev.target.readOnly) { ev.target.checked = ev.target.readOnly = false; } else if (!ev.target.checked) { ev.target.readOnly = ev.target.indeterminate = true; }; });
          • 这里面其实没有什么复杂的实现,只是做了个中间态的判断,就非常轻松的实现了radio的三种状态切换。
      • 秀到头皮发麻法
      • 输入框绑定的可选值
        • 先看效果
          • image.png
        • 其实代码很简单:
        • 这里原理就是通过 来绑定需要下拉显示的数据列表
        • 那么当我们要实现输入联想的时候,也可以通过修改 的子元素来实现,而不是再写一大堆的操作函数来实现。
    • HTML5表单教程之input新增加的六种时间类型
      • 1、Date类型:
        • 如果在之前,我们使用js+css+dom才能实现日历选择日期的效果,在HTML5中,我们只需要设置input为date类型即可,提交表单的时候也不需要我们验证数据了,它已经帮我们实现了。
        • 运行效果如下图:
      • 2、Time类型:
        • 此类型是一个专门用来输入时间的文本框,在提交的时候检查是否输入了有效的时间。
        • 运行效果如下图:
      • 3、DateTime类型:
        • datetime类型的input元素是专门用来输入UTC日期和实践的文本框,在提交的时候,对日期和时间进行有效的检查。
        • 运行效果如下图:
      • 4、DateTime-Local类型:
        • 此类型与datatime类型差不多,只不过是用来输入本地的日期和时间。
        • 运行效果如下图:
      • 5、Month类型:
        • month是一种专门输入月份的文本框,在日历中,你只能选择某一个月,不能选择某一天。
        • 运行效果如下图:
      • 6、Week类型:
        • week是专门用来输入周(星期)的文本框,W后面所跟的数字表示此周是当年的第几个星期。在日历中只能选择一周,同样不能选择某一天。
        • 运行效果如下图:
  • form表单提交方式
    • 总结:
  • HTML 与 XHTML——二者有什么区别
    • Html(超文本标记语言)是标准通用标记语言下的一个应用,也是一种规范,一种标准。
    • Xhtml(可扩展超文本标记语言)是一种置标语言,表现方式与html类似,不过语法上更加严格,主要不同:
    • 1,所有元素必须被正确嵌套,
    • 2,所有标记必须闭合,
    • 3,标签名,属性名必须小写字母,
    • 4,所有的属性必须用“”括起来,
    • 5,所有非标签一部分的><&都必须以编码形式,
    • 6,所有属性必须要有值,
    • 7,注释中不要有—
    • 8,图片必须要有说明文字
    • html与xhtml之间的区别:
    • 1、xhtml对比与html,xhtml文档具有良好完整的排版,体现在两方面:a、元素必须要有结束标签;b、元素必须嵌套;
    • 2、对于html的元素和属性,xhtml必须小写,因为xml是严格区分大小写的,
    • 是不同的标签;
    • 3、xhtml的属性值必须在引号之中;
    • 4、xhtml不支持属性最小化,什么是属性最小化了?
    • 正确:非最小化属性(unminimized attributes)
    • 不正确:最小化属性(minimized attributes)
    • 5、 在xhtml中,name属性是不赞成使用的,在以后的版本中将被删除。
    • HTML与XHTML之间的差别,主要分为功能上的差别和书写习惯的差别两方面。
    • 关于功能上的差别,主要是XHTML可兼容各大浏览器、手机以及PDA,并且浏览器也能快速正确地编译网页。 由于XHTML的语法较为严谨,所以如果你是习惯松散结构的HTML编写者,那需要注意XHTML的规则。
    • 以下是 XHTML 相对 HTML 的几大区别:
    • 1、所有标签都必须小写
    • 在XHTML中,所有的标签都必须小写,不能大小写穿插其中,也不能全部都是大写。
    • 2、标签必须成双成对
    • 当出现一个标签时,必须要有对应的结束标签,缺一不可,就像在任何程序语言中的括号一样
    • 3、标签顺序必须正确
    • 标签由外到内,一层层包覆着,所以假设你先写div后写h1,结尾就要先写h1后写div。只要记住一个原则“先进后出”,先弹出的标签要后结尾。
    • 4、所有属性都必须使用双引号
    • 在XHTML 1.0中规定连单引号也不能使用,所以全程都得用双引号。
    • 5、不允许使用target=”_blank”
    • 从XHTML 1.1开始全面禁止target属性,如果想要有开新窗口的功能,就必须改写为rel=”external”,并搭配JavaScript实现此效果。
  • 116,要动态改变层中内容可以使用的方法?
    • 第一种方法:通过改变DIV的innerHTML属性值动态改变页面内容。这种情况适合动态显示的内容较少时,动态显示的内容(如“用户名”不能为空)占据一行的时候比较适合此种方法,使用myDiv.innerHTML=”HTML代码”来动态改变页面内容。
    • 第二种方法:当动态显示的内容较多,并相对固定时,则应该预先制作好DIV内容,然后使用myDiv.style.display=”none/block”,来动态改变层的隐藏或显示,从而实现动态改变页面内容。经过上面的详细分析,这里应该采用第二种方法,先设计好层中的内容,然后使用none/block 属性值来显示或隐藏层,从而实现动态改变页面内容。
    • 利用层的innerHTML改变内容
    • 利用层的innerText改变内容
    • 可以通过设置层的隐藏和显示来实现
    • 可以通过设置层的样式属性display属性来实现
  • 112,什么是 WebGL,它有什么优点?【https://blog.csdn.net/naooomi/article/details/7045017?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-16.base&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-16.base】【https://www.jianshu.com/p/fd4bbf7d09b6】【】
    • WebGL就是js和OpenGL2.0的结合,也是3d绘图标准,通过增强对OpenGL的绑定,WebGL可以对html5的canvas进行硬件3D加速渲染
    • WebGL(全写 Web Graphics Library )是一种 3D 绘图标准,这种绘图技术标准允许把 JavaScript 和 OpenGL ES 2.0 结合在一起,通过增加OpenGL ES 2.0 的一个 JavaScript 绑定, WebGL 可以为 HTML5 Canvas 提供硬件 3D 加速渲染,这样 Web 开发人员就可以借助系统显卡来在浏览器里更流畅地展示 3D 场景和模型了,还能创建复杂的导航和数据视觉化。显然, WebGL 技术标准免去了开发网页专用渲染插件的麻烦,可被用于创建具有复杂 3D 结构的网站页面,甚至可以用来设计 3D 网页游戏等等。
    • WebGL完美地解决了现有的 Web 交互式三维动画的两个问题:
    • 第一,它通过HTML脚本本身实现 Web 交互式三维动画的制作,无需任何浏览器插件支持 ;
    • 第二,它利用底层的图形硬件加速功能进行的图形渲染,是通过统一的、标准的、跨平台的OpenGL接口实现的。
    • 通俗说WebGL中 canvas 绘图中的 3D 版本。因为原生的 WebGL 很复杂,我们经常会使用一些三方的库,如 three.js 等,这些库多数用于HTML5 游戏开发。
  • css 动画中 ease,seae-in,ease-in-out,ease-out,效果区别
    • 值 | 描述
    • linear | 规定以相同速度开始至结束的过渡效果(等于 cubic-bezier(0,0,1,1))。(匀速)
    • ease | 规定慢速开始,然后变快,然后慢速结束的过渡效果(cubic- bezier(0.25,0.1,0.25,1))(相对于匀速,中间快,两头慢)。
    • ease-in | 规定以慢速开始的过渡效果(等于 cubic-bezier(0.42,0,1,1))(相对于匀速,开始的时候慢,之后快)。
    • ease-out | 规定以慢速结束的过渡效果(等于 cubic-bezier(0,0,0.58,1))(相对于匀速,开始时快,结束时候间慢,)。
    • ease-in-out | 规定以慢速开始和结束的过渡效果(等于 cubic-bezier(0.42,0,0.58,1))(相对于匀速,(开始和结束都慢)两头慢)。
    • cubic-bezier(n,n,n,n) | 在 cubic-bezier 函数中定义自己的值。可能的值是 0 至 1 之间的数值。

  • 动画
    • 【前端动画】实现动画的6种方式
    • 通常在前端中,实现动画的方案主要有6种:
    • javascript直接实现;
    • SVG(可伸缩矢量图形);
    • CSS3 transition;
    • CSS3 animation;
    • Canvas动画;
    • requestAnimationFrame;
    • javascript 直接实现动画
      • 其主要思想是通过setInterval或setTimeout方法的回调函数来持续调用改变某个元素的CSS样式以达到元素样式变化的效果。
      • 示例#rect { width: 200px; height: 200px; background: #ccf; }
    • 3.HTML5 拖放
      • 拖放是一种常见的特性,即抓取对象以后拖到另一个位置。在 HTML5 中,拖放是标准的一部分,任何元素都能够拖放。
      • 设置元素为可拖放
      • 首先,为了使元素可拖动,把 draggable 属性设置为 true :
      • 面试习题【js、网络、算法】 - 图29
      • 拖动什么 - ondragstart 和 setData()
      • 放到何处 - ondragover
      • 进行放置 - ondrop
    • 4.HTML5 地理定位
      • HTML5 Geolocation API 用于获得用户的地理位置。
      • 鉴于该特性可能侵犯用户的隐私,除非用户同意,否则用户位置信息是不可用的。
    • 5.HTML5 Audio(音频)、Video(视频)
      • HTML5 规定了在网页上嵌入音频元素的标准,即使用
      • 您的浏览器不支持 audio 元素。
      • HTML5 规定了一种通过 video 元素来包含视频的标准方法。
      • 您的浏览器不支持Video标签。
    • 6.HTML5 Input 类型
      • HTML5 拥有多个新的表单输入类型。这些新特性提供了更好的输入控制和验证。
        • color、date、datetime、datetime-local、email、month、number、range、search、tel、time、url、week
      • Search Google:
      • 电话号码:
    • 7.HTML5 表单元素
      • HTML5 有以下新的表单元素:
      • 标签定义选项列表。请与 input 元素配合使用该元素,来定义 input 可能的值。
      • > 标签规定用于表单的密钥对生成器字段。
      • 标签定义不同类型的输出,比如脚本的输出。
      • 元素规定输入域的选项列表。
      • 属性规定 form 或 input 域应该拥有自动完成功能。当用户在自动完成域中开始输入时,浏览器应该在该域中显示填写的选项:
      • 使用 元素的列表属性与 元素绑定.
    • 8.HTML5 表单属性
      • HTML5 的
        标签添加了几个新属性.
      • 新属性:
        • autocomplete、novalidate
      • 新属性:
        • autocomplete、autofocus、form、formaction、formenctype、formmethod、formnovalidate、formtarget、height and width、list、min and max、multiple、pattern (regexp)、placeholder、required、step
    • 9.HTML5 语义元素
      • HTML5提供了新的语义元素来明确一个Web页面的不同部分:
        • image.png
    • 10.HTML5 Web 存储
      • Web Storage DOM API 为Web应用提供了一个能够替代cookie的Javascript解决方案
        • sessionStorage—客户端数据存储,只能维持在当前会话范围内。
          • sessionStorage 方法针对一个 session 进行数据存储。当用户关闭浏览器窗口后,数据会被删除。
        • localStorage—客户端数据存储,能维持在多个会话范围内。
          • localStorage 对象存储的数据没有时间限制。第二天、第二周或下一年之后,数据依然可用。
      • 对于大量复杂数据结构,一般使用IndexDB
    • 11.HTML5 离线Web应用(应用程序缓存)
      • HTML5 引入了应用程序缓存,这意味着 web 应用可进行缓存,并可在没有因特网连接时进行访问。
      • 应用程序缓存为应用带来三个优势:
        • 离线浏览 - 用户可在应用离线时使用它们
        • 速度 - 已缓存资源加载得更快
        • 减少服务器负载 - 浏览器将只从服务器下载更新过或更改过的资源。
      • HTML5 Cache Manifest 实例
      • 下面的例子展示了带有 cache manifest 的 HTML 文档(供离线浏览):
      • <!DOCTYPE HTML>
      • The content of the document……
      • Manifest 文件
        • manifest 文件是简单的文本文件,它告知浏览器被缓存的内容(以及不缓存的内容)。
        • manifest 文件可分为三个部分:
          • CACHE MANIFEST - 在此标题下列出的文件将在首次下载后进行缓存
          • NETWORK - 在此标题下列出的文件需要与服务器的连接,且不会被缓存
          • FALLBACK - 在此标题下列出的文件规定当页面无法访问时的回退页面(比如 404 页面
        • CACHE MANIFEST
        • 2012-02-21 v1.0.0

        • /theme.css
        • /logo.gif
        • /main.js
        • NETWORK:
        • login.php
        • FALLBACK:
        • /html/ /offline.html
    • 12.HTML5 Web Workers
      • 当在 HTML 页面中执行脚本时,页面的状态是不可响应的,直到脚本已完成。
      • web worker 是运行在后台的 JavaScript,独立于其他脚本,不会影响页面的性能。您可以继续做任何愿意做的事情:点击、选取内容等等,而此时 web worker 在后台运行。(相当于实现多线程并发)
    • 13.HTML5 SSE
      • Server-Sent 事件指的是网页自动获取来自服务器的更新。
      • 以前也可能做到这一点,前提是网页不得不询问是否有可用的更新。通过服务器发送事件,更新能够自动到达。
      • 例子:Facebook/Twitter 更新、估价更新、新的博文、赛事结果等。
      • EventSource 对象用于接收服务器发送事件通知:
      • var source=new EventSource(“demo_sse.php”);
      • source.onmessage=function(event)
      • {
      • document.getElementById(“result”).innerHTML+=event.data + “
        “;
      • };
      • 为了让上面的例子可以运行,您还需要能够发送数据更新的服务器(比如 PHP 和 ASP)。
      • <?php
      • header(‘Content-Type: text/event-stream’);
      • header(‘Cache-Control: no-cache’);
      • $time = date(‘r’);
      • echo “data: The server time is: {$time}nn”;
      • flush();
      • ?>
    • 14.HTML5 WebSocket
      • WebSocket是HTML5开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。在WebSocket API中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。当你获取 Web Socket 连接后,你可以通过 send() 方法来向服务器发送数据,并通过 onmessage 事件来接收服务器返回的数据。以下 API 用于创建 WebSocket 对象。
  • CSS3
    • CSS3选择器
      • .class .intro 选择所有class=”intro”的元素 1
      • #id #firstname 选择所有id=”firstname”的元素 1
      • * * 选择所有元素 2
      • element p 选择所有

        元素 1

      • element,element div,p 选择所有
        元素和

        元素 1

      • elementelement div p 选择
        元素内的所有

        元素 1

      • element>element div>p 选择所有父级是
        元素的

        元素 2

      • element+element div+p 选择所有紧接着
        元素之后的

        元素 2

      • [attribute] [target] 选择所有带有target属性元素 2
      • [attribute=value] [target=-blank] 选择所有使用target=”-blank”的元素 2
      • [attribute~=value] [title~=flower] 选择标题属性包含单词”flower”的所有元素 2
      • [attribute|=language] [lang|=en] 选择一个lang属性的起始值=”EN”的所有元素 2
      • :link a:link 选择所有未访问链接 1
      • :visited a:visited 选择所有访问过的链接 1
      • :active a:active 选择活动链接 1
      • :hover a:hover 选择鼠标在链接上面时 1
      • :focus input:focus 选择具有焦点的输入元素 2
      • :first-letter p:first-letter 选择每一个

        元素的第一个字母 1

      • :first-line p:first-line 选择每一个

        元素的第一行 1

      • :first-child p:first-child 指定只有当

        元素是其父级的第一个子级的样式。 2

      • :before p:before 在每个

        元素之前插入内容 2

      • :after p:after 在每个

        元素之后插入内容 2

      • :lang(language) p:lang(it) 选择一个lang属性的起始值=”it”的所有

        元素 2

      • element1~element2 p~ul 选择p元素之后的每一个ul元素 3
      • [attribute^=value] a[src^=”https”] 选择每一个src属性的值以”https”开头的元素 3
      • [attribute$=value] a[src$=”.pdf”] 选择每一个src属性的值以”.pdf”结尾的元素 3
      • [attribute=value] a[src=”44lan”] 选择每一个src属性的值包含子字符串”44lan”的元素 3
      • :first-of-type p:first-of-type 选择每个p元素是其父级的第一个p元素 3
      • :last-of-type p:last-of-type 选择每个p元素是其父级的最后一个p元素 3
      • :only-of-type p:only-of-type 选择每个p元素是其父级的唯一p元素 3
      • :only-child p:only-child 选择每个p元素是其父级的唯一子元素 3
      • :nth-child(n) p:nth-child(2) 选择每个p元素是其父级的第二个子元素 3
      • :nth-last-child(n) p:nth-last-child(2) 选择每个p元素的是其父级的第二个子元素,从最后一个子项计数 3
      • :nth-of-type(n) p:nth-of-type(2) 选择每个p元素是其父级的第二个p元素 3
      • :nth-last-of-type(n) p:nth-last-of-type(2) 选择每个p元素的是其父级的第二个p元素,从最后一个子项计数 3
      • :last-child p:last-child 选择每个p元素是其父级的最后一个子级。 3
      • :root :root 选择文档的根元素 3
      • :empty p:empty 选择每个没有任何子级的p元素(包括文本节点) 3
      • :target #news:target 选择当前活动的#news元素(包含该锚名称的点击的URL) 3
      • :enabled input:enabled 选择每一个已启用的输入元素 3
      • :disabled input:disabled 选择每一个禁用的输入元素 3
      • :checked input:checked 选择每个选中的输入元素 3
      • :not(selector) :not(p) 选择每个并非p元素的元素 3
      • ::selection ::selection 匹配元素中被用户选中或处于高亮状态的部分 3
      • :out-of-range :out-of-range 匹配值在指定区间之外的input元素 3
      • :in-range :in-range 匹配值在指定区间之内的input元素 3
      • :read-write :read-write 用于匹配可读及可写的元素 3
      • :read-only :read-only 用于匹配设置 “readonly”(只读) 属性的元素 3
      • :optional :optional 用于匹配可选的输入元素 3
      • :required :required 用于匹配设置了 “required” 属性的元素 3
      • :valid :valid 用于匹配输入值为合法的元素 3
      • :invalid :invalid 用于匹配输入值为非法的元素
    • CSS3 边框(Borders)
      • 用CSS3,你可以创建圆角边框,添加阴影框,并作为边界的形象而不使用设计程序
      • border-image 设置所有边框图像的速记属性。 3
      • border-radius 一个用于设置所有四个边框- *-半径属性的速记属性 3
      • box-shadow 附加一个或多个下拉框的阴影 3
      • div
      • {
      • border:2px solid;
      • border-radius:25px;
      • box-shadow: 10px 10px 5px #888888;
      • border-image:url(border.png) 30 30 round;
      • }
    • CSS3 背景
      • CSS3中包含几个新的背景属性,提供更大背景元素控制。
      • background-clip 规定背景的绘制区域。 3
      • background-origin 规定背景图片的定位区域。 3
      • background-size 规定背景图片的尺寸。 3
      • div
      • {
      • background:url(img_flwr.gif);
      • background-repeat:no-repeat;
      • background-size:100% 100%;
      • background-origin:content-box;
      • }
      • 多背景
      • body
      • {
      • background-image:url(img_flwr.gif),url(img_tree.gif);
      • }
    • CSS3 渐变
      • CSS3 定义了两种类型的渐变(gradients):
        • 线性渐变(Linear Gradients)- 向下/向上/向左/向右/对角方向
          • background: linear-gradient(direction, color-stop1, color-stop2, …);
        • 径向渐变(Radial Gradients)- 由它们的中心定义
          • background: radial-gradient(center, shape size, start-color, …, last-color);
    • CSS3 文本效果
      • hanging-punctuation 规定标点字符是否位于线框之外。 3
      • punctuation-trim 规定是否对标点字符进行修剪。 3
      • text-align-last 设置如何对齐最后一行或紧挨着强制换行符之前的行。 3
      • text-emphasis 向元素的文本应用重点标记以及重点标记的前景色。 3
      • text-justify 规定当 text-align 设置为 “justify” 时所使用的对齐方法。 3
      • text-outline 规定文本的轮廓。 3
      • text-overflow 规定当文本溢出包含元素时发生的事情。 3
      • text-shadow 向文本添加阴影。 3
      • text-wrap 规定文本的换行规则。 3
      • word-break 规定非中日韩文本的换行规则。 3
      • word-wrap 允许对长的不可分割的单词进行分割并换行到下一行。 3
    • CSS3 字体
      • 以前CSS3的版本,网页设计师不得不使用用户计算机上已经安装的字体。使用CSS3,网页设计师可以使用他/她喜欢的任何字体。当你发现您要使用的字体文件时,只需简单的将字体文件包含在网站中,它会自动下载给需要的用户。您所选择的字体在新的CSS3版本有关于@font-face规则描述。您”自己的”的字体是在 CSS3 @font-face 规则中定义的。
    • CSS3 转换和变形
      • 2D新转换属性
        • 以下列出了所有的转换属性:
        • transform 适用于2D或3D转换的元素 3
        • transform-origin 允许您更改转化元素位置
      • 2D 转换方法
        • matrix(n,n,n,n,n,n) 定义 2D 转换,使用六个值的矩阵。
        • translate(x,y) 定义 2D 转换,沿着 X 和 Y 轴移动元素。
        • translateX(n) 定义 2D 转换,沿着 X 轴移动元素。
        • translateY(n) 定义 2D 转换,沿着 Y 轴移动元素。
        • scale(x,y) 定义 2D 缩放转换,改变元素的宽度和高度。
        • scaleX(n) 定义 2D 缩放转换,改变元素的宽度。
        • scaleY(n) 定义 2D 缩放转换,改变元素的高度。
        • rotate(angle) 定义 2D 旋转,在参数中规定角度。
        • skew(x-angle,y-angle) 定义 2D 倾斜转换,沿着 X 和 Y 轴。
        • skewX(angle) 定义 2D 倾斜转换,沿着 X 轴。
        • skewY(angle) 定义 2D 倾斜转换,沿着 Y 轴。
      • 3D转换属性
      • 3D 转换方法
        • matrix3d(n,n,n,n,n,n,n,n,n,n,n,n,n,n,n,n) 定义 3D 转换,使用 16 个值的 4x4 矩阵。
        • translate3d(x,y,z) 定义 3D 转化。
        • translateX(x) 定义 3D 转化,仅使用用于 X 轴的值。
        • translateY(y) 定义 3D 转化,仅使用用于 Y 轴的值。
        • translateZ(z) 定义 3D 转化,仅使用用于 Z 轴的值。
        • scale3d(x,y,z) 定义 3D 缩放转换。
        • scaleX(x) 定义 3D 缩放转换,通过给定一个 X 轴的值。
        • scaleY(y) 定义 3D 缩放转换,通过给定一个 Y 轴的值。
        • scaleZ(z) 定义 3D 缩放转换,通过给定一个 Z 轴的值。
        • rotate3d(x,y,z,angle) 定义 3D 旋转。
        • rotateX(angle) 定义沿 X 轴的 3D 旋转。
        • rotateY(angle) 定义沿 Y 轴的 3D 旋转。
        • rotateZ(angle) 定义沿 Z 轴的 3D 旋转。
        • perspective(n) 定义 3D 转换元素的透视视图。
    • CSS3 过渡
      • 过渡属性
      • 下表列出了所有的过渡属性:
      • div
      • {
      • transition-property: width;
      • transition-duration: 1s;
      • transition-timing-function: linear;
      • transition-delay: 2s;
      • / Safari /
      • -webkit-transition-property:width;
      • -webkit-transition-duration:1s;
      • -webkit-transition-timing-function:linear;
      • -webkit-transition-delay:2s;
      • }
    • CSS3 动画
      • 要创建CSS3动画,你需要了解@keyframes规则。@keyframes规则是创建动画。 @keyframes规则内指定一个CSS样式和动画将逐步从目前的样式更改为新的样式。
      • 实例
      • 当动画为 25% 及 50% 时改变背景色,然后当动画 100% 完成时再次改变:
      • @keyframes myfirst
      • {
      • 0% {background: red;}
      • 25% {background: yellow;}
      • 50% {background: blue;}
      • 100% {background: green;}
      • }
      • 下面的表格列出了 @keyframes 规则和所有动画属性:
        • @keyframes 规定动画。 3
        • animation 所有动画属性的简写属性,除了 animation-play-state 属性。 3
        • animation-name 规定 @keyframes 动画的名称。 3
        • animation-duration 规定动画完成一个周期所花费的秒或毫秒。默认是 0。 3
        • animation-timing-function 规定动画的速度曲线。默认是 “ease”。 3
        • animation-delay 规定动画何时开始。默认是 0。 3
        • animation-iteration-count 规定动画被播放的次数。默认是 1。 3
        • animation-direction 规定动画是否在下一周期逆向地播放。默认是 “normal”。 3
        • animation-play-state 规定动画是否正在运行或暂停。默认是 “running”。 3
        • div
        • {
        • animation-name: myfirst;
        • animation-duration: 5s;
        • animation-timing-function: linear;
        • animation-delay: 2s;
        • animation-iteration-count: infinite;
        • animation-direction: alternate;
        • animation-play-state: running;
        • / Safari and Chrome: /
        • -webkit-animation-name: myfirst;
        • -webkit-animation-duration: 5s;
        • -webkit-animation-timing-function: linear;
        • -webkit-animation-delay: 2s;
        • -webkit-animation-iteration-count: infinite;
        • -webkit-animation-direction: alternate;
        • -webkit-animation-play-state: running;
        • }
    • CSS3 多列
    • CSS3 盒模型
      • 在 CSS3 中, 增加了一些新的用户界面特性来调整元素尺寸,框尺寸和外边框,主要包括以下用户界面属性:
        • resize:none | both | horizontal | vertical | inherit
        • box-sizing: content-box | border-box | inherit
        • outline:outline-color outline-style outline-width outine-offset
      • resize属性指定一个元素是否应该由用户去调整大小。
      • box-sizing 属性允许您以确切的方式定义适应某个区域的具体内容。
      • outline-offset 属性对轮廓进行偏移,并在超出边框边缘的位置绘制轮廓。
    • CSS3伸缩布局盒模型(弹性盒)
      • CSS3 弹性盒( Flexible Box 或 flexbox),是一种当页面需要适应不同的屏幕大小以及设备类型时确保元素拥有恰当的行为的布局方式。
      • 引入弹性盒布局模型的目的是提供一种更加有效的方式来对一个容器中的子元素进行排列、对齐和分配空白空间。
      • 下表列出了在弹性盒子中常用到的属性:
        • display 指定 HTML 元素盒子类型。
        • flex-direction 指定了弹性容器中子元素的排列方式
        • justify-content 设置弹性盒子元素在主轴(横轴)方向上的对齐方式。
        • align-items 设置弹性盒子元素在侧轴(纵轴)方向上的对齐方式。
        • flex-wrap 设置弹性盒子的子元素超出父容器时是否换行。
        • align-content 修改 flex-wrap 属性的行为,类似 align-items, 但不是设置子元素对齐,而是设置行对齐
        • flex-flow flex-direction 和 flex-wrap 的简写
        • order 设置弹性盒子的子元素排列顺序。
        • align-self 在弹性子元素上使用。覆盖容器的 align-items 属性。
        • flex 设置弹性盒子的子元素如何分配空间。
    • CSS3 多媒体查询
      • 从 CSS 版本 2 开始,就可以通过媒体类型在 CSS 中获得媒体支持。如果您曾经使用过打印样式表,那么您可能已经使用过媒体类型。清单 1 展示了一个示例。
      • 清单 1. 使用媒体类型
      • 清单 2. 媒体查询规则
        • @media all and (min-width: 800px) { … }
        • @media all 是媒体类型,也就是说,将此 CSS 应用于所有媒体类型。
        • (min-width:800px) 是包含媒体查询的表达式,如果浏览器的最小宽度为 800 像素,则会告诉浏览器只运用下列 CSS。
      • 清单 3. and 条件
        • @media (min-width:800px) and (max-width:1200px) and (orientation:portrait) { … }
      • 清单 4. or 关键词
        • @media (min-width:800px) or (orientation:portrait) { … }
      • 清单 5. 使用 not
        • @media (not min-width:800px) { … }
  • 16.可以通过哪些方法优化 css3 animation 渲染
  • 44.HTML5 的离线储存怎么使用,工作原理能不能解释一下?
  • 45.浏览器是怎么对 HTML5 的离线储存资源进行管理和加载的呢
  • 47.WEB 标准以及 W3C 标准是什么?理解和认识。
  • 7.HTML5 为什么只写<!DOCTYPE HTML>?
  • 51.你知道多少种 Doctype 文档类型?
  • Doctype 作用? 严格模式与混杂模式如何区分?【严格模式不混杂模式-如何触发这两种模式】它们有何意义?
  • 49.HTML 全局属性(global attribute)有哪些
  • HEADER标签内一般有什么内容
  • 51.如何在页面上实现一个圆形的可点击区域?
  • 115,请你说说 CSS 有什么特殊性?(优先级、计算特殊值)
  • 60.为什么要初始化 CSS 样式
  • 109,说说你对 HTML5 认识?(是什么,为什么)
  • 14,HTML5 的优点与缺点?
  • 58,html5 有哪些新特性、移除了那些元素?如何处理 HTML5 新标签的浏览器兼容问题?如何区分 HTML 和 HTML5?
    1. 有没有关注 HTML5 和 CSS3?如有请简单说一些您对它们的了解情况!【https://www.cnblogs.com/star91/p/5659134.html
  • 58.用纯 CSS 创建一个三角形的原理是什么?【https://blog.csdn.net/pengjunlee/article/details/53002553?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-18.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-18.control】【https://blog.csdn.net/lxcao/article/details/52689313?utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control
  • 20.css 定位方式【https://blog.csdn.net/weixin_38055381/article/details/81558288】【https://www.cnblogs.com/demonswang/p/7161290.html
  • 153,介绍一下 box-sizing 属性?
  • 62.CSS 里的 visibility 属性有个 collapse 属性值?在不同浏览器下以后什么区别?
  • 118,列出 display 的值并说明他们的作用?
    • image.png
  • 63.display:none 与 visibility:hidden 的区别?
  • 83.display:inline-block 什么时候会显示间隙?
  • 122,block,inline 和 inlinke-block 细节对比?
  • 98.列出 display 的值,说明他们的作用。position 的值, relative 和 absolute 分别是相对于谁进行定位的?
  • 61.absolute 的 containing block 计算方式跟正常流有什么不同?
  • 95.position 的 absolute 与 fixed 共同点与不同点
  • 152,position:absolute 和 float 属性的异同
    1. position:fixed;在 android 下无效怎么处理?
  • 2,position 的值, relative 和 absolute 分别是相对于谁进行定位的?
  • 64.position 跟 display、overflow、float 这些特性相互叠加后会怎么样?
  • 73.margin 和 padding 分别适合什么场景使用?
  • 74.元素竖向的百分比设定是相对于容器的高度吗?
  • 75.全屏滚动的原理是什么?用到了 CSS 的哪些属性?【https://blog.csdn.net/tangdou5682/article/details/52351404?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.essearch_pc_relevant&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.essearch_pc_relevant
  • 79.让页面里的字体变清晰,变细用 CSS 怎么做?
  • 82.li 与 li 之间有看不见的空白间隔是什么原因引起的?有什么解决办法?
  • 85.png、jpg、gif 这些图片格式解释一下,分别什么时候用。有没有了解过 webp?
  • 关于overflow:hidden的作用(溢出隐藏、清除浮动、解决外边距塌陷等等)
  • 87.CSS 属性 overflow 属性定义溢出元素内容区的内容会如何处理?
  • 102.解释下 CSS sprites,以及你要如何在页面或网站中使用它。
    1. 一行或多行文本超出隐藏overflow:hidden; //超出的文本隐藏 text-overflow:ellipsis; //溢出用省略号显示 white-space:nowrap; //溢出不换行
    1. 前端页面有哪三层构成,分别是什么?作用是什么?
    1. css 的基本语句构成是?
    1. 如果让你来制作一个访问量很高的大型网站,你会如何来管理所有 CSS 文件、JS 、图片?
  • 1、列举 W3C 推荐的属性标签,说一下 p 和 img 标签的特点。
  • 常规流布局
  • 块盒
  • 块级格式化上下文
  • 浮动
  • 定位
  • 浮动、定位、弹性、table、Grid 网格布局 :优缺点
  • 页面布局的变通
  • CSS的position定位
  • 圣杯布局、双飞翼布局、Flex布局和绝对定位布局
  • H5:div 横向排列的方法。
  • ul、li导航栏居中的两种办法
  • 22.垂直上下居中的方法
    1. 如何居中一个浮动元素?
  • 3、如何让 img 标签在 div 里上下居中
  • 119,如何居中 div
  • 水平居中、 垂直居中、 垂直水平居中
  • 水平垂直居中【https://www.jianshu.com/p/907f99004c3e
  • 23.响应式布局原理【https://blog.csdn.net/sinat_17775997/article/details/89087348】【https://www.jianshu.com/p/d0d29fb7647f
    1. 有一个高度自适应的 div,里面有两个 div,一个高度 100px,希望另一个填满剩下的高度<!DOCTYPE html>
  • 左边导航右边自适应【https://www.cnblogs.com/vicky123/p/8866548.html
  • 2、实现左侧规定宽 200,右侧自适应宽度的布局
  • 三栏布局
  • 三栏布局
  • 弹性布局(display:flex;)属性详解
  • grip布局【http://www.ruanyifeng.com/blog/2019/03/grid-layout-tutorial.html
  • 解释一下Flexbox (弹性盒布局模型)?及适用场景?【https://www.ruanyifeng.com/blog/2015/07/flex-grammar.html
  • display:table的用法
  • 响应布局【https://blog.csdn.net/sinat_17775997/article/details/89087348
  • margin塌陷【https://blog.csdn.net/qq_32381815/article/details/78987828】【https://blog.csdn.net/shi_1204/article/details/80180224?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.essearch_pc_relevant&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.essearch_pc_relevant
  • H5标签、语义化标签【http://caibaojian.com/html5/ele.html】【https://www.cnblogs.com/htzan/p/4516057.html
  • 43.语义化的理解
    • JS
    • 网络
    • 组件库
    • Vue
  • 生命周期函数面试题
    • Vue的核心是什么
      • Vue是一套构建用户界面的渐进式自底向上增量开发的MWM框架,
      • vue的核心只关注视图层,
      • 核心思想:
        • 数据驱动(视图的内容随着数据的改变而改变)
        • 组件化(可以增加代码的复用性,可维护性,可测试性,提高开发效率,方便重复使用,体现了高内聚低偶合)
    • vue响应式原理【https://segmentfault.com/a/1190000019700618
      • 1.响应式原理
        • Vue 的响应式原理核心是通过 ES5 的保护对象的 Object.defindeProperty 中的访问器属性中的 get 和 set 方法,data 中声明的属性都被添加了访问器属性,当读取 data 中的数据时自动调用 get 方法,当修改 data 中的数据时,自动调用 set 方法,检测到数据的变化,会通知观察者 Wacher,观察者 Wacher自动触发重新render 当前组件(子组件不会重新渲染),生成新的虚拟 DOM 树,Vue 框架会遍历并对比新虚拟 DOM 树和旧虚拟 DOM 树中每个节点的差别,并记录下来,最后,加载操作,将所有记录的不同点,局部修改到真实 DOM 树上。
        • 在生成vue实例时,为对传入的data进行遍历,使用Object.defineProperty把这些属性转为getter/setter.
        • Object.defineProperty 是 ES5 中一个无法 shim 的特性,这也就是 Vue 不支持 IE8 以及更低版本浏览器的原因。
        • 每个vue实例都有一个watcher实例,它会在实例渲染时记录这些属性,并在setter触发时重新渲染。
          • image.png
        • 虚拟DOM (Virtaul DOM): 用 js 对象模拟的,保存当前视图内所有 DOM 节点对象基本描述属性和节点间关系的树结构。用 js 对象,描述每个节点,及其父子关系,形成虚拟 DOM 对象树结构。
        • Vue 无法检测到对象属性的添加或删除
        • Vue 不允许动态添加根级别的响应式属性。但是,可以使用 Vue.set(object, propertyName, value)方法向嵌套对象添加响应式属性。
      • 2.声明响应式属性
        • 由于 Vue 不允许动态添加根级响应式属性,所以你必须在初始化实例前声明所有根级响应式属性,哪怕只是一个空值。
        • 如果你未在 data 选项中声明 message,Vue 将警告你渲染函数正在试图访问不存在的属性。
      • 3.异步更新队列
        • vue更新dom时是异步执行的
        • 数据变化、更新是在主线程中同步执行的;在侦听到数据变化时,watcher将数据变更存储到异步队列中,当本次数据变化,即主线成任务执行完毕,异步队列中的任务才会被执行(已去重)。
        • 如果你在js中更新数据后立即去操作DOM,这时候DOM还未更新;vue提供了nextTick接口来处理这样的情况,它的参数是一个回调函数,会在本次DOM更新完成后被调用。
        • 使用方法:
          • 1.在组件内使用 vm.$nextTick() 实例方法特别方便,因为它不需要全局 Vue,并且回调函数中的 this 将自动绑定到当前的 Vue 实例上:Vue.component(‘example’, { template: ‘{{ message }}‘, data: function () { return { message: ‘未更新’ } }, methods: { updateMessage: function () { this.message = ‘已更新’ console.log(this.$el.textContent) // => ‘未更新’ this.$nextTick(function () { console.log(this.$el.textContent) // => ‘已更新’ }) } } })
          • 2.因为 $nextTick() 返回一个 Promise 对象,所以你可以使用新的 ES2016 async/await 语法完成相同的事情:methods: { updateMessage: async function () { this.message = ‘已更新’ console.log(this.$el.textContent) // => ‘未更新’ await this.$nextTick() console.log(this.$el.textContent) // => ‘已更新’ } }
      • 项目中常遇到的关于vue响应式的记录与总结:
        • 因为只要在 data 中声明的基本数据类型的数据,基本不存在数据不响应问题,所以重点介绍数组和对象在vue中的数据响应问题,vue可以检测对象属性的修改,但无法监听数组的所有变动及对象的新增和删除,只能使用数组变异方法及$set方法。
          • image.png
        • 可以看到,arrayMethods 首先继承了 Array,然后对数组中所有能改变数组自身的方法,如 push、pop 等这些方法进行重写。重写后的方法会先执行它们本身原有的逻辑,并对能增加数组长度的 3 个方法 push、unshift、splice 方法做了判断,获取到插入的值,然后把新添加的值变成一个响应式对象,并且再调用 ob.dep.notify() 手动触发依赖通知,这就很好地解释了用 vm.items.splice(newLength) 方法可以检测到变化。。
          1. 向响应式的数组或者对象中修改已有的属性的方法
            • 当想要修改对象或者属性,并非新增属性时,一个已经在 data 中声明过的响应式数据,可以直接操作改变,数据改变会经过上图的步骤,触发视图改变。直接obj.xxx = xxx 即可,数组除外,但是后台传过来的 json 数组,数组中嵌套的对象也可以直接修改数组中的对象,因为 Object.defindeProperty 的缺陷导致无法监听数组的变动,但始终会深度遍历data中数据,给数组中嵌套的对象添加上 get 和 set 方法,完成对对象的监听。所以数组中嵌套的对象的情况是可以直接修改数组中的对象,并且保持响应式。
          1. 向响应式的数组或者对象中新增一个响应式的属性的方法this.$set()或者数组变异方法
            • 即使是一个后台传过来的 json 数组,也可以使用this.$set向数组中的其中一个对象中添加一个响应式的属性,例如 this.$set(arr[0], ‘xxx’, xxx) 。或者使用数组变异方法例如splice,更多数组变异方法可以参考vue文档。
          1. data中声明过的数组或者对象,整体替换数组或者对象保持响应式
            • 向响应式的数组和对象替换为新的响应式数据,可直接复制,因为data中声明的数据已经添加了访问器属性setter,当重新赋值一个新的堆内存地址时,该数组或者对象也会被循环遍历添加访问器属性,所以也是有响应式的。
              • image.png
          1. vue无法监听对象的新增和删除,直接通过obj.xxx = xxx新增一个没有的属性,同时修改当前组件的一个响应式的数据,会重新触发当前组件重新render,可以让非响应式数据也保持更新状态(并非响应式) 。
            • 给一个数据添加一个非响应式的数据,例如一个已经在data中声明过的数据obj,obj.xxx=xxx,新增一个原本没有的数据,同时修改组件中一个其他的响应式数据,该obj也会同步更新到最新的数据,另一种情况,当你向一个对象或者数组中同时增加一个响应式和非响应式数据,非响应式数据也会同步更新到页面。
            • 总结:只要触发当前组件重新render,就可以让数据保持更新的状态,例如this.$forceUpdate()。
              • image.png
      • 为什么vue不能监听数组的变化?
      • Object.defindProperty虽然能够实现双向绑定了,但是还是有缺点,只能对对象的属性进行数据劫持,所以会深度遍历整个对象,不管层级有多深,只要数组中嵌套有对象,就能监听到对象的数据变化无法监听到数组的变化,Proxy就没有这个问题,可以监听整个对象的数据变化,所以用vue3.0会用Proxy代替definedProperty。
      • 最后实现一个数据双向绑定原理
        • image.png
      • 更深的底层原理还在学习中,完全消化以后会继续分享,嗯,就酱~
    • 请简述你对vue的理解
      • Vue是一套构建用户界面的渐进式的自底向上增量开发的MWM框架,核心是关注视图层,vue的核心是为了解决数据的绑定问题,为了开发大型单页面应用和组件化,所以vue的核心思想是数据驱动和组件化,这里 也 说 一 下MWM思 想 ,MVVM思想是 模型 视 图 vm是v和m连接的桥梁,当模型层数据修改时,VM层会检测到,并通知视图层进行相应修改
    • 1.vue 优点
      • 1、低耦合。视图(view)可以独立于model变化和修改,一个viewModel可以绑定到不同的”view“上,当view变化的时候,model可以不变,当model变化的时候view也可以不变。
      • 2、可重用性。你可以把一些视图逻辑放在一个viewModel里面,让很多view重用这段视图逻辑。
      • 3、独立开发。开发人员可以专注于业务逻辑和数据的开发,设计人员可以专注于页面设计。
      • 4、可测试。界面素来是比较难测试的,而现在测试可以针对viewModel来写。
    • vue的整个实现流程?
      • 1、第一步:解析模板成render函数
        • template
      • 2、第二步:响应式开始监听
        • object.defineProperty
        • data属性代理到vm上
      • 3、第三步:首次渲染,显示页面,且绑定依赖
        • (1)为何要监听get,直接监听set不行吗?
          • ①data中有很多属性,有些被用到,有些可能不被用到(data中没有人访问,就不会用get,如没有{{aaa}}指的就是aaa没有被访问)
          • ②被用到的会走到get,不被用到的不会走到get
          • ③未走到get中的属性,set的时候也无需关心
          • ④避免不必要的重复渲染
      • 4、第四步:data属性变化,触发rerender
        • defineProperty, get, set
        • (1)修改属性,被响应式的set监听到
        • (2)set中执行updateComponent
        • (3)updateComponent重新执行vm._render()
        • (4)生成的vnode和prevVnode,通过Patch进行对比渲染到html
    • 1.什么是 vue 生命周期
      • 一、vue的生命周期是什么
        • vue每个组件都是独立的,每个组件都有一个属于它的生命周期,从一个组件创建、数据初始化、挂载、更新、销毁,这就是一个组件所谓的生命周期。在组件中具体的方法有: beforeCreate created beforeMount mounted ( beforeUpdate updated ) beforeDestroy destroyed
        • 对应的中文就如其字面意思,英文不好的童鞋可以有道翻翻
        • 好了,这里要上图啦~~~
          • image.png
      • 二、vue生命周期的在项目中的执行顺序… data () { return { rendered: false, } } …
        • 1.beforeCeate(){
        • console.log(this.rendered); // undefined
        • }
          • image.png
        • 2.created() {
        • console.log(this.$el);//undefined
        • console.log(this.rendered); // false
        • }
          • image.png
        • 3.beforeMount() {
        • console.log(this.$el);//undefined
        • }
          • image.png
        • 4.mounted() {
        • console.log(this.$el);
        • }
          • image.png
        • 5.beforeDestroy(){
        • console.log(this.$el);
        • console.log(this.rendered);
        • }
          • image.png
        • 6.destroyed() {
        • console.log(this.$el);
        • console.log(this.rendered);
        • }
          • image.png
      • 三、vue中内置的方法 属性和vue生命周期的运行顺序(methods、computed、data、watch、props)
        • 从第一二点可知道data的初始化是在created时已经完成数据观测(data observer),并且诸如methods、computed属性 props等已经初始化;那问题来了,
        • data props computed watch methods他们之间的生成顺序是什么呢?
        • 根据翻看vue源码可知:
          • image.png
        • props => methods =>data => computed => watch; 懂了没
      • 四、自己构造的方法与vue生命周期的运行顺序 如show这些
        • 往往我们在开发项目时都经常用到 $refs 来直接访问子组件的方法,但是这样调用的时候可能会导致数据的延迟滞后的问题,则会出现bug。
        • 解决方法则是推荐采取异步回调的方法,然后传参进去,严格遵守vue的生命周期就可以解决 推荐 es6 的promise。
        • 示例代码:handleAsync () { return new Promise(resolve=>{ const res=””; resolve(res) }) } … async handleShow() { await this.handleAsync().then(res=>{ this.$refs.child.show(res); }) } …
      • 五、总结
        • vue 的生命周期,总得来说就是实例的创建和销毁这段时间的一个机制吧。也是vue框架的数据间的交互通信。其实现在看来也没那么难,但是vue的源码实现这一套机制那是难得一逼,涉及到复杂的算法如diff算法
    • Vue的生命周期请简述
      • vue的生命周期就是vue实例创建到实例销毁的过程。期间会有8个钩子函数的调用。
      • beforeCreate (创建实例)
      • created (创建完成)、
      • beforeMount (开始创建模板)
      • mounted (创建完成)、
      • beforeUpdate ( 开始更新)
      • updated (更新完成)、
      • beforeDestroy (开始销毁)
      • destroyed (销毁完成)
    • Vue生命周期一共几个阶段
      • 创建 加载 更新 销毁
      • Beforecreate 创建前
      • Created 创建后
      • Beforemount 加载前
      • Mounted 加载后
      • Beforeupdate 更新前
      • Updated 更新后
      • Beforedestroy 销毁前
      • Destroyed 销毁后
      • 页 面 第一次 加 载 会 触 发
      • beforecreate created beforemount mounted
      • DOM渲染 mounted 周期中就已经完成
    • 2.vue 生命周期的作用是什么
      • 它的生命周期中有多个事件钩子,让我们在控制整个Vue实例的过程时更容易形成好的逻辑。
    • 说下 vue 生命周期钩子函数?
      • 每个 vue 实例在被创建时都要经过一系列的初始化过程。
      • 所有的生命周期钩子自动绑定 this 上下文到实例中,因此可以在函数中访问数据,对属性和方法进行运算。这意味着不能使用箭头函数来定义一个生命周期方法【这是因为箭头函数绑定了父上下文,因此 this 与你期待的 Vue 实例不同】。
      • vue 的生命周期图:
        • image.png
        • 阶段一:Vue 实例创建阶段
          • beforeCreate
            • Vue 实例在内存中刚被创建,this 变量还不能使用,数据对象(data)和方法(methods)未初始化,watcher 中的事件都不能获得到;
          • created
            • 实例已经在内存中创建好,数据和方法已经初始化完成,但是模板还未编译,页面还是没有内容,还不能对 dom 节点进行操作(此时访问 this.$el 和 this.$refs.xxx 都是undefined)
          • beforeMounte
            • 找到对应的 template 模板,编译成 render 函数,转换成虚拟 dom,此时模板已经编译完成,数据未挂载到页面,也就是说在这个阶段你可以看到标签间的双花括号,数据还未渲染到页面中;
          • render : h=>h(App)
            • 在 beforeMounte 之后和 mounted 之前,还有渲染 render 函数,它的作用是把模板渲染成虚拟 dom。
          • mounted
            • 模板编译好了,虚拟 dom 渲染成真正的 dom 标签,数据渲染到页面,此时 Vue 实例已经创建完毕,如果没有其他操作的话,Vue 实例会静静的躺在内存中,一动不动。
            • 一般会在 mounted 中来渲染从后端获取的数据。(页面初始化时,如果有操作 dom 的事件一般也会放在 mounted 钩子函数中。当然,也可以放在 create 中,前提需使用this.$nextTick(function(){}),在回调函数中操作 dom。)
        • 阶段二:Vue 实例运行阶段
          • beforeUpdate
            • 数据依赖改变或者用 $forceUpdata 强制刷新时,对象 data 中的数据已经更改(虚拟 dom 已经重新渲染),但是 页面中的值还是原来,未改变,因为此时还未开始渲染 dom;
          • update
            • 此时 data 中的数据和页面更新完毕,页面已经被重新渲染。
            • 在实际开发中,一般会用监听器 watch 来代替上边 2 个方法,因为 watch 会知道是哪一个数据变化。
        • 阶段三:Vue 实例销毁阶段
          • beforeDestroy
            • 实例销毁前使用,在此刻实例还是可用的。
          • destroyed
            • Vue 实例被销毁,观察者、子组件、事件监听被清除(页面数据不会消失,只不过是响应式无效了)。
    • 7.请详细说下你对 vue 生命周期的理解?
      • 一.Vue生命周期简介
        • 官网:https://cn.vuejs.org/v2/api/#beforeCreate
        • Vue实例从创建到销毁的过程,就是生命周期。详细来说也就是从开始创建、初始化数据、编译模板、挂载Dom、渲染→更新→渲染、卸载等一系列过程。
        • 首先我们来看一下官网的生命周期图(我自己做了一点点注释):
          • image.png
          • image.png
          • image.png
          • image.png
        • Vue提供给我们的钩子为上图的红色的文字
      • 二.钩子详解
        • 1.beforeCreate
          • 在实例初始化之后,数据观测(data observer) 和 event/watcher 事件配置之前被调用。<!DOCTYPE html>
            {{message}}
          • 我们在上面的例子中在的beforeCreate钩子中调用Vue的data和method,来看一下结果:
            • image.png
          • 可以看到Vue中的data和方法都是去不到的,并且是在wath之前执行
        • 2.created
          • 实例已经创建完成之后被调用。在这一步,实例已完成以下的配置:数据观测(data observer),属性和方法的运算, watch/event 事件回调。然而,挂载阶段还没开始,$el 属性目前不可见。
          • 主要应用:调用数据,调用方法,调用异步函数
            • {{item}}—{{message}}

            p1

            p1

            p1

          • 结果
            • image.png
          • 可以看到:created钩子可以获取Vue的data,调用Vue方法,获取原本HTML上的直接加载出来的DOM,但是无法获取到通过挂载模板生成的DOM(例如:v-for循环遍历Vue.list生成li)
        • 3.beforeMount
          • 在挂载开始之前被调用:相关的 render 函数(模板)首次被调用。
          • 例如通过v-for生成的html还没有被挂载到页面上 (接 2created的代码)beforeMount: function () { console.log(‘beforeMount:’,document.getElementsByTagName(‘li’).length); },
          • 结果 beforeMount: 1
        • 4.mounted
          • el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。
          • 有初始值的DOM渲染,例如我们的初始数据list,渲染出来的li,只有这里才能获取 (接 2created的代码)mounted: function () { console.log(‘mounted:’,document.getElementsByTagName(‘li’).length); },
          • 结果 mounted: 3
          • 可以看到到这里为止,挂载到实例上了,我们可以获取到li的个数了
        • 5.beforeUpdate
          • 数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。 你可以在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程。
          • 当我们更改Vue的任何数据,都会触发该函数beforeUpdate: function () { console.log(‘beforeUpdate 钩子执行…’); console.log(‘beforeUpdate:’+this.message) },
        • 6.updated
          • 由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。
          • 当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态,因为这可能会导致更新无限循环。
          • 该钩子在服务器端渲染期间不被调用。
          • 数据更新就会触发(vue所有的数据只有有更新就会触发),如果想数据一遍就做统一的处理,可以用这个,如果想对不同数据的更新做不同的处理可以用nextTick,或者是watch进行监听updated: function () { console.log(‘updated 钩子执行…’); console.log(‘updated:’,this.message) },
        • 7.beforeDestroy
          • 实例销毁之前调用。在这一步,实例仍然完全可用。
        • 8.destroyed
          • Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。 该钩子在服务器端渲染期间不被调用。
          • 结果:
            • image.png
          • 可以看打到销毁Vue实例时会调用这两个函数
        • 补充$mount
          • 当你vue没有挂在el时,我们可以用$mountvar app = new Vue({ data:{ message:’this is mseeage’, }, }).$mount(‘#app’)
      • 三.钩子的一些实战用法
      • 1.异步函数
        • 这里我们用定时器来做异步函数
          • {{item}}
        • 结果为:
          • create: aaaaaaaa
          • mounted: 3
          • created异步函数: 3
          • updated: 4
        • 解释:
          • 可以看到因为是在created的钩子中加入异步函数,所以函数的执行顺序为:
            • ceated钩子,mounted钩子,异步函数,updated钩子(根据事件队列原理,只有在updated后,li才是真的DOM渲染为4个,所以异步函数中获取的li的个数时是没有变化的li的个数)。
            • 因为mounted获取到的是我们在Vue的data中设置初始值渲染的DOM,而我们是在异步函数中变化的list数据,所以mounted获取的li的个数为3。
            • update函数是只要数据vue绑定的数据变化就会触发,所以最后执行,为4
            • 这是不是意味着可以直接在update函数中操作呢,其实不是,因为update函数是针对vue的所有数据的变化,而我们也有可能会有其他数据的变化。
        • 例如下面的例子://我们利用异步函数改变了两次list,会发现update被触发了2次 created:function(){ //异步获取数据 // 因为是异步,就和我们ajax获取数据一样 setTimeout(()=>{ this.list=[‘111’,’222’,’333’,’444’], console.log(‘created异步:’,document.getElementsByTagName(‘li’).length); },0) setTimeout(()=>{ this.list=[‘快乐大本营’,’脚踏实地’,’300033’,’天天向上’,’好好学习’], console.log(‘created异步:’,document.getElementsByTagName(‘li’).length); },1000) }, mounted: function () { console.log(‘mounted:’,document.getElementsByTagName(‘li’).length); }, updated: function () { console.log(‘updated:’,document.getElementsByTagName(‘li’).length) },
        • 结果为:
          • image.png
      • 2.Vue.nextTick对异步函数的结果进行操作
        • 我们想要改变数据时,各自触发各自的方法created:function(){ //异步获取数据 // 因为是异步,就和我们ajax获取数据一样 //为了在数据变化之后等待 Vue 完成更新 DOM ,可以在数据变化之后立即使用 Vue.nextTick(callback) 。这样回调函数在 DOM 更新完成后就会调用。 setTimeout(()=>{ this.list=[‘111’,’222’,’333’,’444’], console.log(‘created异步:’,document.getElementsByTagName(‘li’).length); this.$nextTick(function(){ console.log(“created$nextTick:”,document.getElementsByTagName(‘li’).length) }); },0) setTimeout(()=>{ this.list=[‘快乐大本营’,’脚踏实地’,’300033’,’天天向上’,’好好学习’], console.log(‘created异步:’,document.getElementsByTagName(‘li’).length); this.$nextTick(function(){ console.log(“created$nextTick:”,document.getElementsByTagName(‘li’).length) }); },1000) }, mounted: function () { console.log(‘mounted:’,document.getElementsByTagName(‘li’).length); }, updated: function () { console.log(‘updated:’,document.getElementsByTagName(‘li’).length) },
        • 结果:
          • image.png
        • 我们可以看到通过$nextTick我们可以对异步函数的结果进行各自的操作
    • 4.简述每个周期具体适合哪些场景
      • 1、beforeCreate:可以在这加loading事件,在加载实例时触发。
      • 2、created:初始化完成时的事件写在这里,如在这里结束loading,异步请求也适合在这里调用。
      • 3、mounted:挂载元素,获取到dom节点。
      • 4、updated:如果对数据统一处理,在这里写上相应的函数。
      • 5、beforeDestroy:可以做一个确定停止事件的确认框。
    • 6.vue 获取数据在哪个周期函数
      • 看实际情况,一般在 created(或beforeRouter) 里面就可以,如果涉及到需要页面加载完成之后的话就用 mounted。
      • 在created的时候,视图中的html并没有渲染出来,所以此时如果直接去操作html的dom节点,一定找不到相关的元素
      • 而在mounted中,由于此时html已经渲染出来了,所以可以直接操作dom节点,(此时document.getelementById 即可生效了)。
      • 应用
        • vue中实现先请求数据再渲染dom
        • 在项目中遇到了一个问题,下面是vue template中的代码:
          • image.png
        • 我之前的写法是
          • image.png
        • 这样做的结果是下面取dom的操作,取到的dom都是undefined,也就是没有取到。
        • 原因是并没有按照 请求数据—>渲染dom—>获取dom的顺序执行,实际的执行顺序是 先获取dom,而此时数组option中还是空的,上面的v-for循环也就没有渲染出dom,所以根本取不到(不理解是为什么)
        • 后来我又把请求数据写在了created函数中,把取dom的操作写在mounted函数中,竟然还是先执行取dom的操作(是通过alert的顺序来判断执行的顺序),我也很绝望啊
        • 最后终于找到了解决的办法:
          • image.png
        • 看到一个别人的回答是:“在数据请求的回调中使用nextTick,在nextTick的回调里试试~”
        • 还有一个人的回答是:“如果有依赖dom必须存在的情况,就放到mounted(){this.$nextTick(() => { / code / })}里面”(这种之前我试过,我太好用,不懂为什么)
        • 我把这两种方法综合起来,其实主要是第一种方法,发现好用了!
    • 3.第一次页面加载会触发哪几个钩子
      • 第一次页面加载时会触发 beforeCreate, created, beforeMount, mounted 这几个钩子
    • DOM 渲染在哪个周期中就已经完成?
      • DOM 渲染在 mounted 中就已经完成了
    • 5.created 和 mounted 的区别
      • 一、什么是生命周期?
        • 用通俗的语言来说,就是 Vue中实例或者组件从创建到消灭中间经过的一系列过程。 虽然不太严谨,但是也基本上可以理解。
        • 通过一系列实践,现在把所有遇到的问题整理一遍,今天记录一下created和mounted的区别:
      • 二、created和mounted区别?
        • 官方图解如下:
          • image.png
          • image.png
          • image.png
          • image.png
          • image.png
      • 我们从图中看两个节点:
        • created:在模板渲染成html前调用,即通常初始化某些属性值,然后再渲染成视图。
        • mounted:在模板渲染成html后调用,通常是初始化页面完成后,再对html的dom节点进行一些需要的操作。
        • 其实两者比较好理解,通常created使用的次数多,而mounted通常是在一些插件的使用或者组件的使用中进行操作,比如插件chart.js的使用: var ctx = document.getElementById(ID); 通常会有这一步,而如果你写入组件中,你会发现在created中无法对chart进行一些初始化配置,一定要等这个html渲染完后才可以进行,那么mounted就是不二之选。下面看一个例子(用组件)。
      • 三、例子Vue.component(“demo1”,{ data:function(){ return { name:””, age:””, city:”” } }, template:”
        • {{name}}
        • {{age}}
        • {{city}}
        “, created:function(){ this.name=”唐浩益” this.age = “12” this.city =”杭州” var x = document.getElementById(“name”)//第一个命令台错误 console.log(x.innerHTML); }, mounted:function(){ var x = document.getElementById(“name”)//第二个命令台输出的结果 console.log(x.innerHTML); } }); var vm = new Vue({ el:”#example1” })
        • 可以看到输出如下:
          • image.png
        • 可以看到都在created赋予初始值的情况下成功渲染出来了。
        • 但是同时看console台如下:
          • image.png
        • 可以看到第一个报了错,实际是因为找不到id,getElementById(ID) 并没有找到元素,原因如下:
        • 在created的时候,视图中的html并没有渲染出来,所以此时如果直接去操作html的dom节点,一定找不到相关的元素
        • 而在mounted中,由于此时html已经渲染出来了,所以可以直接操作dom节点,故输出了结果“唐浩益”。
    • MVVM
      • MVC框架:
        • image.png
        • M-Model : 业务逻辑和实体模型(biz/bean)
        • V-View : 布局文件(XML)
        • C-Controller : 控制器(Activity)
        • 相信大家都熟悉这个框架,这个也是初学者最常用的框架,该框架虽然也是把代码逻辑和UI层分离,但是View层能做的事情还是很少的,很多对于页面的呈现还是交由C实现,这样会导致项目中C的代码臃肿,如果项目小,代码臃肿点还是能接受的,但是随着项目的不断迭代,代码量的增加,你就会没办法忍受该框架开发的项目,这时MVP框架就应运而生。
      • MVP框架:
        • image.png
        • M-Model : 业务逻辑和实体模型(biz/bean)
        • V-View : 布局文件(XML)和Activity
        • P-Presenter : 完成View和Model的交互
        • MVP框架相对于MVC框架做了较大的改变,将Activity当做View使用,代替MVC框架中的C的是P,对比MVC和MVP的模型图可以发现变化最大的是View层和Model层不在直接通信,所有交互的工作都交由Presenter层来解决。既然两者都通过Presenter来通信,为了复用和可拓展性,MVP框架基于接口设计的理念大家自然就可以理解其用意。
        • 但MVP框架也有不足之处:
          • 1.接口过多,一定程度影响了编码效率。
          • 2.业务逻辑抽象到Presenter中,较为复杂的界面Activity代码量依然会很多。
          • 3.导致Presenter的代码量过大。
      • MVVM框架:
        • image.png
        • M-Model : 实体模型(biz/bean)
        • V-View : 布局文件(XML)
        • VM-ViewModel : DataBinding所在之处,对外暴露出公共属性,View和Model的绑定器
        • 对比MVP和MVVM模型图可以看出,他们之间区别主要体现在以下两点:
            1. 可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多View重用这段视图逻辑。 在Android中,布局里可以进行一个视图逻辑,并且Model发生变化,View也随着发生变化。
            1. 低耦合。以前Activity、Fragment中需要把数据填充到View,还要进行一些视图逻辑。现在这些都可在布局中完成(具体代码请看后面) 甚至都不需要再Activity、Fragment去findViewById()。这时候Activity、Fragment只需要做好的逻辑处理就可以了。
        • 说了这么多理论知识,相信大家都有所厌烦了,下面就不来“虚”的了直接来“干”的,大家可能会问MVVM框架在Android怎么样使用?
        • Google在2015年的已经为我们提供DataBinding技术,以便让我们快速实现MVVM框架的实现。下面就详细讲解如何使用DataBinding?
        • 由于本人使用的是AndroidStudio(以下简称AS),所以接下来都是关于AS相关使用规则:
          • 1.检查你的AS版本,要求在1.3.0以上
          • 2.Gradle 版本1.3.0-beta4以上
          • 3.在工程根目录build.gradle文件加入如下配置:dependencies { classpath “com.android.tools.build:gradle:1.3.0-beta4” classpath “com.android.databinding:dataBinder:1.0-rc1” } allprojects { repositories { jcenter() } }
          • 4.在app里的build.gradle文件加入如下配置:apply plugin: ‘com.android.application’ apply plugin: ‘com.android.databinding’
          • 下面来比较一下布局与之前大家常用的格式的区别:<?xml version=”1.0” encoding=”utf-8”?>
          • 先将根布局改为layout
          • 在布局里引入的model 中的数据类:(还有一种写法将在后面代码中介绍)
          • 设置布局属性值,通过@{}语法:
          • 数据实体类User:public class User { private final String firstName; private final String lastName; public User(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } public String getFirstName() { return this.firstName; } public String getLastName() { return this.lastName; } }
          • 在Activity中进行数据的绑定:@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); MainActivityBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity); User user = new User(“Test”, “User”); binding.setUser(user); }
          • 这时你运行程序就会看到在界面上会显示你设置的测试用户数据,当然你还可以这样做去获取binding:MainActivityBinding binding = MainActivityBinding.inflate(getLayoutInflater());
          • 如果你使用的是ListView或者RecyclerView去显示界面,这时候在Items布局中使用Data Binding,在Adapter中你可以这样获取binding:ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup, false); //or ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false);
          • 接下来就是事件的实现,在我们以往的使用中对于事件的实现都是android:onClick或者在代码中使用View.setOnClickListener()来实现点击事件,在这里将有一种新的实现方式:
          • 要将事件分配给它的处理程序,使用一个正常的绑定表达式,以值作为调用的方法名称。例如:你的数据对象有两种方法public class MyHandlers { public void onClickFriend(View view) { … } public void onClickEnemy(View view) { … } }
          • 绑定表达式可以为视图指定单击事件监听器<?xml version=”1.0” encoding=”utf-8”?>
          • 由于DataBinding对布局使用改动比较大,下面主要讲解一下布局:
          • ①在布局中import导入
            • 之后在你的布局中通过View控件特性进行对其实现类隐藏和显示操作
            • 可能有人会问如果我导入的类与已导入的类的名字冲突怎么办?那么接下来就会解决这个问题!
            • 这里的“alias”属性就是别名的意思,你可以采用别名的方式解决这个问题。
          • ②当你在布局中引用的变量是一个List集合,需将集合的左”<”使用转义字符输入,如下(想要了解转义字符具体表达形式,请自行查询):
          • ③类型转换
          • ④当导入的类中存在静态属性和方法时,你也是可以在布局中直接使用
          • ⑤属性Variables:
            • 在数据元素中可以使用任意数量的变量元素。每个可变元素描述一个属性,该属性可以设置在布局文件中的绑定表达式中使用的布局上:
          • ⑥自定义Binding类的类名: 在app_package/databinding下生成CustomBinding; 在app_package下生成CustomBinding; 明确指定包名和类名。
          • ⑦include使用<?xml version=”1.0” encoding=”utf-8”?>
          • ⑧DataBinding数据绑定不支持包括合并元素的直接子元素,例如下面的写法是不被允许的:<?xml version=”1.0” encoding=”utf-8”?>
            • 注意:name.xml 和 contact.xml都必须包含
          • ⑨DataBinding支持的表达式有:数学表达式: + - / *% 字符串拼接 + 逻辑表达式&& || 位操作符 & | ^ 一元操作符 + - ! ~ 位移操作符 >>>>> << 比较操作符 == >< >= <= instanceof 分组操作符 () 字面量 -character, String, numeric, null 强转、方法调用 字段访问 数组访问 [] 三元操作符 ? 聚合判断(Null Coalescing Operator)语法 ‘??’
          • 例如:
          • ①android:text=”@{String.valueOf(index + 1)}” android:visibility=”@{age < 13 ? View.GONE : View.VISIBLE}” android:transitionName=’@{“image_” + id}’
          • ②android:text=”@{user.displayName ?? user.lastName}”
            • 上面代码的意思是如果displayName为null,则显示lastName,否则显示displayName;
          • ③android:text=”@{user.displayName != null ? user.displayName : user.lastName}”
          • 集合Collections … android:text=”@{list[index]}” … android:text=”@{sparse[index]}” … android:text=”@{map[key]}”
          • ①String literals(字符串常量)
            • 当使用单引号围绕属性值时,在表达式中使用双引号是很容易的:android:text=’@{map[“firstName”]}’
          • ②也可以使用双引号来环绕属性值。当你这样做的时候,String literals要么用&quot;或反引号():android:text="@{map[firstName`}” android:text=”@{map["firstName"]}”
          • Resources资源:
            • 在DataBinding语法中,可以把resource作为其中的一部分:android:padding=”@{large? @dimen/largePadding : @dimen/smallPadding}”
            • 除了支持dimen,还支持color、string、drawable、anim等。
            • 注意:对mipmap图片资源支持还是有问题,目前只支持drawable。
          • 一些资源要求需明确的类型赋值:
            • image.png
          • 到这里基本的属性使用方法介绍就结束了,下面大家应该对数据变化时,UI如何呈现很迷惑,好了,现在开始讲数据的变化如何让UI的更新?
          • 任何普通的java对象(POJO)可用于数据绑定,但修改一个POJO不会造成UI更新。数据绑定(DataBinding)的真正力量可以通过给你的数据对象在数据改变时通知你来使用。有三种不同的数据变化通知机制,Observable objects, observable fields, and observable collections。
          • 下面来逐个讲解:
          • Observable Objects
            • 一个实现可观察到的接口的类,将允许绑定到绑定一个单一的监听器绑定到一个绑定对象,以监听该对象上的所有属性的更改;观察到的接口有一个机制来添加和删除监听器,但通知是由开发人员来进行的。为使开发更容易,一个基类,baseobservable,是为了实现监听器注册机制。数据类实现者仍然是负责通知时的性能变化。这是通过分配一个绑定注释getter和setter进行通知:private static class User extends BaseObservable { private String firstName; private String lastName; @Bindable public String getFirstName() { return this.firstName; } @Bindable public String getLastName() { return this.lastName; } public void setFirstName(String firstName) { this.firstName = firstName; notifyPropertyChanged(BR.firstName); } public void setLastName(String lastName) { this.lastName = lastName; notifyPropertyChanged(BR.lastName); } }
            • 注意:BR类自动生成的
            • 好了,现在你就会发现当通过set方法改变数据后,UI就会自动更新!
          • ObservableFieldsprivate static class User { public final ObservableField firstName = new ObservableField<>(); public final ObservableField lastName = new ObservableField<>(); public final ObservableInt age = new ObservableInt(); }
            • 在代码中设置数据:user.firstName.set(“Google”); int age = user.age.get();
          • Observable CollectionsObservableArrayMap user = new ObservableArrayMap<>(); user.put(“firstName”, “Google”); user.put(“lastName”, “Inc.”); user.put(“age”, 17);
          • 布局中使用:
          • 如果集合的key是Integer,可以使用ObservableArrayList代替ObservableArrayMap:ObservableArrayList user = new ObservableArrayList<>(); user.add(“Google”); user.add(“Inc.”); user.add(17);
          • 在布局中使用:
          • 什么是MVVM?MVVM是Model-View-ViewModel的缩写。
          • 要编写可维护的前端代码绝非易事。我们已经用MVC模式通过koa实现了后端数据、模板页面和控制器的分离,但是,对于前端来说,还不够。
          • 这里有童鞋会问,不是讲Node后端开发吗?怎么又回到前端开发了?
          • 对于一个全栈开发工程师来说,懂前端才会开发出更好的后端程序(不懂前端的后端工程师会设计出非常难用的API),懂后端才会开发出更好的前端程序。程序设计的基本思想在前后端都是通用的,两者并无本质的区别。这和“不想当厨子的裁缝不是好司机”是一个道理。
          • 当我们用Node.js有了一整套后端开发模型后,我们对前端开发也会有新的认识。由于前端开发混合了HTML、CSS和JavaScript,而且页面众多,所以,代码的组织和维护难度其实更加复杂,这就是MVVM出现的原因。
          • 在了解MVVM之前,我们先回顾一下前端发展的历史。
          • 在上个世纪的1989年,欧洲核子研究中心的物理学家Tim Berners-Lee发明了超文本标记语言(HyperText Markup Language),简称HTML,并在1993年成为互联网草案。从此,互联网开始迅速商业化,诞生了一大批商业网站。
          • 最早的HTML页面是完全静态的网页,它们是预先编写好的存放在Web服务器上的html文件。浏览器请求某个URL时,Web服务器把对应的html文件扔给浏览器,就可以显示html文件的内容了。
          • 如果要针对不同的用户显示不同的页面,显然不可能给成千上万的用户准备好成千上万的不同的html文件,所以,服务器就需要针对不同的用户,动态生成不同的html文件。一个最直接的想法就是利用C、C++这些编程语言,直接向浏览器输出拼接后的字符串。这种技术被称为CGI:Common Gateway Interface。
          • 很显然,像新浪首页这样的复杂的HTML是不可能通过拼字符串得到的。于是,人们又发现,其实拼字符串的时候,大多数字符串都是HTML片段,是不变的,变化的只有少数和用户相关的数据,所以,又出现了新的创建动态HTML的方式:ASP、JSP和PHP——分别由微软、SUN和开源社区开发。
          • 在ASP中,一个asp文件就是一个HTML,但是,需要替换的变量用特殊的<%=var%>标记出来了,再配合循环、条件判断,创建动态HTML就比CGI要容易得多。
          • 但是,一旦浏览器显示了一个HTML页面,要更新页面内容,唯一的方法就是重新向服务器获取一份新的HTML内容。如果浏览器想要自己修改HTML页面的内容,就需要等到1995年年底,JavaScript被引入到浏览器。
          • 有了JavaScript后,浏览器就可以运行JavaScript,然后,对页面进行一些修改。JavaScript还可以通过修改HTML的DOM结构和CSS来实现一些动画效果,而这些功能没法通过服务器完成,必须在浏览器实现。
          • 用JavaScript在浏览器中操作HTML,经历了若干发展阶段:
            • 第一阶段,直接用JavaScript操作DOM节点,使用浏览器提供的原生API:var dom = document.getElementById(‘name’); dom.innerHTML = ‘Homer’; dom.style.color = ‘red’;
            • 第二阶段,由于原生API不好用,还要考虑浏览器兼容性,jQuery横空出世,以简洁的API迅速俘获了前端开发者的芳心:$(‘#name’).text(‘Homer’).css(‘color’, ‘red’);
            • 第三阶段,MVC模式,需要服务器端配合,JavaScript可以在前端修改服务器渲染后的数据。
          • 现在,随着前端页面越来越复杂,用户对于交互性要求也越来越高,想要写出Gmail这样的页面,仅仅用jQuery是远远不够的。MVVM模型应运而生。
          • MVVM最早由微软提出来,它借鉴了桌面应用程序的MVC思想,在前端页面中,把Model用纯JavaScript对象表示,View负责显示,两者做到了最大限度的分离。
          • 把Model和View关联起来的就是ViewModel。ViewModel负责把Model的数据同步到View显示出来,还负责把View的修改同步回Model。
          • ViewModel如何编写?需要用JavaScript编写一个通用的ViewModel,这样,就可以复用整个MVVM模型了。
          • 一个MVVM框架和jQuery操作DOM相比有什么区别?
          • 我们先看用jQuery实现的修改两个DOM节点的例子:

            Hello, Bart!

            You are 12.

          • Hello, Bart!
          • You are 12.
          • 用jQuery修改name和age节点的内容:
          • ‘use strict’;var name = ‘Homer’; var age = 51; $(‘#name’).text(name); $(‘#age’).text(age);
          • // 执行代码并观察页面变化
          • (no output)
          • 如果我们使用MVVM框架来实现同样的功能,我们首先并不关心DOM的结构,而是关心数据如何存储。最简单的数据存储方式是使用JavaScript对象:var person = { name: ‘Bart’, age: 12 };
          • 我们把变量person看作Model,把HTML某些DOM节点看作View,并假定它们之间被关联起来了。
          • 要把显示的name从Bart改为Homer,把显示的age从12改为51,我们并不操作DOM,而是直接修改JavaScript对象:
          • Hello, Homer!
          • You are 51.
          • ‘use strict’;person.name = ‘Homer’; person.age = 51;
          • // 执行代码并观察页面变化
          • (no output)
          • 执行上面的代码,我们惊讶地发现,改变JavaScript对象的状态,会导致DOM结构作出对应的变化!这让我们的关注点从如何操作DOM变成了如何更新JavaScript对象的状态,而操作JavaScript对象比DOM简单多了!
          • 这就是MVVM的设计思想:关注Model的变化,让MVVM框架去自动更新DOM的状态,从而把开发者从操作DOM的繁琐步骤中解脱出来!
          • 对MVC 、MVVM、MVP的理解【https://www.kancloud.cn/small-four/asdasdasdasd/1007502
          • 1.mvvm 框架是什么?
            • MVC简介
              • MVC是Model-View-Controler的简称
                • image.png
              • Model——即模型。模型一般都有很好的可复用性,统一管理一些我们需要使用的数据。
              • View——就是存放视图使用的。
              • Controller——控制器它负责处理View和Model的事件。
            • MVVM简介
            • MVC框架一目了然,也非常好理解,随着App应用功能的强大Controller的负担越来越大因此在MVC的基础上繁衍出了MVVM框架。
              • image.png
            • ViewModel: 相比较于MVC新引入的视图模型。是视图显示逻辑、验证逻辑、网络请求等代码存放的地方。
            • 现实开发中是找到一个合适的框架时使用,并不局限于哪一种,下面举一个简单的例子,在ViewModel里面处理业务逻辑,旨在讲解MVVM框架,不用与工作,当我们处理复杂的业务逻辑的时候可以优先选择MVVM框架。
              • image.png
            • 看图简单的逻辑,下面上代码:
            • User.h和User.m文件#import @interface User : NSObject @property (nonatomic,copy) NSString *userName; @property (nonatomic,assign) NSInteger userId; @end#import “User.h” @implementation User - (id)init{ self = [superinit]; if (self) { self.userName =@””; self.userId = 20; } returnself; } @end
            • UserViewModel.h和UserViewModel.m 文件#import @class User; @interface UserViewModel : NSObject @property (nonatomic,strong) User user; @property (nonatomic,strong) NSString userName; @end#import “UserViewModel.h” #import “User.h” @implementation UserViewModel - (id)init{ self = [superinit]; if (self) { //在这里处理业务逻辑 _user = [[Useralloc]init]; if (_user.userName.length > 0) { _userName =_user.userName; }else { _userName = [NSStringstringWithFormat:@”简书%ld”, (long)_user.userId]; } } returnself; } @end ViewController.m文件 ViewController - (void)viewDidLoad { [superviewDidLoad]; _userLabel = [[UILabelalloc]initWithFrame:CGRectMake(10, 199, 200, 50)]; _userLabel.backgroundColor = [UIColorredColor]; _userViewModel = [[UserViewModelalloc]init]; _userLabel.text =_userViewModel.userName;//显示 [self.viewaddSubview:_userLabel]; // Do any additional setup after loading the view, typically from a nib. }
            • ViewController.m文件#import “ViewController.h” #import “UserViewModel.h” @interface ViewController () @property (nonatomic,strong) UILabel userLabel; @property (nonatomic,strong) UserViewModel userViewModel; @end@implementation ViewController - (void)viewDidLoad { [superviewDidLoad]; _userLabel = [[UILabelalloc]initWithFrame:CGRectMake(10, 199, 200, 50)]; _userLabel.backgroundColor = [UIColorredColor]; _userViewModel = [[UserViewModelalloc]init]; _userLabel.text =_userViewModel.userName;//显示 [self.viewaddSubview:_userLabel]; // Do any additional setup after loading the view, typically from a nib. }
            • 这就是简单的MVVM框架使用,工作中要灵活应用,并不局限于哪一种。
          • 说说你对MVC和MVVM的理解
            • MVC:
              • View 传送指令到 Controller
              • Controller 完成业务逻辑后,要求 Model 改变状态
              • Model 将新的数据发送到 View,用户得到反馈
              • 所有通信都是单向的。
            • MVVM:
              • 组成部分Model、View、ViewModel
              • View:UI界面
              • ViewModel:它是View的抽象,负责View与Model之间信息转换,将View的Command传送到Model;
              • Model:数据访问层
          • vue 路由面试题
            • 什么是RESTful API?怎么使用?
              • 是一个api的标准,无状态请求。请求的路由地址是固定的,
              • 如果是tp5则先路由配置中把资源路由配置好。标准有:.get .post .put .delete
            • vue路由的实现/ vue路由的原理?
              • 1、Vue的路由实现:hash模式 和 history模式
              • hash模式:在浏览器中符号“#”,#以及#后面的字符称之为hash,用window.location.hash读取;
              • 特点:hash虽然在URL中,但不被包括在HTTP请求中;用来指导浏览器动作,对服务端安全无害,hash不会重新加载页面。
              • hash 模式下,仅 hash 符号之前的内容会被包含在请求中,如 http://www.xxx.com,因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回 404 错误。
              • history模式:history采用HTML5的新特性;且提供了两个新方法:pushState(),replaceState()可以对浏览器历史记录栈进行修改,以及popState事件的监听到状态变更。
              • history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,如 http://www.xxx.com/items/id。后端如果缺少对 /items/id 的路由处理,将返回 404 错误。
              • Vue-Router 官网里如此描述:“不过这种模式要玩好,还需要后台配置支持……所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。”
            • 2.vue-router 是什么?它有哪些组件 //路由声明式跳转 ,active-class是标签被点击时的样式 //渲染路由的容器 //缓存组件
              • vue-router路由,通俗来讲主要是来实现页面的跳转,通过设置不同的path,向服务器发送的不同的请求,获取不同的资源。
              • 路由中有三个基本的概念 route, routes, router。
                • 1, route,它是一条路由,由这个英文单词也可以看出来,它是单数, Home按钮 => home内容, 这是一条route, about按钮 => about 内容, 这是另一条路由。
                • 2, routes 是一组路由,把上面的每一条路由组合起来,形成一个数组。[{home 按钮 =>home内容 }, { about按钮 => about 内容}]
                • 3, router 是一个机制,相当于一个管理者,它来管理路由。因为routes 只是定义了一组路由,它放在哪里是静止的,当真正来了请求,怎么办? 就是当用户点击home 按钮的时候,怎么办?这时router 就起作用了,它到routes 中去查找,去找到对应的 home 内容,所以页面中就显示了 home 内容。
                • 4,客户端中的路由,实际上就是dom 元素的显示和隐藏。当页面中显示home 内容的时候,about 中的内容全部隐藏,反之也是一样。客户端路由有两种实现方式:基于hash 和基于html5 history api.
              • 路由跳转等都需要vue-router
            • 6.Vue-router 路由有哪些模式?
              • 一般有两种模式:
                • hash 模式:后面的 hash 值的变化,浏览器既不会向服务器发出请求,浏览器也不会刷新,每次 hash 值的变化会触发 hashchange 事件。
                • history 模式:利用了 HTML5 中新增的 pushState() 和 replaceState() 方法。这两个方法应用于浏览器的历史记录栈,在当前已有的 back、forward、go 的基础之上,它们提供了对历史记录进行修改的功能。只是当它们执行修改时,虽然改变了当前的 URL,但浏览器不会立即向后端发送请求。
            • 5.vue-router 有哪几种导航钩子?它们有哪些参数?
              • 3种。
              • 1、全局导航钩子:
                • (1)前置守卫:跳转前进行拦截。
                  • router.beforeEach(to, from, next)
                • (2)后置钩子
                  • router.afterEach((to, from) => {})
              • 2、组件内的钩子
                • beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave
              • 3、单独路由独享组件。
                • beforeEnter: (to, from ,next)const router = new VueRouter({ routes: [ { path: ‘/foo’, component: Foo, beforeEnter: (to, from, next) => { // … } } ]
            • vue-router导航钩子实现的原理?【https://www.cnblogs.com/tiedaweishao/p/9144531.html
              • 前端路由是直接找到与地址匹配的一个组件或对象并将其渲染出来。改变浏览器地址而不向服务器发出请求有两种方式:
                  1. 在地址中加入#以欺骗浏览器,地址的改变是由于正在进行页内导航
                  1. 使用H5的window.history功能,使用URL的Hash来模拟一个完整的URL。
              • 当打包构建应用时,Javascript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。
                • image.png
                • image.png
              • 目录结构
                • 先来看看整体的目录结构
                  • image.png
                  • image.png
              • 和流程相关的主要需要关注点的就是 components、history 目录以及 create-matcher.js、create-route-map.js、index.js、install.js。下面就从 basic 应用入口开始来分析 vue-router 的整个流程。import Vue from ‘vue’ import VueRouter from ‘vue-router’ // 1. 插件 // 安装 and 组件 // 且给当前应用下所有的组件都注入 $router and $route 对象 Vue.use(VueRouter) // 2. 定义各个路由下使用的组件,简称路由组件 const Home = { template: ‘home’ } const Foo = { template: ‘foo’ } const Bar = { template: ‘bar’ } // 3. 创建 VueRouter 实例 router const router = new VueRouter({ mode: ‘history’, base: __dirname, routes: [ { path: ‘/‘, component: Home }, { path: ‘/foo’, component: Foo }, { path: ‘/bar’, component: Bar } ] }) // 4. 创建 启动应用 // 一定要确认注入了 router // 在中将会渲染路由组件 new Vue({ router, template: <div id="app"> <h1>Basic</h1> <ul> <li><router-link to="/">/</router-link></li> <li><router-link to="/foo">/foo</router-link></li> <li><router-link to="/bar">/bar</router-link></li> <router-link tag="li" to="/bar">/bar</router-link> </ul> <router-view class="view"></router-view> </div> }).$mount(‘#app’)
              • 作为插件
                • 上边代码中关键的第 1 步,利用 Vue.js 提供的插件机制 .use(plugin) 来安装 VueRouter,而这个插件机制则会调用该 plugin 对象的 install 方法(当然如果该 plugin 没有该方法的话会把 plugin 自身作为函数来调用);下边来看下 vue-router 这个插件具体的实现部分。
                • VueRouter 对象是在 src/index.js 中暴露出来的,这个对象有一个静态的 install 方法:/ @flow / // 导入 install 模块 import { install } from ‘./install’ // … import { inBrowser, supportsHistory } from ‘./util/dom’ // … export default class VueRouter { // … } // 赋值 install VueRouter.install = install // 自动使用插件 if (inBrowser && window.Vue) { window.Vue.use(VueRouter) }
                • 可以看到这是一个 Vue.js 插件的经典写法,给插件对象增加 install 方法用来安装插件具体逻辑,同时在最后判断下如果是在浏览器环境且存在 window.Vue 的话就会自动使用插件。
                • install 在这里是一个单独的模块,继续来看同级下的 src/install.js 的主要逻辑:// router-view router-link 组件 import View from ‘./components/view’ import Link from ‘./components/link’ // export 一个 Vue 引用 export let _Vue // 安装函数 export function install (Vue) { if (install.installed) return install.installed = true // 赋值私有 Vue 引用 _Vue = Vue // 注入 $router $route Object.defineProperty(Vue.prototype, ‘$router’, { get () { return this.$root._router } }) Object.defineProperty(Vue.prototype, ‘$route’, { get () { return this.$root._route } }) // beforeCreate mixin Vue.mixin({ beforeCreate () { // 判断是否有 router if (this.$options.router) { // 赋值 _router this._router = this.$options.router // 初始化 init this._router.init(this) // 定义响应式的 _route 对象 Vue.util.defineReactive(this, ‘_route’, this._router.history.current) } } }) // 注册组件 Vue.component(‘router-view’, View) Vue.component(‘router-link’, Link) // … }
                • 这里就会有一些疑问了?
                • · 为啥要 export 一个 Vue 引用?插件在打包的时候是肯定不希望把 vue 作为一个依赖包打进去的,但是呢又希望使用 Vue 对象本身的一些方法,此时就可以采用上边类似的做法,在 install 的时候把这个变量赋值 Vue ,这样就可以在其他地方使用 Vue 的一些方法而不必引入 vue 依赖包(前提是保证 install 后才会使用)。
                • · 通过给 Vue.prototype 定义 $router、$route 属性就可以把他们注入到所有组件中吗?在 Vue.js 中所有的组件都是被扩展的 Vue 实例,也就意味着所有的组件都可以访问到这个实例原型上定义的属性。
                • beforeCreate mixin 这个在后边创建 Vue 实例的时候再细说。
              • 实例化 VueRouter
                • 在入口文件中,首先要实例化一个 VueRouter ,然后将其传入 Vue 实例的 options 中。现在继续来看在 src/index.js 中暴露出来的 VueRouter 类:// … import { createMatcher } from ‘./create-matcher’ // … export default class VueRouter { // … constructor (options: RouterOptions = {}) { this.app = null this.options = options this.beforeHooks = [] this.afterHooks = [] // 创建 match 匹配函数 this.match = createMatcher(options.routes || []) // 根据 mode 实例化具体的 History let mode = options.mode || ‘hash’ this.fallback = mode === ‘history’ && !supportsHistory if (this.fallback) { mode = ‘hash’ } if (!inBrowser) { mode = ‘abstract’ } this.mode = mode switch (mode) { case ‘history’: this.history = new HTML5History(this, options.base) break case ‘hash’: this.history = new HashHistory(this, options.base, this.fallback) break case ‘abstract’: this.history = new AbstractHistory(this) break default: assert(false, invalid mode: ${mode}) } } // … }
                • 里边包含了重要的一步:创建 match 匹配函数。
              • match 匹配函数
                • 匹配函数是由 src/create-matcher.js 中的 createMatcher 创建的:/ @flow / import Regexp from ‘path-to-regexp’ // … import { createRouteMap } from ‘./create-route-map’ // … export function createMatcher (routes: Array): Matcher { // 创建路由 map const { pathMap, nameMap } = createRouteMap(routes) // 匹配函数 function match ( raw: RawLocation, currentRoute?: Route, redirectedFrom?: Location ): Route { // … } function redirect ( record: RouteRecord, location: Location ): Route { // … } function alias ( record: RouteRecord, location: Location, matchAs: string ): Route { // … } function _createRoute ( record: ?RouteRecord, location: Location, redirectedFrom?: Location ): Route { if (record && record.redirect) { return redirect(record, redirectedFrom || location) } if (record && record.matchAs) { return alias(record, location, record.matchAs) } return createRoute(record, location, redirectedFrom) } // 返回 return match } // …
                • 具体逻辑后续再具体分析,现在只需要理解为根据传入的 routes 配置生成对应的路由 map,然后直接返回了 match 匹配函数。
                • 继续来看 src/create-route-map.js 中的 createRouteMap 函数:/ @flow / import { assert, warn } from ‘./util/warn’ import { cleanPath } from ‘./util/path’ // 创建路由 map export function createRouteMap (routes: Array): { pathMap: Dictionary, nameMap: Dictionary } { // path 路由 map const pathMap: Dictionary = Object.create(null) // name 路由 map const nameMap: Dictionary = Object.create(null) // 遍历路由配置对象 增加 路由记录 routes.forEach(route => { addRouteRecord(pathMap, nameMap, route) }) return { pathMap, nameMap } } // 增加 路由记录 函数 function addRouteRecord ( pathMap: Dictionary, nameMap: Dictionary, route: RouteConfig, parent?: RouteRecord, matchAs?: string ) { // 获取 path 、name const { path, name } = route assert(path != null, "path" is required in a route configuration.) // 路由记录 对象 const record: RouteRecord = { path: normalizePath(path, parent), components: route.components || { default: route.component }, instances: {}, name, parent, matchAs, redirect: route.redirect, beforeEnter: route.beforeEnter, meta: route.meta || {} } // 嵌套子路由 则递归增加 记录 if (route.children) { // … route.children.forEach(child => { addRouteRecord(pathMap, nameMap, child, record) }) } // 处理别名 alias 逻辑 增加对应的 记录 if (route.alias !== undefined) { if (Array.isArray(route.alias)) { route.alias.forEach(alias => { addRouteRecord(pathMap, nameMap, { path: alias }, parent, record.path) }) } else { addRouteRecord(pathMap, nameMap, { path: route.alias }, parent, record.path) } } // 更新 path map pathMap[record.path] = record // 更新 name map if (name) { if (!nameMap[name]) { nameMap[name] = record } else { warn(false, Duplicate named routes definition: { name: "${name}", path: "${record.path}" }) } } } function normalizePath (path: string, parent?: RouteRecord): string { path = path.replace(/\/$/, ‘’) if (path[0] === ‘/‘) return path if (parent == null) return path return cleanPath(`${pa
                • 可以看出主要做的事情就是根据用户路由配置对象生成普通的根据 path 来对应的路由记录以及根据 name 来对应的路由记录的 map,方便后续匹配对应。
              • 实例化 History
                • 这也是很重要的一步,所有的 History 类都是在 src/history/ 目录下,现在呢不需要关心具体的每种 History 的具体实现上差异,只需要知道他们都是继承自 src/history/base.js 中的 History 类的:/ @flow / // … import { inBrowser } from ‘../util/dom’ import { runQueue } from ‘../util/async’ import { START, isSameRoute } from ‘../util/route’ // 这里从之前分析过的 install.js 中 export _Vue import { _Vue } from ‘../install’ export class History { // … constructor (router: VueRouter, base: ?string) { this.router = router this.base = normalizeBase(base) // start with a route object that stands for “nowhere” this.current = START this.pending = null } // … } // 得到 base 值 function normalizeBase (base: ?string): string { if (!base) { if (inBrowser) { // respect tag const baseEl = document.querySelector(‘base’) base = baseEl ? baseEl.getAttribute(‘href’) : ‘/‘ } else { base = ‘/‘ } } // make sure there’s the starting slash if (base.charAt(0) !== ‘/‘) { base = ‘/‘ + base } // remove trailing slash return base.replace(/\/$/, ‘’) } // …
                • 实例化完了 VueRouter,下边就该看看 Vue 实例了。
              • 实例化 Vue new Vue({ router, template: <div id="app"> <h1>Basic</h1> <ul> <li><router-link to="/">/</router-link></li> <li><router-link to="/foo">/foo</router-link></li> <li><router-link to="/bar">/bar</router-link></li> <router-link tag="li" to="/bar">/bar</router-link> </ul> <router-view class="view"></router-view> </div> }).$mount(‘#app’)
                • options 中传入了 router,以及模板;还记得上边没具体分析的 beforeCreate mixin 吗,此时创建一个 Vue 实例,对应的 beforeCreate 钩子就会被调用:// … Vue.mixin({ beforeCreate () { // 判断是否有 router if (this.$options.router) { // 赋值 _router this._router = this.$options.router // 初始化 init this._router.init(this) // 定义响应式的 _route 对象 Vue.util.defineReactive(this, ‘_route’, this._router.history.current) } } })
                • 具体来说,首先判断实例化时 options 是否包含 router,如果包含也就意味着是一个带有路由配置的实例被创建了,此时才有必要继续初始化路由相关逻辑。然后给当前实例赋值_router,这样在访问原型上的 $router 的时候就可以得到 router 了。
                • 下边来看里边两个关键:router.init 和 定义响应式的 _route 对象。
              • router.init
                • 然后来看 router 的 init 方法就干了哪些事情,依旧是在 src/index.js 中:/ @flow / import { install } from ‘./install’ import { createMatcher } from ‘./create-matcher’ import { HashHistory, getHash } from ‘./history/hash’ import { HTML5History, getLocation } from ‘./history/html5’ import { AbstractHistory } from ‘./history/abstract’ import { inBrowser, supportsHistory } from ‘./util/dom’ import { assert } from ‘./util/warn’ export default class VueRouter { // … init (app: any / Vue component instance /) { // … this.app = app const history = this.history if (history instanceof HTML5History) { history.transitionTo(getLocation(history.base)) } else if (history instanceof HashHistory) { history.transitionTo(getHash(), () => { window.addEventListener(‘hashchange’, () => { history.onHashChange() }) }) } history.listen(route => { this.app._route = route }) } // … } // …
                • 可以看到初始化主要就是给 app 赋值,针对于 HTML5History 和 HashHistory 特殊处理,因为在这两种模式下才有可能存在进入时候的不是默认页,需要根据当前浏览器地址栏里的 path 或者 hash 来激活对应的路由,此时就是通过调用 transitionTo 来达到目的;而且此时还有个注意点是针对于 HashHistory 有特殊处理,为什么不直接在初始化 HashHistory 的时候监听 hashchange 事件呢?这个是为了修复vuejs/vue-router#725这个 bug 而这样做的,简要来说就是说如果在 beforeEnter 这样的钩子函数中是异步的话,beforeEnter 钩子就会被触发两次,原因是因为在初始化的时候如果此时的 hash 值不是以 / 开头的话就会补上 #/,这个过程会触发 hashchange 事件,所以会再走一次生命周期钩子,也就意味着会再次调用 beforeEnter 钩子函数。
                • 来看看这个具体的 transitionTo 方法的大概逻辑,在 src/history/base.js 中:/ @flow / import type VueRouter from ‘../index’ import { warn } from ‘../util/warn’ import { inBrowser } from ‘../util/dom’ import { runQueue } from ‘../util/async’ import { START, isSameRoute } from ‘../util/route’ import { _Vue } from ‘../install’ export class History { // … transitionTo (location: RawLocation, cb?: Function) { // 调用 match 得到匹配的 route 对象 const route = this.router.match(location, this.current) // 确认过渡 this.confirmTransition(route, () => { // 更新当前 route 对象 this.updateRoute(route) cb && cb(route) // 子类实现的更新url地址 // 对于 hash 模式的话 就是更新 hash 的值 // 对于 history 模式的话 就是利用 pushstate / replacestate 来更新 // 浏览器地址 this.ensureURL() }) } // 确认过渡 confirmTransition (route: Route, cb: Function) { const current = this.current // 如果是相同 直接返回 if (isSameRoute(route, current)) { this.ensureURL() return } // 交叉比对当前路由的路由记录和现在的这个路由的路由记录 // 以便能准确得到父子路由更新的情况下可以确切的知道 // 哪些组件需要更新 哪些不需要更新 const { deactivated, activated } = resolveQueue(this.current.matched, route.matched) // 整个切换周期的队列 const queue: Array = [].concat( // leave 的钩子 extractLeaveGuards(deactivated), // 全局 router before hooks this.router.beforeHooks, // 将要更新的路由的 beforeEnter 钩子 activated.map(m => m.beforeEnter), // 异步组件 resolveAsyncComponents(activated) ) this.pending = route 每一个队列执行的 iterator 函数 const iterator = (hook: NavigationGuard, next) => { // 确保期间还是当前路由 if (this.pending !== route) return hook(route, current, (to: any) => { if (to === false) { // next(false) -> abort navigation, ensure current URL this.ensureURL(true) } else if (typeof to === ‘string’ || typeof to === ‘object’) { // next(‘/‘) or next({ path: ‘/‘ }) -> redirect this.push(to) } else { // confirm transition and pass on the value next(to) } }) } // 执行队列 runQueue(queue, iterator, () => { const postEnterCbs = [] // 组件内的钩子 const enterGuards = extractEnterGuards(activated, postEnterCbs, () => { return this.current === route }) // 在上次的队列执行完成后再执行组件内的钩子 // 因为需要等异步组件以及是OK的情况下才能执行 runQueue(enterGuards, iterator, () => { // 确保期间还是当前路由 if (this.pending === route) { this.pending = null cb(route) this.router.app.$nextTick(() => { postEnterCbs.forEach(cb => cb()) }) } }) }) } // 更新当前 route 对象 updateRoute (route: Route) { const prev = this.current this.current = route // 注意 cb 的值 // 每次更新都会调用 下边需要用到! this.cb && this.cb(route) // 执行 after hooks 回调 this.router.afterHooks.forEach(hook => { hook && hook(route, prev) }) } } // …
                • 可以看到整个过程就是执行约定的各种钩子以及处理异步组件问题,这里有一些具体函数具体细节被忽略掉了(后续会具体分析)但是不影响具体理解这个流程。但是需要注意一个概念:路由记录,每一个路由 route 对象都对应有一个 matched 属性,它对应的就是路由记录,他的具体含义在调用 match() 中有处理;通过之前的分析可以知道这个 match 是在 src/create-matcher.js 中的:// … import { createRoute } from ‘./util/route’ import { createRouteMap } from ‘./create-route-map’ // … export function createMatcher (routes: Array): Matcher { const { pathMap, nameMap } = createRouteMap(routes) // 关键的 match function match ( raw: RawLocation, currentRoute?: Route, redirectedFrom?: Location ): Route { const location = normalizeLocation(raw, currentRoute) const { name } = location // 命名路由处理 if (name) { // nameMap[name] = 路由记录 const record = nameMap[name] const paramNames = getParams(record.path) // … if (record) { location.path = fillParams(record.path, location.params, named route "${name}") // 创建 route return _createRoute(record, location, redirectedFrom) } } else if (location.path) { // 普通路由处理 location.params = {} for (const path in pathMap) { if (matchRoute(path, location.params, location.path)) { // 匹配成功 创建route // pathMap[path] = 路由记录 return _createRoute(pathMap[path], location, redirectedFrom) } } } // no match return _createRoute(null, location) } // … // 创建路由 function _createRoute ( record: ?RouteRecord, location: Location, redirectedFrom?: Location ): Route { // 重定向和别名逻辑 if (record && record.redirect) { return redirect(record, redirectedFrom || location) } if (record && record.matchAs) { return alias(record, location, record.matchAs) } // 创建路由对象 return createRoute(record, location, redirectedFrom) } return match } // …
                • 路由记录在分析 match 匹配函数那里以及分析过了,这里还需要了解下创建路由对象的 createRoute,存在于 src/util/route.js 中:// … export function createRoute ( record: ?RouteRecord, location: Location, redirectedFrom?: Location ): Route { // 可以看到就是一个被冻结的普通对象 const route: Route = { name: location.name || (record && record.name), meta: (record && record.meta) || {}, path: location.path || ‘/‘, hash: location.hash || ‘’, query: location.query || {}, params: location.params || {}, fullPath: getFullPath(location), // 根据记录层级的得到所有匹配的 路由记录 matched: record ? formatMatch(record) : [] } if (redirectedFrom) { route.redirectedFrom = getFullPath(redirectedFrom) } return Object.freeze(route) } // … function formatMatch (record: ?RouteRecord): Array { const res = [] while (record) { res.unshift(record) record = record.parent } return res } // …
                • 回到之前看的 init,最后调用了 history.listen 方法:history.listen(route => { this.app._route = route })
                • listen 方法很简单就是设置下当前历史对象的 cb 的值, 在之前分析 transitionTo 的时候已经知道在 history 更新完毕的时候调用下这个 cb。然后看这里设置的这个函数的作用就是更新下当前应用实例的 _route 的值,更新这个有什么用呢?请看下段落的分析。
              • defineReactive 定义 _route
                • 继续回到 beforeCreate 钩子函数中,在最后通过 Vue 的工具方法给当前应用实例定义了一个响应式的 _route 属性,值就是获取的 this._router.history.current,也就是当前 history 实例的当前活动路由对象。给应用实例定义了这么一个响应式的属性值也就意味着如果该属性值发生了变化,就会触发更新机制,继而调用应用实例的 render 重新渲染。还记得上一段结尾留下的疑问,也就是 history 每次更新成功后都会去更新应用实例的 _route 的值,也就意味着一旦 history 发生改变就会触发更新机制调用应用实例的 render 方法进行重新渲染。
              • router-link 和 router-view 组件new Vue({ router, template: <div id="app"> <h1>Basic</h1> <ul> <li><router-link to="/">/</router-link></li> <li><router-link to="/foo">/foo</router-link></li> <li><router-link to="/bar">/bar</router-link></li> <router-link tag="li" to="/bar">/bar</router-link> </ul> <router-view class="view"></router-view> </div> }).$mount(‘#app’)
                • 可以看到这个实例的 template 中包含了两个自定义组件:router-link 和 router-view。
                • router-view 组件
                  • router-view 组件比较简单,所以这里就先来分析它,他是在源码的 src/components/view.js 中定义的:export default { name: ‘router-view’, functional: true, // 功能组件 纯粹渲染 props: { name: { type: String, default: ‘default’ // 默认default 默认命名视图的name } }, render (h, { props, children, parent, data }) { // 解决嵌套深度问题 data.routerView = true // route 对象 const route = parent.$route // 缓存 const cache = parent._routerViewCache || (parent._routerViewCache = {}) let depth = 0 let inactive = false // 当前组件的深度 while (parent) { if (parent.$vnode && parent.$vnode.data.routerView) { depth++ } 处理 keepalive 逻辑 if (parent._inactive) { inactive = true } parent = parent.$parent } data.routerViewDepth = depth // 得到相匹配的当前组件层级的 路由记录 const matched = route.matched[depth] if (!matched) { return h() } // 得到要渲染组件 const name = props.name const component = inactive ? cache[name] : (cache[name] = matched.components[name]) if (!inactive) { // 非 keepalive 模式下 每次都需要设置钩子 // 进而更新(赋值&销毁)匹配了的实例元素 const hooks = data.hook || (data.hook = {}) hooks.init = vnode => { matched.instances[name] = vnode.child } hooks.prepatch = (oldVnode, vnode) => { matched.instances[name] = vnode.child } hooks.destroy = vnode => { if (matched.instances[name] === vnode.child) { matched.instances[name] = undefined } } } // 调用 createElement 函数 渲染匹配的组件 return h(component, data, children) } }
                  • 可以看到逻辑还是比较简单的,拿到匹配的组件进行渲染就可以了。
              • router-link 组件
            • 6.$route 和 $router 的区别
              • 1.router是VueRouter的一个对象,通过Vue.use(VueRouter)和VueRouter构造函数得到一个router的实例对象,这个对象中是一个全局的对象,他包含了所有的路由包含了许多关键的对象和属性。
                • 举例:history对象
                • $router.push({path:’home’});本质是向history栈中添加一个路由,在我们看来是 切换路由,但本质是在添加一个history记录
                • 方法:
                  • $router.replace({path:’home’});//替换路由,没有历史记录
              • 2.route是一个跳转的路由对象,每一个路由都会有一个route对象,是一个局部的对象,可以获取对应的name,path,params,query等
                • 我们可以从vue devtools中看到每个路由对象的不同
                  • image.png
                • 这两个不同的结构可以看出两者的区别,他们的一些属性是不同的。
                • $route.path
                  • 字符串,等于当前路由对象的路径,会被解析为绝对路径,如 “/home/news” 。
                  • this.$router.push({path:/user/${userId}})
                  • 这样传递参数的话,配置路由的时候需要在path上加参数path:user/:userId。
                  • 这种接收参数的方式是this.$route.params.userId。
                • $route.params
                  • 对象,包含路由中的动态片段和全匹配片段的键值对
                    • image.png
                • $route.query
                  • 对象,包含路由中查询参数的键值对。例如,对于 /home/news/detail/01?favorite=yes ,会得到$route.query.favorite == ‘yes’ 。
                    • image.png
                  • query传参是针对path的,params传参是针对name的。。接收参数的方式都差不多。。this.$route.query.和this.$route.params.
                  • 注意这只是跳转url,跳转到这个url显示什么组件,得配置路由。router跳转和标签跳转,规则差不多。
                  • 展示上的话:
                    • image.png
                  • 注意:如果提供了path,params将会被忽略,但是query不属于这种情况。。。
                  • 如果使用完整路径和query传参,刷新页面时不会造成路由传参的参数丢失。
                  • 这个vue官方文档讲的很详细。
                • $route.router
                  • 路由规则所属的路由器(以及其所属的组件)。
                • $route.matched
                  • 数组,包含当前匹配的路径中所包含的所有片段所对应的配置参数对象。
                • $route.name
                  • 当前路径的名字,如果没有使用具名路径,则名字为空。
                • $route.path, $route.params, $route.name, $route.query这几个属性很容易理解,看示例就能知道它们代表的含义
                  • image.png
                • 有时候配置路由时path有时候会加 ‘/‘ 有时候不加,例如path:’name’和path:’/name’。区别其实官方文档说了,我当时没仔细看,导致这个问题还困扰了我很久。
                  • image.png
                • 意思就是以 / 开头的会被当做路径,就不会一直嵌套之前的路径。
            • 4.怎么定义 vue-router 的动态路由? 怎么获取传过来的值
              • 在router目录下的index.js文件中,对path属性加上/:id。 使用router对象的params.id
            • 怎么定义vue-router的动态路由以及如何获取传过来的动态参数?
              • 1、直接修改地址栏中的路由地址。
              • 2、声明式(标签跳转):通过router-link实现跳转注册
              • 3、编程式(js跳转):通过js的编程方式this.$router.push(‘/myLogin’);
            • 8.vue-router 传参
            • 9.vue-router 的两种模式【https://blog.csdn.net/caoxinhui521/article/details/77688512?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-10.no_search_link&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-10.no_search_link
              • vue-router 有 3 种路由模式:hash、history、abstract,对应的源码如下所示:switch (mode) { case ‘history’: this.history = new HTML5History(this, options.base) break case ‘hash’: this.history = new HashHistory(this, options.base, this.fallback) break case ‘abstract’: this.history = new AbstractHistory(this, options.base) break default: if (process.env.NODE_ENV !== ‘production’) { assert(false, invalid mode: ${mode}) } }
              • 其中,3 种路由模式的说明如下:
                • hash: 使用 URL hash 值来作路由。支持所有浏览器,包括不支持 HTML5 History Api 的浏览器;
                • history : 依赖 HTML5 History API 和服务器配置。具体可以查看 HTML5 History 模式;
                • abstract : 支持所有 JavaScript 运行环境,如 Node.js 服务器端。如果发现没有浏览器的 API,路由会自动强制进入这个模式.
              • Vue Router 是Vue官方的路由管理器。它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌。vue-router 默认 hash 模式,还有一种是history模式。
              • hash模式
                • hash模式的工作原理是hashchange事件,可以在window监听hash的变化。我们在url后面随便添加一个#xx触发这个事件。 window.onhashchange = function(event){ console.log(event); }
                • 打印出来的结果如下:
                  • image.png
                • 可以看到里边有两个属性newURL和oldURL。可以通过模拟改变hsh的值,动态页面数据。
                  • image.png
                • 尽管浏览器没有请求服务器,但是页面状态和url已经关联起来了,这就是所谓的前端路由,单页应用的标配。
              • history模式
                • 把window.history对象打印出来可以看到里边提供的方法和记录长度
                  • image.png
                • 前进,后退,跳转操作方法: history.go(-3);//后退3次 history.go(2);//前进2次 history.go(0);//刷新当前页面 history.back(); //后退 history.forward(); //前进
              • HTML5新增的API
                • A)history.pushState(data, title [, url]):往历史记录堆栈顶部添加一条记录; data会在onpopstate事件触发时作为
                  • 参数传递过去;title为页面标题,当前所有浏览器都会 忽略此参数;url为页面地址,可选,缺省为当前页地址;
                • B)history.replaceState(data, title [, url]) :更改当前的历史记录,参数同上;
                • C)history.state:用于存储以上方法的data数据,不同浏览器的读写权限不一样;
                • D)window.onpopstate:响应pushState或replaceState的调用;有了这几个新的API,针对支持的浏览器,
              • 我们可以构建用户体验更好的应用了。就像刚提到的Facebook相册,虽然是AJAX的方式,但用户可以直接复 制页面地址分享给好友,
              • 如果不想要很丑的 hash,我们可以用路由的 history 模式,这种模式充分利用 history.pushState API 来完成 URL 跳转而无须重新加载页面。const router = new VueRouter({ mode: ‘history’, routes: […] })
              • 当你使用 history 模式时,URL 就像正常的 url,例如 http://www.yongcun.wang/tclass,也好看!
              • 不过这种模式要玩好,还需要后台配置支持。因为我们的应用是个单页客户端应用,如果后台没有正确的配置,当用户在浏览器直接访问http://www.yongcun.wang/tclass就会返回 404,这就不好看了。
              • 所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。
              • 给个警告,因为这么做以后,你的服务器就不再返回 404 错误页面,因为对于所有路径都会返回 index.html 文件。为了避免这种情况,你应该在 Vue 应用里面覆盖所有的路由情况,然后在给出一个 404 页面。const router = new VueRouter({ mode: ‘history’, routes: [ { path: ‘*’, component: NotFoundComponent } ] })
            • 7.vue-router 响应路由参数的变化
              • 在vue项目中,假使我们在同一个路由下,只是改变路由后面的参数值,如果不监听路由参数值的变化,页面无数据刷新,需手动刷新浏览器,这样做就不是我们的预期效果。
                • 举例:当前路由为 /pjthome?pjtid=123456mounted: function () { this.pjtid = this.$route.query.pjtid this.pjtdetail() },
              • 在页面pjtdetail()方法中,需要用到pjtid这个参数,假如在同一页面有相似项目切换,只是pjtid发生变化,在切换时,并未重新加载数据,原因是跟vue的生命周期有关,具体该解决这个问题,添加路由监听即可。
                • exp:watch: { $route(){ this.pjtid = this.$route.query.pjtid }, pjtid() { this.pjtdetail() }, }
                • 解决。
            • 10.vue-router 实现路由懒加载( 动态加载路由 )
              • 1 . vue异步组件技术 ==== 异步加载
                • vue-router配置路由 , 使用vue的异步组件技术 , 可以实现按需加载 .
                • 但是,这种情况下一个组件生成一个js文件/ vue异步组件技术 / { path: ‘/home’, name: ‘home’, component: resolve => require([‘@/components/home’],resolve) }, { path: ‘/index’, name: ‘Index’, component: resolve => require([‘@/components/index’],resolve) }, { path: ‘/about’, name: ‘about’, component: resolve => require([‘@/components/about’],resolve) }
              • 非懒加载:
                • image.png
              • 懒加载
                • image.png
              • 2.组件懒加载方案二 路由懒加载(使用import)
                • const 组件名=() => import(‘组件路径’);
                • // 下面2行代码,没有指定webpackChunkName,每个组件打包成一个js文件。
                • /* const Home = () => import(‘@/components/home’)
                • const Index = () => import(‘@/components/index’)
                • const About = () => import(‘@/components/about’) */
                • // 下面2行代码,指定了相同的webpackChunkName,会合并打包成一个js文件。
                • 把组件按组分块
                  • const Home = () => import(/ webpackChunkName: ‘ImportFuncDemo’ / ‘@/components/home’)
                  • const Index = () => import(/ webpackChunkName: ‘ImportFuncDemo’ / ‘@/components/index’)
                  • const About = () => import(/ webpackChunkName: ‘ImportFuncDemo’ / ‘@/components/about’)
                • { path: ‘/about’, component: About }, { path: ‘/index’, component: Index }, { path: ‘/home’, component: Home }
              • 3.webpack提供的require.ensure()
                • vue-router配置路由,使用webpack的require.ensure技术,也可以实现按需加载。
                • 这种情况下,多个路由指定相同的chunkName,会合并打包成一个js文件。
                • / 组件懒加载方案三: webpack提供的require.ensure() /
                • { path: ‘/home’, name: ‘home’, component: r => require.ensure([], () => r(require(‘@/components/home’)), ‘demo’) },
                • { path: ‘/index’, name: ‘Index’, component: r => require.ensure([], () => r(require(‘@/components/index’)), ‘demo’) },
                • { path: ‘/about’, name: ‘about’, component: r => require.ensure([], () => r(require(‘@/components/about’)), ‘demo-01’) }
                • // r就是resolve
                • const list = r => require.ensure([], () => r(require(‘../components/list/list’)), ‘list’);
                • // 路由也是正常的写法 这种是官方推荐的写的 按模块划分懒加载const router = new Router({ routes: [ { path: ‘/list/blog’, component: list, name: ‘blog’ } ] })
            • 3.active-class 是哪个组件的属性?
              • vue-router模块的router-link组件。
            • 嵌套路由怎么定义?
              • 嵌套路由顾名思义就是路由的多层嵌套。一级路由里面使用children数组配置子路由,就是嵌套路由。
          • vue 常见面试题
            • 35 引进组件的步骤
              • 第一步
                • image.png
              • 第二步
                • image.png
              • 第三步
                • image.png
              • 第四步
                • image.png
              • 在vue的项目开发过程中,基本都是基于组件化开发项目,总结下使用组件的几个点:
              • 一、@符号的使用
                • 在vue项目中 @ 符号代表的是根目录,即 src 目录。
              • 二、组件的放置位置
                • 在项目中,公用的组件放置在 components 目录下,项目组件新建 views 目录来存放:
                  • image.png
              • 三、组件的引和使用方法
              • 1、第一种引入和使用方法:
                • import navs from ‘@/views/nav/index’
                • 使用组件:
                  • components:{ ‘v-nav’:navs}
                • 模板中使用组件:
              • 2、第二种引入和使用方法
                • import navs from ‘@/views/nav/index’
                • import indexList from ‘./index-list’
                • 使用组件:
                  • components: { navs,indexList },
                • 模板中使用:
              • 3、第三种使用方法
                • 组件目录:
                  • image.png
                • 引入方式:
                  • import BackToTop from ‘@/components/BackToTop’
                • 使用组件:
                  • components: { BackToTop },
                • 使用组件:
                • 其实没什么,只是要注意下组件大小写命名的写法。
            • Vue中组件怎么传值
              • 正向:父传子 父组件把要传递的数据绑定在属性上,发送,子组件通过props接收
              • 逆向:子传父 子组件通过this.$emit (自定义事件名,要发送的数据),父组件设置一个监听事件来接收,然后拿到数据
              • 兄弟:eventbus 中央事件总线
              • 通过Vuex
            • 49.请说下封装 vue 组件的过程?
              • vue组件的定义
                • 组件(Component)是Vue.js最强大的功能之一
                • 组件可以扩展HTML元素,封装可重用代码
                • 在较高层面上,组件是自定义元素,Vue.js的编译器为他添加特殊功能
                • 有些情况下,组件也可以表现用 is 特性进行了扩展的原生的HTML元素
                • 所有的Vue组件同时也都是Vue实例,所以可以接受相同的选项对象(除了一些根级特有的选项),并提供相同的生命周期钩子
              • vue组件的功能
                • 1)能够把页面抽象成多个相对独立的模块
                • 2)实现代码重用,提高开发效率和代码质量,使得代码易于维护
              • Vue组件封装过程
                • 首先,使用Vue.extend()创建一个组件
                • 然后,使用Vue.component()方法注册组件
                • 接着,如果子组件需要数据,可以在props中接受定义
                • 最后,子组件修改好数据之后,想把数据传递给父组件,可以使用emit()方法
              • 组件使用流程详细介绍
                • 1、组件创建—-有3中方法,extend()