vue3.2的特性

5 篇文章 0 订阅

vue3.2的特性

全局方法

因为vue3不再有Vue构造函数,所以一些全局的自定义属性或者方法也没法通过Vue.prototype.xx挂在到vm实例上,这时候需要借助app.config.globalProperties来实现这一功能

//如果项目采用了typescript,那么就需要扩展一下`@vue/runtime-core`模块,否则在使用的时候会报找不到$http属性
//在main.ts加上如下代码
declare module '@vue/runtime-core' {
  interface ComponentCustomProperties {
    $http: any;
  }
}
定义全局方法
import { createApp } from 'vue'
const app = createApp(App)
app.config.globalProperties.$http = () => {
 //do something
}

使用全局方法
import { getCurrentInstance } from 'vue'
// getCurrentInstance必须在.vue文件中才获取得到
const { proxy } = getCurrentInstance() as any
console.log('proxy', proxy.$http )

环境变量

## vue2.X
process.env.VUE_APP_BASE_API
## vue3.2
const href = import.meta.env.VITE_APP_CONSOLE_URL + path

style v-bind

// 定义颜色变量
let stateStyle = reactive({
  color: 'red',
  fontSize: '20px'
})

function changeColor() {
  stateStyle.color = 'blue'
  stateStyle.fontSize = '30px'
}

<style scoped>
.color {
  color: v-bind('stateStyle.color');
  font-size: v-bind('stateStyle.fontSize');
}
</style>

响应式侦听

computed是多对一的关系,而watch则是一对多的关系;vue3也提供了两个函数来侦听数据源的变化:watch和watchEffect。

watch的使用

监听单个的ref
const  a = ref(0)
  watch(a,(newval,oldval)=>{
   console.log(newval,oldval)
  })
监听多个ref
const  b = ref(0)
  watch([a,b],(newval,oldval)=>{
   console.log(newval,oldval)
  })
监听整个reactive
const data = reactive({
   a:1,
   b:2,
   c:3
  })
  watch(data,(newVal,oldVal)=>{
   console.log(newVal,oldVal)
  })
  
监听reactive下的属性
  watch([()=>data.a,()=>data.b],(newVal,oldVal)=>{
   console.log(newVal,oldVal)
  })
侦听getter函数
watch(
  () => state.count,
  (count, prevCount) => {
    // 1 0
    console.log(count, prevCount);
  }
);
侦听深度嵌套的对象属性

侦听深度嵌套的对象属性变化时,需要设置deep:true

注意⚠️

侦听响应式对象始终返回该对象的引用,所以打印结果发现都是改变后的值。

解决:对侦探的对象进行深拷贝

import _ from "lodash";
const deepObj = reactive({
  a: {
    b: {
      c: "hello",
    },
  },
});

watch(
  () => _.cloneDeep(deepObj),
  (val, old) => {
    // new hello hello
    console.log(val.a.b.c, old.a.b.c);
  },
  { deep: true }
);

deepObj.a.b.c = "new hello";
停止侦听

一般侦听都会在组件销毁时自动停止,但是有时候我们想在组件销毁前手动的方式进行停止,可以调用watch返回的stop函数进行停止:

const count = ref(0);

const stop = watch(count, (count, prevCount) => {
  // 不执行
  console.log(count, prevCount);
});

setTimeout(()=>{
  count.value = 2;
}, 1000);
// 停止watch
stop();

watchEffect

  • watchEffect不需要手动传入依赖
  • 每次初始化时watchEffect都会执行一次回调函数来自动获取依赖
  • watchEffect无法获取到原值,只能得到变化后的值
import { reactive, ref, watch, watchEffect } from "vue";

const count = ref(0);
const state = reactive({
  year: 2021,
});

watchEffect(() => {
  console.log(count.value);
  console.log(state.year);
});
setInterval(() => {
  count.value++;
  state.year++;
}, 1000);

// 分析: watchEffect会在页面加载时自动执行一次,追踪响应式依赖;在加载后定时器每隔1s执行时,watchEffect都会监听到数据的变化自动执行,每次执行都是获取到变化后的值。

computed

  • computed接收一个函数,该函数的返回值作为计算属性的值。
get函数
  • 需要的值依赖于其他值的状态,computed接受一个getter函数,并为getter返回的值创建了一个不可变的响应式ref对象
