2020面试题大全(js/vue/react/webpack)

〇 Js

手写promise

const PENDING = 'pending';
const RESOLVED = 'resolved';
const REJECTED = 'rejected';

class MyPromise {
  constructor(fn) {
    this.state = PENDING;
    this.value = null; //保存resolve或reject中传入的值
    this.resolvedCallBacks = [];
    this.rejectedCallbacks = [];
    this.resolve = this.resolve.bind(this);
    this.reject = this.reject.bind(this);
    //执行fn函数
    try {
      fn(this.resolve, this.reject); //执行函数之后,将回调函数传入
    } catch (e) {
      this.reject(e);
    }
  }

  resolve(value) {
    //只有等待状态才能更改状态
    if (this.state === PENDING) {
      this.state = RESOLVED;
      this.value = value;
      //把存在调用栈里的then方法中的回调函数调用一下
      this.resolvedCallBacks.map((cb) => cb(this.value));
    }
  }

  reject(value) {
    if (this.state === PENDING) {
      this.state = REJECTED;
      this.value = value;
      this.rejectedCallbacks.map((cb) => cb(this.value));
    }
  }

  then(onFulfilled, onRejected) {
    //确保都转化为函数
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (v) => v;
    onRejected = typeof onRejected === 'function' ? onRejected : (err) => {
      throw err
    };
    switch (this.state) {
      case PENDING:
        this.resolvedCallBacks.push(onFulfilled);
        this.rejectedCallbacks.push(onRejected);
        break;
      case RESOLVED:
        //执行回调函数
        onFulfilled(this.value);
        break;
      case REJECTED:
        onRejected(this.value);
        break;
      default:
        throw 'unknown state';

    }
  }
}

const promise = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('hello world')
  }, 100)
});

promise.then((value) => {
  console.log(value)
});




一 理论

什么是 mvvm?

MVVM 是 Model-View-ViewModel 的缩写。mvvm 是一种设计思想。Model 层代表数据模型,也可以在 Model 中定义数据修改和操作的业务逻辑;View 代表 UI 组件,它负责将数据模型转化成 UI 展现出来,ViewModel 是一个同步 View 和 Model 的对象。

在 MVVM 架构下,View 和 Model 之间并没有直接的联系,而是通过 ViewModel 进行交互,Model 和 ViewModel 之间的交互是双向的, 因此 View 数据的变化会同步到 Model 中,而 Model 数据的变化也会立即反应到 View 上。

ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,而 View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作 DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。

mvvm 和 mvc 区别?

mvc 和 mvvm 其实区别并不大。都是一种设计思想。主要就是 mvc 中 Controller 演变成 mvvm 中的 viewModel。mvvm 主要解决了 mvc 中大量的 DOM 操作使页面渲染性能降低,加载速度变慢,影响用户体验。和当 Model 频繁发生变化,开发者需要主动更新到 View 。

Vue 和 React 之间的区别

Vue 的表单可以使用 v-model 支持双向绑定,相比于 React 来说开发上更加方便,当然了 v-model 其实就是个语法糖,本质上和 React 写表单的方式没什么区别。

改变数据方式不同,Vue 修改状态相比来说要简单许多,React 需要使用 setState 来改变状态,并且使用这个 API 也有一些坑点。并且 Vue 的底层使用了依赖追踪,页面更新渲染已经是最优的了,但是 React 还是需要用户手动去优化这方面的问题。

React 16以后,有些钩子函数会执行多次,这是因为引入 Fiber 的原因,这在后续的章节中会讲到。

React 需要使用 JSX,有一定的上手成本,并且需要一整套的工具链支持,但是完全可以通过 JS 来控制页面,更加的灵活。Vue 使用了模板语法,相比于 JSX 来说没有那么灵活,但是完全可以脱离工具链,通过直接编写 render 函数就能在浏览器中运行。

在生态上来说,两者其实没多大的差距,当然 React 的用户是远远高于 Vue 的。

在上手成本上来说,Vue 一开始的定位就是尽可能的降低前端开发的门槛,然而 React 更多的是去改变用户去接受它的概念和思想,相较于 Vue 来说上手成本略高。

react与vue的对比

有些是个人意见,仅供参考。

相同点:

1. 都用虚拟DOM实现快速渲染
2. 我觉得父子,兄弟通信这些都挺像的,也都有自己的状态管理器:react=>redux, vue=>vuex
3. 都是轻量级框架
4. 现在vue也在渐渐吸收react中的一些语法,比如JSX语法,类式声明写法等

不同点:

1. React属于单向数据流——MVC模式,vue则属于双向——MVVM模式。
2. react兼容性比vue好,vue不兼容IE8.
3. react采用JSX语法,vue采用的则是html模板语法。
4. vue的css可以有组件的私有作用域,react则没有。
5. react比vue好的另一点是,它是团队维护,而vue属于个人,一般来说,大型项目更倾向于react,小型则用vue,当然这也不是绝对。

vue和react的虚拟dom有区别吗?

没有区别。

