web前端面试题
Web前端測試題
姓名:
本試題僅做為參考用,請輕鬆作答,以現有知識回答即可,請勿上網或找朋友問答案
- 页面从输入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,使用Trident的是internet explorer,国产的绝大部分浏览器。Trident是就是ie内核
2,使用Gecko的是Mozilla Firefox,使用 Gecko 内核的浏览器也有不少,如 Netscape MozillaSuite/SeaMonkey 等
3,使用Presto的是opera,这是目前公认网页浏览速度最快的浏览器内核
4,使用WebKit的有苹果的safari,谷歌的chrome,还有国产的大部分双核浏览器其中一核就是WebKit
- 请描述一下 Cookies,SessionStorage 和 LocalStorage 的区别?
 - ⒈localStorage长期存储数据,浏览器关闭数据后不丢失;
 - ⒉sessionStorage数据在浏览器关闭后自动删除;
 - ⒊cookie是网站为了标识用户身份而存储在用户本地终端(Client Side)上的数据(通常经过加密)。cookie始终在同源的http请求中携带(即使不需要)都会在浏览器和服务器端间来回传递。session storage和local storage不会自动把数据发给服务器,仅在本地保存;
 - ⒋存储大小:cookie数据大小不会超过4K,session storage和local storage虽然也有存储大小的限制,但比cookie大得多,可以达到5M或者更多;
 - ⒌有期时间:local storage存储持久数据,浏览器关闭后数据不丢失,除非自动删除数据。session storage数据在当前浏览器窗口关闭后自动删除。cookie 设置的cookie过期时间之前一直有效,即使窗口或者浏览器关闭;
 
- 请写列出前端性能优化的方式
 - 本文将详细介绍前端性能优化的七大手段,包括减少请求数量、减小资源大小、优化网络连接、优化资源加载、减少重绘回流、使用性能更好的API和构建优化
 - 【合并】
 - 如果不进行文件合并,有如下3个隐患
 - 1、文件与文件之间有插入的上行请求,增加了N-1个网络延迟
 - 2、受丢包问题影响更严重
 - 3、经过代理服务器时可能会被断开
 - 但是,文件合并本身也有自己的问题
 - 1、首屏渲染问题
 - 2、缓存失效问题
 - 所以,对于文件合并,有如下改进建议
 - 1、公共库合并
 - 2、不同页面单独合并
 - 【图片处理】
 - 1、雪碧图
 - CSS雪碧图是以前非常流行的技术,把网站上的一些图片整合到一张单独的图片中,可以减少网站的HTTP请求数量,但是当整合图片比较大时,一次加载比较慢。随着字体图片、SVG图片的流行,该技术渐渐退出了历史舞台
 - 2、Base64
 - 将图片的内容以Base64格式内嵌到HTML中,可以减少HTTP请求数量。但是,由于Base64编码用8位字符表示信息中的6个位,所以编码后大小大约比原始值扩大了 33%
 - 3、使用字体图标来代替图片
 - 【减少重定向】
 - 尽量避免使用重定向,当页面发生了重定向,就会延迟整个HTML文档的传输。在HTML文档到达之前,页面中不会呈现任何东西,也没有任何组件会被下载,降低了用户体验
 - 如果一定要使用重定向,如http重定向到https,要使用301永久重定向,而不是302临时重定向。因为,如果使用302,则每一次访问http,都会被重定向到https的页面。而永久重定向,在第一次从http重定向到https之后 ,每次访问http,会直接返回https的页面
 - 【使用缓存】
 - 使用cach-control或expires这类强缓存时,缓存不过期的情况下,不向服务器发送请求。强缓存过期时,会使用last-modified或etag这类协商缓存,向服务器发送请求,如果资源没有变化,则服务器返回304响应,浏览器继续从本地缓存加载资源;如果资源更新了,则服务器将更新后的资源发送到浏览器,并返回200响应
 - 【不使用CSS @import】
 - CSS的@import会造成额外的请求
 - 【避免使用空的src和href】
 - a标签设置空的href,会重定向到当前的页面地址
 - form设置空的method,会提交表单到当前的页面地址
 - 【压缩】
 - 1、HTML压缩
 - HTML代码压缩就是压缩在文本文件中有意义,但是在HTML中不显示的字符,包括空格,制表符,换行符等
 - 2、CSS压缩
 - CSS压缩包括无效代码删除与CSS语义合并
 - 3、JS压缩与混乱
 - JS压缩与混乱包括无效字符及注释的删除、代码语义的缩减和优化、降低代码可读性,实现代码保护
 - 4、图片压缩
 - 针对真实图片情况,舍弃一些相对无关紧要的色彩信息
 - 【webp】
 - 在安卓下可以使用webp格式的图片,它具有更优的图像数据压缩算法,能带来更小的图片体积,同等画面质量下,体积比jpg、png少了25%以上,而且同时具备了无损和有损的压缩模式、Alpha 透明以及动画的特性
 - 【开启gzip】
 - HTTP协议上的GZIP编码是一种用来改进WEB应用程序性能的技术。大流量的WEB站点常常使用GZIP压缩技术来让用户感受更快的速度。这一般是指WWW服务器中安装的一个功能,当有人来访问这个服务器中的网站时,服务器中的这个功能就将网页内容压缩后传输到来访的电脑浏览器中显示出来。一般对纯文本内容可压缩到原大小的40%
 - 【使用CDN】
 - CDN全称是Content Delivery Network,即内容分发网络,它能够实时地根据网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上。其目的是使用户可就近取得所需内容,解决 Internet网络拥挤的状况,提高用户访问网站的响应速度
 - 【使用DNS预解析】
 - 当浏览器访问一个域名的时候,需要解析一次DNS,获得对应域名的ip地址。在解析过程中,按照
