技术分享
首页
  • JavaScript

    • 构造函数和原型
    • Cookie和Session
    • Object.create(null)和{}
    • TypeScript配置
    • typescript入门到进阶
  • 框架

    • Vue-Router
    • React基础入门
  • 其它

    • Http协议
    • 跨域问题总结
  • 分析Promise实现
  • Axios源码分析
  • Webpack原理
  • vueRouter源码分析
  • Vue

    • Vite快速搭建Vue3+TypeScript项目
    • Cordova打包Vue项目的问题
    • Vue将汉字转为拼音,取出首字母
    • h5项目问题总结
  • JavaScript

    • new Function
  • 后端

    • Node.js中使用Crypto生成Token
    • Body-Parser处理多层对象的问题
  • 其它

    • 项目Demo汇总
    • Vuepress+Vercel搭建个人站点
    • 项目中能用到的
    • husky规范代码提交
  • Mongoose基础
  • Multer文件上传中间件的使用
  • JavaScript

    • 浅谈两数全等
    • JavaScript进制转换
    • 手写bind,apply,call和new
  • 算法

    • 数组去重和排序
    • 数组扁平化
    • 斐波那契数列
  • JavaScript 数据结构
  • 其它

    • webpack面试题
    • vite面试题
    • svg和canvas的优缺点
    • TypeScript面试题
    • Vue常见面试题
  • 计算机网络

    • 数据链路层
    • 网络层
  • Git的使用
  • Nginx的使用
  • CentOS7安装Nginx
  • 正则表达式
  • SEO搜索引擎优化
  • Serverless介绍
友链
GitHub (opens new window)

刘誉

总有人要赢,为什么不能是我
首页
  • JavaScript

    • 构造函数和原型
    • Cookie和Session
    • Object.create(null)和{}
    • TypeScript配置
    • typescript入门到进阶
  • 框架

    • Vue-Router
    • React基础入门
  • 其它

    • Http协议
    • 跨域问题总结
  • 分析Promise实现
  • Axios源码分析
  • Webpack原理
  • vueRouter源码分析
  • Vue

    • Vite快速搭建Vue3+TypeScript项目
    • Cordova打包Vue项目的问题
    • Vue将汉字转为拼音,取出首字母
    • h5项目问题总结
  • JavaScript

    • new Function
  • 后端

    • Node.js中使用Crypto生成Token
    • Body-Parser处理多层对象的问题
  • 其它

    • 项目Demo汇总
    • Vuepress+Vercel搭建个人站点
    • 项目中能用到的
    • husky规范代码提交
  • Mongoose基础
  • Multer文件上传中间件的使用
  • JavaScript

    • 浅谈两数全等
    • JavaScript进制转换
    • 手写bind,apply,call和new
  • 算法

    • 数组去重和排序
    • 数组扁平化
    • 斐波那契数列
  • JavaScript 数据结构
  • 其它

    • webpack面试题
    • vite面试题
    • svg和canvas的优缺点
    • TypeScript面试题
    • Vue常见面试题
  • 计算机网络

    • 数据链路层
    • 网络层
  • Git的使用
  • Nginx的使用
  • CentOS7安装Nginx
  • 正则表达式
  • SEO搜索引擎优化
  • Serverless介绍
友链
GitHub (opens new window)
  • 浅谈两数全等
  • JavaScript 进制转换
  • 数组去重和排序
  • 数组扁平化
  • 斐波那契数列
  • webpack面试题
  • 手写bind,apply,call和new
  • vite面试题
  • svg和canvas的优缺点
  • TypeScript面试题
  • Vue常见面试题
    • Vue 面试题
      • new Vue() 发生了什么
      • Vue 数据响应式原理
      • Vue 生命周期
      • SPA 优缺点
      • 说说 vue 的事件管理
      • vue 如何防止数据被再次 observe
      • 数据变化,派发更新的时候,做了何优化
      • Vue.$nextTick 原理
      • CommonJS 和 esModule 的区别
      • 父组件监听子组件的生命周期
    • Vue-Router 面试题
      • 原理
      • history 和 hash 模式区别
      • vue-router 钩子执行顺序
    • Vuex 面试题
    • Ajax 面试题
      • fetch 和 xhr 有什么区别
      • cros 的简单实现
    • 浏览器 面试题
      • 协商缓存和强缓存
      • Cookie 有哪些字段
      • XSS(跨站脚本攻击)
      • CSRF(跨站请求伪造)
    • nginx 和 tomcat 区别
  • JavaScript 数据结构
  • 面试
