likes
comments
collection
share

21天筑基期--Vue系列

作者站长头像
站长
· 阅读数 18

Vue

Vue 是一个构建数据驱动的 Web 界面的渐进式框架。 Vue 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。核心是一个响应的数据绑定系统。 关于 Vue 的优点,主要有响应式编程、组件化开发、虚拟 DOM。

Vue3 setup使用参考

Vue3 script setup 语法糖,就问你甜不甜 - 掘金 (juejin.cn)

数据驱动(MVVM)

MVVM 是一种设计思想

  • Model:模型层,负责处理业务逻辑以及和服务器端进行交互
  • View:视图层:负责将数据模型转化为UI展示出来,可以简单的理解为HTML页面
  • ViewModel:视图模型层,用来连接Model和View,是Model和View之间的通信桥梁

Vue生命周期

关于 Vue 生命周期的变化,可以从下表直观地了解:

Vue 2 生命周期Vue 3 生命周期执行时间说明
beforeCreatesetup组件创建前执行
createdsetup组件创建后执行
beforeMountonBeforeMount组件挂载到节点上之前执行
mountedonMounted组件挂载完成后执行
beforeUpdateonBeforeUpdate组件更新之前执行
updatedonUpdated组件更新完成之后执行
beforeDestroyonBeforeUnmount组件卸载之前执行
destroyedonUnmounted组件卸载完成后执行
errorCapturedonErrorCaptured当捕获一个来自子孙组件的异常时激活钩子函数
## Vue 的路由实现

解释 hash 模式和 history 模式的实现原理

后面 hash 值的变化,不会导致浏览器向服务器发出请求,浏览器不发出请求,就不会刷新页面;通过监听 hashchange 事件可以知道 hash 发生了哪些变化,然后根据 hash 变化来实现更新页面部分内容的操作。

history 模式的实现,主要是 HTML5 标准发布的两个 API,pushState 和 replaceState,这两个 API 可以在改变 URL,但是不会发送请求。这样就可以监听 url 变化来实现更新页面部分内容的操作。

两种模式的区别:

  • 首先是在 URL 的展示上,hash 模式有“#”,history 模式没有
  • 刷新页面时,hash 模式可以正常加载到 hash 值对应的页面,而 history 没有处理的话,会返回 404,一般需要后端将所有页面都配置重定向到首页路由
  • 在兼容性上,hash 可以支持低版本浏览器和 IE

说一下 router与router 与 router与route 的区别

