Vue

Vue和React对比

React 和 Vue 有许多相似之处,它们都有使用 Virtual DOM

提供了响应式(Reactive)和组件化(Composable)的视图组件。

将注意力集中保持在核心库,而将其他功能如路由和全局状态管理交给相关的库。

React 比 Vue 有更丰富的生态系统

都有支持native的方案,React的RN,vue的Wee下

都支持SSR服务端渲染

都支持props进行父子组件间的通信

性能方面:React 和 Vue 在大部分常见场景下都能提供近似的性能。通常 Vue 会有少量优势,因为 Vue 的 Virtual DOM 实现相对更为轻量一些。

不同之处就是

1. 数据绑定方面,vue实现了数据的双向数据绑定,react数据流动是单向的

2.virtual DOM不一样,vue会跟踪每一个组件的依赖关系,不需要重新渲染整个组件树.

而对于React而言,每当应用的状态被改变时,全部组件都会重新渲染,所以react中会需要shouldComponentUpdate这个生命周期函数方法来进行控制

3.state对象在react应用中不可变的,需要使用setState方法更新状态;

在vue中,state对象不是必须的,数据由data属性在vue对象中管理(如果要操作直接this.xxx)

4.组件写法不一样, React推荐的做法是 JSX , 也就是把HTML和CSS全都写进JavaScript了,即'all in js'; Vue推荐的做法是webpack+vue-loader的单文件组件格式,即html,css,js写在同一个文件

vue2和vue3区别

  1. vue3 和 vue2 最大的区别就在于vue2使用的是optionsAPI 而 vue3使用的是compositionAPI

    OptionsAPI:可以让我们用包含多个选项的对象来描述组件的逻辑,比如data、methods、mounted,选项所定义的属性都会暴露在函数内部的this上,指向当前的组件实例。

    CompositionsAPI:我们可以使用导入的API函数来描述组件逻辑一般和<script setup>搭配使用,setup属性是一个表示,告诉VUE需要在编译时进行一些处理,在<script setup>中的导入和顶层变量或函数都能够在模板中直接使用

  2. vue2的响应式才用的是ES5的API Object.definePropert()对数据进行劫持 结合 发布订阅模式的方式来实现的。

    vue3 中使用了 es6 的 ProxyAPI 对数据代理。

    相比于vue2.x,使用proxy的优势如下

    defineProperty只能监听某个属性,不能对全对象监听

    可以省去for in、闭包等内容来提升效率(直接绑定整个对象即可)

    Object.defineProperty 无法监控到数组下标的变化,导致直接通过数组的下标给数组设置值,不能实施响应。
    而proxy可以监听数组,不用再去单独的对数组做特异性操作 vue3.x可以检测到数组内部数据的变化

  3. vue3中的组件可以有多个根节点

  4. 生命周期钩子
    Vue2--------------vue3
    beforeCreate  -> setup()
    created       -> setup()
    beforeMount   -> onBeforeMount
    mounted       -> onMounted
    beforeUpdate  -> onBeforeUpdate
    updated       -> onUpdated
    beforeDestroy -> onBeforeUnmount
    destroyed     -> onUnmounted
    activated     -> onActivated
    deactivated   -> onDeactivated

说说你对vue的理解

Vue是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建,并提供了一套声明式的、组件化的编程模型,帮助我们高效地开发用户界面。

他的数据传输是用了MVVM模式,M是数据层,V是视图层,VM是调度者。

官方对Vue的评价是 灵活、易用和高效,对于这几点我的看法是:

2. 易用性

  • vue提供数据响应式、声明式模板语法等,使我们只需要关注应用的核心业务即可,只要会写js、html和css就能轻松编写vue应用。

3. 灵活性

  • 由于是渐进式框架,所以我们可以由浅入深的开发不同大小类型的项目。
  • 如果是小应用,我们只需要使用vue核心特性即可完成功能。
  • 应用的不断扩大,我们能逐渐引入路由、状态管理、vue-cli等库和工具。
  • 不管是应用体积还是习难度都是一个逐渐增加的平稳曲线。

4. 高效性

  • 在vue2中引入的虚拟dom和diff算法,使得页面渲染性能表现更好。
  • 在vue3中还引入proxy对数据的响应式改进,编译器对静态内容编译的优化,都让性能更好更高效。

SPA 单页应用

单页面应用(SPA--------single page application),一个web项目只有一个页面(即一个HTML文件)刷新页面会请求一个HTML文件,切换页面的时候,并不会发起新的请求一个HTML文件,只是页面内容发生了变化。

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

vue.js原理:JS感知URL变化,当URL发生变化后,使用JS动态把当前的页面内容清除掉,再把下一个页面的内容挂载到页面上。此时的路由就不是后端来做了,而是前端来做,判断页面到底显示哪一个组件,再把以前的组件清除掉使用新的组件。就不会每一次跳转都请求HTML文件。

单页应用的优点:

1.分离了前后端的关注点,前端负责页面显示,后端负责数据储存和计算减轻了服务器的压力,不会让前后端的逻辑混淆

2.用户体验好,快,内容的改变不需要重新去加载

3.前端组件化,前端的开发不再以页面为单位,而是采用组件化的思想让代码的结构更加规范,有利于修改

单页应用的缺点:

1.首次加载耗时久,需要加载大量资源

2.对搜索引擎SEO不友好

解决方式:

1.按需加载,避免首屏加载过慢

2.配置好路由信息,通过记录浏览过的历史路由信息解决导航不可以问题

3.用#!代替#,因为谷歌会抓取带有#!的URL。我们可以解决ajax的不被搜索引擎抓取的问题

使用过 Vue SSR 吗?说说 SSR?

vue.js 是构建客户端应用程序的框架 在默认情况下,可以在浏览器输出Vue组件,进行生成DOM和操作DOM。

也可以将同一个组件渲染为 服务端的HTML 字符串 将他们直接发送给服务器,最后将这些静态标记激活为客户端上完全可以交互的应用程序。

SSR大致的意思就是vue在客户端将标签渲染成的整个 html 片段的工作在服务端完成,服务端形成的html 片段直接返回给客户端这个过程就叫做服务端渲染。

Vue单向数据流的理解

单项数据流 就是一个组件单方向的将数据流向它内部组件,也就是 父组件的数据流向子组件,但子组件不能更改父组件提供的数据,除非返回父组件中修改,再重新流向子组件,从而达到数据更新

Vue组件的data为什么要是函数

因为组件可能被用来创建多个实例,因为对象是引用类型,那么所有的实例都会去 共享引用一个数据对象。
为了保证组件的数据独立性要求每个组件必须通过 data 函数返回一个对象作为组件的状态

Vue 怎么重置 data

使用 Object.assign(),vm.$data可以获取当前状态下的data,vm.$options.data 可以获取到组件初始化状态下的 data。

Object.assign(this.$data, this.$options.data())

路由懒加载

在单页应用中,如果没有懒加载,运用 webpack 打包后的文件将会异常的大,造成进入首页时,需要加载的内容过多,延时过长,不利于用户体验,而运用懒加载则可以将页面进行划分,需要的时候加载页面,可以有效的分担首页所承担的加载压力,减少首页加载用时。

原理:vue 异步组件技术:异步加载,vue-router 配置路由 , 使用 vue 的异步组件技术 , 实现按需加载。

组件中写 name 选项有什么作用

① 项目使用 keep-alive 时,可搭配组件的 name 进行缓存过滤。
②DOM 做递归组件时需要调用自身 name
③vue-devtools 调试工具里显示的组件名称是由 vue 中组件 name 决定的

route 和 router

route 是“路由信息对象”,包括 path,params,hash,query,fullPath,matched,name 等路由信息参数。

router 是“路由实例对象”,包括了路由的跳转方法(push、go),钩子函数等。

Vue的修饰符

组件通信

父子组件之间

1.父组件给子组件传值:子组件通过props接收数据
2.子组件给父组件传值:子组件通过$emit方法传递函数,父组件通过函数接收v-on
或者通过ref给子组件设置名字,父组件通过$refs来得到子组件,子组件也可以通过$parent来获得父组件
再次还可以用 provider/inject 父组件用provider提供变量,子组件通过inject将变量注入组件;
3.兄弟组件传值:兄弟组件之间没有任何相互依赖的关系,想要进行数据传递就需要一个第三方。
可以通过① eventBus,建立事件中心
本质就是创建一个空的Vue实例来作为消息的传递对象;相当一个中转站,用于接收和传递事件;
②VueX来进行数据共享
4.跨层组件通信
① eventBus
如果知识涉及到简单的数据传递选择props,项目中需要保存状态选用Vuex