react和vue的虚拟dom都是一样的, 都是用JS对象来模拟真实DOM,然后用虚拟DOM的diff来最小化更新真实DOM。

除了极个别实现外,两者前半部分(用JS对象来模拟真实DOM)几乎是一样的。

而对于后半部分(用虚拟DOM的diff来最小化更新真实DOM)两者算法也是类似的,包括replace delete insert等

vue首屏加载过慢如何解决?

  • 路由懒加载,会将原来打包一个app.js的文件打包成多个文件 ,异步组件,按需加载
  • webpack开启gzip压缩
  • 如果图片过多,开启图片懒加载
  • 使用cdn资源
  • 如果首页是登录页,做多入口

你之前有做过spa类型的项目吗?怎么实现的?

spa就是单页面应用程序,而单页面应用程序,主要依靠路由来实现,路由根据不同的hash值来展示不同的组件

koa2、koa1、express比较

**在koa中,一切的流程都是中间件,数据流向遵循洋葱模型,先入后出,是按照类似堆栈的方式组织和执行的,**koa-compose是理解koa中间件的关键,在koa中间件中会深入分析。
 koa2与koa1的最大区别是koa2实现异步是通过async/awaite,koa1实现异步是通过generator/yield,而express实现异步是通过回调函数的方式。
 koa2与express 提供的API大致相同,express是大而全,内置了大多数的中间件,更让人省心,koa2不绑定任何的框架,干净简洁,小而精,更容易实现定制化,扩展性好。
 express是没有提供ctx来提供上下流服务,需要更多的手动处理,express本身是不支持洋葱模型的数据流入流出能力的,需要引入其他的插件。

koa的数据流入流出,next()后,会进入下一个中间件并执行,然后从最后一个中间件反向执行。

二 Vue

Vue面试题

https://zhuanlan.zhihu.com/p/97950650

vue 的优点是什么?

  • 低耦合。视图(View)可以独立于 Model 变化和修改,一个 ViewModel 可以绑定到不同的"View"上,当 View 变化的时候 Model 可以不变,当 Model 变化的时候 View 也可以不变。
  • 可重用性。你可以把一些视图逻辑放在一个 ViewModel 里面,让很多 view 重用这段视图逻辑。
  • 独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计,使用 Expression Blend 可以很容易设计界面并生成 xml 代码。
  • 可测试。界面素来是比较难于测试的,而现在测试可以针对 ViewModel 来写。

vue生命周期的理解?

vue实例有一个完整的生命周期,生命周期也就是指一个实例从开始创建到销毁的这个过程

总共分为 8 个阶段创建前/后,载入前/后,更新前/后,销毁前/后。

  • beforeCreated() 在实例创建之间执行,数据未加载状态
  • created() 在实例创建、数据加载后,能初始化数据,dom渲染之前执行
  • beforeMount() 虚拟dom已创建完成,在数据渲染前最后一次更改数据
  • mounted() 页面、数据渲染完成,真实dom挂载完成
  • beforeUpadate() 重新渲染之前触发
  • updated() 数据已经更改完成,dom 也重新 render 完成,更改数据会陷入死循环
  • beforeDestory()destoryed() 前者是销毁前执行(实例仍然完全可用),后者则是销毁后执行

vue 虚拟dom(vdom)

什么是虚拟dom呢
所谓的Virtual dom,也就是我们常说的虚拟节点,它是通过JSObject对象模拟DOM中的节点,然后再通过特定的render方法将其渲染成真实的DOM的节点。
简单点讲,在Vue的底层实现上,Vue将模板编译成虚拟DOM渲染函数。结合Vue自带的响应系统,在状态改变时,Vue能够智能地计算出重新渲染组件的最小代价并应到DOM操作上。
dom的解析流程
浏览器渲染引擎工作流程都差不多,大致分为5步,创建DOM树——创建StyleRules——创建Render树——布局Layout——绘制Painting
第一步,用HTML分析器,分析HTML元素,构建一颗DOM树(标记化和树构建)。
第二步,用CSS分析器,分析CSS文件和元素上的inline样式,生成页面的样式表。
第三步,将DOM树和样式表,关联起来,构建一颗Render树(这一过程又称为Attachment)。每个DOM节点都有attach方法,接受样式信息,返回一个render对象(又名renderer)。这些render对象最终会被构建成一颗Render树。
第四步,有了Render树,浏览器开始布局,为每个Render树上的节点确定一个在显示屏上出现的精确坐标。
第五步,Render树和节点显示坐标都有了,就调用每个节点paint方法,把它们绘制出来。

为什么要用虚拟dom呢?来看下js用dom操作的代价

用我们传统的开发模式,原生JS或JQ操作DOM时,浏览器会从构建DOM树开始从头到尾执行一遍流程。在一次操作中,我需要更新10个DOM节点,浏览器收到第一个DOM请求后并不知道还有9次更新操作,因此会马上执行流程,最终执行10次。例如,第一次计算完,紧接着下一个DOM更新请求,这个节点的坐标值就变了,前一次计算为无用功。计算DOM节点坐标值等都是白白浪费的性能。即使计算机硬件一直在迭代更新,操作DOM的代价仍旧是昂贵的,频繁操作还是会出现页面卡顿,影响用户体验。

