一 css(html)篇
-
页面导入样式时,使用 link 和 @import 有什么区别。
-
从属关系区别。@import 只能导入样式表,link 还可以定义 RSS、rel 连接属性、引入网站图标等
-
加载顺序区别;加载页面时,link 标签引入的 CSS 被同时加载;@import 引入的 CSS 将在页面加载完毕后被加载;
-
兼容性区别
-
简述浏览器的渲染原理
-
首先解析收到的文档,根据文档定义构建一颗 DOM 树,DOM 树是由 DOM 元素及属性节点组成的;
-
然后对 CSS 进行解析,生成 CSSOM 规则树;
-
根据 DOM 树和 CSSOM 规则树构建 Render Tree。渲染树的节点被称为渲染对象,渲染对象是一个包含有颜色和大小等属性的矩形,渲染对象和 DOM 对象相对应,但这种对应关系不是一对一的,不可见的 DOM 元素不会被插入渲染树。
-
当渲染对象被创建并添加到树中,它们并没有位置和大小,所以当浏览器生成渲染树以后,就会根据渲染树来进行布局(也可以叫做回流)。这一阶段浏览器要做的事情就是要弄清楚各个节点在页面中的确切位置和大小。通常这一行为也被称为“自动重排”。
-
布局阶段结束后是绘制阶段,比那里渲染树并调用对象的 paint 方法将它们的内容显示在屏幕上,绘制使用 UI 基础组件。
为了更好的用户体验,渲染引擎会尽可能早的将内容呈现到屏幕上,并不会等到所有的 html 解析完成之后再去构建和布局 render tree。它是解析完一部分内容就显示一部分内容,同时可能还在网络下载其余内容。
-
如何实现浏览器内多个标签页之间的通信?
实现多个标签页之间的通信,本质上都是通过中介者模式来实现的。因为标签页之间没有办法直接通信,因此我们可以找一个中介者来让标签页和中介者进行通信,然后让这个中介者来进行消息的转发。
-
使用 Websocket,通信的标签页连接同一个服务器,发送消息到服务器后,服务器推送消息给所有连接的客户端;
-
可以地调用 localStorage,localStorage 在另一个浏览上下文里被添加、修改或删除时,它都会触发一个 storage 事件,我们可以通过监听 storage 事件,控制它的值来进行页面信息通信;
-
如果我们能够获得对应标签页的引用,通过 postMessage 方法也是可以实现多个标签页通信的;
-
怎么让一个 div 水平垂直居中?
<div class="parent">
<div class="child"></div>
</div>
div.parent {
position: relative;
}
div.child {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
div.parent {
display: grid;
}
div.child {
justify-self: center;
align-self: center;
}
div.parent {
font-size: 0;
text-align: center;
&::before {
content: "";
display: inline-block;
width: 0;
height: 100%;
vertical-align: middle;
}
}
div.child {
display: inline-block;
vertical-align: middle;
}
-
分析比较 opacity: 0、visibility: hidden、display: none 优劣和适用场景?
-
display: none - 不占空间,不能点击,会引起回流,子元素不影响;
-
visibility: hidden - 占据空间,不能点击,引起重绘,子元素可设置 visible 进行显示
-
opacity: 0 - 占据空间,可以点击,引起重绘,子元素不影响
-
分别说一下定位有哪些属性及应用场景?
-
static - HTML 元素的默认值,即没有定位,遵循正常的文档流对象。
-
fixed - 元素的位置相对于浏览器窗口是固定位置,使元素的位置与文档流无关,因此不占据空间,元素和其他元素重叠。。
-
relative - 相对定位元素的定位是相对其正常位置,移动相对定位元素,但它原本所占的空间不会改变,相对定位元素经常被用来作为绝对定位元素的容器块。。
-
absolute - 绝对定位的元素的位置相对于最近的已定位父元素,如果元素没有已定位的父元素,那么它的位置相对于 html 标签,定位使元素的位置与文档流无关,因此不占据空间,元素和其他元素重叠:
-
sticky - 英文字面意思是粘,粘贴,所以可以把它称之为粘性定位,基于用户的滚动位置来定位,粘性定位的元素是依赖于用户的滚动,在 position:relative 与 position:fixed 定位之间切换。它的行为就像 position:relative; 而当页面滚动超出目标区域时,它的表现就像 position:fixed;,它会固定在目标位置。元素定位表现为在跨越特定阈值前为相对定位,之后为固定定位。这个特定阈值指的是 top, right, bottom 或 left 之一,换言之,指定 top, right, bottom 或 left 四个阈值其中之一,才可使粘性定位生效。否则其行为与相对定位相同。
二 JavaScript 篇
-
什么是防抖和节流?有什么区别?如何实现?
-
防抖 - 触发高频事件后 n 秒内函数只会执行一次,如果 n 秒内高频事件再次被触发,则重新计算时间。
function debounce(fn, timing) {
let timer;
return function() {
clearTimeout(timer);
timer = setTimeout(() => {
fn();
}, timing);
}
}
-
节流 - 高频事件触发,但在 n 秒内只会执行一次,所以节流会稀释函数的执行效率。
function throttle(fn, timing) {
let trigger;
return function() {
if (trigger) return;
trigger = true;
fn();
setTimeout(() => {
trigger = false;
}, timing);
}
}
Tips:我记这个很容易把两者弄混,总结了个口诀,就是 DTTV(Debounce Timer Throttle Variable - 防抖靠定时器控制,节流靠变量控制)。
-
介绍下 Set、Map、WeakSet 和 WeakMap 的区别
-
Set
-
成员不能重复;
-
只有键值,没有键名,有点类似数组;
-
可以遍历,方法有 add、delete、has;
-
-
WeakSet
-
成员都是对象(引用);
-
成员都是弱引用,随时可以消失(不计入垃圾回收机制)。可以用来保存 DOM 节点,不容易造成内存泄露;
-
不能遍历,方法有 add、delete、has;
-
-
Map
-
本质上是键值对的集合,类似集合;
-
可以遍历,方法很多,可以跟各种数据格式转换
-
-
WeakMap
-
只接收对象为键名(null 除外),不接受其他类型的值作为键名;
-
键名指向的对象,不计入垃圾回收机制
-
不能遍历,方法同 get、set、has、delete
-
-
ES5/ES6 的继承除了写法以外还有什么区别?
-
class 声明会提升,但不会初始化赋值。(类似于 let、const 声明变量;
-
class 声明内部会启用严格模式;
-
class 的所有方法(包括静态方法和实例方法)都是不可枚举的;
-
class 的所有方法(包括静态方法和实例方法)都没有原型对象 prototype,所以也没有 [[constructor]],不能使用 new 来调用;
-
必须使用 new 来调用 class;
-
class 内部无法重写类名;
-
call 和 apply 的区别是什么,哪个性能更好一些?
-
Function.prototype.apply 和 Function.prototype.call 的作用是一样的,区别在于传入参数的不同;
-
第一个参数都是指定函数体内 this 的指向;
-
第二个参数开始不同,apply 是传入带下标的集合,数组或者类数组,apply 把它传给函数作为参数,call 从第二个开始传入的参数是不固定的,都会传给函数作为参数;
-
call 比 apply 的性能要好,call 传入参数的格式正式内部所需要的格式;
-
谈谈对 MVC、MVP、MVVM 模式的理解?
-
MVC
-
MVC 除了把应用程序分为 View、Model 层,还额外的加了一个 Controller 层,它的职责是进行 Model 和 View 之间的协作(路由、输入预处理等)的应由逻辑(application logic);Model 进行处理业务逻辑。
-
-
用户对 View 操作以后,View 捕获到这个操作,会把处理的权利交移给 Controller(Pass calls);Controller 会对来自 View 数据进行预处理、决定调用哪个 Model 的接口;然后由 Model 执行相关的业务逻辑;当 Model 变更了以后,会通过观察者模式(Observer Pattern)通知 View;View 通过观察者模式收到 Model 变更的消息以后,会向 Model 请求最新的数据,然后重新更新界面。
-
MVP
-
和 MVC 模式一样,用户对 View 的操作都会从 View 交易给 Presenter。Presenter 会执行相应的应用程序逻辑,并且会对 Model 进行相应的操作;而这时候 Model 执行业务逻辑以后,也是通过观察者模式把自己变更的消息传递出去,但是是传给 Presenter 而不是 View。Presenter 获取到 Model 变更的消息以后,通过 View 提供的接口更新界面。
-
-
MVVM
-
MVVM 可以看做是一种特殊的 MVP(Passive View)模式,或者说是对 MVP 模式的一种改良。
-
MVVM 代表的是 Model-View-ViewModel,可以简单把 ViewModel 理解为页面上所显示内容的数据抽象,和 Domain Model 不一样,ViewModel 更适合用来描述 View。MVVM 的依赖关系和 MVP 依赖关系一致,只不过是把 P 换成了 VM。
-
MVVM 的调用关系:- MVVM 的调用关系和 MVP 一样。但是,在 ViewModel 当中会有一个叫 Binder,或者是 Data-binding engine 的东西。以前全部由 Presenter 负责的 View 和 Model 之间数据同步操作交由给 Binder 处理。你只需要在 View 的模板语法当中,指令式声明 View 上的显示的内容是和 Model 的哪一块数据绑定的。当 ViewModel 对进行 Model 更新的时候,Binder 会自动把数据更新到 View 上,当用户对 View 进行操作(例如表单输入),Binder 也会自动把数据更新到 Model 上。这种方式称为:Two-way data-binding,双向数据绑定。可以简单而不恰当地理解为一个模板引擎,但是会根据数据变更实时渲染。
-
-
箭头函数与普通函数(function)的区别是什么?构造函数(function)可以使用 new 生成实例,那么箭头函数可以吗?为什么?
箭头函数是普通函数的简写,可以更优雅的定义一个函数,和普通函数相比,有以下几点差异:
-
函数体内的 this 对象,就是定义时所在的对象,而不是使用时所在的对象;
-
不可以使用 arguments 对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替;
-
不可以使用 yield 命令,因此箭头函数不能用作 Generator 函数;
-
不可以使用 new 命令,因为:
-
没有自己的 this,无法调用 call、apply;
-
没有 prototype 属性,而 new 命令在执行时需要将钩子函数的 prototype 赋值给新的对象的 proto
-
简单说说 js 中有哪几种内存泄露的情况?
-
意外的全局变量;
-
闭包;
-
未被清空的定时器;
-
未被销毁的事件监听;
-
DOM 引用
-
什么是尾调用,使用尾调用有什么好处?
尾调用指的是函数的最后一步调用另一个函数。我们代码执行是基于执行栈的,所以当我们在一个函数里调用另一个函数时,我们会保留当前的执行上下文,然后再新建另外一个执行上下文加入栈中。使用尾调用的话,因为已经是函数的最后一步,所以这个时候我们可以不必再保留当前的执行上下文,从而节省了内存,这就是尾调用优化。ES6 的尾调用优化只在严格模式下开启,正常模式是无效的。
-
简述懒加载?
懒加载也叫延迟加载,指的是在长网页中延迟加载图像,是一种很好优化网页性能的方式。
懒加载的优点:
-
提升用户体验,加快首屏渲染速度;
-
减少无效资源的加载;
-
防止并发加载的资源过多会阻塞 js 的加载;
-
懒加载的原理:首先将页面上的图片的 src 属性设为空字符串,而图片的真实路径则设置在 data-original 属性中,当页面滚动的时候需要去监听 scroll 事件,在 scroll 事件的回调中,判断我们的懒加载的图片是否进入可视区域,如果图片在可视区内则将图片的 src 属性设置为 data-original 的值,这样就可以实现延迟加载。
-
如何实现函数的柯里化?
把接收多个参数的函数变换为接收一个单一参数
(最初函数的第一个参数)的函数,并返回接收
剩余参数而且返回结果的新函数的技术。
JS 函数柯里化的优点:
-
可以延迟计算,即如果调用柯里化函数传入参数是不调用的,会将参数添加到数组中存储,等到没有参数传入的时候进行调用;
-
参数复用,当在多次调用同一个函数,并且传递的参数绝大多数是相同的,那么该函数可能是一个很好的柯里化候选;
-
实现:
function curringAdd() {
let args = [].slice.call(arguments, 0);
function add() {
args = [...args, [].slice.call(arguments, 0)];
return add
}
add.toString = function() {
return args.reduce((t, a) => t + +a, 0);
}
return add;
}
console.log(curringAdd(1)(2)(3)) // 6
console.log(curringAdd(1, 2, 3)(4)) // 10
console.log(curringAdd(1)(2)(3)(4)(5)) // 15
console.log(curringAdd(2, 6)(1)) // 9
console.log(curringAdd(1)) // 1
-
let、var、const 区别?
-
var 声明的变量会挂载在 window 上,而 let 和 const 声明的变量不会
-
var 声明变量存在变量提升,let 和 const 不存在变量提升
-
let 和 const 声明形成块作用域,var 变量提升不会形成作用域
-
同一作用域下 let 和 const 不能声明同名变量,而 var 可以
-
var 和 let 可以可以修改声明的变量,const 不可以
-
const 定义的变量时必须初始化
-
let、const 存在暂时性死区
-
简单说说 js 中的继承
-
原型链继承 JavaScript 实现继承的基本思想:通过原型将一个引用类型继承另一个引用类型的属性和方法。
-
借用构造函数继承(伪造对象或经典继承) JavaScript 实现继承的基本思想:在子类构造函数内部调用超类型构造函数。通过使用 apply()和 call()方法可以在新创建的子类对象上执行构造函数
-
组合继承(原型+借用构造)(伪经典继承) JavaScript 实现继承的基本思想:将原型链和借用构造函数的技术组合在一块,从而发挥两者之长的一种继承模式。将原型链和借用构造函数的技术组合到一起,从而取长补短发挥两者长处的一种继承模式
-
原型式继承 JavaScript 实现继承的基本思想:借助原型可以基于已有的对象创建新对象,同时还不必须因此创建自定义的类型。
-
寄生式继承 JavaScript 实现继承的基本思想:创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真正是它做了所有工作一样返回对象。寄生式继承是原型式继承的加强版
-
寄生组合式继承 JavaScript 实现继承的基本思想:通过借用函数来继承属性,通过原型链的混成形式来继承方法
-
介绍 this 各种情况
-
以函数形式调用时,this 永远都是 window
-
以方法的形式调用时,this 是调用方法的对象
-
以构造函数的形式调用时,this 是新创建的那个对象
-
使用 call 和 apply 调用时,this 是指定的那个对象
-
箭头函数:箭头函数的 this 看外层是否有函数 如果有,外层函数的 this 就是内部箭头函数的 this 如果没有,就是 window
-
特殊情况:通常意义上 this 指针指向为最后调用它的对象。这里需要注意的一点就是如果返回值是一个对象,那么 this 指向的就是那个返回的对象,如果返回值不是一个对象那么 this 还是指向函数的实例
-
for...in 和 for...of 的区别
for…of 是 ES6 新增的遍历方式,允许遍历一个含有 iterator 接口的数据结构(数组、对象等)并且返回各项的值,和 ES3 中的 for…in 的区别如下
-
for…of 遍历获取的是对象的键值,for…in 获取的是对象的键名;
-
for… in 会遍历对象的整个原型链,性能非常差不推荐使用,而 for … of 只遍历当前对象不会遍历原型链;
-
对于数组的遍历,for…in 会返回数组中所有可枚举的属性(包括原型链上可枚举的属性),for…of 只返回数组的下标对应的属性值
-
总结:for...in 循环主要是为了遍历对象而生,不适用于遍历数组;for...of 循环可以用来遍历数组、类数组对象,字符串、Set、Map 以及 Generator 对象。
三 Vue 篇
-
vue 的路由模式及区别?
-
hash 模式在浏览器中符号“#”,#以及#后面的字符称之为 hash,用 window.location.hash 读取;
-
特点:hash 虽然在 URL 中,但不被包括在 HTTP 请求中;用来指导浏览器动作,对服务端安全无用,hash 不会重加载页面。
-
history 模式:history 采用 HTML5 的新特性;
-
提供了两个新方法:pushState(),replaceState()可以对浏览器历史记录栈进行修改,以及 popState 事件的监听到状态变更。history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,否则会报 404 错误
-
你们 vue 项目是打包了一个 js 文件,一个 css 文件,还是有多个文件?
根据 vue-cli 脚手架规范,一个 js 文件,一个 CSS 文件。
-
说一下 v-if 和 v-show 的区别
当条件不成立时,v-if 不会渲染 DOM 元素,v-show 操作的是样式(display),切换当前 DOM 的显示和隐藏。
-
vue 组件传值
-
父子组件传值
-
1,父组件通过 prop 传值,子组件通过 $emit 向父组件 传值
-
2,通过 v-model value input 传值(看下面示例)
-
3,通过 update sync 进行传值(看下面示例)
-
4,通过事件总线 eventBus
-
5,直接操作子组件的数据 子组件通过$parent 向父组件传值
-
6, vuex
-
-
组件跨代传值(爷孙组件及更深层组件)的方法
-
1,listeners(看示例)
-
// 父组件
<template>
<div>
<child :price="price" :from='from' :num='num'@upRocket="reciveRocket"></child>
</div>
</template>
<script>
import child from './child.vue'
export default {
name:'parent',
components:{
model
},
data() {
return {
// child 组件接受的参数
from:{
name:'wg',
age:18
},
price:200,
num:2
}
},
methods:{
// 要传递的函数
reciveRocket () {
console.log("reciveRocket success")
}
}
}
// 子组件
<template>
<div class="a">
<div>子组件</div>
<grandson :pageNum='pageNum' :pageSize='pageSize' v-bind="$attrs" v-on="$listeners"></grandson>
</div>
</template>
<script>
import grandson from './grandson.vue'
export default {
name: 'child',
props: ['from'], // 通过 props 接受一个 from
components: {
grandson
},
data () {
return {
pageNum:1,
pageSize:10
}
},
mounted() {
console.log(this.$attr,'attr')
console.log(this.$listeners,'listeners')
// price num 'attr' (父组件传进来 from price num, 子组件通过 props 接受了 from, \$attr 就剩下 price num)
// upRocket 'listeners' 父组件定义的函数
},
methods: {
}
}
</script>
// 孙子组件
<template>
<div class="grandson">
<div>孙子组件</div>
<button></button>
</div>
</template>
<script>
export default {
name: 'grandson',
components: {
grandson
},
data () {
return {
}
},
mounted() {
console.log(this.$attr,'attr')
console.log(this.$listeners,'listeners')
// pageNum pageSize price num 'attr' => attr 接受除 props 接受的外的 所有属性
// upRocket 'listeners' 接受父组件绑定的函数,(父组件直接绑定的爷组件定义的函数)
},
methods: {
}
}
</script>
//如果跨代很多,$attr $listeners 就会有点太繁琐,可以考虑 provide/inject
// 父级组件提供 'foo'
var Provider = {
provide: {
foo: 'bar'
},
// ...
}
// 子组件注入 'foo'
var Child = {
inject: ['foo'],
created () {
console.log(this.foo) // => "bar"
}
// ...
}
-
2,通过 v-model value input 传值(看下面示例)
-
3,通过 update sync 进行传值(看下面示例)
-
4,通过事件总线 eveBus
-
5,直接操作子组件的数据 子组件通过$parent 向父组件传值
-
6, vuex
-
兄弟组件传值
-
eventBus
-
vuex
-
实际开发中,更倾向于子组件通过$emit 触发父组件函数,父组件再改变值传给 兄弟组件
-
watch computed 有什么区别
-
1.当一个对象改变需要做一些逻辑处理或者开销比较大的时候,考虑使用watch
-
2.当多个变量都会对某一个产品影响时,考虑使用 computed
-
怎么缓存组件或者页面
// 用keep-alive,进行缓存,(不需要重新创建卸载 => 缓存) keep-alive是一个抽象组件:它自身不会渲染一个DOM元素,也不会出现在父组件链中;使用keep-alive包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。其有三个参数:
-
include定义缓存白名单,会缓存的组件;
-
exclude定义缓存黑名单,不会缓存的组件;
-
以上两个参数可以是逗号分隔字符串、正则表达式或一个数组,include="a,b"、:include="/a|b/"、:include="['a', 'b']";
-
匹配首先检查组件自身的 name 选项,如果 name 选项不可用,则匹配它的局部注册名称 (父组件 components 选项的键值)。匿名组件不能被匹配;
-
max最多可以缓存多少组件实例。一旦这个数字达到了,在新实例被创建之前,已缓存组件中最久没有被访问的实例会被销毁掉;
-
不会在函数式组件中正常工作,因为它们没有缓存实例;
-
当组件在内被切换,它的activated和deactivated这两个生命周期钩子函数将会被对应执行。
-
vuex
-
vuex 有那几个模块组成
-
state 定义变量
-
getter 派生状态 类似于计算属性
-
mutations 改变 store 中的状态
-
actions 主要操作异步操作,在组件中通过 this.$store.dispatch('xxx') 去触发actions方法
-
modules 把store 分为细小的模块,应用于复杂的模块
-
-
mutations 和 actions 的区别
-
mutations 主要用于同步操作 store.commit('xxx') 触发
-
actions 主要用于异步操作 store.dispatch('xxx') 触发actions, actions 在通过触发 mutations 更改 state状态
-
-
怎么解决 vuex 刷新数据丢失的问题
-
把数据存到 sessionStorage中,当页面刷新时 store的初始值 从 sessionStorage中取
-
其关键点在于 把store的值什么时候存到 sessionStorage中,建议是 当页面刷新之前,可以再入口app.vue组件created钩子中 添加监听 beforeunload 进行处理赋值
-
-
vue2.0 中 双向绑定的原理
vue通过 Object.defineProperty 劫持data中各个属性的getter和setter,结合发布订阅者模式 进行数据和视图的响应
-
为什么组件中data必须用函数返回一个对象?
对象为引用类型,当重用组件时,由于数据对象都指向同一个data对象,当在一个组件中修改data时,其他重用的组件中的data会同时被修改;而使用返回对象的函数,由于每次返回的都是一个新对象(Object的实例),引用地址不同,则不会出现这个问题
-
Vue组件里的定时器要怎么销毁?
-
如果页面上有很多定时器,可以在data选项中创建一个对象timer,给每个定时器取个名字一一映射在对象timer中 在beforeDestroy构造函数中for(let k in this.timer){clearInterval(k)};
-
如果页面只有单个定时器,可以这么做
const timer = setInterval(() =>{}, 500);
this.$once('hook:beforeDestroy', () => {
clearInterval(timer);
})
-
在Vue中那些数组变化无法监听,为什么,怎么解决?
-
利用索引直接设置一个数组项时
-
修改数组的长度时
-
第一个情况,利用已有索引直接设置一个数组项时Object.defineProperty()是可以监听到,利用不存在的索引直接设置一个数组项时Object.defineProperty()是不可以监听到,但是官方给出的解释是由于JavaScript的限制,Vue不能检测以上数组的变动,其实根本原因是性能问题,性能代价和获得的用户体验收益不成正比。
-
第二个情况,原因是Object.defineProperty()不能监听到数组的length属性
-
-
用this.$set(this.items, indexOfItem, newValue)或this.items.splice(indexOfItem, 1, newValue)来解决第一种情况
-
用this.items.splice(newLength)来解决第二种情况
-
在Vue中哪些对象变化无法监听,为什么,怎么解决?
-
对象属性的添加
-
对象属性的删除 因为Vue是通过Object.defineProperty来将对象的key转成getter/setter的形式来追踪变化,但getter/setter只能追踪一个数据是否被修改,无法追踪新增属性和删除属性,所以才会导致上面对象变化无法监听。
-
用this.$set(this.obj,"key","newValue")来解决第一种情况;
-
用Object.assign来解决第二种情况。
-
删除对象用delete和Vue.delete有什么区别?
-
delete:只是被删除对象成员变为' '或undefined,其他元素键值不变;
-
Vue.delete:直接删了对象成员,如果对象是响应式的,确保删除能触发更新视图,这个方法主要用于避开 Vue 不能检测到属性被删除的限制。
-
Vue怎么定义全局方法
-
挂载在Vue的prototype上
// base.js
const install = function (Vue, opts) {
Vue.prototype.demo = function () {
console.log('我已经在Vue原型链上')
}
}
export default {
install
}
//main.js
//注册全局函数
import base from 'service/base';
Vue.use(base);
-
利用全局混入mixin
-
用this.on绑定方法,用this.off解绑方法,用this.emit全局调用
this.$root.$on('demo',function(){
console.log('test');
})
this.$root.$emit('demo');
this.$root.$off('demo');
-
route和router有什么区别
route是“路由信息对象”,包括path,params,hash,query,fullPath,matched,name等路由信息参数。而router是“路由实例对象”,包括了路由的跳转方法,钩子函数等。
-
说说你对SPA单页面的理解,它的优缺点分别是什么
是一种只需要将单个页面加载到服务器之中的web应用程序。当浏览器向服务器发出第一个请求时,服务器会返回一个index.html文件,它所需的js,css等会在显示时统一加载,部分页面按需加载。url地址变化时不会向服务器在请求页面,通过路由才实现页面切换。
-
优点:
-
良好的交互体验,用户不需要重新刷新页面,获取数据也是通过Ajax异步获取,页面显示流畅;
-
良好的前后端工作分离模式。
-
-
缺点:
-
SEO难度较高,由于所有的内容都在一个页面中动态替换显示,所以在SEO上其有着天然的弱势。
-
首屏加载过慢(初次加载耗时多)
-
-
什么是双向绑定?原理是什么
-
双向绑定是指数据模型(Module)和视图(View)之间的双向绑定。
-
其原理是采用数据劫持结合发布者-订阅者模式的方式来实现。
-
Vue中先遍历data选项中所有的属性(发布者)用Object.defineProperty劫持这些属性将其转为getter/setter。读取数据时候会触发getter。修改数据时会触发setter。
-
然后给每个属性对应new Dep(),Dep是专门收集依赖、删除依赖、向依赖发送消息的。先让每个依赖设置在Dep.target上,在Dep中创建一个依赖数组,先判断Dep.target是否已经在依赖中存在,不存在的话添加到依赖数组中完成依赖收集,随后将Dep.target置为上一个依赖。
-
组件在挂载过程中都会new一个Watcher实例。这个实例就是依赖(订阅者)。Watcher第二参数式一个函数,此函数作用是更新且渲染节点。在首次渲染过程,会自动调用Dep方法来收集依赖,收集完成后组件中每个数据都绑定上该依赖。当数据变化时就会在seeter中通知对应的依赖进行更新。在更新过程中要先读取数据,就会触发Wacther的第二个函数参数。一触发就再次再次自动调用Dep方法收集依赖,同时在此函数中运行patch(diff运算)来更新对应的DOM节点,完成了双向绑定。
-
路由传参有那些方式
-
动态路由 路由表中配置占位符
{
path: '/list/:id',
name: 'list',
component:list
}
-
通过 push 跳转
this.$router.push({
path: `/particulars/${id}`
})
// 通过 this.$route.params.id 取参
-
通过 push 跳转
this.$router.push({
path: '/list',
query: {
name: 'wg'
}
})
// 通过 this.$route.query.name 取参
-
name 方法传参
this.$router.push({
name: 'list',params: {
age: '18'
}
})
// 通过 this.$route.query.name 取参
-
nextTick 使用场景和原理
nextTick 中的回调是在下次 DOM 更新循环结束之后执行的延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。主要思路就是采用微任务优先的方式调用异步方法去执行 nextTick 包装的方法
-
你都做过哪些 Vue 的性能优化
-
对象层级不要过深,否则性能就会差
-
不需要响应式的数据不要放到 data 中(可以用 Object.freeze() 冻结数据)
-
v-if 和 v-show 区分使用场景
-
computed 和 watch 区分使用场景
-
v-for 遍历必须加 key,key 最好是 id 值,且避免同时使用 v-if
-
大数据列表和表格性能优化-虚拟列表/虚拟表格
-
防止内部泄漏,组件销毁后把全局变量和事件销毁
-
图片懒加载
-
路由懒加载
-
第三方插件的按需引入
-
适当采用 keep-alive 缓存组件
-
防抖、节流运用
-
服务端渲染 SSR or 预渲染
-
Vue 模板编译原理
Vue 的编译过程就是将 template 转化为 render 函数的过程 分为以下三步
-
将 模板字符串 转换成 element ASTs(解析器)
-
对 AST 进行静态节点标记,主要用来做虚拟DOM的渲染优化(优化器)
-
使用 element ASTs 生成 render 函数代码字符串(代码生成器)