中央事件总线 bus

使用中央事件总线实际就是创建一个vue实例,利用这个vue实例来传递消息。

使用方式一:

// 定义一个bus文件
import Vue from "vue";
const bus = new Vue();
export default bus;
// 引入bus文件
import bus from "@/bus";
bus.$emit("myEvent", "bus msg")
// 引入bus文件
import bus from "@/bus";
bus.$on("myEvent", data => {console.log(data);
});

使用方式二:

// main.js
import Vue from 'vue'
import App from './App.vue'
​
Vue.prototype.$bus = new Vue();
​
new Vue({render: h => h(App),
}).$mount('#app')
​
// 发送事件
this.$bus.$emit("myEvent", "bus msg");
// 接收事件
this.$bus.$on("myEvent", data => {console.log(data);
})

你在需要的地方写一个 $on,然后在其他地方用 emit,这个 on 的回调就会被调用,有点像在其他地方调用某个组件内部的方法一样。

适用场景:适用于不是特别大的单页面应用跨级跨兄弟组件间通信

computed 、 methods 、 watch

computed 是计算属性,依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值

watch: 适用于一条数据影响多个数据的场景;例如搜索

watch更多的是观察的作用,类似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操作

methods: 函数调用,每使用一次,都需要重新加载,不需要缓存时用methods;性能开销较大,

总结

当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用computed 的缓存特性,避免每次获取值时,都要重新计算;

当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的


Vue为什么是''异步渲染''?

Vue是组件级的更新,一个组件可能涉及的数据非常的多,每次更新都会非常损耗性能,为了节约性能,Vue在更新DOM时是异步执行的。

只要监听到数据的变化,Vue就会开启一个队列,把同一事件循环中发生的所有数据变更都推入这个队列,如果同一个数据变更被多次触发,它也只会被推入队列中一次,这种缓冲行为可以去除重复数据的不必要计算和 DOM操作。


为什么要使用nextTick

当你在 Vue 中更改响应式状态时,最终的 DOM 更新并不是同步生效的,而是由 Vue 将它们缓存在一个队列中,直到下一个“tick”才一起执行。这样是为了确保每个组件无论发生多少状态改变,都仅执行一次更新。

为了在数据变化之后等待 Vue 完成更新 DOM,可以在数据变化之后立即使用 nextTick,该方法的 callback 会在 DOM 更新完成后被调用。

nextTick() 可以在状态改变后立即使用,以等待 DOM 更新完成。你可以传递一个回调函数作为参数,或者 await 返回的 Promise。


v-show 和 v-if

v-show的元素总是会被渲染,对于不显示的元素 也只是在行内设置了display:none的样式

v-if 是真正的条件渲染,他不会去渲染不显示的元素,除非条件为真。

一般情况下v-if的切换开销比较高,而v-show的初始渲染开销比较高

如果一个元素会频繁切换隐藏或者显示的时候 使用v-show,反之如果元素在运行时条件改变较少,就用v-if

v-if 和 v-for 的优先级

在VUE2中

v-for的优先级高于 v-if 
在渲染时,会先去循环渲染出列表,然后再去看列表item上的v-if 的情况;
如果两者同时出现,每次渲染的时候它会去遍历整个列表,造成性能的浪费
1.为了避免这个情况就需要把循环执行的变量替换为一个计算属性;然后让他返回过滤后的列表;把需要渲染的留下,不需要渲染的去掉 从而 v-if=""就不需要,只遍历了自己需要渲染的
2.可以把V-if 移动到父元素 或者把列表经过过滤后再循环
在VUE3中
v-if 的优先级高于 v-for
当它们同时存在于一个节点上时,v-if 比 v-for 的优先级更高。这意味着 v-if 的条件将无法访问到 v-for 作用域内定义的变量别名

Vue的生命周期

Vue实例从创建到销毁的过程就是生命周期;它总共分为8个阶段
beforeCreate
在实例初始化之后,数据观测 (data observer) 和
event/watcher 事件配置之前 被调用。
在这个事件中我们 获取不到data数据
data,watcher,methods都不存在这个阶段;只有$route这个对象
因此此阶段就可以根据路由信息进行重定向等操作。
常用于初始化非响应式的变量
created
在实例创建完成后触发,
可以访问data、methods等属性,
但是组件还未被挂载到页面 ,所以不能访问$el,且页面视图未出现,如果请求过多页
面会长时间处于白屏状态;
我们一般在这个函数中进行页面初始化的工作,
例如ajax请求数据来对页面进行初始化
beforeMount
在组件被挂载到页面之前调用,会找到对应template,
并翻译成render函数
mounted
在组件被挂载到页面之后触发,可以通过DOM
API来获取页面中的DOM元素;
el 被新创建的 vm.$el
替换,并挂载到实例上去,可以进行dom操作
beforeUpdate
在响应式数据更新时调用,发生在虚拟 DOM
打补丁之前,
此时我们可以对一些可能被移除的元素做一些操作,例如
移除事件监听器
updated
组件数据更新之后,发生虚拟DOM重新渲染和打补丁之
后进行调用
避免在这个钩子函数中操作数据,可能陷入死循环
beforeDestory
组件销毁前调用
做一些优化操作,一般在这一步 我们去销毁定时器
解绑全局组件
destoryed
组件销毁后调用,Vue实例中所有东西都会解除绑定
事件监听器会被移除,所有子实例会被销毁
activited
keep-alive专属,组件被激活时调用
deactivated
在被包裹组件停止使用时调用

守卫

导航守卫

to: Route : 即将要进入的目标 路由对象
from: Route : 当前导航正要离开的路由
next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数
确保 next
函数在任何给定的导航守卫中都被严格调用一次。它可以出现多于一次,但是只能在所有的逻辑
路径都不重叠的情况下,否则钩子永远都不会被解析或报错
全局守卫 触发路由就会触发这些钩子函数

  1. 全局前置守卫 在路由跳转前触发
    Router.beforeEach((to, form, next)=> { } )
  2. 全局解析守卫 与 全局前置守卫类似 在路由跳转前触发 区别是 在导航解析之前,同时在所有组件内守卫和路由组件被解析之后调用
    Router.beforeResolve((to, form, next)=> { } )
  3. 全局后置钩子 这个钩子不会接收next 函数 也不改变导航本身
    初始化时执行、每次路由切换后执行
    Router.afterEach((to, form )=>{ })

路由独享守卫

  1. beforeEnter :在路由跳转前触发
    和 beforeEach 完全相同,如果都设置则在beforeEach之后紧随执行
    可以在路由配置上直接定义beforeEnter守卫,和全局前置守卫的方法参数相同
  2. beforeLeave()

