《vue》前端面试题

vue的函数式组件

Vue 提供了一种称为函数式组件的组件类型,用来定义那些没有响应数据,也不需要有任何生命周期的场景,它只接受一些props 来显示组件。

参数

functional设置为true 即表示该组件为一个函数组件
props(可选)传递值到组件内部,2.3.0版本后可以省略,框架会自动将组件上的特性解析为prop
render函数提供渲染函数来返回一个vnode

和正常自定义组件的区别?

  • 不维护响应数据
  • 无钩子函数
  • 没有instance实例,所以在组件内部没有办法像传统组件一样通过this来访问组件属性,组件需要的一切都是通过context传递的,context是一个对象

vue作用域插槽

作用域插槽就是子组件可以给父组件传参,父组件决定怎么展示,作用域插槽给了子组件将数据返给父组件的能力,子组件一样可以复用,同时父组件也可以重新组织内容和样式

常用于多级列表展示,滑动验证页面

vue-router有哪几种导航钩子?

    1.全局的钩子:beforeEach和aftrEach
    2.单个路由独享的钩子
    3.组件级的钩子:beforeRouteEnter和beforeRouteUpdate

简述$ nextTick的使用场景和原理?

$ nextTick是在下次DOM更新循环结束之后执行延迟回调,在修改数据之后使用$ nextTick,

原理:

Vue 在更新 DOM 时是异步执行的。只要侦听到数据变化,Vue 将开启一个事件队列,并缓冲在同一事件循环中发生的所有数据变更。

如果同一个 watcher 被多次触发,只会被推入到事件队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。

然后,在下一个的事件循环“tick”中,Vue 刷新事件队列并执行实际 (已去重的) 工作。

当刷新事件队列时,组件会在下一个事件循环“tick”中重新渲染。所以当我们更新完数据后,此时又想基于更新后的 DOM 状态来做点什么,此时我们就需要使用Vue.nextTick(callback)

把基于更新后的DOM 状态所需要的操作放入回调函数callback中,这样回调函数将在 DOM 更新完成后被调用。

通俗易懂了解Vue中nextTick的内部实现原理-布布扣-bubuko.com

$on,$once,$off以及$emit的使用

$on

  使用:vm.$on('事件名称',callback)

  说明:监听当前实例(vm)中的自定义事件,事件可以由$emit定义

$once

  使用:vm.$once('事件名称',callback)

  说明:监听当前实例(vm)中的自定义事件,事件可以由$emit定义,但是只会触发一次,触发后即解除

$off

  使用:vm.$off('事件名称')

  说明:关闭当前实例中的自定义事件

$emit

  使用:vm.$emit('事件名称',args)

  说明:自定义事件

简述vuex有哪几个模块,存取方式都有哪些?

  1. state:定义了应用程序的数据,可以设置默认的初始状态。
  2. getters:允许组件从 store 中获取数据 。
  3. mutations:是唯一更改 store 中状态的方法,且必须是同步函数。但不可以直接调用mutation,必须使用commit函数来告诉Vuex更新存储并提交更改。
  4. actions:执行异步操作来存取状态,但也不可以直接调用action,必须使用dispatch函数来执行。

通过dispatch来调用actions中的方法。当actions调用commit的方法来触发mutations里面的方法修改数据

 存值: this.$store.commit('xx', '')

 取值:this.$store.state.xx

 Vuex也提供了一些辅助工具,如mapStates,mapGetters,mapMutations,mapActions,从而来减少繁杂的工作量

用法:

 ...mapGetters(['realname','money_us'])

映射关系

mapState > computed

mapGetters > computed

mapMutations > methods

mapActions > methods

简述vue中procide / inject的使用场景及工作方式

provide 和 inject 主要为高阶插件/组件库提供用例。并不推荐直接用于应用程序代码中。

定义说明:这对选项是一起使用的。以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。

简单的来说就是在父组件中通过provider来提供变量,然后在子组件中通过inject来注入变量。

