浅谈Vue3

一说到vue3,大家可能第一想到的就是他的响应式原理以及compositionApi了,今天小编来浅谈一下这两部分以及vue3的一些更改。

响应式

vue3中实现响应的方法是使用es6引入Proxy,俗称代理。

那么问题来了,为什么要更换响应式的实现方式呢?

首先小编先说说vue2响应式的痛点 

  1. Vue2 将遍历此data所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setter。当data对象很复杂,层级很多时无疑是会影响到性能的。
  2. 对于对象:Vue2 无法检测 property 的添加或移除。由于 Vue 会在初始化实例时对 property 执行 getter/setter 转化,所以 property 必须在 data 对象上存在才能让 Vue 将它转换为响应式的
  3. 对于数组:Vue 2不能检测以下数组的变动:
    1. 当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue
    2. 当你修改数组的长度时,例如:vm.items.length = newLength

再来说说使用proxy的好处

  1. 首先proxy他是代理的整个对象
  2. Proxy 可以直接监听数组的变化;
  3. Proxy 有多达 13 种拦截方法,不限于 apply、ownKeys、deleteProperty、has 等等是Object.defineProperty 不具备的;

CompositionApi(组合式api)

为什么会产生组合式api呢?

当你在参与vue项目的编写的时候,有没有遇到一个组件里拥有大量的逻辑,比如说既有展示列表,还有搜索过滤,点赞,收藏等逻辑,然后你会发现这个组件异常的庞大,并且当你想去找某个逻辑的代码时,就会变得很麻烦,这就使得组件变得难以理解及维护。

什么是组合式api呢?

组合式api就是将所有同一逻辑的代码集合在一块

官方案例

怎么写组合式api呢?

setup函数便是组合式api的入口


interface Data {
  [key: string]: unknown
};

interface SetupContext {
  attrs: Data // 属性
  slots: Slots // 插槽
  emit: (event: string, ...args: unknown[]) => void // 事件
  expose: (exposed?: Record<string, any>) => void // 暴露出去的属性或方法 即可以被ref调用
}
/**
* @params props: 组件接收的参数
* @params content: 上下文
* return 将返回值暴露给模板
*/
setup(props:Data,context:SetupContext):Data

注意:

  1. setup 中没有this。
  2. props 是响应式的,当传入新的 props 时,它将被更新。但是,因为 props 是响应式的,你不能使用 ES6 解构,它会消除 prop 的响应性,但context只是个普通js对象。
  3. 如果data,props,setup都有一个同名属性,setup返回的该属性优先级最高
  4. 如果非要解构props,可以使用toRefs(只会将对象的第一层属性转化为ref对象)|toRef 
    import { toRefs,toRef } from 'vue'
     
    setup(props) {
      const { obj } = toRefs(props);
      console.log(obj.value);
    // 当obj可能不存在props上时,这时toRefs 将不会为 obj创建一个 ref ,
    // 你应该使用toRef代替他
       const { obj } = toRef(props)
        return{
        obj
        }
    }

如何在setup中创建响应式对象?

使用ref reactive 

import {ref,reactive} from 'vue'
export default {
  setup() {
    const countRef = ref({count:0});
    const countReactive = reactive({count:0});
    console.log(countRef); // {__v_isShallow: false, dep: undefined, __v_isRef: true, _rawValue: 0, _value: {count:0}}
    console.log(countReactive); //{count:0}
    return {
        countRef,
        countReactive,
    }
  }
}

区别 :

  1. reactive 接受入参必须是对象形式,而 ref 可以是对象形式,也可以是一个单值。
  2. reactive 创建的响应式对象会自动解包(即不要调用.value)

如何在setup中使用生命周期钩子?

你可以通过在生命周期钩子前面加上 “on” 来访问组件的生命周期钩子。

下表包含如何在 setup () 内部调用生命周期钩子:

选项式 APIHook inside setup
beforeCreateNot needed*
createdNot needed*
beforeMountonBeforeMount
mountedonMounted
beforeUpdateonBeforeUpdate
updatedonUpdated
beforeUnmountonBeforeUnmount
unmountedonUnmounted
errorCapturedonErrorCaptured
renderTrackedonRenderTracked
renderTriggeredonRenderTriggered
activatedonActivated
deactivatedonDeactivated

 组合式api生命周期钩子接收一个回调函数,当钩子触发时,调用回调函数

import { onMounted } from 'vue';
export default {
  setup() {
    // mounted
    onMounted(() => {
      console.log('Component is mounted!')
    })
  }
}

在setup 中使用 watch

<script setup>
import { ref, watch,reactive } from 'vue'
/*
    watch 接收三个参数
    第一个参数:一个想要侦听的响应式引用或 getter 函数
    第二个参数:一个回调
    第三个参数:可选的配置选项
*/
const counter = ref(0)
// 直接侦听ref
watch(counter, (newValue, oldValue) => {
  console.log('The new counter value is: ' + counter.value)
})