vue等单页面应用及其优缺点

缺点:

不支持低版本的浏览器,最低只支持到IE9;
不利于SEO的优化(如果要支持SEO,建议通过服务端来进行渲染组件);
第一次加载首页耗时相对长一些;
不可以使用浏览器的导航按钮需要自行实现前进、后退。

优点:

无刷新体验,提升了用户体验;
前端开发不再以页面为单位,更多地采用组件化的思想,代码结构和组织方式更加规范化,便于修改和调整;
API 共享,同一套后端程序代码不用修改就可以用于Web界面、手机、平板等多种客户端
用户体验好、快,内容的改变不需要重新加载整个页面。

那么为什么用虚拟dom呢?

Web界面由DOM树(树的意思是数据结构)来构建,当其中一部分发生变化时,其实就是对应某个DOM节点发生了变化,
虚拟DOM就是为了解决浏览器性能问题而被设计出来的。如前,若一次操作中有10次更新DOM的动作,虚拟DOM不会立即操作DOM,而是将这10次更新的diff内容保存到本地一个JS对象中,最终将这个JS对象一次性attch到DOM树上,再进行后续操作,避免大量无谓的计算量。所以,用JS对象模拟DOM节点的好处是,页面的更新可以先全部反映在JS对象(虚拟DOM)上,操作内存中的JS对象的速度显然要更快,等更新完成后,再将最终的JS对象映射成真实的DOM,交由浏览器去绘制。

大白话:就是为了提高代码效率,渲染效果,就如往常的js jquery这些使用dom操作时都是一段复杂的过程,整个过程要遍历属性,标签啦,就是在你的代码上一遍遍寻找大半天还不知道有没有收获,很慢。而且做多件事情的时候只能一件件去做,浪费很多时间,而我们的虚拟dom呢,就是,把你想要做的事情都用小本子记起来(记在本地js对象上)在虚拟dom上更新完成后,再拿到真是的dom上去渲染,然后交给浏览器绘制页面呀。从头到末尾,人家知道每一步要做什么,而且可以同时做,还不会等到你的页面渲染完成后才出发,人家可是早早出发了,

真实DOM和虚拟DOM的区别

那么说回到真实DOM虚拟DOM真实DOM又有什么区别呢?
我想,应该会有一下几点:

  • 虚拟DOM不会进行排版与重绘操作
  • 真实DOM频繁排版与重绘的效率是相当低的
  • 虚拟DOM进行频繁修改,然后一次性比较并修改真实DOM中需要改的部分,最后并在真实DOM中进行排版与重绘,减少过多DOM节点排版与重绘损耗
  • 虚拟DOM有效降低大面积(真实DOM节点)的重绘与排版,因为最终与真实DOM比较差异,可以只渲染局部

vue中v-if和v-show有什么区别?

v-if和v-show都会让元素显示和隐藏,但是v-if是通过移除和添加dom元素,v-show是通过display:none来实现隐藏

v-show是css切换,v-if是完整的销毁和重新创建
使用频繁切换时用v-show,运行时较少改变时用v-if
V-if=’false’v-if是条件渲染,当false的时候不会渲染
使用v-if的时候,如果值为false,那么页面将不会有这个html标签生成
v-show则是不管值是为true还是false,html元素都会存在,只是css中的display显示或隐藏
v-show 仅仅控制元素的显示方式,将 display 属性在 block 和 none 来回切换;而v-if会控制这个 DOM 节点的存在与否。当我们需要经常切换某个元素的显示/隐藏时,使用v-show会更加节省性能上的开销;当只需要一次显示或隐藏时,使用v-if更加合理。

为什么在使用v-for的时候需要添加key属性

因为vue在更新渲染dom的时候是根据新旧dom树进行对比的,使用key来给每个节点做一个唯一标识,Diff算法就可以正确的识别此节点,找到正确的位置区插入新的节点。Vue的优化点之一

vue中的父子组件传值和兄弟组件传值都是如何实现的?

父向子传值,主要通过子组件的props,获取父组件绑定的数据

子向父传值,主要通过子组件利用$emit触发父组件上的事件

兄弟组件传值利用eventbus的方式,主要利用创建一个空的vm实例,作为中间者

组件之间数据共享

组件是 vue.js最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引用。针对不同的使用场景,如何选择行之有效的通信方式?

1:props emit 缺点:如果组件嵌套层次多的话,数据传递比较繁琐
2:provide inject (依赖注入),缺点:不支持响应式
3:this. r o o t t h i s . root this. rootthis.parent this.$refs
4: eventbus 缺点:数据不支持响应式
5: vuex 缺点:数据的读取和修改需要按照流程来操作,不适合小型项目

父子通信:
父组件向子组件传递数据可以通过 props
子组件向父组件是通过 $emit$on事件;
provide / inject
还可以通过 $root$parent$refs属性相互访问组件实例;
兄弟通信: eventbusVuex
跨级通信: eventbusVuexprovide / inject;