需要注意的是这里不论子组件有多深,只要调用了inject那么就可以注入provider中的数据。而不是局限于只能从当前父组件的prop属性来获取数据。

 vue的生命周期

    1、创建vue实例,Vue();

  2、在创建Vue实例的时候,执行了init(),在init过程中首先调用了beforeCreate钩子函数;在beforeCreated阶段:ue实例的挂载元素$el和数据对象data都为undefined,还未初始化

  3、同时监听data数据,初始化vue内部事件,进行属性和方法的计算

  4、以上都干完了,调用Created钩子函数;在created阶段,vue实例的数据对象data有了,$el还没有

  5、模板编译,把data对象里面的数据和vue语法写的模板编译成HTML。编译过程分三种情况:1)实例内部有template属性,直接调用,然后调用render函数去渲染;2)没有该属性调用外部html;3)都没有抛出错误;

  6、编译模板完成,调用beforeMount钩子函数;在beforeMount阶段,vue实例的$el和data都初始化了,但还是挂载之前为虚拟的dom节点,data.message还未替换

  7、render函数执行之后,将渲染出来的内容挂载到DOM节点上;

  8、挂载结束,调用Mounted钩子函数;vue实例挂载完成,data.message成功渲染。

  9、数据发生变化,调用beforeUpdate钩子函数,经历virtual Dom

  10、更新完成,调用Updated钩子函数

  11、beforeDestory销毁所有观察者、组件及事件监听;

  12、Destoryed实例销毁;

keep-alive 是Vue内置的一个组件,他也有生命周期,页面第一次进入,钩子的触发顺序 activated,退出时触发 deactivated

vue3生命周期的变化

  • beforeCreate ---->setup
  • created ---->setup
  • beforeMount ---->onBeforeMount
  • mounted ---->onMounted
  • beforeUpdate ---->onBeforeUpdate
  • updated ---->onUpdated
  • beforeDestory ---->onBeforeUnmount
  • destoryed ---->onUnmounted

vue双向绑定的原理

Vue是采用数据劫持结合观察者(发布/订阅)模式的方式,通过Object.defineProperty()来劫持各个属性的settergetter,在数据变动时发布消息给订阅者,订阅者会触发它的update方法,对视图进行更新。

具体步骤:

第一步:需要observe的数据对象进行递归遍历,包括子属性对象的属性,都加上setter和getter

这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化

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

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

1、在自身实例化时往属性订阅器(dep)里面添加自己

2、自身必须有一个update()方法

3、待属性变动dep.noticf()通知时,能调用自身的update()方法,并触发Compile中绑定的回调,则功成身退。

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

VUE双向绑定_双向绑定set和get是什么-CSDN博客

active-class是什么?

       主动类是VUE路由器模块的路由器连接组件中的属性,用来做选中样式的切换

vue中data为什么是一个函数?

一个组件可以被多个组件使用,那么这个子组件在每个组件中的 data 数据应该是相互独立的,所以使用工厂函数,每次都会生成一个新的对象(不是拷贝)
组建中的data写成一个函数,数据以函数返回值的形式定义,这样每次复用组件的时候,都会返回一份新的data,相当于每个组件实例都有自己私有的数据空间,它们只负责各自维护的数据,不会造成混乱。而单纯的写成对象形式,就是所有的组件实例共用了一个data,这样改一个全都改了。

 vuex刷新页面后数据状态丢失怎么处理?

使用sessionStorage,让vuex中store的状态从sessionStorage取值,并和sessionStorage保持一致

 Vue中V-if如果和V-show有什么区别?

相同点:v-if与v-show都可以动态控制dom元素显示隐藏

不同点:v-if显示隐藏是将dom元素整个添加或删除,而v-show隐藏则是为该元素添加css--display:none,dom元素还在。

 vue插槽的原理及使用场景

原理:Vue 实现了一套内容分发的 API,这套 API 的设计灵感源自 Web Components 规范草案,将 <slot> 元素作为承载分发内容的出口。其实就相当于占位符。它在组件中给你的HTML模板占了一个位置,让你来传入一些东西。插槽又分为匿名插槽具名插槽以及作用域插槽

场景:复用组件

VUE中插槽slot用法 及其使用场景_vue的slot插槽-CSDN博客

【Vue原理】Slot - 源码版之普通插槽 - 知乎

