vue3中的 Composition API 、新特性

 

1.setup的执行

  是在 beforeCreate 之前执行,所以在 setup 内部,this 不是当前组件实例的引用,也就是说 setup 中无法直接调用组件的其他数据

 Composition API 让我们 setup 中使用生命周期钩子函数、响应式数据等组件特性

2. props 、context

setup不是生命周期钩子函数,并且具备props 和 context

1
2
3
4
5
6
7
8
9
import { toRefs } from 'vue';setup(props, context) {<br>   //因为 props 是响应式的,所以<strong>不能直接对 props 使用 ES6 解构,因为它会消除 prop 的响应性</strong><br>   const {title} = toRefs(props); //通过toRefs包装后的props可以在解构后仍然具有响应式
    const { attrs, slots, emit } = context;
    // Attribute (非响应式对象)
    console.log(attrs);
    // 组件插槽 (非响应式对象)
    console.log(slots);<br>   
    // 触发事件 (方法)
    console.log(emit);
}

 3.在 setup 中使用生命周期钩子(需要从 Vue 引入带有 on 前缀的生命周期)

1
2
3
4
5
6
7
8
import { toRefs, onMounted } from 'vue';
 
setup (props) {
    const { title } = toRefs(props);
    onMounted(() => {
        console.log('onMounted', title);
    });
}

因为 setup 本身是基于beforeCreate运行, 所以在setup 中没有 beforeCreate 和 created 这两个生命周期

4.setup的返回值

 (1)ref

 通过ref 包装的变量会具有响应性

 setup 内部,ref 包装后的变量需要通过 value 来修改变量的值

  (2)setup 还可以返回一个渲染函数,则组件中定义的 template 将会失效

1
2
3
4
5
6
7
8
9
10
import { h, ref, reactive } from 'vue'
 
export default {
  setup() {
    const readersNumber = ref(0)
    const book = reactive({ title: 'Vue 3 Guide' })
    // 返回一个渲染函数以覆盖 template
    return () => h('div', [readersNumber.value, book.title])
  }
}

5. Computed、Watch

1
2
3
4
5
6
7
8
9
10
import { ref, computed } from 'vue'
 
setup() {
  const list = ref([]);
  const lable = computed(() => list.value.length > 0 ? list.join('-')  : '暂无数据');
  return {
    list,
    lable,
  }
}

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { ref, watch } from 'vue'
 
setup() {
  const type = ref('');
 
  watch(type, (newValue, oldValue) => {
   switch (type) {
    case 0:
        console.log('good')
        break;
    case 1:
        console.log("just so so");
         break;
    case 2:
        console.log("wrong");
   }
  });
 
  return {
    type,
  }
}  

监听多个数据

watch([() => state.age, year], ([curAge, newVal], [preAge, oldVal]) => {
console.log("新值:", curAge, "老值:", preAge); console.log("新值:", newVal,
"老值:", oldVal); });

监听复杂的嵌套对象

复制代码
const state = reactive({
  room: {
    id: 100,
    attrs: {
      size: "140平方米",
      type: "三室两厅",
    },
  },
});
watch(
  () => state.room,
  (newType, oldType) => {
    console.log("新值:", newType, "老值:", oldType);
  },
  { deep: true }
);
复制代码

如果不使用第三个参数deep:true, 是无法监听到数据变化的。 前面我们提到,默认情况下,watch 是惰性的, 那什么情况下不是惰性的, 可以立即执行回调函数呢?其实使用也很简单, 给第三个参数中设置immediate: true即可

7.抽取公共逻辑

例如有一个列表展示与数据搜索过滤的功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// src/composables/useList.js
 
import { ref, onMounted, watch } from 'vue';
 
// 发起接口请求
function fetchList() {
  // ...
}
 
