web前端面试题

Web前端測試題

姓名:                 

 

本試題僅做為參考用,請輕鬆作答,以現有知識回答即可,請勿上網或找朋友問答案

  1. 页面从输入URL到页面加载显示完成,这个过程中都发生了什么?

1、首先,在浏览器地址栏中输入url

 

2、浏览器先查看浏览器缓存-系统缓存-路由器缓存,如果缓存中有,会直接在屏幕中显示页面内容。若没有,则跳到第三步操作。

浏览器缓存:浏览器会记录DNS一段时间,因此,只是第一个地方解析DNS请求;

操作系统缓存:如果在浏览器缓存中不包含这个记录,则会使系统调用操作系统,获取操作系统的记录(保存最近的DNS查询缓存);

路由器缓存:如果上述两个步骤均不能成功获取DNS记录,继续搜索路由器缓存;

ISP缓存:若上述均失败,继续向ISP搜索。

————————————————

版权声明:本文为CSDN博主「xingxingba123」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/xingxingba123/article/details/52743335/

 

3、在发送http请求前,需要域名解析(DNS解析),解析获取相应的IP地址。

 

4、浏览器向服务器发起tcp连接,与浏览器建立tcp三次握手。

 

5、握手成功后,浏览器向服务器发送http请求,请求数据包。

 

6、服务器处理收到的请求,将数据返回至浏览器

 

7、浏览器收到HTTP响应

 

8、读取页面内容,浏览器渲染,解析html源码

 

9、生成Dom树、解析css样式、js交互

 

10、客户端和服务器交互

 

11、ajax查询

 

 

 

  1. 常见的浏览器内核有哪些?

1,使用Trident的是internet explorer,国产的绝大部分浏览器。Trident是就是ie内核
2,使用Gecko的是Mozilla Firefox,使用 Gecko 内核的浏览器也有不少,如 Netscape MozillaSuite/SeaMonkey 等
3,使用Presto的是opera,这是目前公认网页浏览速度最快的浏览器内核
4,使用WebKit的有苹果的safari,谷歌的chrome,还有国产的大部分双核浏览器其中一核就是WebKit

 

 

 

  1. 请描述一下 Cookies,SessionStorage 和 LocalStorage 的区别?
  2. ⒈localStorage长期存储数据,浏览器关闭数据后不丢失;
  3. ⒉sessionStorage数据在浏览器关闭后自动删除;
  4. ⒊cookie是网站为了标识用户身份而存储在用户本地终端(Client   Side)上的数据(通常经过加密)。cookie始终在同源的http请求中携带(即使不需要)都会在浏览器和服务器端间来回传递。session  storage和local storage不会自动把数据发给服务器,仅在本地保存;
  5. ⒋存储大小:cookie数据大小不会超过4K,session  storage和local storage虽然也有存储大小的限制,但比cookie大得多,可以达到5M或者更多;
  6. ⒌有期时间:local  storage存储持久数据,浏览器关闭后数据不丢失,除非自动删除数据。session  storage数据在当前浏览器窗口关闭后自动删除。cookie 设置的cookie过期时间之前一直有效,即使窗口或者浏览器关闭;

 

 

 

 

  1. 请写列出前端性能优化的方式
  2. 本文将详细介绍前端性能优化的七大手段,包括减少请求数量、减小资源大小、优化网络连接、优化资源加载、减少重绘回流、使用性能更好的API和构建优化
  3.  
  4. 【合并】
  5. 如果不进行文件合并,有如下3个隐患
  6. 1、文件与文件之间有插入的上行请求,增加了N-1个网络延迟
  7. 2、受丢包问题影响更严重
  8. 3、经过代理服务器时可能会被断开
  9. 但是,文件合并本身也有自己的问题
  10. 1、首屏渲染问题
  11. 2、缓存失效问题
  12. 所以,对于文件合并,有如下改进建议
  13. 1、公共库合并
  14. 2、不同页面单独合并
  15. 【图片处理】
  16. 1、雪碧图
  17. CSS雪碧图是以前非常流行的技术,把网站上的一些图片整合到一张单独的图片中,可以减少网站的HTTP请求数量,但是当整合图片比较大时,一次加载比较慢。随着字体图片、SVG图片的流行,该技术渐渐退出了历史舞台
  18. 2、Base64
  19. 将图片的内容以Base64格式内嵌到HTML中,可以减少HTTP请求数量。但是,由于Base64编码用8位字符表示信息中的6个位,所以编码后大小大约比原始值扩大了 33%
  20. 3、使用字体图标来代替图片
  21. 【减少重定向】
  22. 尽量避免使用重定向,当页面发生了重定向,就会延迟整个HTML文档的传输。在HTML文档到达之前,页面中不会呈现任何东西,也没有任何组件会被下载,降低了用户体验
  23. 如果一定要使用重定向,如http重定向到https,要使用301永久重定向,而不是302临时重定向。因为,如果使用302,则每一次访问http,都会被重定向到https的页面。而永久重定向,在第一次从http重定向到https之后 ,每次访问http,会直接返回https的页面
  24. 【使用缓存】
  25. 使用cach-control或expires这类强缓存时,缓存不过期的情况下,不向服务器发送请求。强缓存过期时,会使用last-modified或etag这类协商缓存,向服务器发送请求,如果资源没有变化,则服务器返回304响应,浏览器继续从本地缓存加载资源;如果资源更新了,则服务器将更新后的资源发送到浏览器,并返回200响应
  26. 【不使用CSS @import】
  27. CSS的@import会造成额外的请求
  28. 【避免使用空的src和href】
  29. a标签设置空的href,会重定向到当前的页面地址
  30. form设置空的method,会提交表单到当前的页面地址
  31.  
  32. 【压缩】
  33. 1、HTML压缩
  34. HTML代码压缩就是压缩在文本文件中有意义,但是在HTML中不显示的字符,包括空格,制表符,换行符等
  35. 2、CSS压缩
  36. CSS压缩包括无效代码删除与CSS语义合并
  37. 3、JS压缩与混乱
  38. JS压缩与混乱包括无效字符及注释的删除、代码语义的缩减和优化、降低代码可读性,实现代码保护
  39. 4、图片压缩
  40. 针对真实图片情况,舍弃一些相对无关紧要的色彩信息
  41. 【webp】
  42. 在安卓下可以使用webp格式的图片,它具有更优的图像数据压缩算法,能带来更小的图片体积,同等画面质量下,体积比jpg、png少了25%以上,而且同时具备了无损和有损的压缩模式、Alpha 透明以及动画的特性
  43. 【开启gzip】
  44. HTTP协议上的GZIP编码是一种用来改进WEB应用程序性能的技术。大流量的WEB站点常常使用GZIP压缩技术来让用户感受更快的速度。这一般是指WWW服务器中安装的一个功能,当有人来访问这个服务器中的网站时,服务器中的这个功能就将网页内容压缩后传输到来访的电脑浏览器中显示出来。一般对纯文本内容可压缩到原大小的40%
  45.  
  46. 【使用CDN】
  47. CDN全称是Content Delivery Network,即内容分发网络,它能够实时地根据网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上。其目的是使用户可就近取得所需内容,解决 Internet网络拥挤的状况,提高用户访问网站的响应速度
  48. 【使用DNS预解析】
  49. 当浏览器访问一个域名的时候,需要解析一次DNS,获得对应域名的ip地址。在解析过程中,按照浏览器缓存系统缓存路由器缓存ISP(运营商)DNS缓存根域名服务器顶级域名服务器主域名服务器的顺序,逐步读取缓存,直到拿到IP地址
  50. DNS Prefetch,即DNS预解析就是根据浏览器定义的规则,提前解析之后可能会用到的域名,使解析结果缓存到系统缓存中,缩短DNS解析时间,来提高网站的访问速度
  51. 方法是在 head 标签里面写上几个 link 标签
  52. 对以上几个网站提前解析 DNS,由于它是并行的,不会堵塞页面渲染,这样可以缩短资源加载的时间
  53. 【并行连接】
  54. 由于在HTTP1.1协议下,chrome每个域名的最大并发数是6个。使用多个域名,可以增加并发数
  55. 【持久连接】
  56. 使用keep-alive或presistent来建立持久连接,持久连接降低了时延和连接建立的开销,将连接保持在已调谐状态,而且减少了打开连接的潜在数量
  57. 【管道化连接】
  58. 在HTTP2协议中,可以开启管道化连接,即单条连接的多路复用,每条连接中并发传输多个资源,这里就不需要添加域名来增加并发数了
  59.  
  60. 【资源加载位置】
  61. 通过优化资源加载位置,更改资源加载时机,使尽可能快地展示出页面内容,尽可能快地使功能可用
  62. 1、CSS文件放在head中,先外链,后本页
  63. 2、JS文件放在body底部,先外链,后本页
  64. 3、处理页面、处理页面布局的JS文件放在head中,如babel-polyfill.js文件、flexible.js文件
  65. 4、body中间尽量不写style标签和script标签
  66. 【资源加载时机】
  67. 1、异步script标签
  68. defer:  异步加载,在HTML解析完成后执行。defer的实际效果与将代码放在body底部类似
  69. async: 异步加载,加载完成后立即执行
  70. 2、模块按需加载
  71. 在SPA等业务逻辑比较复杂的系统中,需要根据路由来加载当前页面需要的业务模块
  72. 按需加载,是一种很好的优化网页或应用的方式。这种方式实际上是先把代码在一些逻辑断点处分离开,然后在一些代码块中完成某些操作后,立即引用或即将引用另外一些新的代码块。这样加快了应用的初始加载速度,减轻了它的总体体积,因为某些代码块可能永远不会被加载
  73. webpack 提供了两个类似的技术,优先选择的方式是使用符合 ECMAScript 提案 的 import() 语法。第二种则是使用 webpack 特定的 require.ensure
  74. 3、使用资源预加载preload和资源预读取prefetch
  75. preload让浏览器提前加载指定资源,需要执行时再执行,可以加速本页面的加载速度
  76. prefetch告诉浏览器加载下一页面可能会用到的资源,可以加速下一个页面的加载速度
  77. 4、资源懒加载与资源预加载
  78. 资源延迟加载也称为懒加载,延迟加载资源或符合某些条件时才加载某些资源
  79. 资源预加载是提前加载用户所需的资源,保证良好的用户体验
  80. 资源懒加载和资源预加载都是一种错峰操作,在浏览器忙碌的时候不做操作,浏览器空间时,再加载资源,优化了网络性能
  81. 【样式设置】
  82. 1、避免使用层级较深的选择器,或其他一些复杂的选择器,以提高CSS渲染效率
  83. 2、避免使用CSS表达式,CSS表达式是动态设置CSS属性的强大但危险方法,它的问题就在于计算频率很快。不仅仅是在页面显示和缩放时,就是在页面滚动、乃至移动鼠标时都会要重新计算一次
  84. 3、元素适当地定义高度或最小高度,否则元素的动态内容载入时,会出现页面元素的晃动或位置,造成回流
  85. 4、给图片设置尺寸。如果图片不设置尺寸,首次载入时,占据空间会从0到完全出现,上下左右都可能位移,发生回流
  86. 5、不要使用table布局,因为一个小改动可能会造成整个table重新布局。而且table渲染通常要3倍于同等元素时间
  87. 6、能够使用CSS实现的效果,尽量使用CSS而不使用JS实现
  88. 【渲染层】
  89. 1、此外,将需要多次重绘的元素独立为render layer渲染层,如设置absolute,可以减少重绘范围
  90. 2、对于一些进行动画的元素,使用硬件渲染,从而避免重绘和回流
  91. 【DOM优化】
  92. 1、缓存DOM
  93. 由于查询DOM比较耗时,在同一个节点无需多次查询的情况下,可以缓存DOM
  94. 2、减少DOM深度及DOM数量
  95. HTML 中标签元素越多,标签的层级越深,浏览器解析DOM并绘制到浏览器中所花的时间就越长,所以应尽可能保持 DOM 元素简洁和层级较少。
  96. 3、批量操作DOM
  97. 由于DOM操作比较耗时,且可能会造成回流,因此要避免频繁操作DOM,可以批量操作DOM,先用字符串拼接完毕,再用innerHTML更新DOM
  98. 4、批量操作CSS样式
  99. 通过切换class或者使用元素的style.csstext属性去批量操作元素样式
  100. 5、在内存中操作DOM
  101. 使用DocumentFragment对象,让DOM操作发生在内存中,而不是页面上
  102. 6、DOM元素离线更新
  103. 对DOM进行相关操作时,例、appendChild等都可以使用Document Fragment对象进行离线操作,带元素“组装”完成后再一次插入页面,或者使用display:none 对元素隐藏,在元素“消失”后进行相关操作
  104. 7、DOM读写分离
  105. 浏览器具有惰性渲染机制,连接多次修改DOM可能只触发浏览器的一次渲染。而如果修改DOM后,立即读取DOM。为了保证读取到正确的DOM值,会触发浏览器的一次渲染。因此,修改DOM的操作要与访问DOM分开进行
  106. 8、事件代理
  107. 事件代理是指将事件监听器注册在父级元素上,由于子元素的事件会通过事件冒泡的方式向上传播到父节点,因此,可以由父节点的监听函数统一处理多个子元素的事件
  108. 利用事件代理,可以减少内存使用,提高性能及降低代码复杂度
  109. 9、防抖和节流
  110. 使用函数节流(throttle)或函数去抖(debounce),限制某一个方法的频繁触发
  111. 10、及时清理环境
  112. 及时消除对象引用,清除定时器,清除事件监听器,创建最小作用域变量,可以及时回收内存
  113.  
  114. 1、用对选择器
  115. 选择器的性能排序如下所示,尽量选择性能更好的选择器
  116. 2、使用requestAnimationFrame来替代setTimeout和setInterval
  117. 希望在每一帧刚开始的时候对页面进行更改,目前只有使用 requestAnimationFrame 能够保证这一点。使用 setTimeout 或者 setInterval 来触发更新页面的函数,该函数可能在一帧的中间或者结束的时间点上调用,进而导致该帧后面需要进行的事情没有完成,引发丢帧
  118. 3、使用IntersectionObserver来实现图片可视区域的懒加载
  119. 传统的做法中,需要使用scroll事件,并调用getBoundingClientRect方法,来实现可视区域的判断,即使使用了函数节流,也会造成页面回流。使用IntersectionObserver,则没有上述问题
  120. 4、使用web worker
  121. 客户端javascript一个基本的特性是单线程:比如,浏览器无法同时运行两个事件处理程序,它也无法在一个事件处理程序运行的时候触发一个计时器。Web Worker是HTML5提供的一个javascript多线程解决方案,可以将一些大计算量的代码交由web Worker运行,从而避免阻塞用户界面,在执行复杂计算和数据处理时,这个API非常有用
  122. 但是,使用一些新的API的同时,也要注意其浏览器兼容性
  123.  
  124. 【打包公共代码】
  125. 使用CommonsChunkPlugin插件,将公共模块拆出来,最终合成的文件能够在最开始的时候加载一次,便存到缓存中供后续使用。这会带来速度上的提升,因为浏览器会迅速将公共的代码从缓存中取出来,而不是每次访问一个新页面时,再去加载一个更大的文件
  126. webpack 4 将移除 CommonsChunkPlugin, 取而代之的是两个新的配置项 optimization.splitChunks 和 optimization.runtimeChunk
  127. 通过设置 optimization.splitChunks.chunks: "all" 来启动默认的代码分割配置项
  128. 【动态导入和按需加载】
  129. webpack提供了两种技术通过模块的内联函数调用来分离代码,优先选择的方式是,使用符合 ECMAScript 提案 的 import() 语法。第二种,则是使用 webpack 特定的 require.ensure
  130. 【剔除无用代码】
  131. tree shaking 是一个术语,通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)。它依赖于 ES2015 模块系统中的静态结构特性,例如 import 和 export。这个术语和概念实际上是兴起于 ES2015 模块打包工具 rollup
  132. JS的tree shaking主要通过uglifyjs插件来完成,CSS的tree shaking主要通过purify CSS来实现的
  133. 【长缓存优化】
  134. 1、将hash替换为chunkhash,这样当chunk不变时,缓存依然有效
  135. 2、使用Name而不是id
  136. 每个 module.id 会基于默认的解析顺序(resolve order)进行增量。也就是说,当解析顺序发生变化,ID 也会随之改变
  137. 下面来使用两个插件解决这个问题。第一个插件是 NamedModulesPlugin,将使用模块的路径,而不是数字标识符。虽然此插件有助于在开发过程中输出结果的可读性,然而执行时间会长一些。第二个选择是使用 HashedModuleIdsPlugin,推荐用于生产环境构建
  138. 【公用代码内联】
  139. 使用html-webpack-inline-chunk-plugin插件将mainfest.js内联到html文件中

