前端面试题 —— Vue (二)

目录

一、过滤器的作用,如何实现一个过滤器

二、v-model 是如何实现的,语法糖实际是什么?

三、$nextTick 原理及作用

四、Vue 中给 data 中的对象属性添加一个新的属性时会发生什么?如何解决?

五、简述 mixin、extends 的覆盖逻辑

六、子组件可以直接改变父组件的数据吗?

七、assets和static的区别

八、delete和Vue.delete删除数组的区别

九、什么是 mixin ?

十、template和jsx的有什么分别?

十一、vue初始化页面闪动问题

十二、Vue 子组件和父组件执行顺序

十三、Vue-Router 的懒加载如何实现

十四、如何定义动态路由?如何获取传过来的动态参数?

(1)param方式

路由定义

路由跳转

参数获取

(2)query方式

路由定义

跳转方法

获取参数

十五、Vue-router 路由钩子在生命周期的体现

一、Vue-Router导航守卫

二、Vue路由钩子在生命周期函数的体现

十六、Vue-router跳转和location.href有什么区别

十七、Vue-router 导航守卫有哪些

十八、Vuex和单纯的全局对象有什么区别?

十九、为什么 Vuex 的 mutation 中不能做异步操作?

二十、Vue 3.0 中的 Vue Composition API?

二十一、Composition API与React Hook很像,区别是什么

二十二、为什么要用虚拟DOM

(1)保证性能下限,在不进行手动优化的情况下,提供过得去的性能

(2)跨平台

二十三、为什么不建议用index作为key?


一、过滤器的作用,如何实现一个过滤器

根据过滤器的名称,过滤器是用来过滤数据的,在Vue中使用 filters 来过滤数据,filters 不会修改数据,而是过滤数据,改变用户看到的输出(计算属性 computed,方法 methods 都是通过修改数据来处理数据格式的输出显示)。

使用场景:

  • 需要格式化数据的情况,比如需要处理时间、价格等数据格式的输出 / 显示。
  • 比如后端返回一个年月日的日期字符串,前端需要展示为多少天前的数据格式,此时就可以用fliters过滤器来处理数据。

过滤器是一个函数,它会把表达式中的值始终当作函数的第一个参数。过滤器用在插值表达式 {{ }} 和 v-bind 表达式 中,然后放在操作符|后面进行指示。

例如,在显示金额,给商品价格添加单位:

<li>商品价格:{{item.price | filterPrice}}</li>


filters: {

    filterPrice (price) {

      return price ? ('¥' + price) : '--'

    }

}

二、v-model 是如何实现的,语法糖实际是什么?

(1)作用在表单元素上

动态绑定了 input 的 value 指向了 messgae 变量,并且在触发 input 事件的时候去动态把 message设置为目标值:

<input v-model="sth" />

//  等同于

<input v-bind:value="message" v-on:input="message=$event.target.value" />


//$event 指代当前触发的事件对象;

//$event.target 指代当前触发的事件对象的dom;

//$event.target.value 就是当前dom的value值;

//在@input方法中,value => sth;

//在:value中,sth => value;

(2)作用在组件上

在自定义组件中,v-model 默认会利用名为 value 的 prop和名为 input 的事件,本质是一个父子组件通信的语法糖,通过prop和$.emit实现。

因此父组件 v-model 语法糖本质上可以修改为:

<child :value="message"  @input="function(e){message = e}"></child>

在组件的实现中,可以通过 v-model属性来配置子组件接收的prop名称,以及派发的事件名称。

例子:

// 父组件

<aa-input v-model="aa"></aa-input>

// 等价于

<aa-input v-bind:value="aa" v-on:input="aa=$event.target.value"></aa-input>



// 子组件:

<input v-bind:value="aa" v-on:input="onmessage"></aa-input>



props:{value:aa,}

methods:{

    onmessage(e){

        $emit('input',e.target.value)

    }

}

默认情况下,一个组件上的v-model 会把 value 用作 prop且把 input 用作 event。但是一些输入类型比如单选框和复选框按钮可能想使用 value prop 来达到不同的目的。使用 model 选项可以回避这些情况产生的冲突。js 监听input 输入框输入数据改变,用oninput,数据改变以后就会立刻出发这个事件。通过input事件把数据$emit 出去,在父组件接受。父组件设置v-model的值为input $emit过来的值。

三、$nextTick 原理及作用