vue 的双向绑定的原理是什么(常考)

vue.js 是采用数据劫持结合发布者-订阅者模式的方式,通过 Object.defineProperty()来劫持各个属性的 setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。

具体步骤:
第一步:需要 observe 的数据对象进行递归遍历,包括子属性对象的属性,都加上 setter 和 getter 这样的话,给这个对象的某个值赋值,就会触发 setter,那么就能监听到了数据变化

第二步:compile 解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图

第三步:Watcher 订阅者是 Observer 和 Compile 之间通信的桥梁,主要做的事情是:

  • 在自身实例化时往属性订阅器(dep)里面添加自己
  • 自身必须有一个 update()方法
  • 待属性变动 dep.notice()通知时,能调用自身的 update() 方法,并触发 Compile 中绑定的回调,则功成身退。

第四步:MVVM 作为数据绑定的入口,整合 Observer、Compile 和 Watcher 三者,通过 Observer 来监听自己的 model 数据变化,通过 Compile 来解析编译模板指令,最终利用 Watcher 搭起 Observer 和 Compile 之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据 model 变更的双向绑定效果。

vue3中的双向绑定的原理

Vue3.x是用ES6的语法 Proxy对象来实现的,

如何让css只在当前组件中起作用

在每一个vue组件中都可以定义各自的css,js,如果希望组件内写的css只对当前组件起作用,只需要在style中写入scoped,即:


$root$parent$refs

1、$root Vue 子组件可以通过$root 属性获取vue的根实例,比如在简单的项目中将公共数据放再vue根实例上(可以理解为一个全局 store ),因此可以代替vuex实现状态管理;

2、$parent 属性可以用来从一个子组件访问父组件的实例,可以替代将数据以 prop 的方式传入子组件的方式;当变更父级组件的数据的时候,容易造成调试和理解难度增加;

3、在子组件上使用ref特性后,this.$refs 属性可以直接访问该子组件。可以代替事件$emit$on 的作用。使用方式是通过 ref 特性为这个子组件赋予一个 ID 引用,再通过this.$refs.testId获取指定元素。注意:$refs 只会在组件渲染完成之后生效,并且它们不是响应式的。这仅作为一个用于直接操作子组件的“逃生舱”——你应该避免在模板或计算属性中访问 $refs

Vue输入框事件监听blur与change的差异

blur与change事件在绝大部分的情况下表现都非常相似,输入结束后,离开输入框,会先后触发change与blur,唯有两点例外。

  1. 没有进行任何输入时,不会触发change
    在这种情况下,输入框并不会触发change事件,但一定会触发blur事件。在判断表单的修改状态时,这种差异会非常有用,通过change事件能轻易地找到哪些字段发生了变更以及其值的变更轨迹。
  2. 输入后值并没有发生变更
    这种情况是指,在没有失焦的情况下,在输入框内进行返回的删除与输入操作,但最终的值与原值一样,这种情况下,keydown、input、keyup、blur都会触发,但change依旧不会触发。

vue store存储commit 和dispatch

主要区别是:

  • dispatch:含有异步操作,例如向后台提交数据,写法: this.$store.dispatch('action方法名',值)
  • commit:同步操作,写法:this.$store.commit('mutations方法名',值)

Vue项目性能优化

1.懒加载

懒加载应该是提高性能的最简单有效的方式了,一个项目加上懒加载速度和逼格都会上一个台阶。懒加载的意义在于按需加载,不会让项目刚开始运行速度就很慢,能大大的优化用户体验。Vue项目懒加载分为图片懒加载和路由懒加载,具体写法如下:

路由懒加载

{
     
  path: '/home',
  name: 'home',
  component: resolve => require(['@/components/home'],resolve)
},{
     
  path: '/index',
  name: 'Index',
  component: resolve => require(['@/components/index'],resolve)
},{
     
  path: '/about',
  name: 'about',
  component: resolve => require(['@/components/about'],resolve)
} 

图片懒加载



2.代码优化

  • 不要将所有的数据都放在data中,data中的数据都会增加getter和setter,会收集对应的 watcher
  • vue 在 v-for 时给每项元素绑定事件需要用事件代理
  • SPA 页面采用keep-alive缓存组件
  • 拆分组件( 提高复用性、增加代码的可维护性,减少不必要的渲染 )
  • v-if 当值为false时内部指令不会执行,具有阻断功能,很多情况下使用v-if替代v-show
  • key 保证唯一性 ( 默认 vue 会采用就地复用策略 )
  • Object.freeze 冻结数据
  • 合理使用路由懒加载、异步组件
  • 尽量采用runtime运行时版本
  • 数据持久化的问题 (防抖、节流)
  • 减少本地储存

3.用户体验优化

添加Loading

当用户需要等待时间较长时,必须添加等待loading,这个不多说,用处大大地

添加骨架屏

路由逻辑