vue组件通信(6种方式)

  1. props/$emit(父组件通过props给子组件传值,子组件$emit给父组件传值)
  2. $emit/$on(通过一个空的Vue实例作为中央事件总线(事件中心),用它来触发事件和监听事件。 var Event=new Vue();   Event.$emit(事件名,数据);   Event.$on(事件名,data => {});
  3. vuex
  4. $attrs/$listeners($attrs$listeners 是两个对象,$attrs 里存放的是父组件中绑定的非 Props 属性,$listeners里存放的是父组件中绑定的非原生事件。
  5. provide/inject(祖先组件中通过provider来提供变量,然后在子孙组件中通过inject来注入变量
  6. $parent / $children与 ref(ref:在子组件上,引用就向组件实例,$parent / $children:访问父 / 子实例

滑动验证页面

vue路由如何传参?

一、直接调用$router.push 实现携带参数的跳转
        this.$router.push({
           path: `/describe/${id}`,
        })
二、通过路由属性中的name来确定匹配的路由,通过params来传递参数。
       this.$router.push({
          name: 'Describe',
          params: {
            id: id
          }})
三、使用path来匹配路由,然后通过query来传递参数
         this.$router.push({
          path: '/describe',
          query: {
            id: id
          }})

 vue父子组件的渲染顺序

加载渲染过程

父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted

子组件更新过程

父beforeUpdate->子beforeUpdate->子updated->父updated

父组件更新过程

父beforeUpdate->父updated

销毁过程

父beforeDestroy->子beforeDestroy->子destroyed->父destroyed

vue项目优化

一、代码层面的优化

  • v-if 和 v-show 区分使用场景(v-if 适用于在运行时很少改变条件,不需要频繁切换条件的场景;v-show 则适用于需要非常频繁切换条件的场景。)
  • computed 和 watch 区分使用场景
  • v-for 遍历必须为 item 添加 key,且避免同时使用 v-if
  • 长列表性能优化(Object.freeze 方法冻结一个对象)
  • 事件的销毁
  • 图片资源懒加载(vue-lazyload 插件)
  • 路由懒加载
  • 第三方插件的按需引入( babel-plugin-component)
  • 优化无限列表性能(虚拟窗口)
  • 服务端渲染 SSR or 预渲染

二、Webpack 层面的优化

  • Webpack 对图片进行压缩(image-webpack-loader)
  • 减少 ES6 转为 ES5 的冗余代码( babel-plugin-transform-runtime )
  • 提取公共代码(CommonsChunkPlugin)
  • 模板预编译(vue-template-loader,当使用 DOM 内模板或 JavaScript 内的字符串模板时,模板会在运行时被编译为渲染函数。)
  • 提取组件的 CSS
  • 优化 SourceMap(解决不好调式代码问题)
  • 构建结果输出分析(为了更简单、直观地分析输出结果)

三、基础的 Web 技术优化

  • 开启 gzip 压缩
  • 浏览器缓存
  • CDN 的使用
  • 使用 Chrome Performance 查找性能瓶颈

Vue之性能优化篇 - 简书

vue项目统计性能数据

通过Performance API提供的对象,Performace接口允许访问当前页面性能相关的信息,可以统计到页面白屏时间及各种阶段所占用的时间。滑动验证页面

请讲述下VUE的MVVM的理解?

MVVM 是 Model-View-ViewModel的缩写,即将数据模型与数据表现层通过数据驱动进行分离,从而只需要关系数据模型的开发,而不需要考虑页面的表现,具体说来如下:

Model代表数据模型:主要用于定义数据和操作的业务逻辑。

View代表页面展示组件(即dom展现形式):负责将数据模型转化成UI 展现出来。

ViewModel为model和view之间的桥梁:监听模型数据的改变和控制视图行为、处理用户交互。通过双向数据绑定把 View 层和 Model 层连接了起来,而View 和 Model 之间的同步工作完全是自动的,无需人为干涉

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

v-for中的key的理解?

key 值:用于管理可复用的元素。因为 Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染

需要使用key来给每个节点做一个唯一标识,Diff算法就可以正确的识别此节点。主要是为了高效的更新虚拟DOM。

vue diff算法的理解

1)diff算法的作用:用来修改dom的一小段,不会引起dom树的重绘

2)diff算法的实现原理:diff算法将virtual dom的某个节点数据改变后生成的新的vnode与旧节点进行比较,并替换为新的节点,具体过程就是调用patch方法,比较新旧节点,一边比较一边给真实的dom打补丁进行替换

vue diff算法和react diff的区别

相同点:
Vue和react的diff算法,都是不进行跨层级比较,只做同级比较

不同点:

1.Vue进行diff时,调用patch打补丁函数,一边比较一边给真实的DOM打补丁
2.Vue对比节点,当节点元素类型相同,但是className不同时,认为是不同类型的元素,删除重新创建,而react则认为是同类型节点,进行修改操作
3.① Vue的列表比对,采用从两端到中间的方式,旧集合和新集合两端各存在两个指针,两两进行比较,如果匹配上了就按照新集合去调整旧集合,每次对比结束后,指针向队列中间移动;
②而react则是从左往右依次对比,利用元素的index和标识lastIndex进行比较,如果满足index < lastIndex就移动元素,删除和添加则各自按照规则调整;
③当一个集合把最后一个节点移动到最前面,react会把前面的节点依次向后移动,而Vue只会把最后一个节点放在最前面,这样的操作来看,Vue的diff性能是高于react的

什么是虚拟DOM?

虚拟DOM,也就是我们常说的虚拟节点,是用JS对象来模拟真实DOM中的节点,该对象包含了真实DOM的结构及其属性,用于对比虚拟DOM和真实DOM的差异,从而进行局部渲染来达到优化性能的目的。

为什么需要虚拟dom?

  • DOM操作非常耗时,所以使用VDOM,我们把计算转移为JS计算,
  • VDOM-用JS模拟DOM结构,计算出最小的变更,操作DOM
  • 因为有了虚拟DOM,所以让Vue有了跨平台的能力

真实的元素节点:

<div id="wrap">
    <p class="title">Hello world!</p>
</div>

VNode:

{
    tag:'div',
    attrs:{
        id:'wrap'
    },
    children:[
        {
            tag:'p',
            text:'Hello world!',
            attrs:{
                class:'title',
            }
        }
    ]

虚拟DOM与Diff算法_为生命 虚拟dom会减少重排-CSDN博客

如何封装一个vue组件?

根据业务需求,建立组件的模板,先把架子搭起来,写写样式,考虑好组件的基本逻辑。

准备好组件的数据输入。即分析好逻辑,定好 props 里面的数据、类型。
准备好组件的数据输出。即根据组件逻辑,做好要暴露出来的方法。

vue中的v-cloak的理解?

使用 v-cloak 指令设置样式,这些样式会在 Vue 实例编译结束时,从绑定的 HTML 元素上被移除。

一般用于解决网页闪屏的问题,在对一个的标签中使用v-cloak,然后在样式中设置[v-cloak]样式,[v-cloak]需写在 link 引入的css中,或者写一个内联css样式,写在import引入的css中不起作用。

98.v-model原理及底层实现

v-model本质上不过是语法糖,真正的实现靠的还是,v-bind:绑定响应式数据,v-on做事件的绑定

v-model等同于:

{{username}} <br/>
<input type="text" :value="username" @input="username=$event.target.value">

input 输入值后更新data

  首先在页面初始化时候,vue的编译器会编译该html模板文件,将页面上的dom元素遍历生成一个虚拟的dom树。再递归遍历虚拟的dom的每一个节点。当匹配到其是一个元素而非纯文本,则继续遍历每一个属性。
  如果遍历到v-model这个属性,则会为这个节点添加一个input事件,当监听从页面输入值的时候,来更新vue实例中的data想对应的属性值。

    // 假如node是遍历到的input节点
    node.addEventListener("input",function(e){
        vm.name=e.target.value;
    })  

data的属性赋值后更新input的值

  同样初始化vue实例时候,会递归遍历data的每一个属性,并且通过defineProperty来监听每一个属性的get,set方法,从而一旦某个属性重新赋值,则能监听到变化来操作相应的页面控制。

    Object.defineProperty(data,"name",{
        get(){
            return data["name"];
        },
        set(newVal){
            let val=data["name"];
            if (val===newVal){
                return;
            }
            data["name"]=newVal;
            // 监听到了属性值的变化,假如node是其对应的input节点
            node.value=newVal;
        }    
    })

总结

  其核心就是,一方面modal层通过defineProperty来劫持每个属性,一旦监听到变化通过相关的页面元素更新。另一方面通过编译模板文件,为控件的v-model绑定input事件,从而页面输入能实时更新相关data属性值。

computed和watch的用法和区别?

computed

1)根据传入的变量的变化 进行结果的更新。
2)计算属性基于响应式依赖进行缓存。如其中的任意一个值未发生变化,它调用的就是上一次计算缓存的数据,因此提高了程序的性能。而methods中每调用一次就会重新计算一次,为了进行不必要的资源消耗,选择用计算属性。

watch

1)计算属性的时候 初始化的时候就可以被监听到并且计算 但是watch是发生改变的时候才会触发。
2)当有一些数据需要随着其它数据变动而变动时,或者当需要在数据变化时执行异步或开销较大的操作时,使用 watch。