Vue 的 nextTick 其本质是对 JavaScript 执行原理 EventLoop 的一种应用。

nextTick 的核心是利用了如 Promise 、MutationObserver、setImmediate、setTimeout的原生 JavaScript 方法来模拟对应的微/宏任务的实现,本质是为了利用 JavaScript 的这些异步回调任务队列来实现 Vue 框架中自己的异步回调队列。

nextTick 不仅是 Vue 内部的异步队列的调用方法,同时也允许开发者在实际项目中使用这个方法来满足实际应用中对 DOM 更新数据时机的后续逻辑处理

nextTick 是典型的将底层 JavaScript 执行原理应用到具体案例中的示例,引入异步更新队列机制的原因∶

  • 如果是同步更新,则多次对一个或多个属性赋值,会频繁触发 UI/DOM 的渲染,可以减少一些无用渲染
  • 同时由于 VirtualDOM 的引入,每一次状态发生变化后,状态变化的信号会发送给组件,组件内部使用 VirtualDOM 进行计算得出需要更新的具体的 DOM 节点,然后对 DOM 进行更新操作,每次更新状态后的渲染过程需要更多的计算,而这种无用功也将浪费更多的性能,所以异步渲染变得更加至关重要

Vue采用了数据驱动视图的思想,但是在一些情况下,仍然需要操作DOM。有时候,可能遇到这样的情况,DOM1的数据发生了变化,而DOM2需要从DOM1中获取数据,那这时就会发现DOM2的视图并没有更新,这时就需要用到了nextTick了。

由于Vue的DOM操作是异步的,所以,在上面的情况中,就要将DOM2获取数据的操作写在$nextTick中。

this.$nextTick(() => {

    // 获取数据的操作...

})

所以,在以下情况下,会用到nextTick:

  • 在数据变化后执行的某个操作,而这个操作需要使用随数据变化而变化的DOM结构的时候,这个操作就需要方法在`nextTick()`的回调函数中。
  • 在vue生命周期中,如果在created()钩子进行DOM操作,也一定要放在`nextTick()`的回调函数中。

因为在created()钩子函数中,页面的DOM还未渲染,这时候也没办法操作DOM,所以,此时如果想要操作DOM,必须将操作的代码放在nextTick()的回调函数中。

四、Vue 中给 data 中的对象属性添加一个新的属性时会发生什么?如何解决?

<template>
   <div>
      <ul>
         <li v-for="value in obj" :key="value"> {{value}} </li>
      </ul>
      <button @click="addObjB">添加 obj.b</button>
   </div>
</template>

<script>
    export default {
       data () {
          return {
              obj: {
                  a: 'obj.a'
              }
          }
       },
       methods: {
          addObjB () {
              this.obj.b = 'obj.b'
              console.log(this.obj)
          }
      }
   }
</script>

点击 button 会发现,obj.b 已经成功添加,但是视图并未刷新。这是因为在Vue实例创建时,obj.b并未声明,因此就没有被Vue转换为响应式的属性,自然就不会触发视图的更新,这时就需要使用Vue的全局 api $set()

addObjB () (
   this.$set(this.obj, 'b', 'obj.b')
   console.log(this.obj)
}

$set()方法相当于手动的去把obj.b处理成一个响应式的属性,此时视图也会跟着改变了。

五、简述 mixin、extends 的覆盖逻辑

(1)mixin 和 extends

mixin 和 extends均是用于合并、拓展组件的,两者均通过 mergeOptions 方法实现合并。

  • mixins 接收一个混入对象的数组,其中混入对象可以像正常的实例对象一样包含实例选项,这些选项会被合并到最终的选项中。Mixin 钩子按照传入顺序依次调用,并在调用组件自身的钩子之前被调用。
  • extends 主要是为了便于扩展单文件组件,接收一个对象或构造函数。

(2)mergeOptions 的执行过程

  • 规范化选项(normalizeProps、normalizelnject、normalizeDirectives)
  • 对未合并的选项,进行判断
if(!child._base) {
    if(child.extends) {
        parent = mergeOptions(parent, child.extends, vm)
    }
    if(child.mixins) {
        for(let i = 0, l = child.mixins.length; i < l; i++){
            parent = mergeOptions(parent, child.mixins[i], vm)
        }
    }
}
  • 合并处理。根据一个通用 Vue 实例所包含的选项进行分类逐一判断合并,如 props、data、 methods、watch、computed、生命周期等,将合并结果存储在新定义的 options 对象里。
  • 返回合并结果 options。