const num = ref(0);
const double = computed(() => num.value * 2);
num.value++;
// 2
console.log(double.value);
// Warning: computed value is readonly
double.value = 4
get和set函数

可以使用get和set函数创建一个可读写的ref对象

const num = ref(0);
const double = computed({
  get: () => num.value * 2,
  set: (val) => (num.value = val / 2),
});

num.value++;
// 2
console.log(double.value);

double.value = 8
// 4
console.log(num.value);

案例

<template>
  <div class="test-font">
    <div>num:{{ num }}</div>
    <div>double-num:{{ doubleNum }}</div>
    <button class="test-button" @click="addOne">+1</button>
  </div>
</template>
<script lang="ts">
import { defineComponent, ref, computed } from "vue";
export default defineComponent({
  setup() {
    const num = ref(0);
    const doubleNum = computed(() => {
      return num.value * 2;
    });
    const addOne = () => {
      num.value++;
    };
    return {
      num,
      doubleNum,
      addOne,
    };
  },
});
</script>


响应式API

ref和reactive

reactive主要负责复杂数据结构,而ref主要处理基本数据结构,但ref本身也是能处理对象和数组的:

let book = reactive({
  name: 'Learn Vue',
  year: 2020,
  title: 'Chapter one'
})



// 1.会消除它的响应式
let {
  name,
} = book

name = 'new Learn'
// Learn Vue
console.log(book.name);
 
 
 
// 2.保存响应式
let {
  name,
} = toRefs(book)

// 注意这里解构出来的name是ref对象
// 需要通过value来取值赋值
name.value = 'new Learn'
// new Learn
console.log(book.name);

//3.通过readonly来创建一个只读的对象

const copy = readonly(book);
//Set operation on key "name" failed: target is readonly.
copy.name = "new copy";

Teleport

  • Teleport翻译过来就是传送、远距离传送的意思;它可以将插槽中的元素或者组件传送到页面的其他位置:
  • Teleport一个常见的使用场景,就是在一些嵌套比较深的组件来转移模态框的位置。虽然在逻辑上模态框是属于该组件的,但是在样式和DOM结构上,嵌套层级后较深后不利于进行维护(z-index等问题);因此我们需要将其进行剥离出来:
<template>
  <button @click="showDialog = true">打开模态框</button>

  <teleport to="body">
    <div class="modal" v-if="showDialog" style="position: fixed">
      我是一个模态框
      <button @click="showDialog = false">关闭</button>
      <child-component :msg="msg"></child-component>
    </div>
  </teleport>
</template>
<script>
export default {
  data() {
    return {
      showDialog: false,
      msg: "hello"
    };
  },
};
</script>
//解析: Teleport中的modal div就被传送到了body的底部;虽然在不同的地方进行渲染,但是Teleport中的元素和组件还是可以和父组件进行数据通信。Teleport接收两个参数to和disabled:

to - string:必须是有效的查询选择器或 HTMLElement,可以id或者class选择器等。
disabled - boolean:如果是true表示禁用teleport的功能,其插槽内容将不会移动到任何位置,默认false不禁用

Suspense

作用:suspense在等待异步组件时额外渲染一些内容,使用户拥有更好的体验

定义:是Vue3推出的一个内置组件,它允许我们的程序在等待异步组件时渲染一些后备的内容,可以让我们创建一个平滑的用户体验;Vue中加载异步组件其实在Vue2.x中已经有了,我们用的vue-router中加载的路由组件其实也是一个异步组件

语法关键:提供了两个slot插槽,一个default默认,一个fallback加载中的状态:

// 1.定义

// 全局定义异步组件
//src/main.js
import { defineAsyncComponent } from "vue";
const AsyncButton = defineAsyncComponent(() =>
  import("./components/AsyncButton.vue")
);
app.component("AsyncButton", AsyncButton);


// 组件内定义异步组件
// src/views/Home.vue
import { defineAsyncComponent } from "vue";
export default {
  components: {
    AsyncButton: defineAsyncComponent(() =>
      import("../components/AsyncButton")
    ),
  },
};

// 2.使用-定义异步加载的子组件
export default {
  components: {
    AsyncButton: defineAsyncComponent({
      delay: 100,
      timeout: 3000,
      loader: () => import("../components/AsyncButton"),
      errorComponent: ErrorComponent,
      onError(error, retry, fail, attempts) {
        if (attempts <= 3) {
          retry();
        } else {
          fail();
        }
      },
    }),
  },
};