如果我们想在创建时监听value,要使用 handler  和 immediate

watch: {
    value:{
      handler:function(o,n){},
      immediate: true
    } 
  }

总结:

1)计算属性变量在computed中定义,属性监听在data中定义。
2)计算属性是声明式的描述一个值依赖了其他值,依赖的值改变后重新计算结果更新DOM。属性监听的是定义的变量,当定义的值发生变化时,执行相对应的函数。

使用场景:

computed:当模板中的某个值需要通过一个或多个数据计算得到时,就可以使用计算属性,还有计算属性的函数不接受参数;

watch:监听属性主要是监听某个值发生变化后,对新值去进行逻辑处理。

vue中delete和Vue.delete删除数组的区别?

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

vue路由跳转和location.href的区别?

使用location.href='/url'来跳转,简单方便,但是刷新了页面;
使用路由方式跳转,无刷新页面,静态跳转;

vue的自定义指令?

自定义指令分为全局指令和组件指令,其中全局指令需要使用directive来进行定义,组件指令需要使用directives来进行定义,具体定义方法同过滤器filter或者其他生命周期,具体使用方法如下:

全局自定义指令 directive(name,{}),其中name表示定义的指令名称(定义指令的时候不需要带v-,但是在调用的时候需要带v-),第二个参数是一个对象,对象中包括五个自定义组件的钩子函数,具体包括:

  1. bind函数:只调用一次,指令第一次绑定在元素上调用,即初始化调用一次,

  2. inserted函数:并绑定元素插入父级元素(即new vue中el绑定的元素)时调用(此时父级元素不一定转化为了dom)

  3. update函数:在元素发生更新时就会调用,可以通过比较新旧的值来进行逻辑处理

  4. componentUpdated函数:元素更新完成后触发一次