路由逻辑是一个项目的核心,如果路由逻辑不通的话,用户很有可能点返回按钮的时候一直在两个页面之间跳转,进入死循环。其次,路由逻辑和用户体验息息相关,比如用户下完单应该跳转到订单详情页,而不是首页等等

样式统一

两个页面的相同功能按钮,它的大小颜色如果不用的话就会让人感觉你的App不够专业

vue-cli如何新增自定义指令?

分为局部指令和全局指令,通过directive [dəˈrektɪv] ,创建,

在单文件中创建局部指令,使用Vue.directive创建全局指令

vue更新数组时触发视图更新的方法

Vue.set    ==========Vue.set(target,key,value)这个方法主要是用于避开vue不能检测属性被添加的限制
Vue.set(array, indexOfItem, newValue)//indexOfItem指的索引
this.array.$set(indexOfItem, newValue)
Vue.set(obj, keyOfItem, newValue)
this.obj.$set(keyOfItem, newValue)
Vue.delete   这个方法主要用于避开vue不能检测到属性被删除;

Vue.delete(array, indexOfItem)
this.array.$delete(indexOfItem)
Vue.delete(obj, keyOfItem)
this.obj.$delete(keyOfItem)

Vue 对象怎么添加删除 ? $set 有神吗作用

当⽣成vue实例,再次给数据赋值,有时候数据并没有更新视图,是因为受到es5 的限制,vue不能检测到对象属性的添加或者删除,vue在初始化实例的时候将属性 转换为getter/setter, 使⽤ s e t , 让 其 有 g e t t e r / s e t t e r V u e . s e t ( ) 是 将 s e t 函 数 绑 定 在 V u e 的 构 造 函 数 上 , t h i s . set,让其有getter/setter Vue.set()是将set函数绑定在Vue的构造函数上,this. setgetter/setterVue.set()setVuethis.set是将set函数绑定在Vue原 型上

Vue中computed和watch的区别

computed特性
1.是计算值,
2.应用:就是简化tempalte里面{ {}}计算和处理props或$emit的传值
3.具有缓存性,页面重新渲染值不变化,计算属性会立即返回之前的计算结果,而不必再次执行函数

watch特性
1.是观察的动作,
2.应用:监听props,$emit或本组件的值执行异步操作
3.无缓存性,页面重新渲染时值不变化也会执行

  • 计算属性可以简化差值表达式写法
  • 计算属性变量定义在computed中,可以直接使用在{}中的,跟methods中函数类似,只不过有利于缓存,性能更好
  • 计算属性可以防止监听属性的滥用,但一些异步请求,计算属性做不到,还得watch来完成。

methods与computed的区别

  • computed是属性调用,而methods是函数调用;
  • computed带有缓存功能,而methods不是;
  • 我们可以使用 methods 来替代 computed,效果上两个都是一样的,但是 computed 是基于它的依赖缓存,只有相关依赖发生改变时才会重新取值。而使用 methods ,在重新渲染的时候,函数总会重新调用执行。
  • 可以说使用 computed 性能会更好,但是如果你不希望缓存,你可以使用 methods 属性。

三 Vuex

vuex 原理

vuex 仅仅是作为 vue 的一个插件而存在,不像 Redux,MobX 等库可以应用于所有框架,vuex 只能使用在 vue 上,很大的程度是因为其高度依赖于 vue 的 computed 依赖检测系统以及其插件系统,

vuex 整体思想诞生于 flux,可其的实现方式完完全全的使用了 vue 自身的响应式设计,依赖监听、依赖收集都属于 vue 对对象 Property set get 方法的代理劫持。最后一句话结束 vuex 工作原理,vuex 中的 store 本质就是没有 template 的隐藏着的 vue 组件;

Vuex实现了一个单向数据流,在全局拥有一个State存放数据,当组件要更改State中的数据时,必须通过Mutation进行,Mutation同时提供了订阅者模式供外部插件调用获取State数据的更新。而当所有异步操作(常见于调用后端接口异步获取更新数据)或批量的同步操作需要走action,但action也是无法直接修改State的,还是需要通过Mutation来修改State的数据。最后,根据State的变化,渲染到视图上。

vuex 是什么?怎么使用?哪种功能场景使用它?

vue 框架中全局状态管理。新建了一个目录 store,…… export 。import 引入 。场景有:单页应用中,组件之间的状态。音乐播放、登录状态、加入购物车

简要介绍各模块在流程中的功能:

state
Vuex 使用单一状态树,即每个应用将仅仅包含一个store 实例,但单一状态树和模块化并不冲突。存放的数据状态,不可以直接修改里面的数据。
mutations
mutations定义的方法动态修改Vuex 的 store 中的状态或数据。
getters
类似vue的计算属性,主要用来过滤一些数据。
action
actions可以理解为通过将mutations里面处里数据的方法变成可异步的处理数据的方法,简单的说就是异步操作数据。view 层通过 store.dispath 来分发 action。

modules
项目特别复杂的时候,可以让每一个模块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理。

vuex 有哪几种属性

有 5 种,分别是 state、getter、mutation、action、module

