Vue3.0面试题(一)

Vue3.0与Vue2.0的对比

1. 修改了 Vue2.x 时期遗留的一些缺陷

2. 引入了 组合式 API 、自定义渲染 API 等新特性

Vue3.0 的新特性

1.Proxy 响应式绑定

2.Tree-Shaking Support

3.组合式 API

4.Fragment 片段、Teleport [ˈtelɪpɔːt] (传送)、Suspense[səsp'ens]

5.自定义渲染 API

6.源码优化

一、Proxy 响应式绑定

Vue2.x是通过 Object.defineProperty 的get和 set方法实现数据结持,从而实现双向数据绑定。这个API必须预先知道要拦截的 key 是什么,所以它并不能检测对象属性的添加和删除。造成数组元素的直接修改不会触发响应式机制。

例如,对象obj的text属性进行劫持:

const obj = {};

Object.defineProperty(obj,  'text',  {

  get: function() {

    console.log('get val');

  },

  set: function(newVal) {

    console.log('set val:' + newVal);

    document.getElementById('input').value = newVal;

    document.getElementById('span').innerHTML = newVal;

  }

});

const input = document.getElementById('input');

input.addEventListener('keyup', function (e) {

  obj.text = e.target.value;

})
复制代码

Vue3.0使用Proxy API做数据劫持,劫持的是整个对象,对于对象属性的增加和删除都能检测到。

const input = document.getElementById('input');

const p = document.getElementById('p');

const obj = {};

const newObj = new Proxy(obj, {

  get: function(target, key, receiver) {

    // Reflect: 反映,反射,表达

    console.log(`getting ${key}!`);

    return Reflect.get(target, key, receiver);

  },

  set: function(target, key, value, receiver) {

    console.log(target, key, value, receiver);

    if (key === 'text') {

      input.value = value;

      p.innerHTML = value;

    }

    return Reflect.set(target, key, value, receiver);

  },

});

input.addEventListener('keyup', function(e) {

  newObj.text = e.target.value;

});
复制代码

通过Proxy实现双向响应式绑定,相比 defineProperty的遍历属性的方式效率更高,性能更好,另外Virtual DOM 更新只 diff 动态部分、事件缓存等,也带来了性能上的提升。

二、Tree-Shaking Support(摇树优化)

tree-sharking在构建工具打包后消除程序中无用的代码,来减少包的体积。相比Vue2.0导入整个Vue对象,Vue3.0支持按需导入,只打包需要的代码。Tree-Shaking依赖 ES2015 模块语法的静态结构(即 import 和 export),通过编译阶段的静态分析,找到没有引入的模块并打上标记。像我们在项目中如果没有引入 Transition、KeepAlive 等不常用的组件,那么它们对应的代码就不会打包进去。

三、组合式 API

首先需要明确的是,Vue2.0中组件传统的 data,computed,watch,methods 写法,我们称之为选项式 API(Options API )。

(1)选项式 API 存在的缺陷

随着业务复杂度越来越高,代码量会不断的加大;由于代码需要遵循 option 的配置写到特定的区域,导致后续维护非常的复杂,代码可复用性也不高。比如,很长的 methods 区域代码、data变量声明与方法区域在一起。

(2)与 mixins 的比较

对于上述提到的问题,我们可能会想到会 mixins 来解决,但是当抽离并引用了大量的 mixins,就会发现两个不可避免的问题:命名冲突和数据来源不清晰。

组合式 API 和 mixins 二者的差别:

· 层级不同:组合式 API 与组件是嵌套关系,而mixin与组件是同层级关系

· 影响面不同:组合式 API 作为组件的被调用方,并且变量逻辑是组件控制,耦合性很低。而 mixins 是耦合在代码逻辑里,并且存在变量的互相引用,为将来的升级和维护埋下隐患。

(3)与 React Hook 的比较

组合式 API 的实现与 React Hook 是截然不同的,比 React Hooks简便了不少:

· 同样的逻辑组合、组件复用能力

· 只调用一次 setup 方法
更加符合 JS 直觉
没有闭包变量问题
没有内存/GC压力
不存在内联回调导致子组件永远更新的问题

(4)组合式 API 的使用

下面简单概述了组合式 API 的使用,主要是一些方法增加和概念迁移:

setup方法:
vue3.0 中,所有的代码逻辑将在setup方法中实现,包括 data、watch、computed、methods 等,并且不再有this。
vue3.0 会先执行setup方法,再执行兼容2.0的其他方法。
vue3.0 中setup方法在组件生命周期内只执行一次,不会重复执行。

生命周期钩子:
2.0中生命周期钩子放在跟 methods 同级属性下。
3.0中需要先导入钩子,然后在 setup 方法中注册钩子回调,并且钩子命名也跟 React 保持一样。
3.0移除了2.0中的 beforeCreate 和 created 钩子,通过 setup 方法代替。

// vue2.0

export default {

    data () {

        return {}

    },

    methods: {

        ...

    },

    beforeCreate() {},

    created() {},

    beforeMount() {},

    mounted() {},

    beforeUpdate() {},

    updated() {},

    beforeDestroy() {},

    destroyed() {}

}
复制代码

// vue3.0

import {

    onBeforeMount,

    onMounted,

    onBeforeUpdate,

    onUpdated,

    onBeforeUnmount,

    onUnmounted

} from 'vue'

 

export default {

    setup() {

        onBeforeMount(() => {

            console.log('component is onBeforeMount')

        })

        onMounted(() => {

            console.log('component is onMounted')

        })

        onBeforeUpdate(() => {

            console.log('component is onBeforeUpdate')

        })

        onUpdated(() => {

            console.log('component is onUpdated')

        })

        onBeforeUnmount(() => {

            console.log('component is onBeforeUnmount')

        })

        onUnmounted(() => {

            console.log('component is onUnmounted')

        })

    }

}
复制代码

响应式数据对象相关方法:reactive方法、ref方法、toRef等方法,数据的只读属性,计算属性,方法的拆解,watchEffect,useStore 等相关方法不在此一一详解。

四、Fragment 片段、Teleport、Suspense

(1)Fragment 片段

· Vue2.0中,vue template只允许有一个根节点。

· Vue3.0中,vue template支持多个根节点。

// vue2.0

<template>

    <div>

        <Headers></Headers>

        <Main></Main>

        <Footer></Footer>

    </div>

</template>
复制代码

// Vue3.0

<template>

    <Headers></Headers>

    <Main></Main>

    <Footer></Footer>

</template>
复制代码

(2)Teleport

Teleport 是一种能够将我们的模板渲染至指定 DOM 节点,不受父级 style 、v-show 等属性影响,但 data、prop 数据依旧能够共用的技术;类似于 React 的 Portal。

(3)Suspense

<Suspense>是一个特殊的组件,它将呈现回退内容,而不是对于的组件,直到满足条件为止,这种情况通常是组件 setup 功能中,发生的异步操作或者是异步组件中使用。

使用场景:父组件展示的内容包含异步的子组件,异步的子组件需要一定的时间才可以加载并展示,这时就需要一个组件处理一些占位逻辑或者加载异常逻辑。

// vue2.x

<template>
  <div>
    <div v-if="!loading">
      ...
    </div>
    <div v-if="loading">Loading...</div>
  </div>
</template>
复制代码

// vue3.x

<Suspense>
  <template >
    <Suspended-component />
  </template>
  <template #fallback>
    Loading...
  </template>
</Suspense>
复制代码

五、自定义渲染 API

自定义渲染 API 将 Virtual DOM(虚拟DOM)和平台相关的渲染分离。

通过 createRendererAPI 我们可以自定义 Virtual DOM 渲染到某一平台中时的所有操作,比如新增、修改、删除一个“元素”,我们可以在这些方法中替换或修改为我们自定义的逻辑,从而打造一个我们自定义的渲染器。

利用这个 API,在 Vue3.0 中我们可以自由方便地去构建 Web(浏览器)平台或非 Web 平台的自定义渲染器。

在 Vue 中是利用 runtime-dom 方法提供的一个上层的抽象层,它帮我们完成了 Virtual DOM 渲染到 Web DOM 中的复杂浏览器接口编程操作。

通过编写自定义渲染器,极大丰富了 Vue 的使用场景。

六、源码优化

(1)使用 monorepo 来管理源码

· Vue.js 2.x 的源码托管在 src 目录,然后依据功能拆分 出了 compiler(模板编译的相关代码)、core(与平台无关的通用运行时代码)、platforms(平台专有代码)、server(服务端渲染的相关代码)、sfc(.vue 单文件解析相关代码)、shared(共享工具代码)等目录。

· Vue.js 3.0,整个源码是通过 monorepo 的方式维护的,根据功能将不同的模块拆分到 packages 目录下面不同的子目录中,每个 package 有各自的 API、类型定义和测试。

(2)使用 Typescript 来开发源码

· Vue.js 2.x 选用 Flow 做类型检查,来避免一些因类型问题导致的错误,但是 Flow 对于一些复杂场景类型的检查,支持得并不好。

· Vue.js 3.0 抛弃了 Flow ,使用 TypeScript 重构了整个项目。TypeScript 提供了更好的类型检查,能支持复杂的类型推导;由于源码就使用 TypeScript 编写,也省去了单独维护 d.ts 文件的麻烦。

Vue3 面试题总结

Vue3.0 源码分析

· Vue.js 3.0 响应式系统的实现原理?

· 响应式是惰性的

vue3 新增 Composition API

在Vue3中,定义 methodswatchcomputeddata数据 等都放在了 setup() 函数中

setup()函数会在created()生命周期之前执行。

执行顺序为:beforeCreate > setup > created

setup() 函数的第一个参数是 props,组件接收的 props 数据可以在 setup() 函数内访问到。

setup(props) {
    console.log(props.p1)
}
复制代码

context 是 setup() 的第二个参数,它是一个上下文对象,可以通过 context 来访问Vue的实例 this 。注意:在 setup() 函数中访问不到Vue的 this 实例

setup(props, context) {
    console.log(this)
    console.log(context)
}
复制代码

reactive() 函数接收一个普通的对象,返回出一个响应式对象。在Vue2.x的版本中,我们只需在 data() 中定义一个数据就能将它变为响应式数据,在 Vue3.0 中,需要用 reactive 函数或者 ref 来创建响应式数据。

用reactive创建响应式对象

// 在组件库中引入reactive
import { reactive } from '@vue/composition-api'

setup() {
    // 创建响应式对象
    const state = reactive({
        count:0
    });

    // 将响应式对象return出去,暴露给模板使用
    return state;
}

// 使用响应式对象

<p>当前的count的值为:{{count}}</p>
<button @click="count++">点击增加count</button>

复制代码

用 ref 创建响应式对象

ref() 函数可以根据给定的值来创建一个响应式的数据对象,返回值是一个对象,且只包含一个 .value 属性。

// 引入 ref
import { ref } from '@vue/composition-api'

setup() {
    // 创建响应式对象
    const count = ref(0);

    return {
        count
    }
}

// 使用响应式对象

<p>当前的count的值为:{{count}}</p>
<button @click="count++">点击增加count</button>

复制代码

ref 的注意事项:

在 setup() 函数内,由 ref() 创建的响应式数据返回的是对象,所以需要用 .value 来访问;

而在 setup() 函数外部则不需要 .value ,直接访问即可。

可以在 reactive 对象中访问 ref() 函数创建的响应式数据。

新的 ref() 会覆盖旧的 ref() 。

3. computed()

computed() 用来创建计算属性,返回值是一个 ref() 实例。按照惯例,使用前需要先引入。

  • computed创建只读计算属性

    给 computed() 传入一个函数,可以得到一个只读的计算属性:

const count = ref(1)

// 创建一个计算属性,使其值比 count 大 1
const bigCount = computed(() => count.value + 1)

console.log(bigCount.value) // 输出 2
bigCount.value++ // error 不可写
复制代码
  • computed创建可读可写计算属性
const count = ref(1)

// 创建一个 computed 计算属性,传入一个对象
const bigCount = computed({
    // 取值函数
    get: () => (count.value + 1),
    // 赋值函数
    set: val => {
      count.value = val - 1
    }
})

// 给计算属性赋值的操作,会触发 set 函数
bigCount.value = 9
// 触发 set 函数后,count 的值会被更新
console.log(count.value) // 8

复制代码

4. readonly()

传入一个响应式对象、普通对象或 ref ,返回一个只读的对象代理。这个代理是深层次的,对象内部的数据也是只读的。

const state = reactive({ count: 0 })

const copy = readonly(state)

watchEffect(() => {
  // 依赖追踪
  console.log(copy.count)
})

// state 上的修改会触发 copy 上的侦听
state.count++

// 这里只读属性不能被修改
copy.count++ // warning!

复制代码

5. watchEffect()

watchEffect() 会立即执行传入的函数,并响应式侦听其依赖,并在其依赖变更时重新运行该函数。

基本用法:

const count = ref(0)

// 初次直接执行,打印出 0
watchEffect(() => console.log(count.value))

setTimeout(() => {
  // 被侦听的数据发生变化,触发函数打印出 1
  count.value++
}, 1000)

复制代码

停止侦听:

watchEffect() 使用时返回一个函数,当执行这个返回的函数时,就停止侦听。

const stop = watchEffect(() => {
  /* ... */
})

// 停止侦听
stop()

复制代码

6. watch()

composition-api 中的 watch 和 Vue2.x 中是一样的,watch 需要侦听数据,并执行它的侦听回调。默认情况下初次渲染不执行。

watch 与 watchEffect 的不同

watch 初次渲染不执行

watch 侦听的更具体

watch 可以访问侦听数据变化前后的值

watch 侦听单个数据源

侦听的数据可以是个 reactive 创建出的响应式数据(拥有返回值的 getter 函数),也可以是个 ref

// 侦听一个 getter
const state = reactive({ count: 0 })
watch(
  () => state.count,
  (count, prevCount) => {
    /* ... */
  }
)

// 直接侦听一个 ref
const count = ref(0)
watch(count, (count, prevCount) => {
  /* ... */
})

复制代码

watch 侦听多个数据源

在侦听多个数据源时,把参数以数组的形式给 watch

watch([ref1, ref2], ([newRef1, newRef2],   [prevRef1, prevRef2]) => {
  /* ... */
})

复制代码

isRef()

isRef() 顾名思义,是用来判断某个值是否为 ref() 创建出来的响应式的值。

当你需要展开某个可能为 ref() 创建的响应式的值的时候,会用到它:

import { isRef } from '@vue/composition-api'

const unwrapper = isRef(foo) ? foo.value : foo
复制代码

toRefs()

toRefs() 可以将 reactive() 创建出来的响应式对象转换成内容为 ref 响应式的值的普通对象

在搞清楚 toRefs() 的用法之前,我们需要先了解一下用 reactive() 和 ref() 创建出来的响应式对象的区别:

用 reactive() 创建的响应式对象,整个对象是响应式的,而对象里的每一项都是普通的值。当你把它用展开运算符展开后,整个对象的普通值都不是响应式的;

而用 ref() 创建的响应式的值,本身就是响应式的,并不依赖于其他对象。

所以需要展开 reactive() 创建的响应式对象,又不想让他们失去响应式特点的时候,就需要用 toRefs() 将它进行转换:

import { toRefs } from '@vue/composition-api'

setup() {
    // 定义响应式数据对象
    const state = reactive({
        count: 0
    })

    // 定义简单的函数,使count每次+1
    const add = () => {
        state.count++
    }

    // 将setup函数的内容return出去,供外界使用
    return {
        // 将 state 展开导出,同时将其属性都转化为 ref 形式的响应式数据
        ...toRefs(state),
        add
    }
}


<template>
    <div>
        <p>当前的count值为:{{count}}</p>
        <button @click="add">点击+1</button>
    </div>
</template>

复制代码

Vue 3.0 所采用的 Composition Api 与 Vue 2.x使用的 Options Api 有什么区别?

· vue3 新增内置组件 Teleport

· Props 初始化和更新流程改进

· Vue3 Slot内容分发

· Vue 3.0 在编译方面有哪些优化?

· Vue3 依赖注入子孙组件如何共享数据

· Vue3 侦听器实现原理与使用场景

· Vue3 组件实现原理核心源码解读

· Vuex 数据流管理方案

· 原生服务端渲染(SSR)的实现、同构开发

· Nuxt.js 集成式 SSR 框架

vue3.0实战应用

1.说说 Vue2.0 和 Vue3.0 有什么区别

2.Vue3 的新特性

3.基于 vite/webpack

4.实现 Vue3 工程化部署

5.掌握 setup 和 10 种响应式系统 API

6.掌握新生命周期函数

7.模板 refs 的使用

当使用组合式API时,reactive refs 和 template refs的概念是统一的。为了获得对模板内元素或组件实例的引用,我们可以像往常一样在setup()中声明一个ref并返回它:

<template>
  <div class="home" ref='root'>
    我是hme元素
  </div>
</template>

<script>
  import {ref, onMounted} from 'vue';
  export default {
    setup(){
      const root = ref(null);
      onMounted(()=>{
        console.log(root.value)    // <div class="home"> 我是hme元素 </div>
      })

      return {
        root,
      }
    }
  }
</script>
复制代码

1.1 配合render函数

<template>
  <div></div>
</template>

<script>
  import { ref, h } from 'vue';
  export default {
    setup() {
      const root = ref(null);
      return () => h('div', {
        ref: root,
        style: "width:100px;height:100px;background:red;"
      })
    }
  }
</script>
复制代码

1.2 使用JSX语法

<template>
  <div></div>
</template>

<script>
  import { ref } from 'vue';
  export default {
    setup() {
      const root = ref(null);
      return ()=><div ref = {root} style="width:100px;height:100px;background:blue;"/>
    }
  }
</script>
复制代码

1.3 ref在v-for中的使用

模板ref在v-for中使用vue,没有做特殊处理,需要使用函数型的ref来自定义处理方式:

<template>
  <ul>
    <li v-for="(item, i) in lists" :ref="el => { divs[i] = el }" :key="i">
      {{item}}
    </li>
  </ul>
</template>

<script>
  import {ref, reactive, onBeforeUpdate} from 'vue';
  export default {
    setup(){
      const lists = reactive([1,2,3]);
      const divs = ref([]);
      // 确保在每次变更之前重置引用
      onBeforeUpdate(()=>{
        divs.value = [];
      })
      return {
        lists,
        divs
      }
    }
  }
</script>
复制代码

8.Vue3 中的响应式系统和 dom-diff


作者:yuqifang
链接:https://juejin.cn/post/7028372909596885005
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值