createApp
createApp 的作用是创建一个 vue 对象,它可以接收一个选项作为一个参数,也就是一个组件的选项,和vue2中给构造函数传入的选项一样,可以传入 data、methods、computed、created等选项。
data不支持对象的写法,data必须是函数。
<script type="module">
import { createApp } from './node_modules/vue/dist/vue.esm-browser.js'
const app = createApp({
// setup 需要返回一个对象,可以使用在模板、methods、computed、以及生命周期的钩子函数中
// setup执行的时机:props解析完毕,组件实例被创建之前执行。
// 因此在 setup 内部,无法通过 this 获取到组件的实例,因为组件实例还未被创建。
// setup 中的 this 此时指向 undefined
setup () {
// 第一个参数 props, props 的作用是用来接收外部传入的参数,props 是一个响应式的对象,但是不能被解构
// 第二个参数 context,context 是一个对象,它具有三个成员:attrs、emit、slots
const position = {
x: 0,
y: 0
}
return {
position // 将来可以在模板以及组件的其他位置,使用这个对象,但此时并不是响应式对象
}
}
})
console.log(app)
app.mount('#app')
</script>
reactive
reactive 的作用是把一个对象转换成响应式对象,并且该对象的嵌套对象也都转换成响应式对象。
它返回的是一个 proxy 对象。
<script type="module">
import { createApp, reactive } from './node_modules/vue/dist/vue.esm-browser.js'
const app = createApp({
setup () {
// 使用 reactive 将对象转换成响应式对象
const position = reactive({
x: 0,
y: 0
})
return {
position
}
}
})
console.log(app)
app.mount('#app')
</script>
生命周期钩子函数
setup 是在 beforeCreate 和 created 之间执行的。
onRenderTracked 和 onRenderTriggered 在 render 函数被重新调用时触发。
onRenderTracked 在 render 函数首次调用时也会触发。
onRenderTriggered 在 render 函数首次调用时不会触发。
toRefs
const { x, y } = useMousePosition()
将 x,y 从 useMousePosition() 中解构出来,x,y 不是响应式数据。
这是因为
const position = reactive({
x: 0,
y: 0
})
这里使用 reactive 将 position 包装成了 proxy 对象,当调用 position.x,position.y时,会调用代理中的 get 方法拦截并收集依赖。当 position.x,position.y 发生变化时,会调用 set 拦截并触发更新。
当把代理对象解构的时候,就相当于定义了x,y这两个变量来接收positon.x,position.y,而基本类型的赋值就是把值在内存中复制一份,所以这里的x,y只是2个基本类型的变量,和代理对象无关。
toRefs 可以把一个响应式对象中的属性也转换成响应式的。
toRefs 要求传入的参数必须是一个代理对象。
toRefs 原理:内部会创建一个新的对象,遍历传入的代理对象的所有属性,把所有属性的值都转换成响应式对象,
并且属性是一个对象,具有 value 属性,在模板中使用,可以将value省略,但是在代码中不能省略。
然后挂载到新创建的对象上,再将新创建的对象返回。
<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 () {
const position = reactive({
x: 0,
y: 0
})
const update = e => {
position.x = e.pageX
position.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>
ref
ref的作用是将基本类型的数据包装成响应式对象。
而 reactive 是将一个对象转换成响应式数据。
如果 ref 传入的是对象时,内部会去调用 reactive 返回一个代理对象。
如果 ref 传入的是基本类型数据时,会创建一个只有 value 属性的对象。
<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>
reactive vs ref
ref 可以把基本数据类型数据,转成响应式对象
ref 返回的对象,重新赋值成对象也是响应式的
reactive 返回的对象,重新赋值丢失响应式
reactive 返回的对象不可以解构
computed
<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>
Watch
Watch 的三个参数:
- 第一个参数:要监听的数据,可以是一个获取值的函数,监听这个函数返回值的变化、或者是一个ref, 或者是 reactive 返回的对象,或者是数组
- 第二个参数:监听到数据变化后执行的函数,这个函数有两个参数分别是新值和旧值
- 第三个参数;选项对象,deep 和 immediate
Watch 的返回值:
- 取消监听的函数
<div id="app">
<p>
请问一个 yes/no 的问题:
<input v-model="question">
</p>
<p>{{ answer }}</p>
</div>
<script type="module">
// https://www.yesno.wtf/api
import { 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>
WatchEffect
- 是 watch 函数的简化版本,也用来监视数据的变化,内部实现是和 Watch 调用的同一个函数,不同的是,watchEffect 没有第二个回调函数的参数,watchEffect 会接收一个 函数 作为参数,它会监听 函数内部响应式数据的变化,它会立即执行一次这个函数,当数据发生变化后,会重新运行该函数,返回一个取消监听的函数
- 接收一个函数作为参数,监听函数内响应式数据的变化
<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>
Effect
effect 使用方法和 watchEffect 一样,watchEffect 内部就是使用了 effect
effect 首先会执行一次,当 effect 中的响应式数据发生变化时,effect会再次执行
<script type="module">
import { reactive, effect } from './reactivity/index.js'
const product = reactive({
name: 'iPhone',
price: 5000,
count: 3
})
let total = 0
effect(() => {
total = product.price * product.count
})
console.log(total)
product.price = 4000
console.log(total)
product.count = 1
console.log(total)
</script>
获取DOM
<template>
<h1 ref="title">
{{ msg }}
</h1>
<comp ref="comp" />
</template>
<script lang="ts">
import { ref, defineComponent, onMounted, PropType } from 'vue'
import comp from './comp.vue'
interface User {
name: string
age: number
}
export default defineComponent({
name: 'HelloWorld',
components: {
comp
},
props: {
msg: {
type: String,
required: true
},
obj: {
type: Object as PropType<User>,
default: () => {}
}
},
setup (prop) {
onMounted(() => {
console.log('title', title.value)
})
const title = ref<HTMLHeadElement | null>(null)
const component = ref<InstanceType<typeof comp> | null>(null)
return {
title,
component
}
}
})
</script>
<script setup>
语法
Composition API 的语法糖
<template>
<h1>{{ msg }}</h1>
<button
type="button"
@click="increment"
>
count is: {{ count }}
</button>
</template>
<script setup lang="ts">
import { ref, defineProps, defineEmits } from 'vue'
const props = defineProps({
msg: {
type: String,
default: ''
}
})
const emit = defineEmits(['increment'])
const count = ref(0)
const increment = () => {
console.log(props.msg)
count.value++
emit('increment')
}
</script>
使用tsx
.vue 文件
<template>
<h1>hello world</h1>
<abc />
</template>
<script setup lang="tsx">
const abc = <h1>abc</h1>
</script>
.tsx 文件
Options API
import { defineComponent } from '@vue/runtime-core'
export default defineComponent({
props: {
msg: {
type: String,
required: true
}
},
data() {
return {
count: 0
}
},
render () {
return (
<div>
<div>{ this.msg }</div>
<div>{ this.count }</div>
</div>
)
}
})
Composition API
import { defineComponent, ref } from '@vue/runtime-core'
interface PropsType {
msg: string
}
export default defineComponent({
props: {
msg: {
type: String,
required: true
}
},
setup() {
const count = ref(0)
return (props: PropsType) => (
<div>
<p>{ props.msg }</p>
<p>{ count.value }</p>
</div>
)
}
})
1