$route 对象表示当前的路由信息,包含了当前 URL 解析得到的信息。包含当前的路径,参数,query 对象等。

  • $route.params: 一个 key/value 对象,包含了 动态片段 和 全匹配片段,如果没有路由参数,就是一个空对象。
  • route.query:一个key/value对象,表示URL查询参数。例如对于路径/foo?user=1,则有route.query:一个 key/value 对象,表示 URL 查询参数。例如对于路径 /foo?user=1,则有 route.query:一个key/value对象,表示URL查询参数。例如对于路径/foo?user=1,则有route.query.user == 1,如果没有查询参数,则是个空对象。
  • $route.hash:当前路由的 hash 值 (不带 #) ,如果没有 hash 值,则为空字符串。
  • $route.fullPath:完成解析后的 URL,包含查询参数和 hash 的完整路径。
  • $route.matched:数组,包含当前匹配的路径中所包含的所有片段所对应的配置参数对象。
  • $route.name:当前路径名字
  • $route.meta:路由元信息

$route 对象出现在多个地方:

  • 组件内的 this.$route 和 route watcher 回调(监测变化处理)
  • router.match(location) 的返回值
  • scrollBehavior 方法的参数
  • 导航钩子的参数,例如 router.beforeEach 导航守卫的钩子函数中,to 和 from 都是这个路由信息对象。

$router 对象是全局路由的实例,是 router 构造方法的实例。

$router 对象常用的方法有:

  • push:向 history 栈添加一个新的记录
  • go:页面路由跳转前进或者后退
  • replace:替换当前的页面,不会向 history 栈添加一个新的记录

vueRouter 有哪几种导航守卫?

  • 全局前置/钩子:beforeEach、beforeR-esolve、afterEach
  • 路由独享的守卫:beforeEnter
  • 组件内的守卫:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave

解释一下 vueRouter 的完整的导航解析流程是什么

一次完整的导航解析流程如下:

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

路由守卫

由配置中对需要登录权限的页面添加一个meta:{isRequireLogin:true},表示跳转到该页面后需要进行登录权限验证 之后在路由守卫中(to, from, next)三个参数中的to获取到meta.isRequireLogin,如果有且为真,则需要对该页面进行登录校验,

// 路由守卫,登录判断,以及主子页面之间的切换
router.beforeEach((to, from, next) => {
  const { requiredLogin } = to.meta;
});

然后再从本地存储localstore中获取login,看看用户有没有登录,如果登录了,则能获取到login且为真,之后放行,执行next,如果不能获取,则表示用户没有登录,那么就跳转到登录页面

router.beforeEach((to, from, next) => {
  const { requiredLogin } = to.meta;
  const isLogin = localStorage.getItem("isLogin");
  // 判断是否已经登录并是否页面需要登录权限,如果是,跳转到登录页面,若否,则放行
  if (!isLogin && requiredLogin) {
    next("login");
  } else {
    next();
  }
});

v-show与v-if

v-show  与 v-if 的作用效果是相同的(不含v-else),都能控制元素在页面是否显示

  • 当表达式为true的时候,都会占据页面的位置
  • 当表达式都为false时,都不会占据页面位置 v-if 与 v-show 都能控制dom元素在页面的显示

v-if 相比 v-show 开销更大的(直接操作dom节点增加与删除)

如果需要非常频繁地切换,则使用 v-show 较好

如果在运行时条件很少改变,则使用 v-if 较好

v-if和v-for不建议一起用

v-if 指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 true值的时候被渲染

v-for 指令基于一个数组来渲染一个列表。v-for 指令需要使用 item in items 形式的特殊语法,其中 items 是源数据数组或者对象,而 item 则是被迭代的数组元素的别名

在 v-for 的时候,建议设置key值,并且保证每个key值是独一无二的,这便于diff算法进行优化

  1. 永远不要把 v-if 和 v-for 同时用在同一个元素上,带来性能方面的浪费(每次渲染都会先循环再进行条件判断)
  2. 如果避免出现这种情况,则在外层嵌套template(页面渲染不生成dom节点),在这一层进行v-if判断,然后在内部进行v-for循环
    <template v-if="isShow">
        <p v-for="item in items">
    </template>
  1. 如果条件出现在循环内部,可通过计算属性computed提前过滤掉那些不需要显示的项
    computed: {
        items: function() {
          return this.list.filter(function (item) {
            return item.isShow
          })
        }
    }

Vue组件间通信方式

整理vue中8种常规的通信方案

props传递数据

  • 适用场景:父组件传递数据给子组件
  • 子组件设置props属性,定义接收父组件传递过来的参数
  • 父组件在使用子组件标签中通过字面量来传递值

$emit 触发自定义事件

  • 适用场景:子组件传递数据给父组件
  • 子组件通过$emit触发自定义事件,$emit第二个参数为传递的数值
  • 父组件绑定监听器获取到子组件传递过来的参数

Chilfen.vue

    this.$emit('add', good)  

Father.vue

    <Children @add="cartAdd($event)" />  

ref

  • 父组件在使用子组件的时候设置ref
  • 父组件通过设置子组件ref来获取数据 父组件
<Children ref="foo" />  
  
this.$refs.foo  // 获取子组件实例,通过子组件实例我们就能拿到对应的数据  

EventBus

  • 使用场景:兄弟组件传值
  • 创建一个中央时间总线EventBus
  • 兄弟组件通过$emit触发自定义事件,$emit第二个参数为传递的数值
  • 另一个兄弟组件通过$on监听自定义事件

Bus.js

    // 创建一个中央时间总线类  
    class Bus {  
      constructor() {  
        this.callbacks = {};   // 存放事件的名字  
      }  
      $on(name, fn) {  
        this.callbacks[name] = this.callbacks[name] || [];  
        this.callbacks[name].push(fn);  
      }  
      $emit(name, args) {  
        if (this.callbacks[name]) {  
          this.callbacks[name].forEach((cb) => cb(args));  
        }  
      }  
    }  
      
    // main.js  
    Vue.prototype.$bus = new Bus() // 将$bus挂载到vue实例的原型上  
    // 另一种方式  
    Vue.prototype.$bus = new Vue() // Vue已经实现了Bus的功能  

Children1.vue

    this.$bus.$emit('foo')  

Children2.vue

    this.$bus.$on('foo'this.handle)  

parent或parent 或parentroot

  • 通过共同祖辈$parent或者$root搭建通信侨联

兄弟组件

this.$parent.on('add',this.add)

另一个兄弟组件

this.$parent.emit('add')

attrs 与 listeners

  • 适用场景:祖先传递数据给子孙
  • 设置批量向下传属性$attrs和 $listeners
  • 包含了父级作用域中不作为 prop 被识别 (且获取) 的特性绑定 ( class 和 style 除外)。
  • 可以通过 v-bind="$attrs" 传⼊内部组件
    // child:并未在props中声明foo  
    <p>{{$attrs.foo}}</p>  
      
    // parent  
    <HelloWorld foo="foo"/>  
    // 给Grandson隔代传值,communication/index.vue  
    <Child2 msg="lalala" @some-event="onSomeEvent"></Child2>  
      
    // Child2做展开  
    <Grandson v-bind="$attrs" v-on="$listeners"></Grandson>  
      
    // Grandson使⽤  
    <div @click="$emit('some-event', 'msg from grandson')">  
    {{msg}}  
    </div>  

provide 与 inject

  • 在祖先组件定义provide属性,返回传递的值
  • 在后代组件通过inject接收组件传递过来的值

祖先组件

    provide(){  
        return {  
            foo:'foo'  
        }  
    }  

后代组件

inject:['foo'// 获取到祖先组件传递过来的值  

Vuex和Pinia

vuex
  • 适用场景: 复杂关系的组件数据传递
  • Vuex作用相当于一个用来存储共享变量的容器 21天筑基期--Vue系列
  • state用来存放共享变量的地方
  • getter,可以增加一个getter派生状态,(相当于store中的计算属性),用来获得共享变量的值
  • mutations用来存放修改state的方法。
  • actions也是用来存放修改state的方法,不过action是在mutations的基础上进行。常用来做一些异步操作
Pinia

Pinia官方文档

和Vuex相比,Pinia有什么优势?

  • mutations不再存在,只有state,gettes,actions。
  • 更友好的TypeScript支持。
  • 不再有modules的嵌套结构,每个store都是独立的,互不影响。
  • 没有命名空间模块。
  • 无需动态添加 Store,默认情况下它们都是动态的。
  • 不再需要注入、导入函数、调用函数。
  • 支持插件来扩展自身功能。
  • 支持服务端渲染(SSR)。

Vue中的$nextTick有什么作用

在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM

当数据发生变化,Vue将开启一个异步更新队列,视图需要等队列中所有数据变化完成之后,再统一进行更新

如果想要在修改数据后立刻得到更新后的DOM结构,可以使用Vue.nextTick()

父组件

    <Children ref="foo" />  
      
    this.$refs.foo  // 获取子组件实例,通过子组件实例我们就能拿到对应的数据 

watch 与 computed 的区别是什么

都是观察数据变化的(相同)

区别:

  • 计算属性将会混入到 vue 的实例中,所以需要监听自定义变量;watch 监听 data 、props 里面数据的变化;
  • computed 有缓存,它依赖的值变了才会重新计算,watch 没有;
  • watch 支持异步,computed 不支持;
  • watch 是一对多(监听某一个值变化,执行对应操作);computed 是多对一(监听属性依赖于其他属性)
  • watch 监听函数接收两个参数,第一个是最新值,第二个是输入之前的值;
  • computed 属性是函数时,都有 get 和 set 方法,默认走 get 方法,get 必须有返回值(return)

watch 的 参数:

  • deep:深度监听
  • immediate :组件加载立即触发回调函数执行

computed 缓存原理:

conputed本质是一个惰性的观察者;当计算数据存在于 data 或者 props里时会被警告;

vue 初次运行会对 computed 属性做初始化处理(initComputed),初始化的时候会对每一个 computed 属性用 watcher 包装起来 ,这里面会生成一个 dirty 属性值为 true;然后执行 defineComputed 函数来计算,计算之后会将 dirty 值变为 false,这里会根据 dirty 值来判断是否需要重新计算;如果属性依赖的数据发生变化,computed 的 watcher 会把 dirty 变为 true,这样就会重新计算 computed 属性的值。

slot

slot可以分来以下三种:

  • 默认插槽
  • 具名插槽
  • 作用域插槽

vue中key的理解

1.key 是为 Vue 中 vnode 的唯一标记,通过这个 key,我们的 diff 操作可以更准确、更快速 2.如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法。 3.如果绑定数组的索引index,则起不到优化diff算法的作用,因为一旦数组内元素进行增删,后续节点的绑定的key也会发生变化,导致diff进行多余的更新操作。

Vue 实现双向数据绑定原理是什么?

  1. Vue2.x 采用数据劫持结合发布订阅模式(PubSub 模式)的方式,通过 Object.defineProperty 来劫持各个属性的 setter、getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
  • 当把一个普通 Javascript 对象传给 Vue 实例来作为它的 data 选项时,Vue 将遍历它的属性,用 Object.defineProperty 将它们转为 getter/setter。用户看不到 getter/setter,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。

  • Vue 的数据双向绑定整合了 Observer,Compile 和 Watcher 三者,通过 Observer 来监听自己的 model 的数据变化,通过 Compile 来解析编译模板指令,最终利用 Watcher 搭起 Observer 和 Compile 之间的通信桥梁,达到数据变化->视图更新,视图交互变化(例如 input 操作)->数据 model 变更的双向绑定效果。

  1. Vue3.x 放弃了 Object.defineProperty ,使用 ES6 原生的 Proxy,来解决以前使用 Object.defineProperty 所存在的一些问题。

v-model 双向绑定的原理是什么

全网最详细的v-model讲解 - 掘金 (juejin.cn)

keep-alive缓存

keep-alivevue中的内置组件,能在组件切换过程中将状态保留在内存中,防止重复渲染DOM

keep-alive 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们

修饰符

vue中修饰符分为以下五种:

  • 表单修饰符
  • 事件修饰符
  • 鼠标按键修饰符
  • 键值修饰符
  • v-bind修饰符

自定义指令

面试官:你有写过自定义指令吗?自定义指令的应用场景有哪些? · Issue #21 · febobo/web-interview · GitHub

转载自:https://juejin.cn/post/7254014646779576378
评论
请登录