Vue3 新特性初体验

1,684 阅读3分钟

Vue3 新特性

1、初始化开发环境

// 安装 / 更新 vue-cli (保证版本在4.5.0以上)
npm install -g @vue/cli

// 创建
vue create vue3-basic

// 然后选择 Manually select features (因为我们需要使用TS),进入界面后选择TS(按空格选择)

// 然后选择Vue3, 后面都保持默认和选择 No

2、ref 的使用

在 vue3 的 script 中不再使用 datamethods ,而是使用 setup() 方法

refcomputed 等都是属于 composition API

<template>
  <img alt="Vue logo" src="./assets/logo.png">
  <div>{{count}}</div>    <!-- 此处直接写 count 就能获取到值 -->
  <div>{{double}}</div>
  <button @click="increase">点击</button>
</template>

<script lang="ts">
import { ref, computed } from "vue";

export default ({
  
  name: 'App',
  setup() {
    const count = ref(0)    // ref 接受一个参数,返回的是一个响应式对象
    const double = computed(() => {
      return count.value * 2
    })
    const increase = () => {
      count.value ++    // count是对象,这样才能获取到值
    }
    return {
      count,
      double,
      increase
    }
  }

});
</script>

3、reactive 的使用

reactive 可以将需要导出的数据都包裹在一个对象中,而不是单独存在

<template>
  <img alt="Vue logo" src="./assets/logo.png">
  <div>{{count}}</div>    <!-- 此处直接写 count 就能获取到值 -->
  <div>{{double}}</div>
  <button @click="increase">点击</button>
</template>

<script lang="ts">
import { computed, reactive, toRefs } from "vue";

interface DataProps {   // 此处是为了解决 data 对象显示为 any 类型报错
  count: number;
  increase: () => void;
  double: number;
}

export default ({
  
  name: 'App',
  setup() {
    const data: DataProps = reactive({    // reactive 是一个方法,接受一个对象
      count: 0,
      increase: () => {
        data.count ++
      },
      double: computed(() => {
        return data.count * 2
      })
    })

    // 如果仅仅将 data 进行展开会丧失响应式,所以用 toRefs把每一项都转化为响应式对象
    const refData = toRefs(data)    

    return {
      ...refData    // 展开之后再模板中就不用写 data. 的前缀了
    }
  }

});
</script>

同时, vue3 还支持直接新增 data 中的数据以及修改 data 中数组和对象的每一项,这样的修改也是响应式的。

4、生命周期钩子

在旧的 beforeCreate 钩子函数之后和 created 的钩子函数之前立即调用 setup 方法。因此,我们不再需要这两个钩子,我们可以简单地将本应有的代码放在 setup() 方法中。

此外,还有 9 个旧的生命周期函数可以在 setup() 中使用,使用前必须先导入

  • onBeforeMount
  • onMounted
  • onBeforeUpdate
  • onUpdated
  • onBeforeUnmount
  • onUnmounted
  • onActivated
  • onDeactivated
  • onErrorCaptured
// 例子

import { onMounted, onUpdated } from 'vue'

export default {
  setup() {
    onMounted(() => {
      // ... 
    })
    onUpdated(() => {
      // ... 
    })
  }
}

5、 Watch 侦听变化

import { watch } from 'vue'

export default {
  setup() {
    // 1. 当监听的是 ref 返回的响应式对象
    watch(data, (newValue, oldValue) => {
		// ...
	})
      
    // 2. 当监听的是多个数据
    watch([data1, data2], (newValue, oldValue) => {
		// ...
	})
      
      // 3. 当监听的是多个对象,且包含 reactive 对象内的数据时
    watch([data1, () => data.data2], (newValue, oldValue) => {
		// ...
	})
  }
}

6、模块化

以下是两个例子:

鼠标追踪器

由于将代码都写入 setup() 会使代码过于冗余和复杂,故对多次需要使用的代码实现模块化

src / hooks / useMousePosition.ts