coderly
2021-03-20

Vue常见面试题

# Vue 常见面试题

# Vue 面试题

# new Vue() 发生了什么

  • 内部执行了根实例的初始化过程
  • options 合并
  • 属性初始化
  • 自定义事件处理
  • 数据响应式处理
  • 初始化生命周期钩子

# Vue 数据响应式原理

  • defineReactive 把数据定义成响应式的
  • 给属性增加一个 dep,用来收集对应的那些 watcher
  • 等数据变化进行更新

# Vue 生命周期

# beforeCreate

这是第一个生命周期函数,此时,组件的 data 和 methods 以及页面 DOM 结构都还没有初始化,所以这个阶段,什么都做不了

# created

这是组件创建阶段第二个生命周期函数,此时,组件的 data 和 methods 已经可以用了,但是页面还没有渲染出来;在这个生命周期函数中,我们经常会发起 Ajax 请求

# beforeMount

当模板在内存中编译完成,会立即执行实例创建阶段的第三个生命周期;此时内存中的模板结构,还没有真正渲染到页面上,此时页面上看不到真实的数据( 用户看到的只是一个模板页面 )

# mounted

这个是组件创建阶段最后一个生命周期函数,此时,页面已经真正的渲染好了,用户已经可以看到真实的页面数据了;当这个生命周期函数执行完,组件就离开了创建阶段,进入到了运行中的阶段

  • 如果用到了第三方的 UI 插件,而且这个插件还需要被初始化,那么,必须在 mounted 及之后来初始化插件(echarts 图表)

# beforeUpdate

当执行 beforeUpdate 生命周期函数的时候, 数据肯定时最新的,但是页面上呈现的数据,还是旧的

# updated

页面已经完成了更新,此时,data 数据是最新的,同时,页面上呈现的数据,也是最新的

# beforeDestory

当执行 beforeDestory 的时候,组件即将被销毁,但是还没有真正开始销毁,此时组件还是正常可用的,data、methods 等数据或方法,依然可以被正常访问

# destoryed

组件已经完成了销毁,此时组件已经废了,data、methods 都不可用了

# SPA 优缺点

SPA( single-page application )仅在 Web 页面初始化时加载相应的 HTML、JavaScript 和 CSS。⼀旦⻚⾯加载完成,SPA 不会因为用户的操作而进行页面的重新加载或跳转;取而代之的是利用路由机制实现 HTML 内容的变换,UI 与用户的交互,避免页面的重新加载。

# 优点

  • 前后端分离
  • 减轻服务器的负担
  • 良好的交互体验 - ajax

# 缺点

  • 不利于 SEO
  • 首屏渲染时间长
  • 前进、后退难以管理

# 说说 vue 的事件管理

待更新

# vue 如何防止数据被再次 observe

待更新

# 数据变化,派发更新的时候,做了何优化

将变更放入队列,在下一个 nexttick 之后更新

# Vue.$nextTick 原理

在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM

  • 言外之意就是在主线程执行代码完成之后,立刻执行
  • 也就是在微任务队列中(也可能是宏任务队列,视运行环境而定)
  • 内部实现依次使用 Promise -> MutationObserver -> setImmediate -> setTimeout 做兼容
  • Vue 源码 src/core/util/next-tick.js 文件
if (typeof Promise !== 'undefined' && isNative(Promise)) {
  const p = Promise.resolve()
  timerFunc = () => {
    p.then(flushCallbacks)
    if (isIOS) setTimeout(noop)
  }
  isUsingMicroTask = true
} else if (
  !isIE &&
  typeof MutationObserver !== 'undefined' &&
  (isNative(MutationObserver) ||
    // PhantomJS and iOS 7.x
    MutationObserver.toString() === '[object MutationObserverConstructor]')
) {
  // Use MutationObserver where native Promise is not available,
  let counter = 1
  const observer = new MutationObserver(flushCallbacks)
  const textNode = document.createTextNode(String(counter))
  observer.observe(textNode, {
    characterData: true,
  })
  timerFunc = () => {
    counter = (counter + 1) % 2
    textNode.data = String(counter)
  }
  isUsingMicroTask = true
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
  // Fallback to setImmediate.
  timerFunc = () => {
    setImmediate(flushCallbacks)
  }
} else {
  // Fallback to setTimeout.
  timerFunc = () => {
    setTimeout(flushCallbacks, 0)
  }
}
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