vue2和vue3的区别及vue3的提升

1. vue2和vue3双向数据绑定原理发生了改变

vue2 是利用Object.definePropert()对数据进行劫持 结合 发布订阅模式的方式来实现的。

vue3 中使用ProxyAPI 对数据代理。

相比于vue2.x,使用proxy的优势如下

  • defineProperty只能监听某个属性,不能对全对象监听,因为是通过遍历data属性,利用Object.definePrototype将其转化成setter/getter,但是由于现代js的限制以及object.observe的限制,vue无法检测到对象属性的添加或删除
  • 可以省去for in、闭包等内容来提升效率(直接绑定整个对象即可)
  • 可以监听数组,不用再去单独的对数组做特异性操作 vue3.x可以检测到数组内部数据的变化

2. 默认进行懒观察(lazy observation)。

在 2.x 版本里,不管数据多大,都会在一开始就为其创建观察者。当数据很大时,这可能会在页面载入时造成明显的性能压力。

3.x 版本,只会对「被用于渲染初始可见部分的数据」创建观察者,而且 3.x 的观察者更高效。

3. 更精准的变更通知。

比例来说:2.x 版本中,使用 Vue.set 来给对象新增一个属性时,这个对象的所有 watcher 都会重新运行;3.x 版本中,只有依赖那个属性的 watcher 才会重新运行。

4.大幅提升运行时的性能:重写虚拟dom,效果提升30%~300%,跳过静态节点,只处理动态节点。而静态节点渲染一次就不管了,所以处理的数据量会有一个巨大的下降,从而提升巨大性能。

5.提升网络性能:tree-shaking机制,shaking:通过代码反向监测那些特性被用到,因此会决定打包的时候会打包那些

6.完全typescript支持

7.Fragment:模板更简单。不需要最外层div节点

8.Suspense:强大的异步组件。

9.composition-api:逻辑重用

10.vue2和vue3组件编写方式改变

vue3.0 到底提升了什么?(值得收藏)-CSDN博客

 Proxy与Object.defineProperty的区别及用法

