易学易用,性能出色,适用场景丰富的 Web 前端框架。
bash# 使用vite直接创建项目,配置项较少,可选项目类型
npm create vite
bash# 使用vue创建项目,提供较多配置项
npm create vue@latest
Vue 官方 VS Code 插件:
main.ts
中的 createApp(App).mount('#app')
,vue的 createApp()
方法将根组件 App
挂载到 #app
的div上
Vue的三个标签(Vue2中不能有多个根标签):
vue<template> <div class="app"> <h1>Hello</h1> </div> </template> <script lang="ts"> export default { name: 'App' } </script> <style> .app { background-color: #ddd; box-shadow: 0 0 10px; border-radius: 6px; padding: 20px; } </style>
vue<template> <div class="person"> <h2>姓名:{{ name }}</h2> <h2>年龄:{{ age }}</h2> <button @click="changeName">修改姓名</button> <button @click="changeAge">修改年龄</button> <button @click="showTel">查看联系方式</button> </div> </template> <script lang="ts"> export default { name: 'Person', data() { return { name: '张三', age: 18, tel: '1888888888' } }, methods: { changeName() { this.name = 'ZhangSan' }, changeAge() { this.age += 1 }, showTel() { alert(this.tel) } } } </script> <style scoped> .person { background-color: skyblue; box-shadow: 0 0 10px; border-radius: 6px; padding: 20px; } button { margin: 2px; } </style>
Vue2:选项式API(OptionsAPI)
Vue3:组合式API(CompositionAPI)
Options
类型的 API
,数据、方法、计算属性等,是分散在:data
、methods
、computed
中的,若想新增或者修改一个需求,就需要分别修改:data
、methods
、computed
,不便于维护和复用。
可以用函数的方式,更加优雅的组织代码,让相关功能的代码更加有序的组织在一起。
注
动图原创作者:大帅老猿
setup
是 Vue3
中的一个新的配置项,其值为一个函数,组件中所用到的:数据、方法、计算属性、监视等,均配置在 setup
中
setup
函数返回的对象中的内容,可直接在模板中使用setup
中访问 this
是 undefined
setup
在 beforeCreate
之前setup
可以和选项式API同时存在,选项式API可以访问到 setup
中的属性和方法,反之不可
vue<script lang="ts"> export default { name: 'Person', // setup 中的 this 是undefined setup() { // 数据 -> 此时数据不是响应式 let name = '张三' let age = 18 let tel = '188888' // 方法 function changeName() { name = 'zhangsan' // name值变化,页面不变 } const changeAge = () => { age += 1 } const showTel = () => { alert(tel) } return {name, age, changeName, changeAge, showTel} } } </script>
vue<script lang="ts"> export default { name: 'Person', } </script> <script setup lang="ts"> let name = '张三' let age = 18 let tel = '188888' function changeName() { name = 'zhangsan' } const changeAge = () => { age += 1 } const showTel = () => { alert(tel) } </script>
Vue3
中的 ref
不同于 Vue2
中的 ref
,Vue3
中,ref
是一个函数,从 vue
中引用,const age = ref(18)
,返回一个 RefImpl
的实例对象,简称 ref对象
或 ref
,ref
对象的 value
属性是响应式的
JS
中操作数据需要:xxx.value
,模板中不需要.value
,直接使用let name = ref('张三')
来说,name
不是响应式的,name.value
是响应式的vue<script setup lang="ts"> import { ref } from 'vue' let name = ref('张三') const age = ref(18) let tel = '188888' // 方法 function changeName() { name.value = 'zhangsan' } const changeAge = () => { age.value += 1 } const showTel = () => { alert(tel) } </script>
reactive
定义对象类型的响应式数据,不能用于定义基本类型,但 ref
也可用于定义对象类型,其内部也是调用了 reactive
let 响应式对象= reactive(源对象)
,返回一个 Proxy
的实例对象(简称:响应式对象),定义的响应式数据是“深层次”的
vue<script setup lang="ts"> import { reactive } from 'vue'; let car = reactive({ brand: 'BMW M3 GTR', price: 100 }) let games = reactive([ {id: 'asjelf1', name: 'Titanfall 2'}, {id: 'asjelf2', name: 'NFS Heat'}, {id: 'asjelf3', name: 'Brotato'}, ]) console.log('car', car) const changeCar = () => { car.price += 10 car.brand = 'RSR' } const changeGame = () => { games[0].name = 'Titanfall 3? 不可能辣' } </script>
ref
与 reactive
对比宏观角度:
ref
用来定义:基本类型数据、对象类型数据;reactive
用来定义:对象类型数据。区别:
ref
创建的变量必须使用.value
(可以使用volar
插件自动添加.value
)reactive
重新分配一个新对象,会失去响应式(可以使用Object.assign
去整体替换)推荐使用原则:
ref
ref
、reactive
都可以reactive
toRefs
与 toRef
toRefs
用于在解构 reactive
定义的响应式对象时取得 ref
响应式数据,即:将 reactive
定义的响应式对象变为由 ref
响应式数据组成的对象
toRef
则是取出其中的某一组key-value
vue<script setup lang="ts"> import { reactive, toRefs } from 'vue' let person = reactive({ name: '张三', age: 18 }) let { name, age } = toRefs(person) console.log('toRefs(person)', toRefs(person)) let function changeName() { name.value += '~' } function changeAge() { age.value += 1 } </script>
单向绑定
vue<input type="text" :value="lastName">
双向绑定
vue<input type="text" v-model="firstName">
计算属性有缓存,重复使用时不会重复调用其中的方法
vue<template> <div class="person"> 姓:<input type="text" v-model="firstName"> <br> 名:<input type="text" v-model="lastName"> <br> <button @click="changeFullName">将全名改为lisi</button> <br> 全名:<span>{{fullName}}</span><br> 全名:<span>{{fullName}}</span><br> 全名:<span>{{fullName}}</span><br> </div> </template> <script lang="ts"> export default { name: 'Computed', // setup 中的 this 是undefined } </script> <script setup lang="ts"> import { ref, computed } from 'vue' let firstName = ref('zhang') let lastName = ref('san') // fullName 是计算属性,且是只读的 // let fullName = computed(() => { // return firstName.value.slice(0, 1).toUpperCase() + firstName.value.slice(1) + '-' + lastName.value // }) // fullName 是计算属性,且是可读可写的 let fullName = computed({ get() { return firstName.value.slice(0, 1).toUpperCase() + firstName.value.slice(1) + '-' + lastName.value }, set(changedValue) { const [fN, lN] = changedValue.split('-') firstName.value = fN lastName.value = lN } }) const changeFullName = () => { fullName.value = 'li-si' } </script> <style scoped> .person { background-color: pink; box-shadow: 0 0 10px; border-radius: 6px; padding: 20px; } button { margin: 2px; } </style>
watch
监视数据的变化(与 Vue2 中作用一致)
Vue3 中的 watch
只能监视以下四种数据:
ref
reactive
响应式对象ref
定义的基本类型数据直接写数据名,监视的是其 value
值的改变
vue<template> <div class="sum"> <h1>监视 ref 类型数据</h1> <h2>当前求和为:{{ sum }}</h2> <button @click="add">点击sum+1</button> </div> </template> <script setup lang="ts"> import { ref, watch } from 'vue'; let sum = ref(0); const add = () => { sum.value += 1; }; const stopWatch = watch(sum, (newValue, oldValue) => { console.log(`sum由${oldValue}变为${newValue}`); if (newValue >= 10) { // 停止监视 stopWatch(); } }) </script>
ref
定义的对象类型数据默认情况下不传第三个参数,监视的是对象的地址值,若要监视对象内部属性的变化,需要手动开启深度监视
vue<template> <div class="sum"> <h1>监视 ref 对象类型数据</h1> <h2>姓名:{{ person.name }}</h2> <h2>年龄:{{ person.age }}</h2> <button @click="changeName">修改姓名</button> <button @click="changeAge">修改年龄</button> <button @click="changePerson">修改此人</button> </div> </template> <script lang="ts"> export default { name: 'Sum' } </script> <script setup lang="ts"> import { ref, watch } from 'vue'; let person = ref({ name: '张三', age: 18 }); function changeName() { person.value.name += '~'; } function changeAge() { person.value.age += 1; } function changePerson() { person.value = { name: '李四', age: 50 }; } // 第三个参数为配置项:deep开启深度监视,immediate在初始时立即监视 watch(person, (newValue, oldValue) => { console.log('Person变化', newValue, oldValue); }, { deep: true }) </script>
注意
由于 watch
监视的是对象的地址值,因此,如果直接修改整个 ref
定义的对象,则 newValue
为修改后的值,oldValue
为修改前的值;而如果仅修改对象中的属性值,对象在内存中的地址没有发生变化,因此 newValue
和 oldValue
都为修改后的值。
Vue 3 官方文档:
immediate
:在侦听器创建时立即触发回调。第一次调用时旧值是undefined
。deep
:如果源是对象,强制深度遍历,以便在深层级变更时触发回调。参考深层侦听器。flush
:调整回调函数的刷新时机。参考回调的刷新时机及watchEffect()
。onTrack / onTrigger
:调试侦听器的依赖。参考调试侦听器。once
: 回调函数只会运行一次。侦听器将在回调函数首次运行后自动停止。
reactive
定义的数据隐式创建深层监听,{deep: false}
不可用
vue<script setup lang="ts"> import { reactive, watch } from 'vue'; let person = reactive({ name: '张三', age: 18 }); function changeName() { person.name += '~'; } function changeAge() { person.age += 1; } function changePerson() { // person的地址没有变化,因此 newValue 和 oldValue 仍然相同 Object.assign(person, { name: '李四', age: 50 }); } watch(person, (newValue, oldValue) => { console.log('Person变化', newValue, oldValue); }) </script>
ref
或 reactive
定义的对象中的某个属性getter
函数vue<script setup lang="ts"> import { reactive, watch } from 'vue'; let person = reactive({ name: '张三', age: 18, car: { brand: '奔驰', price: 100 } }); function changeName() { person.name += '~'; } function changeAge() { person.age += 1; } function changePerson() { // person的地址没有变化,因此 newValue 和 oldValue 仍然相同 Object.assign(person, { name: '李四', age: 50 }); } function changeBrand() { person.car.brand = '保时捷' } function changePrice() { person.car.price = 150 } function changeCar() { person.car = { brand: '宝马', price: 160 } } /*watch(() => person.name, (newValue, oldValue) => { console.log('Person变化', newValue, oldValue); }, {deep: true})*/ watch(() => person.car, (newValue, oldValue) => { console.log('Person变化', newValue, oldValue); }, {deep: true}) </script>
有趣的现象
jswatch(person.car, (newValue, oldValue) => {
console.log('Person变化', newValue, oldValue);
})
修改 car
的属性会监视到,而直接修改 car
却监视不到
jswatch(() => person.car, (newValue, oldValue) => {
console.log('Person变化', newValue, oldValue);
})
修改 car
的属性不会监视,而直接修改 car
能够监视到,此时需要开启 deep: true
才能监视到 car
属性的变化(无论是否为 reactive
),因为 person
是 reactive
定义的对象,而 person.car
不是
jswatch([() => person.name, () => person.car.brand], (newValue, oldValue) => {
console.log('Person变化', newValue, oldValue);
}, {deep: true})
立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行。
watch
要明确指出监视的数据watchEffect
不需要明确指出监视的数据vue<template> <div class="sum"> <h2>需求:当护甲低于40或kd大于5时,打印</h2> <h2>当前护甲:{{ armor }}</h2> <h2>当前kd:{{ kd }}</h2> <button @click="changeArmor">armor</button> <button @click="changeKd">kd</button> </div> </template> <script lang="ts"> export default { name: 'Sum' } </script> <script setup lang="ts"> import { ref, watch, watchEffect } from 'vue'; let armor = ref(100); let kd = ref(0); function changeArmor() { armor.value -= 10; } function changeKd() { kd.value += 1; } // watch 实现 /*watch([armor, kd], (value) => { let [newArmor, newKd] = value; if (newArmor <= 40 || newKd >= 5) { console.log('打印'); } })*/ // watchEffect 实现 watchEffect(() => { if (armor.value < 40 || kd.value > 5) { console.log('打印'); } }) </script>
用于注册模板引用
ref
作用于普通 html
标签上,拿到的是 DOM 元素;作用于 Vue
组件上,拿到的是该组件实例,若需要拿到某些属性,需要在该组件中的 setup
中使用 defineExpose
进行暴露
vue<template> <div class="sum"> <h1>中国</h1> <h2 ref="province">山东</h2> <h3>烟台</h3> <button @click="showH2">输出h2元素</button> </div> </template> <script lang="ts"> export default { name: 'City' } </script> <script setup lang="ts"> import { ref } from 'vue'; let province = ref(); function showH2() { console.log(province.value) } </script>
一个 <style>
标签可以使用 scoped
或 module
attribute 来帮助封装当前组件的样式。使用了不同封装模式的多个 <style>
标签可以被混合入同一个组件。
组件间传递数据
App.vue
vue<template> <!-- <City /> --> <!-- <Sum /> --> <!-- <Computed /> --> <Person a="haha" :personList="personList" /> <!-- <Car /> --> </template> <script setup lang="ts"> import Car from './components/Car.vue'; import City from './components/City.vue'; import Computed from './components/Computed.vue'; import Person from './components/Person.vue'; import Sum from './components/Sum.vue'; import { reactive } from 'vue'; import { type Persons } from '@/types'; let personList = reactive<Persons>([ { id: '3456-7890', name: '张三', age: 18 }, { id: '3456-7891', name: '李四', age: 20 }, { id: '3456-7892', name: '王五', age: 22, x: 166 } ]); </script> <script lang="ts"> export default { name: 'App' } </script>
components/Person.vue
vue<template> <div class="person"> <ul> <!-- 不写 key 默认为 index --> <!-- 可以写数组,也可以写遍历的次数 --> <li v-for="person in personList" :key="person.id"> <span>{{ person.name }} - {{ person.age }}</span> </li> </ul> </div> </template> <script lang="ts"> export default { name: 'Person', } </script> <script setup lang="ts"> import type { Persons } from '@/types'; // 直接接收props // let props = defineProps(['a', 'personList']) // 接收props并限制类型 // let props = defineProps<{ personList: Persons }>() // 接收props + 限制类型 + 限制必要性 + 指定默认值 let props = withDefaults(defineProps<{ personList?: Persons }>(), { personList: () => [{ id: '000', name: '康师傅', age: 30 }] }) console.log('props',props) </script>
types/index.ts
typescriptexport interface PersonType {
id: string,
name: string,
age: number,
x?: number
}
export type Persons = PersonType[]
本文作者:Morales
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 License 许可协议。转载请注明出处!