# 作用

  • 视图更新之后,获取最新的 dom 节点
  • data 数据修改后,立即拿到最新数据

# CommonJS 和 esModule 的区别

参考 (opens new window)

# CommonJS

  1. module.exports 默认值为 {}
  2. exports 是 module.exports 的引用(exports 默认指向 module.exports 的内存空间)
  3. require() 返回的是 module.exports 而不是 exports
//foo.js
exports = {
  foo: 'foo',
} // 给 exports 重新赋值,断开了 exports 指向 module.exports 的地址,重新指向一个新地址

//bar.js
const { foo } = require('./foo.js')
//reuqire 返回的是 module.exports 对象, 默认为 {}
1
2
3
4
5
6
7
8

# esModule

  1. 浏览器加载 ES6 模块,也使用 <script> 标签,但是要加入 type="module" 属性。
<script type="module" src="./foo.js"></script>
1

# 差异

  1. CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
  2. CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
  3. CommonJS 模块的 require() 是同步加载模块,ES6 模块的 import 命令是异步加载,有一个独立的模块依赖的解析阶段。

# CommonJS 模块加载 ES6 模块

使用 import() 方法加载

;(async () => {
  await import('./foo.mjs')
})()
1
2
3

# ES6 模块加载 CommonJS 模块

ES6 模块的 import 命令可以加载 CommonJS 模块,但是只能整体加载,不能只加载单一的输出项。

// 正确
import packageMain from 'commonjs-package'

// 报错
import { method } from 'commonjs-package'
1
2
3
4
5

# CommonJS 模块循环引用问题

# 父组件监听子组件的生命周期

  1. 子组件生命周期中 emit
// Parent.vue
<Child @mounted="doSomething"/>
    
// Child.vue
mounted() {
  this.$emit('mounted')
}
1
2
3
4
5
6
7
  1. hook
// Parent.vue
<Child @hook:mounted="doSomething"/>
1
2

# Vue-Router 面试题

# 原理

# 前端路由

  1. hash 模式

    • 类似于服务端路由,前端路由实现起来其实也很简单,就是匹配不同的 url 路径,进行解析,然后动态的渲染出区域 html 内容
    • 这种 # 后面 hash 值的变化,并不会导致浏览器向服务器发出请求,浏览器不发出请求,也就不会刷新页面
    • 另外每次 hash 值的变化,还会触发 hashchange 这个事件,通过这个事件我们就可以知道 hash 值发生了哪些变化
    • 然后我们便可以监听 hashchange 来实现更新页面部分内容的操作
window.addEventListener('hashchange', () => {
  // do something
})
1
2
3
  1. history 模式

    • HTML5 新增的API: pushState 和 replaceState,通过这两个 API 可以改变 url 地址且不会发送请求
    • 但因为没有 # 号,所以当用户刷新页面之类的操作时,浏览器还是会给服务器发送请求
    • 为了避免出现这种情况,所以这个实现需要服务器的支持,需要把所有路由都重定向到根页面。
// 当活动历史记录条目更改时,将触发popstate事件
// 调用history.pushState()或history.replaceState()不会触发popstate事件。只有在做出浏览器动作时,才会触发该事件,如用户点击浏览器的回退按钮(或者在Javascript代码中调用history.back()或者history.forward()方法)
window.addEventListener('popstate', () => {
  // do something
})
1
2
3
4
5

# history 和 hash 模式区别

  • hash 模式路径上有 #, 用 window.location.hash 读取。而 history 路由没有会好看一点
  • hash 路由支持低版本的浏览器,而 history 路由是 HTML5 新增的 API
  • 回车刷新操作时,hash 路由会加载到地址栏对应的页面,而 history 路由一般 404 报错
  • history 模式需要后台配置支持。当我们刷新页面或者直接访问路径的时候就会返回 404,那是因为在 history 模式下,只是动态的通过 js 操作 window.history 来改变浏览器地址栏里的路径,并没有发起 http 请求,但是当我直接在浏览器里输入这个地址的时候,就一定要对服务器发起 http 请求,但是这个目标在服务器上又不存在,所以会返回 404

# vue-router 钩子执行顺序

# 导航钩子分类

  • 全局守卫
  • 路由独享守卫
  • 组件守卫

# 全局守卫


 




 