12.  减少请求数量

41.  减小资源大小

56.  优化网络连接

63. <link rel="dns-prefecth" href="https://www.google.com">
64. <link rel="dns-prefecth" href="https://www.google-analytics.com">

73.  优化资源加载

96.  减少重绘回流

109.      const div = document.getElementById('div')

131.         性能更好的API

135.      id选择器(#myid)
136.      类选择器(.myclassname)
137.      标签选择器(div,h1,p)
138.      相邻选择器(h1+p)
139.      子选择器(ul > li)
140.      后代选择器(li a)
141.      通配符选择器(*)
142.      属性选择器(a[rel="external"])
143.      伪类选择器(a:hover,li:nth-child)

153.         webpack优化

 

 

 

170. 談談对前端工程化的理解

171. 当前环境?

  1. 172.  目前来说,web业务日益复杂化和多元化,前端开发从webpage模式为主转变为webapp模式为主,前端的开发工作在某些 
    场景下被认为是日常的一项简单工作,或者是某个项目的附属品,而没有看成一个软件被认真对待。
    在模式的转变下,前端工程化日益复杂,会产生很多问题:
    例如 如何进行高效的多人协作,如何保证项目的可维护性,如何提高项目的开发质量,如何降低项目的生产风险。
    前端工程化是根据软件工程的技术和方法对前端的工具,技术,开发流程,经验等进行规范,使其更具规范化和标准化 
    ,其目的是提高开发效率和降低生产成本,即提高开发过程的开发效率,减少重复的开发时间,其实前端工程化是软件 
    工程的一部分,可以从理解软件工程的角度去探讨。

173. 什么是前端工程化?

174. 前端工程化里的工程是一个很大的概念,甚至创建一个git仓库,也可以理解为创建了一个工程,软件工程的定义是运 
用计算机科学的理论和技术,以及工程管理的原则和方法,按进度和预算,实现满足用户要求的软件产品的定义,开发 
和维护的工程以及研究的学科。
前端工程化是为了让前端可以自成体系,具体可以从四方面去讨论,模块化,组件化,规范化和自动化。

175. 模块化?
模块化:将大的文件拆分成互相依赖的小文件,再进行统一的拼装和加载。
js的模块化:利用webpack+babel的模式将所有模块系统进行打包,同步加载,也可以搭乘多个chunk异步加载。
利用浏览器的script标签,type类型选modules类型即可。
css模块化:之前的sass less 等预处理器虽然实现了css的拆分,但是并没有解决模块化很重要的一个问题,即选择器 
的全局污染问题。有三种解决办法,第一种是利用webcomponents的技术实现,这个技术虽然解决了全局污染问题,但 
是由于兼容性问题,目前用的不多,第二种是css in js 将css的技术全部摒弃,利用js或者json格式去加载css,这种 
方式简单粗暴,并且不容易处理伪类选择器的问题,被大众所认可的是第三种解决方案,即 css modules ,所有的css 
文件由js来管理,这种技术最大程度利用了css的生态和模块化的原则,其中vue中的scoped 就是这种技术的提现。
资源的模块化:webpack的成功不仅仅是因为将js系统进行模块化处理,而是它的模块化原理,即任何资源都可以模块 
化且应该模块化处理,优点有以下三点,1:目录结构清晰化,2:资源处理集成化,3:项目依赖单一化。

176. 组件化?
组件化:将UI页面拆分正有模板+样式+逻辑组成的功能单元,称为组件,组件化不等于模块化,模块化是在资源和代码 
方面对文件的拆分,而组件化是在UI层面进行的拆分。
传统前端框架的思想是以dom优先,先操作dom,再写出可复用的逻辑单元来操作dom,而组件化框架是组件优先,将dom 
和与之一起的逻辑组成一个组件,再进行引用。
我们封装了组件后,还需要对组件间的关系进行判定,例如继承,扩展,嵌套,包含等,这些关系统称为依赖

177. 规范化?
规范化:规范化是前端工程化很重要的一部分,项目前期规范制定的好坏,直接决定后期的开发质量,分为
项目目录规范化,编码规范化,前后端接口规范化,git分支管理,commit描述规范,组件管理等编码规范化分为html 
css js img 命名规范这几类 接口规范,目的是规则先行,以减少联调中不必要的问题和麻烦,自责划分 前端,渲染 
逻辑和交互逻辑,后台,处理业务逻辑,各种格式的规定,例如 json尽量简洁轻量,日期尽量字符串,等等。

178. 自动化?
自动化:让简单重复的工作交给机器完成,例如自动化测试,自动化部署,自动化构建,持续继承等。

 

 

 

 

179. 请解释一下CSS3的flexbox(弹性盒布局模型),以及适用场景?

Flex是Flexible Box的缩写,意为”弹性布局”,用来为盒状模型提供最大的灵活性。

 

任何一个容器都可以指定为Flex布局。

 

.box{

display: flex;

}

行内元素也可以使用Flex布局。

 

.box{

display: inline-flex;

}

Webkit内核的浏览器,必须加上-webkit前缀。

 

.box{

display: -webkit-flex; /* Safari */

display: flex;

}

注意,设为Flex布局以后,子元素的float、clear和vertical-align属性将失效。

 

二、基本概念

采用Flex布局的元素,称为Flex容器(flex container),简称”容器”。它的所有子元素自动成为容器成员,称为Flex项目(flex item),简称”项目”。

 

容器默认存在两根轴:水平的主轴(main axis)和垂直的交叉轴(cross axis)。主轴的开始位置(与边框的交叉点)叫做main start,结束位置叫做main end;交叉轴的开始位置叫做cross start,结束位置叫做cross end。

 

项目默认沿主轴排列。单个项目占据的主轴空间叫做main size,占据的交叉轴空间叫做cross size。

 

三、容器的属性

以下6个属性设置在容器上。

 

flex-direction

flex-wrap

flex-flow

justify-content

align-items

align-content

3.1 flex-direction属性

flex-direction属性决定主轴的方向(即项目的排列方向)。

 

.box {

flex-direction: row | row-reverse | column | column-reverse;

}

 

它可能有4个值。

 

row(默认值):主轴为水平方向,起点在左端。

row-reverse:主轴为水平方向,起点在右端。

column:主轴为垂直方向,起点在上沿。

column-reverse:主轴为垂直方向,起点在下沿。

3.2 flex-wrap属性

默认情况下,项目都排在一条线(又称”轴线”)上。flex-wrap属性定义,如果一条轴线排不下,如何换行。

 

.box{

flex-wrap: nowrap | wrap | wrap-reverse;

}

它可能取三个值。

 

(1)nowrap(默认):不换行。

 

(2)wrap:换行,第一行在上方。

 

(3)wrap-reverse:换行,第一行在下方。

 

3.3 flex-flow

flex-flow属性是flex-direction属性和flex-wrap属性的简写形式,默认值为row nowrap。

 

.box {

flex-flow: || ;

}

3.4 justify-content属性

justify-content属性定义了项目在主轴上的对齐方式。

 

.box {

justify-content: flex-start | flex-end | center | space-between | space-around;

}

 

它可能取5个值,具体对齐方式与轴的方向有关。下面假设主轴为从左到右。

 

flex-start(默认值):左对齐

flex-end:右对齐

center: 居中

space-between:两端对齐,项目之间的间隔都相等。

space-around:每个项目两侧的间隔相等。所以,项目之间的间隔比项目与边框的间隔大一倍。

3.5 align-items属性

align-items属性定义项目在交叉轴上如何对齐。

 

.box {

align-items: flex-start | flex-end | center | baseline | stretch;

}

 

它可能取5个值。具体的对齐方式与交叉轴的方向有关,下面假设交叉轴从上到下。

 

flex-start:交叉轴的起点对齐。

flex-end:交叉轴的终点对齐。

center:交叉轴的中点对齐。

baseline: 项目的第一行文字的基线对齐。

stretch(默认值):如果项目未设置高度或设为auto,将占满整个容器的高度。

3.6 align-content属性

align-content属性定义了多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用。

 

.box {

align-content: flex-start | flex-end | center | space-between | space-around | stretch;

}

 

该属性可能取6个值。

 

flex-start:与交叉轴的起点对齐。

flex-end:与交叉轴的终点对齐。

center:与交叉轴的中点对齐。

space-between:与交叉轴两端对齐,轴线之间的间隔平均分布。

space-around:每根轴线两侧的间隔都相等。所以,轴线之间的间隔比轴线与边框的间隔大一倍。

stretch(默认值):轴线占满整个交叉轴。

四、项目的属性

以下6个属性设置在项目上。

 

  • order

flex-grow

flex-shrink

flex-basis

flex

align-self

4.1 order属性

  • order属性定义项目的排列顺序。数值越小,排列越靠前,默认为0。

 

.item {

  • order: ;

}

 

4.2 flex-grow属性

flex-grow属性定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大。

 

.item {

flex-grow: ; /* default 0 */

}

 

如果所有项目的flex-grow属性都为1,则它们将等分剩余空间(如果有的话)。如果一个项目的flex-grow属性为2,其他项目都为1,则前者占据的剩余空间将比其他项多一倍。

 

4.3 flex-shrink属性

flex-shrink属性定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小。

 

.item {

flex-shrink: ; /* default 1 */

}

 

如果所有项目的flex-shrink属性都为1,当空间不足时,都将等比例缩小。如果一个项目的flex-shrink属性为0,其他项目都为1,则空间不足时,前者不缩小。

 

负值对该属性无效。

 

4.4 flex-basis属性

flex-basis属性定义了在分配多余空间之前,项目占据的主轴空间(main size)。浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为auto,即项目的本来大小。

 

.item {

flex-basis: | auto; /* default auto */

}

它可以设为跟width或height属性一样的值(比如350px),则项目将占据固定空间。

 

4.5 flex属性

flex属性是flex-grow, flex-shrink 和 flex-basis的简写,默认值为0 1 auto。后两个属性可选。

 

.item {

flex: none | [ <‘flex-grow’> <‘flex-shrink’>? || <‘flex-basis’> ]

}

该属性有两个快捷值:auto (1 1 auto) 和 none (0 0 auto)。

 

建议优先使用这个属性,而不是单独写三个分离的属性,因为浏览器会推算相关值。

 

4.6 align-self属性

align-self属性允许单个项目有与其他项目不一样的对齐方式,可覆盖align-items属性。默认值为auto,表示继承父元素的align-items属性,如果没有父元素,则等同于stretch。

 

.item {

align-self: auto | flex-start | flex-end | center | baseline | stretch;

}

 

该属性可能取6个值,除了auto,其他都与align-items属性完全一致。

180. 用Javascript实现随机选取10~100之间的10个数字,存入一个数组,并且排序

181.      var iArray = [];
182.       2     function getRandom(istart, iend) {
183.       3         var iChoice = iend - istart + 1;    //加1是为了取到100
184.       4         var res = Math.floor(Math.random() * iChoice + istart);  //[0,90]+10
185.       5         return res;
186.       6     }
187.       7     for (var i = 0; i < 10; i++) {
188.       8         iArray.push(getRandom(10, 100));
189.       9     }
190.      10     iArray.sort(function (a, b) {
191.      11         return a > b;
192.      12     });
193.      13     console.log(iArray);

 

 

 

 

194. HTML5的新特性

 1. 新的选择器

通过 class 定位元素 (DOM API)

var el = document.getElementById(‘section1’);

el.focus();

 

var els = document.getElementsByTagName(‘div’);

els[0].focus();

 

var els = document.getElementsByClassName(‘section’);

els[0].focus();

通过类似 css 选择器的语法定位元素 (Selectors API)

var els = document.querySelectorAll(“ul li:nth-child(odd)”);

var els = document.querySelectorAll(“table.test > tr > td”);

 

2. 本地储存 - Web Storage

// use localStorage for persistent storage

// use sessionStorage for per tab storage

textarea.addEventListener(‘keyup’, function () {

window.localStorage[‘value’] = area.value;

window.localStorage[‘timestamp’] = (new Date()).getTime();

}, false);

textarea.value = window.localStorage[‘value’];

 

3. 本地数据库 - Web SQL Database

var db = window.openDatabase(“Database Name”, “Database Version”);

db.transaction(function(tx) {

tx.executeSql(“SELECT * FROM test”, [], successCallback, errorCallback);

});

 

4.文件缓存 - Application Cache API

manifest=”cache-manifest”>

window.applicationCache.addEventListener(‘checking’, updateCacheStatus, false);

CACHE MANIFEST

 

5.让程序在后台运行 - Web Workers

main.js:

var worker = new Worker(‘extra_work.js’);

worker.onmessage = function (event) { alert(event.data); };

 

extra_work.js:

// do some work; when done post message.

postMessage(some_data);

6.双向信息传输 - Web Sockets

 

var socket = new WebSocket(location);

socket.onopen = function(event) {

socket.postMessage(“Hello, WebSocket”);

}

socket.onmessage = function(event) { alert(event.data); }

socket.onclose = function(event) { alert(“closed”); }

7.桌面提醒 - Notifications

 

if (window.webkitNotifications.checkPermission() == 0) {

// you can pass any url as a parameter

window.webkitNotifications.createNotification(tweet.picture, tweet.title,

tweet.text).show();

} else {

window.webkitNotifications.requestPermission();

}

8.拖放操作 - Drag and drop

 

document.addEventListener(‘dragstart’, function(event) {

event.dataTransfer.setData(‘text’, ‘Customized text’);

event.dataTransfer.effectAllowed = ‘copy’;

}, false);

即将支持: 从桌面拖动文件到页面。

9.地理位置 - Geolocation

 

if (navigator.geolocation) {

navigator.geolocation.getCurrentPosition(function(position) {

var lat = position.coords.latitude;

var lng = position.coords.longitude;

map.setCenter(new GLatLng(lat, lng), 13);

map.addOverlay(new GMarker(new GLatLng(lat, lng)));

});

https://www.cnblogs.com/vicky1018/p/7705223.html

 

195. Javascript哪些操作会造成内存泄露

https://www.cnblogs.com/lilife/p/14643377.html

 

 

 

196. 谈谈对于闭包的理解

https://www.cnblogs.com/lsy0403/p/5854683.html

 

 

 

197. 常见的状态码分别表示什么

198. 1(信息类):接受到请求并且继续处理 2(响应成功):表示动作被成功接受,理解和接受 200 -表示请求被成功完成,请求的资源发送回客户端 202 -接受和处理,但处理未完成 203 -返回信息不确定或不完整 204 -请求收到,但返回信息为空 3**(重定向):为了完成指定的动作,必须接受进一步处理 300 -请求的资源可在多处得到 301 -本页面被永久性转移到另一个URL

199. 304 -自从上次请求后,请求的网页未修改过,服务器返回此响应时,不会返回网页内容,代表上次的文档已经被缓存了,还可以继续使用 305 -请求的资源必须从服务器指定的地址得到

200. 4**(客户端错误类) 400 -客户端请求语法错误,不能被服务器所理解 403 -禁止访问,服务器收到请求,但是拒绝提供服务 404 -服务器无法取得所请求的网页,请求资源不存在。

 

 

 

 

201. 介绍一下你对浏览器内核的理解?

主要分成两部分:渲染引擎(layout engineer或Rendering Engine)和JS引擎

渲染引擎:负责取得网页的内容(HTML、XML、图像等等)、整理讯息(例如加入CSS等),以及计算网页的显示方式,然后会输出至显示器或打印机。浏览器的内核的不同对于网页的语法解释会有不同,所以渲染的效果也不相同。所有网页浏览器、电子邮件客户端以及其它需要编辑、显示网络内容的应用程序都需要内核

JS引擎则:解析和执行javascript来实现网页的动态效果

最开始渲染引擎和JS引擎并没有区分的很明确,后来JS引擎越来越独立,内核就倾向于只指渲染引擎

————————————————

版权声明:本文为CSDN博主「LuckXinXin」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/LuckXinXin/article/details/112544333

 

 

 

202. ES5和ES6的区别,说一下你所知道的ES6

目录

 

ES5 和 ES6 的区别

 

ES6 的新增方法

 

1、新增声明命令 let 和 const

 

1.1)特点

 

2、模板字符串(Template String)

 

3、函数的扩展

 

3.1)函数的默认参数

 

