1. computed和watch有什么区别
| computed | watch | |
|---|---|---|
| 缓存? | 支持缓存 | 不支持缓存 |
| 依赖? | 依赖于其他属性,当依赖的属性发生变化时才会重新计算 | 监听的数据必须是data中声明过或父组件传递过来的props中的数据 |
| 返回? | return返回值就是属性的属性值,在computed中,每个属性都有一个get和set方法 | 当数据变化时,就会触发监听器 |
| 异步? | 不支持异步 | 支持异步 |
| 总结 | 一个数据受多个数据影响 | 一个数据影响多个数据 |
2. v-for里面的key作用是什么?为什么不用index?
(1) key是虚拟dom对象的唯一标识,v-for循环时,根据数据生成虚拟dom,再根据虚拟dom生成真实的dom
(2) 当数据发生变化时,会生成新的虚拟dom,然后vue会将新的虚拟dom和初始虚拟dom进行对比,即:diff算法
(3) 如果新的虚拟dom中的key和初始虚拟dom中的key
一致:
- 如果虚拟dom的内容没变,就直接复用之前的真实dom
- 如果虚拟dom的内容变了,就生成新的dom节点
不一致:
- 就创建新的真实dom节点,随后渲染页面
为什么不用index?
- 当对数据进行逆向删除或者添加操作的时候,会产生没有必要的真实dom节点的更新,或者产生错误的dom更新,导致页面错误。
3. let和var和const区别
(1) var是函数作用域,let和const是块级作用域。
比如在for循环中用var声明一个变量,在for循环外也可以访问到,但let不可以,在外部访问不到。
(2) var存在变量提升,let、const不存在变量提升。
var可以先使用再声明,在变量没有被声明的时候,其值为undefined,这就是变量提升;但在使用let时,必须先声明再使用,否则会报错。
(3) var变量可以重复声明,let、const变量不可以重复声明。
(4) const用来定义常量,使用时必须初始化。
4. 了解跨域吗?上线遇到哪些跨域问题
跨域意味着违反同源策略,同源策略是指域名、端口、协议都相同。
常见的跨域方式有:
(1)jsonp
使用<script>标签的src属性获取其他源的数据,通过callback参数传递函数名,将数据返回来。
<script>
function getData(res){
console.log(res);
}
</script>
<script src="http://localhost:8080?callback=getData"></script>
问题:只能处理get请求。
(2)cors跨域资源共享
发送请求时,请求头中会自动添加Origin字段,服务器应该在响应头中添加:Access-Control-Allow-Origin:* 字段。
问题:数据不安全。
注意:在vue中常用proxy代理来解决跨域问题
a) 修改vue.config.js文件,在proxy中设置跨域
devServer: {
proxy: {
"/api": {
target: "目标服务器网址",
changOrigin:true, //允许跨域
pathRewrite:{ //重写路径
'^/api':''
}
},
},
},
b) 创建axios实例时,将baseUrl设置为’/api’
const requests = axios.create({
baseURL: "/api",
});
(3)使用webpack可以做代理,实现跨域
5. npm run dev与npm run serve 与npm run build区别
dev和serve的作用都是启动项目,使用哪个要查看package.json 中的script内容,如果是dev,则使用npm run dev,如果是serve,就使用npm run serve
build的作用是打包
6. Es6中的promise方法,链式调用?
Promise对象具有then,catch等方法,执行这些方法之后,也会返回一个promise对象,所以才会有链式写法。
then方法的两个参数都是函数,第一个参数是resolved状态的回调函数,第二个参数是rejected状态的回调函数。
也可以调用catch方法捕获错误,接收rejected状态的值
7. Promise中的all和race方法
Promise.all()可以将多个promise实例包装成一个新的promise实例,同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,且数组里面的数据顺序和传入的数组顺序是一致的,而失败的时候返回的是最先被reject的实例的值。
Promise.race()意思是说,哪个实例的结果获得的快,就返回哪个结果,不管结果本身是成功状态还是失败状态。
8. 组件中的data为什么是一个函数
vue中组件是用来复用的,为了防止多个组件实例对象之间共用一个data,所以要定义为函数。(类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据)
如果在组件实例中的data写成对象的话,会产生数据污染,因为object是引用数据类型,如果不用function返回,每个组件的data都是内存的同一个地址,一个数据改变了其他也就改变了
9. Js继承方法
(1) 原型链继承:将父类的实例作为子类的原型
function Parent() {
this.name = "parent";
}
function Son() {
this.age = 18;
}
Son.prototype = new Parent();
let s = new Son();
console.log(s, s.name, s.age);
缺点是:实例使用的是一个原型对象,内存空间共享,当一个发生变化的时候,另一个也随之发生变化。
(2) 构造函数继承:通过在子类构造函数中调用父类构造函数,借助call()方法
function Parent(name, sex) {
this.name = name;
this.sex = "男"
}
function Son(name) {
Parent.call(this, name);
this.age = 18;
}
let s = new Son("child");
console.log(s);
优点:对比原型链继承,子类构造函数可以传参,实例间不共享父类构造函数的引用属性
缺点:子类不能访问父类原型上的方法,即不能访问Parent.prototype上定义的方法
(3) 组合继承:使用原型链继承原型上的属性和方法,使用构造函数继承实例属性
function Parent() {
this.name = "parent";
}
function Child() {
Parent.call(this);
this.age = 18;
}
Child.prototype = new Parent();
let s = new Child();
console.log(s);
优点:父类的方法可以共用,父类构造函数的引用类型不会共享,子类构造函数可以传参。
(4) 原型式继承:使用object.create方法实现继承
这个方法接收两个参数,第一个参数是用作新对象原型的对象,第二个是为新对象定义额外属性的对象,第二个可选
let parent4 = {
name: "parent4",
friends: ["p1", "p2", "p3"],
getName: function () {
return this.name;
},
};
let person = Object.create(parent4);
console.log(person.name);
缺点是:多个实例的引用类型属性指向相同的内存,存在篡改的可能,子类不能向父类传参。
(5) Es6的extends方法继承
class Parent {
constructor() {
this.age = 18;
}
}
class Son extends Parent {
constructor() {
super();
this.name = "son";
}
}
let s = new Son();
console.log(s.name, s.age);
10. 路由守卫
路由守卫是路由在跳转前后过程中的一些钩子函数。路由守卫分为:全局守卫、组件内守卫、路由独享守卫。
(1)全局守卫:所有的路由切换都会执行,一般写在路由配置文件中。
router.beforeEach((to,from,next)=>{})
router.beforeResolve((to,from,next)=>{})
router.afterEach((to,from)=>{})
router.beforeEach(async (to, from, next) => {
let token = store.state.user.token;
let name = store.state.user.userInfo.name;
if (token) {
if (to.path == "/login") { //用户登录了,不能去login
next("/home");
} else {
if (name) { //用户登陆了,而且还有用户信息【去的并非是login】
next();
} else {
try {
await store.dispatch("getUserInfo");
next();
} catch (error) {
await store.dispatch("userLogout");
next("/login");
}
}
}
} else {
…………
}
});
(2)组件内守卫:写在组件中,即将渲染组件的时候触发
beforeRouterEnter(to,from,next){}
//在渲染该组件的对应路由时被comfire前调用,不能获取组件实例 this
beforeRouterUpdate(to,from,next){}
beforeRouterLeave(to,from,next){}
beforeRouteEnter (to, from, next) {
if(from.path=='/pay'){
next()
}else{
next(false)
}
}
(3)路由独享守卫:一般写在路由配置中
beforeEnter:(to,from,next)=>{}
11. 箭头函数和普通函数的区别
(1) 写法不同,箭头函数使用箭头定义,普通函数用function定义。
(2) 箭头函数都是匿名函数,而普通函数既可以是匿名函数,也可以是具名函数。
(3) 箭头函数不能作为构造函数来使用,也就是不能使用new,普通函数可以用作构造函数,以此来创建一个对象的实例。
(4) this指向不同,箭头函数没有this,它在声明的时候捕获上下文的this供自己使用,一旦确定不会再变化。在普通函数中,this指向调用自己的对象,如果用在构造函数,this指向创建的对象实例。普通函数可以使用call、apply、bind改变this的指向。
(5) 箭头函数没有arguments对象,每一个普通函数调用后都有一个arguments对象,用来存储实例传递的参数。
(6) 箭头函数没有原型对象,而普通函数有。
12. Vue组件通信方式
-
父组件 >>> 子组件 (props)
在子组件中通过props接收,子组件props里面接收的对象名与父组件的子组件绑定的名称一致。
-
子组件 >>> 父组件 ($emit)
在子组件通过this.$emit()方法绑定一个自定义事件,在父组件的子组件上绑定这个事件,通过v-on监听并接收参数
-
任意组件之间通信 (bus事件总线)
有两种导入方式,一种是全局导入,另一种是局部导入。全局引入时,在main.js里面将
$bus作为Vue实例的原型方法,能直接在各组件里面通过this.$bus方式调用。通过this.$bus.$emit()触发,通过this.$bus.$on()监听。 -
Vuex状态管理库
Vuex相当于一个仓库,可以将数据存储在仓库中,在main.js全局引入之后就可以直接使用了,可以使用
this.$store.state直接对state进行操作
13. Vue路由传参
Vue路由传参有两种方式:query 和 params
在地址栏中显示:
(1) query
<router-link to="/wrap?n=666&name=yqy">跳转到wrap</router-link>
获取:this.$route.query
(2) params
<router-link :to="/wrap/${n}/${name}">跳转到wrap</router-link>
//router配置
{
path: "/detail/:n/:name",
component: Detail,
},
获取:this.$route.params
14. 路由传递参数
第一种:字符串形式(传params参数keyword和query参数k)
this.$router.push( "/search/" + this.keyword + "?k=" + this.keyword.toUpperCase() );
第二种:模板字符串
this.$router.push(
`/search/${this.keyword}?k=${this.keyword.toUpperCase()}`
);
第三种:对象的写法
this.$router.push(
{
name: "search",
params: {
keyword: this.keyword,
},
query: {
k: this.keyword.toUpperCase(),
},
},
() => {},
() => {}
);
15. 路由传递参数(对象写法)path是否可以结合params参数一起使用?
答:不可以,路由跳转传参的时候,对象的写法可以是name或path形式,但是需要注意的是,path写法不能和params一起用。
16. 如何指定params参数可传可不传?
答:如果路由要求传递params参数,但是你没传,那么url就会有问题,指定params参数可传可不传:在配置路由的时候,在占位的后面加上一个问号【params可以传递也可以不传递】。
17. params参数可以传递也可以不传递,但是如果传递时空串,如何解决?
答:使用undefined解决:params参数可以传递、不传递(空的字符串)
this.$router.push({
name: "search",
params: {
keyword: "" || undefined,
},
query: {
k: this.keyword.toUpperCase(),
},
});
18. cookie和localStorage和sessionStorage的区别
(1)与服务器交互:
cookie 是网站为了标识用户身份而储存在用户本地终端上的数据,cookie始终会在同源 http 请求头中携带(即使不需要),在浏览器和服务器来回传递, sessionStorage 和 localStorage 不会自动把数据发给服务器,仅在本地保存
(2)存储大小:
cookie 数据根据不同浏览器限制,大小一般不能超过 4k,sessionStorage 和 localStorage 虽然也有存储大小的限制,但比 cookie 大得多,可以达到 5M 或者 更大
(3)有效时间:
localStorage 存储持久数据,浏览器关闭后数据不丢失除非主动删除数据, sessionStorage 数据在当前浏览器窗口关闭后自动删除,cookie 设置的 cookie过期时间之前一直有效,与浏览器是否关闭无关
19. 如果直接输入网站的网址怎么处理(未登录的情况下)
20. 怎么做到搜索的时候推荐这个网站
21. Seo?
22. Ajax与axios的区别
23.什么是MVC和MVVM模型
MVC 是一种分层开发思想,M 表示 Model,是数据库操作层,V 表示 View, 是页面视图层,C 表示 Controller,是业务处理层。Controller 层是 View 和 Model 层的纽带,当用户与页面产生交互的时候,Controller 中的事件触发器就开始工 作了,通过调用 Model 层,来完成对 Model 的修改,然后 Model 层再去通知 View 层更新。
MVVM 是前端视图层的分层开发思想,也是一种双向数据绑定的模式,主要 是把每个页面分成了 Model、View 和 ViewModel,其中 VM 是核心,数据的双 向绑定也是由 VM 提供的,用 viewModel 来建立起 model 数据层和 view 视图层 的连接,数据改变会影响视图,视图改变会影响数据
24.Vue双向数据绑定原理
Vue 双向数据绑定的原理主要通过数据劫持和发布订阅模式实现的,通过 Object.defineProperty()来劫持各个属性的 setter、getter,监听数据发生变化然后 通知订阅者 watcher,订阅者触发相应的监听回调
1、实现一个数据监听器 Observe,对 data 中的数据进行监听,若有变化,通知 相应的订阅者。
2、实现一个指令解析器 Compile,对于每个元素上的指令进行解析,根据指令 替换数据,更新视图。
3、实现一个 Watcher,用来连接 Observe 和 Compile, 并为每个属性绑定相应的 订阅者,当数据发生变化时,执行相应的回调函数,从而更新视图。
4、构造函数 (new Vue({}))
25.Vue生命周期
Vue 实例从创建到销毁的过程就是生命周期,也就是从开始创建、初始化数 据、编译模板、挂载 dom->渲染、更新->渲染、准备销毁、销毁一系列过程 Vue 生命周期常见的主要分为 4 大阶段 8 大钩子函数。
一、创建前/后
在 beforeCreate 生命周期函数执行的时候,data 和 method 还没有初始化。
在 create 生命周期函数执行的时候,data 和 method 初始化完成。
二、渲染前/后
在 beforeMount 生命周期函数执行的时候,已经编译好了模板字符串,但还没有真正渲染到页面中去【例{{msg}}】。
在 mount 生命周期函数执行的时候,已经渲染完,可以看到页面【mounted 是实例创建期间最后一个生命周期函数】。
三、数据更新前/后
在 beforeUpdate 生命周期函数执行的时候,已经可以拿到最新的数据,但还没渲染到视图中去。
在 update 生命周期函数执行的时候,已经把更新后的数据渲染到视图中去了。
四、销毁前/后
在 beforeDestory 生命周期函数执行的时候,实例进入准备销毁的阶段,此时 data、methods、指令等还是可用状态。
在 destoryed 生命周期函数执行的时候,实例已经完成销毁、此时 data、methods、指令等都不可用。
26.v-if和v-show有什么区别
v-if 是“真正”的条件渲染,在切换过程中会合适的销毁和重建内部的事件 监听和子组件,操作的实际上是 dom 元素的创建或销毁。
v-show 不管初始条件是什么,元素都会被渲染,并且只是简单的基于 CSS 进行切换,它操作的是 display:none/block 属性。
一般来说,v-if 有更高的切换开销,而 v-show 有的初始渲染开销,因此,如果需要频繁的切换,使用 v-show,如果运行时条件很少改变,则使用 v-if 较好。
27.async await是什么?它有哪些作用?
async 用于申明一个 function 是异步的,而 await 用于等待一个异步方法执行完成,它能很好的替代 promise 中的then。async 函数返回的是一个 Promise对象,可以使用 then 方法添加回调。当函数执行的时候,一旦遇到 await 就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。
28.常用的数组方法有哪些
- indexOf() 方法返回数组中某个指定的元素位置,如果不存在就返回-1【通常 用来判断数组中有没有这个元素】
- find() 方法返回数组中满足测试函数的第一个元素的值,否则返回undefined。
- findIndex() 方法返回满足测试函数的第一个元素的索引值,否则返回-1。includes() 方法用来判断一个数组是否包含一个指定的值,包含返回true,否则返回 false。
不改变原数组的:
- concat() 方法用于合并两个或多个数组,此方法不会更改现有数组,而是返 回一个新数组。
- join() 方法将一个数组的所有元素连接成一个字符串并返回这个字符串,如 果数组只有一个元素,那么将返回该元素而不使用分隔符。
- slice() 截取。
会改变原数组的:\
- pop() 方法从数组删除最后一个元素,并返回该元素的值,此方法更改数组的长度。
- push() 方法将一个或多个元素添加到数组的末尾,并返回该数组的新长度。
- shift() 方法从数组中删除第一个元素,并返回该元素的值,此方法会更改数组的长度。
- unshift() 方法将一个或多个元素添加到数组的开头,并返回该数组的新长度(该方法修改原有数组)。
- splice() 方法用于添加或删除数组中的元素,并以数组形式返回被修改的内容,如果删除一个元素,则返回一个元素的数组。如果未删除任何元素,则返回空数组。
- reverse() 方法用于颠倒数组中元素的顺序,并返回该数组,该方法会改变原数组。
- sort() 方法用于对数组的元素进行排序。排序顺序可以是字母或数字,并按 升序或降序,默认排序顺序为按字母升序。使用数字排序,必须通过一个函数 作为参数来调用。函数指定数字是按照升序还是降序排列。
var points = [40,100,1,5,25,10];
points.sort(function(a,b){return b-a}); //1,5,10,25,40,100
points.sort(function(a,b){return b-a});