// 3.使用异步加载的子组建
<template>
  <div>
    <button @click="showButton">展示异步组件</button>
    <template v-if="isShowButton">
      <Suspense>
        <template #default>
          <AsyncButton></AsyncButton>
        </template>
        <template #fallback>
          <div>组件加载中...</div>
        </template>
      </Suspense>
    </template>
  </div>
</template>
<script>
export default {
  setup() {
    const isShowButton = ref(false);
    function showButton() {
      isShowButton.value = true;
    }
    return {
      isShowButton,
      showButton,
    };
  },
}
</script>

Fragment

所谓的Fragment,就是片段;在vue2.x中,要求每个模板必须有一个根节点,在Vue3中我们可以直接不需要根节点

API 改变

sync修饰符

.sync修饰符 => v-model:propName

data、mixin和filter

  • 在Vue3中,data只接受function类型,通过function返回对象;同时Mixin的合并行为也发生了改变,当mixin和基类中data合并时,会执行浅拷贝合并
  • vue3只进行浅层拷贝,对data中数据发现已存在就不合并拷贝。
  • 在vue3中,过滤器filter已经删除,不再支持了,官方建议使用方法调用或者计算属性computed来进行代替。
const Mixin = {
  data() {
    return {
      user: {
        name: 'Jack',
        id: 1,
        address: {
          prov: 2,
          city: 3,
        },
      }
    }
  }
}
const Component = {
  mixins: [Mixin],
  data() {
    return {
      user: {
        id: 2,
        address: {
          prov: 4,
        },
      }
    }
  }
}

// vue2结果:
{
  id: 2,
  name: 'Jack',
  address: {
    prov: 4,
    city: 3
  }
}

// vue3结果:
user: {
  id: 2,
  address: {
    prov: 4,
  },
}

v-for和key

在Vue3中,key值应该被放置在template标签上

bind合并

在vue3中,如果一个元素同时定义了v-bind="object"和一个相同的单独的属性,那么声明绑定的顺序决定了最后的结果(后者覆盖前者)

在vue2.x中,如果一个元素同时定义了v-bind="object"和一个相同的单独的属性,那么这个单独的属性会覆盖object中的绑定

// vue2.x
<div id="red" v-bind="{ id: 'blue' }"></div>
<div v-bind="{ id: 'blue' }" id="red"></div>

<!-- 最后结果都相同 -->
<div id="red"></div>


// vue3.x
<!-- template -->
<div id="red" v-bind="{ id: 'blue' }"></div>
<!-- result -->
<div id="blue"></div>

<!-- template -->
<div v-bind="{ id: 'blue' }" id="red"></div>
<!-- result -->
<div id="red"></div>

for中ref

vue2.x中,在v-for上使用ref属性,通过this.$refs会得到一个数组:

<template
  <div v-for="item in list" :ref="setItemRef"></div>
</template>
<script>
export default {
  data(){
    list: [1, 2]
  },
  mounted () {
    // [div, div]
    console.log(this.$refs.setItemRef) 
  }
}
</script>

vue3不再自动创建数组,而是将ref的处理方式变为了函数,该函数默认传入该节点:

<template
  <div v-for="item in 3" :ref="setItemRef"></div>
</template>
<script>
import { reactive, onUpdated } from 'vue'
export default {
  setup() {
    let itemRefs = reactive([])

    const setItemRef = el => {
      itemRefs.push(el)
    }

    onUpdated(() => {
      console.log(itemRefs)
    })

    return {
      itemRefs,
      setItemRef
    }
  }
}
</script>

v-for和v-if优先级

在vue3中,v-if比v-for有更高的优先级。因此下面的代码,在vue2.x中能正常运行,但是在vue3中v-if生效时并没有item变量,因此会报错:

<template>
  <div v-for="item in list" v-if="item % 2 === 0" :key="item">{{ item }}</div>
</template>

<script>
export default {
  data() {
    return {
      list: [1, 2, 3, 4, 5],
    };
  },
};
</script>

参考

官方文档——内置指令

官方文档——内置组件

官方文档——SFC 语法定义

官方文档——单文件组件 CSS 功能

官方文档——响应式 API:核心

官方文档——响应式 API:工具函数

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值