vuex 的 store 特性是什么

  • vuex 就是一个仓库,仓库里放了很多对象。其中 state 就是数据源存放地,对应于一般 vue 对象里面的 data
  • state 里面存放的数据是响应式的,vue 组件从 store 读取数据,若是 store 中的数据发生改变,依赖这相数据的组件也会发生更新
  • 它通过 mapState 把全局的 state 和 getters 映射到当前组件的 computed 计算属性

vuex 的 getter 特性是什么

  • getter 可以对 state 进行计算操作,它就是 store 的计算属性
  • 虽然在组件内也可以做计算属性,但是 getters 可以在多给件之间复用
  • 如果一个状态只在一个组件内使用,是可以不用 getters

vuex 的 mutation 特性是什么

  • action 类似于 muation, 不同在于:action 提交的是 mutation,而不是直接变更状态
  • action 可以包含任意异步操作

vue 中 ajax 请求代码应该写在组件的 methods 中还是 vuex 的 action 中

如果请求来的数据不是要被其他组件公用,仅仅在请求的组件内使用,就不需要放入 vuex 的 state 里

如果被其他地方复用,请将请求放入 action 里,方便复用,并包装成 promise 返回

不用 vuex 会带来什么问题

  • ​ 可维护性会下降,你要修改数据,你得维护 3 个地方
  • 可读性下降,因为一个组件里的数据,你根本就看不出来是从哪里来的
  • 增加耦合,大量的上传派发,会让耦合性大大的增加,本来 Vue 用 Component 就是为了减少耦合,现在这么用,和组件化的初衷相背

使用 Vuex 只需执行 Vue.use(Vuex),并在 Vue 的配置中传入一个 store 对象的示例,store 是如何实现注入的?美团

Vue.use(Vuex) 方法执行的是 install 方法,它实现了 Vue 实例对象的 init 方法封装和注入,使传入的 store 对象被设置到 Vue 上下文环境的store中。因此在VueComponent任意地方都能够通过this.store 访问到该 store。

state 内部支持模块配置和模块嵌套,如何实现的?美团

在 store 构造方法中有 makeLocalContext 方法,所有 module 都会有一个 local context,根据配置时的 path 进行匹配。所以执行如 dispatch(‘submitOrder’, payload)这类 action 时,默认的拿到都是 module 的 local state,如果要访问最外层或者是其他 module 的 state,只能从 rootState 按照 path 路径逐步进行访问。

在执行 dispatch 触发 action(commit 同理)的时候,只需传入(type, payload),action 执行函数中第一个参数 store 从哪里获取的?美团

store 初始化时,所有配置的 action 和 mutation 以及 getters 均被封装过。在执行如 dispatch(‘submitOrder’, payload)的时候,actions 中 type 为 submitOrder 的所有处理方法都是被封装后的,其第一个参数为当前的 store 对象,所以能够获取到 { dispatch, commit, state, rootState } 等数据。

Vuex 如何区分 state 是外部直接修改,还是通过 mutation 方法修改的?美团

Vuex 中修改 state 的唯一渠道就是执行 commit(‘xx’, payload) 方法,其底层通过执行 this._withCommit(fn) 设置_committing 标志变量为 true,然后才能修改 state,修改完毕还需要还原_committing 变量。外部修改虽然能够直接修改 state,但是并没有修改_committing 标志位,所以只要 watch 一下 state,state change 时判断是否_committing 值为 true,即可判断修改的合法性。

四 vue-router

vue-router是什么?有哪些组件?

  • Vue Router 是 Vue.js 官方的路由管理器。它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌。

active-class 是哪个组件的属性?

active-class是router-link终端属性,用来做选中样式的切换,当router-link标签被点击时将会应用这个样式

vue路由的钩子函数

首页可以控制导航跳转,beforeEach,afterEach等,一般用于页面title的修改。一些需要登录才能调整页面的重定向功能。

beforeEach主要有3个参数to,from,next:

to:route即将进入的目标路由对象,

from:route当前导航正要离开的路由

next:function一定要调用该方法resolve这个钩子。执行效果依赖next方法的调用参数。可以控制网页的跳转。

指令keep-alive

keep-alive的作用以及好处

在做电商有关的项目中,当我们第一次进入列表页需要请求一下数据,当我从列表页进入详情页,详情页不缓存也需要请求下数据,然后返回列表页,这时候我们使用keep-alive来缓存组件,防止二次渲染,这样会大大的节省性能。

当引入keep-alive的时候,页面第一次进入,钩子的触发顺序created-> mounted-> activated,退出时触发deactivated。当再次进入(前进或者后退)时,只触发activated。

在vue-router写着keep-alive,keep-alive的含义:

如果把切换出去的组件保留在内存中,可以保留它的状态或避免重新渲染。为此可以添加一个keep-alive指令

keep-alive是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。
在vue 2.1.0 版本之后,keep-alive新加入了两个属性: include(包含的组件缓存) 与 exclude(排除的组件不缓存,优先级大于include) 。

使用方法


  
    
  