3.2)箭头函数

 

4、对象的扩展

 

4.1)属性的简写

 

4.2)Object.keys()方法

 

4.3)Object.assign ()

 

5、for...of 循环

 

6、import 和 export

 

7、Promise 对象

 

8、解构赋值

 

8.1)数组的解构赋值

 

8.2)对象的解构赋值

 

9、Set 数据结构

 

9.1)Set 属性和方法

 

9.2)主要应用场景:数组去重

 

10、class

 

11、…

 

12、async、await

 

13、修饰器

 

14、Symbol

 

15、Proxy

 

ES5 和 ES6 的区别

ECMAScript5,即 ES5,是 ECMAScript 的第五次修订,于 2009 年完成标准化

ECMAScript6,即 ES6,是 ECMAScript 的第六次修订,于 2015 年完成,也称 ES2015

ES6 是继 ES5 之后的一次改进,相对于 ES5 更加简洁,提高了开发效率.

ES6 的新增方法

1、新增声明命令 let 和 const

在 ES6 中通常用

let 和 const 来声明,let 表示变量、const 表示常量

1.1)特点

let 和 const

都是块级作用域。以{}代码块作为作用域范围 只能在代码块里面使用

不存在变量提升,只能先声明再使用,否则会报错。在代码块内,在声明变量之前,