区别 :

  1. Proxy使用上比Object.defineProperty方便的多。
  2. Proxy代理整个对象,Object.defineProperty只代理对象上的某个属性。(Vue.set(object, key, value),解决视图不更新问题)
  3. 如果对象内部要全部递归代理,则Proxy可以只在调用时递归,而Object.defineProperty需要在一开始就全部递归,Proxy性能优于Object.defineProperty。
  4. 对象上定义新属性时,Proxy可以监听到,Object.defineProperty监听不到。
  5. 数组新增删除修改时,Proxy可以监听到,Object.defineProperty监听不到。
  6. Proxy不兼容IE,Object.defineProperty不兼容IE8及以下。

用法:

  • defineProperty用法 Object.defineProperty(obj, prop, descriptor)-----------------》obj:必需。目标对象  prop:必需。需定义或修改的属性的名字  descriptor:必需。目标属性所拥有的特性
  • proxy用法 var proxy = new Proxy(target, handler);----------------》target——要对其基本操作进行自定义的对象。handler——要自定义操作方法.

Proxy 用于修改某些操作的默认行为,可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。

new Proxy()表示生成一个Proxy实例,target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为。

  • get方法用于拦截某个属性的读取操作,可以接受三个参数,依次为目标对象、属性名和 proxy 实例本身(严格地说,是操作行为所针对的对象),其中最后一个参数可选。
  • set方法用来拦截某个属性的赋值操作,可以接受四个参数,依次为目标对象、属性名、属性值和 Proxy 实例本身,其中最后一个参数可选。
  • apply方法拦截函数的调用、call和apply操作。接受三个参数,分别是目标对象、目标对象的上下文对象(this)和目标对象的参数数组。
  • has方法用来拦截HasProperty操作,即判断对象是否具有某个属性时,这个方法会生效。典型的操作就是in运算符。接受两个参数,分别是目标对象、需查询的属性名。
  • deleteProperty方法用于拦截delete操作,如果这个方法抛出错误或者返回false,当前属性就无法被delete命令删除。

Proxy返回的是一个新对象,我们可以只操作新的对象达到目的,而Object.defineProperty只能遍历对象属性直接修改。

vue首屏白屏如何解决?

1)路由懒加载
2)vue-cli开启打包压缩 和后台配合 gzip访问
3)进行cdn加速
4)开启vue服务渲染模式
5)用webpack的externals属性把不需要打包的库文件分离出去,减少打包后文件的大小
6)在生产环境中删除掉不必要的console.log

7)开启nginx的gzip ,在nginx.conf配置文件中配置

8)添加loading效果,给用户一种进度感受

 怎样理解 Vue 的单向数据流?

所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。

vue的keep-alive的原理?

介绍

keep-alive是Vue的一个内置组件。可以使被包含的组件保留状态,或避免重新渲染  (它能够将不活动的组件实例保存在内存中,而不是直接将其销毁)。

它是一个抽象组件,不会被渲染到真实DOM中,也不会出现在父组件链中。它提供了include与exclude两个属性,允许组件有条件地进行缓存。

页面第一次进入,钩子的触发顺序:created-> mounted-> activated,退出时触发 deactivated

当再次进入(前进或者后退)时,只触发activated事件挂载的方法等。只执行一次的放在 mounted 中;组件每次进去执行的方法放在 activated 中

属性

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

原理

其实就是在created时将需要缓存的VNode节点保存在this.cache中。在render时,如果VNode的name符合在缓存条件(可以用include以及exclude控制),则会从this.cache中取出之前缓存的VNode实例进行渲染。

因为keep-alive会将组件保存在内存中,并不会销毁以及重新创建,所以不会重新调用组件的created等方法,需要用activated与deactivated这两个生命钩子来得知当前组件是否处于活动状态。

滑动验证页面

vue项目实现按需加载的3种方式

vue异步组件

   component: resolve => require(['../components/PromiseDemo'], resolve)

es提案的import()

const ImportFuncDemo1 = () => import('../components/ImportFuncDemo1')

webpack的require.ensure()

  component: resolve => require.ensure([], () => resolve(require('../components/PromiseDemo')), 'demo')

vue事件修饰符

.stop 阻止事件继续传播

.prevent 阻止标签默认行为

.capture 使用事件捕获模式,即元素自身触发的事件先在此处处理,然后才交由内部元素进行处理

.self 只当在 event.target 是当前元素自身时触发处理函数

.once 事件将只会触发一次

