一、Vue 相关问题
1、如何理解Vue?
vue是一套构建用户界面的渐进式框架,核心是数据模型与组件化设计,是一款MVVM框架,基于双向数据绑定,简洁轻量。兼容IE可以添加 babel-polyfill
插件。
- babel-polyfuu 插件的使用方法:在 build 文件里的 webpack.base.conf.js 文件里配置,然后在 main.js 文件里引入:
// webpack.base.conf.js:
module.exports = {
entry: {
app: ['babel-polyfill', './src/main.js']
},
}
// main.js:
import 'babel-polyfill'
- promise 问题,axios 不兼容 ie 的使用方法:
import promise from 'es6-promise';
promise.polyfill();
- URLSearchParams 未定义的问题,IE9不支持URLSearchParams:
// 下载 qs
npm install qs
// 在 main.js 中引入
import qs from 'qs';
2、什么是MVVM开发模式?
M是Model代表数据模型,数据和业务逻辑都在Model层;V是View代表UI视图,负责数据的展示;VM是ViewModel负责监听Model中的数据变化并且对View的视图更新,处理交互操作。
3、简述Vue的响应式原理。
当一个vue实例被创建时,vue会遍历 data 中的属性,用 Object.defineProperty 将它们转化为 getter/setter 并在内部追踪相关依赖,在属性被访问和修改时被通知。每个组件实例都有 watcher,它会在组件渲染的过程中把属性记录为依赖,当依赖项的 setter 被调用的时候,会通知关联组件更新。
4、简述Vue的双向数据绑定原理。
vue通过 Object.defineProperty() 做数据劫持,重写 data 的 getter/setter ,在数据变动时触发 Watcher,改变虚拟dom中的数据更新dom树,完成双向数据绑定。
5、简述Vue组件之间的数据传递。
父组件向子组件传递数据:
子组件用 props 接收父组件传来的数据,在父组件中注册子组件并赋值给声明的属性。子组件向父组件传递数据:
子组件用 $emit 触发一个自定义函数并传递参数,在父组件中注册子组件并绑定方法接收数据。兄弟组件之间传递数据:
在简单的场景下,可以使用一个空的vue实例 Bus作为中央事件总线;在复杂的场景下,使用vuex。
6、Vue中如何监听某个属性值的变化?
比如现在需要监听data中,obj.a 的变化:
- 用 watch 监听
watch: {
obj: {
handler (newValue, oldValue) {
console.log('obj changed')
},
deep: true
}
}
deep 属性表示深层遍历,这么写会监听 obj 所有属性变化,并不是我们想要的,所以需要修改一下:
watch: {
obj.a: {
handler (newValue, oldValue) {
console.log('obj.a changed')
}
}
}
- 用 computed 实现
computed: {
a () {
return this.obj.a
}
}
7、Vue中如何给 data 中的对象添加一个新的属性?
this.$set(this.obj, 'b', 'obj.b')
8、delete 和 Vue.delete 删除数组有什么区别?
- delete被删除的元素只是变成了 empty 或 undefined 其他元素的键值都不变。
let a = [1, 2, 3, 4]
delete a[1]
console.log(a)
- Vue.delete直接删除了数组,改变了数组原来的键值。
let b = [1, 2, 3, 4]
this.$delete(b, 1)
console.log(b)
9、简述Vue的生命周期。
beforeCreate
:完成实例初始化,挂载没开始,DOM没有初始化,loading 事件可以放在这里。created
:实例创建完成,完成数据初始化,未挂载DOM不能访问 el,若要访问DOM一定要放在 Vue.nextTick() 的回调函数中,$ref 为空数组,可以对 data 数据进行操作但请求不宜过多避免白屏时间太长,loading 结束放在这里。beforeMount
:数据初始化完成,找到对应的 template,并编译成 render 函数。mounted
:完成挂载DOM和渲染,完成双向绑定,可发起后端请求,对DOM进行操作。beforeUpdate
:数据更新前触发,可在更新前访问现有的DOM,如手动移除事件监听器。updated
:数据更新完成,不要随意在这里操作数据,会陷入死循环。activated
:keep-alive 组件激活时调用(用来缓存组件状态),这个时候 created 钩子就不会被重复调用了,如果子组件需要在每次加载的时候进行某些操作,可以使用 activated 钩子触发。deactivated
:keep-alive 组件停用时调用。beforeDestroy
:实例销毁之前调用,但实例仍然可用,可以在这里销毁定时器、解绑全局时间、销毁插件对象。destroyed
:实例销毁后调用,Vue 实例指示的所有东西都会解除绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
10、<keep-alive></keep-alive>
的作用是什么?
<keep-alive></keep-alive>
包裹动态组件时,会缓存不活动的组件实例,主要用于保留组件状态或避免重新渲染。
比如有一个列表和一个详情页,那么用户会经常打开详情=>返回列表=>打开详情…这样列表和详情都是一个频率很高的页面,那么就可以对列表组件使用<keep-alive></keep-alive>
进行缓存,用户每次返回列表的时候,都能从缓存中快速渲染,而不是重新渲染,可以减轻服务器压力,提高性能。
11、Vue有哪些修饰符?
事件修饰符:stop(阻止冒泡)、prevent(提交事件不再重载页面)、once、left、right
键盘修饰符:up、down、enter、space
表单修饰符:number(输入字符串转为有效的数字)、trim(输入首尾空格过滤)、lazy(取代input监听change时间)
12、Vue中 $mount 与 el 区别?
两者在使用中没有什么区别,都是将实例化后的vue挂载到指定的DOM元素中,如果指定了 el 则渲染在 el 对应的DOM中,没有指定 el 就用 $mount 手动挂载。
13、Vue中 key 值和 is 的作用?
使用 key,vue会基于 key 的变化重新排列元素,并且移出 key 不存在的元素,可以做优化处理;is 可以扩展原生 html 元素,也可以绑定动态组件。
14、组件中 data 为什么是函数?
组件是用来复用的,Js 中对象是引用关系,作用域没有隔离,而vue的实例是不会被复用的,因此不存在引用对象的问题。
15、什么是单向数据流?
单向数据流出现在组件通信,数据从父级流向子级,数据本身还是父级的,如果操作子级要改变父级的数据,只能通过子级告诉父级要操作哪个数据,然后父级修改数据,再传给子级。
16、Vue中 $router
和 $route
的区别?
router 是 VueRouter 的一个实例,相当于一个全局的路由器对象,包含了所有的路由、路由的关键属性及方法,经常用的跳转链接 $router.push({path:'/login',query:{name:'userName'}})
route 是一个跳转的路由对象(局部变量),每一个路由都有一个自己的 route,可以获取本次路由跳转的 name、path、params、query 等参数,用法 let query=this.$route.query
17、vue-router 中 query 和 params 的区别?
两者都是在路由中传参,query 用 path 引入,参数在地址栏中显示;params 用 name 传递,参数不会在地址栏中显示。
18、vue-router有哪几种导航钩子?
- 第一种:全局导航钩子(全局守卫):router.beforeEach(to,from,next),跳转前进行判断拦截;
- 第 二种:全局解析守卫:router.beforeResolve,在导航被确认前,所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用;
- 第三种:单独路由独享的守卫:beforeEnter,在路由配置时定义,与全局守卫的方法是一样的。
- 第四种:组件内的守卫。直接在路由组件内定义守卫,
beforeRouteEnter (to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
},
beforeRouteUpdate (to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave (to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
}
19、vue.cli 中怎样使用自定义的组件
- 在 components 目录新建组件页面
- 在需要用的页面中导入
- 注入到vue的子组件的 components 属性上面
- 在视图中使用
20、简书Vue中的 slot 插槽。
插槽是Vue实现内容分发的API,将插槽元素作为分发内容的出口(子组件中给父组件提供一个占位符slot,父组件可以在组件标签中放任何内容)。
- 匿名插槽:没有插槽时,在组件标签写入内容是不起作用的,当在组件中声明了slot元素后,在组件标签中写入内容起了作用。
具名插槽:就是在父组件中添加一个
slot = '自定义名字'
的属性,在子组件中的 slot 元素里面添加name = '自定义名字'
即可。作用域插槽:就是组件上的属性,可以在组件元素内使用。
21、如何理解Vue中的 Render 渲染函数?
Vue一般使用 template 来创建 html,有的时候需要使用javascript 来创建 html,这时候我们需要使用 render 函数,render 函数 return 一个 createElement 组件中的子元素存储在组件实列中,createElement 返回的是包含的信息会告诉Vue页面上需要渲染什么样的节点。我们称这样的节点为虚拟DOM
22、如何解决在加载页面时出现的闪烁问题?
使用 v-cloak。
出现闪烁的原因可能有二:
- 加载时遇到 {{value.name}} 闪烁
- 加载时遇到一个空盒子里边什么也没有 <p v-html="value.name"></p>
解决方法:
<div class="#app" v-cloak>
<p>{{value.name}}</p>
</div>
// css 中要添加
[v-cloak] {
display: none;
}
如果还是闪烁,那可能是 v-cloak 的 display 属性被层级更高的给覆盖掉了;或者 v-cloak 的样式放在 @import 引入的css文件中不起作用,可以放在 link 引入的 css 文件里或者内联样式中。
23、vuex是什么?
是一个专为vue应用程序开发的状态管理模式。
24、vuex有哪几种状态和属性?
- state:存储数据,在根实例中注册了 store 后,用 this.$store.state 来访问;
- getter:可以认为是 store 的计算属性,它的返回值会根据它的依赖被缓存起来,而且只有当它的依赖值发生了改变才会被重新计算;
- mutation:更改 Vuex 的 store 中的状态唯一方法是提交 mutation;
- action:包含任意异步操作,通过提交 mutation 间接更变状态;
- module:将 store 分割成模块,每个模块都具有state、mutation、action、getter、甚至是嵌套子模块。
vuex的流程:
页面通过 mapAction 异步提交事件到 action;action 通过 commit 把对应参数同步提交到 mutation;mutation 会修改 state 中对于的值。;最后通过 getter 把对应值抛出去,在页面的计算属性中,通过 mapGetter 动态获取 stat e中的值。
二、javaScript 和 Jquery 相关问题
1、javascript 的数据类型有哪些?
- 基本数据类型:number、boolean、string、array、null、undefined
- 复合型数据类型:object、function、array
2、什么是window对象?什么是document对象?
- window 对象是指浏览器打开的窗口;
- document 对象是 HTML 文档对象的一个只读引用,window 对象的一个属性。
3、call 和 apply 的区别?
call 与 apply 的相同点:
- 方法功能是一样的,都是调用一个对象的一个方法,用另一个对象替换当前对象;
- 第一个参数的作用是一样的;
call 与 apply 的不同点:两者传入的列表形式不一样
- call 可以传入多个参数;
- apply 只能传入两个参数,所以其第二个参数往往是作为数组形式传入。
4、document.load 和 document.ready 的区别?
document.load 是当页面全部内容都加载完再执行;
document.ready 是当页面的DOM数渲染完就执行;
5、JS如何阻止事件冒泡和阻止默认事件?
- 阻止事件冒泡:e.stopPropagation()
- 阻止默认事件:e.preventDefault()
6、如何将 arguments 转化为数组?
// 方法一:
function toArr1 () {
var arr = [];
for (var i = 0; i < arguments.length; i ++){
arr.push(arguments[i]);
}
return arr;
}
console.log(toArr1('你好', 100)); // ["你好", 100]
// 方法二:
function toArr2 () {
return [].slice.call(arguments);
}
console.log(toArr2('你好', 200)); // ["你好", 200]
// 方法三:
function toArr3 () {
return Array.prototype.slice.apply(arguments);
}
console.log(toArr3('你好', 300)); // ["你好", 300]
// 方法四:es6
function toArr4 () {
return Array.from(arguments);
}
console.log(toArr4('你好', 400)); // ["你好", 400]
// 方法五:es6扩展运算符
function toArr5 () {
return [...arguments];
}
console.log(toArr5('你好', 500)); // ["你好", 500]
7、列举js跨域的方法,并对其原理加以解释?
同源策略:就是浏览器为了保证用户的信息安全,防止恶意网站窃取数据,禁止不同域之间的JS进行交互。
同源策略限制了以下行为:
① Cookie、LocalStorage 和 IndexDB 无法读取;
② DOM 无法获得;
③ AJAX 请求不能发送。
- jsonp 跨域:插入 script 标签,引入一个js文件,当js文件加载成功后,会执行callback对应的方法,并把我们需要的 jsonp 数据作为参数传入(需要后端配合)。
// 方法一:在 aja x函数中设置 dataType 为 'jsonp'
$.ajax({
dataType: 'jsonp',
url: 'http://www.a.com/user?id=123',
success: function (data) {
// 处理data数据
}
});
// 方法二:用 getJSON 来实现,只要在地址中加上 callback=? 参数即可
$.getJSON('http://www.a.com/user?id=123&callback=?', function (data) {
// 处理data数据
});
- 跨域资源共享 CORS:基本思想就是使用自定义的HTTP头让浏览器与服务器进行沟通,从而决定请求或响应是成功还是失败;服务器端对于CORS的支持,主要是通过设置 Access-Control-Allow-Origin 来进行的,如果浏览器检测到相应的设置,就可以允许 Ajax 进行跨域的访问。
// 指定允许其他域名访问
Access-Control-Allow-Origin:*
// 响应类型
Access-Control-Allow-Methods:GET,POST
// 响应头设置
Access-Control-Allow-Headers:x-requested-with,content-type
window.name:window 对象有个 name 属性,在一个窗口的生命周期内,窗口载入的所有页面无论是否同源都共享一个 window.name 并且都有读写的权限,name 的值只能是字符串的形式。
window.postMessage:H5为了解决跨域问题,引入了一个全新的API:跨文档通信 API(Cross-document messaging),这个API为 window 对象新增了一个 window.postMessage 方法,允许跨窗口通信,不论这两个窗口是否同源;有两个参数,第一个参数是要发送的数据,第二个参数是接收消息的域,用 onMessage 接收。
document.domain:两个网页一级域名相同,只是二级域名不同,浏览器允许通过设置 document.domain 共享 Cookie 或者处理 iframe。
document.domain = 'example.com';
// 现在,A网页通过脚本设置一个 Cookie
document.cookie = "test1=hello";
// B网页就可以读到这个 Cookie
var allCookie = document.cookie;
- WebSocket:是一种浏览器 API,在js创建了 websocket 之后,会有一个HTTP请求发送到浏览器以发起连接,在取得服务器响应后,建立的连接会使用HTTP协议交换为websocket协议,只有在支持 websocket 协议的服务器上才能正常工作。
var socket = new WebSockt('ws://www.baidu.com');//http->ws; https->wss
socket.send('hello WebSockt');
socket.onmessage = function(event){
var data = event.data;
}
8、排序
// 方法一:
function fnSort1 (arr) {
if (arr.length <= 1) {
return arr;
}
var pivotIndex = Math.floor(arr.length / 2);
var pivot = arr.splice(pivotIndex, 1)[0];
var left = [], right = [];
for (var i = 0; i < arr.length; i++) {
if (arr[i] <= pivot) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
return sort(left).concat([pivot], sort(right));
}
// 方法二:冒泡排序
function fnSort2 (arr) {
for (var i = 0; i < arr.length - 1; i++) {
for (var j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
var temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
return arr;
}
9、去重
// 方法一:
function deRepet1 (arr) {
arr.sort();
for (var i = 0; i < arr.length-1; i > 0; i--) {
if (arr[i] == arr[i - 1]) {
arr.splice(i, 1);
}
}
return arr;
}
// 方法二:es6
function deReprt2 (arr) {
return Array.from(new Set(arr));
}
// 方法三:
function deRepet3 (arr) {
var newArr = [];
for (var i = 0; i < arr.length; i++) {
if (newArr.indexOf(arr[i]) === -1) {
newArr.push(arr[i]);
}
}
return newArr;
}
// 方法四:双重遍历
function deRepet4 (arr) {
for (var i = 0; i < arr.length; i++) {
for (var j = i + 1; j < arr.length; j++) {
if (arr[i] === arr[j]) {
arr.splice(j, 1);
j--;
}
}
}
return arr;
}
// 方法五:for...of
function deRepet5 (arr) {
var result = [],obj = {};
for (var i of arr) {
if (!obj[i]) {
result.push(arr[i]);
obj[i] = 1;
}
}
return return;
}
10、Session 和 Cookie 的区别?
- cookie 数据存放在客户的浏览器上,session 数据放在服务器上。
- cookie 不是很安全,别人可以分析存放在本地的 cookie 并进行 cookie 欺骗,考虑到安全应当使用 session。
- session 会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,考虑到减轻服务器性能方面,应当使用 cookie。
- 单个 cookie 保存的数据不能超过 4K,很多浏览器都限制一个站点最多保存 20 个 cookie。
- 可以考虑将登陆信息等重要信息存放为 session,其他信息如果需要保留,可以放在 cookie 中。