六、子组件可以直接改变父组件的数据吗?

子组件不可以直接改变父组件的数据。这样做主要是为了维护父子组件的单向数据流。每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。如果这样做了,Vue 会在浏览器的控制台中发出警告。

Vue提倡单向数据流,即父级 props 的更新会流向子组件,但是反过来则不行。这是为了防止意外的改变父组件状态,使得应用的数据流变得难以理解,导致数据流混乱。如果破坏了单向数据流,当应用复杂时,debug 的成本会非常高。

只能通过$emit派发一个自定义事件,父组件接收到后,由父组件修改。

七、assets和static的区别

相同点:assets 和 static 两个都是存放静态资源文件。项目中所需要的资源文件图片,字体图标,样式文件等都可以放在这两个文件下,这是相同点

不相同点:assets 中存放的静态资源文件在项目打包时,也就是运行 npm run build 时会将 assets 中放置的静态资源文件进行打包上传,所谓打包简单点可以理解为压缩体积,代码格式化。而压缩后的静态资源文件最终也都会放置在 static 文件中跟着 index.html 一同上传至服务器。static 中放置的静态资源文件就不会要走打包压缩格式化等流程,而是直接进入打包好的目录,直接上传至服务器。因为避免了压缩直接进行上传,在打包时会提高一定的效率,但是 static 中的资源文件由于没有进行压缩等操作,所以文件的体积也就相对于 assets 中打包后的文件提交较大点。在服务器中就会占据更大的空间。

建议:将项目中 template 需要的样式文件js文件等都可以放置在 assets 中,走打包这一流程。减少体积。而项目中引入的第三方的资源文件如iconfoont.css 等文件可以放置在 static 中,因为这些引入的第三方文件已经经过处理,不再需要处理,直接上传。

八、delete和Vue.delete删除数组的区别

  • delete 只是被删除的元素变成了 empty/undefined 其他的元素的键值还是不变。
  • Vue.delete 直接删除了数组 改变了数组的键值。

九、什么是 mixin ?

  • Mixin 使我们能够为 Vue 组件编写可插拔和可重用的功能。
  • 如果希望在多个组件之间重用一组组件选项,例如生命周期 hook、 方法等,则可以将其编写为 mixin,并在组件中简单的引用它。
  • 然后将 mixin 的内容合并到组件中。如果你要在 mixin 中定义生命周期 hook,那么它在执行时将优化于组件自已的 hook。

十、template和jsx的有什么分别?

对于 runtime 来说,只需要保证组件存在 render 函数即可,而有了预编译之后,只需要保证构建过程中生成 render 函数就可以。在 webpack 中,使用 vue-loader 编译.vue文件,内部依赖的vue-template-compiler 模块,在 webpack 构建过程中,将template预编译成 render 函数。与 react 类似,在添加了jsx的语法糖解析器 babel-plugin-transform-vue-jsx 之后,就可以直接手写render函数。

所以,template和jsx的都是render的一种表现形式,不同的是:JSX相对于template而言,具有更高的灵活性,在复杂的组件中,更具有优势,而 template 虽然显得有些呆滞。但是 template 在代码结构上更符合视图与逻辑分离的习惯,更简单、更直观、更好维护。

十一、vue初始化页面闪动问题

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

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

[v-cloak] {
    display: none;
}

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

十二、Vue 子组件和父组件执行顺序

加载渲染过程:

  1. 父组件 beforeCreate
  2. 父组件 created
  3. 父组件 beforeMount
  4. 子组件 beforeCreate
  5. 子组件 created
  6. 子组件 beforeMount
  7. 子组件 mounted
  8. 父组件 mounted

更新过程:

  1. 父组件 beforeUpdate
  2. 子组件 beforeUpdate
  3. 子组件 updated
  4. 父组件 updated

销毁过程:

  1. 父组件 beforeDestroy
  2. 子组件 beforeDestroy
  3. 子组件 destroyed
  4. 父组件 destoryed

十三、Vue-Router 的懒加载如何实现

非懒加载:

import List from '@/components/list.vue'

const router = new VueRouter({
  routes: [
    { path: '/list', component: List }
  ]
})

(1)方案一(常用):使用箭头函数+import动态加载

const List = () => import('@/components/list.vue')

