当前位置: 代码迷 >> 综合 >> Vue3.0 Composition API
  详细解决方案

Vue3.0 Composition API

热度:65   发布时间:2023-12-05 14:40:33.0

Composition API

是vue 3.0 新增的API,依然可以使用之前版本options API

实现一个监听鼠标位置案例,使用的是vue 3.0 的源码
data必须是函数,统一了data的写法

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><div id="app">x: {
    {
     x }} <br>y: {
    {
     y }}</div><script type="module">import {
     createApp, reactive, onMounted, onUnmounted, toRefs } from './node_modules/vue/dist/vue.esm-browser.js'const app = createApp({
      // createApp 是创建vue对象用的setup () {
     // setup 需要返回一个对象,执行在props解析完毕,组件实例创建之前执行 ,是Composition API 的入口// const position = useMousePosition()// 第一个参数 props// 第二个参数 context,attrs、emit、slotsconst position = reactive({
     // 创建响应式对象的x: 0,y: 0})return {
    position}}})console.log(app)app.mount('#app') // 进行挂载</script>
</body>
</html>

生命周期钩子函数

在案例中增加生命周期函数,作用是当鼠标移动时,显示对应的位置,目的是将所有逻辑封装到一个函数中,所有地方都可以重用

在这里插入图片描述
生命周期函数使用时候,需要在对应前加上on,并且首字母大写

在setup中使用导入的生命周期函数,需要传入一个函数,在函数中注册事件

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><div id="app">x: {
    {
     x }} <br>y: {
    {
     y }}</div><script type="module">import {
     createApp, reactive, onMounted, onUnmounted, toRefs } from './node_modules/vue/dist/vue.esm-browser.js'function useMousePosition () {
    // 第一个参数 props// 第二个参数 context,attrs、emit、slotsconst position = reactive({
    x: 0,y: 0})const update = e => {
    position.x = e.pageXposition.y = e.pageY}onMounted(() => {
    window.addEventListener('mousemove', update)})onUnmounted(() => {
    window.removeEventListener('mousemove', update)})return toRefs(position)}const app = createApp({
    setup () {
    // const position = useMousePosition()const {
     x, y } = useMousePosition()return {
    x,y}}})console.log(app)app.mount('#app')</script>
</body>
</html>

reactive-toRefs-ref

介绍Composition API 三个函数,都是创建响应式数据的

  • toRefs是将所有响应式对象的所有属性都转换成响应式的,要求传入的属性必须是代理对象,不是代理的话,会报出警告,然后内部遍历所有属性,增加对应的get,set,通过torefs的对象可以结构
  • reactive 是将传入的对象转换为响应式代理对象,修改时会有对应的get,set
  • 响应式的API --ref,是一个函数,作用是把普通数据转化成响应式数据,是把基本类型数据转化

新增案例,点击按钮,增加数据

  <!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title></head><body><div id="app"><button @click="increase">按钮</button><span>{
    {
     count }}</span></div><script type="module">import {
     createApp, ref } from './node_modules/vue/dist/vue.esm-browser.js'function useCount () {
    const count = ref(0)return {
    count,increase: () => {
    count.value++}}}createApp({
    setup () {
    return {
    ...useCount()}        }}).mount('#app')</script></body></html>

Computed

计算属性的作用是简化模板中的代码,缓存计算的结果,数据变化后,才会重新计算

Computed可以创建响应式数据,但是依赖其他相应响应式数据,当数据发生变化后,会重新计算属性依赖的函数
在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><div id="app"><button @click="push">按钮</button>未完成:{
    {
     activeCount }}</div><script type="module">import {
     createApp, reactive, computed } from './node_modules/vue/dist/vue.esm-browser.js'const data = [{
     text: '看书', completed: false },{
     text: '敲代码', completed: false },{
     text: '约会', completed: true }]createApp({
    setup () {
    const todos = reactive(data)const activeCount = computed(() => {
    return todos.filter(item => !item.completed).length})return {
    activeCount,push: () => {
    todos.push({
    text: '开会',completed: false})}}}}).mount('#app')</script>
</body>
</html>

Watch