router.beforeEach((to, from, next) => {
  console.log('全局 - beforeEach')
  next() // 必须要有调用 next
})

router.afterEach(() => {
  console.log('全局 - afterEach')
})
1
2
3
4
5
6
7
8

# 路由独享守卫







 








 



{
  path: '/home',
  name: 'Home',
  component: () => import('../views/Home.vue'),
  beforeEnter (to, from , next) {
    next() // 必须要有调用 next
    console.log('路由 - beforeEnter ---> Home')
  }
},
{
  path: '/about',
  name: 'About',
  component: () => import('../views/About.vue'),
  beforeEnter (to, from, next) {
    next() // 必须要有调用 next
    console.log('路由 - beforeEnter ---> About')
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 组件守卫



 



 






 




 


// Home 组件
beforeRouteEnter(to, from, next) {
  console.log('组件 - beforeRouteEnter ---> Home')
  next() // 必须要有调用 next
},
beforeRouteLeave(to, from, next) {
  console.log('组件 - beforeRouteLeave ---> Home')
  next() // 必须要有调用 next
},


// About 组件
beforeRouteEnter(to, from, next) {
  console.log('组件 - beforeRouteEnter ---> About')
  next()
},
beforeRouteLeave(to, from, next) {
  next()
  console.log('组件 - beforeRouteLeave ---> About')
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 顺序

从 Home 组件 跳转到 About 组件 依次输出


 
 
 
 

- 组件 - beforeRouteLeave ---> Home
- 全局 - beforeEach
- 路由 - beforeEnter ---> About
- 组件 - beforeRouteEnter ---> About
- 全局 - afterEach
1
2
3
4
5

假设,一打开页面就进入 Home 页面,此时的钩子顺序 如下

 
 
 
 

- 全局 - beforeEach
- 路由 - beforeEnter ---> Home
- 组件 - beforeRouteEnter ---> Home
- 全局 - afterEach
1
2
3
4

综合上诉两种情况,我们可以得出如下结论

  • 如果是第一次进入页面,依次执行 全局的进入前置钩子 -> 路由的进入钩子 -> 组件的进入钩子 -> 全局的进入后置钩子
  • 如果从组件内离开,将会优先执行组件的 beforeRouteLeave 钩子,此后依次执行 全局的进入前置钩子 -> 路由的进入钩子 -> 组件的进入钩子 -> 全局的进入后置钩子
  • 顺序依次为: 组件离开(可有可无) -> 全局 -> 路由 -> 组件 -> 全局

# Vuex 面试题

# vuex 判断修改state 是直接修改还是 提交commit

在严格模式下,直接修改 state 会报错,this.$store.state.xxx = xxx

# Ajax 面试题

# fetch 和 xhr 有什么区别

# xhr

  1. 浏览器内置 api
  2. 使用起来也比较繁琐,需要设置很多值。
  3. 使用回调函数

# fetch

  1. 浏览器内置 api
  2. Fetch 提供了对 Request 和 Response (以及其他与网络请求有关的)对象的通用定义
  3. 返回一个 Promise 对象

# 差异

  1. fetch 使用 Promise,不使用回调函数,因此大大简化了写法,写起来更简洁。
  2. fetch 采用模块化设计,API 分散在多个对象上(Response 对象、Request 对象、Headers 对象),更合理一些;相比之下,XMLHttpRequest 的 API 设计并不是很好,输入、输出、状态都在同一个接口管理,容易写出非常混乱的代码。
  3. fetch 通过数据流(Stream 对象)处理数据,可以分块读取,有利于提高网站性能表现,减少内存占用,对于请求大文件或者网速慢的场景相当有用。XMLHTTPRequest 对象不支持数据流,所有的数据必须放在缓存里,不支持分块读取,必须等待全部拿到后,再一次性吐出来。

# cros 的简单实现

跨域 是 浏览器存在的一种安全策略,在非浏览器环境中不存在 跨域 问题

  1. 本地另起一个 node 服务,将 浏览器 发起的跨域请求代理到本地
  2. 在 node 环境中根据请求的路径 发起真实请求,等到请求返回了数据,再 response 给浏览器

# 浏览器 面试题

# 协商缓存和强缓存

# 协商缓存

提示

让客户端与服务器之间能实现缓存文件是否更新的验证、提升缓存的复用率,将缓存信息中的 Etag 和 Last-Modified 通过请求发送给服务器,由服务器校验,返回304状态码时,浏览器直接使用缓存。

  1. Last-Modified/Etag
    • Last-Modified: 文件最后更改时间,Etag:存储的是文件的特殊标识(一般都是 hash 生成的)

# 强缓存

提示

服务器通知浏览器一个缓存时间,在缓存时间内,下次请求,直接用缓存,不在时间内,执行比较缓存策略。

  1. Cache-Control(http1.1)和Expires(http1.0)
    • Cache-Control:max-age > 0(直接从游览器缓存中 提取),max-age <= 0(向server 发送http 请求确认 ,该资源是否有修改)

# Cookie 有哪些字段

参看 (opens new window)

  • domain:指定浏览器发出 HTTP 请求时,哪些 域名 要附带这个 Cookie
  • path:指定浏览器发出 HTTP 请求时,哪些 路径 要附带这个 Cookie
  • HttpOnly:指定该 Cookie 无法通过 JavaScript 脚本拿到
  • secure:标记为 Secure 的 Cookie 只应通过被 HTTPS 协议加密过的请求发送给服务端
  • SameSite:允许服务器要求某个 cookie 在跨站请求时不会被发送
  • Expires:指定一个具体的到期时间,到了指定时间以后,浏览器就不再保留这个 Cookie;不设置该属性,或者设为null,Cookie 只在当前会话(session)有效
  • Max-Age:指定从现在开始 Cookie 存在的秒数,过了这个时间以后,浏览器就不再保留这个 Cookie

# XSS(跨站脚本攻击)

攻击者想尽一切办法将可以执行的代码注入到网页中

# 存储型

  • 攻击者将恶意代码提交到目标网站的数据库中。
  • 用户打开目标网站时,网站服务端将恶意代码从数据库取出,拼接在 HTML 中返回给浏览器。
  • 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
  • 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。

# 反射型

  • 攻击者构造出特殊的 URL,其中包含恶意代码。
  • 用户打开带有恶意代码的 URL 时,网站服务端将恶意代码从 URL 中取出,拼接在 HTML 中返回给浏览器。
  • 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
  • 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。

# DOM 型

  • 攻击者构造出特殊的 URL,其中包含恶意代码。
  • 用户打开带有恶意代码的 URL。
  • 用户浏览器接收到响应后解析执行,前端 JavaScript 取出 URL 中的恶意代码并执行。
  • 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。

预防:

  1. 对数据进行严格的输出编码:如HTML元素的编码,JS编码,CSS编码,URL编码等等
  2. CSP HTTP Header,即 Content-Security-Policy、X-XSS-Protection
  3. 输入验证:比如一些常见的数字、URL、电话号码、邮箱地址等等做校验判断
  4. 开启浏览器XSS防御:Http Only cookie
  5. 验证码

# CSRF(跨站请求伪造)

攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。

  • 受害者登录 a.com,并保留了登录凭证(Cookie)
  • 攻击者引诱受害者访问了b.com
  • b.com 向 a.com 发送了一个请求:a.com/act=xx浏览器会默认携带a.com的Cookie
  • a.com接收到请求后,对请求进行验证,并确认是受害者的凭证,误以为是受害者自己发送的请求
  • a.com以受害者的名义执行了act=xx
  • 攻击完成,攻击者在受害者不知情的情况下,冒充受害者,让a.com执行了自己定义的操作

预防:

  1. 同源检测:通过Header中的Origin Header 、Referer Header 确定
  2. CSRF Token 校验:将CSRF Token输出到页面中(通常保存在Session中),页面提交的请求携带这个Token,服务器验证Token是否正确
  3. Samesite Cookie属性

# nginx 和 tomcat 区别

nginx

  • 静态服务器,静态资源,处理静态资源速度快
  • 反向代理服务器 ,且支持负载均衡
  • 并发性比较好,CPU内存占用低

tomcat

  • 是应用(Java)服务器
  • 动态解析容器,处理动态请求

总结: Nginx有动态分离机制,静态请求直接就可以通过Nginx处理,动态请求才转发请求到后台交由Tomcat进行处理

#Vue
上次更新: 2021/08/25, 17:01:13
TypeScript面试题
JavaScript 数据结构

← TypeScript面试题 JavaScript 数据结构→

最近更新
01
代码片段
04-22
02
koa全家桶
03-29
03
mocks项目复盘
03-29
更多文章>
Theme by Vdoing | Copyright © 2021-2022 coderly | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式