const router = new VueRouter({
  routes: [
    { path: '/list', component: List }
  ]
})

(2)方案二:使用箭头函数+require动态加载

const router = new Router({
  routes: [
   {
     path: '/list',
     component: resolve => require(['@/components/list'], resolve)
   }
  ]
})

(3)方案三:使用webpack的require.ensure技术,也可以实现按需加载。 这种情况下,多个路由指定相同的chunkName,会合并打包成一个js文件。

// r就是resolve
const List = r => require.ensure([], () => r(require('@/components/list')), 'list');

// 路由也是正常的写法  这种是官方推荐的写的 按模块划分懒加载
const router = new Router({
  routes: [
  {
    path: '/list',
    component: List,
    name: 'list'
  }
 ]
}))

十四、如何定义动态路由?如何获取传过来的动态参数?

(1)param方式

  • 配置路由格式:/router/:id
  • 传递的方式:在path后面跟上对应的值
  • 传递后形成的路径:/router/123
路由定义
//在APP.vue中

<router-link :to="'/user/'+userId" replace>用户</router-link>   

//在index.js

{
   path: '/user/:userid',
   component: User,
},
路由跳转
// 方法1:
<router-link :to="{ name: 'users', params: { uname: wade }}">按钮</router-link



// 方法2:
this.$router.push({name:'users',params:{uname:wade}})



// 方法3:
this.$router.push('/user/' + wade)
参数获取

通过 $route.params.userid 获取传递的值

(2)query方式

  • 配置路由格式:/router,也就是普通配置
  • 传递的方式:对象中使用query的key作为传递方式
  • 传递后形成的路径:/route?id=123
路由定义
//方式1:直接在router-link 标签上以对象的形式
<router-link :to="{path:'/profile',query:{name:'why',age:28,height:188}}">档案</router-link>

// 方式2:写成按钮以点击事件形式
<button @click='profileClick'>我的</button>   

profileClick(){
  this.$router.push({
    path: "/profile",
    query: {
        name: "kobi",
        age: "28",
        height: 198
    }
  });
}
跳转方法
// 方法1:
<router-link :to="{ name: 'users', query: { uname: james }}">按钮</router-link>

// 方法2:
this.$router.push({ name: 'users', query:{ uname:james }})

// 方法3:
<router-link :to="{ path: '/user', query: { uname:james }}">按钮</router-link>

// 方法4:
this.$router.push({ path: '/user', query:{ uname:james }})

// 方法5:
this.$router.push('/user?uname=' + jsmes)
获取参数

通过$route.query 获取传递的值

十五、Vue-router 路由钩子在生命周期的体现

一、Vue-Router导航守卫

有的时候,需要通过路由来进行一些操作,比如最常见的登录权限验证,当用户满足条件时,才让其进入导航,否则就取消跳转,并跳到登录页面让其登录。

为此有很多种方法可以植入路由的导航过程:全局的,单个路由独享的,或者组件级的

全局路由钩子

vue-router全局有三个路由钩子:

  • router.beforeEach 全局前置守卫 进入路由之前
  • router.beforeResolve 全局解析守卫(2.5.0+)在 beforeRouteEnter 调用之后调用
  • router.afterEach 全局后置钩子 进入路由之后

具体使用∶

  • beforeEach(判断是否登录了,没登录就跳转到登录页)
router.beforeEach((to, from, next) => { 
    let ifInfo = Vue.prototype.$common.getSession('userData');  // 判断是否登录的存储信息
    if (!ifInfo) {
        // sessionStorage里没有储存user信息   
        if (to.path == '/') {
            //如果是登录页面路径,就直接next()     
            next();   
        } else {
            //不然就跳转到登录     
            Message.warning("请重新登录!");    
            window.location.href = Vue.prototype.$loginUrl;   
        } 
    } else {   
        return next(); 
    }
})
  • afterEach (跳转之后滚动条回到顶部)
router.afterEach((to, from) => { 
    // 跳转之后滚动条回到顶部 
    window.scrollTo(0,0);
});

单个路由独享钩子

  • beforeEnter

如果不想全局配置守卫的话,可以为某些路由单独配置守卫,有三个参数∶ to、from、next

export default [   
    {       
        path: '/',       
        name: 'login',       
        component: login,       
        beforeEnter: (to, from, next) => {         
            console.log('即将进入登录页面')         
            next()       
        }   
    }
]