组件独享守卫

  1. BeforeRouterEnter(to, form, next) =>  {
             next(vm => { 通过vm访问组件实例})
    }
    在渲染组件的对应路由被确认前调用 在执行当前守卫之前 组件实例还未被创建 所以不能获取组件实例 this 在路由进入前调用 ,

    你可以通过传一个回调给 next来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。
    是支持给 next传递回调的唯一守卫

  2. BeforeRouterUpdate(to,from,next){ }
    路由改变 但是该组件被复用时调用,例如切换页面但是 该组件被复用 此时可以访问组件实例 this
     
  3. BeforeRouterLeave(to, form, next ) {

      const answer = window.confirm('你还没有保存!')
      if (answer) {
      next()
      } else { next(false) //这样取消

    }

在导航离开组件对应的路由时调用,可以访问组件实例this 这个离开守卫通常用来禁止用户在还未保存修改前突然离开。该导航可以通过 next(false) 来取消

总结:

全局路由钩子:beforeEach(to,from, next)、beforeResolve(to,from, next)、afterEach(to,from);
独享路由钩子:beforeEnter(to,from, next);beforeLeave()
组件内路由钩子:beforeRouteEnter(to,from, next)、beforeRouteUpdate(to,from, next)、beforeRouteLeave(to,from, next)
完整的导航解析过程
1. 导航被触发
2. 在失活的组件里调用beforeRouteLeave守卫
3. 调用全局的beforeEach守卫
4. 在复用组件调用beforeRouteUpdate守卫
5. 在路由配置里调用beforeEnter
6. 解析异步路由组件
7. 在被激活的组件里调用beforeRouterEnter
8. 调用全局的beforeResolve
9. 导航被确认
10. 调用全局afterEach钩子
11. 触发Dom更新
12. 调用beforeRoterEnter守卫中传递给next的回调函数,创建好的组件实例会作为 回调函数的参数传入

Key的作用和原理

key 这个特殊的 attribute 主要作为 Vue 的虚拟 DOM 算法提示,在比较新旧节点列表时用于识别 vnode。

在没有 key 的情况下,Vue 会尽可能地就地更新/复用相同类型的元素。如果传了 key,则将根据 key 的变化顺序来重新排列元素,并且将始终移除/销毁 key 已经不存在的元素。

同一个父元素下的子元素必须具有唯一的 key。重复的 key 将会导致渲染异常。

  1. 在v-for中使用key
    当我们使用v-for去更新已经渲染过的元素时,vue的行为就是原地复用元素,如果元素的位置改变,Vue就会重新进行渲染,它不能根据元素的顺序更换来渲染元素,做不到很好的复用

    所以Key的使用是为了提供一个排序的标识,在使用Key的时候Vue会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。

    diff算法主要是做同级比较,它会同时比较标签名和key,如果设置了key,当子节点位置发生变化时,vue 也能很迅速的定位到该节点,无需重新去创建、销毁。
  2. v-if中使用key
    Vue在渲染时会去尽可能的复用元素,而不是从头开始渲染,所以在使用v-if实现元素切换的时候,如果前后含有相同类型的元素 那么这个元素就会被复用,这时候key的作用就是用来标记独立的元素

    例如 在做不同登录方式的切换的时候,它的input将不会被替换,而只是替换他们的placeholder;这也导致了input内原本被输入的内容在切换后也不会被清除;

虚拟Dom

const vnode = {type: 'div',props: {id: 'hello'},children: [/* 更多 vnode */]
}
  虚拟DOM本质上是一个js对象,通过对象来表示真实的DOM结构。type用来描述标签,props用来描述属性,children用来表示嵌套的层级关系。该对象包含了真实DOM的结构及其属性,目的是为了 减少频繁操作DOM而引起回流重绘所引发的性能问题
虚拟 DOM 带来的主要收益是它让开发者能够灵活、声明式地创建、检查和组合所需 UI 的结构,同时只需把具体的 DOM 操作留给渲染器去处理。

Vue如何把组件挂载到页面上?

Vue Compiler 将SFC文件转换成一个JS模块,通过创建Vue实例来把组件模块变成一棵虚拟DOM树

一个运行时渲染器将会遍历整个虚拟 DOM 树,并据此构建真实的 DOM 树。这个过程被称为挂载 (mount)。

如果我们有两份虚拟 DOM 树,渲染器将会有比较地遍历它们,找出它们之间的区别,并应用这其中的变化到真实的 DOM 上。这个过程被称为更新 (patch),又被称为“比对”(diffing) 或“协调”(reconciliation)。

Diff算法

diff算法的特点是同级对比,深度优先,当数据改变就会触发setter,通过dep.notify()来通知订阅者,订阅者就会调用patch方法来打补丁。

patch方法会用sameVnode()的方法来对比 同层虚拟节点是否为 同一类型 的节点(标签/key/如果是input 时 type是否相同/ 是否定义data/ 是否为注释节点)

如果不是同一类型节点,则直接把整个节点替换为新节点

如果是同一类型节点,则执行patchNode()方法进行比对

①首先先找到真实DOM称为el

当新节点有子节点而老节点没有的时候,则直接把新节点的子节点添加到el

当新节点没有子节点而老节点有的时候,则删除el的子节点

当新节点和老节点都有文本节点,且不相等时,将el中的文本节点设置为新节点中的文本节点

当新老节点都有子节点时,就会调用updateChildren()方法进行比对它们的子节点

updateChildren()的

是首尾指针对比法,新老节点的首尾各有一个指针,进行互相比较,一共有五种情况

1.oldS 与 newS 他会通过sameVnode()的方法来对比,

2.oldE 与 newE

3.oldE 与 new S

4. oldS 与 newE

5.当以上四种都匹配不到时,再把所有旧子节点的 key 做一个映射到旧节点下标的 key -> index 表,然后用新 vnode 的 key 去找出在旧节点中可以复用的位置。

不需要响应式的数据应该怎么处理?

// 方法一:将数据定义在data之外
data () {this.list1 = { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }this.list2 = { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }this.list3 = { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }this.list4 = { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }this.list5 = { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }return {}}// 方法二:Object.freeze()
data () {return {list1: Object.freeze({xxxxxxxxxxxxxxxxxxxxxxxx}),list2: Object.freeze({xxxxxxxxxxxxxxxxxxxxxxxx}),list3: Object.freeze({xxxxxxxxxxxxxxxxxxxxxxxx}),list4: Object.freeze({xxxxxxxxxxxxxxxxxxxxxxxx}),list5: Object.freeze({xxxxxxxxxxxxxxxxxxxxxxxx}),}}

Vue2.0 响应式数据的原理

整体思路是数据劫持+观察者模式

使用 Object.defineProperty 将属性进行劫持(只会劫持已经存在的属性),当页面使用对应属性时,每个属性都拥有自己的 dep 属性,存放他所依赖的 watcher(依赖收集),当属性变化后会通知自己对应的 watcher 去更新(派发更新)。

数组则是通过重写数组方法来实现。

双向绑定

Vue通过双向绑定来实现了View和Model的同步更新,vue的双向绑定主要是通过
数据劫持和结合发布者-订阅者模式的方式实现的
数据劫持:通过object.defineProperty()来劫持对象各个属性的getter和setter
发布者-订阅者模式:在数据变动的时候 发布者 发布消息给订阅者,触发相应监听回调
总结:
当把一个普通js对象传递给Vue实例作为它的data选项,Vue会遍历它的属性,用Object.defineProperty()来监听他们 的getter和setter 从而让Vue追踪依赖,在对象属性被访问(get)和修改(set)的时候通知变化
具体步骤:
1.需要Observe的数据对象进行递归遍历,包括子属性对象的属性,都加上getter和setter,当给这个对象赋值的时候,就会触发setter,就能监听数据的变化
2.Compile解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图;并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据变动;收到通知;更新视图
3.Watcher订阅者是Observe和Complie通信的桥梁
它主要是在自身实例化时往属性订阅器里添加自己,他自身必须有一个update(),当属性变动,dep.notifiy()通知,就调用自己的update()方法触发Complie中绑定的回调
4.MVVM作为数据绑定的入口,整合Observe,Compile、Watcher三者;通过Observe来监听
Model数据的变化,通过Compile来编译模板指令,最终通过Watcher搭建Oberve和Compile之
间的通信桥梁,达到数据变化、视图更新,视图交互,数据Model变更的双向绑定效果
数据劫持:object.defineProperty()来对Model数据各个属性添加访问器属性,从而实现数据劫持;
当Model中的数据发生变化,通setter和getter方法实现对View层数据的更新;
数据在html模板中;有两种绑定的情况
1.v-model对value的值进行绑定 2.作为文本绑定;

Vue 2.0 如何检测数组变化

数组考虑性能原因没有用 defineProperty 对数组的每一项进行拦截,而是选择对 7 种数组(push,shift,pop,splice,unshift,sort,reverse)方法进行重写(AOP 切片思想)

所以在 Vue 中修改数组的索引和长度是无法监控到的。需要通过以上 7 种变异方法修改数组才会触发数组对应的 watcher 进行更新

原因:

Vue2 是用 Object.defineProperty 重写 data 的各个属性,只有 data 的属性发生变化才会触发视图更新,数组内部的变化,不会让数组本身变化,数组或对象 就像一个箱子,你往里面放东西,或者换掉里面的东西,箱子本身还是不变的,只是箱子里面变了,所以箱子还是那个箱子,除非你再造一个出来。

但是 vue3 就不会,因为 vue3 是 proxy,他天生就可以监听内部变化。

Proxy 相比于 defineProperty 的优势

Vue3.0 摒弃了 Object.defineProperty,改为基于 Proxy 的观察者机制探索。

首先说一下 Object.defineProperty 的缺点:

①Object.defineProperty 无法监控到数组下标的变化,导致直接通过数组的下标给数组设置值,不能实施响应。
②Object.defineProperty 只能劫持对象的属性,因此我们需要对每个对象的每个属性进行遍历。

Vue2.X 里,是通过递归 + 遍历 data 对象来实现对数据的监控的,如果属性值也是对象那么需要深度遍历,显然如果能劫持一个完整的对象才是更好的选择。

而要取代它的 Proxy 有以下两个优点
可以劫持整个对象,并返回一个新对象。
有多种劫持操作(13 种)
补充:
Proxy 是 ES6 新增的一个属性,翻译过来的意思就是代理,用在这里表示由它来“代理”某些操作。Proxy 让我们能够以简洁易懂的方式控制外部对象的访问,其功能非常类似于设计模式中的代理模式。
Proxy 可以理解为,在目标对象之前设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
使用 Proxy 的核心优点是可以交由它来处理一些非核心逻辑(如:读取或设置对象的某些属性前记录日志;设置对象的某些属性值前,需要验证;某些属性的访问控制等)。从而可以让对象只需要关注核心逻辑,达到关注点分离,降低对象复杂度等目的。

vue-cli 替我们做了哪些工作

vue-cli 是基于 Vue.js 进行快速开发的完整系统,也可以理解成是很多 npm 包的集合。

vue-cli 完成的功能:

.vue 文件 --> .js 文件
ES6 语法 --> ES5 语法
Sass,Less,Stylus --> CSS
对 jpg,png,font 等静态资源的处理
热更新
定义环境变量,区分 dev 和 production 模式
如果开发者需要补充或修改默认设置,需要在 package.json 同级下新建一个 vue.config.js 文件

keep-alive

它主要用于控制组件是否需要被缓存,如果你需要在组件切换的时候,保存一些组件的状态防止多次渲染,就可以使用 keep-alive 包裹需要保存的组件。
答:它主要用于控制组件是否需要被缓存,它包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。它有两个生命周期actived和deactived
  1. include包含的组件只有匹配的组件会被保存
  2. exclude排除的组件任何匹配的组件都不会被保存
  3. max 缓存组件的最⼤值

Vue.extend 作用和原理

官方解释:Vue.extend 使用基础 Vue 构造器,创建一个“子类”。参数是一个包含组件选项的对象。

其实就是一个子类构造器 是 Vue 组件的核心 api 实现思路就是使用原型继承的方法返回了 Vue 的子类 并且利用 mergeOptions 把传入组件的 options 和父类的 options 进行了合并

Vue自定义指令

• vue允许我们去注册自定义指令,可以通过vue.directive来全局注册或局部注册,一个指令能够提供几个钩子函数,例如bind和update、 inserted ;
  1. bind会在指令第一次绑定到函数上时调用,可以做一些初始化的设置,update则是在组件更新时调用,例 如对dom进行一些操作;
  2. inserted :被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。

钩子函数有几个参数,较为常用的是el和binding ;

el存储指令绑定的元素,可以用来直接操作dom,binding里的name,代表指令名,value是指令绑定的值;

Update 有oldvalue是绑定的前一个指令的值; 我们可以在绑定指令的时候给他传值

1. bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。

2. inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。

3. update:被绑定于元素所在的模板更新时调用,而无论绑定值是否变化。通过比较更新前后的绑定值,可以忽略不必要的模板更新。

4. componentUpdated:被绑定元素所在模板完成一次更新周期时调用。

5. unbind:只调用一次,指令与元素解绑时调用。

v-for v-if 此类就是内置的指令;
我们可以创建一个directives的文件夹 然后建立js文件,在需要的地方引入指令js文件
要在directives里注册它;
directive文件夹里的js文件详情
export default {bind: function (el, binding) {console.log('bind成功');const options = binding.value;const { className, activeClass,currentIndex } = optionsconst children = el.getElementsByClassName(className)children[currentIndex].className += ` ${activeClass}`// el 自定义指令所在元素 binding name子元素 class},update: function (el, binding) {//binding里有一个oldValueconst options = binding.value;const oldOptions = binding.oldValue;//上一次操作的const { className, activeClass, currentIndex } = optionsconst children = el.getElementsByClassName(className)const { currentIndex: oldCurIndex } = oldOptionschildren[currentIndex].className += ` ${activeClass}`//让上一个变成原本的children[oldCurIndex].className = classNameconsole.log('操作item触发update');}
<template><div class="home"> hi im home<div class="nav-bar" v-nav-active="{ className:'nav-item', //找他的子元素activeClass:'nav-active', //要绑定的类名currentIndex, }"><div @click="change(index)" class="nav-item" v-for="(item, index) initems" :key="index"> {{ item }}</div></div></div>
</template><script>import NavActive from "../directives/navActive";export default {name: "Home",directives: {NavActive,},methods: {change(index) {this.currentIndex = index}},data() {return {currentIndex: 0,items: ["1", "2", "3"],};},components: {},};
</script>
<style lang="scss">.nav-bar {display: flex;margin-top: 20px;}.nav-item {color: white;width: 100px;height: 40px;line-height: 40px;background-color: black;}.nav-active {background-color: white;color: black;border: 1px solid black;box-sizing: border-box;}
</style>

VueX

VueX是一个专门为vue.js 程序开发的状态管理模式  它采用集中式 储存管理 所有引用的组件的状态

VueX的作用

在项目中可以用vueX来存放数据,可以实现多组件、多页面间的数据共享;解决了组件之间统一状态的共 享问题,实现了组件之间的数据持久化。

什么情况下去使用VueX

我们一般会在嵌套较为复杂的组件或者组件之间传参,参数可能在多个地方被改变的情况下去使用VueX,这样我们能够保证数据的单向性,避免数据流混乱,增加代码的可维护性。
如果应用足够简单,就不要去使用vuex,但如果要去构建中大型的单页面应用,想要更好的管理组件外部状 态就去使用Vuex

不使用Vuex的坏处

  1. 代码可维护性下降
  2. 代码可读性下降
  3. 耦合度增加

VueX的属性

  1. state:Vuex是一个用于存放很多对象的仓库,state就是数据的存放地,对应Vue对象里的data,state里存放的数据是响应式的
  2. getters:用于获得需要计算state后得到的值,实现简单的数据转换;接收state作为第一个参数,可以在多个组件之间复用
  3. mutations:更改state的值的方法集合
  4. actions:通常在这发送异步请求;通过调用mutations修改state的值,类似mutations但是不是直接改变状态;利用 context.commit 提交一个 mutation,
  5. modules:当应用变得非常复杂时,store 对象就有可能变得相当臃肿。为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、避免冲突以便维护
action和mutaion的区别 ?
  • mutation 是同步更新, $watch 严格模式下会报错,mutation主要是用于修改state的值的方法的集合,可以通过commit调用
  • action 是异步操作,使用dispatch调用,之后可以获取数据后调用 mutation 提交最终数据

首屏加载优化

① 把不常改变的库放到 index.html 中,通过 cdn 引入

②vue 路由懒加载

③ 不生成 map 文件,找到 config/index.js 文件,修改为 productionSourcceMap:false

④vue 组件尽量不要全局引入

⑤ 使用更轻量级的工具库

⑥ 开启 gzip 压缩:这个优化是两方面的,前端将文件打包成.gz 文件,然后通过 nginx 的配置,让浏览器直接解析.gz 文件。

⑦ 首页单独做服务端渲染:如果首页真的有瓶颈,可以考虑用 node 单独做服务端渲染,而下面的子页面仍用 spa 单页的方式交互。这里不推荐直接用 nuxt.js 服务端渲染方案,因为这样一来增加了学习成本,二来服务端的维护成本也会上升,有时在本机测试没问题,在服务端跑就有问题,为了省心,还是最大限度的使用静态页面较好。

图片懒加载

IntersectionObserver 它是浏览器用来监听元素是否进入了设备的可视区域之内,而不需要频繁的计算来做这个判断API

IntersectionObserver支持两个参数:

  1. callback是当被监听元素的可见性变化时,触发的回调函数

  2. options是一个配置参数,可选,有默认的属性值

IntersectionObserver.observe() 方法向 IntersectionObserver 对象监听的目标集合添加一个元素。一个监听者有一组阈值和一个根, 但是可以监视多个目标元素,以查看这些目标元素可见区域的变化。

调用IntersectionObserver.unobserve()方法可以停止观察元素。

步骤

1.得到所有的Images数组集合

2.创建 obverser = new IntersectionObserver(callback)

3.Images数组遍历item  通过IntersectionObserver.observer( item )观察元素

4.

Mixin

在日常的开发中,我们经常会遇到在不同的组件中经常会需要用到一些相同或者相似的代码,这些代码的功能相对独立,可以通过 Vue 的 mixin 功能抽离公共的业务逻辑,原理类似“对象的继承”,当组件初始化时会调用 mergeOptions 方法进行合并,采用策略模式针对不同的属性进行合并。当组件和混入对象含有同名选项时,这些选项将以恰当的方式进行“合并”。

与vuex的区别

vuex:用来做状态管理的,里面定义的变量在每个组件中均可以使用和修改,在任一组件中修改此变量的值之后,其他组件中此变量的值也会随之修改
Mixins:可以定义共用的变量,在每个组件中使用,引入组件中之后,各个变量是相互独立的,值的修改在组件中不会相互影响

与公共组件的区别

组件:在父组件中引入组件,相当于在父组件中给出一片独立的空间供子
组件使用: 然后根据props来传值,但本质上两者是相对独立的
Mixins:则是在引入组件之后与组件中的对象和方法进行合并,相当于扩展了父组件的对象与方法,可以理解为形成了一个新的组件


JS

JS的基本数据类型

js有六种基本数据类型 :null undefined  number string boolean symbol

JS复杂数据类型

Object:Array,Function、RegExp、Date

总结

基本数据类型和复杂数据类型它们两者的不同主要是在存储的位置不一样。

基本数据类型存储在栈(stack)中,复杂数据类型存储在堆(heap)里,基本数据类型占据空间小,大小固定 且使用频繁;
复杂数据类型一般都是Obj类型,所有Array、Date等数据类型都可以理解为Obj的子类;
复杂数据类型 占据空间大,大小不固定,如果存储在栈中会影响程序性能;所以对于引用数据类型,栈中会存放他们的指针, 根据指针的地址指向堆中的实体;

null 和 undefined

undefined 代表不存在这个值,他是一个变量最原始的状态

null 代表存在这个值 ,但是这个值是空的



什么是DOM 、BOM

DOM( Document Object Model)(文档对象模型),把文档当做一个对象;DOM的最根本对象是document(window.docum ent)这个对象主要定义了处理网页内容的接口和方法
Bom(browser object model)
浏览器对象模型,把浏览器当做一个对象,BOM的最核心对象是window对象,这个对象既是js访问浏览 器的一个接口,又是一个全局对象,这意味着网页中定义的任意对象、方法和属性是全局对象的一个方法或 者属性;Window对象里有location、screen对象的子对象;且DOM的document对象也是BOM
window对象的子对象;

ES6的新特性你知道哪些?

1.let、const
在es6之前我们定义变量使用的都是var,var没有块级作用域的概念,变量会被提升;ES6中新增的 let和 const解决了这个问题;
let声明的变量可以重新赋值,但是不能在同一个作用域里重复声明;
const声明的变量必须赋值初始化,不能在同一个作用域里重复声明,且无法重新赋值

如何在ES5使用const

function const ( key , value ){const desc = {value ,writable : false}Object.defineProperty( window , key , desc )
}const ( 'obj' ,{ a : 1 })
obj = {} //重新赋值不生效


闭包

什么是闭包?

一个可以访问其他函数内部变量的函数就是闭包,最常见创建闭包的方法就是在一个函数内部创建另一个函数

闭包的作用?

通过使用闭包我们可以从外部访问函数内部的变量,可以让已经运行结束的函数上下文中的变量继续存在于内存,让它不会被回收
闭包的缺点?
内存不能被释放

this对象

js中this的指向不是在函数定义的时候就确定的,而是在函数调用的时候才确定的,也就是说函数的调用方式决定了this的指向;
1.this总是指向函数的直接调用者
2.如果有new关键字;this指向new出来的对象
3.在事件中 this指向触发这个事件的对象,例如按钮click就指向按钮,在IE中的attachEvent
this指向window全局

箭头函数的特点

1.箭头表达式的语法比函数表达式更加简洁,且没有自己的this、arguements、super、或new.ta
rget
2.箭头函数永远指向当前调用的对象

Object.is 和 === 的区别

Object.is在严格等于上的基础修复了一些特殊情况下的错误,比如NaN 不等于 NaN

== (全等)和 ===(严等) 的区别

== 会进行隐式转换,将两个比较的值 转换为相同类型再去比较值是否相等
.
=== 不会进行隐式转换,会去比较 它们的类型以及值

typeof 和 instanceof

typeof 会返回一个值的类型  对于基本数据类型;除了null会返回object,其余都会返回本身类型

而对于引用类型来说,除了函数之外都返回object;
instanceof:判断对象是否是另一个对象的实例;只能用来判断对象;它会基于原型链进行查询,如果查询
结果在原型链中,就会返回true
如果我们想判断一个对象的正确类型,这时候可以考虑使用 instanceof ,因为内部机制是通过原型链来判断的

Object.prototype.toString.call(检测数据类型最佳方案)

对于 Object.prototype.toString() 方法,会返回一个形如 "[object XXX]" 的字符串。

如果对象的 toString() 方法未被重写,就会返回如上面形式的字符串。

({}).toString();     // => "[object Object]"
Math.toString();     // => "[object Math]"

但是,大多数对象,toString() 方法都是重写了的,这时,需要用 call()来调用

1.判断基本类型:

Object.prototype.toString.call(null);//”[object Null]”
Object.prototype.toString.call(undefined);//”[object Undefined]”
Object.prototype.toString.call(“abc”);//”[object String]”
Object.prototype.toString.call(123);//”[object Number]”
Object.prototype.toString.call(true);//”[object Boolean]”

2.判断原生引用类型:

函数类型

Function fn(){console.log(“test”);}
Object.prototype.toString.call(fn);//”[object Function]”

日期类型

var date = new Date();
Object.prototype.toString.call(date);//”[object Date]”

数组类型

var arr = [1,2,3];
Object.prototype.toString.call(arr);//”[object Array]”

正则表达式

var reg = /[hbc]at/gi;
Object.prototype.toString.call(arr);//”[object Array]”

自定义类型

function Person(name, age) {this.name = name;this.age = age;
}
var person = new Person("Rose", 18);
Object.prototype.toString.call(arr); //”[object Object]”

很明显

这种方法不能准确判断person是Person类的实例,而只能用instanceof 操作符来进行判断,如下所示:

console.log(person instanceof Person);//输出结果为true

3.判断原生JSON对象:

var isNativeJSON = window.JSON && Object.prototype.toString.call(JSON);
console.log(isNativeJSON);//输出结果为”[object JSON]”说明JSON是原生的,否则不是;

Promise

什么是promise ?

promise是ES6提出的异步编程解决方案,相对于传统容易陷入回调地狱的异步回调方案来说,promise会让异步操作更加的优雅;

首先Promise他是一个构造函数 需要通过new关键字来生成一个Promise实例对象

他接收一个函数作为参数,函数中的代码会在new Promise的时候立刻执行,作为参数的函数有两个默认的参数 resolve 和 reject,这两个参数是函数标记异步执行的状态

当异步操作完成 我们可以调用resolve函数

当异步操作失败 我们可以调用reject函数

这些标记的状态可以在then和catch中接收;.then代表异步完成的回调,.catch是异步失败的回调

1. Promise他本身是同步执行的,

输出结果为: 1 ,2 (如果是异步,输出结果应该为 2 , 1)

let a = new Promise( (res, rej) => {
console.log(1);
});
console.log(2);

2. promise的回调 .then 和.catch 是异步的

输出结果为 1,2, 3  因为是异步 所以先打印2

let a = new Promise((res, rej) => {console.log(1);res(3)
});
//异步
a.then((res) => {console.log(res);
});
console.log(2);
promise的原理
//Promise内部有一个状态管理器的存在;一个promise实例有三种状态;pending,fulfilded,rejected;分别代表进行中、已成功以及以失败;
(1)promise的初始化状态是pending
(2)当调用成功resolve 会由pending变成fulfiled
(3)当调用失败会由pending => rejected
如何让promise 停止?
工作中什么时候会用到promise?

原型和原型链

1.原型

每个构造函数都有一个prototype属性,这个属性指向一个对象(prototype/实例原型/冰条模具),这个对象内包括该构造函数所有公用的方法和属性,这个对象就是调用该构造函数创建的实例原型,每个原型对象都有一个constructor属性,这个属性会指向关联的构造函数。

通过构造函数实例化出来的实例对象有一个_proto_ 可以访问实例原型,实例对象在创建的时候会默认关联原型,并从原型上继承属性

2.原型链

当我们想去访问实例对象的属性时,实例对象却没有这个属性,JS只能去该实例对象的实例原型上寻找该属性,如果实例原型上依旧找不到,就会去原型的原型上找,直到找到Object为止,这个行为将形成一个链条,该链条就叫做原型链。


bind/call/apply

call和apply方法都是为了改变this的指向,作用相同,传参的方式不同;

call可以接受一个参数列表,而apply只能接受一个参数数组

bind也接收一个参数列表,和call以及apply一样 它的作用是为了改变this的指向,只是该方法会返回一个函数;通过bind实现柯里化


Event Loop

js最大的特点就是单线程 ,在同一时间就只能做同一件事,

这也意味着 任务是需要排队的。只有前一个任务完成了,才能去执行下一个任务。

所以任务也被分为了两种, 同步任务和异步任务(宏任务)  /(微任务)

同步任务是在主线程执行,形成一个执行栈,只有前一个任务完成了,才能去执行下一个任务。

而异步任务不进入主线程,而是先在任务队列中放置

只有执行栈中所有同步任务被执行,才会去读取任务队列,把异步任务拉入执行栈执行

这个过程被不断的重复

在清空了调用栈,也就是所有的同步任务后,
首先会先去执行微任务队列的任务;
微任务执行完才会去执行宏任务
宏任务(消息队列):
分类:setTimeout setInterval requrestAnimationFrame 事件的回调函数,例如onclike
1.第一个宏任务队列只有一个任务:执行主线程的js代码
2.宏任务队列可以有多个
微任务:
分类:new Promise()、.then(回调)、 process.nextTick 、.finally( )、 . catch() 、
MutationObserver Object.observe
t1.只有一个微任务队列
2.在上一个宏任务队列执行完毕后,如果有微任务队列就会执行微任务队列
把回调函数放进evenq(事件队列)然后根据轮询 放入主线程指向
流程
1.主线程执行完毕;调用栈被清空,
2.事件循环优先去寻找微任务队列中的任务;
3.直到微任务队列中的任务被清空才会去执行下一轮宏任务


get和post的区别

1.在应用场景上, get是一个幂等的请求 ,一般用于不会对服务器造成影响的场景,例如请求一个页面;

post是一个非幂等的请求,一般用于会对服务器造成影响的场景;例如注册账号;所以浏览器一般会对get请求缓存,而很少对post请求进行缓存

2.从发送报文的格式来说,Get请求中实体部分为空,而post请求中,报文实体部分一般是向服务器发送的数据;

3.GET请求也可以将参数放在URL中向服务器发送,但是这种方法不是很安全,因为请求的URL会保存

在历史记录中,且浏览器对URL会有长度上的限制;会影响GET请求发送数据的长度;相比之下POST请求更加安全且参数的传递支持更多的数据类型;


深拷贝和浅拷贝

浅拷贝:对于基本数据类型 直接将值赋值给新对象,对于引用数据类型,只复制内存地址而非对象本身,新旧对象共享同一内存,改变其中一个对另一个也有影响

深拷贝:对于基本数据类型 直接将值赋值给新对象,对于引用数据类型,深拷贝会新建一个对象空间 然后拷贝里面的内容,他们指向不同的内存空间 改变其中一个对另一个没有影响

如何实现浅拷贝:

//对象浅拷贝

1. let obj2 = Object.assign ({}, obj )
2. let obj2 = { ... obj }
//数组浅拷贝
let arr = [ 1 , 2 , { a : 3 }]
1. let arr2 = arr . slice ()
2. let arr2 = arr . concat ()
3. let arr2 = [ ... arr ]

如何实现深拷贝

1. 递归去复制所有层级的属性

2. JSON对象的parse和stringify

function deepClone ( obj ){
let _obj = JSON.stringify ( obj ),
objClone = JSON.parse ( _obj );
return objClone
}
let a = [ 0 , 1 ,[ 2 , 3 ], 4 ],
b = deepClone ( a );
a [ 0 ] = 1 ;
a [ 2 ][ 0 ] = 1 ;
console . log ( a , b );

手写函数

要求手写:转换驼峰的函数、冒泡排序、翻转数组、数组去重、求数组最大值和最小值、forEach、map、filter、reduce的实现、伪数组转换真数组、函数节流和函数防抖、数组扁平化处理

节流函数和防抖函数

1.节流函数:一段时间内只执行一次某个操作,过了这段时间 如果还有操作再继续执行新的操作

2.防抖函数:一段时间内只执行最后一次操作,

总结:防抖函数先清除定时器,再调用回调函数,节流函数先调用回调函数,再清除定时器

防抖函数

function debounce(fn, delay) {// 记录上一次定时器let timer = nullreturn () => {// 每次点击都清除上一个定时器 保证触发的是最后一次clearTimeout(timer)timer = setTimeout(() => {fn.apply(this)}, delay)}
}
let btn = document.querySelector('.btn')
console.log(btn);
btn.onclick = debounce(() => {console.log('debounce',);
}, 200)

节流函数

const box = document.querySelector('.box')
box.addEventListener('drag', throttle(() => {console.log('test');},2000))function throttle(fn, delay) {let timer = nullreturn () => {//如果有定时器 就直接返回if (timer) returntimer = setTimeout(() => {fn()timer = null}, delay)}
}
let btn = document.querySelector('.btn')btn.addEventListener('click', throttle1(function () {console.log(111);
}, 2000))function throttle1(fn, delay) {// 记录上一次触发的时间let lastTime = 0return function () {let nowTime = Date.now()if (nowTime - lastTime > delay) {// 如果现在时间-最后触发时间大于 规定触发时间delay,就调用需要被执行的函数;并且同步一下最后一次调用的时间fn.call(this)// 这里要call回自己否则可能出现this指向的问题lastTime = nowTime}}
}

转换驼峰的函数

let word = 'alice-love-u'
convertCamelCase('-', word)function convertCamelCase(symbol, str) {//根据- 来切割字符串 -> [alice,love,u]let arr = str.split(symbol)arr.map((item, index) => {// 如果是第一个就reurn if(index == 0) return  // 截取item的首字母 转换为大写 + 去除第一个字母的item // alice -> A + licearr[index] = item.charAt(0).toUpperCase() + item.slice(1)})// 把[alice,Love,U] -> aliceLoveUlet newStr = arr.join('')return newStr
}

冒泡排序

// 让 [ 5, 4, 3, 2, 1]按从小到大的顺序排列
// 外循环控制轮数(有几个数),内循环控制比较(每个数要换几次)let arr = [5, 4, 3, 2, 1,10]for (let i = 0; i < arr.length - 1; i++) {for (let j = 0; j < arr.length - 1 - i; j++) {let temp = arr[j]if (arr[j] > arr[j + 1]) {arr[j] = arr[j + 1]arr[j + 1] = temp}}
}
console.log(arr);

翻转数组 reverse

let arr = [6,5, 4, 3, 2, 1]
for (let i = 0; i < arr.length / 2; i++) {let temp = arr[i]arr[i] = arr[arr.length - i -1]arr[arr.length - i -1] = temp
}console.log(arr);// [1,2,3,4,5,6]

用reduce 实现forEach

// 1.用reduce 实现 foreachconst arr = [1,2,3,4,5,6]function forEeach(arr) {arr.reduce((pre,cur,index,arr) => {console.log(cur,index);},0)
//要设置初始值为0,否则第一个item不显示;
}forEeach(arr)

map

// 实现map
let double = item => item * 2function Map(arr, handle) {const mapArr = []for (let item of arr) {mapArr.push(handle(item))}console.log(mapArr);return mapArr
}
Map(arr,double)// 用reduce 实现maplet double = item => item * 2
function Map(arr, handle) {const mapArr = []arr.reduce((accumlator,item) {mapArr.push(handle(item))}console.log(mapArr);return mapArr
}
Map(arr,double)

filter

// 实现filter
let getDouble = item => item % 2 === 0
function filter(arr,handle) {let newArr = []for(let item of arr) {if(handle(item)){newArr.push(item)}}console.log(newArr);return newArr
}

小野森森 题

题目1

a 在进行对比的时候 会自动调用toString的方法 每次对比都会+1,

'1' == 1 , 但若是恒等(===) 则不成立


网络协议

解释一下什么是HTTP和HTTPS

HTTP是超文本传输协议(HyperText Transfer Protocol)就是一种发布和接收HTML页面的方法,用于Web浏览器和网络服务器之间传输信息,它默认工作在TCP协议80端口,HTTP协议以明文的方式发送内容,不提供任何方式的加密,所以如果被攻击者截取Web浏览器和网络服务器之间的传输报文,就可以直接读懂其中的讯息,因此HTTP不适合传输一些敏感信息,例如银行卡号以及密码等支付信息;

HTTPS 是超文本传输安全协议(Hypertext Transfer Protocol Secure)是一种透过计算机网络进行安全通信的传输协议;HTTPS经由HTTP进行通信,但是利用SSL/TLS 来加密数据包

HTTPS开发的主要目的是为了提供对网站服务器的身份认证,保证交换数据的隐私性和完整性;

HTTPS默认工作在TCP协议443端口,它的工作流程是

  1. TCP三次同步握手
  2. 客户端检验服务器数字证书
  3. DH算法协商对称加密算法的密钥hash算法的密钥
  4. SSL安全加密隧道协商完成
  5. 页面以加密方式传输,用协商的对称加密算法和密钥加密,保证数据机密性,用协商的hash算法进行数据完整性的保护,保护数据不被篡改

HTTP和HTTPS的不同?

HTTP是明文协议,数据都是没有加密的,安全性差,HTTPS协议数据传输的过程都是加密的,安全性好,

HTTPS协议需要用到CA(数字证书认证机构)申请证书,一般免费证书较少,所以需要一定的费用

HTTPS的响应速度比HTTP慢,主要HTTP只使用TCP三次握手建立连接,客户端和服务端只需要交换3个包,而HTTPS除了TCP要交换的3个包还有SSL握手需要的9个包,一共是12个包

HTTP和HTTPS使用的连接方式不同,而且端口也不一样,HTTP是在80端口,而HTTPS是在443端口

HTTPS其实就是构建在SSL/TLS之上的HTTP协议,所以比HTTP更耗费服务器资源

HTML

回流和重绘

1.介绍一下回流和重绘

浏览器采用流式布局模型,浏览器会将HTML解析成DOM把CSS解析成CSSOM ,然后把CSSOM和DOM结合生成一个render tree

根据render tree我们能够知道节点的样式  浏览器会从根节点递归调用去计算每个节点的位置,然后把节点绘制到页面上

重绘的概念

当渲染树中的一些元素属性需要更新,但是这些属性只涉及和影响了元素的外观以及风格不影响布局的情况下,我们称之为重绘

回流的概念

当渲染树中的一部分或者全部元素的结构或者尺寸、布局、隐藏等发生改变需要重新建构,会
影响到布局,我们成为回流

重绘不一定引起回流  但是回流一定会引起重绘

性能上 重绘的性能消耗小于回流

2.常见的引起回流的操作

(1)添加或者删除可见的 DOM 元素;
(2)元素尺寸改变——边距、填充、边框、宽度和高度
(3)内容变化,比如用户在 input 框中输入文字
(4)浏览器窗口尺寸改变——resize 事件发生时
(5)计算 offsetWidth 和 offsetHeight 属性 clientTop等
(6)设置 style 属性的值
(7)当你修改网页的默认字体时

3.如何减少回流?

1.使用documentFragment的方式来操作DOM

正确写法:先加入文档碎片 再一次性添加

错误写法:一条一条加

2.用transform代替 top

3.不要使用table布局

4.预先定义好class 直接修改className 而不是一条一条的去修改DOM样式

5.避免多层内联样式

6.将需要多次重排的元素 position属性设置为absolute 或fixed,脱离文档流

如何让浏览器不缓存

可以让URL的参数里随机生成一个数字,但是浏览器不缓存会导致页面加载速度变慢,服务器压力增加

浏览器本地缓存

localStorage sessionStorage cookies 的区别

localStorage是一种长期存储的方式,他的有效期是永久,除非用户手动清除,否则存储的信息会一直存在,它可以存放5M大小的数据,仅仅在浏览器中保存,不会和服务器通信,也不会把数据自动发送给服务器

sessionStorage仅在当前会话有效,关闭页面或者浏览器后就会被清除,存储大小一般为5M,数据仅仅在浏览器中保存,不会和服务器通信,也不会把数据自动发送给服务器

cookie的大小为4KB左右,始终会在同源http请求中携带,它的的有效期可以通过expire来设置。

localStoragese:常用于长期登录(+判断用户是否已登录),适合长期保存在本地的数据,而 sessionStorage :敏感账号一次性登录

localStorage &  sessionStorage & cookies

cookies;sessionStorage 和 localStorage的区别(考察本地存储方式是否熟悉)
sessionStorage, localStorage, Cookie
这三者都可以被用来在浏览器端存储数据,而且都是字符串类型的键值对。
1.cookies 的大小大概在4KB左右 存储的数据一般都是用来标记用户信息的加密字符串,且只在同源页面共享(域名、端口号、协议号相同),生命周期通过expires设置
如果在浏览器中设置了cookie的过期时间,cookie被保存在硬盘中,关闭浏览器后,cookie数据仍然存在,直到过期时间结束才消息。
如果不在浏览器中设置过期时间,这种cookie简称会话cookie。
2.sessionStorage
是浏览器本地存储的一种方式,一般纯存储5M或更大的数据,页面会话在浏览器打开期间一直保持,并且重新加载或恢复页面仍会保持原来的页面会话;存储的数据会在浏览器关闭之后自动删除
3.localStorage
是浏览器本地存储的一种方式,存储的数据将保存在浏览器会话中,他储存的是一个持久化的数据;不会主动
删除;数据会一直存在,除非手动删除
  • localStorage只要在相同的协议、相同的主机名、相同的端口下,就能读取/修改到同一份localStorage数据。

  • sessionStoragelocalStorage更严苛一点,除了协议、主机名、端口外,还要求在同一窗口(也就是浏览器的标签页)下


什么是跨域?

1.什么是跨域
一个项目中多台服务器提供不同功能,服务器的域名地址不同,就会产生跨域的问题;跨域主要是因为浏览器 要遵守同源政策,就算请求成功了,也会被拦截
2.什么是同源政策
同源策略:协议名、域名、端口号必须一致
是浏览器的安全策略,它限制了同一个源加载的文档或者脚本如何与另一源的资源进行交互,这是一个用于 隔离潜在恶意文件的重要安全机制; 违背同源策略就会产生跨域;
3.为什么要有同源策略
如果没有同源策略,浏览器很容易受到XSS、CSFR等攻击
( 1 )XSS攻击
· 即Cross Site Script 跨站脚本攻击
· HttpOnly防止截取Cookie
· 用户的输入检查
· 服务端的输出检查
( 2 ) CSFR攻击
· 跨站请求伪造 是一种劫持受信任用户向服务器发送非预期请求的攻击方式
· 验证码
·Referer Check
·Token 验证

4.跨域的解决方案

jsonP、 cors服务器代理

核心是动态添加script标签调用服务器提供的JS脚本,允许用户传递一个callback参数给服务器,服务器在返回数据会将这个callback参数作为函数名来包裹JSON数据,这样客户端就可以随意定制自己的 函数来自动处理返回数据(仅支持GET方法)

!DOCTYPE

用于告知浏览器的解析器用什么模式来解析文档

在标准模式下,浏览器的解析规则都会按照最新标准进行。

在兼容模式下,浏览器会以向后兼容的方式显示,模拟老浏览器的行为,确保一些老的网站能够正常访问。

如果!DOCTYPE 不存在或者格式不正确会导致文档以兼容模式呈现
指定了之后会按标准模式来解析文档;

为什么HTML5只需要写!DOCTYPE 而不需要引入DTD

因为HTML5不再需要基于SGML ,而只是需要通过 !DOCTYPE来规范浏览器行为;

而HTML5之前的html文档都是基于SGML;使用需要通过指定DTD来定义文档的属性以及规则,才
能告知浏览器是文档所使用的的文档类型。

CSS

选择器权重

1.选择器权重只能同级比较,例如再多的 class选择器也抵不过一个id 选择器

!important > 行内选择器 > id > class > 标签选择器 >  通配符 > 继承 > 浏览器默认属性

动画特性可以使用js实现,为什么还要用css?

不占js的主线程,性能更高

postCss是什么 有什么用?

postCss
是一个平台;基于这个平台可以去使用插件来优化代码,例如autoprefixer插件就需要基于postcss使 用,可以帮助我们为css加上不同浏览器的前缀

margin 负值

margin合并

1.有内容的div之间的margin会合并为他们之间较大的那个margin

2.有内容的div和空的div之间也会发生margin合并 空的div被忽略

垂直居中/水平居中

垂直居中
方法1.
.block {position: relative;top:50%;/* margin-top:-50px ; */transform: translateY(-50%);height: 100px;width: 100px;background-color: yellow;}2.还有给父元素设置display: flex;align-items: center;3.以及给父元素设置display: grid;给子元素设置align-self: center;4.line-height: center
水平居中
1.text-align:center
2.父元素设置display:flex 子元素设置 justify-content::center
3.父元素设置display:grid 子元素设置 justify-self:center
4.margin: 0 auto
5.子元素:{position: absolute;left: 50%;
transform: translateX(-50%);//或者margin-left -子元素的宽的一半}

BFC block formatting context 块级格式化上下文

什么是BFC

形成独立的渲染区域 ;内部元素的渲染不会影响外界

BFC的特性

内部的box会在垂直方向,从顶部一个一个放置

Box垂直方向的距离由margin决定。同属于一个BFC的两个相邻box的margin会发生叠加

BFC的区域不会和float box叠加
计算BFC高度,浮动元素也要参与计算

形成BFC的常见条件

1.浮动元素 float 不是none
2.绝对定位元素 position是absolute或者fixed
3.块级元素 overflow 不是visible (例如hidden)
4.flex元素 inline-block元素 table-cell

BFC 的作用?

BFC主要是应用于清除浮动
案例:
如果我想要让div container的高度和图片的高度一样
就需要给container加一个带有bfc样式的class

但实际上 p标签占满了container的宽度;覆盖到了图片上,对此我们需要对p标签也设置class = bfc
设置前

设置后

三角形

.red {height:0;width:0;border:100px solid;border-color:red transparent transparent red;
}
.beauty{height:0;width:0;border:100px solid;border-color:pink yellow white orange;
}

display:none /opcity 与visibility:hidden的区别

他们的共同点是让元素不可见
1.display:none :渲染树不会渲染该对象,所以它不占用任何空间;**是非继承的属性**,子孙节点的消失是因为元素从渲染 树消失造成,无法通过修改子孙节点的display属性来让它们展示;
2.visibility:hidden
只是隐藏了元素,元素会继续占用空间;但不会相应绑定的监听事件;且它是继承属性;子孙节点的消失是由于继承了hidden 可以通过设置子孙节点的visibility:visible来让子孙节点显示;
3.opcity:0 将元素的透明度设置为0,元素隐藏但是在页面中仍然占用空间,且能去响应元素绑定的事件

如何进行网站性能优化

首先我们需要去压缩代码和图片,删除一些不必要的代码,对于小的一些静态资源我们可以去整理成精灵图的形式。还有要将js文件放在页面底部,减少对dom的查询,对于非必要的组件我们可以延迟加载;

js方面

1. 将脚本放在页面的底部
2. 将js和css从外部引入
3. 压缩js和css
4. 删除不需要的脚本
5. 减少DOM查询
6. 合理设置事件监听器

图片方面

1. 优化图片,根据实际颜色选择色深、压缩
2. 优化CSS精灵
3. 不要在HTML中拉伸图片
4. 保证favicon、ico小并且可以缓存

css方面

1. 将样式表放在顶部
2. 不要使用css表达式
3. 不要使用@import
4. 不要使用IE的filter

移动方面

1. 保证组件小于25k
2. 将组件打包到多部分文档中

cookie方面

1. 减小Cookie
2. 引入资源的域名不要包含cookie
server方面
1. 避免空src的img标签
2. 使用CDN
3. 配Etag
4. Ajax使用GET请求
5. 使用Gzip压缩

content方面

1. 减少HTTP请求:合并文件、CSS精灵 inline imgage
2. 减少NDS查询3. 使用AJAX缓存
4. 非必要组件延迟加载
5. 减少DOM元素数量
6. 未来需要的组件预先加载
7. 避免重定向
8. 不要404

前端学习(面试收集)相关推荐

  1. 2020前端学习路线收集整理

    本人18年毕业,刚毕业自学Java,第一份工作因部门解散,时效半年,其实那时我一直在想往前端发展,恰巧拿到下家offer,第二份工作确实是前端,不过是客户是银行,样式全部统一,与我想象的色彩缤纷的前端 ...

  2. 前端学习+面试小总结(二)

    从过完年开始系统学习相关的知识,已经接近两个月了.这篇文章想系统的回顾一下自己的各种骚操作,并对接下来的路进行一个修正吧. 大致分为几个部分: 1. 我学了什么,学习的过程中有什么可取的点和不可取的点 ...

  3. 关于前端学习和笔试面试的总结

    前沿 以前总是希望在技术论坛和博客能有人关注,最近收到一些小伙伴请教问题的来信和私信,在深感荣幸的同时也深知自己技术和经验的不足,怕会误人子弟,所以现在打算以应届生的身份尽自己的一点绵薄之力给大家一点 ...

  4. 2020 - 2021 年 Web 前端最新导航 - 前端学习资源分享前端面试资源汇总

    前端javascriptvue.jses6typescript 发布于 10月9日 国庆这几天,我收集了大量可以显著提升开发效率的前端导航链接. 这些导航链接对我很有帮助,希望对你也是如此. 这些好用 ...

  5. GitHub上收集的最全的前端资源汇总(包括前端学习、求职、开发资源)

    http://www.imooc.com/article/12645 个人结合github上各位大神分享的资源进行了简单的汇总整理,每一个条目下面都有丰富的资料,是前端学习.工作的好帮手. 项目地址: ...

  6. 前端学习资料网址收集整理

    前端学习资料整理:百度cdn,jquery插件网站收集,html5资料整理等.方便查阅. A:基础知识,行业动态 http://www.51cto.com 51cto http://www.html5 ...

  7. 【学习笔记】前端开发面试锦集

    链接地址:https://microzz.com/2017/02/10/interview/ 前端还是一个年轻的行业,新的行业标准, 框架, 库都不断在更新和新增,正如赫门在2015深JS大会上的&l ...

  8. web前端学习到什么程度可以面试工作

    前端学习想要达到可以找工作的程度还是比较简单的,但是想要通过学习找到高工资的工作可就不容易了,如果只是想要找个普通的工作只要学了前端的基础知识,主流框架实现一定的兼容展示功能,在就是做几个前端 Web ...

  9. 一些有用的资源分享(工具+电子书+GitHub最全的前端资源汇总仓库(包括前端学习、开发资源、求职面试等))

    原地址:https://mp.weixin.qq.com/s/wSN1w2mM6Fh51RDGZYOdIQ 工具类 图片相关工具 TinyPNG:https://tinypng.com/ 免费的在线图 ...

  10. 前端资源汇集(个人觉得不错的学习资源收集)

    前言:此前在"掘金"上看到技术大牛们分享的技术贴,其中涵盖了很多本人所未接触过使用过的前端学习的资源.通过对里面几个自己比较感兴趣的资源网站进行学习之后发现真心不错,因此想与各位跟 ...

最新文章

  1. matplotlib 制作不等间距直方图
  2. 简单团队-爬虫豆瓣top250-项目总结
  3. 前端技术分享:教你玩转vue-router命令视图
  4. python参数化建模 书_Python 中如何实现参数化测试?
  5. JAVA绘制图片原理_java开发_图片截取工具实现原理
  6. 右键新建里面没有word和excel_Excel中为什么修改了新建工作簿的选项,新建以后还是没有生效...
  7. python元祖组成字典_Python基础之元组和字典
  8. 斯坦福「AI百年研究」首份报告:2030年的人工智能与生活
  9. FPGA有哪些优质的带源码的IP开源网站?
  10. 【热门收藏】iOS开发人员必看的精品资料(100个) ...
  11. 郁闷,做了很多无用功
  12. ubuntu火狐浏览器安装flash_player插件
  13. vue+element实现导入和导出excel
  14. java 使用mediainfo_使用mediainfo工具统计每个视频文件(媒体文件)播放时长
  15. 350导热油 shell_导热油320与350的区别,克拉克给你详细解说
  16. 2018 年最受欢迎的电影,你都看过哪些?爬猫眼电影
  17. 联奕“云计算”数字校园整体解决方案,让高校云计算不再是“浮云”
  18. HDU-5813-Elegant Construction-贪心
  19. 英汉翻译词典软件代码
  20. ant design pro / umi入门

热门文章

  1. 【自用】华南师范大学918c++程序设计选填错题
  2. IIS - 实现HTTPS的主机名绑定(解决IIS7下主机名灰色无法修改问题)
  3. 一梦江湖获取服务器信息,《一梦江湖》手游官方网站_《楚留香》现已全面升级重制-4月24日维护更新公告,灵犀NPC甜蜜奇遇更新...
  4. ssm汽车销售系统毕业设计(附源码、运行环境)
  5. 怎样设置范围使数据透视表能自适应数据源记录的变化
  6. 手机看html网页内容重叠,html5手机端叠加图片触屏滑动查看特效
  7. 小程序公众号平台添加服务器,微擎绑定对接微信公众号小程序图文教程
  8. upload-labs详细教程
  9. 网络学习云平台计算机基础答案,网络自主学习平台(计算机基础)
  10. 游戏音乐制作应该注意什么呢?