Vue相关知识点

569 阅读13分钟

vue

经常会被问及的 Vue 面试题(上)

经常会被问及的 Vue 面试题(下)

100 道 JavaScript 面试题及答案汇总(下)

谈谈你对 MVVM 的理解

  1. MVC 指的是用户操作会请求服务端路由,路由调用对应的控制器来处理,控制器会获取数据,将结果返回前端,页面重新渲染。 MVVM 是 Model-View-ViewModel 模块-视图-双向绑定

  2. 传统的前端会将数据手动渲染到页面上, MVVM 模式不需要用户操作 dom 元素,将数据绑定在 ViewModel 层,会自动将数据渲染到页面上,视图变化时,会通知 ViewModel 层更新数据, ViewModel 是连接 View 和 Model 的桥梁。

1、说说你对 vue 双向绑定的理解

vue.js 利用数据劫持结合发布者-订阅者模式的方式,通过 Object.definedProperty 劫持各个属性的 getter,setter,dep.addsub 收集订阅依赖,watcher 监听数据的变化,数据变化时发消息给订阅者,触发相应的监听回调。

  1. new vue() 初始化,对 data 响应化处理,这个过程发生在 Observe 中。
  2. 同时对模板进行编译,找到动态绑定的数据,从 data 中获取并初始化视图,这个过程发生在 compile 中。
  3. 同时定义一个更新函数和 watcher,数据变化时 watcher 更新函数。
  4. data 的某个 key 在一个视图中可能会出现多次,所以每个 key 都需要一个管家 dep 来管理多个 watcher。
  5. data 数据发生变化,找到对应的 dep 通知 watcher 更新函数。

2、为什么 data 是一个函数,不是对象

  1. JavaScript 中的对象是引用类型的数据,当多个实例引用同一个对象时,只要一个实例对这个对象进行了操作,其他实例中的数据也会发生变化。
  2. Vue 更多的是想要复用组件,就需要每个组件都有自己的数据,组件间才不会相互干扰。
  3. 组件不能写成对象的形式,要写成对象的形式,数据以函数返回值的形式定义,每次复用组件的时候,就会返回一个新的 data。
  4. 每个组件都有了自己的数据,不影响其他组件的数据。

2、你做过vue的哪些性能优化

  1. 路由懒加载:使用 import 引用,以函数的方式进行引入,把各自的路由组件分别打包,只要给定解析的路由时,才会下载路由组件。
import VueRouter from 'vue-router'
 
