初始化项目
$ npm init vite-app <project-name>
$ cd <project-name>
$ npm install
$ npm run dev
关于vite的介绍链接
app的挂载方式
import { createApp } from 'vue'
import App from './App.vue'
import './index.css'
createApp(App).mount('#app')
开始写一个简单的计数器的例子:
<template>
<button @click="change">count is: {{ count }}</button>
</template>
<script>
export default {
setup () {
let count = 0
let change = () => count++
return { count, change }
}
}
</script>
代码中使用到了 setup 函数,setup 函数是一个新的组件选项。作为在组件内使用 Composition API 的入口点。
调用时机:创建组件实例,然后初始化 props ,紧接着就调用 setup 函数。从生命周期钩子的视角来看,它会在 beforeCreate 钩子之前被调用。
如果 setup 返回一个对象,则对象的属性可以在组件的模板中使用。
在上面代码中,我们声明了一个 count 变量,然后给 button 绑定了 click 事件,每次点击让 count 加1,但是此时页面并不会发生任何变化,因为 count 此时还不是响应式数据。接下来开始了解一些响应式的 API 来创建响应式的数据。
ref
Vue 本身已经有 "ref" 的概念了。但只是为了在模板中获取 DOM 元素或组件实例 (“模板引用”)。新的 ref 系统同时用于逻辑状态和模板引用。
ref 函数接受一个参数值并返回一个响应式且可改变的 ref 对象。ref 对象拥有一个指向内部值的单一属性 .value。
当返回的 ref 对象在模板中使用的时候,它会自动取 .value 的内容,无需在模板中额外书写.value。
<template>
<button @click="change">count is: {{ count }}</button>
</template>
<script>
import { ref } from 'vue'
export default {
setup () {
let count = ref(0)
let change = () => count.value++
return { count, change }
}
}
</script>
模板引用:
<template>
<div ref="root">hello</div>
</template>
<script>
import { ref, onMounted } from 'vue'
export default {
setup () {
const root = ref(null)
onMounted(() => {
console.log(root.value) // <div/>
})
return { root }
}
}
</script>
reactive
reactive 函数接收一个普通对象然后返回该普通对象的响应式代理。ref 和 reactive 都可以创建响应式的数据,但是读写 ref 的操作比普通值的更冗余,因为需要访问 .value。所以 ref 一般用来创建简单值,reactive 用来创建较为复杂的值。
<template>
<button @click="change">count is: {{ state.count }}</button>
</template>
<script>
import { reactive } from 'vue'
export default {
setup () {
let state = reactive({count: 0})
let change = () => state.count++
return { state, change }
}
}
</script>
不要解构 state 对象,那样会使其失去响应性。现在的模板中只能书写 state.count ,如果想变成 count 可以先转换成 ref 对象。使用 toRef 或者 toRefs 函数。
toRef
toRef 函数可以用来为一个 reactive 对象的属性创建一个 ref。这个 ref 可以被传递并且能够保持响应性。
<template>
<button @click="change">count is: {{ count }}</button>
</template>
<script>
import { reactive, toRef } from 'vue'
export default {
setup () {
let state = reactive({count: 0})
let change = () => state.count++
let count = toRef(state, 'count')
return { count, change }
}
}
</script>
toRefs
toRefs 函数把一个响应式对象转换成普通对象,该普通对象的每个 property 都是一个 ref ,和响应式对象 property 一一对应。
<template>
<button @click="change">count is: {{ count }}</button>
</template>
<script>
import { reactive, toRefs } from 'vue'
export default {
setup () {
let state = reactive({count: 0})
let change = () => state.count++
let stateAsRefs = toRefs(state)
return { ...stateAsRefs, change }
}
}
</script>
computed
计算属性和vue2中的一样,没啥区别,可以传对象也可以传函数。
<template>
<input type="checkbox" id="all" v-model="checkedAll">
<label for="all">全部选择</label>
<div></div>
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames">
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mike</label>
<br>
<span>Checked names: {{ checkedNames }}</span>
</template>
<script>
import { ref, computed } from 'vue'
export default {
setup(props){
let checkedNames = ref([])
let checkedAll = computed({
get () {
return checkedNames.value.length === 3
},
set (value) {
if (value) {
checkedNames.value = ['Jack', 'John', 'Mike']
} else {
checkedNames.value = []
}
}
})
return { checkedNames, checkedAll }
}
}
</script>
watch
watch 需要侦听特定的数据源,并在回调函数中执行副作用。默认情况是懒执行的,也就是说仅在侦听的源变更时才执行回调。
<template>
<button @click="change">count is: {{ state.count }}</button>
</template>
<script>
import { reactive, watch } from 'vue'
export default {
setup () {
let state = reactive({count: 0})
let change = () => state.count++;
watch(state, () => {
console.log(state, '改变')
})
return { state, change }
}
}
</script>
注意参数的写法:
<template>
<button @click="change">count is: {{ state.count }}</button>
</template>
<script>
import { reactive, watch } from 'vue'
export default {
setup () {
let state = reactive({count: 0})
let change = () => state.count++;
watch(() => state.count, (oldVlaue, newValue) => {
console.log(oldVlaue, newValue, '改变')
})
return { state, change }
}
}
</script>
watchEffect
立即执行传入的一个函数,并响应式追踪其依赖,并在其依赖变更时重新运行该函数。
与watch的区别:
1、watch 是需要传入侦听的数据源,而 watchEffect 是自动收集需要侦听的数据源。2、watch 可以访问侦听状态变化前后的值,而 watchEffect 没有。3、watch 是属性改变的时候执行,而 watchEffect 是默认会执行一次,然后属性改变也会执行。
<template>
<button @click="change">count is: {{ state.count }}</button>
</template>
<script>
import { reactive, watchEffect } from 'vue'
export default {
setup () {
let state = reactive({count: 0})
let change = () => state.count++;
watchEffect(() => {
console.log(state.count, '改变')
})
return { state, change }
}
}
</script>
生命周期
可以直接导入 onXXX 一族的函数来注册生命周期钩子: 与 2.x 版本生命周期相对应的组合式 API
- beforeCreate -> 使用 setup()
- created -> 使用 setup()
- beforeMount -> onBeforeMount
- mounted -> onMounted
- beforeUpdate -> onBeforeUpdate
- updated -> onUpdated
- beforeDestroy -> onBeforeUnmount
- destroyed -> onUnmounted
- errorCaptured -> onErrorCaptured
import { onMounted, onUpdated, onUnmounted } from 'vue'
const MyComponent = {
setup() {
onMounted(() => {
console.log('mounted!')
})
onUpdated(() => {
console.log('updated!')
})
onUnmounted(() => {
console.log('unmounted!')
})
},
}
这些生命周期钩子注册函数只能在 setup() 期间同步使用, 因为它们依赖于内部的全局状态来定位当前组件实例(正在调用 setup() 的组件实例), 不在当前组件下调用这些函数会抛出一个错误。
依赖注入
provide 和 inject 提供依赖注入,功能类似 2.x 的 provide/inject。两者都只能在当前活动组件实例的 setup() 中调用。
在上游组件提供 provide:
setup () {
provide('Theme', 'dark')
}
在下游组件使用:
<template>
<div>
hello
</div>
</template>
<script>
import { ref, onMounted, inject } from 'vue'
export default {
setup () {
const theme = inject('Theme', 'light' /* 默认值 */)
console.log(theme, 'provide')
const root = ref(null)
return { root }
}
}
</script>
其它
- readonly 把对象变成只读的
- isRef 检查一个值是否为一个 ref 对象。
- isProxy 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理。
- isReactive检查一个对象是否是由 reactive 创建的响应式代理。
- isReadonly检查一个对象是否是由 readonly 创建的只读代理。
- unref如果参数是一个 ref 则返回它的 value,否则返回参数本身