浏览器缓存、系统缓存、路由器缓存、ISP(运营商)DNS缓存、根域名服务器、顶级域名服务器、主域名服务器的顺序,逐步读取缓存,直到拿到IP地址 - DNS Prefetch,即DNS预解析就是根据浏览器定义的规则,提前解析之后可能会用到的域名,使解析结果缓存到
系统缓存中,缩短DNS解析时间,来提高网站的访问速度 - 方法是在 head 标签里面写上几个 link 标签
 - 对以上几个网站提前解析 DNS,由于它是并行的,不会堵塞页面渲染,这样可以缩短资源加载的时间
 - 【并行连接】
 - 由于在HTTP1.1协议下,chrome每个域名的最大并发数是6个。使用多个域名,可以增加并发数
 - 【持久连接】
 - 使用keep-alive或presistent来建立持久连接,持久连接降低了时延和连接建立的开销,将连接保持在已调谐状态,而且减少了打开连接的潜在数量
 - 【管道化连接】
 - 在HTTP2协议中,可以开启管道化连接,即单条连接的多路复用,每条连接中并发传输多个资源,这里就不需要添加域名来增加并发数了
 - 【资源加载位置】
 - 通过优化资源加载位置,更改资源加载时机,使尽可能快地展示出页面内容,尽可能快地使功能可用
 - 1、CSS文件放在head中,先外链,后本页
 - 2、JS文件放在body底部,先外链,后本页
 - 3、处理页面、处理页面布局的JS文件放在head中,如babel-polyfill.js文件、flexible.js文件
 - 4、body中间尽量不写style标签和script标签
 - 【资源加载时机】
 - 1、异步script标签
 - defer: 异步加载,在HTML解析完成后执行。defer的实际效果与将代码放在body底部类似
 - async: 异步加载,加载完成后立即执行
 - 2、模块按需加载
 - 在SPA等业务逻辑比较复杂的系统中,需要根据路由来加载当前页面需要的业务模块
 - 按需加载,是一种很好的优化网页或应用的方式。这种方式实际上是先把代码在一些逻辑断点处分离开,然后在一些代码块中完成某些操作后,立即引用或即将引用另外一些新的代码块。这样加快了应用的初始加载速度,减轻了它的总体体积,因为某些代码块可能永远不会被加载
 - webpack 提供了两个类似的技术,优先选择的方式是使用符合 ECMAScript 提案 的 import() 语法。第二种则是使用 webpack 特定的 require.ensure
 - 3、使用资源预加载preload和资源预读取prefetch
 - preload让浏览器提前加载指定资源,需要执行时再执行,可以加速本页面的加载速度
 - prefetch告诉浏览器加载下一页面可能会用到的资源,可以加速下一个页面的加载速度
 - 4、资源懒加载与资源预加载
 - 资源延迟加载也称为懒加载,延迟加载资源或符合某些条件时才加载某些资源
 - 资源预加载是提前加载用户所需的资源,保证良好的用户体验
 - 资源懒加载和资源预加载都是一种错峰操作,在浏览器忙碌的时候不做操作,浏览器空间时,再加载资源,优化了网络性能
 - 【样式设置】
 - 1、避免使用层级较深的选择器,或其他一些复杂的选择器,以提高CSS渲染效率
 - 2、避免使用CSS表达式,CSS表达式是动态设置CSS属性的强大但危险方法,它的问题就在于计算频率很快。不仅仅是在页面显示和缩放时,就是在页面滚动、乃至移动鼠标时都会要重新计算一次
 - 3、元素适当地定义高度或最小高度,否则元素的动态内容载入时,会出现页面元素的晃动或位置,造成回流
 - 4、给图片设置尺寸。如果图片不设置尺寸,首次载入时,占据空间会从0到完全出现,上下左右都可能位移,发生回流
 - 5、不要使用table布局,因为一个小改动可能会造成整个table重新布局。而且table渲染通常要3倍于同等元素时间
 - 6、能够使用CSS实现的效果,尽量使用CSS而不使用JS实现
 - 【渲染层】
 - 1、此外,将需要多次重绘的元素独立为render layer渲染层,如设置absolute,可以减少重绘范围
 - 2、对于一些进行动画的元素,使用硬件渲染,从而避免重绘和回流
 - 【DOM优化】
 - 1、缓存DOM
 - 由于查询DOM比较耗时,在同一个节点无需多次查询的情况下,可以缓存DOM
 - 2、减少DOM深度及DOM数量
 - HTML 中标签元素越多,标签的层级越深,浏览器解析DOM并绘制到浏览器中所花的时间就越长,所以应尽可能保持 DOM 元素简洁和层级较少。
 - 3、批量操作DOM
 - 由于DOM操作比较耗时,且可能会造成回流,因此要避免频繁操作DOM,可以批量操作DOM,先用字符串拼接完毕,再用innerHTML更新DOM
 - 4、批量操作CSS样式
 - 通过切换class或者使用元素的style.csstext属性去批量操作元素样式
 - 5、在内存中操作DOM
 - 使用DocumentFragment对象,让DOM操作发生在内存中,而不是页面上
 - 6、DOM元素离线更新
 - 对DOM进行相关操作时,例、appendChild等都可以使用Document Fragment对象进行离线操作,带元素“组装”完成后再一次插入页面,或者使用display:none 对元素隐藏,在元素“消失”后进行相关操作
 - 7、DOM读写分离
 - 浏览器具有惰性渲染机制,连接多次修改DOM可能只触发浏览器的一次渲染。而如果修改DOM后,立即读取DOM。为了保证读取到正确的DOM值,会触发浏览器的一次渲染。因此,修改DOM的操作要与访问DOM分开进行
 - 8、事件代理
 - 事件代理是指将事件监听器注册在父级元素上,由于子元素的事件会通过事件冒泡的方式向上传播到父节点,因此,可以由父节点的监听函数统一处理多个子元素的事件
 - 利用事件代理,可以减少内存使用,提高性能及降低代码复杂度
 - 9、防抖和节流
 - 使用函数节流(throttle)或函数去抖(debounce),限制某一个方法的频繁触发
 - 10、及时清理环境
 - 及时消除对象引用,清除定时器,清除事件监听器,创建最小作用域变量,可以及时回收内存
 - 1、用对选择器
 - 选择器的性能排序如下所示,尽量选择性能更好的选择器
 - 2、使用requestAnimationFrame来替代setTimeout和setInterval
 - 希望在每一帧刚开始的时候对页面进行更改,目前只有使用 requestAnimationFrame 能够保证这一点。使用 setTimeout 或者 setInterval 来触发更新页面的函数,该函数可能在一帧的中间或者结束的时间点上调用,进而导致该帧后面需要进行的事情没有完成,引发丢帧
 - 3、使用IntersectionObserver来实现图片可视区域的懒加载
 - 传统的做法中,需要使用scroll事件,并调用getBoundingClientRect方法,来实现可视区域的判断,即使使用了函数节流,也会造成页面回流。使用IntersectionObserver,则没有上述问题
 - 4、使用web worker
 - 客户端javascript一个基本的特性是单线程:比如,浏览器无法同时运行两个事件处理程序,它也无法在一个事件处理程序运行的时候触发一个计时器。Web Worker是HTML5提供的一个javascript多线程解决方案,可以将一些大计算量的代码交由web Worker运行,从而避免阻塞用户界面,在执行复杂计算和数据处理时,这个API非常有用
 - 但是,使用一些新的API的同时,也要注意其浏览器兼容性
 - 【打包公共代码】
 - 使用CommonsChunkPlugin插件,将公共模块拆出来,最终合成的文件能够在最开始的时候加载一次,便存到缓存中供后续使用。这会带来速度上的提升,因为浏览器会迅速将公共的代码从缓存中取出来,而不是每次访问一个新页面时,再去加载一个更大的文件
 - webpack 4 将移除 CommonsChunkPlugin, 取而代之的是两个新的配置项 optimization.splitChunks 和 optimization.runtimeChunk
 - 通过设置 optimization.splitChunks.chunks: "all" 来启动默认的代码分割配置项
 - 【动态导入和按需加载】
 - webpack提供了两种技术通过模块的内联函数调用来分离代码,优先选择的方式是,使用符合 ECMAScript 提案 的 import() 语法。第二种,则是使用 webpack 特定的 require.ensure
 - 【剔除无用代码】
 - tree shaking 是一个术语,通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)。它依赖于 ES2015 模块系统中的静态结构特性,例如 import 和 export。这个术语和概念实际上是兴起于 ES2015 模块打包工具 rollup
 - JS的tree shaking主要通过uglifyjs插件来完成,CSS的tree shaking主要通过purify CSS来实现的
 - 【长缓存优化】
 - 1、将hash替换为chunkhash,这样当chunk不变时,缓存依然有效
 - 2、使用Name而不是id
 - 每个 module.id 会基于默认的解析顺序(resolve order)进行增量。也就是说,当解析顺序发生变化,ID 也会随之改变
 - 下面来使用两个插件解决这个问题。第一个插件是 NamedModulesPlugin,将使用模块的路径,而不是数字标识符。虽然此插件有助于在开发过程中输出结果的可读性,然而执行时间会长一些。第二个选择是使用 HashedModuleIdsPlugin,推荐用于生产环境构建
 - 【公用代码内联】
 - 使用html-webpack-inline-chunk-plugin插件将mainfest.js内联到html文件中
 