Vue.use(VueRouter)
const router = new VueRouter({
 routes: [
    {path:'/login',component: ()=> import(@/views/login/index.vue) }
    {path:'/home', component: ()=> import(@/views/home/index.vue) }
  ]
 
export default router

  1. 第三方插件的按需引入:Element-ui的按需引入
import { Buttom } from 'element-ui'
Vue.use(Buttom)
  1. v-if v-show computed watch 区别使用应用场景
  2. v-for 必须加 key,key使用id,而且避免同时使用 v-if
  3. 使用 防抖和节流
  4. 使用 keep-alive 缓存组件
  5. 对象的层级不写很深

3、v-if 和 v-show 有什么区别

  1. 控制手段:v-show 使用 css 的 display:none,;控制。v-if 是将 dom 元素整个添加删除。
  2. 编译过程:v-show 只是 css 的简单切换,v-if 是局部编译/卸载事件和组件的过程
  3. v-show 为 false ,true 的时候不会涉及到 vue 生命周期
  4. v-if 为 true 的时候会涉及到 vue 的前面四个周期 beforeCreate、create、beforeMount、mounted,为 false 的时候涉及到 vue 后面两个生命周期 beforeDestory、destoryed

4、computed 和 watch 的区别

  1. computed: 支持缓存,不支持异步操作,因为无法监听数据的变化,依赖数据改变,重新进行计算,如果一个属性依赖其他属性,多对一,一般用 computed。
  2. watch: 不支持缓存,支持异步监听,数据变,直接触发相应操作,监听的数据必须是 data 中声明或者是父级传过来的 props 数据
  3. immediate:会立即执行一次
  4. deep:深度监听。
  5. 操作比较多的时候用 watch
  6. 性能消耗:v-show 消耗比较小,v-if 消耗比较大

5、Computed 和 Methods 的区别?

  • Computed 是计算属性,可以进行缓存数据,依赖的数据发生变化的才会执行。
  • Methods:只要调用都会执行。

7、防抖和节流

就像网站荣耀的回城和技能的冷却时间。

  • 防抖:回城,电梯,点击跳转页的时候,点1还没响应,手速很快点到了2,最后执行肯定是2页的内容啊
  • 节流:技能冷却,下拉加载。
<button id="debounce">点我防抖!</button>;
// 防抖
var myDebounce = document.getElementById("debounce");
myDebounce.addEventListener("click", debounce(sayDebounce));
function debounce(fn) {
  let timer = null;
  return function () {
    if (timer) clearTimeout(timer);
    timer = setTimeout(() => {
      fn.call(this, arguments);
    }, 1000);
  };
}
function sayDebounce() {
  console.log("防抖成功!");
}
<button id="throttle">点我节流!</button>;
// 节流
var myThrottle = document.getElementById("throttle");
myThrottle.addEventListener("click", throttle(sayThrottle));
function throttle(fn) {
  let timer = true;
  return function () {
    if (!timer) {
      return;
    }
    timer = false;
    setTimeout(() => {
      fn.call(this, arguments);
      timer = true;
    }, 500);
  };
}
function sayThrottle() {
  console.log("节流成功!");
}

8、对 keep-alive 的理解?它是怎么实现的?具体缓存了什么?

  • 组件切换的时候,为了保存组件的状态,防止多次渲染。就使用 ·keep-alive 包裹需要保存的组件。
  • keep-alive 会触发组件的 activated deactivated 两个生命周期
  • keep-alive 是通过 catch 数组 实现缓存组件的 vnode 实例
  • keep-alive 通常搭配 动态组件router-view keep-alive有三个属性:
  1. include:接收参数是字符串或者正则表达式,名称匹配的被缓存。
  2. exclude:接收参数是字符串或者正则表达式,名称匹配的不被缓存。
  3. max:接收数字,最多可以包裹组件。 它的过程是:
  • 判断 name ,是否在 include,exclude中,直接返回 vnode,该组件需不需要被缓存。
  • 当缓存的数量超过最大 max 时,清除 keys 数组里第一个组件

12、在 Vue 中给 data 中的对象增加属性时会发生什么?如何解决?

  1. 对象改变了但是页面并没有被刷新,因为没有被 vue 做响应化处理
  2. 需要通过 set方法调用object.definedProperty()进行处理。set 方法调用 object.definedProperty()进行处理。 set()方法相当于手动的去把obj.b处理成一个响应式的属性,此时视图也会跟着改变了。

13、单页面应用和多页面应用的区别?

  1. 单页面应用:一个主界面和多个组件,页面切换快,资源文件只加载一次,组件切换是局部更新,路由模式有 hash,history模式,数据传递有 vuex,组件通信,前期开发成本高,后期维护成本低。
  2. 多页面应用:单独完整页面,页面切换慢,每个页面都需要加载资源,内容更新的话会整体刷新,跳转方式是普通连接跳转,数据传递是 cookie、localStorage,前期开发简单,后期修改功能会修改多个界面。

filters 有了解吗?

filters 是一个过滤器,不会改变数据,是过滤数据,变成自己子想要展示的数据。

应用场景有: 展示时间,展示价格等...

常见的修饰符及其作用

  1. .stop 停止事件的,相当于 event.stopPropagation() 防止事件冒泡
  2. .prevent 相当于 evnt.preventDefault() 阻止默认行为
  3. .self 只触发自己的事件,不包括子元素
  4. .once 只触发一次事件

说说 vue 内置指令

  1. v-bind:绑定属性,动态更新 HTML 上的属性,可以动态传值,传方法,动态绑定 class 名等等
  2. v-on:用于监听 dom 事件 v-on:click v-on:change
  3. v-hmtl:渲染 HTML 代码,防止被 XSS 攻击。
  4. v-text:更新元素的
  5. v-model:处理 value 和 input 的语法糖
  6. v-if/v-show:判断元素/组件的消失出现的。
  7. v-for:循环指令,不要用 index 当做 key,可以用 +new Date() 当做 key 不能和 v-if 一起使用,因为 v-for 优先级高,如果非要用就写成计算属性的方式。

Vue 中封装的数组有哪些?如何实现页面的更新?

  1. 封装的数组有:
    • push 在数组的没添加一个元素并返回数组的长度 [1, 2, 3, 1] 4
    • pop 用于删除数组最后的一个元素,并返回最后一个元素 [1,2] 3
    • shift 删除数组的第一个长度并返回第一个元素的值 [2,3] 1
    • unshift 向数组头部添加一个长度并返回数组的长度 [1,1,2,3] 4
    • splice 删除,添加,替换
    • sort 排列数组
    • reverse 倒叙数组
  2. 使用函数劫持的方式,重写了数组的方法。
  3. vue 将 data 中的数组进行了原型链的重写,指向了自己定义的数组原型方法中,当调用数组的 api 时,通知依赖更新。
  4. 如果数组中包含引用类型数据,会再次监听引用类型数据。

$route$router 的区别

  1. $route 是路由信息对象,包含了 path、params、hash、query、name、fullPath、matched.
  2. $router 是路由实例对象,包含了路由的跳转方法,钩子函数等

Vue 组件间的通信方式

  1. props/$emit(父组件向子组件传参,子组件向父组件传参,$emit:父组件通过 v-on 监听并接收参数)
    • 响应式的
  2. eventBus(非父子间,$emit/$on)
  3. project/inject(父子间通信/或者是祖孙通信)
  4. ref/$refs(ref在子组件上定义,在父组件中使用this.$refs 来用变量和方法)
    • 响应式的
  5. $parent/$chidren($chidren是个无需数组,$parent 是个对象)
    • 响应式的
    • 父组件获取子组件是个无序的数组
  6. $attrs/$listeners(组件间的跨代通信)
    • 响应式的
  • 以上的这些方法层次感都太多了,建议使用 vuex

组件和插件的区别

  • 组件(Component):组件是用于构成内容、业务模块功能的,目标是 App.vue
  • 插件(Plugin):是用于增强技术栈功能模块的,目标是 vue 本身。

插件和依赖的区别

依赖:运行时开发时都需要用到的包,比如项目中需要一个包,就要添加一个依赖,这个依赖在项目运行时也需要,因此在项目打包时需要把这些依赖也打包进项目里;

插件:在项目开的发时需要,但是在项目运行时不需要,因此在项目开发完成后不需要把插件打包进项目中,比如有个可以自动生成 getter 和 setter 的插件,嗯对这就是插件了,因为这玩意在编译时生成 getter 和 setter,编译结束后就没用了,所以项目打包时并不需要把插件放进去

npm 安装时 --save --dev 和 --save 区别

--save:将保存配置信息到 pacjage.json 的 dependencies 节点中。

--save-dev:将保存配置信息到 pacjage.json 的 devDependencies 节点中。

dependencies:运行时的依赖,发布后,即生产环境下还需要用的模块

devDependencies:开发时的依赖。里面的模块是开发时用的,发布时用不到它。

跨域

跨域的本质是浏览器基于同源策略的一种安全方式,前端通过一些方式避开浏览器安全的限制。同源指的是协议``端口``域名一一致,不一致就是跨域。 有三种方式:JSONP,CORS,Proxy

  1. JSONP: <script> 标签的 src 属性不会被同源策略所约束,可以获取任意服务器上的脚本并执行。jsonp 通过插入script标签的方式来实现跨域,参数只能通过url传入,仅能支持get请求。在 get 请求的时候不太安全,携带的数据比较小。
  2. CORS:CORS 是一个系统,是由一系列传输的 HTTP 头组成, HTTP 头决定是否阻止前端 Js 代码获取跨域请求的响应。 只需要增加一些 HTTP 头,让服务器声明允许访问来源,就实现了跨域。
  3. Proxy:Proxy 是网络代理,网关,路由器等设备具有网络代理功能,客户端通过网络代理与服务器进行非直接的连接。网路代理可以保护隐私安全,防止攻击。 我是通过 Vue-clii 脚手架搭建项目的,用 node 起一个本地服务器作为请求的代理对象。 通过本地服务器发请求给目标服务器,得到结果再转发给前端

我的毕业设计就是一个 IT 信息平台。 当时自己不想写很多的数据,就从 Vue 中文社区里面找了一个 API 接口。通过跨域拿到了里面的同步数据。 下载 axios 进行封装,通过发送请求,配置请求的的根路径。 新增一个 vue.config.jsaxios.js 文件

// axios
import axios from "axios";
axios.defaults.baceURL = "/api";
const $axios = axios;
export default $axios;
// vue.config.js
module.exports = {
  devServer: {
    proxy: {
      "/api": {
        target: "目标路径",
        ws: true, //是否缓存
        changeOrigin: true, //是否跨域
        pathRewrite: {
          "^/api": "", //路径重写
        },
      },
    },
  },
};

vue3

Vue3 和 Vue2

  1. 响应式原理的改变: vue3 使用的是 proxy,vue2 使用的是 object.definedProperty()
    • Object,definedProperty() 只是劫持对象的属性,(监听数据还需要通过 set()方法)
    • proxy 可以直接代理整个对象,包括增加属性和删除属性,可以监听数组的变化
  2. 组件选项声明方式:vue3 使用的是 Composition API,setup 是 vue3 新增的的一个选项,他是组件内使用 Composition API 的入口
      1. setup 在 beforeCreate 之前执行,不能访问 this,显示 undefined,有 props content 两个参数,props 是响应式的,不能使用解构赋值,可以使用 toRef,content 包含三个属性 attrs,slot,emit
      1. vue2 定义数据在 data 中,vue3 定义数据在 ref 和 reactive 中。
      1. watch 可以监听多个属性
      1. <template> 可以接收多个节点
  3. 模板语法的变化:slot 具名插槽,自定义指令 v-model 升级
      1. vue2 的具名插槽和作用域插槽用 slot 和 slot-scope 实现,vue3 使用 v-slot 实现
      1. v-model 和 .sync 结合

对比v-model掌握.sync的使用

13 Vue3.0 和 2.0 的响应式原理区别

Vue3.x 改用 Proxy 替代 Object.defineProperty。因为 Proxy 可以直接监听对象和数组的变化,并且有多达 13 种拦截方法。

Object.definedProperty() 和 proxy 的区别

  • proxy 可以监听数据的变化,是 ES6 新增的属性,可以直接代理整个对象,包括新增属性和删除属性,可以监听数组的变化。
  • vue 对象添加删除属性时,监听不到变化,因为初始化的时候没有进行响应化处理,只能通过 $set 方法调用 Object.definedProperty()处理

做过SSR吗?说一说

在vue项目中没有做过,但是有了解过。

SSR是服务器端渲染,主要是将Vue在客户端把标签渲染成HTML页面的工作放在服务器端完成,然后把HTML返回客户端。

优点

SSR有着更好的SEO,并且首屏加载速度更快。

缺点

  1. 但是开发条件会受到限制,服务器端只支持两个生命周期 beforeCrate,Created。
  2. 还要对外部扩展库做特殊的处理,服务器端渲染应用程序还要处于node.js运行环境中。
  3. 这就意味着服务器端有着很大的负载需求。

Vue 事件绑定原理了解吗

原生事件是通过 addEventListener 绑定给真实元素的,组件事件绑定是通过 Vue 自定义的 $on 方法实现的。

如果想在组件上绑定原生事件,需要加 .native 修饰符,这样就相当于父组件把子组件当做普通的HTML标签,然后加上原生事件。

28 对 React 和 Vue 的理解,他们的异同

相同点:

  • 都有 路由vuex 和其他组件库。
  • 都有 props 的概念,允许组件间通信。
  • 都鼓励 组件化 ,提高复用性

不同之处:

  • Vue 支持数据双向绑定,React 支持单项数据流。
  • Vue 的模板编写接近 HTML, React 的模板编写是使用 JSX
  • Vue 使用 getter ,setter 劫持数据的方式,React 通过引入的方式。
  • Vue 强调数据可变, React 强调数据不可变。
  • Vue 通过 mixins 进行扩展,React 通过 HOC 高阶组件(高阶函数)扩展。
  • Vue 有自己的 Vue Cli, React 有 Create React App
  • 跨平台 Vue 有 weex,React 有 React Native

Vue 的优点

  1. 简单易学
  2. 组件化
  3. 双向数据绑定
  4. 轻量级框架
  5. 视图、数据、结构分离
  6. 虚拟 dom
  7. 运行速度快

Vue 的生命周期

八个

  • beforeCreate(初始化界面前)
  • created(初始化界面后)
  • beforeMount(渲染 dom 前)
  • mounted(渲染 dom 后)
  • beforeUpdate(更新数据前)
  • updated(更新数据后)
  • beforeDestroy(卸载组件前)
  • destroyed(卸载组件后) 还有两个是 keep-alive 的生命周期 activated deactivated

每个周期适合的场景

  1. beforCreate: 写一个 loading 事件,在加载时触发
  2. created: 写一个结束 loading 事件,写异步请求
  3. mounted:挂载元素,获取到 DOM 节点
  4. updated:写一个处理数据的函数
  5. beforeDestroyed:写一个确认停止事件的确认框

crated 和 mounted 的区别

  • created:在模板渲染成 HTML 前调用,此时 data 已经准备完毕,el 仍是 undefined,因为模板还没有渲染成 HTML ,所以不能操作 dom 节点,通常用来初始化数据。 如果非要想与 Dom 进行交互,可以通过 vm.$nextTick 来访问 Dom
  • mounted:模板渲染成 HTML 后调用,此时 data,el 已经准备完毕,可以操作 dom 节点,可以通过 id 查找页面的元素,也可以加载一些组件。

异步请求放在哪里最合适

  • 放在 created beforeMount mounted 这三个周期内,因为这三个周期 data 已经初始化完毕,可以操作数据。
  • 如果不操作 dom 节点的话最好放在 created 里面,因为他能最早拿到数据,而且SSR不支持 beforeMount mounted 这两个生命周期。

vue 父子组件生命周期执行过程

  • 加载渲染过程 父 beforeCreate -> 父 created -> 父 beforeMount-> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted
  • 子组件更新过程 父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated
  • 父组件更过程 父 beforeUpdate -> 父 updated
  • 子组件销毁过程 父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed

做过 SSR 吗?说说 SSR

  1. SSR 是服务器端渲染,将 Vue 在客户端把标签渲染 HTML 页面的工作放到服务器端去渲染,然后再把 HTML 返回客户端。
  2. SSR 有着更好的 SEO,并且首屏加载速度更快。但是开发条件会受到限制,服务器端只支持两个生命周期 beforeCreate,Created,还要对外部扩展库进行特殊的处理,服务器端渲染应用程序也需要处于 node.js 的运行环境。
  3. 服务器有很大的负载需求

组件的通信方式有哪些?

  1. props/$emit:props接收父组件传过来的参数,子组件通过 $emit提交父组件的方法,并携带参数改变数据。$emit 的第一个参数是父组件的方法,第二个参数是传参。
  • 父组件 parent
<template>
  <div>
    <span>父组件数据:{{ num }}</span>
    <hr />
    <chidren :num="num" @ChangeNum="ChangeNum" />
  </div>
</template>

<script>
import chidren from "./chidren.vue";
export default {
  name: "parent",
  components: {
    chidren,
  },
  data() {
    return {
      num: 666,
    };
  },
  methods: {
    ChangeNum(newNum) {
      this.num = newNum;
    },
  },
};
</script>

  • 子组件 chidren
<template>
  <div>
    <span>子组件数据:{{ num }}</span>
    <button @click="Demo">点击改变数据</button>
  </div>
</template>

<script>
export default {
  name: "chidren",
  props: {
    num: {
      type: Number,
      default: 0,
    },
  },
  methods: {
    Demo() {
      this.$emit("ChangeNum", 999);
    },
  },
};
</script>

image.png

  1. $parent/ $children

  2. project/inject

    • 依赖注入所提供的属性是非响应式的。
  3. $attrs/$listeners

  4. eventBus事件总线($emit / $on)

  • 通过 $parent/$refs 来获取到兄弟组件,也可以进行通信。

history 和 hash 模式的区别

vue-router 有几种模式?有什么区别?history 模式下 404 后台应该配置什么? 路由有两种模式:hash模式,history模式

  1. hash模式是路由的默认模式,它的URL里面带 #
  2. hash 值在 URL 里面,不在 HTTP 请求中,不会对后端产生影响。修改hash值时不会加载页面。hash 模式在浏览器中的兼容性比较好。
  3. hash值变化对应的URL会被浏览器记录下来,浏览器可以实现页面的前进后退。
  4. hash值的主要原理是 onhashchange() 事件
window.onhashchange = function(event){
    console.log(event.oldURL, event.newURL);
    let hash = location.hash.slice(1);
}
  1. history 模式比较美观,URL里面并没有 #,只有/
  2. 采用的是路由分发模式。如果后台没有正确配置 history 模式,会返回404。
  3. history api 有两种状态:修改历史状态和切换历史状态。
  4. 修改历史状态有两个方法,pushState()、replaceState、记录浏览器历史记录,可以改变URL,不刷新页面。
  5. 切换历史状态有三个方法 forward() back() go ,对应浏览器的前进后退跳转操作。

如何获取页面的hash变化

  1. watch 监听 $route 的和变化。
  2. window.location.hash 读取。

39. vue初始化页面闪动问题

使用vue开发时,在vue初始化之前,由于div是不归vue管的,所以我们写的代码在还没有解析的情况下会容易出现花屏现象,看到类似于{{message}}的字样,虽然一般情况下这个时间很短暂,但是还是有必要让解决这个问题的。

首先:在css里加上以下代码:

[v-cloak] {
    display: none;
}

如果没有彻底解决问题,则在根元素加上style="display: none;" :style="{display: 'block'}"

Vuex

不用 Vuex 会带来什么问题

  1. 可维护性会下降,你要想修改数据,你得维护三个地方
  2. 可读性会下降,因为一个组件里的数据,你根本就看不出来是从哪来的
  3. 增加耦合,大量的上传派发,会让耦合性大大的增加,本来 Vue 用 Component 就是为了减少耦合,现在这么用,和组件化的初衷相背。
  4. 兄弟组件有大量通信的,建议一定要用,不管大项目和小项目,因为这样会省很多事

Vuex 有哪几种属性?

State mutation action getter module

mutation 和 action 有什么区别?

  1. action 提交的是 mutation,不可以直接修改数据状态,mutation 可以直接修改数据状态。
  2. action 可以是异步操作,mutation 不可以使异步操作。
  3. 提交的方式不同,action 是 this.$store.dispatch() mutation 是 this.$store.commit()
  4. 接收的参数不同,action 的第一个参数是 context,包含了 state,commit等属性,mutation 的第一个参数是 state ,他们的第二个参数都是接收参数,如果接受多个参数的话可以写成数组或对象的形式。

vuex 和单独的全局对象有什么区别?

  1. vuex 数据是响应式的、不可直接修改。需要 Mutation 提交 commit 修改
  2. 全局对象不是响应式的,可以直接修改

如何在组件中批量使用Vuex的getter属性

使用mapGetters辅助函数, 利用对象展开运算符将getter混入computed 对象中

import {mapGetters} from 'vuex'
export default{
    computed:{
        ...mapGetters(['total','discountTotal'])
    }
}

如何在组件中重复使用Vuex的mutation

使用mapMutations辅助函数,在组件中这么使用

import { mapMutations } from 'vuex'
methods:{
    ...mapMutations({
        setNumber:'SET_NUMBER',
    })
}

然后调用this.setNumber(10)相当调用this.$store.commit('SET_NUMBER',10)

vuex 的原理

  1. vuex 的存储于状态时响应式的,store 发生变化,组件的数据也会发生变化。
  2. 通过 action commit 提交 mutation ,再通过 mutation 提交 commit 提交 store 改变数据。

Vue 里用 vuex做组件之间的数据双向绑定

监听 Vuex 里的数据变化 mapState

    import { mapState } from 'vuex'
    computed: {
        ...mapState({
          count: state => state.number.count,
        })
    },

怎么解决 vuex 里的数据在页面刷新后丢失的问题?

  1. 需要做 vuex 数据持久化,一般使用本地存储的方案解决
  2. 有可以使用第三方插件 vuex-persist 插件,它是为 vuex 数据持久化存储而生的插件,不需要手动存取 Storage, 直接将数据存储到 cookie 或者 locolstorage 中。

路由

1、 parmas 和 query 的区别?

  1. params 传参必须使用命名路由的方式传参,不会显示在地址栏上,会保存在内存中,刷新会丢失。可以配合本地存储进行使用。
  2. query 参数会显示在地址栏上,刷新不会丢失。

router 跳转和 locaotion.herf 跳转有什么区别?

一个是会刷新,一个不会刷新

路由守卫

  1. 全局路由钩子:
    • 全局前置守卫 beforeEach 判断是否登录,没登录就跳转登录页,三个参数 (to,from,next)
    • 全局解析钩子 beforeResolve
    • 全局后置钩子 afterEach 跳转之后滚动条回到顶部 (to,from)
  2. 路由独享守卫:beforeEnter 即将进入登录首页
  3. 组件内守卫:beforeRouteEnter beforeRouteUpdate beforeRouteLeave

key 在 vue 项目中的作用。

  1. 在 diff 算法执行的过程中快速的找到对应的节点,提高效率,高效的更新渲染虚拟 dom。
  2. 在数据变化的时候强制更新组件,避免 '就地复用' 带来的副作用。

意思是 v-for 更新已渲染的元素列表时,就默认使用 '就地复用' 策略,数据项的顺序发生变化时,vue 不会移动 dom 元素去匹配数据项的顺序,而是简单的复用此处的每个元素,从而通过为每个列表项提供一个 key 值,方便 vue 跟踪元素的身份,高效的实现复用。

  • 不能用 index 作为 key,可以使用时间戳 new Date() 作为 key,或者 id 作为key,因为用 index 作为 key 和没用基本没区别,数组不管再怎么颠倒,index 的排列顺序还是 0,1,2, 导致 vue 复用作物的旧子节点,做很多额外的工作。

为什么 v-for 和 v-if 不能在一起使用?

  1. 因为 v-for 的优先级比较高,每次渲染都会先循环再进行判断,带来性能的浪费。
  2. 如果要是用 v-if 可以在外层套 <template>
  3. v-if也可以写在 computed 内。
computed: {
    items: function() {
      return this.list.filter(function (item) {
        return item.isShow
      })
    }
}

虚拟 dom 是什么?有什么优缺点?

  1. 浏览器操作 dom 的代价很昂贵,频繁的操作 dom 会产生性能的问题。
  2. 虚拟dom的本质是利用原生JS对象描述一个 dom 节点,是对真实 dom 节点的一层抽象。
  3.  template (真实DOM)先转成 ast , ast 树通过 codegen 生成 render 函数, render 函数里的 _c 方法将它转为虚拟dom
  4. 数据变化前,虚拟 dom 缓存一份,变化时,通过 diff 算法比较现在的虚拟 dom 和缓存的虚拟 dom,然后渲染改变的部分,没有改变的部分用之前的数据渲染。 优点:
  • 无需手动操作 DOM: 我们不再需要手动去操作 DOM,只需要写好 View-Model 的代码逻辑,框架会根据虚拟 DOM 和 数据双向绑定,帮我们以可预期的方式更新视图,极大提高我们的开发效率;
  • 跨平台:可以在服务器渲染。

缺点:

  • 对于一些业务性能要求比较高的逻辑无法极致的优化。
  • 首屏加载的时候会比较慢。

4、另外一种说法 为什么虚拟dom会提高性能?

参考答案 ### 为什么虚拟dom会提高性能?

  • 虚拟dom相当于在js和真实dom中间加了一个缓存,利用dom diff算法避免了没有必要的dom操作,从而提高性能。 具体实现步骤如下:
  1. 用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中
  2. 当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异
  3. 把2所记录的差异应用到步骤1所构建的真正的DOM树上,视图就更新了。

diff 有了解吗?说一说

diff 是通过同层树节点进行比较的高效算法。它有两个特点:

  1. 深度优先、同层比较、不会跨级。
  2. diff 比较过程中,循环从两边向中间比较。 应用场景有: 虚拟 dom 节点渲染成真实 dom 节点的新旧 vnode 节点比较。
  • 当数据变化时,set 方法调用 dep.notify() 通知所有的订阅者 Watch,订阅者调用 patch 函数给真实的 dom 打补丁,然后更新视图。
  • patch 函数有两个参数 oldvnode newvnode,然后点用 isSamevnode 方法进行判断。
  • diff 算法的核心是判断如何对这些新老节点的子节点进行操作。

Vue 的 $nextTick 原理和作用

我们可以理解成 vue 更新 dom 是异步执行的,数据变化时,vue 开启一个异步更新队列,视图等队列上所有的数据变化完成之后,再统一进行更新。主要思路是采用微任务优先的方式,调用异步任务去执行,nextTick 包装好的方法。

this.$nextTick(()=>{
  // 操作具体的数据
})

vue 中应用了哪些设计模式

  1. 工厂模式(传入参数就可以创建实例)
  2. 单例模式(整个程序只有一个实例)
  3. 发布订阅模式(Vue事件机制)
  4. 观察者模式(响应式数据原理)
  5. 装饰模式(@装饰器的用法)

mixin 和 mixins 的区别

  • mixin 是全局混入,会影响每个组件的实例,通常会初始化一个插件。
  1. mixin 优先于 created 执行
  2. 缺点是定义的变量会重名,冲突,不清楚暴露的变量是干嘛的
// 在 main.js 里面插入
Vue.mixin({
  created() {
    // 总共有 APP.vue  MixinDemo.vue  还有根组件呢,所以执行了三次
    // console.log(this);
    console.log("全局 mixin 加入成功");
  },
});
  • mixins 是扩展组件的方式。如果组件有很多相同的业务逻辑就把它剥离出来,通过 mixins 混入代码
  1. mixins 优先于 created 执行
// 组件代码
<script>
import helloMixin from "../mixins/hello";
export default {
  name: "MixinDemo",
  data() {
    return {
      number: 10000,
    };
  },
  mixins: [helloMixin],
  beforeCreate() {
    console.log("beforeCreate");
  },
  created() {
    console.log("created");
  },
};
</script>
//  js 代码
const helloMixin = {
  // 该对象内的格式和组件导出对象格式一样
  // 当组件和混入对象含有同名选项时,这些选项将以恰当的方式进行 "合并"
  // 可以拿得到组件的 this

  // 恰当的方式
  // 1. data 以组件为主 (如果是不同名的话就都展示)
  // 2. 生命周期函数  自动合并(mixin混入的先执行)
  // 3. methods components 和 directives 以组件本身为主
  data() {
    return {
      num: 100,
    };
  },
  created() {
    console.log("mixins 内的 hello");
    // console.log("判断用户是否登录了");
    // console.log(this);
    // const loading = true;
    // if (loading) {
    //   console.log("用户已经登陆");
    // } else {
    //   console.log("用户还没有登录");
    // }
  },
};
export default helloMixin;

Vue 自定义指令

directive 方法的第二个参数会接收一个对象,对象内有一些钩子函数,用来设置指令的功能,钩子函数会在特定的情况下自动触发

常用的两个钩子函数

  1. bind 元素绑定时调用
  2. inserted 元素插入 dom 时调用
  3. update 元素更新时调用
  4. componentUpdate 元素更新后调用
  5. unbind 元素解绑时调用

自己写了两个自定义指令

<input v-focus="true" type="text" />
// main.js 全局绑定
Vue.diretive("focus",{
    inserted: function(el,binding){
        if(binding.value) {
            el.focus()
        }
    }
})
<input v-focus="true" type="text" />

main.js 全局绑定

Vue.diretive("focus",{
    inserted: function(el,binding){
        if(binding.value) {
            el.focus()
        }
    }
})


注册组件内的局部指令,只有该组件内使用
  directives:{
    focus:{
        inserted(el,binding){
          console.log(el,binding);
          // 节点: "focus", rawName: "v-focus", modifiers: {…}, def: {…}}
          el.focus()
        }
    }
  }
Vue.directive("my-click", {
  inserted(el, binding) {
    console.log(binding);
    if (binding.value) el.onclick = binding.value
  },
});

vuex 的实现原理

  1. Vue.use(vuex)会调用 vuex 的 install 方法
  2. 在 beforeCreate 钩子前混入 vuexInit 方法,vuexInit 方法实现了 store 注入 vue 组件实例,并注册了 vuex store 的引用属性$store。
  3. Vuex 的 state 状态是响应式,是借助 vue 的 data 是响应式,将 state 存入 vue 实例组件的 data 中;
  4. Vuex 的 getters 则是借助 vue 的计算属性 computed 实现数据实时监听。

vue.component() vue.use()

  • vue.component() 是注册全局组件的,有两个参数,第一个参数是自定义名称,第二个参数是要注册的组件。
  • vue.use() 注册插件的,接收一个参数,这个参数必须由 install 方法,vue.use() 方法会自动调用 插件的 install 方法。

Vue 事件的绑定原理

  1. 原生事件是通过 addEventListener 绑定的
  2. Vue 事件是通过 $on 方法绑定的

v-model 的实现原理

  1. v-model 是 value 和 input 的语法糖,会根据标签的不同,生成不同的事件和标签。
  2. text 和 textarea 元素使用是用 value property 和 input 事件
  3. checkbox 元素使用的是 checked property 和change 事件。
  4. select 元素使用的是 prop 和 change 事件。

为什么vue采用异步渲染呢?

  1. 因为 vue 是组件级更新,不采用异步更新,每次数据改变都会对当前组件重新渲染。
  2. 为了性能消耗,vue 会在本轮数据更新后,异步更新视图。

描述组件渲染和更新过程

  1. 渲染组件时,会通过 vue.extend() 方法构建子组件的构造函数,并进行实例化,最终手动调用 $mount() 进行挂载。
  2. 更新组件时会进行 patchVnode 流程,核心就是 diff 算法。

Object.definedProperty() 的特点

  1. Object.definedProperty() 只能够劫持对象数组的属性,不能检测到对象数组添加删除属性,因为在初始化的时候没有进行相应化处理,可以通过 $set() 方法调用 Object.definedProperty() 手动进行相应化处理。
  2. vue3 使用 Proxy 监听数据的变化,Proxy 是ES6新增的属性,能够代理整个对象数组,包括添加删除属性。

extend 有什么作用?

extend 是扩展组件生成一个构造器,通常和 $mount 一起使用

vuex 和 LocalStorage 的区别

最重要的是 vuex 存储在内存中,LocalStorage 存储在本地

  1. LocalStorage 保存对象需要使用 JSON.stringfy 和 parse
  2. 读取内存的速度比硬盘的速度快

  1. vuex 的数据是响应式的,LocalStorage 不是
  2. vuex 是在 组件间通信的,LocalStorage 是在网页建通信的
  3. vuex 是刷新数据会丢失,LocalStorage 刷新数据不会丢失

vuex 和全局对象的区别

  1. vuex 数据是响应式, 全局对象不是响应式的
  2. vuex 不可以直接变更状态,全局对象可以直接变更状态

1. 懒加载的概念

懒加载也叫做延迟加载、按需加载,指的是在长网页中延迟加载图片数据,是一种较好的网页性能优化的方式。在比较长的网页或应用中,如果图片很多,所有的图片都被加载出来,而用户只能看到可视窗口的那一部分图片数据,这样就浪费了性能。

如果使用图片的懒加载就可以解决以上问题。在滚动屏幕之前,可视化区域之外的图片不会进行加载,在滚动屏幕时才加载。这样使得网页的加载速度更快,减少了服务器的负载。懒加载适用于图片较多,页面列表较长(长列表)的场景中。

2. 懒加载的特点

  • 减少无用资源的加载:使用懒加载明显减少了服务器的压力和流量,同时也减小了浏览器的负担。
  • 提升用户体验: 如果同时加载较多图片,可能需要等待的时间较长,这样影响了用户体验,而使用懒加载就能大大的提高用户体验。
  • 防止加载过多图片而影响其他资源文件的加载 :会影响网站应用的正常使用。

3. 懒加载的实现原理

图片的加载是由src引起的,当对src赋值时,浏览器就会请求图片资源。根据这个原理,我们使用HTML5 的data-xxx属性来储存图片的路径,在需要加载图片的时候,将data-xxx中图片的路径赋值给src,这样就实现了图片的按需加载,即懒加载。

注意:data-xxx 中的xxx可以自定义,这里我们使用data-src来定义。

懒加载的实现重点在于确定用户需要加载哪张图片,在浏览器中,可视区域内的资源就是用户需要的资源。所以当图片出现在可视区域时,获取图片的真实地址并赋值给图片即可。

使用原生JavaScript实现懒加载: 知识点:

(1)window.innerHeight 是浏览器可视区的高度

(2)document.body.scrollTop || document.documentElement.scrollTop 是浏览器滚动的过的距离

(3)imgs.offsetTop 是元素顶部距离文档顶部的高度(包括滚动条的距离)

(4)图片加载条件:img.offsetTop < window.innerHeight + document.body.scrollTop;

浏览器.png

<div class="container">
     <img src="loading.gif"  data-src="pic.png">
     <img src="loading.gif"  data-src="pic.png">
     <img src="loading.gif"  data-src="pic.png">
     <img src="loading.gif"  data-src="pic.png">
     <img src="loading.gif"  data-src="pic.png">
     <img src="loading.gif"  data-src="pic.png">
</div>
<script>
var imgs = document.querySelectorAll('img');
function lozyLoad(){
        var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
        var winHeight= window.innerHeight;
        for(var i=0;i < imgs.length;i++){
            if(imgs[i].offsetTop < scrollTop + winHeight ){
                imgs[i].src = imgs[i].getAttribute('data-src');
            }
        }
    }
  window.onscroll = lozyLoad();
</script>

4. 懒加载与预加载的区别

这两种方式都是提高网页性能的方式,两者主要区别是一个是提前加载,一个是迟缓甚至不加载。懒加载对服务器前端有一定的缓解压力作用,预加载则会增加服务器前端压力。

  • 懒加载也叫延迟加载,指的是在长网页中延迟加载图片的时机,当用户需要访问时,再去加载,这样可以提高网站的首屏加载速度,提升用户的体验,并且可以减少服务器的压力。它适用于图片很多,页面很长的电商网站的场景。懒加载的实现原理是,将页面上的图片的 src 属性设置为空字符串,将图片的真实路径保存在一个自定义属性中,当页面滚动的时候,进行判断,如果图片进入页面可视区域内,则从自定义属性中取出真实路径赋值给图片的 src 属性,以此来实现图片的延迟加载。
  • 预加载指的是将所需的资源提前请求加载到本地,这样后面在需要用到时就直接从缓存取资源。 通过预加载能够减少用户的等待时间,提高用户的体验。我了解的预加载的最常用的方式是使用 js 中的 image 对象,通过为 image 对象来设置 scr 属性,来实现图片的预加载。
如何学习前端技术?

看红宝书打基础,边看边复现案例,然后在掘金社区关注不同的博主对同一知识点的看法,然后看视频查漏补缺,还有就是在群聊里面互相讨论在学习和工作中遇到的一些坑和难点

JS 的基本数据类型、引用类型,二者存储的方式

有 number string undefined null boolean BigInt symbol object 队列:先进先出 原数数据 undefined null number string boolean 线性结构,存储在栈中,先进后出,后进先出 引用数据 object array function 存储在堆中,树形结构

  1. ES6 的模块化和 CommonJS 模块化的区别 CommonJs 模块是输出一个值的拷贝,ES6 模块是输出一个值的引用 CommonJs 模块是运行时加载,ES6 模块是编译时输出接口
  2. import 和 requeir 的最主要的区别,静态引用和动态引用 import 是 ES6 模块的,requere 是 AMD 模块的 import 是编译时调用,必须放在文件的开头,requere 是运行时调用,可以放在代码的任何地方,requere 的结果是对象,数组,字符串,函数,是把结果赋值给某个变量。
  3. promise 和 await async 之间的关系 async/await 是基于 promise 实现的,跟同步代码差不多 async/await 不需要写箭头函数处理 promise 的 resolve() 函数 async/await 让 try catch 可以同事处理同步和异步错误 async/await 的第一个方法返回值,用作第二个方法的参数,解决了嵌套问题。
  4. http 缓存类型有哪些? https://www.jianshu.com/p/227cee9c8d15 有强缓存和协商缓存 区别是: 强缓存不会向服务器发请求,状态码是 200 协会缓存通过 get 发请求,状态码是 304
  5. 依赖收集在哪一个方法中去完成的? 计算属性是在 beforeMounte 和 Mounted 之间完成 监听属性是在 beforeCreate 和 Created 之间完成
  6. 说一下 dep 和 watcher 之间的关系
  7. 双向绑定的功能是在哪一个生命周期中实现的? Mounted 之间实现的
  8. vue 模板编译的过程,每一个过程细说一下做了些什么 <template> 转换成 ast 树,ast 树通过 ganerate 变成 reder 函数,render 函数再通过 _C 方法变成虚拟 dom 节点
  9. 你怎么做到降低代码的耦合度 少用类的继承,多用接口 避免使用全局变量 模块功能划分
  10. 图片懒加载怎么做到的 自己先写一个自定义属性,把这个属性的值写成列表数据,在可视化范围内的时候,就把自定义属性的值做展示,这就实现懒加载