参数解释
include - 字符串或正则表达式,只有名称匹配的组件会被缓存
exclude - 字符串或正则表达式,任何名称匹配的组件都不会被缓存
include 和 exclude 的属性允许组件有条件地缓存。二者都可以用“,”分隔字符串、正则表达式、数组。当使用正则或者是数组时,要记得使用v-bind 。

怎么定义vue-router的动态路由?怎么获取传过来的值?

  • 动态路由的创建,主要是使用path属性过程中,使用动态路径参数,以冒号开头,如下:
{
     
  path: '/details/:id'
  name: 'Details'
  components: Details
}
12345

访问details目录下的所有文件,如果details/a,details/b等,都会映射到Details组件上。

  • 当匹配到/details下的路由时,参数值会被设置到this.$route.params下,所以通过这个属性可以获取动态参数
console.log(this.$route.params.id)

vue-router 传参

name传递
to来传递
采用url传参

Params

  • 只能使用name,不能使用path
  • 参数不会显示在路径上
  • 浏览器强制刷新参数会被清空,

Query:

  • 参数会显示在路径上,刷新不会被清空
  • name 可以使用path路径

vue-router的两种模式

hash

  • 原理是onhashchage事件,可以在window对象上监听这个事件

history

  • 利用了HTML5 History Interface 中新增的pushState()和replaceState()方法。
  • 需要后台配置支持。如果刷新时,服务器没有响应响应的资源,会刷出404,

vue-router 的导航钩子,主要用来作用是拦截导航,让他完成跳转或取消。

全局的:**前置守卫、后置钩子(beforeEach,afterEach)beforeResolve

**单个路由独享的:**beforeEnter

**组件级的:beforeRouteEnter(不能获取组件实例 this)、beforeRouteUpdate、beforeRouteLeave
这是因为在执行路由钩子函数beforRouteEnter时候,组件还没有被创建出来;
先执行beforRouteEnter,再执行组件周期钩子函数beforeCreate,可以通过 next 获取组件的实例对象,如:next( (vm)=>{} ),参数vm就是组件的实例化对象。

完整的 vue-router 导航解析流程

1.导航被触发;
2.在失活的组件里调用beforeRouteLeave守卫;
3.调用全局beforeEach守卫;
4.在复用组件里调用beforeRouteUpdate守卫;
5.调用路由配置里的beforeEnter守卫;
6.解析异步路由组件;
7.在被激活的组件里调用beforeRouteEnter守卫;
8.调用全局beforeResolve守卫;
9.导航被确认;
10…调用全局的afterEach钩子;
11.DOM更新;
12.用创建好的实例调用beforeRouteEnter守卫中传给next的回调函数。

五 React

什么是React?

  • React 是 Facebook 在 2011 年开发的前端 JavaScript 库。
  • 它遵循基于组件的方法,有助于构建可重用的UI组件。
  • 它用于开发复杂和交互式的 Web 和移动 UI。
  • 尽管它仅在 2015 年开源,但有一个很大的支持社区。

React有什么特点?

React的主要功能如下:

  1. 它使用**虚拟DOM **而不是真正的DOM。
  2. 它可以用服务器端渲染
  3. 它遵循单向数据流或数据绑定。

列出React的一些主要优点。

React的一些主要优点是:

  1. 它提高了应用的性能
  2. 可以方便地在客户端和服务器端使用
  3. 由于 JSX,代码的可读性很好
  4. React 很容易与 Meteor,Angular 等其他框架集成
  5. 使用React,编写UI测试用例变得非常容易

React有哪些限制?

React的限制如下:

  1. React 只是一个库,而不是一个完整的框架
  2. 它的库非常庞大,需要时间来理解
  3. 新手程序员可能很难理解
  4. 编码变得复杂,因为它使用内联模板和 JSX

什么是JSX?

JSX 是J avaScript XML 的简写。是 React 使用的一种文件,它利用 JavaScript 的表现力和类似 HTML 的模板语法。这使得 HTML 文件非常容易理解。此文件能使应用非常可靠,并能够提高其性能。

什么是虚拟DOM

【React深入】深入分析虚拟DOM的渲染原理和特性_code秘密花园 - SegmentFault 思否

虚拟DOM是一个js对象。怎么理解这个js对象呢?

我们都知道对象使用之前要定义,不定义直接使用的话,就会报错。也就是说是用不了。

而这个虚拟DOM呢,就像是一个空对象,操作虚拟DOM就像是网这个对象里面添加属性和对应的属性值。对象内的属性全部定义好了之后,

再按照这个对象里面的内容,全部转化,输送给真实的DOM,让其在页面中渲染出来。

因为这个对象不可能一直保持不变,在开发中出现代码的增删改是很正常的现象。

操作的是虚拟DOM,那是所以只要对象一改动,就能马上反应到虚拟DOM上。

那又是如何反应的呢?

这里面又牵扯到一个概念就是diff算法。

反应的方式就是通过diff算法来实现的。

对象更新时会生成一个新DOM对象,diff算法就是把新的DOM对象和老的DOM对象进行比较,