12. 减少请求数量
41. 减小资源大小
56. 优化网络连接
63.
64.
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. 当前环境?
- 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)
}
}
- 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"]
| 
 10 11 12 13 14 15 16 17 18 19  | 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
  | 
| 
 10 11 12 13 14 15 16 17 18 19  | 
 
 
 
 
 
 
 
 
 
 
 
  | 
总结:父组件通过props向下传递数据给子组件。注:组件中的数据共有三种形式:data、props、computed
2.子组件向父组件传值(通过事件形式)
接下来我们通过一个例子,说明子组件如何向父组件传递值:当我们点击“Vue.js Demo”后,子组件向父组件传递值,文字由原来的“传递的是一个值”变成“子向父组件传值”,实现子组件向父组件值的传递。
| 
 10 11 12 13 14 15 16 17 18 19 20 21  | 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
  | 
| 
 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27  | 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
  | 
 
总结:子组件通过events给父组件发送消息,实际上就是子组件把自己的数据发送到父组件。
方法二、$emit/$on
这种方法通过一个空的Vue实例作为中央事件总线(事件中心),用它来触发事件和监听事件,巧妙而轻量地实现了任何组件间的通信,包括父子、兄弟、跨级。当我们的项目比较大时,可以选择更好的状态管理解决方案vuex。
1.具体实现方式:
| 
 
 
 
  | 
