vue3.0新增与变动

1 篇文章 0 订阅

新增

  1. setup:setup是新增的用于封装抽离代码的一个函数,他在实例组件渲染前执行,这个时候不能使用实例上的属性和方法
  2. setup生命周期:生命周期函数以on开头,不包含created之前的钩子,distroyed也变成了onUnmounted,新增调试钩子onRenderTracked, onRenderTriggered
  3. ref用于生成响应式数据,然后使用 ref返回值.value在setup中获取响应式数据
  4. 喜欢用单例模式开发项目同样也可以使用reactive生成响应式数据,如果需要重新解构,或者重新赋值的话,可以使用toRefs方法将数据响应化
  5. computed与watch无太大变化,知识形式长需要使用函数初始化,在回调用进行功能构建
<template>
  <h1>{{ num }} | {{ doubleNum }}</h1>
  <button @click="add">自增</button>
</template>

<script lang="ts">
<template>
  <h1>{{ num }} | {{ doubleNum }}</h1>
  <button @click="add">自增</button>
</template>

<script lang="ts">
import { ref, computed, reactive, toRefs, onMounted, onUpdated, onUnmounted, onRenderTracked, onRenderTriggered, watch } from 'vue'
interface DataProps {
  num: number
  add: () => void
  doubleNum: number
}
export default {
  setup() {
    // const num = ref(0)
    // const info = ref({name:'jack',age:19})
    // const doubleNum = computed(() => num.value * 2)
    // const add = () => { num.value++ }
    // return {
    //   num,
    //   doubleNum,
    //   add
    // }

    onMounted(() => {
      console.log('mounted')
    })
    onUpdated(() => {
      console.log('update')
    })
    onUnmounted(() => {
      console.log('destroyed')
    })
    onRenderTracked((e) => {
      console.log('onRenderTracked', e)
    })
    onRenderTriggered((e) => {
      console.log('onRenderTriggered', e)
    })
    const data: DataProps = reactive({
      num: 1,
      add: () => {
        data.num++
      },
      doubleNum: computed(() => data.num * 2),
    })
    let refData = toRefs(data)
    watch([() => data.num], (n, o) => {
      console.log(n, o)
    })
    return {
      ...refData,
    }
  },
}
</script>

使用

  1. 下例是vue3集合typescript实际中的使用,defineComponent函数的作用在于生成自定义组件时能够获得良好的代码提示
  2. /hooks/useLoading 文件是一个简单的获取点击事件的位置的抽离
  3. /hooks/useLoading文件是vue结合typescript的一个小案例,由于ts预先声明的null没有任何属性,同时我们也无法获取到接口的返回值信息,所以需要手动的设置泛型来约束result的返回值
-->test.vue
<template>
  {{ x }}:{{ y }}
  <div v-if="loading">loading....</div>
  <div v-if="loaded">
    <img :src="result[0].url" alt="" />
  </div>
</template>


<script lang="ts">
import { watch, defineComponent } from 'vue'
import useMouseUpdate from '../hooks/useMouseUpdate'
import useLoading from '../hooks/useLoading'

interface DogResult {
  message: string
  status: string
}
interface CatResult {
  id: string
  url: string
  width: number
  height: number
}

export default {
  setup() {
    // let { result, loading, loaded } = useLoading<DogResult>('https://dog.ceo/api/breeds/image/random')
    let { result, loading, loaded } = useLoading<CatResult[]>('http://api.thecatapi.com/v1/images/search?limit=1')

    let { x, y } = useMouseUpdate()

    watch(result, (n) => {
      if (result.value) {
        console.log(result.value[0].url)
      }
    })

    return {
      x,
      y,
      result,
      loading,
      loaded,
    }
  },
}
</script>

-->/hooks/useMouseUpdate
import { onMounted, onUnmounted, reactive, toRefs } from "vue";
function useMouseUpdate() {
  const data = reactive({
    x: 0,
    y: 0
  });
  function updateMouseEvent(e: MouseEvent) {
    data.x = e.pageX;
    data.y = e.pageY;
  }
  onMounted(() => {
    document.addEventListener("click", updateMouseEvent);
  });
  onUnmounted(() => {
    document.removeEventListener("click", updateMouseEvent);
  });
  const refData = toRefs(data);
  return {
    ...refData
  };
}
export default useMouseUpdate;

-->/hooks/useLoading
import axios from "axios";
import { ref } from "vue";

function useLoading<T>(url: string) {
  let result = ref<T | null>(null);
  let loading = ref(true);
  let loaded = ref(false);
  let error = ref(null);

  axios
    .get(url)
    .then(res => {
      loading.value = false;
      loaded.value = true;
      result.value = res.data;
    })
    .catch(e => {
      loading.value = false;
      error.value = e;
    });
  return {
    result,
    loading,
    loaded,
    error
  };
}
export default useLoading;

新增2

  • teleport传送门,如下列所示如果在index.html中body元素下添加一个id为app的元素,那么下面这个组件就会被渲染到#modal这个元素下方,所以适合用来做模态框
  • Suspense是一个用来做异步等待的内置组件,可以用于做调接口时的异步动画
  • onErrorCaptured可以用来处理异常
-->test.vue
<template>
  {{ error }}
  <Suspense>
    <template #default> <async-temp></async-temp></template>
    <template #fallback>
      <div>loading....</div>
    </template>
  </Suspense>
  <button @click="openModal">打开按钮</button>
  <Modal @handle-close="closeModal" :isOpen="isModalOpen">我是一个快乐的小猫咪</Modal>
</template>


<script >
import { ref, watch, defineComponent, onErrorCaptured } from 'vue'
import Modal from './Modal.vue';
import AsyncTemp from './AsyncTemp.vue';
export default {
  components: { Modal, AsyncTemp },
  setup (props, ctx) {
    let error = ref(null)
    let isModalOpen = ref(false)
    onErrorCaptured(e => {
      error.value = e
      return true
    })
    const closeModal = () => {
      isModalOpen.value = false
    }
    const openModal = () => {
      console.log('open');
      isModalOpen.value = true
    }
    return {
      error,
      isModalOpen,
      closeModal,
      openModal
    }
  },
}
</script>

-->/AsyncTemp.vue
<template>
  <img :src="result && result.message" alt="" />
</template>
<script type="ts">
import { defineComponent } from 'vue'
import axios from 'axios'
export default defineComponent({
  async setup (props, context) {
    let rowData = await axios.get('https://dog.ceo/api/breeds/image/random')
    return { result: rowData.data }
  }
})
</script>
-->/Modal.vue
<template>
  <teleport to="#modal">
    <div class="modal" v-if="isOpen">
      <div class="content">
        <slot> 内容区域 </slot>
      </div>
      <button class="btn" @click="close">关闭</button>
    </div>
  </teleport>
</template>

<script>
import { defineComponent, watch } from 'vue'
export default defineComponent({
  props: {
    isOpen: {
      type: Boolean,
      default: true
    }
  },
  emits: {
    'handle-close': (payload) => {
      // 这里用来做验证
      return payload.type === 'close'
    }
  },
  setup (props, context) {
    function close () {
      context.emit('handle-close', { type: 'close' })
    }
    return {
      close
    }
  }
})
</script>
<style>
#modal .modal {
  width: 300px;
  height: 300px;
  top: calc(50% - 150px);
  left: calc(50% - 150px);
  border: 1px solid #f40;
  text-align: center;
  position: absolute;
  z-index: 999;
  background-color: #fff;
}
#modal .modal .btn {
  position: absolute;
  bottom: 10px;
}
</style>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值