1、发现新属性里没有老属性,那么老属性就直接卸载;新属性直接安装

2、发现新属性的和老属性全部相同(属性值也有可能是对象,也要往下面核对),那么就保留

3、发现同一个属性里,新和老的有些值不同,那么则要排列比较,看哪些修改了,哪些是新增的,哪些是删除的。

这样在一定程度上也比更深的遍历发现同更加节约时间。

因为老对象的改变,则会使真实的DOM也会发生改变,这样就在页面上实现了刷新

在上面3的内容中,我们又可以得到一个知识点:那就是虚拟DOM的key值。

key值就相当于给这些属性值加上了一个标签,这样我们就可以通过这个key值去找到对应的值与之比较或是别的操作。

所以,这里我们就要求key值最好是独一无二的。

如果用index作为key值得话,如果值得位置有变动,那么对应的index指向的就是变动后的值,所以变动后的值的位置和原来的位置是不是一样,不能确定

因此,index作为key值会有可能会存在一定偏差。

为什么虚拟 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 生命周期函数

  • 初始化阶段:
    • getDefaultProps:获取实例的默认属性
    • getInitialState:获取每个实例的初始化状态
    • componentWillMount:组件即将被装载、渲染到页面上
    • render:组件在这里生成虚拟的 DOM 节点
    • componentDidMount:组件真正在被装载之后
  • 运行中状态:
    • componentWillReceiveProps:组件将要接收到属性的时候调用
    • shouldComponentUpdate:组件接受到新属性或者新状态的时候(可以返回 false,接收数据后不更新,阻止 render 调用,后面的函数不会被继续执行了)
    • componentWillUpdate:组件即将更新不能修改属性和状态
    • render:组件重新描绘
    • componentDidUpdate:组件已经更新
  • 销毁阶段:
    • componentWillUnmount:组件即将销毁

展示组件(Presentational component)和容器组件(Container component)之间有何不同

  • 展示组件关心组件看起来是什么。展示专门通过 props 接受数据和回调,并且几乎不会有自身的状态,但当展示组件拥有自身的状态时,通常也只关心 UI 状态而不是数据的状态。
  • 容器组件则更关心组件是如何运作的。容器组件会为展示组件或者其它容器组件提供数据和行为(behavior),它们会调用 Flux actions,并将其作为回调提供给展示组件。容器组件经常是有状态的,因为它们是(其它组件的)数据源。

类组件(Class component)和函数式组件(Functional component)之间有何不同

  • 类组件不仅允许你使用更多额外的功能,如组件自身的状态和生命周期钩子,也能使组件直接访问 store 并维持状态
  • 当组件仅是接收 props,并将组件自身渲染到页面时,该组件就是一个 ‘无状态组件(stateless component)’,可以使用一个纯函数来创建这样的组件。这种组件也被称为哑组件(dumb components)或展示组件

(组件的)状态(state)和属性(props)之间有何不同

  • State 是一种数据结构,用于组件挂载时所需数据的默认值。State 可能会随着时间的推移而发生突变,但多数时候是作为用户事件行为的结果。
  • Props(properties 的简写)则是组件的配置。props 由父组件传递给子组件,并且就子组件而言,props 是不可变的(immutable)。组件不能改变自身的 props,但是可以把其子组件的 props 放在一起(统一管理)。Props 也不仅仅是数据–回调函数也可以通过 props 传递。

React中 state 的用法

state 是一种数据结构,用于组件挂载时所需数据的默认值。state 可能会随着时间的推移而发生突变,但多数时候是作为用户事件行为的结果。
Props则是组件的配置。props 由父组件传递给子组件,并且就子组件而言,props 是不可变的(immutable)

setState为什么是异步的

参考链接:React 中 setState() 为什么是异步的?、 react的setstate原理

1.保证内部的一致性

因为props是要等到父组件渲染过后才能拿到,也就是不能同步更新,state出于统一性设成异步更新。

2.性能优化

举例说你正在一个聊天窗口输入,如果来了一条新消息又要render,那就会阻塞你的当前操作,导致延迟什么的。

3.支持state在幕后渲染

异步可以使state在幕后更新,而不影响你当前旧的页面的交互,提升用户体验。

为什么建议传递给 setState 的参数是一个 callback 而不是一个对象

因为 this.props 和 this.state 的更新可能是异步的,不能依赖它们的值去计算下一个 state。

如何解决setState 异步问题

this.setState

如果需要修改this.state中的数据 必须调用this.setstate这个方法

这个方法里面有2个参数

参数1:类型 对象 key是this.state中的key值 val是修改后的数据

参数2:类型 函数 1、查看数据是否已经更新 2、可以获取到数据更新后的最新的DOM结构

书写方案1:this.setstate({},()=>{})

书写方案2:this.setstate(()=({}),()=>{})

//同步的操作
 
this.setState({count:1},()=>{
console.log(this.state.count)//输出count=1
});

//异步的操作  (常用操作)
 
this.setState({count:1})
console.log(this.state.count)

何为受控组件(controlled component)

在 HTML 中,类似 ,