1、HTML
1.1、html 行内标签和块级元素
- 行内元素(span,a,img,input,strong)
- 块级元素(div,p,h1...h6,ol,ul,table)
2、CSS
2.1、如何让盒子实现垂直水平居中?
- absolute + 负margin
- absolute + margin auto
- absolute + calc
- absolute + transform
- flex
- css-table
仅居中元素定宽高适用
-
absolute + 负margin
//box1父盒子,box2子盒子 //绝对定位50%,子元素左上角居中,减去子元素自身一半修正位置 .box1 { position: relative; } .box2 { position: absolute;; top: 50%; left: 50%; margin-left: -50px; margin-top: -50px; } -
absolute + margin auto
//所有方向距离为0,margin设置auto .box1 { position: relative; } .box2 { position: absolute;; top: 0; left: 0; right: 0; bottom: 0; margin: auto; } -
absolute + calc(calc,css3计算)
//css3计算属性,基于父元素移动50%,在减去自身宽高的一半 .box1 { position: relative; } .box2 { position: absolute;; top: calc(50% - 50px); left: calc(50% - 50px); }
居中元素不定宽高
-
absolute + transform
//基于父元素移动50%,通过transform:translate:(50%, 50%)修正位置 .box1 { position: relative; } .box2 { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); } -
flex
.box1 { display: flex; justify-content: center; align-items: center; } -
css-table
//css新增的table属性,可以让我们把普通元素,变为table元素的现实效果 .box1{ display: table-cell; text-align: center; vertical-align: middle; } .box2 { display: inline-block; }
2.2、圣杯布局、双飞翼?
最终效果类似,left 和 right 的宽度是固定的,center宽度自适应
圣杯布局
-
content 设置一个padding:0 200px,将左右两边各腾出200px宽度。
-
left设置margin:-100%,right设置margin;-200px
-
最后通过绝对定位,移动到合适位置,left:-200px;left:200px
#content{ overflow: hidden; padding: 0 200px;//给两边盒子留位置 } #left{ margin-left:-100% ;//向左移动父元素100%距离,和父元素左边对齐 position: relative; left:-200px; } #right{ margin-left: -200px;//向左移动自身宽度距离,和父元素右边对其 position: relative; left:200px; }
2.3、BFC
BFC概念
也叫【块级格式化上下文】,简单来说,BFC 是一个完全独立的空间(布局环境),让空间里的子元素不会影响到外面的布局。
怎样触发BFC
- float:left / right
- position:absoulte / fixed
- display: inline-block / table-cell / flex
- overflow: hidden / auto / scroll
BFC解决了什么问题
- 阻止
margin重叠 - 可以包含浮动元素 —— 清除内部浮动
- 自适应两栏布局
- 可以阻止元素被浮动元素覆盖
2.4、优先级
!important > 内联 > ID选择器 > 类选择器 > 标签选择器。
- 每个选择器都有权值,权值越大越优先
- 继承的样式优先级低于自身指定样式
- 权值相同时,靠近元素的样式优先级高 顺序为内联样式表(标签内部)> 内部样式表(当前文件中)> 外部样式表(外部文件中)
2.5、盒子模型
- IE盒模型:属性
width,height包含content、border和padding,指的是content + padding + border - 标准盒模型:属性
width,height只包含内容content,不包含border和padding。
切换盒子模型
//W3C盒子模型
box-sizing: content-box
//IE盒子模型
box-sizing: border-box
3、HTML5
3.1、语义化标签
| 标签 | 描述 |
|---|---|
| header | 页眉 |
| footer | 页脚 |
| nav | 导航链接 |
| main | 主内容 |
| article | 文章 |
| dialog | 对话框(弹窗) |
语义化的优点有:
- 代码结构清晰,易于阅读,利于开发和维护
- 方便其他设备解析(如屏幕阅读器)根据语义渲染网页。
- 有利于搜索引擎优化(SEO)
3.2、本地储存
Cookie / sessionStorage / localStorage
共同点:
- 都是保存在浏览器端,且同源的
区别:
-
cookie数据始终在同源的http请求中携带,即cookie在浏览器和服务器间来回传递。而sessionStorage和localStorage不会自动把数据发给服务器,仅在本地保存。
-
cookie数据不能超过4k(适合保存小数据)。 sessionStorage和localStorage容量较大
-
数据有效期也不同
- sessionStorage:仅在当前浏览器窗口关闭前有效
- localStorage:始终有效,窗口或浏览器关闭也一直保存,需手动清除
- cookie只在设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭
-
作用域也不同
- sessionStorage 不在不同的浏览器窗口中共享
- localStorage 在所有同源窗口中都是共享的;
- cookie 也是在所有同源窗口中都是共享的。
应用场景
- localStorage:常用于长期登录(+判断用户是否已登录),适合长期保存在本地的数据
- sessionStorage :敏感账号一次性登录
- cookies与服务器交互
4、CSS3
4.1、CSS3的新特性
| css3新特性 | 描述 |
|---|---|
| box-shadow | 盒子阴影 |
| border-radius | 圆角 |
| box-sizing | 盒子模型 |
| linear-gradient, radial-gradient | 渐变 |
| transition | 过渡 |
| animate | 动画 |
| transform | 2D 转换/3D 转换 |
| iconfont | 字体图标 |
| flex | 弹性布局 |
4.2、flex常见属性
| flex常见属性 | 描述 |
|---|---|
| * flex-direction | 设置容器主轴的方向 |
| * flex-wrap | 用于设置容器盛不下,是否换行(不 / 向上 / 向下) |
| * justify-content | 用于设置容器中的内容对齐方式 |
| * align-items | 用于设置容器整体对齐方式 |
| align-content | 定义了多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用 |
| flew-flow | 是flex-deriction和flex-wrap属性的简写,默认值为[row nowrap] |
5、JS
5.1、var let const 的区别?
- const 定义常量,不可以重复赋值
- var 有变量提升,可以重复声明。let 不存在变量提升,不可以重复声明
- var 全局作用域或块级作用域,let const 块级作用域
5.2、javascript 有哪些基础数据类型?
-
基础数据类型Undefined、Null、String、Number、Boolean、Symbol、BigInt
-
Symbol、BigInt是ES6之后新增的
- Symbol 指的是独一无二的值
- BigInt 是一种数字类型的数据,它可以表示任意精度格式的整数
5.3、判断数据类型的方法有哪些,有什么区别
| 判断数据类型的方法 | 描述 |
|---|---|
| typeof | 判断基础数据类型(数组、对象、null都会被判断为object) |
| instanceof | 判断引用数据类型,不能判断基本数据类型 |
| constructor | 判断数据的类型 |
| Object.prototype.toString.call() | 使用 Object 对象的原型方法 toString 来判断数据类型 |
5.4、== 与 === 的区别?
- 双等号(==)进行相等判断时,如果两边的类型不一致,会强制类型转化再进行比较。
- 三等号(===)进行相等判断时,如果两边的类型不一致,不强制类型准换,直接返回 false。
5.5、js中那些数据在 if 判断时是 false
- 0、false、null、undefined、“”、NaN 判断为false
- 其他皆为true
5.6、call、apply、bind
- call() 和apply()的第一个参数相同,就是指定的对象。这个对象就是该函数的执行上下文。
- call()和apply()的区别就在于,两者之间的参数。
- call()在第一个参数之后的 后续所有参数就是传入该函数的值。
- apply() 只有两个参数,第一个是对象,第二个是数组,这个数组就是该函数的参数。 bind() 方法和前两者不同在于: bind() 方法会返回执行上下文被改变的函数而不会立即执行,而前两者是 直接执行该函数。他的参数和call()相同。
5.7、原型、原型链?
原型:
所有对象,都有一个隐式引用,它被称之为这个对象的 prototype 原型。当访问对对象的某个属性时,会先在对象本身的属性上寻找,没找到就会去他的原型对象上寻找。
原型链
当访问一个对象的属性时,如果这个对象内部不存在这个属性,那么它就会去它的原型对象里找这个属性,这个原型对象又会有自己的原型,于是就这样一直找下去,也就是原型链的概念。原型链的尽头一般来说都是 Object.prototype 所以这就是新建的对象为什么能够使用 toString() 等方法的原因。
5.8、闭包、闭包的使用场景
闭包的概念:
闭包简单理解就是内嵌函数,也即在函数中嵌套函数。
作用:函数内部调用外部变量、构造函数的私有属性、延长变量生命周期
闭包的优缺点
- 优点:延长变量生命周期、私有化变量
- 缺点:过多的闭包可能会导致内存泄漏
闭包的应用场景
- ajax请求的成功回调
- 事件绑定的回调方法
- setTimeout的延时回调
- 函数内部返回另一个匿名函数
- 函数节流、防抖
5.9、箭头函数和普通函数的区别
- 箭头函数比普通函数更加简洁
- 箭头函数没有自己的this
- 箭头函数继承来的this指向永远不会改变
- 箭头函数不能作为构造函数使用
- 箭头函数没有自己的arguments
- 箭头函数没有prototype
- call()、apply()、bind()等方法不能改变箭头函数中this的指向
5.10、防抖和节流
- 防抖:在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时
- 节流:规定在一个单位时间内,只能触发一次函数
防抖应用场景
- 搜索框搜索输入。只需用户最后一次输入完,再发送请求
- 手机号、邮箱验证输入检测 onchange / oninput事件
- 窗口大小计算。只需窗口调整完成后,计算窗口大小。防止重复渲染。
节流应用场景
- 懒加载、滚动加载、加载更多或监听滚动条位置;
- 百度搜索框,搜索联想功能;
- 防止高频点击提交,防止表单重复提交;
5.11、回流和重绘
回流(重排)
-
定义:
- 当
DOM的变化影响了元素的几何信息(DOM对象的位置和尺寸大小),浏览器需要重新计算元素的几何属性,将其安放在界面中的正确位置,这个过程叫做回流(重排)。
- 当
-
触发:
- 页面初次渲染
- 浏览器窗口大小改变
- 元素尺寸、位置、内容发生改变
- 元素字体大小变化
- 添加或者删除可见的 dom 元素
重绘
-
定义:
- 当一个元素的外观发生改变,但没有改变布局,重新把元素外观绘制出来的过程,叫做重绘
-
触发:
- 改变元素的
color、background、box-shadow等属性
- 改变元素的
回流(重排)优化建议
- 分离读写操作
- 样式集中修改
- 缓存需要修改的
DOM元素 - 尽量只修改
position:absolute或fixed元素,对其他元素影响不大 - 动画开始
GPU加速,translate使用3D变化
5.12、JS垃圾回收
有两种垃圾回收策略:
- 标记清除: 标记阶段即为所有活动对象做上标记,清除阶段则把没有标记(也就是非活动对象)销毁。
- 引用计数: 它把对象是否不再需要简化定义为对象有没有其他对象引用到它。如果没有引用指向该对象(引用计数为 0),对象将被垃圾回收机制回收。
5.13、事件循环Eventloop(宏任务、微任务)(笔试题)
Event Loop概念
即事件循环,是指浏览器的一种解决javaScript单线程运行时不会阻塞的一种机制,也就是我们经常使用异步的原理。
在一次事件循环中,会先执行js线程的主任务,然后会去查找是否有微任务,如果有那就优先执行微任务,如果没有,在去查找宏任务进行执行。
微任务
- process.nextTick (Node)
- promise
- Object.observe
- MutationObserver (浏览器)
宏任务
- script
- setTimeout
- setInterval
- setImmediate
- 回调函数(事件、ajax等)
5.14、深拷贝、浅拷贝(笔试题)
-
浅拷贝: 以赋值的形式拷贝引用对象,仍指向同一个地址,修改时原对象也会受到影响
Object.assign,- 展开运算符
...
-
深拷贝: 完全拷贝一个新对象,修改时原对象不再受到任何影响
-
JSON.parse(JSON.stringify(obj)): 性能最快
- 具有循环引用的对象时,报错
- 当值为函数、
undefined、或symbol时,无法拷贝
-
递归进行逐一赋值
-
lodash工具库:cloneDeep
-
5.15、数组的方法
| 数组的方法 | 描述 |
|---|---|
| map | 遍历数组,返回回调返回值组成的新数组 |
| forEach | 无法break,可以用try/catch中throw new Error来停止 |
| filter | 过滤 |
| some | 有一项返回true,则整体为`true |
| every | 有一项返回false,则整体为`false |
| join | 通过指定连接符生成字符串 |
| push / pop | 末尾推入和弹出,改变原数组。push 返回数组长度, pop 返回原数组最后一项; |
| unshift / shift | 头部推入和弹出,改变原数组,unshift 返回数组长度,shift 返回原数组第一项 ; |
| sort(fn) / reverse | 排序与反转,改变原数组 |
| concat | 连接数组,不影响原数组, 浅拷贝 |
| slice(start, end) | 返回截断后的新数组,不改变原数组 |
| splice(start, number, value...) | 返回删除元素组成的数组,value 为插入项,改变原数组 |
| indexOf / lastIndexOf(value, fromIndex) | 查找数组项,返回对应的下标 |
| reduce / reduceRight(fn(prev, cur), defaultPrev) | 两两执行,prev 为上次化简函数的return值,cur 为当前值 当传入 defaultPrev 时,从第一项开始; 当未传入时,则为第二项 |
数组乱序
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
arr.sort(function () {
return Math.random() - 0.5;
});
//输出结果:[4, 2, 10, 6, 9, 5, 1, 3, 8, 7]
//通过sort排序,用随机数和0.5比较大小,实现伪随机排序
5.16、promise、手写promise
function Promise(fn) {
// Promise resolve时的回调函数集
this.cbs = [];
// 传递给Promise处理函数的resolve
// 这里直接往实例上挂个data
// 然后把onResolvedCallback数组里的函数依次执行一遍就可以
const resolve = (value) => {
// 注意promise的then函数需要异步执行
setTimeout(() => {
this.data = value;
this.cbs.forEach((cb) => cb(value));
});
}
// 执行用户传入的函数
// 并且把resolve方法交给用户执行
fn(resolve);
}
Promise.prototype.then = function (onResolved) {
// 这里叫做promise2
return new Promise((resolve) => {
this.cbs.push(() => {
const res = onResolved(this.data);
if (res instanceof Promise) {
// resolve的权力被交给了user promise
res.then(resolve);
} else {
// 如果是普通值 就直接resolve
// 依次执行cbs里的函数 并且把值传递给cbs
resolve(res);
}
});
});
};
5.17、ES6新特性
- let const
- 块级作用域
- 箭头函数
- class
- 模板字符串
- 展开运算符
- 结构赋值
- 新增的数组API和对象API
- promise
- proxy
- Symbol、bigInt
- Set、Map/WeapMap
5.18、跨域、同源策略
同源策略: 是一种约定,它是浏览器最核心也最基本的安全功能,所谓同源是指:域名、协议、端口相同。
跨域: 是基于同源策略延伸出来的一种概念,所谓跨域是指:域名、协议、端口不一样,跨域只存在浏览器中
解决跨域
jsonp(利用script标签没有跨域限制的漏洞实现。缺点:只支持GET请求)CORS(设置Access-Control-Allow-Origin:指定可访问资源的域名)postMessage()(HTML5新增API ),通过onmessage监听 传递过来的数据Websocket是HTML5的一个持久化的协议,也是跨域的一种解决方案。proxy反向代理
日常工作中用的最对的跨域方案是CORS和proxy反向代理。
5.19、输入url地址到页面展示发生了什么?
- 浏览器的地址栏输入URL并按下回车。
- 浏览器查找当前URL是否存在缓存,并比较缓存是否过期。
- DNS解析URL对应的IP。
- 根据IP建立TCP连接(三次握手)。
- HTTP发起请求。
- 服务器处理请求,浏览器接收HTTP响应。
- 渲染页面,构建DOM树。
- 关闭TCP连接(四次挥手)
5.20、状态码
| 状态码 | 状态 | 类型 | 描述 | 例子 |
|---|---|---|---|---|
| 1xx | Informational | 信息状态码 | 接受请求正在处理 | |
| 2xx | Success | 成功状态码 | 请求正常处理完毕 | 200响应成功 |
| 3xx | Redirection | 重定向状态码 | 需要附加操作已完成请求 | 301永久重定向 302临时重定向 |
| 4xx | Client Error | 客户端错误状态码 | 服务器无法处理请求 | 403服务器禁止访问 404服务器资源未找到 |
| 5xx | Server Error | 服务器错误状态码 | 服务器处理请求出错 | 500 502服务器内部错误 504 服务器繁忙 |
5.21、ES6新特性
- let const
- 字符串、数组、对象的方法扩展
- symbol、set、map新的数据类型和数据结构
- proxy代理拦截
- 异步解决方案:promise、generate,async、await
- class类
- module模块
5.22、异步编程的实现方式?
JavaScript中的异步机制可以分为以下几种:
- 回调函数 的方式,使用回调函数的方式有一个缺点是,多个回调函数嵌套的时候会造成回调函数地狱,上下两层的回调函数间的代码耦合度太高,不利于代码的可维护。
Promise 的方式,使用 Promise 的方式可以将嵌套的回调函数作为链式调用。但是使用这种方法,有时会造成多个 then 的链式调用,可能会造成代码的语义不够明确。
generator 的方式,它可以在函数的执行过程中,将函数的执行权转移出去,在函数外部还可以将执行权转移回来。当遇到异步函数执行的时候,将函数执行权转移出去,当异步函数执行完毕时再将执行权给转移回来。因此在 generator 内部对于异步操作的方式,可以以同步的顺序来书写。使用这种方式需要考虑的问题是何时将函数的控制权转移回来,因此需要有一个自动执行 generator 的机制,比如说 co 模块等方式来实现 generator 的自动执行。
async 函数 的方式,async 函数是 generator 和 promise 实现的一个自动执行的语法糖,它内部自带执行器,当函数内部执行到一个 await 语句的时候,如果语句返回一个 promise 对象,那么函数将会等待 promise 对象的状态变为 resolve 后再继续向下执行。因此可以将异步逻辑,转化为同步的顺序来书写,并且这个函数可以自动执行。
6、VUE
6.1、双向数据绑定原理?
Vue的双向数据绑定是由数据劫持结合发布者订阅者实现的。 数据劫持是通过Object.defineProperty()来劫持对象数据的setter和getter操作。 在数据变动时作你想做的事
- 原理 通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,
- 达到数据变化->视图更新 在初始化vue实例时,遍历data这个对象,给每一个键值对利用Object.definedProperty对data的键值对新增get和set方法,利用了事件监听DOM的机制,让视图去改变数据
6.2、vue生命周期
| 生命周期 | 描述 |
|---|---|
| beforeCreate | 初始化之前(数据拿不到) |
| created | 初始化之后(数据可以拿到) |
| beforeMount | 挂载之前(页面的元素看不到) |
| mounted | 挂载之后(页面元素可以看到) |
| beforeUpdate | 更新前(数据更新,页面未更新) |
| updated | 更新后(数据更新,页面更新) |
| beforeDestory | 销毁前 |
| destoryed | 销毁后 |
常用的生命钩子
- mounted(): 发送ajax请求, 启动定时器等异步任务
- beforeDestory(): 做收尾工作, 如: 清除定时器
6.3、data为什么是一个函数?
- 组件中的 data 写成一个函数,数据以函数返回值形式定义,这样每复用一次组件,就会返回一份新的 data,类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据。
- 而单纯的写成对象形式,就使得所有组件实例共用了一份 data,就会造成一个变了全都会变的结果
6.4、computed、watch的区别
- computed 是计算属性,依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值;
- watch 更多的是「观察」的作用,类似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操作;
应用场景
- 当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算;
- 当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
6.5、组件通信的几种方式
- props 和 emit 触发事件来做到
- eventBus ---- 兄弟组件数据传递 这种情况下可以使用事件总线的方式
- v-model 和 sync修饰符 ---- 实现父子组件双向数据同步(本质上还是自定义事件和props组合)
- children ---- 获取当前组件的父组件和当前组件的子组件
- listeners ---- 就是父组件中给子组件传递的,所有属性组成的对象(排除props声明接收的属性 以及class style),及自定义事件方法组成的对象
- $refs ---- 可以直接操作子组件内部的数据及方法
- 作用域插槽 ---- 父向子传数据,子组件展示数据,数据的结构和样式由父组件决定
- vuex 状态管理
- PubSubJS ---- 消息的订阅与发布
6.6、v-if、v-show的区别
- v-if 在编译过程中会被转化成三元表达式,条件不满足时不渲染此节点。
- v-show 会被编译成指令,条件不满足时控制样式将对应节点隐藏 (display:none)
使用场景
- v-if 适用于在运行时很少改变条件,不需要频繁切换条件的场景
- v-show 适用于需要非常频繁切换条件的场景
6.7、v-model的实现原理
我们在 vue 项目中主要使用 v-model 指令在表单 input、textarea、select 等元素上创建双向数据绑定,我们知道 v-model 本质上不过是语法糖,v-model 在内部为不同的输入元素使用不同的属性并抛出不同的事件:
- text 和 textarea 元素使用 value property 和 input 事件;
- checkbox 和 radio 使用 checked property 和 change 事件;
- select 字段将 value 作为 prop 并将 change 作为事件。
6.8、nextTick(了解)
在下次dom更新循环结束之后执行延迟回调,可用于获取更新后的dom状态
6.9、为什么v-for和v-if不建议用在一起
- 当 v-for 和 v-if 处于同一个节点时,v-for 的优先级比 v-if 更高,这意味着 v-if 将分别重复运行于每个 v-for 循环中。如果要遍历的数组很大,而真正要展示的数据很少时,这将造成很大的性能浪费(Vue2.x)
- 这种场景建议使用 computed,先对数据进行过滤
注意:3.x 版本中
v-if总是优先于v-for生效。
6.10、v-for中key的作用
key 是为 Vue 中 vnode 的唯一标记,通过这个 key,我们的 diff 操作可以更准确、更快速。
- 更准确:因为带 key 就不是就地复用了,在 sameNode 函数 a.key === b.key 对比中可以避免就地复用的情况。所以会更加准确。
- 更快速:利用 key 的唯一性生成 map 对象来获取对应节点,比遍历方式更快
6.11、diff算法
-
Diff 算法是同级比较,内部是深度优先遍历节点
-
判断节点是否同一个元素,
- 如果是对比属性对比孩子节点,
- 如果不是直接删除老的替换新的。
-
Vue2采用了双指针对一些场景做了优化策略(静态节点可以直接掉过diff)对头头,尾尾,头尾,尾头进行了优化
-
最后根据老节点创造了一个映射表,用新的去里面找能复用就复用
6.12、vm.$set()
- 由于 Vue 会在初始化实例时对属性执行 getter/setter 转化,所以属性必须在 data 对象上存在才能让 Vue 将它转换为响应式的。
- 但是 Vue 提供了 Vue.set / vm.$set 来实现为对象添加响应式属性
如何实现的
- 如果目标是数组,直接使用数组的 splice 方法触发相应式;
- 如果目标是对象,会先判读属性是否存在、对象是否是响应式,最终如果要对属性进行响应式处理,则是通过调用 defineReactive 方法进行响应式处理
6.13、父子级生命周期的调用顺序
Vue 的父组件和子组件生命周期钩子函数执行顺序可以归类为以下 4 部分:
加载渲染过程
- 父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted
子组件更新过程
- 父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated
父组件更新过程
- 父 beforeUpdate -> 父 updated
销毁过程
- 父beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed
小技巧:父组件先开始,子组件先完成
6.14、路由hash、history区别和原理
hash模式: 灵活运用了html的瞄点功能、改变#后的路径本质上是更换了当前页面的瞄点,所以不会刷新页面。
history模式: 是使用了 H5 提供的pushState() 和 replaceState(),允许开发者直接更改前端路由,即更新浏览器 URL 地址而不重新发起请求(将url替换并且不刷新页面)。
两者区别
- 外观:hash的url有个#符号,history没有,history外观更好看。
- 刷新:hash刷新会加载到地址栏对应的页面,history刷新浏览器会重新发起请求,如果服务端没有匹配当前的url,就会出现404页面。
- 兼容性:hash能兼容到IE8,history只能兼容到IE10。
- 服务端支持:hash(#及后面的内容)的变化不会导致浏览器向服务器发起请求;history刷新会重新向服务器发起请求,需要在服务端做处理:如果没有匹配到资源,就返回同一个html页面。
- 原理:hash通过监听浏览器的onhashchange()事件,查找对应的路由规则;history利用H5新增的pushState()和replaceState()方法改变url。
- 记录:hash模式只有#后面的内容被修改才会添加新的记录栈;history通过pushState()设置的url于当前url一模一样也会被记录到历史记录栈。
6.15、路由钩子函数
路由钩子函数也叫导航守卫,分为【全局守卫】、【路由独享守卫】、【组件内的守卫】
全局守卫
- beforeEach 全局前置守卫 进入路由之前
- beforeResolve 全局解析守卫
- afterEach 全局后置钩子 进入路由之后
| 参数 | 描述 |
|---|---|
| to | 将要进入的路由对象 |
| from | 将要离开的路由对象 |
| next | 进入 / 取消 / 跳转新路由 |
路由独享守卫
可以直接在某个路由配置上定义 beforeEnter 守卫,只在进入路由时触发
组件内的守卫
- beforeRouteEnter 进入路由前
- beforeRouteUpdate (2.2) 路由复用同一个组件时
- beforeRouteLeave 离开当前路由时
6.16、keep-alive、钩子、includes
keep-alive 是 Vue 内置的一个组件,可以实现组件缓存,当组件切换时不会对当前组件进行卸载。
-
配置属性
include只有名称匹配的组件会被缓存exclude任何名称匹配的组件都不会被缓存
-
钩子函数
activated组件渲染后调用deactivated组件销毁后调用
6.17、vue单向数据流
- 所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。
- 子组件想修改时,只能通过 $emit 派发一个自定义事件,父组件接收到后,由父组件修改。
6.18、虚拟DOM优缺点
虚拟 DOM概念
由于在浏览器中操作 DOM 是很昂贵的。频繁的操作 DOM,会产⽣⼀定的性能问题,所以在vue中将真实的DOM节点抽离成⼀个虚拟的DOM树,这个虚拟的DOM树就是虚拟DOM。
优点:
- 保证性能下限: 需要适配任何上层 API 可能产生的操作,所以它的性能并不是最优的,但是比直接操作 DOM 性能要好很多。
- 无需手动操作 DOM: 我们不再需要手动去操作 DOM,只需要写好 View-Model 的代码逻辑,框架会根据虚拟 DOM 和 数据双向绑定,帮我们以可预期的方式更新视图,极大提高我们的开发效率
- 跨平台: 虚拟 DOM 本质上是 JavaScript 对象,而 DOM 与平台强相关,相比之下虚拟 DOM 可以进行更方便地跨平台操作,例如服务器渲染、weex 开发等等。
缺点:
- 无法进行极致优化: 虽然虚拟 DOM + 合理的优化,足以应对绝大部分应用的性能需求,但在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化。
- 首次渲染大量 DOM 时,由于多了一层虚拟 DOM 的计算,会比 innerHTML 插入慢。
6.19、路由传参
-
Params
- 只能使用name,不能使用path
- 参数不会显示在路径上
- 浏览器强制刷新参数会被清空,
-
Query
- 参数会显示在路径上,刷新不会被清空
- name 可以使用path路径
6.20、什么是vuex
vuex 是专门为 vue 提供的全局状态管理系统,用于多个组件中数据共享、数据缓存等。(无法持久化、内部核心原理是通过创造一个全局实例 new Vue)
6.21、vuex的几大核心
- State:定义了应用状态的数据结构,可以在这里设置默认的初始状态。
- Getter:允许组件从 Store 中获取数据,mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性。
- Mutation:是唯一更改 store 中状态的方法,且必须是同步函数。
- Action:用于提交 mutation,而不是直接变更状态,可以包含任意异步操作。
- Module:允许将单一的 Store 拆分为多个 store 且同时保存在单一的状态树中。
6.22、vuex同步与异步提交
- Mutation:同步提交
- Action:异步提交
6.23、vue项目优化
(1)代码层面的优化
- v-if 和 v-show 区分使用场景
- computed 和 watch 区分使用场景
- v-for 遍历必须为 item 添加 key,且避免同时使用 v-if
- 长列表性能优化
- 事件的销毁
- 图片资源懒加载
- 路由懒加载
- 第三方插件的按需引入
- 优化无限列表性能
- 服务端渲染 SSR or 预渲染
(2)Webpack 层面的优化
- Webpack 对图片进行压缩
- 减少 ES6 转为 ES5 的冗余代码
- 提取公共代码
- 模板预编译
- 提取组件的 CSS
- 优化 SourceMap
- 构建结果输出分析
- Vue 项目的编译优化
(3)基础的 Web 技术的优化
- 开启 gzip 压缩
- 浏览器缓存
- CDN 的使用
- 使用 Chrome Performance 查找性能瓶颈
7、项目相关问题
7.1、说一下前端登录的流程?
- 初次登录的时候,前端调后调的登录接口,发送用户名和密码,后端收到请求,验证用户名和密码,验证成功,就给前端返回一个token,和一个用户信息的值,
- 前端拿到token,将token储存到Vuex中,然后从Vuex中把token的值存入浏览器Cookies中。
- 把用户信息存到Vuex然后再存储到LocalStroage中,然后跳转到下一个页面,
- 根据后端接口的要求,只要不登录就不能访问的页面需要在前端每次跳转页面师判断Cookies中是否有token,没有就跳转到登录页,有就跳转到相应的页面,
- 我们应该再每次发送post/get请求的时候应该加入token,常用方法再项目utils/service.js中添加全局拦截器,将token的值放入请求头中 后端判断请求头中有无token,有token,就拿到token并验证token是否过期,在这里过期会返回无效的token然后有个跳回登录页面重新登录并且清除本地用户的信息