组件内钩子

  • beforeRouteUpdate、beforeRouteEnter、beforeRouteLeave

这三个钩子都有三个参数∶to、from、next

  1. beforeRouteEnter∶ 进入组件前触发
  2. beforeRouteUpdate∶ 当前地址改变并且改组件被复用时触发,举例来说,带有动态参数的路径foo/∶id,在 /foo/1 和 /foo/2 之间跳转的时候,由于会渲染同样的foa组件,这个钩子在这种情况下就会被调用
  3. beforeRouteLeave∶ 离开组件被调用

注意点,beforeRouteEnter组件内还访问不到this,因为该守卫执行前组件实例还没有被创建,需要传一个回调给 next来访问,例如:

beforeRouteEnter(to, from, next) {     
    next(target => {       
        if (from.path == '/classProcess') {         
            target.isFromProcess = true       
        }     
    })   
}

二、Vue路由钩子在生命周期函数的体现

完整的路由导航解析流程(不包括其他生命周期)

  • 触发进入其他路由
  • 调用要离开路由的组件守卫beforeRouteLeave
  • 调用局前置守卫∶ beforeEach
  • 在重用的组件里调用 beforeRouteUpdate
  • 调用路由独享守卫 beforeEnter
  • 解析异步路由组件
  • 在将要进入的路由组件中调用 beforeRouteEnter
  • 调用全局解析守卫 beforeResolve
  • 导航被确认
  • 调用全局后置钩子的 afterEach 钩子
  • 触发DOM更新(mounted)
  • 执行beforeRouteEnter 守卫中传给 next 的回调函数

触发钩子的完整顺序

路由导航、keep-alive、和组件生命周期钩子结合起来的,触发顺序,假设是从a组件离开,第一次进入b组件∶

  • beforeRouteLeave:路由组件的组件离开路由前钩子,可取消路由离开。
  • beforeEach:路由全局前置守卫,可用于登录验证、全局路由loading等。
  • beforeEnter:路由独享守卫。
  • beforeRouteEnter:路由组件的组件进入路由前钩子。
  • beforeResolve:路由全局解析守卫。
  • afterEach:路由全局后置钩子。
  • beforeCreate:组件生命周期,不能访问tAis。
  • created;组件生命周期,可以访问tAis,不能访问dom。
  • beforeMount:组件生命周期
  • deactivated:离开缓存组件a,或者触发a的beforeDestroy和destroyed组件销毁钩子。
  • mounted:访问/操作dom。
  • activated:进入缓存组件,进入a的嵌套子组件(如果有的话)。
  • 执行beforeRouteEnter回调函数next。

导航行为被触发到导航完成的整个过程

  • 导航行为被触发,此时导航未被确认。
  • 在失活的组件里调用离开守卫 beforeRouteLeave。
  • 调用全局的 beforeEach守卫。
  • 在重用的组件里调用 beforeRouteUpdate 守卫(2.2+)。
  • 在路由配置里调用 beforeEnteY。
  • 解析异步路由组件(如果有)。
  • 在被激活的组件里调用 beforeRouteEnter。
  • 调用全局的 beforeResolve 守卫(2.5+),标示解析阶段完成。
  • 导航被确认。
  • 调用全局的 afterEach 钩子。
  • 非重用组件,开始组件实例的生命周期:beforeCreate&created、beforeMount&mounted。
  • 触发 DOM 更新。
  • 用创建好的实例调用 beforeRouteEnter守卫中传给 next 的回调函数。
  • 导航完成。

十六、Vue-router跳转和location.href有什么区别

  • 使用 location.href= /url 来跳转,简单方便,但是刷新了页面;
  • 使用 history.pushState( /url ) ,无刷新页面,静态跳转;
  • 引进 router ,然后使用 router.push( /url ) 来跳转,使用了 diff 算法,实现了按需加载,减少了 dom 的消耗。其实使用 router 跳转和使用 history.pushState() 没什么差别的,因为vue-router就是用了 history.pushState(),尤其是在history模式下。

十七、Vue-router 导航守卫有哪些

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

十八、Vuex和单纯的全局对象有什么区别?

  • Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
  • 不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样可以方便地跟踪每一个状态的变化,从而能够实现一些工具帮助更好地了解我们的应用。