.passive 告诉浏览器你不想阻止事件的默认行为

 Vue3 ref、reactive、toRef、toRefs的区别

reactive 用于为对象添加响应式状态。接收一个js对象作为参数,返回一个具有响应式状态的副本

ref 用于为数据添加响应式状态。获取数据值的时候需要加.value,参数可以传递任意数据类型,vue 3.0 setup里定义数据时推荐优先使用ref。ref 和 reactive 本质我们可以简单地理解为ref是对reactive的二次包装

toRef 用于为源响应式对象上的属性新建一个ref,从而保持对其源对象属性的响应式连接。接收两个参数:源响应式对象和属性名,返回一个ref数据。例如使用父组件传递的props数据时,要引用props的某个属性且要保持响应式连接时就很有用。

toRefs 用于将响应式对象转换为结果对象,其中结果对象的每个属性都是指向原始对象相应属性的ref。用法和 toRef 类似,只不过 toRef 是一个个手动赋值,而 toRefs 是自动赋值。。

Vue监听Array三步曲

第一步:先获取原生 Array 的原型方法,因为拦截后还是需要原生的方法帮我们实现数组的变化。

第二步:对 Array 的原型方法使用 Object.defineProperty 做一些拦截操作。

第三步:把需要被拦截的 Array 类型的数据原型指向改造后原型。

const arrayProto = Array.prototype // 获取Array的原型
 function def (obj, key) {
    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        value: function(...args) {
            console.log(key); // 控制台输出 push
            console.log(args); // 控制台输出 [Array(2), 7, "hello!"]
            
            // 获取原生的方法
            let original = arrayProto[key];
            // 将开发者的参数传给原生的方法,保证数组按照开发者的想法被改变
            const result = original.apply(this, args);
 
            // do something 比如通知Vue视图进行更新
            console.log('我的数据被改变了,视图该更新啦');
            this.text = 'hello Vue';
            return result;
        }
    });
}
 

Vue 中$set 的原理是什么,是怎么实现的?

用法:数组 this.$set(Array, index, newValue) / 对象 this.$set(Object, key, value)

先进行一个判断,判断target不是undefined、null、string、number、symbol、boolean类型的数据。

1.如果target是数组,那么根据key值及数组长度更改数组的长度(取其中较大者),然后直接使用splice函数修改数组,虽然vue没有监听数组变化,但是监听了数组的push,pop,shift,unshift,splice,sort,reverse函数,所以使用splice也可以达到更新dom的目的

2.如果target是一个对象,且key是对象已存在的私有属性,那么直接赋值就可以了,因为这个key必然是被监听过的

3.如果这个key目前没有存在于对象中,那么会进行赋值并监听。这里省略了ob的判断,那么ob是什么呢,vue中初始化的数据(比如data中的数据)在页面初始化的时候都会被监听,而被监听的属性都会被绑定__ob__属性,这里就是判断这个数据有没有被监听的。如果这个数据没有被监听,那么就默认你不想监听这个数据,所以直接赋值并返回

vue中route和router的区别?

$router用于跳转,属性有push、go、replace

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

vue三种 watcher 的执行顺序

computed-render -> normal-watcher ( watch 中定义 ) -> render-watcher(更新组件的视图)

这样安排是有原因的,这样就能尽可能的保证,在更新组件视图的时候,computed 属性已经是最新值了,如果 render-watcher 排在 computed-render 前面,就会导致页面更新的时候 computed 值为旧数据

vue组件模板渲染和更新的过程

 vue 组件初次渲染过程

  1. 解析模板为 render 函数(把 vue 语法编译 成 js 语法,通过执行 vue-template-compiler 的 compiler 函数,得到 render)
  2. 触发响应式,监听 data 属性的 getter 和 setter(响应式关键 Object.defineProperty(),将模版初次渲染使用到的变量绑定到 Object.defineProperty() 中,首次渲染会 触发 getter )
  3. 执行 render 函数, 生成 vnode,patch(elem,vnode)(第一点说到已经得到render函数,现在要执行render函数, 将 vue 语法转成 h 函数的结果,也就是 vNode,后续进行一系列操作)

vue 组件更新过程

  1. 修改 data, 触发 setter (此前在getter中已被监听,调用Dep.notify(),将通知它内部的所有的Watcher对象进行视图更新)
  2. 重新执行 render 函数,生成 newVnode
  3. patch(vnode, newVnode)

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值