export default function useList(query) {
  // 创建数据列表 list (data)
  const list = ref([]);
  // 创建查询并更新 list 的方法 (methods)
  const getList = async () => {
    list.value = await fetchList(query);
  };
 
  // 生命周期 mounted
  onMounted(getList);
  // 监听 query 的变化并更新 list
  watch(query, getList);
 
  return {
    list,
    getList,
  };
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<br><br><br>// src/composables/useFilter.js
 
import { ref, computed } from 'vue';
 
export default function useFilter(list) {
  // 创建搜索关键字 keyword (data)
  const keyword = ref('');
  // 创建筛选结果 filterRes (computed)
  const filterRes = computed(() => {
    return list.filter((value) => {
      return value.includes(keyword.value);
    });
  });
 
  return {
    keyword,
    filterRes,
  };
}

 在组件中引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { defineComponent, toRefs } from 'vue';
import useList from '@/composables/useList';
import useFilter from '@/composables/useFilter';
 
export default defineComponent({
  props: ['query'],
  setup(props) {
    const { query } = toRefs(props);
    const { list, getList } = useList(query);
    const { keyword, filterRes } = useFilter(list);
    return {
      keyword, // 筛选关键字
      getList, // 查询并更新数据列表
      list: filterRes, // 不需要返回未经筛选的列表
    };
  },
});

8.自定义hooks

复制代码
import { ref, Ref, computed } from "vue";

type CountResultProps = {
  count: Ref<number>;
  multiple: Ref<number>;
  increase: (delta?: number) => void;
  decrease: (delta?: number) => void;
};

export default function useCount(initValue = 1): CountResultProps {
  const count = ref(initValue);

  const increase = (delta?: number): void => {
    if (typeof delta !== "undefined") {
      count.value += delta;
    } else {
      count.value += 1;
    }
  };
  const multiple = computed(() => count.value * 2);

  const decrease = (delta?: number): void => {
    if (typeof delta !== "undefined") {
      count.value -= delta;
    } else {
      count.value -= 1;
    }
  };

  return {
    count,
    multiple,
    increase,
    decrease,
  };
}
复制代码

在组件中使用这个hooks

复制代码
<template>
  <p>count: {{ count }}</p>
  <p>倍数: {{ multiple }}</p>
  <div>
    <button @click="increase()">加1</button>
    <button @click="decrease()">减一</button>
  </div>
</template>

<script lang="ts">
import useCount from "../hooks/useCount";
 setup() {
    const { count, multiple, increase, decrease } = useCount(10);
        return {
            count,
            multiple,
            increase,
            decrease,
        };
    },
</script>
复制代码

9.Teleport

 使用场景,例如在子组件Header中使用到Dialog组件,但希望Dialog渲染到外部挂载的 DOM,同时还可以使用到 Vue 组件内的状态

在index.html中定一个供挂载的元素

<body>
  <div id="app"></div>
  <div id="dialog"></div>
</body>

定义一个Dialog组件, to 属性的值与挂载的id相同

复制代码
<template>
  <teleport to="#dialog">
    <div class="dialog">
      <div class="dialog_wrapper">
        <div class="dialog_header" v-if="title">
          <slot name="header">
            <span>{{ title }}</span>
          </slot>
        </div>
      </div>
      <div class="dialog_content">
        <slot></slot>
      </div>
      <div class="dialog_footer">
        <slot name="footer"></slot>
      </div>
    </div>
  </teleport>
</template>
复制代码

在Header组件中使用Dialog组件

<div class="header"><navbar />
    <Dialog v-if="dialogVisible"></Dialog>
</div>

10.Suspense

使用场景:在前后端交互获取数据时, 是一个异步过程,一般我们都会提供一个加载中的动画,当数据返回时配合v-if来控制数据显示

vue3中内置组件Suspense, 提供两个template slot, 刚开始会渲染一个 fallback 状态下的内容, 直到到达某个条件后才会渲染 default 状态的正式内容

复制代码
<Suspense>
        <template #default>
            <async-component></async-component>
        </template>
        <template #fallback>
            <div>
                Loading...
            </div>
        </template>
  </Suspense>
复制代码
asyncComponent.vue
复制代码
<<template>
<div>
    <h4>这个是一个异步加载数据</h4>
    <p>用户名:{{user.nickname}}</p>
    <p>年龄:{{user.age}}</p>
</div>
</template>

<script>
import { defineComponent } from "vue"
import axios from "axios"
export default defineComponent({
    setup(){
        const rawData = await axios.get("http://xxx.xinp.cn/user")
        return {
            user: rawData.data
        }
    }
})
</script>
复制代码

11.自定义指令

在 Vue 3 中对自定义指令的 API 进行了更加语义化的修改, 就如组件生命周期变更一样

 

 

 自定义指令

const { createApp } from "vue"

const app = createApp({})
app.directive('focus', {
    mounted(el) {
        el.focus()
    }
})

在组件中使用

<input v-focus />

 

 

  

posted @   飞驰的小马哥  阅读(225)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~
点击右上角即可分享
微信分享提示