十九、为什么 Vuex 的 mutation 中不能做异步操作?

  • Vuex中所有的状态更新的唯一途径都是mutation,异步操作通过 Action 来提交 mutation实现,这样可以方便地跟踪每一个状态的变化,从而能够实现一些工具帮助更好地了解我们的应用。
  • 每个mutation执行完成后都会对应到一个新的状态变更,这样devtools就可以打个快照存下来,然后就可以实现 time-travel 了。如果mutation支持异步操作,就没有办法知道状态是何时更新的,无法很好的进行状态的追踪,给调试带来困难。

二十、Vue 3.0 中的 Vue Composition API?

在 Vue2 中,代码是 Options API 风格的,也就是通过填充 (option) data、methods、computed 等属性来完成一个 Vue 组件。这种风格使得 Vue 相对于 React极为容易上手,同时也造成了几个问题:

  1. 由于 Options API 不够灵活的开发方式,使得Vue开发缺乏优雅的方法来在组件间共用代码。
  2. Vue 组件过于依赖 this 上下文,Vue 背后的一些小技巧使得 Vue 组件的开发看起来与 JavaScript 的开发原则相悖,比如在 methods 中的 this 竟然指向组件实例来不指向 methods 所在的对象。这也使得 TypeScript 在Vue2 中很不好用。

于是在 Vue3 中,舍弃了 Options API,转而投向 Composition API。Composition API本质上是将 Options API 背后的机制暴露给用户直接使用,这样用户就拥有了更多的灵活性,也使得 Vue3 更适合于 TypeScript 结合。

如下,是一个使用了 Vue Composition API 的 Vue3 组件:

<template>
  <button @click="increment">
    Count: {{ count }}
  </button>
</template>

<script>
// Composition API 将组件属性暴露为函数,因此第一步是导入所需的函数
import { ref, computed, onMounted } from 'vue'

export default {
  setup() {
    // 使用 ref 函数声明了称为 count 的响应属性,对应于Vue2中的data函数
    const count = ref(0)

    // Vue2中需要在methods option中声明的函数,现在直接声明
    function increment() {
      count.value++
    }

    // 对应于Vue2中的mounted声明周期
    onMounted(() => console.log('component mounted!'))

    return {
      count,
      increment
    }
  }
}

</script>

显而易见,Vue Composition API 使得 Vue3 的开发风格更接近于原生 JavaScript,带给开发者更多地灵活性

二十一、Composition API与React Hook很像,区别是什么

从React Hook的实现角度看,React Hook是根据useState调用的顺序来确定下一次重渲染时的state是来源于哪个useState,所以出现了以下限制

  • 不能在循环、条件、嵌套函数中调用Hook
  • 必须确保总是在你的React函数的顶层调用Hook
  • useEffect、useMemo等函数必须手动确定依赖关系

而Composition API是基于Vue的响应式系统实现的,与React Hook的相比

  • 声明在setup函数内,一次组件实例化只调用一次setup,而React Hook每次重渲染都需要调用Hook,使得React的GC比Vue更有压力,性能也相对于Vue来说也较慢
  • Compositon API的调用不需要顾虑调用顺序,也可以在循环、条件、嵌套函数中使用
  • 响应式系统自动实现了依赖收集,进而组件的部分的性能优化由Vue内部自己完成,而React Hook需要手动传入依赖,而且必须必须保证依赖的顺序,让useEffect、useMemo等函数正确的捕获依赖变量,否则会由于依赖不正确使得组件性能下降。

虽然Compositon API看起来比React Hook好用,但是其设计思想也是借鉴React Hook的。

二十二、为什么要用虚拟DOM

(1)保证性能下限,在不进行手动优化的情况下,提供过得去的性能

看一下页面渲染的流程:

解析HTML —> 生成DOM —> 生成 CSSOM —> Layout —> Paint —> Compiler

下面对比一下修改DOM时真实DOM操作和Virtual DOM的过程,来看一下它们重排重绘的性能消耗∶

  • 真实DOM∶ 生成HTML字符串+重建所有的DOM元素
  • 虚拟DOM∶ 生成vNode+ DOMDiff+必要的dom更新

Virtual DOM的更新DOM的准备工作耗费更多的时间,也就是JS层面,相比于更多的DOM操作它的消费是极其便宜的。尤雨溪在社区论坛中说道∶ 框架给你的保证是,你不需要手动优化的情况下,依然可以给你提供过得去的性能。

(2)跨平台

Virtual DOM本质上是JavaScript的对象,它可以很方便的跨平台操作,比如服务端渲染、uniapp等。