import { ref, onMounted, onUnmounted } from 'vue'

function useMousePosition() {
  const x = ref(0)
  const y = ref(0)

  const updateMouse = (e: MouseEvent) => {
    x.value = e.pageX
    y.value = e.pageY
  }

  onMounted(() => {
    document.addEventListener('click', updateMouse)
  })

  onUnmounted(() => {
    document.removeEventListener('click', updateMouse)
  })

  return { x, y }
}

export default useMousePosition;

使用

import { useMousePosition } from './hooks/useMousePosition.ts'

export default {
  setup() {
	const { x, y } = useMousePosition()
    return {
    	x,
    	y
    }
  }
}

useURLLoader

import { reactive, toRefs } from 'vue'
import axios from 'axios'

function useURLLoader(url: string) {
  const data = reactive({
    result: null,
    loadState: true,
    errInfo: null
  })

  axios.get(url).then((res) => {
    data.loadState = false
    data.result = res.data.message
  }).catch(e => {
    data.loadState = false
    data.errInfo = e
  })

  
  const refData = toRefs(data)
  return {
    ...refData
  }
}

export default useURLLoader

7、defineComponent 的使用

使用 defineComponent 能够让传入的对象获得类型以及能够获得自动提示

<script lang="ts">
import { defineComponent } from 'vue';

export default defineComponent({
  name: 'HelloWorld',
  props: {
    msg: String,
  },
  setup(props, context) {
	...
  }
});
</script>

8、Teleport (瞬间移动) 组件的使用

有时在组件中需要用到弹窗时,将弹窗的代码全部写到层层嵌套的组件中,不太符合逻辑也不太方便查看。使用Teleport 能将组件的内容挂在到其他组件上

// 首先需要在父组件中引入该组件

// 使用
<template>
  <teleport to="#model">	<!-- 挂载到id为 model 的节点上 -->
      <div>
        this is a model
      </div>
  </teleport>
</template>

// 挂载
// 在index.html中
<div id="model"></div>

9、 emits向父组件触发事件

// 例子
<template>
  <teleport to="#model">
    <div v-if="isOpen">
      <div>this is a model</div>
      <button @click="buttonClick">关闭</button>
    </div>
  </teleport>
</template>

<script>
import { defineComponent } from "vue";

export default defineComponent({
  name: "Model",
  props: {
    isOpen: Boolean,
  },
  emits: {
    "close-modal": (payload: any) => {    // 进行类型的校验,没有就写 null
      return payload.type === "close";
    },
  },
  setup(props, context) {
    const buttonClick = () => {
      context.emit("close-modal", {
        type: "hello",
      });
    };
    return {
      buttonClick
    }
  },
});
</script>

10、 Suspense 组件

  • 解决异步请求的困境
  • Suspense是Vue3推出的一个内置的特殊组件
  • 如果使用Suspense,要返回一个promise

使用Promise:

<template>
  <div>{{result}}</div>		<!--返回后的结果可以直接使用 -->
</template>

<script>
import {defineComponent} from 'vue'

export default defineComponent({
  setup() {
    return new Promise((resolve) => {		// 返回的必须是Promise
      setTimeout(() => {
        return resolve({
          result: 10
        })
      },1000)
    })
  }
})
</script>


<!--在父组件中使用该组件 -->
<Suspense>
  <template #default>
    <AsyncTest />
  </template>
    <!--当还没有请求到结果时显示 -->
  <template #fallback>
    <h1>loading!!!</h1>
  </template>
</Suspense>

使用 async:

<template>
  <div>
    <img :src="result && result.message" alt="123" />
  </div>
</template>

<script>
import { defineComponent } from "vue";
import axios from "axios";

export default defineComponent({
  async setup() {
    const rawData = await axios.get("https://www.example.com/"); // 测试使用
    return {
      result: rawData.data,
    };
  },
});
</script>

<!--在父组件中使用该组件同上面的 Promise -->

可以使用 onErrorCaptured() 生命周期函数监听网络请求的错误