该变量

都是不可用的。这在语法上,称为“暂时性死区”

(temporal dead zone,简称 TDZ,

在同一个代码块内,不允许重复声明

const 声明的是一个只读常量,在声明时就需要赋值。(如果 const 的是一个对象,对

象所包含的值是可以被修改的。抽象一点儿说,就是对象所指向的地址不能改变,而

变量成员 是可以修改的。)

2、模板字符串(Template String)

用一对反引号(`)标识,它可以当作普通字符串使用,也可以用来定义多行字符串,也可以

在字符串中嵌入变量,js 表达式或函数,变量、js 表达式或函数需要写在${ }中。

3、函数的扩展

3.1)函数的默认参数

ES6 为参数提供了默认值。在定义函数时便初始化了这个参数,以便在参数没有被传递

进去时使用。

3.2)箭头函数

在 ES6 中,提供了一种简洁的函数写法,我们称作“箭头函数”。

3.2.1)写法

函数名=(形参)=>{……}

当函数体中只有一个表达式时,{}和 return 可以省

略当函数体中形参只有一个时,()可以省略。

3.2.2)特点

箭头函数中的 this 始终指向箭头函数定义时的离 this 最近的一个函数,如果没有最

近的函数就指向 window。

4、对象的扩展

4.1)属性的简写

ES6 允许在对象之中,直接写变量。这时,属性名为变量名, 属性值为变量 的值。

var foo = 'bar';

var baz = {foo};

 //等同于 var baz = {foo: foo}; 方法的简写。省略冒号与 function 关键字。

var o = { method() { return "Hello!"; } };

// 等同于

var o = { method: function() { return "Hello!"; } };

4.2)Object.keys()方法

获取对象的所有属性名或方法名(不包括原形的内容),返回一个数组。

var obj={name: "john", age: "21", getName: function () { alert(this.name)}};

 

console.log(Object.keys(obj)); // ["name", "age", "getName"]

 

console.log(Object.keys(obj).length); //3

 

console.log(Object.keys(["aa", "bb", "cc"])); //["0", "1", "2"]

 

console.log(Object.keys("abcdef")); //["0", "1", "2", "3", "4", "5"]

4.3)Object.assign ()

assign 方法将多个原对象的属性和方法都合并到了目标对象上面。可以接收多个参数,

第一个参数是目标对象,后面的都是源对象

var target = {}; //目标对象

 

var source1 = {name : 'ming', age: '19'}; //源对象 1

 

var source2 = {sex : '女'}; //源对象 2

 

var source3 = {sex : '男'}; //源对象 3,和 source2 中的对象有同名属性 sex

 

Object.assign(target,source1,source2,source3);

 

console.log(target); //{name : 'ming', age: '19', sex: '男'}

5、for...of 循环

var arr=["小林","小吴","小佳"];

 

 for(var v of arr){ console.log(v); }//小林 //小吴 //小佳

6、import 和 export

ES6 标准中,JavaScript 原生支持模块(module)。这种将 JS 代码分割成不同功能的小块进行

模块化,将不同功能的代码分别写在不同文件中,各模块只需导出公共接口部分,然后通

过模块的导入的方式可以在其他地方使用.

export 用于对外输出本模块(一个文件可以理解为一个模块)变量的接口

import 用于在一个模块中加载另一个含有 export 接口的模块

import 和 export 命令只能在模块的顶部,不能在代码块之中

7、Promise 对象

Promise 是异步编程的一种解决方案,将异步操作以同步操作的流程表达出来,避免了层层 嵌套的回调函数,要是为了解决异步处理回调地狱(也就是循环嵌套的问题)而产生的 Promise 构造函数包含一个参数和一个带有 resolve(解析)和 reject(拒绝)两个参数的回 调。在回调中执行一些操作(例如异步),如果一切都正常,则调用 resolve,否则调用 reject。 对于已经实例化过的 Promise 对象可以调用 Promise.then() 方法,传递 resolve 和 reject 方法作为回调。then()方法接收两个参数:onResolve 和 onReject,分别代表当前 Promise 对 象在成功或失败时

Promise 的 3 种状态

Fulfilled 为成功的状态,Rejected 为失败的状态,Pending 既不是 Fulfilld 也不是 Rejected 的状态,可以理解为 Promise 对象实例创建时候的初始状态

8、解构赋值

8.1)数组的解构赋值

解构赋值是对赋值运算符的扩展。 是一种针对数组或者对象进行模式匹配,然后对其中的变量进行赋值。 在代码书写上简洁且易读,语义更加清晰明了;也方便了复杂对象中数据字段获取。数组中的值会自动被解析到对应接收该值的变量中,数组的解构赋值要一一对应如果有对应不上的就是 undefined.

let [a, b, c] = [1, 2, 3];

// a = 1 // b = 2 // c = 3

8.2)对象的解构赋值

对象的解构赋值和数组的解构赋值其实类似,但是数组的数组成员是有序的 而对象的属性则是无序的,所以对象的解构赋值简单理解是等号的左边和右边的结构 相同

et { foo, bar } = { foo: 'aaa', bar: 'bbb' };

 // foo = 'aaa' // bar = 'bbb'

let { baz : foo } = { baz : 'ddd' };

// foo = 'ddd'

9、Set 数据结构

Set 数据结构,类似数组。所有的数据都是唯一的,没有重复的值。它本身是一个构造函数。

9.1)Set 属性和方法

Size()

数据的长度

Add()

添加某个值,返回 Set 结构本身。

Delete() 删除某个值,返回一个布尔值,表示删除是否成功。

Has() 查找某条数据,返回一个布尔值。

Clear()清除所有成员,没有返回值。

9.2)主要应用场景:数组去重

10、class

class 类的继承 ES6 中不再像 ES5 一样使用原型链实现继承,而是引入 Class 这个概念

ES6 所写的类相比于 ES5 的优点:

区别于函数,更加专业化(类似于 JAVA 中的类)

写法更加简便,更加容易实现类的继承

11、…

展开运算符可以将数组或对象里面的值展开;还可以将多个值收集为一个变量

12、async、await

使用 async/await, 搭配 Promise,可以通过编写形似同步的代码来处理异步流程, 提高代码

的简洁性和可读性 async 用于申明一个 function 是异步的,而 await 用于等待一个异步方

法执行完成

13、修饰器

@decorator 是一个函数,用来修改类甚至于是方法的行为。修饰器本质就是编译时执行的函

14、Symbol

Symbol 是一种基本类型。Symbol 通过调用 symbol 函数产生,它接收一个可选的名字参数,

该函数返回的 symbol 是唯一的

15、Proxy

Proxy 代理使用代理(Proxy)监听对象的操作,然后可以做一些相应事情

————————————————

 

 

203. Vue生命周期函数有哪些?

一个有11个生命周期函数,

 

分别是:

 

beforeCreate : 创建Vue实例前的时候执行,

 

created :  创建Vue实例完成后执行,

 

beforeMount : Vue实例开始渲染前执行,

 

mounted  :  Vue实例渲染完成后执行,

 

beforeUpdate  :  Vue实例修改前执行,

 

updated  :  Vue实例修改完成后执行,

 

beforeDestroy  : Vue开始消亡前执行,

 

destroyed  : Vue实例消亡后执行,

 

activated  :组件激活时调用。该钩子在服务器端渲染期间不被调用。

 

deactivated  :  组件停用时调用。该钩子在服务器端渲染期间不被调用。

 

errorCaptured  :   当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回 false 以阻止该错误继续向上传播。

 

生命周期图示如下:

 

 

————————————————

版权声明:本文为CSDN博主「慕枫520」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/qq_20446879/article/details/100057303

 

 

 

204. Vue-router有哪几种导航钩子?

问题一:vue-router有哪几种导航钩子( 导航守卫 )?

1、全局守卫: router.beforeEach

2、全局解析守卫: router.beforeResolve

3、全局后置钩子: router.afterEach

4、路由独享的守卫: beforeEnter

5、组件内的守卫: beforeRouteEnter、beforeRouteUpdate (2.2 新增)、beforeRouteLeave

导航表示路由正在发生改变,vue-router 提供的导航守卫主要用来:通过跳转或取消的方式守卫导航。有多种机会植入路由导航过程中:全局的, 单个路由独享的, 或者组件级的。

注意:参数或查询的改变并不会触发进入/离开的导航守卫。 你可以通过 观察 $route 对象 来应对这些变化,或使用 beforeRouteUpdate的组件内守卫。


1、全局守卫:

使用 router.beforeEach 注册一个全局前置守卫:

const router = new VueRouter({ ... })

  router.beforeEach((to, from, next) => {

  // ...

})

当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于等待中。

每个守卫方法接收三个参数:

to: Route: 即将要进入的目标 路由对象

from: Route: 当前导航正要离开的路由

next: Function: 一定要调用该方法来resolve这个钩子。执行效果依赖 next 方法的调用参数。

  • next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是confirmed (确认的)。
  • next(false): 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。
  • next('/') 或者 next({ path: '/' }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next 传递任意位置对象,且允许设置诸如 replace: true、name: 'home' 之类的选项以及任何用在router-link的 to prop或 router.push中的选项。
  • next(error): (2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError()注册过的回调。

确保要调用 next方法,否则钩子就不会被 resolved。


2、全局解析守卫:

2.5.0 新增

在 2.5.0+ 你可以用 router.beforeResolve 注册一个全局守卫。这和 router.beforeEach 类似,区别是:在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。


3、全局后置钩子

你也可以注册全局后置钩子,然而和守卫不同的是,这些钩子不会接受 next 函数也不会改变导航本身:

router.afterEach((to, from) => {

  // ...

})


4、路由独享的守卫

你可以在路由配置上直接定义 beforeEnter 守卫:

const router = new VueRouter({

  routes: [

    {

      path: '/foo',

      component: Foo,

      beforeEnter: (to, from, next) => {

        // ...

      }

    }

  ]

})

这些守卫与全局前置守卫的方法参数是一样的。


5、组件内的守卫

最后,你可以在路由组件内直接定义以下路由导航守卫:

beforeRouteEnter

beforeRouteUpdate (2.2 新增)

beforeRouteLeave

const Foo = {

  template: `...`,

  beforeRouteEnter (to, from, next) {

    // 在渲染该组件的对应路由被 confirm 前调用

    // 不!能!获取组件实例 `this`

    // 因为当守卫执行前,组件实例还没被创建

  },

  //不过,你可以通过传一个回调给 next来访问组件实例。

  //在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。

  beforeRouteEnter (to, from, next) {

    next(vm => {

      // 通过 `vm` 访问组件实例

    })

  },

  beforeRouteUpdate (to, from, next) {

    // 在当前路由改变,但是该组件被复用时调用

    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,

    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。

    // 可以访问组件实例 `this`

  },

  beforeRouteLeave (to, from, next) {

    // 导航离开该组件的对应路由时调用

    // 可以访问组件实例 `this`

  }

}

注意:beforeRouteEnter 是支持给next 传递回调的唯一守卫。对于beforeRouteUpdate 和 beforeRouteLeave 来说,this 已经可用了,所以不支持传递回调,因为没有必要了:

beforeRouteUpdate (to, from, next) {

  // just use `this`

  this.name = to.params.name

  next()

}

离开守卫beforeRouteLeave:通常用来禁止用户在还未保存修改前突然离开。该导航可以通过 next(false) 来取消:

beforeRouteLeave (to, from , next) {

  const answer = window.confirm('Do you really want to leave? you have unsaved changes!')

  if (answer) {

    next()

  } else {

    next(false)

  }

}

  1. 205.  

 

 

 

 

206. MVVM框架是什么?它和其它框架(jQuery)的区别是什么?哪些场景适合?

试题目之:mvvm框架是什么?它与其他框架(jquery)的区别是什么?哪些场景适合?

(1)mvvm框架是什么?

MVVM是Model-View-ViewModel的简写

Model:模型

View:视图

ViewModel:视图模型,连接view和model的桥梁

通常要实现一个observer观察者,当数据发生变化,ViewModel能够监听到数据的这种变化,然后通知到对应的视图做自动更新,而当用户操作视图,ViewModel 也能监听到视图的变化,然后通知数据做改动,这实际上就实现了数据的双向绑定。

(2)它和其它框架(jquery)的区别是什么?

概念上:vue:前端js库,是一个精简的MVVM,它专注于MVVM模型的viewModel层,通过双向数据绑定把view和model层连接起来,通过对数据的操作就可以完成对页面视图的渲染;

jquery:轻量级的js库,在操作思想上: vue是使用数据驱动的方式,通过vue对象将数据和view完全分离开,对数据操 作,不在引用相应的DOM对象,通过vue对象,将数据和相应的DOM对象相互绑定起 来;主要是操作数据基于一种MVVM模式,jQuery是使用选择器($)选取DOM对象,并对其进行赋值、取值、事件绑定等 操作,主要是操作DOM

(3)哪些场景适合?

应用场景的区别: vue适用的场景:复杂数据操作的后台页面,表单填写页面;

jquery适用的场景:比如说一些html5的动画页面,一些需要js来操作页面样式的页面。 二者也是可以结合起来一起使用的,vue侧重数据绑定,jquery侧重样式操作, 动画效果等,则会更加高效率的完成业务

 

 

 

207. Vue 组件间通信有哪些方式?

208. Vue 组件间通信有哪些方式?

如上图所示,A 和 B、B 和 C、B 和 D 都是父子关系,C 和 D 是兄弟关系,A 和 C 是隔代关系(可能隔多代)。

针对不同的使用场景,如何选择行之有效的通信方式?这是我们所要探讨的主题。本文总结了vue组件间通信的几种方式,如props、$emit/$on、vuex、$parent / $children$attrs/$listeners和provide/inject,以通俗易懂的实例讲述这其中的差别及使用场景,希望对小伙伴有些许帮助。

本文的代码请猛戳github博客,纸上得来终觉浅,大家动手多敲敲代码!

方法一、props/$emit

父组件A通过props的方式向子组件B传递,B to A 通过在 B 组件中 $emit, A 组件中 v-on 的方式实现。

1.父组件向子组件传值

接下来我们通过一个例子,说明父组件如何向子组件传递值:在子组件Users.vue中如何获取父组件App.vue中的数据 users:["Henry","Bucky","Emily"]

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

//App.vue父组件

<template>

  <div id="app">

    <users v-bind:users="users"></users>//前者自定义名称便于子组件调用,后者要传递数据名

  </div>

</template>

<script>

import Users from "./components/Users"

export default {

  name: 'App',

  data(){

    return{

      users:["Henry","Bucky","Emily"]

    }

  },

  components:{

    "users":Users

  }

}

  

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

//users子组件

<template>

  <div class="hello">

    <ul>

      <li v-for="user in users">{{user}}</li>//遍历传递过来的值,然后呈现到页面

    </ul>

  </div>

</template>

<script>

export default {

  name: 'HelloWorld',

  props:{

    users:{           //这个就是父组件中子标签自定义名字

      type:Array,

      required:true

    }

  }

}

</script>

  

总结:父组件通过props向下传递数据给子组件。注:组件中的数据共有三种形式:datapropscomputed

2.子组件向父组件传值(通过事件形式)

接下来我们通过一个例子,说明子组件如何向父组件传递值:当我们点击“Vue.js Demo”后,子组件向父组件传递值,文字由原来的“传递的是一个值”变成“子向父组件传值”,实现子组件向父组件值的传递。

 

 

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

// 子组件

<template>

  <header>

    <h1 @click="changeTitle">{{title}}</h1>//绑定一个点击事件

  </header>

</template>

<script>

export default {

  name: 'app-header',

  data() {

    return {

      title:"Vue.js Demo"

    }

  },

  methods:{

    changeTitle() {

      this.$emit("titleChanged","子向父组件传值");//自定义事件  传递值子向父组件传值

    }

  }

}

</script>

  

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

// 父组件

<template>

  <div id="app">

    <app-header v-on:titleChanged="updateTitle" ></app-header>//与子组件titleChanged自定义事件保持一致

   // updateTitle($event)接受传递过来的文字

    <h2>{{title}}</h2>

  </div>

</template>

<script>

import Header from "./components/Header"

export default {

  name: 'App',

  data(){

    return{

      title:"传递的是一个值"

    }

  },

  methods:{

    updateTitle(e){   //声明这个函数

      this.title = e;

    }

  },

  components:{

   "app-header":Header,

  }

}

</script>

  

 

总结:子组件通过events给父组件发送消息,实际上就是子组件把自己的数据发送到父组件。

方法二、$emit/$on

这种方法通过一个空的Vue实例作为中央事件总线(事件中心),用它来触发事件和监听事件,巧妙而轻量地实现了任何组件间的通信,包括父子、兄弟、跨级。当我们的项目比较大时,可以选择更好的状态管理解决方案vuex。

1.具体实现方式:

1

2

3

var Event=new Vue();

Event.$emit(事件名,数据);

Event.$on(事件名,data => {});

 

2.举个例子

假设兄弟组件有三个,分别是A、B、C组件,C组件如何获取A或者B组件的数据

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

<div id="itany">

    <my-a></my-a>

    <my-b></my-b>

    <my-c></my-c>

</div>

<template id="a">

  <div>

    <h3>A组件:{{name}}</h3>

    <button @click="send">将数据发送给C组件</button>

  </div>

</template>

<template id="b">

  <div>

    <h3>B组件:{{age}}</h3>

    <button @click="send">将数组发送给C组件</button>

  </div>

</template>

<template id="c">

  <div>

    <h3>C组件:{{name}}{{age}}</h3>

  </div>

</template>

<script>

var Event = new Vue();//定义一个空的Vue实例

var A = {

    template: '#a',

    data() {

      return {

        name: 'tom'

      }

    },

    methods: {

      send() {

        Event.$emit('data-a', this.name);

      }

    }

}

var B = {

    template: '#b',

    data() {

      return {

        age: 20

      }

    },

    methods: {

      send() {

        Event.$emit('data-b', this.age);

      }

    }

}

var C = {

    template: '#c',

    data() {

      return {

        name: '',

        age: ""

      }

    },

    mounted() {//在模板编译完成后执行

     Event.$on('data-a',name => {

         this.name = name;//箭头函数内部不会产生新的this,这边如果不用=>,this指代Event

     })

     Event.$on('data-b',age => {

         this.age = age;

     })

    }

}

var vm = new Vue({

    el: '#itany',

    components: {

      'my-a': A,

      'my-b': B,

      'my-c': C

    }

});

</script>

  

 

 

$on 监听了自定义事件 data-a和data-b,因为有时不确定何时会触发事件,一般会在 mounted 或 created 钩子中来监听。

 

方法三、vuex

 

 

 

1.简要介绍Vuex原理

Vuex实现了一个单向数据流,在全局拥有一个State存放数据,当组件要更改State中的数据时,必须通过Mutation进行,Mutation同时提供了订阅者模式供外部插件调用获取State数据的更新。而当所有异步操作(常见于调用后端接口异步获取更新数据)或批量的同步操作需要走Action,但Action也是无法直接修改State的,还是需要通过Mutation来修改State的数据。最后,根据State的变化,渲染到视图上。

2.简要介绍各模块在流程中的功能:

  • Vue Components:Vue组件。HTML页面上,负责接收用户操作等交互行为,执行dispatch方法触发对应action进行回应。
  • dispatch:操作行为触发方法,是唯一能执行action的方法。
  • actions:操作行为处理模块,由组件中的$store.dispatch('action 名称', data1)来触发。然后由commit()来触发mutation的调用 , 间接更新 state。负责处理Vue Components接收到的所有交互行为。包含同步/异步操作,支持多个同名方法,按照注册的顺序依次触发。向后台API请求的操作就在这个模块中进行,包括触发其他action以及提交mutation的操作。该模块提供了Promise的封装,以支持action的链式触发。
  • commit:状态改变提交操作方法。对mutation进行提交,是唯一能执行mutation的方法。
  • mutations:状态改变操作方法,由actions中的commit('mutation 名称')来触发。是Vuex修改state的唯一推荐方法。该方法只能进行同步操作,且方法名只能全局唯一。操作之中会有一些hook暴露出来,以进行state的监控等。
  • state:页面状态管理容器对象。集中存储Vue components中data对象的零散数据,全局唯一,以进行统一的状态管理。页面显示所需的数据从该对象中进行读取,利用Vue的细粒度数据响应机制来进行高效的状态更新。
  • getters:state对象读取方法。图中没有单独列出该模块,应该被包含在了render中,Vue Components通过该方法读取全局state对象。

3.Vuex与localStorage

vuex 是 vue 的状态管理器,存储的数据是响应式的。但是并不会保存起来,刷新之后就回到了初始状态,具体做法应该在vuex里数据改变的时候把数据拷贝一份保存到localStorage里面,刷新之后,如果localStorage里有保存的数据,取出来再替换store里的state

 

let defaultCity = "上海"
try {   // 用户关闭了本地存储功能,此时在外层加个try...catch
  if (!defaultCity){
    defaultCity = JSON.parse(window.localStorage.getItem('defaultCity'))
  }
}catch(e){}
export default new Vuex.Store({
  state: {
    city: defaultCity
  },
  mutations: {
    changeCity(state, city) {
      state.city = city
      try {
      window.localStorage.setItem('defaultCity', JSON.stringify(state.city));
      // 数据改变的时候把数据拷贝一份保存到localStorage里面
      } catch (e) {}
    }
  }
})

 

 

这里需要注意的是:由于vuex里,我们保存的状态,都是数组,而localStorage只支持字符串,所以需要用JSON转换:

1

2

JSON.stringify(state.subscribeList);   // array -> string

JSON.parse(window.localStorage.getItem("subscribeList"));    // string -> array

方法四、$attrs/$listeners

1.简介

多级组件嵌套需要传递数据时,通常使用的方法是通过vuex。但如果仅仅是传递数据,而不做中间处理,使用 vuex 处理,未免有点大材小用。为此Vue2.4 版本提供了另一种方法----$attrs/$listeners

  • · $attrs:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件。通常配合 inheritAttrs 选项一起使用。
  • · $listeners:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件

接下来我们看个跨级通信的例子:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

// index.vue

<template>

  <div>

    <h2>浪里行舟</h2>

    <child-com1

      :foo="foo"

      :boo="boo"

      :coo="coo"

      :doo="doo"

      title="前端工匠"

    ></child-com1>

  </div>

</template>

<script>

const childCom1 = () => import("./childCom1.vue");

export default {

  components: { childCom1 },

  data() {

    return {

      foo: "Javascript",

      boo: "Html",

      coo: "CSS",

      doo: "Vue"

    };

  }

};

</script>

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

// childCom1.vue

<template class="border">

  <div>

    <p>foo: {{ foo }}</p>

    <p>childCom1$attrs: {{ $attrs }}</p>

    <child-com2 v-bind="$attrs"></child-com2>

  </div>

</template>

<script>

const childCom2 = () => import("./childCom2.vue");

export default {

  components: {

    childCom2

  },

  inheritAttrs: false, // 可以关闭自动挂载到组件根元素上的没有在props声明的属性

  props: {

    foo: String // foo作为props属性绑定

  },

  created() {

    console.log(this.$attrs); // { "boo": "Html", "coo": "CSS", "doo": "Vue", "title": "前端工匠" }

  }

};

</script>

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

// childCom2.vue

<template>

  <div class="border">

    <p>boo: {{ boo }}</p>

    <p>childCom2: {{ $attrs }}</p>

    <child-com3 v-bind="$attrs"></child-com3>

  </div>

</template>

<script>

const childCom3 = () => import("./childCom3.vue");

export default {

  components: {

    childCom3

  },

  inheritAttrs: false,

  props: {

    boo: String

  },

  created() {

    console.log(this.$attrs); // { "coo": "CSS", "doo": "Vue", "title": "前端工匠" }

  }

};

</script>

  

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

// childCom3.vue

<template>

  <div class="border">

    <p>childCom3: {{ $attrs }}</p>

  </div>

</template>

<script>

export default {

  props: {

    coo: String,

    title: String

  }

};

</script>

// childCom3.vue

<template>

  <div class="border">

    <p>childCom3: {{ $attrs }}</p>

  </div>

</template>

<script>

export default {

  props: {

    coo: String,

    title: String

  }

};

</script>

  

 

 

如上图所示$attrs表示没有继承数据的对象,格式为{属性名:属性值}。Vue2.4提供了$attrs , $listeners 来传递数据与事件,跨级组件之间的通讯变得更简单。

 

简单来说:$attrs$listeners 是两个对象,$attrs 里存放的是父组件中绑定的非 Props 属性,$listeners里存放的是父组件中绑定的非原生事件。

方法五、provide/inject

1.简介

Vue2.2.0新增API,这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。一言而蔽之:祖先组件中通过provider来提供变量,然后在子孙组件中通过inject来注入变量。 provide / inject API 主要解决了跨级组件间的通信问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系

2.举个例子

假设有两个组件: A.vue 和 B.vue,B 是 A 的子组件

1

2

3

4

5

6

// A.vue

export default {

  provide: {

    name: '浪里行舟'

  }

}

1

2

3

4

5

6

7

// B.vue

export default {

  inject: ['name'],

  mounted () {

    console.log(this.name);  // 浪里行舟

  }

}

  

  

可以看到,在 A.vue 里,我们设置了一个 provide: name,值为 浪里行舟,它的作用就是将 name 这个变量提供给它的所有子组件。而在 B.vue 中,通过 inject 注入了从 A 组件中提供的 name 变量,那么在组件 B 中,就可以直接通过 this.name 访问这个变量了,它的值也是 浪里行舟。这就是 provide / inject API 最核心的用法。

需要注意的是:provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的----vue官方文档 所以,上面 A.vue 的 name 如果改变了,B.vue 的 this.name 是不会改变的,仍然是 浪里行舟。

3.provide与inject 怎么实现数据响应式

一般来说,有两种办法:

  • provide祖先组件的实例,然后在子孙组件中注入依赖,这样就可以在子孙组件中直接修改祖先组件的实例的属性,不过这种方法有个缺点就是这个实例上挂载很多没有必要的东西比如props,methods
  • 使用2.6最新API Vue.observable 优化响应式 provide(推荐)

我们来看个例子:孙组件D、E和F获取A组件传递过来的color值,并能实现数据响应式变化,即A组件的color变化后,组件D、E、F不会跟着变(核心代码如下:)

 

 

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

// A 组件

<div>

      <h1>A 组件</h1>

      <button @click="() => changeColor()">改变color</button>

      <ChildrenB />

      <ChildrenC />

</div>

......

  data() {

    return {

      color: "blue"

    };

  },

  // provide() {

  //   return {

  //     theme: {

  //       color: this.color //这种方式绑定的数据并不是可响应的

  //     } // A组件的color变化后,组件DEF不会跟着变

  //   };

  // },

  provide() {

    return {

      theme: this//方法一:提供祖先组件的实例

    };

  },

  methods: {

    changeColor(color) {

      if (color) {

        this.color = color;

      } else {

        this.color = this.color === "blue" ? "red" : "blue";

      }

    }

  }

  // 方法二:使用2.6最新API Vue.observable 优化响应式 provide

  // provide() {

  //   this.theme = Vue.observable({

  //     color: "blue"

  //   });

  //   return {

  //     theme: this.theme

  //   };

  // },

  // methods: {

  //   changeColor(color) {

  //     if (color) {

  //       this.theme.color = color;

  //     } else {

  //       this.theme.color = this.theme.color === "blue" ? "red" : "blue";

  //     }

  //   }

  // }

  

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

// F 组件

<template functional>

  <div class="border2">

    <h3 :style="{ color: injections.theme.color }">F 组件</h3>

  </div>

</template>

<script>

export default {

  inject: {

    theme: {

      //函数式组件取值不一样

      default: () => ({})

    }

  }

};

</script>

  

 

虽说provide 和 inject 主要为高阶插件/组件库提供用例,但如果你能在业务中熟练运用,可以达到事半功倍的效果!

方法六、$parent / $children与 ref

  • ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例
  • $parent / $children:访问父 / 子实例

需要注意的是:这两种都是直接得到组件实例,使用后可以直接调用组件的方法或访问数据。我们先来看个用 ref来访问组件的例子:

1

2

3

4

5

6

7

8

9

10

11

12

13

// component-a 子组件

export default {

  data () {

    return {

      title: 'Vue.js'

    }

  },

  methods: {

    sayHello () {

      window.alert('Hello');

    }

  }

}

  

1

2

3

4

5

6

7

8

9

10

11

12

13

// 父组件

<template>

  <component-a ref="comA"></component-a>

</template>

<script>

  export default {

    mounted () {

      const comA = this.$refs.comA;

      console.log(comA.title);  // Vue.js

      comA.sayHello();  // 弹窗

    }

  }

</script>

  

 

不过,这两种方法的弊端是,无法在跨级或兄弟间通信

1

2

3

4

// parent.vue

<component-a></component-a>

<component-b></component-b>

<component-b></component-b>

  

我们想在 component-a 中,访问到引用它的页面中(这里就是 parent.vue)的两个 component-b 组件,那这种情况下,就得配置额外的插件或工具了,比如 Vuex 和 Bus 的解决方案。

总结

常见使用场景可以分为三类:

  • 父子通信: 父向子传递数据是通过 props,子向父是通过 events($emit);通过父链 / 子链也可以通信($parent / $children);ref 也可以访问组件实例;provide / inject API;$attrs/$listeners
  • 兄弟通信: Bus;Vuex
  • 跨级通信: Bus;Vuex;provide / inject API、$attrs/$listeners

 

 

 

 

209. Vue 首屏加载优化

  1. 单页面应用的一个问题就是首页加载东西过多,加载时间过长。特别在移动端,单页面应用的首屏加载优化更是绕不开的话题。下面我会写出我在项目中做的一些优化,希望大家能够相互讨论,共同进步。
  2. 我的项目vue-cli3构建的,vue+vue-router+vuexUI框架选用 element-uiajax方案选用 axios,服务器使用Nginx。用到的这些技术都是现在用的比较广泛的,看到这篇文章,我估计你和我用的技术应该差不多。
  3. 首页我们来看看没有经过任何优化的打包分析,vue-cli3的项目直接vue-cli-service build --report就会生成一个report.html,打开这个html就能看到,不是vue-cli3的项目需要自行安装这个插件,参考链接,点击
  4. 如上图所示在vendor比较大的文件有element,moment,echart,还有jquery,然后还有一些没见过的vue-qriously这些组件,接下来我们来一步一步让vendor变小
  5. 1. 仔细考虑组件是否需要全局引入
  6. 在我们的main.js,我发现有很多组件被全局引入,其中有些组件只有1,2个页面用到,这些组件不需要全部引入
  7. 上面一段代码是我们main.js中的代码,其中ImageComponent是用来处理图片的,用到的页面很多,其他的组件都只要较少的页面用到,我们在main.js中删除,移到具体的页面中去。
  8. 2. 手动引入 ECharts 各模块
  9. 默认引入 ECharts 是引入全部的```import * as ECharts from 'echarts' ```我们只需要部分组件,只需引入自己需要的部分。参考地址,点击
  10. 3.使用更轻量级的工具库
  11. moment是处理时间的标杆,但是它过于庞大且默认不支持tree-shaking,而且我们的项目中只用到了moment(), format(), add(), subtract()等几个非常简单的方法,有点大材小用,所以我们用 date-fns 来替换它,需要什么方法直接引入就行。
  12.  
  13.  
  14. 经过上面的三步初步优化,我们可以看到vendor.js变小了很多,去除了moment,我们项目之前echart就是按需加载的。
  15.  进过上面的优化,发现 Vue 全家桶以及 ElementUI 仍然占了很大一部分 vendors 体积,这部分代码是不变的,但会随着每次 vendors 打包改变 hash 重新加载。我们可以使用 CDN 剔除这部分不经常变化的公共库。我们将vuevue-routervuexaxiosjqueryunderscore,使用CDN资源引入。国内的CDN服务推荐使用 BootCDN
  16. 1.首先我们在index.html中,添加CDN代码
  17. 2.vue.config.js中加入webpack配置代码,关于webpack配置中的externals,请参考地址
  18. 3. 去除vue.use相关代码
  19. 需要注意的是,通过 CDN 引入,在使用 VueRouter Vuex ElementUI 的时候要改下写法。CDN会把它们挂载到window上,因此不再使用Vue.use(xxx)
  20. 也不在需import Vue from 'vue', import VueRouter from 'vue-router' 等。
  21. 剔除全家桶和Element-ui等只有,剩下的需要首次加载 vendors 就很小了。
  22. 使用 CDN 的好处有以下几个方面
  23. 1)加快打包速度。分离公共库以后,每次重新打包就不会再把这些打包进 vendors 文件中。
  24. 2CDN减轻自己服务器的访问压力,并且能实现资源的并行下载。浏览器对 src 资源的加载是并行的(执行是按照顺序的)
  25.  
  26. 如下图所示,开启了gzip后js的大小比未开启gzip的js小2/3左右,所以如果没开启gzip,感觉我们做的再多意义也不大,如何看自己的项目有没有开启gzip,如下图所示,开启了gzip,在浏览器的控制台Content-Encoding一栏会显示gzip,否则没有。Nginx如果开启gzip,请自行搜索,或者叫服务端来开启。
  27.  
  28.  
  29. 路由组件如果不按需加载的话,就会把所有的组件一次性打包到app.js中,导致首次加载内容过多,vue官方文档中也有提到,地址
  30. 上面的两种引入组件的方法都是正确的,都能实现路由的懒加载。
  31.  
  32. 最后我们可以发现vendor.js的大小减少了很多。其中第一步到第三步我们项目中都没做,第四步和第五步我们做了。如果读者你没做,一定要注意了。最后希望这篇文字能够对大家有一点点帮组

212.    第一步:webpack-bundle-analyzer 分析

216.    第二步:初步优化

220.      import ImageComponent from 'COMMON/imageComponent'
221.      import InfiniteLoading from 'COMMON/infiniteLoading'
222.      import SearchDialog from 'COMMON/SearchDialog'
223.      import BasicTable from 'COMMON/BasicTable'
224.      import VueQriously from 'vue-qriously'
225.       
226.      Vue.use(ImageComponent)
227.      Vue.use(InfiniteLoading) // 可以去除
228.      Vue.use(SearchDialog) // 可以去除
229.      Vue.use(BasicTable)  // 可以去除
230.      Vue.use(VueQriously)  // 可以去除
236.      import VueECharts from 'vue-echarts/components/ECharts.vue'
237.      import 'echarts/lib/chart/line'
238.      import 'echarts/lib/chart/bar'
239.      import 'echarts/lib/chart/pie'
240.      import 'echarts/lib/component/title'
241.      import 'echarts/lib/component/tooltip'
242.      import 'echarts/lib/component/legend'
243.      import 'echarts/lib/component/markPoint'

250.    第三步:CDN优化

254.      ...
255.      <link href="https://cdn.bootcss.com/element-ui/2.7.2/theme-chalk/index.css" rel="stylesheet">
256.        </head>
257.        <body>
258.          <div id="app"></div>
259.          <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
260.          <script src="https://cdn.bootcss.com/vuex/3.1.0/vuex.min.js"></script>
261.          <script src="https://cdn.bootcss.com/vue-router/3.0.4/vue-router.min.js"></script>
262.          <script src="https://cdn.bootcss.com/axios/0.18.0/axios.min.js"></script>
263.          <script src="https://cdn.bootcss.com/element-ui/2.7.2/index.js"></script>
264.          <script src="https://cdn.bootcss.com/jquery/3.4.0/jquery.min.js"></script>
265.          <script src="https://cdn.bootcss.com/underscore.js/1.9.1/underscore-min.js"></script>
266.        </body>
267.      </html>
271.      configureWebpack: {
272.        externals: {
273.          'vue': 'Vue',
274.          'vue-router': 'VueRouter',
275.          'vuex': 'Vuex',
276.          'element-ui': 'ELEMENT',
277.          'axios': 'axios',
278.          'underscore' : {
279.            commonjs: 'underscore',
280.            amd: 'underscore',
281.            root: '_'
282.          },
283.          'jquery': {
284.            commonjs: 'jQuery',
285.            amd: 'jQuery',
286.            root: '$'
287.          }
288.        },
289.      }

300.     第四步:检查Nginx 是否开启 gzip

304.    第五步:检查路由懒加载

307.      {
308.        name: 'vipBoxActivity',
309.        path:'vipBoxActivity',
310.        component(resolve) {
311.          require(['COMPONENTS/vipBox/vipBoxActivity/main.vue'], resolve)
312.        }
313.      },
315.      {
316.        path: 'buyerSummary',
317.        name: 'buyerSummary',
318.        component: () => import('VIEWS/buyer/buyerSummary/index'),
319.      },

322.    最后 

324. 标签jsvuespa

 

 

 

 

325. Vue-cli 替我们做了哪些工作?

vue-cli是Vue.js官方脚手架命令行工具,我们可以用它快速搭建Vue.js项目,vue-cli最主要的功能就是初始化项目,既可以使用官方模板,也可以使用自定义模板生成项目,而且从2.8.0版本开始,vue-cli新增了build命令,能让你零配置启动一个Vue.js应用。接下来,我们一起探究一下vue-cli是如何工作的。

全局安装

首先,vue-cli是一个node包,且可以在终端直接通过vue命令调用,所以vue-cli需要全局安装,当npm全局安装一个包时,主要做了两件事:

  1. 将包安装到全局的node_modules目录下。
  2. 在bin目录下创建对应的命令,并链接到对应的可执行脚本。

看一下vue-cli的package.json,可以发现如下代码:

{
  "bin": {
    "vue": "bin/vue",
    "vue-init": "bin/vue-init",
    "vue-list": "bin/vue-list",
    "vue-build": "bin/vue-build"
  }
}

这样在全局安装vue-cli后,npm会帮你注册vuevue-initvue-listvue-build这几个命令。

 

项目结构

vue-cli项目本身也不大,项目结构如下:

.
├── bin
├── docs
├── lib
└── test
    └── e2e

bin目录下是可执行文件,docs下是新特性vue build的文档,lib是拆分出来的类库,test下是测试文件,我们着重看bin目录下的文件即可。

bin/vue

首先看bin/vue,内容很简短,只有如下代码:

#!/usr/bin/env node
 
require('commander')
  .version(require('../package').version)
  .usage('<command> [options]')
  .command('init', 'generate a new project from a template')
  .command('list', 'list available official templates')
  .command('build', 'prototype a new project')
  .parse(process.argv)

vue-cli是基于commander.js写的,支持Git-style sub-commands,所以执行vue init可以达到和vue-init同样的效果。

bin/vue-init

接下来看bin/vue-initvue-init的主要作用是根据指定模板生成项目原型。文件首先是引入一些依赖模块和lib中的辅助函数,因为init命令需要接收至少一个参数,所以vue-init第一个被执行到的就是检验入参的help函数,如果没有传入参数,则打印提示,传入参数则继续运行。

再向下是解析参数的过程:

var template = program.args[0]
var hasSlash = template.indexOf('/') > -1
var rawName = program.args[1]
var inPlace = !rawName || rawName === '.'
var name = inPlace ? path.relative('../', process.cwd()) : rawName
var to = path.resolve(rawName || '.')
var clone = program.clone || false
 
var tmp = path.join(home, '.vue-templates', template.replace(/\//g, '-'))
if (program.offline) {
  console.log(`> Use cached template at ${chalk.yellow(tildify(tmp))}`)
  template = tmp
}

template是模板名,第二个参数(program.args[1])rawName 为项目名,如果不存在或为.则视为在当前目录下初始化(inPlace = true),默认项目名称name也为当前文件夹名。to是项目的输出路径,后面会用到。clone参数判断是否使用git clone的方式下载模板,当模板在私有仓库时用得上。offline参数决定是否使用离线模式,如果使用离线模式,vue-cli会尝试去~/.vue-templates下获取对应的模板,可以省去漫长的downloading template的等待时间,但是模板是不是最新的版本就无法确定了。

前面在处理参数时会得到一个变量to,表示即将生成的项目路径,如果已存在,则会输出警告,让用户确认是否继续,确认后执行run函数:

if (exists(to)) {
  inquirer.prompt([{
    type: 'confirm',
    message: inPlace
      ? 'Generate project in current directory?'
      : 'Target directory exists. Continue?',
    name: 'ok'
  }], function (answers) {
    if (answers.ok) {
      run()
    }
  })
} else {
  run()
}

run函数主要检查了模板是否是本地模板,然后获取或下载模板,获取到模板后执行generate函数。

generate函数是生成项目的核心,主要代码:

module.exports = function generate (name, src, dest, done) {
  var opts = getOptions(name, src)
  // Metalsmith读取template下所有资源
  var metalsmith = Metalsmith(path.join(src, 'template'))
  var data = Object.assign(metalsmith.metadata(), {
    destDirName: name,
    inPlace: dest === process.cwd(),
    noEscape: true
  })
  opts.helpers && Object.keys(opts.helpers).map(function (key) {
    Handlebars.registerHelper(key, opts.helpers[key])
  })
 
  var helpers = {chalk, logger}
 
  if (opts.metalsmith && typeof opts.metalsmith.before === 'function') {
    opts.metalsmith.before(metalsmith, opts, helpers)
  }
  // 一次使用askQuestions, filterFiles, renderTemplateFiles处理读取的内容
  metalsmith.use(askQuestions(opts.prompts))
    .use(filterFiles(opts.filters))
    .use(renderTemplateFiles(opts.skipInterpolation))
 
  if (typeof opts.metalsmith === 'function') {
    opts.metalsmith(metalsmith, opts, helpers)
  } else if (opts.metalsmith && typeof opts.metalsmith.after === 'function') {
    opts.metalsmith.after(metalsmith, opts, helpers)
  }
  // 将处理后的文件输出
  metalsmith.clean(false)
    .source('.') // start from template root instead of `./src` which is Metalsmith's default for `source`
    .destination(dest)
    .build(function (err, files) {
      done(err)
      if (typeof opts.complete === 'function') {
        var helpers = {chalk, logger, files}
        opts.complete(data, helpers)
      } else {
        logMessage(opts.completeMessage, data)
      }
    })
 
  return data
}

首先通过getOptions获取了一些项目的基础配置信息,如项目名,git用户信息等。然后通过metalsmith结合askQuestions,filterFiles,renderTemplateFiles这几个中间件完成了项目模板的生成过程。metalsmith是一个插件化的静态网站生成器,它的一切都是通过插件运作的,这样可以很方便地为其扩展。
通过generate函数的代码,很容易看出来生成项目的过程主要是以下几个阶段。

 

每个过程主要用了以下库:

  • getOptions: 主要是读取模板下的meta.jsonmeta.jsmeta.json是必须的文件,为cli提供多种信息,例如自定义的helper,自定义选项,文件过滤规则等等。该如何写一个自定义模板,可以参考这里
  • 通过Metalsmith读取模板内容,需要注意的是,此时的模板内容还是未被处理的,所以大概长这样:
  • 获取自定义配置: 主要是通过asyncinquirer的配合完成收集用户自定义配置。
  • filterFiles: 对文件进行过滤,通过minimatch进行文件匹配。
  • 渲染模板:通过consolidate.js配合handlebars渲染文件。
  • 输出:直接输出
/* eslint-disable no-new */
new Vue({
  el: '#app',
  {{#router}}
  router,
  {{/router}}
  {{#if_eq build "runtime"}}
  render: h => h(App){{#if_eq lintConfig "airbnb"}},{{/if_eq}}
  {{/if_eq}}
  {{#if_eq build "standalone"}}
  template: '<App/>',
  components: { App }{{#if_eq lintConfig "airbnb"}},{{/if_eq}}
  {{/if_eq}}
}){{#if_eq lintConfig "airbnb"}};{{/if_eq}}

vue-init的整个工作流程大致就是这样,vue-cli作为一个便捷的命令行工具,其代码写的也简洁易懂,而且通过分析源码,可以发现其中用到的很多有意思的模块。

bin/vue-list

vue-list功能很简单,拉取vuejs-templates的模板信息并输出。

bin/vue-build

vue-build则是通过一份webpack配置将项目跑起来,如果是入口仅是一个.vue组件,就使用默认的default-entry.es6加载组件并渲染。

其他

在看vue-cli源码时,发现了user-home这个模块,这个模块的内容如下:

'use strict';
module.exports = require('os-homedir')();

os-homedir这个包是一个os.homedir的polyfill,在Why not just use the os-home module?下,我看到了Modules are cheap in Node.js这个blog。事实上sindresorhus写了很多的One-line node modules,他也很喜欢One-line node moduels,因为模块越小,就意味着灵活性和重用性更高。当然对于One-line modules,每个人的看法不一样,毕竟也不是第一次听到就这一个函数也tm能写个包的话了。我认为这个要因人而异,sindresorhus何许人也,很多著名开源项目的作者,发布的npm包1000+,大多数他用到的模块,都是他自己写的,所以对他来说,使用各种“积木”去组建“高楼”得心应手。不过对于其他人来说,如果习惯于这种方式,可能会对这些东西依赖性变强,就像现在很多前端开发依赖框架而不重基础一样,所以我认为这种“拼积木”开发方式挺好,但最好还是要知其所以然。但是我感觉One-line modules的作用却不大,就像user-home这个模块,如果没有它,const home = require('os-homedir')();也可以达到目的,可能处于强迫症的原因,user-home才诞生了吧,而且像negative-zero这样的One-line modules,使用场景少是其一,而且也没带来什么方便,尤其是2.0版本,这个包直接使用Object.is去判断了:

'use strict';
module.exports = x => Object.is(x, -0);

不知道大家对One-line modules是什么看法?

 

 

 

 

326. Axios和Ajax的区别

327. ajax

328. 1、什么是ajax

329.   Ajax是对原生XHR的封装,为了达到我们跨越的目的,增添了对JSONP的支持。

330.   异步的javascript和xml,ajax不是一门新技术,而是多种技术的组合,用于快速的创建动态页面,能够实现无刷新更新数据从而提高用户体验。

331. 2ajax的原理?

332.   由客户端请求ajax引擎,再由ajax引擎请求服务器,服务器作出一系列响应之后返回给ajax引擎,由ajax引擎决定将这个结果写入到客户端的什么位置。实现页面无刷新更新数据。

333. 3、核心对象?

334.   XMLHttpReques

335. 4ajax优缺点?

336.   优点 

337.     1、 无刷新更新数据

338.     2、异步与服务器通信

339.     3、前端和后端负载平衡

340.     4、基于标准被广泛支持

341.     5、界面与应用分离

342.   缺点:

343.     1、ajax不能使用Back和history功能,即对浏览器机制的破坏。

344.     2、安全问题  ajax暴露了与服务器交互的细节

345.     3、对收索引擎的支持比较弱

346.     4、破坏程序的异常处理机制

347.     5、违背URL和资源定位的初衷

348.     6、ajax不能很好的支持移动设备

349.     7、太多客户端代码造成开发上的成本

350. 5Ajax适用场景
  <1>.表单驱动的交互
  <2>.深层次的树的导航
  <3>.快速的用户与用户间的交流响应
  <4>.类似投票、yes/no等无关痛痒的场景
  <5>.对数据进行过滤和操纵相关数据的场景
  <6>.普通的文本输入提示和自动完成的场景
6Ajax不适用场景
  <1>.部分简单的表单
  <2>.搜索
  <3>.基本的导航
  <4>.替换大量的文本
  <5>.对呈现的操纵

351. 7、代码

  1. $.ajax({
  2.   type: 'POST',
  3.   url: url,
  4.   data: data,
  5.   dataType: dataType,
  6.   success: function () {},
  7.   error: function () {}
  8. });

362. 8ajax请求的五个步骤

363.   1. 创建XMLHttpRequest异步对象

364.   2. 设置回调函数

365.   3. 使用open方法与服务器建立连接

366.   4. 向服务器发送数据

367.   5. 在回调函数中针对不同的响应状态进行处理

368. axios

369. 1axios是什么

370. Axios 是一个基于 Promise 的 HTTP 库,可以用在浏览器和 node.js 中。

371. 2axios有那些特性?

372.   1、在浏览器中创建 XMLHttpRequests

373.   2、在node.js则创建http请求

374.   3、支持Promise API

375.   4、支持拦截请求和响应

376.   5、转换请求和响应数据

377.   6、取消请求

378.   7、自动转换成JSON数据格式

379.   8、客户端支持防御XSRF

380. 3、执行get请求,有两种方式

  1. // 第一种方式  将参数直接写在url中
  2. axios.get('/getMainInfo?id=123')
  3. .then((res) => {
  4.   console.log(res)
  5. })
  6. .catch((err) => {
  7.   console.log(err)
  8. })
  9. // 第二种方式  将参数直接写在params中
  10. axios.get('/getMainInfo', {
  11.   params: {
  12.     id: 123
  13.   }
  14. })
  15. .then((res) => {
  16.   console.log(res)
  17. })
  18. .catch((err) => {
  19.   console.log(err)
  20. })

403. 4、执行post请求,注意执行post请求的入参,不需要写在params字段中,这个地方要注意与get请求的第二种方式进行区别。

  1. axios.post('/getMainInfo', {
  2.   id: 123
  3. })
  4. .then((res) => {
  5.   console.log(res)
  6. })
  7. .catch((err) => {
  8.   console.log(err)
  9. })

415. axiosajax的区别:

416. axios是通过Promise实现对ajax技术的一种封装,就像jquery对ajax的封装一样,简单来说就是ajax技术实现了局部数据的刷新,axios实现了对ajax的封装,axios有的ajax都有,ajax有的axios不一定有,总结一句话就是axios是ajax,ajax不止axios。

417. 注: 传统Ajax 指的是 XMLHttpRequest(XHR),
  axios和jQuer ajax都是对Ajax的封装

 

posted @ 2021-11-17 09:43  竹雨禅月  阅读(641)  评论(0)    收藏  举报