二十三、为什么不建议用index作为key?

 使用index 作为 key和没写基本上没区别,因为不管数组的顺序怎么颠倒,index 都是 0, 1, 2...这样排列,导致 Vue 会复用错误的旧子节点,做很多额外的工作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/46964.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【C# 数据结构】Heap 堆

【C# 数据结构】Heap 堆 先看看C#中有那些常用的结构堆的介绍完全二叉树最大堆 Heap对类进行排序实现 IComparable<T> 接口 对CompareTo的一点解释 参考资料 先看看C#中有那些常用的结构 作为 数据结构系类文章 的开篇文章&#xff0c;我们先了解一下C# 有哪些常用的数据…

【防火墙】iptables防火墙(一)

防火墙具有隔离功能 主要部署在网络边缘或者主机边缘&#xff0c;防火墙的主要作用是决定哪些数据可以被外网访问&#xff0c;哪些数据可以进入内网访问 网络层&#xff08;路由器&#xff09;&#xff1a;数据的转发 安全技术 1.入侵监测系统&#xff1a;在检测到威胁&…

城市气象数据可视化:洞察气候变化,构建智慧城市

随着城市化进程的加速&#xff0c;城市气象数据的采集和分析变得越来越重要。气象数据不仅影响着人们的生活和出行&#xff0c;还与城市的发展和规划息息相关。在数字化时代&#xff0c;如何将城市中各个气象数据进行可视化&#xff0c;让复杂的数据变得简单易懂&#xff0c;成…

全国大学生数据统计与分析竞赛2021年【本科组】-B题:用户消费行为价值分析

目录 摘 要 1 任务背景与重述 1.1 任务背景 1.2 任务重述 2 任务分析 3 数据假设 4 任务求解 4.1 任务一&#xff1a;数据预处理 4.1.1 数据清洗 4.1.2 数据集成 4.1.3 数据变换 4.2 任务二&#xff1a;对用户城市分布情况与分布情况可视化分析 4.2.1 城市分布情况可视化分析 4…

微信小程序客服系统-对接消息推送-对接模板订阅消息-嵌入webview客服链接

想要给自己的小程序增加客服系统功能 小程序客服对接导自己的系统等需求&#xff0c;可以参照我开发的客服系统&#xff0c;实现私有化部署搭建对接的微信小程序 小程序消息推送对接 首先登录小程序后台在小程序后台>开发管理>开发设置>服务器域名部分&#xff0c;配置…

使用TensorFlow训练深度学习模型实战(下)

大家好&#xff0c;本文接TensorFlow训练深度学习模型的上半部分继续进行讲述&#xff0c;下面将介绍有关定义深度学习模型、训练模型和评估模型的内容。 定义深度学习模型 数据准备完成后&#xff0c;下一步是使用TensorFlow搭建神经网络模型&#xff0c;搭建模型有两个选项…

Java-运算符

目录 一、什么是运算符 二、算术运算符 1.基本四则运算符&#xff1a;加减乘除&#xff08;、-、*、/、%&#xff09; 2.增量运算符&#xff08;、-、*、%&#xff09; 3.自增、自减运算符&#xff08;、--&#xff09; 三、关系运算符 四、逻辑运算符 1.逻辑与 && …

Vue前端渲染blob二进制对象图片的方法

近期做开发&#xff0c;联调接口。接口返回的是一张图片&#xff0c;是对二进制图片处理并渲染&#xff0c;特此记录一下。 本文章是转载文章&#xff0c;原文章&#xff1a;Vue前端处理blob二进制对象图片的方法 接口response是下图 显然&#xff0c;获取到的是一堆乱码&…

【Docker】安全及日志管理

目录 一、Docker 安全及日志管理1.1 Docker 容器与虚拟机的区别1. 隔离与共享2. 性能与损耗 1.2Docker 存在的安全问题1.Docker 自身漏洞2.Docker 源码问题 1.3 Docker 架构缺陷与安全机制1. 容器之间的局域网攻击2. DDoS 攻击耗尽资源3. 有漏洞的系统调用4. 共享root用户权限 …

网安第二天笔记

ssh 22端口 账号密码登陆、证书登录 smtp 25端口 邮件协议 DNS 53 DHCP 67 68端口 四个包 1.DHCP服务器&#xff1a;服务器管理IP地址池和配置参数 2.客户端请求&#xff1a;发送DHCP广播请求&#xff0c;discover消息 3.DHCP服务器回应&#xff1a;收到discover会回复offer…

