2021-6月面试总结-vue,uniapp,小程序,h5,更新中
Posted 程序员超时空
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2021-6月面试总结-vue,uniapp,小程序,h5,更新中相关的知识,希望对你有一定的参考价值。
**一、**vue 2.6.11
vue****页面的生命周期?
总共分为8个阶段创建前/后,载入前/后,更新前/后,销毁前/后。
创建前/后: 在beforeCreate阶段,vue实例的挂载元素 e l 和 数 据 对 象 d a t a 都 为 u n d e f i n e d , 还 未 初 始 化 。 在 ∗ ∗ c r e a t e d ∗ ∗ 阶 段 , v u e 实 例 的 数 据 对 象 d a t a 有 了 , el和数据对象data都为undefined,还未初始化。在**created**阶段,vue实例的数据对象data有了, el和数据对象data都为undefined,还未初始化。在∗∗created∗∗阶段,vue实例的数据对象data有了,el还没有。
载入前/后:在beforeMount阶段,vue实例的$el和data都初始化了,但还是挂载之前为虚拟的dom节点,data.message还未替换。在mounted阶段,vue实例挂载完成,data.message成功渲染。
更新前/后:当data变化时,会触发beforeUpdate和updated方法。
销毁前/后:beforeDestroy在执行destroyed方法后,对data的改变不会再触发周期函数,说明此时vue实例已经解除了事件监听以及和dom的绑定,但是dom结构依然存在
一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一
vue****组件之间如何传值通信
父到子:
子组件在props中创建一个属性,用来接收父组件传过来的值;
在父组件中注册子组件;
在子组件标签中添加子组件props中创建的属性;
把需要传给子组件的值赋给该属性
子到父:
子组件中需要以某种方式(如点击事件)的方法来触发一个自定义的事件;
将需要传的值作为$emit的第二个参数,该值将作为实参传给响应事件的方法;
在父组件中注册子组件并在子组件标签上绑定自定义事件的监听。
平行组件:
e m i t 推 送 , emit推送, emit推送,on接收
Vue2.x****组件通信有哪些方式
- 父子组件通信: props 、 o n 、 on、 on、emit、 Ref 获取实例的方式调用组件的属性或者方法、(Provide、inject 官方不推荐使用,但是写组件库时很常用)
- 兄弟组件通信: EventBus 、Vuex
- 跨级组件通信:Vuex、 a t t r s 、 attrs、 attrs、listeners、Provide、(Provide、inject 官方不推荐使用,但是写组件库时很常用)
created和mounted****的区别?
created:在模板渲染成html前调用,即通常初始化某些属性值,然后再渲染成视图。
mounted:在模板渲染成html后调用,通常是初始化页面完成后,再对html的dom节点进行一些需要的操作。
DOM 渲染在哪个周期中就已经完成?
DOM 渲染在 mounted 中就已经完成了。
mvvm**,vue双向数据绑定的原理**
MVVM分为Model、View、ViewModel三者。
· Model 代表数据模型,数据和业务逻辑都在Model层中定义;
· View 代表UI视图,负责数据的展示;
· ViewModel 负责监听 Model 中数据的改变并且控制视图的更新,处理用户交互操作;
实现数据的双向绑定,首先要对数据进行劫持监听,所以我们需要设置一个监听器Observer,用来监听所有属性。如果属性发上变化了,就需要告诉订阅者Watcher看是否需要更新。因为订阅者是有很多个,所以我们需要有一个消息订阅器Dep来专门收集这些订阅者,主要是在监听器Observer和订阅者Watcher之间进行统一管理
MVC模式将软件分为下面三个部分 1.视图(View) :用户界面 2.控制器(Controller) :业务逻辑 3.模型(Model) :数据保存
vuex的state**、getter、mutation、action、module特性分别是什么?**
State: 保存着所有的全局变量
Getter: store中的计算属性,就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。 getters接收state作为其第一个参数,接受其他 getters 作为第二个参数,如不需要,第二个参数可以省略。
Mutation: 更改 Vuex 的 store 中的状态的唯一方法是提交 mutation, mutation 必须是同步函数
Action: Action 可以包含任意异步操作, 在组件中使用 this.$store.dispatch(‘xxx’) 分发 action
Module: 可以写很多模块,Vuex 允许我们将 store 分割到模块(module)。每个模块拥有自己的 state、mutation、action、getters,最后都引入到一个文件。分散管理。
为什么mutation里面只能同步,不能异步?
就会方便追踪到state的变化,因为state里面的数据会改变 异步的话组件有可能会收不到 ,不能实时追踪
严格模式会报错
页面刷新后vuex的state数据丢失怎么解决?
将Vuex里的数据同步更新到localStorage里。
即:一改变vuex里的数据,便触发localStorage.setItem 方法
在 App.vue 的 created 钩子函数里写
//在页面加载时读取localStorage里的状态信息
localStorage.getItem(“userMsg”)&& this.$store.replaceState(JSON.parse(localStorage.getItem(“userMsg”)));
//在页面刷新时将vuex里的信息保存到localStorage里
window.addEventListener(“beforeunload”,()=>
localStorage.setItem(“userMsg”,JSON.stringify(this.$store.state))
)
store****提供了哪些函数
提供getState( )方法获取state;
提供dispatch(action)方法更新state;
通过subscribe(listener)注册监听器;
通过subscribe(listener)返回的函数注销监听器
使用 mapState工具函数会将store中的state映射到局部计算属性中
mapGetter,mapMutation,mapAction会将store中各自的方法映射过来
vuex****是什么呢?哪些场景会用到?
vuex是一个专为vue.js应用程序开发的状态管理模式
我们大概可以理解为vuex是一个公共 状态库 , 你可以在所有的组件里面去使用,修改
场景有:单页应用中,组件之间的状态、音乐播放、登录状态、加入购物车
vuex为状态管理,它集中存储管理应用的所有组件的状态,可以理解成一个全局仓库
VueRouter是路由(spa)单页面应用的方式
使用vuex的优势是什么?有用过vuex吗?它主要解决的是什么问题?
Vuex的优势:
1.解决了非父子组件的消息传递(将数据存放在state中)
2.减少了AJAX请求次数,有些情景可以直接从内存中的state获取
主要解决的问题是:
用来管理全局的组件状态,比如有很多个组件都会修改同一个数据,同时这个数据又要在多个组件上同时显示,这个时候用 vuex 来统一管理这些组件的状态,会让逻辑更清晰,更可维护
vue和react****渲染有什么区别
vue—会跟踪每一个组件的依赖关系, 去重新渲染有依赖关系的组件,不是说重新渲染整个组件树。
react–如果某个组件状态发生变化,react会把这个组件还有这个组件的所有后代组件全部重新渲染,不过重新渲染并不是代表全部丢掉上一次的渲染结果,react通过diff去比较两次虚拟dom,比较之后再反映倒真实dom上,如果说组件树比较大,diff算法也是一比开销,react提供出来解决方案shouldComponentUpdate-----根据这个生命周期的返回结果来判断是不是需要执行后面的diff,update这些东西;
vue—Object.defineProperty get去收集依赖, 因此不会像react- 样去比较整颗组件树,去更加细粒度的去更新状态有变化的组件;
vue****中路由的模式如何选择,不同模式有什么区别?
Hash 模式
www.test.com/#/ 就是 Hash URL,当 # 后面的哈希值发生变化时,可以通过 hashchange 事件来监听到 URL 的变化,从而进行跳转页面,并且无论哈希值如何变化,服务端接收到的 URL 请求永远是 www.test.com。
window.addEventListener(‘hashchange’, () =>
// … 具体逻辑
)
Hash 模式相对来说更简单,并且兼容性也更好。
History 模式
History 模式是 HTML5 新推出的功能,主要使用 history.pushState 和 history.replaceState改变 URL。
通过 History 模式改变 URL 同样不会引起页面的刷新,只会更新浏览器的历史记录。
// 新增历史记录
history.pushState(stateObject, title, URL)
// 替换当前历史记录
history.replaceState(stateObject, title, URL)
当用户做出浏览器动作时,比如点击后退按钮时会触发 popState 事件
window.addEventListener(‘popstate’, e =>
// e.state 就是 pushState(stateObject) 中的 stateObject
console.log(e.state)
)
两种模式对比
· Hash 模式只可以更改 # 后面的内容,History 模式可以通过 API 设置任意的同源 URL
· History 模式可以通过 API 添加任意类型的数据到历史记录中,Hash 模式只能更改哈希值,也就是字符串
· Hash 模式无需后端配置,并且兼容性好。History 模式在用户手动输入地址或者刷新页面的时候会发起 URL 请求,后端需要配置 index.html 页面用于匹配不到静态资源的时候
r o u t e ∗ ∗ ∗ ∗ 和 ∗ ∗ ∗ ∗ route****和**** route∗∗∗∗和∗∗∗∗router****有什么区别,有什么关联关系
- $router 为 VueRouter 实例,想要导航到不同 URL,则使用 $router.push 方法。 $route 为当前 router 跳转对象里面可以获取 name 、 path 、 query 、 params 等。
插槽****slot
插槽(Slot)插槽用于决定将所携带的内容,插入到指定的某个位置,从而使模板分块,具有模块化的特质和更大的重用性,插槽就是Vue实现的一套内容分发的API
1.匿名插槽 :叫做默认插槽 就是没有名字
2.具名插槽:需要多个插槽时,可以利用元素的一个特殊的特性:name来定义具名插槽
3.作用域插槽:是父组件引用子组件中的数据,使用slot-scope 进行数据的传递,子组件中 将数据传递给父组件
v-for和v-if****的优先级
v-for的优先级高于v-if
watch
watch的原理 通过watch的方法,监听被改变的变量,然后再watch的那个变量命名的函数中去对我们要修改的值进行重新的赋值,或者是触发一次更新。 watch的执行类似于emit与on这种触发方式,通过vue的watch实例监听值来自动触发一个函数的执行。
· watch函数的参数中,第一个是改变之前的值,第二个是改变之后的值,这两个参数非常有用。
· 这里分别使用了 三种定义函数(或option)的方法。
· 如果要观察data下一个对象的属性,我们可以使用对象.属性的方式, 注意: 一定要要引号。
· 如果改变了一个对象的属性,就必须使用deep: true,否则检测不到变化。
watch和computed****差异
watch是进行数据监听,然后进行相应的操作,执行方法等conputed和methods的合体使用,比较耗性能,与vue性能优化相背而驰,尽量减少使用!computed是数据改变进行相应的数据变化,由老数据迸发新的数据(return返回),会利用缓存机制对 数据进行缓存 ,只有当 依赖数据变化的时候才会进行相应的变化
跨域
组件 data 为什么必须是函数
因为 JS 本身的特性带来的,如果 data 是一个对象,那么由于对象本身属于引用类型,当我们修改其中的一个属性时,会影响到所有 Vue 实例的数据。如果将 data 作为一个函数返回一个对象,那么每一个实例的 data 属性都是独立的,不会相互影响了。
$nextTick
$nextTick 是在下次 DOM 更新循环结束之后执行延迟回调,在修改数据之后使用 $nextTick,则可以在回调中获取更新后的 DOM。
key
key的作用是为了在diff算法执行时更快的找到对应的节点,提高diff速度
key具有唯一性,使用 key,它会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。 有相同父元素的子元素必须有独特的 key。重复的 key 会造成渲染错误。
带key就不会使用就地复用了,在sameNode函数 a.key===b.key对比中可以避免就地复用的情况。
vue中列表循环需加:key=“唯一标识” 唯一标识可以是item里面id index等,因为vue组件高度复用增加Key可以标识组件的唯一性,为了更好地区别各个组件 key的作用主要是为了高效的更新虚拟DOM
可以这样简单地理解,无:key属性时,状态默认绑定的是位置;有:key属性时,状态根据key的属性值绑定到了相应的数组元素。
若用数组索引index为key,当向数组中指定位置插入一个新元素后,对应着后面的虚拟DOM的key值全部更新了,这个时候还是会做不必要的更新,就像没有加KEY一样
为什么官网说的是就地更新的效率更高呢
key的作用就是更新组件时判断两个节点是否相同。相同就复用,不相同就删除旧的创建新的。在渲染简单的无状态组件时,如果不添加key组件默认都是就地复用,不会删除添加节点,只是改变列表项中的文本值,要知道节点操作是十分耗费性能的。而添加了key之后,当对比内容不一致时,就会认为是两个节点,会先删除掉旧节点,然后添加新节点。
vue****图片懒加载
vue-lazyload
vue****路由懒加载
方法一 resolve
这一种方法较常见。它主要是使用了resolve的异步机制,用require代替了import,实现按需加载,下面是代码示例:
import Vue from ‘vue’
import Router from ‘vue-router’
// import HelloWorld from ‘@/components/HelloWorld’
Vue.use(Router)
export default new Router(
routes: [
//
// path: ‘/’,
// name: ‘HelloWorld’,
// component: HelloWorld
//
path: ‘/’,
name: ‘HelloWorld’,
component: resolve => require([’@/components/HelloWorld’], resolve)
]
)
方法二 官网方法
vue-router在官网提供了一种方法,可以理解也是为通过Promise的resolve机制。因为Promise函数返回的Promise为resolve组件本身,而我们又可以使用import来导入组件。整合起来代码示例如下:
import Vue from ‘vue’
import Router from ‘vue-router’
// import HelloWorld from ‘@/components/HelloWorld’
Vue.use(Router)
export default new Router(
routes: [
//
// path: ‘/’,
// name: ‘HelloWorld’,
// component: HelloWorld
//
path: ‘/’,
name: ‘HelloWorld’,
component: () => import(’@/components/HelloWorld.vue’)
]
)
VUE****自定义指令
注册自定义指令分为全局注册与局部注册两种:
1.创建局部指令
var app = new Vue(
el: ‘#app’,
data:
,
// 创建指令(可以多个)
directives:
// 指令名称
dir1:
inserted(el)
// 指令中第一个参数是当前使用指令的DOM
console.log(el);
console.log(arguments);
// 对DOM进行操作
el.style.width = ‘200px’;
el.style.height = ‘200px’;
el.style.background = ‘#000’;
)
2.全局指令
Vue.directive(‘dir2’,
inserted(el)
console.log(el);
)
3.指令的使用
vue****自定义插件
在Toast组件中里面写方法,比如显示隐藏之类的
在同级目录下创建一个插件入口文件index.js
在index.js里面
const obj = ; obj.install = function(Vue)
1.创建组件构造器
2.使用new的方式,根据组件构造器,可以创建出一个组件对象
3.将组件对象,手动挂载到某一个元素上
toast. $el对应的就是div
4.通过Vue的原型注册一 个方法
Vue.prototype. $toast=toast
其他组件可以引用
this $toast.show(这是一 个toast’,3)
vue-cli3****开发环境与生产环境的区分
在vue-cli3的项目中,
npm run serve时会把process.env.NODE_ENV设置为‘development’;
npm run build 时会把process.env.NODE_ENV设置为‘production’;
那么,就可以直接根据不同环境配置vue.config.js:
keep-alive
路由跳转 vue-router
keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。
include - 字符串或正则表达,只有匹配的组件会被缓存 exclude - 字符串或正则表达式,任何匹配的组件都不会被缓存
两个生命周期activated/deactivated,用来得知当前组件是否处于活跃状态。 keep-alive的中还运用了LRU(Least Recently Used)算法。
在vue里面你如何做数据的监听
- watch里面监听
- 第一种写法
watch: obj(newval,oldval) console.log(newval,oldval) ,
-
- 第二种写法可设置deep为true对数据进行深层遍历监听
watch: obj: handler(newval,oldval) console.log(222) console.log(newval,oldval) , deep:true
- computed 里面监听
- computed里面的依赖改变时,所计算的属性或作出事实的改变
关于单页应用首屏加载速度慢,出现白屏时间过长问题你怎么处理
- 将公用的JS库通过script标签在index.html进行外部引入,减少我们打包出来的js文件的大小,让浏览器并行下载资源文件,提高下载速度
- 在配置路由的时候进行路由的懒加载,在调用到该路由时再加载此路由相对应的js文件
- 加一个首屏loading图或骨架屏,提高用户的体验
- 尽可能使用CSS Sprites和字体图标库
- 图片的懒加载等
Vue2.x响应式数据原理(数据劫持)
Vue在初始化数据时,会对data进行遍历,并使用Object.defineProperty把这些属性转为getter/setter。 每个组件实例都对应一个watcher 实例,当页面使用对应属性时,首先会用getter进行依赖收集(收集当前组件的watcher)如果属性发生变化setter 触发时会通知相关依赖进行更新操作(发布订阅)。
注意:
- Object.defineProperty 是 ES5 中一个无法 shim 的特性,这也就是 Vue 不支持 IE8 以及更低版本浏览器的原因。
- 由于 javascript _的限制,_Vue 不能检测数组和对象的变化。
Vue3.x****响应式数据原理
Vue3.x改用Proxy替代Object.defineProperty。因为Proxy可以直接监听对象和数组的变化,并且有多达13种拦截方法。并且作为新标准将受到浏览器厂商重点持续的性能优化。
vue-router 中的导航钩子
vue-router 的导航钩子,主要用来作用是拦截导航,让他完成跳转或取消。
有三种方式可以植入路由导航过程中:
- 全局的
- 单个路由独享的
- 组件级的
1.****全局导航钩子:
全局导航钩子主要有两种钩子:前置守卫、后置钩子,
注册一个全局前置守卫:beforeEach
const router = new VueRouter( … );
router.beforeEach((to, from, next) =>
// do someting
);
这三个参数 to 、from 、next 分别的作用:
这三个参数 to 、from 、next 分别的作用:
1.to: Route,代表要进入的目标,它是一个路由对象
2.from: Route,代表当前正要离开的路由,同样也是一个路由对象
3.next: Function,这是一个必须需要调用的方法,而具体的执行效果则依赖 next 方法调用的参数
1、next():进入管道中的下一个钩子,如果全部的钩子执行完了,则导航的状态就是 confirmed(确认的) 2、next(false):这代表中断掉当前的导航,即 to 代表的路由对象不会进入,被中断,此时该表 URL 地址会 被重置到 from 路由对应的地址 3、next(‘/’) 和 next(path: ‘/’):在中断掉当前导航的同时,跳转到一个不同的地址 4、next(error):如果传入参数是一个 Error 实例,那么导航被终止的同时会将错误传递给 router.onError() 注册过的回调
注意:next 方法必须要调用,否则钩子函数无法 resolved
对于全局后置钩子:afterEach
router.afterEach((to, from) =>
// do someting
);
不同于前置守卫,后置钩子并没有 next 函数,也不会改变导航本身
不同于前置守卫,后置钩子并没有 next 函数,也不会改变导航本身
2.****路由独享的钩子
beforeEnter
顾名思义,即单个路由独享的导航钩子,它是在路由配置上直接进行定义的:
cont router = new VueRouter(
routes: [
path: ‘/file’,
component: File,
beforeEnter: (to, from ,next) =>
// do someting
]
);
至于他的参数的使用,和全局前置守卫是一样的
至于他的参数的使用,和全局前置守卫是一样的
3.****组建内的导航钩子
组件内的导航钩子主要有这三种:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave。他们是直接在路由组件内部直接进行定义的
我们看一下他的具体用法:
const File =
template: `
beforeRouteEnter(to, from, next)
// do someting
// 在渲染该组件的对应路由被 confirm 前调用
,
beforeRouteUpdate(to, from, next)
// do someting
// 在当前路由改变,但是依然渲染该组件是调用
,
beforeRouteLeave(to, from ,next)
// do someting
// 导航离开该组件的对应路由时被调用
需要注意是:
需要注意是:
beforeRouteEnter 不能获取组件实例 this,因为当守卫执行前,组件实例被没有被创建出来,剩下两个钩子则可以正常获取组件实例 this
但是并不意味着在 beforeRouteEnter 中无法访问组件实例,我们可以通过给 next 传入一个回调来访问组件实例。在导航被确认是,会执行这个回调,这时就可以访问组件实例了,如:
beforeRouteEnter(to, from, next)
next (vm =>
// 这里通过 vm 来访问组件实例解决了没有 this 的问题
)
注意,仅仅是 beforRouteEnter 支持给 next 传递回调,其他两个并不支持。因为归根结底,支持回调是为了解决 this 问题,而其他两个钩子的 this 可以正确访问到组件实例,所有没有必要使用回调
注意,仅仅是 beforRouteEnter 支持给 next 传递回调,其他两个并不支持。因为归根结底,支持回调是为了解决 this 问题,而其他两个钩子的 this 可以正确访问到组件实例,所有没有必要使用回调
最后是完整的导航解析流程:
导航被触发
在失活的组件里调用离开守卫
调用全局的 beforeEach 守卫
在重用的组件里调用 beforeRouteUpdate 守卫
在路由配置里调用 beforEnter
解析异步路由组件
在被激活的组件里调用 beforeRouteEnter
调用全局的 beforeResolve 守卫
导航被确认
调用全局的 afterEach 钩子
触发 DOM 更新
在创建好的实例调用 beforeRouteEnter 守卫中传给 next 的回调函数
单页面应用(SPA)
单页面应用(SPA),通俗一点说就是指只有一个主页面的应用,浏览器一开始要加载所有必须的 html, js, css。所有的页面内容都包含在这个所谓的主页面中。但在写的时候,还是会分开写(页面片段),然后在交互的时候由路由程序动态载入,单页面的页面跳转,仅刷新局部资源。
单页面的优点:
- 用户体验好,快,内容的改变不需要重新加载整个页面,基于这一点spa对服务器压力较小
- 前后端分离
- 页面效果会比较炫酷(比如切换页面内容时的专场动画)
单页面缺点:
- 不利于seo
- 导航不可用,如果一定要导航需要自行实现前进、后退。(由于是单页面不能用浏览器的前进后退功能,所以需要自己建立堆栈管理)
- 初次加载时耗时多
- 页面复杂度提高很多
多页面应用(MPA)
多页面(MPA),就是指一个应用中有多个页面,页面跳转时是整页刷新
vue.js****的两个核心是什么?
答:数据驱动、组件系统
vue****常用的修饰符?
.prevent: 提交事件不再重载页面;
.stop: 阻止单击事件冒泡;
.self: 当事件发生在该元素本身而不是子元素的时候会触发;
.capture: 事件侦听,事件发生的时候会调用
element-ui****中遇到的问题
vue 项目整体框架解析
各个文件夹
- node_modules**:**用来放很多很多的环境依赖。
- public**:**用来存放公共资源,其中的 index.html 文件,就是初始的挂载点。被 App.vue 给取代了。
- src**:**放各种资源的
- assets**:**用来存放静态资源,比如图片之类的。
- components**:**放置各种小组件,相当于是子组件的子组件。
- router**:**路由,用来设置哪个 url 访问哪个页面组件。
- store**:**仓库。
- views**:**都是子组件,用来替换 App.vue 里面的 ,然后展示这个子组件,所以也叫页面组件,里面可以视图组件。
文件
- App.vue**:**子组件,用来取代 index.html 的挂载点的。
- main.js**:**入口文件
vue和react的diff****算法
vue和react的diff算法,都是忽略跨级比较,只做同级比较。vue diff时调动patch函数,参数是vnode和oldVnode,分别代表新旧节点。
. vue比对节点,当节点元素类型相同,但是className不同,任务是不同类型元素,删除重建,而react会认为是同类型节点,只是修改节点属性
. vue的列表比对,采用从两端到中间的比对方式,而react则采用从左到右依次比对的方式。当一个集合,只是把最后一个节点移动到了第一个,react会把前面的节点依次移动,而vue只会把最后一个节点移动到第一个。总体上,vue的对比方式更高效。
diff 算法原理
Diff算法的作用是用来计算出 Virtual DOM 中被改变的部分,然后针对该部分进行原生DOM操作, 而不用重新渲染整个页面。
在采取diff算法比较新旧节点的时候,比较只会在同层级进行, 不会跨层级比较。
diff****流程图
当数据发生改变时,set方法会让调用Dep.notify通知所有订阅者Watcher,订阅者就会调用patch给真实的DOM打补丁,更新相应的视图。
1、旧数组为空,将新数组的剩余元素插入; 2、新数组为空,将旧数组的剩余元素删除; 3、新、旧数组都不为空,执行第二步。
#### 二、找到需要被删除、插入、移动的元素
数组p:与新数组的长度相同,与新数组是相互映射关系, 元素在旧数组中的索引 存储在 元素在新数组中的位置
##### 三、找到最少的移动次数
1、找到 P 数组的最长递增子序列来做动态规划,新集合中不属于这个序列的将会被移动。
2、同时尾部遍历新数组和 LIS 序列,查看元素的位置是否能与 LIS 序列的任何一个值匹配:
a:可以匹配,保留位置; b:不能匹配,移动到到前面; c:找不到,插入元素;
vue和react****的相同和不同
Vue是用于构建用户界面的渐进式框架,React是构建用户界面的组件化开发
相同点:
- .都支持服务器端渲染
- 都使用虚拟DOM来实现
- 都有Virtual DOM,组件化开发,通过props参数进行父子组件数据的传递,都实现webComponent规范
- 只有框架的骨架,其他的功能如路由、状态管理等是框架分离的组件。
- 都是JavaScript的UI框架,数据驱动视图,专注于创造前端的富应用
- 都有支持native的方案,React的React native,Vue的weex
- 都有管理状态,React有redux,Vue有自己的Vuex(自适应vue,量身定做)
不同点:
Vue是用于构建用户界面的渐进式框架,React是构建用户界面的组件化开发
- React严格上只针对MVC的view层,Vue则是MVVM模式
- virtual DOM不一样,vue会跟踪每一个组件的依赖关系,不需要重新渲染整个组件树.
- 而对于React而言,每当应用的状态被改变时,全部组件都会重新渲染,所以react中会需要shouldComponentUpdate这个生命周期函数方法来进行控制
- 组件写法不一样, React推荐的做法是 JSX + inline style, 也就是把HTML和CSS全都写进JavaScript了,即’all in js’;
- Vue推荐的做法是webpack+vue-loader的单文件组件格式,即html,css,jd写在同一个文件;
- 数据绑定: vue实现了数据的双向绑定,react数据流动是单向的
- state对象在react应用中不可变的,需要使用setState方法更新状态;
- 在vue中,state对象不是必须的,数据由data属性在vue对象中管理
- vue是进行template渲染的,react是jsx
- 路由不一样,一个现有,一个封装路由表
- 生命周期不一样
- vue是class,react是classname
- 传值
- 在以下场景中,Vue比反应更好:
最新文档和更简单的语法。更小,更快,更灵活。丰富的HTML模板,易于开发。
React比Vue.js好:
需要构建移动应用程序。专业和出色的社区支持,以解决任何问题。需要构建大型应用程序。轻量级,易于版本迁移
- 跳转路由不一样,vue是this.$router.push,react是this.props.history.push()
react跳转路由
this.props.history.push(
pathname:" /detail",
query:
id:id
)
this.props.location.query.id
vue跳转路由
quxiang(id)
this.$router.push(
path: “/detail” ,
query:
id: id
);
**二、**react 16.13.1
react 的生命周期?
可以说他有 11 个,也可说他有 10 个 有 3 个阶段 实例化,存在期,销毁期
实例化:
props的实例化,state的实例化, componentWillMount 渲染前 (修改 state 的最后一次机会), render(渲染), componentDidMount(渲染后, 首次可以访问到 dom) 在实例化阶段除了 render 生命周期外,其它的生命周期只会执行一次
存在期 componentWillReceiveProps 当 *props* 值发生变化就会执行 shouldComponentUpdate 可以用来确认组件的更新,通过返回布尔值来确定组件是 否重新渲染 componentWillUpdate 更新前 render 更新 compoentDidUpdate 更新后
销毁期 componentWillUnmount 销毁
二**.** 由于未来采用异步渲染机制,所以即将在17版本中去掉的生命周期钩子函数
- componentWillMount
- componentWillRecieveProps
- componentWIllUpdate
三**.** 新增两个
- static getDerivedStateFromProps 会在初始化和update时触发,用于替换componentWillReceiveProps,可以用来控制 props 更新 state 的过程;它返回一个对象表示新的 state;如果不需要更新,返回 null 即可
- getSnapshotBeforeUpdate 用于替换 componentWillUpdate,该函数会在update后 DOM 更新前被调用,用于读取最新的 DOM 数据,返回值将作为 componentDidUpdate 的第三个参数
可以获取 dom 的生命周期 componentDidMount componentWillReceiveProps (不能操作) shouldComponentUpdate (不能操作) componentWillUpdate (不能操作) render (不能操作) compoentDidUpdate componentWillUnmount (不能操作)
只执行一次的生命周期 初始化阶段除了 render 之外所有的,销毁期的那个
可以修改 *state* 的生命周期 componentWillMount, componentDidMount, componentWillReceiveProps, shouldComponentUpdate, compoentDidUpdate
调用 setState 之后执行哪些生命周期 shouldComponentUpdate 如果返回 true 就会执行 WillUpdate *render * DidUpdate
react 生命周期函数
- 初始化阶段:
- getDefaultProps:获取实例的默认属性
- getInitialState:获取每个实例的初始化状态
- componentWillMount:组件即将被装载、渲染到页面上
- render:组件在这里生成虚拟的 DOM 节点
- componentDidMount:组件真正在被装载之后
- 运行中状态:
- componentWillReceiveProps:组件将要接收到属性的时候调用
- shouldComponentUpdate:组件接受到新属性或者新状态的时候(可以返回 false,接收数据后不更新,阻止 render 调用,后面的函数不会被继续执行了)
- componentWillUpdate:组件即将更新不能修改属性和状态
- render:组件重新描绘
- componentDidUpdate:组件已经更新
- 销毁阶段:
- componentWillUnmount:组件即将销毁
react****中组件传值
父传子(组件嵌套浅):父组件定义一个属性,子组件通过this.props接收。 子传父:父组件定义一个属性,并将一个回调函数赋值给定义的属性,然后子组件进行调用传过来的函数, 并将参数传进去,在父组件的回调函数中即可获得子组件传过来的值。
vue****组件之间如何传值通信
父到子:
子组件在props中创建一个属性,用来接收父组件传过来的值;
在父组件中注册子组件;
在子组件标签中添加子组件props中创建的属性;
把需要传给子组件的值赋给该属性
子到父:
子组件中需要以某种方式(如点击事件)的方法来触发一个自定义的事件;
将需要传的值作为$emit的第二个参数,该值将作为实参传给响应事件的方法;
在父组件中注册子组件并在子组件标签上绑定自定义事件的监听。
平行组件:
e m i t 推 送 , emit推送, emit推送,on接收
React 中 keys 的作用是什么?
Keys 是 React 用于追踪哪些列表中元素被修改、被添加或者被移除的辅助标识。
render ()
return (
- this.state.todoItems.map((item, key) => return
- item
- )
在开发过程中,我们需要保证某个元素的 key 在其同级元素中具有唯一性。在 React Diff 算法中 React 会借助元素的 Key 值来判断该元素是新近创建的还是被移动而来的元素,从而减少不必要的元素重渲染。此外,React 还需要借助 Key 值来判断元素与本地状态的关联关系,因此我们绝不可忽视转换函数中 Key 的重要性。
调用 setState 之后发生了什么?
在代码中调用 setState 函数之后,React 会将传入的参数对象与组件当前的状态合并,然后触发所谓的调和过程(Reconciliation)。经过调和过程,React 会以相对高效的方式根据新的状态构建 React 元素树并且着手重新渲染整个 UI 界面。在 React 得到元素树之后,React 会自动计算出新的树与老树的节点差异,然后根据差异对界面进行最小化重渲染。在差异计算算法中,React 能够相对精确地知道哪些位置发生了改变以及应该如何改变,这就保证了按需更新,而不是全部重新渲染。
React Hooks
所有组件都将是Function组件,这个函数却有自己的状态(count),同时它还可以更新自己的状态(setCount),除了useState这个hook外,还有很多别的hook,比如useEffect提供了类似于componentDidMount等生命周期钩子的功能,useContext提供了上下文(context)的功能等等。
useRef获取DOM元素和保存变量
useMemo优化React Hooks程序性能
import useState from ‘react’;
function Example()
const [count, setCount] = useState(0);
return (
You clicked count times
简单介绍下什么是hooks,hooks产生的背景?hooks的优点?
hooks是针对在使用react时存在以下问题而产生的: 1、组件之间复用状态逻辑很难,在hooks之前,实现组件复用,一般采用高阶组件和 Render Props,它们本质是将复用逻辑提升到父组件中,很容易产生很多包装组件,带来嵌套地域。 2、组件逻辑变得越来越复杂,尤其是生命周期函数中常常包含一些不相关的逻辑,完全不相关的代码却在同一个方法中组合在一起。如此很容易产生 bug,并且导致逻辑不一致。 3、复杂的class组件,使用class组件,需要理解 JavaScript 中 this 的工作方式,不能忘记绑定事件处理器等操作,代码复杂且冗余。除此之外,class组件也会让一些react优化措施失效。
针对上面提到的问题,react团队研发了hooks,它主要有两方面作用: 1、用于在函数组件中引入状态管理和生命周期方法 2、取代高阶组件和render props来实现抽象和可重用性
优点: 1、避免在被广泛使用的函数组件在后期迭代过程中,需要承担一些副作用,而必须重构成类组件,它帮助函数组件引入状态管理和生命周期方法。 2、Hooks 出现之后,我们将复用逻辑提取到组件顶层,而不是强行提升到父组件中。这样就能够避免 HOC 和 Render Props 带来的「嵌套地域」 3、避免上面陈述的class组件带来的那些问题
Hook****有哪些优势
- 减少状态逻辑复用的风险
Hook__和 Mixin__在用法上有一定的相似之处,但是 Mixin__引入的逻辑和状态是可以相互覆盖的,而多个 Hook__之间互不影响,这让我们不需要在把一部分精力放在防止避免逻辑复用的冲突上。在不遵守约定的情况下使用 HOC__也有可能带来一定冲突,比如 props__覆盖等_>_ 等,使用 Hook__则可以避免这些问题。
- 避免地狱式嵌套
大量使用 HOC__的情况下让我们的代码变得嵌套层级非常深,使用 Hook_,我们可以实现扁平式的状态逻辑复用,而避免了大量的组件嵌套。_
- 让组件更容易理解
在使用 class__组件构建我们的程序时,他们各自拥有自己的状态,业务逻辑的复杂使这些组件变得越来越庞大,各个生命周期中会调用越来越多的逻辑,越来越难以维护。使用 Hook_,可以让你更大限度的将公用逻辑抽离,将一个组件分割成更小的函数,而不是强制__>_ 基于生命周期方法进行分割。
- 使用函数代替class
相比函数,编写一个 class__可能需要掌握更多的知识,需要注意的点也越多,比如 this__指向、绑定事件等等。另外,计算机理解一个 _class__比理解一个函数更快。_Hooks__让你可以在 classes__之外使用更多 React__的新特性。
虚拟****Dom
虚拟DOM是在DOM的基础上建立了一个抽象层,对数据和状态所做的任何改动,都会被自动且高效的同步到虚拟DOM,最后再批量同步到DOM中。减少对真实Dom的操作,减少性能的消耗,保证非常高效的渲染。
因为改变真实dom,会导致整个dom树的重绘和回流
为什么虚拟 dom 会提高性能**(必考)**
虚拟 dom 相当于在 js 和真实 dom 中间加了一个缓存,利用 dom diff 算法避免了没有必要的 dom 操作,从而提高性能。
用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异把 2 所记录的差异应用到步骤 1 所构建的真正的 DOM 树上,视图就更新了。
react diff 原理(常考,大厂必考)
- 把树形结构按照层级分解,只比较同级元素。
- 给列表结构的每个单元添加唯一的 key 属性,方便比较。
- React 只会匹配相同 class 的 component(这里面的 class 指的是组件的名字)
- 合并操作,调用 component 的 setState 方法的时候, React 将其标记为 dirty.到每一个事件循环结束, React 检查所有标记 dirty 的 component 重新绘制.
- 选择性子树渲染。开发人员可以重写 shouldComponentUpdate 提高 diff 的性能。
React 中 refs 的作用是什么?
Refs 是 React 提供给我们的安全访问 DOM 元素或者某个组件实例的句柄。我们可以为元素添加 ref 属性然后在回调函数中接受该元素在 DOM 树中的句柄,该值会作为回调函数的第一个参数返回:
React****解决长列表方案
react-virtualized
react****路由懒加载
react-loadable
简述react、redux、react-redux、redux-saga之间的关系
【react】
- 定位:React 是一个用于构建用户界面的JavaScript库。
- 特点:它采用声明范式来描述应用,建立虚拟dom,支持JSX语法,通过react构建组件,能够很好的去复用代码;
- 缺点:react抽离了dom,使我们构建页面变得简单,但是对于一个大型复杂应用来说,只有dom层的便捷是不够的,如何组织、管理应用的代码,如何在组件件进行有效通信,这些它都没有解决;因此,它还需要一个前端架构才能应对大型应用;
【redux】
- 定位:它是将flux和函数式编程思想结合在一起形成的架构;
- 思想:视图与状态是一一对应的;所有的状态,都保存在一个对象里面;a
- API:
- store:就是一个数据池,一个应用只有一个;
- state:一个 State 对应一个 View。只要 State 相同,View 就相同。
- action:State 的变化,会导致 View 的变化。但是,用户接触不到 State,只能接触到 View。所以,State 的变化必须是 View 导致的。Action 就是 View 发出的通知,表示 State 应该要发生变化了。Action 是一个对象。其中的type``属性是必须的,表示 Action 的名称。其他属性可以自由设置。
- dispatch:它是view发出action的唯一方法;
- reducer:view发出action后,state要发生变化,reducer就是改变state的处理层,它接收action和state,通过处理action来返回新的state;
- subscribe:监听。监听state,state变化view随之改变;
【react-redux】
- 定位:react-redux是为了让redux更好的适用于react而生的一个库,使用这个库,要遵循一些规范;
- 主要内容
- UI组件:就像一个纯函数,没有state,不做数据处理,只关注view,长什么样子完全取决于接收了什么参数(props)
- 容器组件:关注行为派发和数据梳理,把处理好的数据交给UI组件呈现;React-Redux规定,所有的UI组件都由用户提供,容器组件则是由React-Redux自动生成。也就是说,用户负责视觉层,状态管理则是全部交给它。
- connect:这个方法可以从UI组件生成容器组件;但容器组件的定位是处理数据、响应行为,因此,需要对UI组件添加额外的东西,即mapStateToProps和mapDispatchToProps,也就是在组件外加了一层state;
- mapStateToProps:一个函数, 建立一个从(外部的)state对象到(UI组件的)props对象的映射关系。 它返回了一个拥有键值对的对象;
- mapDispatchToProps:用来建立UI组件的参数到store.dispatch方法的映射。 它定义了哪些用户的操作应该当作动作,它可以是一个函数,也可以是一个对象。
以上,redux的出现已经可以使react建立起一个大型应用,而且能够很好的管理状态、组织代码,但是有个棘手的问题没有很好地解决,那就是异步;
如果不用中间件middleware,Redux store只支持同步数据流。
【redux-saga】:
- 定位:react中间件;旨在于更好、更易地解决异步操作(action);redux-saga相当于在Redux原有数据流中多了一层,对Action进行监听,捕获到监听的Action后可以派生一个新的任务对state进行维护;
- 特点:通过 Generator 函数来创建,可以用同步的方式写异步的代码;
- API:
- Effect: 一个简单的对象,这个对象包含了一些给 middleware 解释执行的信息。所有的Effect 都必须被 yield 才会执行。
- put:触发某个action,作用和dispatch相同;
react中setState****为什么是异步的
也可以同步,
如果不是经过react处理的是同步,
定时器,不是合成事件都是同步的
shouldComponentUpdate **是做什么的,(**react 性能优化是哪个周期函数?)以上是关于2021-6月面试总结-vue,uniapp,小程序,h5,更新中的主要内容,如果未能解决你的问题,请参考以下文章