文章目录
vue
说说你对vue的理解
- vue是什么
是一个用于创建用户界面的开源JavaScript框架,也是一个创建单页应用的web应用框架。旨在更好的组织与简化web开发。 - vue核心特性
数据驱动(MVVM)
- Model:模型层,负责处理业务逻辑和服务器端进行交互
- View:视图层,负责将数据模型转化为UI展示出来
- ViewModel:是用来连接model和view之间通信的桥梁
组件化:就是把图形、非图形的各种逻辑均抽象为一个统一的概念(组件)来实现开发的模式,在vue中每一个.vue文件都可以视为一个组件。
组件化优势: 降低整个系统的耦合度;调试方便;提高可维护性
指令系统 是当表达式的值改变时,将其产生的连带影响,响应式的作用于dom。
- vue跟传统开发的区别
用jquery来实现注册账号就是选择流程dom对象,点击按钮隐藏当前活动流程dom对象,显示下一个流程dom对象;而用vue只在视图层使用一变量控制dom节点显示与否,点击按钮则改变该变量。
总结 vue所有的界面事件,都是只去操作数据,jquery操作DOM;vue所有界面的变动,都是根据数据自动绑定出来的。 - vue和react对比
没有所谓的好与坏,只是使用场景不同。
- 数据流向不同,react使用单向数据流,vue使用双向数据流;
- 数据变化的实现原理不同,react使用的是不可变数据,vue是可变的数据;
- 组件化通信不同,react中我们通过使用回调函数来进行通信的,vue中子组件向父组件传递消息有事件和回调函数;
- diff算法不同,react主要使用diff队列保存需要跟新哪些DOM,得到patch树,再统一操作批量更新DOM,vue使用双向指针,边对比,边跟新DOM。
vue生命周期
单页面生命周期过程可分为创建、挂载、更新、销毁四个阶段
created: 可调用methods中的方法,访问和修改data数据触发响应式渲染dom,可通过computed和watch完成数据计算
父子组件生命周期执行顺序
加载渲染过程
父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted
更新过程
父beforeUpdate->子beforeUpdate->子updated->父updated
销毁过程
父beforeUpdate->子beforeUpdate->子updated->父updated
编程式路由导航
通过js代码来实现页面的跳转
(1)this.\$router.push(path)
想要导航到不同的 URL,可以使用 router.push 方法。这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,会回到之前的 URL。
(2)this.\$router.repalce(path)
用新路由替换当前路由(不可以返回当前路由界面)==>栈的方式
跟 router.push 很像,唯一的不同就是,它不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录。
vue里面路由传参的方式,平行组件通过路由怎么跳转
主要有两种方式:一是,使用编程式的导航;二是:使用声明式导航router-link
。
- 编程式导航
当你点击
<router-link>
时,这个方法会在内部调用,所以说,点击<router-link :to="...">
等同于调用router.push(...)
。
router.push(...)方法有两种方式传递参数,一种是params,另一种是query。
params是需要进行路由命名的,query是不需要路由命名的,直接通过路由地址就可以。
this.$router.push({name:'跳转路由'},params:{'参数':'参数Value'})
this.$router.push({path: '准去的路由地址'},query:{'参数':'参数Value'})
- 声明式导航
router-link
通过to属性指定目标地址,router-link
比<a href="...">
会好一些,理由如下:
vue-router导航守卫
vue-router 提供的导航钩子主要用来拦截导航,让它完成跳转或取消。
导航守卫分为: 全局的、单个路由独享的、组件内的三种。
-
全局路由
触发路由就会触发这些钩子函数
- router.beforeEach(to,from,next)
在路由跳转前触发,参数包括to,from,next(参数会单独介绍)三个,这个钩子作用主要是用于登录验证,也就是路由还没跳转提前告知,以免跳转了再通知就为时已晚。 - router.beforeResolve(to,from,next)
这个钩子和beforeEach类似,也是路由跳转前触发,参数也是to,from,next三个,和beforeEach区别官方解释为:在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。即在 beforeEach 和 组件内beforeRouteEnter 之后,afterEach之前调用。 - router.afterEach(to,from)
和beforeEach相反,他是在路由跳转完成后触发,参数包括to,from没有了next(参数会单独介绍),他发生在beforeEach和beforeResolve之后,beforeRouteEnter之前。
- router.beforeEach(to,from,next)
-
路由独享的
是指在单个路由配置的时候也可以设置的钩子函数;
beforeEnter(to,from,next)和beforeEach完全相同,如果都设置则在beforeEach之后紧随执行,参数to、from、next;
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
- 组件内的
是指在组件内执行的钩子函数,类似于组件内的生命周期,相当于为配置路由的组件添加的生命周期钩子函数。- beforeRouteEnter(进入该路由时执行)
- beforeRouteUpdate(该路由中参数改变时执行)
- beforeRouteLeave(离开该路由时执行)
<template>
<div>关于页面</div>
</template>
<script>
export default {
name: "about",
beforeRouteEnter(to, from, next) {
//进入该路由时执行
//不能获取当前实例this,因为当守卫执行前,组件实例还没被创建
},
beforeRouteUpdate(to, from, next) {
//该路由参数更新时执行
//可以获取当前实例this
},
beforeRouteLeave(to, from, next) {
//离开该路由时执行
//可以获取当前实例this
}
}
</script>
<style scoped>
</style>
Vue事件总线(EventBus)
主要是现实途径是在要相互通信的兄弟组件之中,都引入一个新的vue实例,然后通过分别调用这个实例的事件触发和监听来实现通信和参数传递。
B页面 向 A页面发送消息
// 发送消息
EventBus.$emit(channel: string, callback(payload1,…))
// 监听接收消息
EventBus.$on(channel: string, callback(payload1,…))
但是在使用eventbus时,会出现一些问题,如果在一个页面刷新了之后,与之相关的eventbus会被移除,这样就导致业务走不下去。通常会用到在vue页面销毁时,同事移除eventbus事件监听。
import {
eventBus
} from './event-bus.js'
EventBus.$off('事件名', callback)
//只移除这个回调的监听器
在vue层面,怎么控制页面的渲染性能
- 路由懒加载
懒加载是通过import()
引用的子模块会被单独分离出来,打包成一个单独的文件(chunk);借助函数来实现延迟执行子模块的加载代码
const router = new VueRouter({ routes: [
{ path: '/foo', component: () => import('./Foo.vue') }
]
})
- 第三方插件按需引入
像element-ui这样的第三方组件库可以按需引入避免体积太大。
import Vue from 'vue';
import { Button, Select } from 'element-ui';
Vue.use(Button) Vue.use(Select)
- 事件的销毁
Vue 组件销毁时,会自动解绑它的全部指令及事件监听器,但是仅限于组件本身的事件。
created() {
this.timer = setInterval(this.refresh, 2000)
},
beforeDestroy() {
clearInterval(this.timer)
}
双向数据绑定原理
- 实现mvvm的双向绑定,是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
v-show和v-if的区别
- v-if 是动态添加,当值为false 时,是完全移除该元素,即dom 树中不存在该元素。
- v-if 是在表达式返回值切换时,才渲染和摧毁组件及其子组件,他会触发组件的生命周期。
- v-show 仅是隐藏/ 显示,值为false 时,该元素依旧存在于dom 树中。
- v-show 是在页面初始化时就进行了完整的渲染,这会在某些情况下,明显增加页面的加载时间。
- 在需要频繁切换显示状态的组件或者相对渲染强度大但是过分依赖生命周期或组件,使用v-show
路由占位符
是将来通过路由规则匹配到的组件,将会被渲染到router-view所在的位置。
vuex
vuex是什么
为什么会出现vuex
当我们遇到多个组件共享状态时,单向数据流就会很容易受到破坏:
- 多个视图依赖同一状态;
- 来自不同视图需要变更同一状态。
代码会很难维护,我们可以吧共享状态抽取出来,以一个全局单例模式管理,然后通过定义和隔离状态管理中的各种概念并通过强制规则维持视图和状态之间的独立性,我们的代码就会变得更结构化和易维护。
综上所述,vuex就出现啦。
什么情况下使用vuex
当在开发大型页面应用时,我们就可以利用vuex管理共享状态。
vuex与单纯的全局对象的区别
每一个 Vuex 应用的核心就是
store(仓库)
。“store”
基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。Vuex
和单纯的全局对象有以下两点不同:
Vuex
的状态存储是响应式的。当 Vue 组件从store
中读取状态的时候,若store
中的状态发生变化,那么相应的组件也会相应地得到高效更新。- 你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit)
mutation
。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。
核心概念
- State:每个应用只包含一个
store
实例,因此单一状态树可以直接地定位到任一特定的状态片段; - getter:其返回值根据它的依赖缓存起来,只有依赖发生变化才会重新计算;
- mutation:更改state状态的唯一方法就是提交
mutation
,每个mutation
都有一个字符串的事件类型和一个回调函数,回调函数就是进行状态更改的地方;mutation
必须是同步函数,因为如果是异步函数,回调函数不知道什么时候实际被调用,实质上任何回调函数进行的状态变更是不可追踪的; - action:
action
提交的是mutation
,不能直接变更状态;但是可以包含任意的异步操作。store.dispatch
可以处理被触发的action
的处理函数返回的Promise
,并且store.dispatch
仍旧返回Promise
; - modules:每个模块拥有自己的
state、mutation、action、getter
、甚至是嵌套子模块。
总结
(1)vuex
用于做状态管理,主要是应用于vue.js中管理数据状态的一个库,即把项目中公用数据放到一个公用存储空间去存储,某一个组件改变这个公用数据,其他组件就能感知到。
(2)vuex
由统一的方法修改数据,全局变量可以任意修改。
(3)全局变量多了会造成命名污染,vuex
不会,同时解决了父组件与孙组件,以及兄弟组件之间通信的问题。
JavaScript
promise
如果promise没有调用resolve和reject函数,会造成内存泄漏吗?搞不懂,谁能帮忙解答一下
promise注意点:
- 一旦新建Promise就会立即执行,无法中途取消。
- 如果不设置回调函数callback,Promise内部抛出的错误,就不会反应到外部。
- Promise 新建后就会立即执行,并且调用resolve或reject后不会终结 Promise的参数函数的执行。
箭头函数中的this指向
- 一般情况下,this指向函数运行(调用)时所在的执行环境;
- 但是箭头函数根本没有自己的this,导致内部的this指向了外层代码的this,这个指向在定义时就已经确定而不会在调用时指向其执行环境的(变量)对象。因为箭头函数内部的this是指向外层代码块的this的,所以我们可以通过改变外层代码块的this的指向从而改变箭头函数中this的指向。
事件循环和任务队列
单线程循环是并发的一种形式,一个线程中只有一个事件循环。
任务队列是用来配合事件循环完成操作的,一个线程可以拥有多个任务队列。
- 任务队列: 所谓任务就是webapis返回的一个个通知,让js主线程在读取任务队列的时候得知这个异步任务已经完成,下一步该执行这个任务的回调函数了。主线程拥有多个任务队列,不同的任务队列用来排列来自不同任务源的任务。(像
setTimeout
、Promise
、DOM事件都是任务源,来自同类任务源的任务称为是同源的,setTimeout
和setInterval
就是同源的) 在ES6标准中任务队列分为宏任务队列和微任务队列。
-
函数调用栈:即执行栈
-
WebAPIs:浏览器的接口。比如一个Ajax操作,主线程会把收发Ajax交给浏览器的API,之后就继续做别的事情,浏览器在接收到Ajax返回的数据之后,会把一个Ajax完成的事件排到相应的任务队列后边。
-
任务队列们:主线程中有多个任务队列,同源的任务排在属于自己的任务队列。
-
ES6标准中任务队列存在两种类型,一种就是上边提到的一些队列,比如
setTimeout
、网络请求Ajax
、用户I\O等都属于宏观任务队列(macrotask queue),另一种是微观任务队列(microtask queue),Promise
就属于微观任务队列。
添加了微观任务队列之后事件循环有什么变化呢?
在执行栈执行的过程中会把属于微观任务队列的任务分配到相应的微观任务队列中去。而在调用栈执行空之后,主线程读取任务队列时,会先读取所有微观任务队列,然后读取一个宏观任务队列,再读取所有的微观任务队列。如图:
以上参考了
http://www.ruanyifeng.com/blog/2014/10/event-loop.html
http://www.jianshu.com/p/12b9f73c5a4f
https://www.zhihu.com/question/36972010
http://www.cnblogs.com/hity-tt/p/6733062.html
防抖节流是怎么实现的
防抖和节流面向的应用场景都是在短时间内大量触发浏览器事件的,然后会不断调用绑定在事件上的回调函数。这两个函数主要是优化高效率执行js代码,提高前端性能。
- 防抖是在事件被触发N秒后再执行回调,如果在这N秒内又被触发,就会重新计时。也就是说在规定时间内,只让最后一次生效,前面的不生效。需要注意清除上一次的计时器,然后再重新计时。
- 节流每隔一段时间,只执行一次函数,只有大于设定的执行周期后才能执行第二次函数。也就是说在规定时间内,只让函数触发的第一次生效,后面不生效。
async函数和promise的区别
async
函数是一种兼顾了基于Promise的实现和 「生成器」(也就是ES6的新特性Generator)的同步写法。
promise
对象的状态是不受外界影响的,而且状态一旦改变就不会再改变,任何时候都可以得到这个结果。生成器函数:
调用 Generator 函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象(迭代器对象)。- 下一步,必须调用遍历器对象的
next
方法,使得指针移向下一个状态。也就是说,Generator
函数是分段执行的,yield
表达式是暂停执行的标记,而next方法可以恢复执行。 - 如果不调用
next
的话生成器函数会在yield
处挂起,并且返回值是迭代器{value:..., done: true/false}
。这也是async
函数会在await
处挂起的基础。所以在async
中使用await
可以做到和yield
类似的效果,但是await
只能接受promise
对象和async
函数。
如果后台请求失败,async是怎么处理的
async和defer是怎么实现的
当浏览器在加载页面遇到async
属性的时候,script
就会立即下载和执行,同时继续加载页面,可以解决阻塞问题,但是无法确定async
下载的脚本是在页面加载之前还是之后执行。如果遇到defer
属性,就会立即下载,与此同时继续加载页面到结束,然后再执行下载的脚本。两个属性都只适用于外部脚本。
0.1+0.2!=0.3 怎么来减少精度的丢失?
javascript只有一个数字类型:number;采用的是双精度浮点数,所以转化为十进制数的时候就会不等于0.3。我们可以设置一个误差范围,JavaScript这个值通常为
2
−
52
2^{-52}
2−52。在ES6中,提供了Nubmer.EPSILON
属性,其值就是
2
−
52
2^{-52}
2−52。
css
媒体查询是怎么实现的
使用
@media
查询,你可以针对不同的媒体类型定义不同的样式。
@media
可以针对不同的屏幕尺寸设置不同的样式,特别是如果你需要设置设计响应式的页面,@media 是非常有用的。
当你重置浏览器大小的过程中,页面也会根据浏览器的宽度和高度重新渲染页面。
引入方式:
1. 通过link标签中的media属性来指定不同的媒体类型
<link rel="stylesheet" type="text/css" href="swordair.css" media="screen and (min-width: 400px)">
2.@import可以用来引入样式文件,同样也可以用来引用媒体类型。
@import url('./index.css') screen and (max-width: 480px);
3. media方式,@media引入媒体类型和@import有点类似
//在样式文件中引入
@media screen {
选择器{
样式代码
}
}
实现一个固定列宽,随着容器的宽度来自定义列数,同时屏幕两侧不留白。
flex布局和grid布局的区别,哪一个更好用
- flex布局是一种一维布局,只能处理一个维度上的布局,一行或者一列;适合做局部布局,例如导航栏组件
- grid布局是基于网格的二维布局,可以同时处理行和列,通常用于整个页面的规划
弹性盒从内容出发。一个使用弹性盒的理想情形是你有一组元素,希望它们能平均地分布在容器中。你让内容的大小决定每个元素占据多少空间。如果元素换到了新的一行,它们会根据新行的可用空间决定它们自己的大小。
网格则从布局入手。当使用CSS网格时,你先创建网格,然后再把元素放入网格中,或者让自动放置规则根据把元素按照网格排列。我们能创建根据内容改变大小的网格轨道,但整个轨道的大小都会随之改变。
当你使用弹性盒,并发现自己禁用了一些弹性特性,那你可能需要的是CSS网格布局。例如,你给一个弹性元素设置百分比宽度来使它和上一行的元素对齐。这种情况下,网格很可能是一个更好的选择。