PostMan+Jmeter+QTP工具介绍及安装

目录 一、PostMan介绍​编辑 二、下载安装 三、Postman与Jmeter的区别 一、开发语言区别&#xff1a; 二、使用范围区别&#xff1a; 三、使用区别&#xff1a; 四、Jmeter安装 附一个详细的Jmeter按照新手使用教程&#xff0c;感谢作者&#xff0c;亲测有效。 五、Jme…

Linux_CentOS_7.9部署Docker以及镜像加速配置等实操验证全过程手册

前言&#xff1a;实操之前大家应该熟悉一个新的名词DevOps 俗称开发即运维、新一代开发工程师&#xff08;Development和Operations的组合词&#xff09;是一组过程、方法与系统的统称&#xff0c;用于促进开发&#xff08;应用程序/软件工程&#xff09;、技术运营和质量保障&…

Angular:动态依赖注入和静态依赖注入

问题描述&#xff1a; 自己写的服务依赖注入到组件时候是直接在构造器内初始化的。 直到看见代码中某大哥写的 private injector: Injector 动态依赖注入和静态依赖注入 在 Angular 中&#xff0c;使用构造函数注入的方式将服务注入到组件中是一种静态依赖注入的方式。这种方…

Python显示循环代码的进度条

目录 1. tqdm库 2. alive_progress库 3. progressbar库 1. tqdm库 tqdm是一个快速&#xff0c;可扩展的Python进度条&#xff0c;可以在Python长循环中添加一个进度提示信息 import time from tqdm import trangefor i in trange(100):# do somethingtime.sleep(0.5) 2. a…

微服务——Nacos配置管理

目录 Nacos配置管理——实现配置管理 配置管理实践 Nacos配置管理——微服务配置拉取 Nacos配置管理——配置热更新 方式一: ​编辑 方式二(推荐方式): Nacos配置管理——多环境配置共享 优先级问题 Nacos配置管理——nacos集群搭建 总结​编辑 Nacos配置管理——实现配置管…

【ADS】导入CMOS衬底文件+使用coilsys生成电感

新建工程经常忘记怎么操作&#xff0c;简记防遗忘。 操作步骤 1.unzip file2.原理图仿真3.Layout加载衬底文件4.使用coilsys生成电感 1.unzip file designKits-》unzip&#xff0c;选择对应库的压缩包&#xff0c;我这里是&#xff08;TSMC_CRN65GP_v2.zip&#xff09;。 为了…

BHQ 1Mal,BHQ-1 Maleimide,BHQ1马来酰亚胺,黑洞猝灭剂

资料编辑|陕西新研博美生物科技有限公司小编MISSwu​ PART1----产品描述&#xff1a; BHQ-1 Maleimide黑洞猝灭剂-1(BHQ-1)被归类为暗猝灭剂&#xff0c;该淬灭剂能够将一定距离内荧光基团发出的光全部吸收&#xff0c;实现对荧光信号的淬灭&#xff0c;所以可得到更强的特异性…

【App管理04-Bug修正 Objective-C语言】

一、咱们刚才已经把这个给大家做完了吧 1.这个Label怎么显示到上面去了, 我们现在是把它加到我们的控制器的View里面吧 我们看一下这个坐标是怎么算的,来,我们找一个坐标, 咱们的坐标,是不是用这个View的frame,减的吧 来,咱们在这里,输出一下这个Frame,看一下吧 在…

2023年第三届能源、电力与电气工程国际会议 (CoEEPE 2023)

会议简介 Brief Introduction 2023年第三届能源、电力与电气工程国际会议(CoEEPE 2023) 会议时间&#xff1a;2023年11月22日-24日 召开地点&#xff1a;澳大利亚墨尔本 大会官网&#xff1a;www.coeepe.org 2023年第三届能源、电力与电气工程国际会议(CoEEPE 2023)由安徽大学、…

Linux:Linux的发展史和作用有哪些?

文章目录 Linux是什么&#xff1f;Linux的开源特征为什么要学习Linux&#xff1f;Linux的应用场景有哪些&#xff1f; Linux是什么&#xff1f; 简单来说&#xff0c;Linux就是操作系统&#xff0c;它和Windows等软件一样&#xff0c;都只是操作系统&#xff0c;并无区别 Linu…