2.举个例子
假设兄弟组件有三个,分别是A、B、C组件,C组件如何获取A或者B组件的数据
| 
 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  | 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
  | 
$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转换:
| 
 
 
  | 
方法四、$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" 传入内部组件 
接下来我们看个跨级通信的例子:
| 
 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27  | 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
  | 
| 
 10 11 12 13 14 15 16 17 18 19 20 21 22 23  | 
 
 
 foo: {{ foo }} childCom1 
 
 
 
 
 
 
 
 
 
 
 
 
  | 
| 
 10 11 12 13 14 15 16 17 18 19 20 21 22 23  | 
 
 boo: {{ boo }} childCom2: {{ $attrs }} 
 
 
 
 
 
 
 
 
 
 
 
 
  | 
| 
 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28  | 
 
 childCom3: {{ $attrs }} 
 
 
 
 
 
 
 childCom3: {{ $attrs }} 
 
 
 
 
 
  | 
如上图所示$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 的子组件
| 
 
 
 
 
 
 
  | 
|
| 
 
 
 
 
 
 
 
  | 
可以看到,在 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不会跟着变(核心代码如下:)
| 
 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  | 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
  | 
| 
 10 11 12 13 14 15 16  | 
 
 
 
 
 
 
 
 
 
 
 
  | 
 
虽说provide 和 inject 主要为高阶插件/组件库提供用例,但如果你能在业务中熟练运用,可以达到事半功倍的效果!
方法六、$parent / $children与 ref
ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例$parent/$children:访问父 / 子实例
需要注意的是:这两种都是直接得到组件实例,使用后可以直接调用组件的方法或访问数据。我们先来看个用 ref来访问组件的例子:
| 
 10 11 12 13  | 
 
 
 
 
 
 
 
 
 
 
 
 
 
  | 