侦听器,和computed类似,在setup中可以使用watch,和之前使用方式一样
在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><div id="app"><p>请问一个 yes/no 的问题:<input v-model="question"></p><p>{
    {
     answer }}</p></div><script type="module">// https://www.yesno.wtf/apiimport {
     createApp, ref, watch } from './node_modules/vue/dist/vue.esm-browser.js'createApp({
    setup () {
    const question = ref('')const answer = ref('')watch(question, async (newValue, oldValue) => {
    const response = await fetch('https://www.yesno.wtf/api')const data = await response.json()answer.value = data.answer})return {
    question,answer}}}).mount('#app')</script>
</body>
</html>

watchEffect

  • 是watch函数的简化版本,也用来监听数据的变化
  • 接收函数作为参数,监听函数内部响应式数据变化

使用watchEffect监听数值的变化

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><div id="app"><button @click="increase">increase</button><button @click="stop">stop</button><br>{
    {
     count }}</div><script type="module">import {
     createApp, ref, watchEffect } from './node_modules/vue/dist/vue.esm-browser.js'createApp({
    setup () {
    const count = ref(0)const stop = watchEffect(() => {
    console.log(count.value)})return {
    count,stop,increase: () => {
    count.value++}}}}).mount('#app')</script>
</body>
</html>

实现一个todolist

在这里插入图片描述
搭建结构
需要升级脚手架vue-cli,4.5以上版本
使用和之前一样,使用vue create 创建新的项目

添加待办事项

import {
     ref, computed} from 'vue'const storage = useLocalStorage()// 1. 添加待办事项
const useAdd = todos => {
    const input = ref('')const addTodo = () => {
    const text = input.value && input.value.trim()if (text.length === 0) returntodos.value.unshift({
    text,completed: false})input.value = ''}return {
    input,addTodo}
}export default {
    name: 'App',setup () {
    const todos = ref([])const {
     remove, removeCompleted } = useRemove(todos)return {
    todos,}}
}

删除代办事项

// 2. 删除待办事项
const useRemove = todos => {
    const remove = todo => {
    const index = todos.value.indexOf(todo)todos.value.splice(index, 1)}const removeCompleted = () => {
    todos.value = todos.value.filter(todo => !todo.completed)}return {
    remove,removeCompleted}
}
export default {
    name: 'App',setup () {
    const todos = ref([])const {
     remove, removeCompleted } = useRemove(todos)return {
    todos,remove,removeCompleted,}}
}

编辑待办事项
在这里插入图片描述

// 3. 编辑待办项
const useEdit = remove => {
    let beforeEditingText = ''const editingTodo = ref(null)const editTodo = todo => {
    beforeEditingText = todo.texteditingTodo.value = todo}const doneEdit = todo => {
    if (!editingTodo.value) returntodo.text = todo.text.trim()todo.text || remove(todo)editingTodo.value = null}const cancelEdit = todo => {
    editingTodo.value = nulltodo.text = beforeEditingText}return {
    editingTodo,editTodo,doneEdit,cancelEdit}
}export default {
    name: 'App',setup () {
    const todos = ref([])const {
     remove, removeCompleted } = useRemove(todos)return {
    todos,remove,removeCompleted,...useAdd(todos),...useEdit(remove),}}
}

指令
在这里插入图片描述
在这里插入图片描述

export default {
    name: 'App',setup () {
    const todos = useStorage()const {
     remove, removeCompleted } = useRemove(todos)return {
    todos,remove,removeCompleted,...useAdd(todos),...useEdit(remove),...useFilter(todos)}},directives: {
    editingFocus: (el, binding) => {
    binding.value && el.focus()}}
}

总代码

<template><section id="app" class="todoapp"><header class="header"><h1>todos</h1><inputclass="new-todo"placeholder="What needs to be done?"autocomplete="off"autofocusv-model="input"@keyup.enter="addTodo"></header><section class="main" v-show="count"><input id="toggle-all" class="toggle-all" v-model="allDone" type="checkbox"><label for="toggle-all">Mark all as complete</label><ul class="todo-list"><liv-for="todo in filteredTodos":key="todo":class="{ editing: todo === editingTodo, completed: todo.completed }"><div class="view"><input class="toggle" type="checkbox" v-model="todo.completed"><label @dblclick="editTodo(todo)">{
    {
     todo.text }}</label><button class="destroy" @click="remove(todo)"></button></div><inputclass="edit"type="text"v-editing-focus="todo === editingTodo"v-model="todo.text"@keyup.enter="doneEdit(todo)"@blur="doneEdit(todo)"@keyup.esc="cancelEdit(todo)"></li></ul></section><footer class="footer" v-show="count"><span class="todo-count"><strong>{
    {
     remainingCount }}</strong> {
    {
     remainingCount > 1 ? 'items' : 'item' }} left</span><ul class="filters"><li><a href="#/all">All</a></li><li><a href="#/active">Active</a></li><li><a href="#/completed">Completed</a></li></ul><button class="clear-completed" @click="removeCompleted" v-show="count > remainingCount">Clear completed</button></footer></section><footer class="info"><p>Double-click to edit a todo</p><!-- Remove the below line ↓ --><p>Template by <a href="http://sindresorhus.com">Sindre Sorhus</a></p><!-- Change this out with your name and url ↓ --><p>Created by <a href="https://www.lagou.com">教瘦</a></p><p>Part of <a href="http://todomvc.com">TodoMVC</a></p></footer>