const state = reactive({ count: 0 })
// 侦听get函数
watch(
  () => state.count,
  (count, prevCount) => {
    /* ... */
  }
)

// 侦听多个数据源
const firstName = ref('')
const lastName = ref('')

watch([firstName, lastName], (newValues, prevValues) => {
  console.log(newValues, prevValues)
})
/**
* 尽管如此,如果你在同一个函数里同时改变这些被侦听的来源,侦听器仍只会执行一次:
* 通过更改设置 flush: 'sync',我们可以为每个更改都强制触发侦听器
*/
</script>

在setup中使用计算属性

<script setup>
import { ref, computed } from 'vue'

const counter = ref(0)
const twiceTheCounter = computed(() => counter.value * 2)

counter.value++
console.log(counter.value) // 1
console.log(twiceTheCounter.value) // 2
</script>

相较于vue2其他的改动

改动有很多,小编只讲一些,想了解更多的小伙伴可以去官网查看

  1.  destroyed ,beforeDestroy 生命周期选项被重命名为 unmounted,beforeUnmount
  2. Vue 3 现在正式支持了多根节点的组件,也就是片段!但是,这要求开发者显式定义 attribute 应该分布在哪里
    <template>
      <header>...</header>
      <main v-bind="$attrs">...</main>
      <footer>...</footer>
    </template>
    一个非 prop 的 attribute 是指传向一个组件,但是该组件并没有相应 props 或 emits 定义的 attribute。常见的示例包括 classstyle 和 id attribute。可以通过 $attrs property 访问那些 attribute。 
    /**
    * 当组件返回单个根节点时,根节点默认继承非props属性
    */
    app.component('date-picker', {
      // inheritAttrs: false, // 当inheritAttrs为false时,根节点将不再继承非props属性
      template: `
        <div class="date-picker">
          <input type="datetime-local" />
        </div>
      `
    })
    
    <!-- 具有非 prop 的 attribute 的 date-picker 组件-->
    <date-picker data-status="activated"></date-picker>
    
    <!-- 渲染后的 date-picker 组件 -->
    <div class="date-picker" data-status="activated">
      <input type="datetime-local" />
    </div>
    /**
    * 当组件返回多个根节点时,根节点不再自动继承非props属性,如果未显式绑定 $attrs,将发出运行时
    * 警告。
    */
    // 这将发出警告
    app.component('custom-layout', {
      template: `
        <header>...</header>
        <main>...</main>
        <footer>...</footer>
      `
    })
    
    // 没有警告,$attrs 被传递到 <main> 元素
    app.component('custom-layout', {
      template: `
        <header>...</header>
        <main v-bind="$attrs">...</main>
        <footer>...</footer>
      `
    })

    了解更多非props继承

  3. 新增teleport,用将内容渲染到指定元素里面 to 指定渲染到哪个元素里
    <div class="box">
        <teleport to="body">
          <div>A</div>
        </teleport>
      </div>
      <div id="container"></div>
      <teleport to="#container">
        <div>B</div>
      </teleport>
      <teleport to="#container">
        <div>C</div>
      </teleport>

    当同时指定一个元素时,会按先后顺序依次添加在目标元素里面

  4. 组件可以使用多个v-model
    <user-name
      v-model:first-name="firstName"
      v-model:last-name="lastName"
    ></user-name>
    app.component('user-name', {
      props: {
        firstName: String,
        lastName: String
      },
      emits: ['update:firstName', 'update:lastName'],
      template: `
        <input 
          type="text"
          :value="firstName"
          @input="$emit('update:firstName', $event.target.value)">
    
        <input
          type="text"
          :value="lastName"
          @input="$emit('update:lastName', $event.target.value)">
      `
    })

  5. ​​​​​​ 单文件组件状态驱动的 CSS 变量 (<style> 中的 v-bid)
    // 单文件组件的 <style> 标签可以通过 v-bind 这一 CSS 函数将 CSS 的值关联到动态的组件状态上:
    
    <template>
      <div class="text">hello</div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          color: 'red'
        }
      }
    }
    </script>
    
    <style>
    .text {
      color: v-bind(color);
    }
    </style>
    
    // 这个语法同样也适用于 <script setup>,且支持 JavaScript 表达式 (需要用引号包裹起来)
    <script setup>
    const theme = {
      color: 'red'
    }
    </script>
    
    <template>
      <p>hello</p>
    </template>
    
    <style scoped>
    p {
      color: v-bind('theme.color');
    }
    </style>
    单文件组件状态驱动的 CSS 变量 (<style> 中的 v-bid)
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值