vue3组合式api初体验

279 阅读3分钟

组合式api

什么是组合式api

官方地址

随着项目的扩大,功能越来越复杂,定义的数据以及对其数据的操作被放在不同的地方,如methods,watch,碎片化使得理解和维护复杂组件变得困难。选项的分离掩盖了潜在的逻辑问题。此外,在处理单个逻辑关注点时,我们必须不断地“跳转”相关代码的选项块,于是出现了组合式api

使用

组合式api在vue3中是通过setup函数实现的,在这个函数中进行使用,该函数接受 props 和 context 两个参数

在执行setup函数时,还没有创建实例,所以在setup函数中是没有this指向

参数

  • props

    export default {
      props: {
        title: String
      },
      setup(props) {
        console.log(props.title)
    
    		// 如果想使用结构赋值
    		const { title } = toRefs(props)
      }
    }
    
  • context

    context 是一个普通的 JavaScript 对象,它暴露组件的三个 property

    export default {
      setup(props, context) {
        // Attribute (非响应式对象)
        console.log(context.attrs)
    
        // 插槽 (非响应式对象)
        console.log(context.slots)
    
        // 触发事件 (方法)
        console.log(context.emit)
      }
    }
    

响应式基础

将数据变为响应式数据,现在有ref、reactive

  • ref函数 ,会将基本数据类型变为响应式的数据,本质是变为proxy对象,变为一个引用对象

    import { ref } from 'vue'
    
    const counter = ref(0)
    
    console.log(counter) // Proxy {value: 0}
    console.log(counter.value) // 0
    
    counter.value++
    console.log(counter.value) // 1
    

    ref函数还可以获取到模板中的html对象

    <div ref='homeDiv'>home</div>
    
    setup() {
    	const homeDiv = ref(null)
    	// 挂载之后去获取dom元素
    	onMounted(() => {
    	   console.log(homeDiv.value)
    	})
    	return {
    		// 要记得将其导出
    		homeDiv
    	}
    }
    
  • reactive函数,可以将对象变为响应式数据,返回对象的响应式副本

    import { reactive } from 'vue'
    
    const person = reactive({
    	name: '张三',
    	age: 22,
    	languages: ['英语', '汉语']
    })
    
    return {
    	// toRefs将响应式对象转换为普通对象,其中结果对象的每个 property 都是指向原始对象相应 property 的 ref
    	...toRefs(person) 
    }
    
  • toRef函数

    为源响应式对象上的某个 property 新创建一个 ref。然后,ref 可以被传递,它会保持对其源 property 的响应式连接

    const state = reactive({
      foo: 1,
      bar: 2
    })
    
    const fooRef = toRef(state, 'foo')
    
    fooRef.value++
    console.log(state.foo) // 2
    
    state.foo++
    console.log(fooRef.value) // 3
    
  • 计算属性

    接受一个getter函数,并从 getter 返回的值返回一个不变的响应式 ref 对象

    const count = ref(1)
    const plusOne = computed(() => count.value + 1)
    
    console.log(plusOne.value) // 2
    
    plusOne.value++ // error
    

    也可以接受get和set函数的对象

    const count = ref(1)
    const plusOne = computed({
      get: () => count.value + 1,
      set: val => {
        count.value = val - 1
      }
    })
    
    plusOne.value = 1
    console.log(count.value) // 0
    return {
    	count,
    	plusOne
    }
    
  • 监听属性

    可以直接监听一个ref对象,或者是具有返回值的getter函数

    // 侦听一个 getter
    const state = reactive({ count: 0 })
    watch(
      () => state.count,
      (count, prevCount) => {
        console.log(count)
      }
    )
    
    // 直接侦听一个 ref
    const count = ref(0)
    watch(count, (count, prevCount) => {
      console.log(count)
    })
    

    可以监视多个对象

    watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
      /* ... */
    })
    

    生命周期

    由于setup函数建立时,没有创建组件实例,所以没有beforeCreate 和 created 生命周期钩子,在这两个生命周期内进行的操作都可以在setup函数中进行

    onBeforeMount(() => {
    	console.log('onBeforeMount')
    })
    onMounted(() => {
       console.log('mounted!')
     })
    onBeforeUpdate(() => {
    	console.log('onBeforeUpdate!')
    })
    onUpdated(() => {
      console.log('updated!')
    })
    onUnmounted(() => {
      console.log('unmounted!')
    })
    

    provide和inject

    在组件之间传值时会使用到

    • provide 函数允许你通过两个参数定义 property,name和value
    provide: {
        location: 'North Pole',
        geolocation: {
          longitude: 90,
          latitude: 135
        }
     }
    
    • inject函数允许接受两个参数,一个是要 inject 的 property 的名称,另一个是默认值

    可以将其变为响应式的,如果想让传递的值不被改变,可以使用readonly来进行修饰

    const location = ref('North Pole')
    const geolocation = reactive({
      longitude: 90,
      latitude: 135
    })
    
    provide('location', readonly(location))
    provide('geolocation', geolocation)
    

    实例

    做了一个tosdolist的组件,该组件是输入一个值,在下面的列表中进行显示,可以将其逻辑单处抽离出去,使得整个逻辑比较清楚

    <ul>
       <li v-for="(item, index) in list" :key="index">
          <div v-if="item.edit" style="margin-right: 10px">{{ item.val }}</div>
            <el-input
              style="width: 300px"
              v-else
              v-model="item.val"
              @blur="item.edit = !item.edit"
            />
           <div>
              <el-button @click="edit(index)" type="primary">编辑</el-button>
              <el-button @click="remove(index)" type="primary">删除</el-button>
           </div>
         </li>
      </ul>
    
    setup() {
    	const {state, handleChange, edit, remove} = useState()
    	return {
    		...toRefs(state),
        handleChange,
        edit,
        remove,
    	}
    }
    
    import { reactive} from "vue";
    import Home from "@/model/home";
    type stateType = {
    	state: Home,
    	handleChange: () => void,
    	edit: (val: number) => void,
    	remove: (val: number) => void
    }
    export function useState(): stateType {
      const state: Home = reactive({
        input: "",
        list: [],
      });
    	const handleChange = () => {
    		state.list.push({
    			edit: true,
    			val: state.input,
    		});
    		state.input = "";
    	};
    	const edit = (index: number) => {
    		state.list[index].edit = false;
    	};
    	const remove = (index: number) => {
    		state.list.splice(index, 1);
    	};
    	return {
    		state, 
    		handleChange,
    		edit,
    		remove
    	}
    }