</template><script>
import './assets/index.css'
import useLocalStorage from './utils/useLocalStorage'
import {
     ref, computed, onMounted, onUnmounted, watchEffect } from 'vue'const storage = useLocalStorage()// 1. 添加待办事项
const useAdd = todos => {
    const input = ref('')const addTodo = () => {
    const text = input.value && input.value.trim()if (text.length === 0) returntodos.value.unshift({
    text,completed: false})input.value = ''}return {
    input,addTodo}
}// 2. 删除待办事项
const useRemove = todos => {
    const remove = todo => {
    const index = todos.value.indexOf(todo)todos.value.splice(index, 1)}const removeCompleted = () => {
    todos.value = todos.value.filter(todo => !todo.completed)}return {
    remove,removeCompleted}
}// 3. 编辑待办项
const useEdit = remove => {
    let beforeEditingText = ''const editingTodo = ref(null)const editTodo = todo => {
    beforeEditingText = todo.texteditingTodo.value = todo}const doneEdit = todo => {
    if (!editingTodo.value) returntodo.text = todo.text.trim()todo.text || remove(todo)editingTodo.value = null}const cancelEdit = todo => {
    editingTodo.value = nulltodo.text = beforeEditingText}return {
    editingTodo,editTodo,doneEdit,cancelEdit}
}// 4. 切换待办项完成状态
const useFilter = todos => {
    const allDone = computed({
    get () {
    return !todos.value.filter(todo => !todo.completed).length},set (value) {
    todos.value.forEach(todo => {
    todo.completed = value})}})const filter = {
    all: list => list,active: list => list.filter(todo => !todo.completed),completed: list => list.filter(todo => todo.completed)}const type = ref('all')const filteredTodos = computed(() => filter[type.value](todos.value))const remainingCount = computed(() => filter.active(todos.value).length)const count = computed(() => todos.value.length)const onHashChange = () => {
    const hash = window.location.hash.replace('#/', '')if (filter[hash]) {
    type.value = hash} else {
    type.value = 'all'window.location.hash = ''}}onMounted(() => {
    window.addEventListener('hashchange', onHashChange)onHashChange()})onUnmounted(() => {
    window.removeEventListener('hashchange', onHashChange)})return {
    allDone,count,filteredTodos,remainingCount}
}// 5. 存储待办事项
const useStorage = () => {
    const KEY = 'TODOKEYS'const todos = ref(storage.getItem(KEY) || [])watchEffect(() => {
    storage.setItem(KEY, todos.value)})return todos
}export default {
    name: 'App',setup () {
    const todos = useStorage()const {
     remove, removeCompleted } = useRemove(todos)return {
    todos,remove,removeCompleted,...useAdd(todos),...useEdit(remove),...useFilter(todos)}},directives: {
    editingFocus: (el, binding) => {
    binding.value && el.focus()}}
}
</script><style>
</style>
function parse (str) {
    let valuetry {
    value = JSON.parse(str)} catch {
    value = null}return value
}function stringify (obj) {
    let valuetry {
    value = JSON.stringify(obj)} catch {
    value = null}return value
}export default function useLocalStorage () {
    function setItem (key, value) {
    value = stringify(value)window.localStorage.setItem(key, value)}function getItem (key) {
    let value = window.localStorage.getItem(key)if (value) {
    value = parse(value)}return value}return {
    setItem,getItem}
}

源码地址 : https://github.com/qifutian/learngit/tree/main/vue3.0-todolist