| 
 10 11 12 13  | 
 
 
 
 
 
 
 
 
 
  | 
 
不过,这两种方法的弊端是,无法在跨级或兄弟间通信。
| 
 
  | 
我们想在 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 首屏加载优化
- 单页面应用的一个问题就是首页加载东西过多,加载时间过长。特别在移动端,单页面应用的首屏加载优化更是绕不开的话题。下面我会写出我在项目中做的一些优化,希望大家能够相互讨论,共同进步。
 - 我的项目vue-cli3构建的,vue+vue-router+vuex,UI框架选用 element-ui,ajax方案选用 axios,服务器使用Nginx。用到的这些技术都是现在用的比较广泛的,看到这篇文章,我估计你和我用的技术应该差不多。
 - 首页我们来看看没有经过任何优化的打包分析,vue-cli3的项目直接vue-cli-service build --report就会生成一个report.html,打开这个html就能看到,不是vue-cli3的项目需要自行安装这个插件,参考链接,点击。
 - 如上图所示在vendor比较大的文件有element,moment,echart,还有jquery,然后还有一些没见过的vue-qriously这些组件,接下来我们来一步一步让vendor变小
 - 1. 仔细考虑组件是否需要全局引入
 - 在我们的main.js,我发现有很多组件被全局引入,其中有些组件只有1,2个页面用到,这些组件不需要全部引入
 - 上面一段代码是我们main.js中的代码,其中ImageComponent是用来处理图片的,用到的页面很多,其他的组件都只要较少的页面用到,我们在main.js中删除,移到具体的页面中去。
 - 2. 手动引入 ECharts 各模块
 - 默认引入 ECharts 是引入全部的```import * as ECharts from 'echarts' ```我们只需要部分组件,只需引入自己需要的部分。参考地址,点击
 - 3.使用更轻量级的工具库
 - moment是处理时间的标杆,但是它过于庞大且默认不支持tree-shaking,而且我们的项目中只用到了moment(), format(), add(), subtract()等几个非常简单的方法,有点大材小用,所以我们用 date-fns 来替换它,需要什么方法直接引入就行。
 - 经过上面的三步初步优化,我们可以看到vendor.js变小了很多,去除了moment,我们项目之前echart就是按需加载的。
 - 进过上面的优化,发现 Vue 全家桶以及 ElementUI 仍然占了很大一部分 vendors 体积,这部分代码是不变的,但会随着每次 vendors 打包改变 hash 重新加载。我们可以使用 CDN 剔除这部分不经常变化的公共库。我们将vue,vue-router,vuex,axios,jquery,underscore,使用CDN资源引入。国内的CDN服务推荐使用 BootCDN
 - 1.首先我们在index.html中,添加CDN代码
 - 2.在vue.config.js中加入webpack配置代码,关于webpack配置中的externals,请参考地址
 - 3. 去除vue.use相关代码
 - 需要注意的是,通过 CDN 引入,在使用 VueRouter Vuex ElementUI 的时候要改下写法。CDN会把它们挂载到window上,因此不再使用Vue.use(xxx)
 - 也不在需import Vue from 'vue', import VueRouter from 'vue-router' 等。
 - 剔除全家桶和Element-ui等只有,剩下的需要首次加载 vendors 就很小了。
 - 使用 CDN 的好处有以下几个方面
 - (1)加快打包速度。分离公共库以后,每次重新打包就不会再把这些打包进 vendors 文件中。
 - (2)CDN减轻自己服务器的访问压力,并且能实现资源的并行下载。浏览器对 src 资源的加载是并行的(执行是按照顺序的)。
 - 如下图所示,开启了gzip后js的大小比未开启gzip的js小2/3左右,所以如果没开启gzip,感觉我们做的再多意义也不大,如何看自己的项目有没有开启gzip,如下图所示,开启了gzip,在浏览器的控制台Content-Encoding一栏会显示gzip,否则没有。Nginx如果开启gzip,请自行搜索,或者叫服务端来开启。
 - 路由组件如果不按需加载的话,就会把所有的组件一次性打包到app.js中,导致首次加载内容过多,vue官方文档中也有提到,地址。
 - 上面的两种引入组件的方法都是正确的,都能实现路由的懒加载。
 - 最后我们可以发现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.
256.
257.
258.
259.
260.
261.
262.
263.
264.
265.
266.
267.