赞
踩
vue3中新增了组合式API,本文讲解组合式API setup 的使用
关于setup 的出现和 vue3 js setup 的使用,笔者已经在2022年的文章中说明,这里不再赘述,需要的朋友可以阅读:《vue3 setup 使用教程》
目录
在电脑上的空白文件目录下打开 cmd 窗口,执行下面命令
npm create vue@latest
输入y 按回车
输入项目名 vue3-ts-project
是否使用 ts 语法,选择 是
是否启用 JSX 支持,这个不影响学习,是或否都行,笔者选择 是
是否引入vue router,选择 是
是否使用 Pinia 用于状态管理,选择 是
是否引入 Vitest 用于单元测试,选择 是
是否要引入一款端到端测试工具,选择 不需要
是否引入 ESLint 用于代码质量检测,选择 是
是否引入 Prettier 用于代码格式化,选择 否
创建完成
进入项目目录,安装依赖
cd vue3-ts-project
安装依赖
npm install
依赖安装完成
依赖安装完成后,使用 VS Code 打开项目
执行下面命令运行项目
npm run dev
浏览器访问:http://localhost:5173/
出现这个页面说明项目创建成功
先将 main.css 中的样式替换为下面代码
- @import './base.css';
-
- #app {
- margin: 0 auto;
- padding: 2rem;
- font-weight: normal;
- }
再App.vue 原来的内容全部删除,替换为下面代码
- <template>
- <div>
- <p>{{name}}</p>
- <button @click="change">修改</button>
- </div>
- </template>
-
- <script setup lang="ts">
- let name = '年少相逢意气豪,千金买醉度良宵'
- function change() {
- name = '调筝人去秋风冷,一院梧桐影自摇'
- }
- </script>
-
- <style scoped>
- </style>
运行效果
发现点击按钮,变量不能修改,这是因为默认的 name 不再像vue2 一样默认就是响应式变量,需要使用 ref 或 reactive 函数转换一下,看下面代码
使用 ref
- <template>
- <div>
- <p>{{name}}</p>
- <button @click="change">修改</button>
- </div>
- </template>
-
- <script setup lang="ts">
- import { ref , reactive } from 'vue'
- let name = ref('年少相逢意气豪,千金买醉度良宵')
- function change() {
- name.value = '调筝人去秋风冷,一院梧桐影自摇'
- }
- </script>
-
- <style scoped>
- </style>
运行效果
使用 reactive
- <template>
- <div>
- <p>{{nameObj.name}}</p>
- <button @click="change">修改</button>
- </div>
- </template>
-
- <script setup lang="ts">
- import { ref , reactive } from 'vue'
- let nameObj = reactive({name:'一客若蜀士,相逢意气豪'})
- function change() {
- nameObj.name = '偶谈唐夹寨,遂及楚成皋'
- }
- </script>
-
- <style scoped>
- </style>
运行效果
注意 ref 和 reactive 的区别
ref 一般处理基本类型;reactive 处理复杂的数据类型
- <template>
- <div>
- <p>{{name}}</p>
- <input type="text" v-model="name" />
- </div>
- </template>
-
- <script setup lang="ts">
- import { ref , reactive } from 'vue'
- let name = ref('顿洗风尘恶,都忘箠辔劳')
-
- </script>
-
- <style scoped>
- </style>
运行效果
使用 computed 可实现计算
- <template>
- <div>
- <p>{{numberOfOnlineUsers}}</p>
- <button @click="add">添加在线人数</button>
- </div>
- </template>
-
- <script setup lang="ts">
- import { ref , reactive, computed } from 'vue'
- let name = ref('顿洗风尘恶,都忘箠辔劳')
- let users = ref([])
- const numberOfOnlineUsers = computed(()=>{
- return users.value.length > 0 ? '当前在线人数'+ users.value.length : '无人在线'
- })
- const add = ()=>{
- let date = new Date()
- users.value.push(date.getTime())
- }
- </script>
-
- <style scoped>
- </style>
运行效果
计算属性默认是只读的,可以通过同时提供 getter 和 setter 来创建
- <template>
- <div>
- <p>{{bookInfo}}</p>
- <button @click="add">修改书籍信息</button>
- </div>
- </template>
-
- <script setup lang="ts">
- import { ref , reactive, computed } from 'vue'
- const bookName = ref('三国演义')
- const authorName = ref('罗贯中')
-
- const bookInfo = computed({
- // getter
- get() {
- return bookName.value + ' ' + authorName.value
- },
- // setter
- set(newValue) {
- let tmp = newValue.split(' ')
- bookName.value = tmp[0]
- authorName.value = tmp[1]
- }
- })
- const add = ()=>{
- bookInfo.value = '红楼梦 曹雪芹'
- }
- </script>
-
- <style scoped>
- </style>
运行效果
- <template>
- <div>
- <input type="text" v-model="name">
- </div>
- </template>
-
- <script setup lang="ts">
- import { ref , reactive, watch } from 'vue'
- let name = ref('三国演义')
- watch(name, (newValue, oldValue)=>{
- console.log(oldValue);
- console.log(newValue);
- })
-
- </script>
-
- <style scoped>
- </style>
运行效果
深层侦听器需要添加 deep: true 属性。默认直接给 watch()
传入一个响应式对象,会隐式地创建一个深层侦听器——该回调函数在所有嵌套的变更时都会被触发,相比之下,一个返回响应式对象的函数,只有在返回不同的对象时,才会触发回调
看下面代码
- <template>
- <div>
- <p>{{book.name}}</p>
- <button @click="change">修改</button>
- </div>
- </template>
-
- <script setup lang="ts">
- import { ref , reactive, watch } from 'vue'
- let book = reactive({name:'西游记', author: '施耐庵'})
- watch(()=>book, (newValue)=>{
- console.log(newValue);
- }
- )
-
- const change = ()=> {
- book.name = '道德经'
- }
- </script>
-
- <style scoped>
- </style>
运行效果
可以看到没有触发侦听
添加 深层侦听 后看下面代码
- <template>
- <div>
- <p>{{book.name}}</p>
- <button @click="change">修改</button>
- </div>
- </template>
-
- <script setup lang="ts">
- import { ref , reactive, watch } from 'vue'
- let book = reactive({name:'西游记', author: '施耐庵'})
- watch(()=>book, (newValue)=>{
- console.log(newValue);
- },
- { deep: true }
- )
-
- const change = ()=> {
- book.name = '道德经'
- }
- </script>
-
- <style scoped>
- </style>
运行效果
watch
默认是懒执行的,只有当数据源变化时,才会执行回调 。如果想在创建侦听器时,立即执行一遍回调,可以通过传入 immediate: true
选项来强制侦听器的回调立即执行
- <template>
- <div>
- <input type="text" v-model="name">
- </div>
- </template>
-
- <script setup lang="ts">
- import { ref , reactive, watch } from 'vue'
- let name = ref('三国演义')
- watch(name, (newValue, oldValue)=>{
- console.log(oldValue);
- console.log(newValue);
- },
- { immediate: true }
- )
-
- </script>
-
- <style scoped>
- </style>
运行效果
默认侦听器是每当被侦听源发生变化时,侦听器的回调就会执行。如果想让回调只在源变化时触发一次,可以使用 once: true
选项
- <template>
- <div>
- <input type="text" v-model="name">
- </div>
- </template>
-
- <script setup lang="ts">
- import { ref , reactive, watch } from 'vue'
- let name = ref('三国演义')
- watch(name, (newValue, oldValue)=>{
- console.log(oldValue);
- console.log(newValue);
- },
- { once: true }
- )
-
- </script>
-
- <style scoped>
- </style>
运行效果
watchEffect()
当侦听器的回调使用与源完全相同的响应式状态时,可以使用 watchEffect 简化代码
先看 watch 的代码
- <template>
- <div>
- <input type="text" v-model="name">
- </div>
- </template>
-
- <script setup lang="ts">
- import { ref , reactive, watch, watchEffect } from 'vue'
- let name = ref('三国演义')
- watch(name, ()=>{
- httpGetRequest(name.value)
- },
- { immediate: true }
- )
- //模拟发送请求
- function httpGetRequest(username:string) {
- console.log('发送请求:' + username);
- }
- </script>
-
- <style scoped>
- </style>
运行效果
使用 watchEffect 简化上面 watch 代码
- <template>
- <div>
- <input type="text" v-model="name">
- </div>
- </template>
-
- <script setup lang="ts">
- import { ref , reactive, watch, watchEffect } from 'vue'
- let name = ref('水浒传')
- // watch(name, ()=>{
- // httpGetRequest(name.value)
- // },
- // { immediate: true }
- // )
- watchEffect(()=>{
- httpGetRequest(name.value)
- })
- //模拟发送请求
- function httpGetRequest(username:string) {
- console.log('发送请求:' + username);
- }
- </script>
-
- <style scoped>
- </style>
运行效果
ref
是一个特殊的 attribute,它允许我们在一个特定的 DOM 元素或子组件实例被挂载后,获得对它的直接引用
- <template>
- <div>
- <input ref="inputRef" type="text">
- <button @click="get">获取</button>
- </div>
- </template>
-
- <script setup lang="ts">
- import { ref , reactive, watch } from 'vue'
- const inputRef = ref(null)
-
- const get = ()=> {
- console.log(inputRef.value);
- inputRef.value.focus()
- }
- </script>
-
- <style scoped>
- </style>
运行效果
- <template>
- <div>
- <div :class="{ active: isActive }">
- <p>忽匆匆,三月桃花随水转。</p>
- <p>飘零零,二月风筝线儿断。</p>
- <p>噫,郎呀郎,</p>
- <p>巴不得下一世,你为女来我做男。</p>
- </div>
- <br>
- <div :class="classObject">
- 一朝别后,二地相悬。
- </div>
- <button @click="change">改变</button>
- </div>
- </template>
-
- <script setup lang="ts">
- import { ref, reactive } from 'vue'
- let isActive = ref(true)
- const classObject = reactive({
- active: true,
- 'text-primary': true
- })
- const change = ()=> {
- isActive.value = false
- classObject.active = false
- classObject['text-primary'] = false
- }
- </script>
-
- <style scoped>
- .active {
- background: #f56c6c;
- }
- .text-primary {
- color: #ffff;
- }
- </style>
运行效果
- <template>
- <div>
- <div :class="[activeClass, primaryClass]">
- 万语千言说不完,百无聊赖,十依栏杆。
- </div>
- </div>
- </template>
-
- <script setup lang="ts">
- import { ref, reactive } from 'vue'
- const activeClass = ref('active')
- const primaryClass = ref('text-primary')
- </script>
-
- <style scoped>
- .active {
- background: #f56c6c;
- }
- .text-primary {
- color: #ffff;
- }
- </style>
运行效果
- <template>
- <div>
- <!-- 三目表达式单独使用 -->
- <div :class="[isActive ? activeClass : primaryClass]">
- <p>六月三伏天,人人摇扇我心寒。</p>
- <p>五月石榴红似火,偏遇阵阵冷雨浇花端。</p>
- </div>
-
- <br>
-
- <!-- 三目表达式和其他样式一起使用 -->
- <div :class="[isActive ? successBackgroundClass : primaryBackgroundClass, textClass]">
- <p>六月三伏天,人人摇扇我心寒。</p>
- <p>五月石榴红似火,偏遇阵阵冷雨浇花端。</p>
- </div>
- <button @click="change">修改</button>
- </div>
- </template>
-
- <script setup lang="ts">
- import { ref, reactive } from 'vue'
- let isActive = ref(true)
- const activeClass = ref('active')
- const primaryClass = ref('text-primary')
-
- const textClass = ref('text-class')
- const successBackgroundClass = ref('success-background')
- const primaryBackgroundClass = ref('primary-background')
- const change = ()=> {
- isActive.value = false
- }
- </script>
-
- <style scoped>
- .active {
- color: #67c23a;
- }
- .text-primary {
- color: #409eff;
- }
- .text-class {
- color: #ffff;
- }
- .success-background {
- background: #67c23a;
- }
- .primary-background {
- background: #409eff;
- }
- </style>
运行效果
- <template>
- <div>
- <!-- 绑定对象 -->
- <div :style="{ color: activeColor, fontSize: fontSize + 'px' }">
- 十里长亭望眼欲穿。百思想,千系念,万般无奈把郎怨。
- </div>
- <br>
- <div :style="styleObject">
- 七弦琴无心弹,八行书无可传。
- </div>
- <br>
- <!-- 绑定数组 -->
- <div :style="[styleObject, backStyles]">
- 四月枇杷未黄,我欲对镜心意乱。
- </div>
- </div>
- </template>
-
- <script setup lang="ts">
- import { ref, reactive } from 'vue'
- const activeColor = ref('#67c23a')
- const fontSize = ref(30)
- const styleObject = reactive({
- color: '#409eff',
- fontSize: '18px'
- })
- const backStyles = reactive({
- background: 'black'
- })
- </script>
-
- <style scoped>
- </style>
运行效果
官网 实例生命周期的图表
在setup 中引入生命周期函数使用
- <template>
- <div>
- <p>{{name}}</p>
- <button @click="change">修改</button>
- </div>
- </template>
-
- <script setup lang="ts">
- import { ref, onMounted,onUpdated } from 'vue'
- let name = ref('阅尽天涯离别苦,不道归来,零落花如许。')
- const change = ()=> {
- name.value = '花底相看无一语,绿窗春与天俱莫。'
- }
-
- onMounted(() => {
- console.log('挂载完成')
- })
-
- onUpdated(()=>{
- console.log('更新完成');
- })
- </script>
-
- <style scoped>
- </style>
运行效果
更多生命周期函数可以看官网文档:https://cn.vuejs.org/api/composition-api-lifecycle.html
在 components 目录下定义子组件 Book.vue
代码如下
- <template>
- <div>
- <p>书名:{{bookName}}</p>
- <p>作者:{{author}}</p>
- <p>价格:{{price}}</p>
- </div>
- <button @click="buy">下单</button>
- <button @click="cart">加入购物车</button>
- </template>
- <script setup lang="ts">
- //父传子定义props
- const props = defineProps({
- bookName: String,
- author: {
- type: String,
- //必传
- required: true
- },
- price: Number
- })
- //定义子传父事件
- const emit = defineEmits(['buyEmit', 'cartEmit'])
- const buy = ()=> {
- emit('buyEmit', props.bookName)
- }
- const cart = ()=> {
- emit('cartEmit')
- }
- </script>
在 App.vue 中引入 Book.vue
- <template>
- <div>
- <Book :bookName="name" :author="author" :price="price" @buyEmit="buy" @cartEmit="cart" />
- </div>
- </template>
-
- <script setup lang="ts">
- import { ref } from 'vue'
- import Book from '@/components/Book.vue'
- let name = ref('稼轩长短句')
- let author = ref('辛弃疾')
- let price = ref(50)
-
- const buy = (bookName)=> {
- alert(bookName)
- }
- const cart = ()=> {
- alert('点击购物车')
- }
- </script>
-
- <style scoped>
- </style>
运行效果
- <template>
- <div>
- <p>书名:{{bookName}}</p>
- <p>作者:{{author}}</p>
- <p>价格:{{price}}</p>
- </div>
- <button @click="buy">下单</button>
- <button @click="cart">加入购物车</button>
- </template>
- <script setup lang="ts">
- //父传子定义props
- const props = defineProps({
- bookName: String,
- author: {
- type: String,
- //必传
- required: true
- },
- price: Number
- })
- //定义子传父事件
- const emit = defineEmits<{
- (e: 'buyEmit', bookName:String):void
- (e: 'cartEmit'):void
- }>()
- const buy = ()=> {
- emit('buyEmit', props.bookName)
- }
- const cart = ()=> {
- emit('cartEmit')
- }
- </script>
- <template>
- <div>
- <p>书名:{{bookName}}</p>
- <p>作者:{{author}}</p>
- <p>价格:{{price}}</p>
- </div>
- <button @click="buy">下单</button>
- <button @click="cart">加入购物车</button>
- </template>
- <script setup lang="ts">
- //父传子定义props
- const props = defineProps({
- bookName: String,
- author: {
- type: String,
- //必传
- required: true
- },
- price: Number
- })
- //定义子传父事件
- const emit = defineEmits({
- //校验 buyEmit 事件
- buyEmit:(bookName:String) => {
- if(bookName.length > 1) {
- console.log('buyEmit error');
-
- return false
- } else {
- return true
- }
- },
- //没有校验
- cartEmit: null
- })
-
-
- const buy = ()=> {
- emit('buyEmit', props.bookName)
- }
- const cart = ()=> {
- emit('cartEmit')
- }
- </script>
运行效果
在 App.vue 中提供使用 provide
- <template>
- <div>
- <Book :bookName="name" :author="author" :price="price" />
- </div>
- </template>
-
- <script setup lang="ts">
- import { ref, provide } from 'vue'
- import Book from '@/components/Book.vue'
- let name = ref('史记')
- let author = ref('司马迁')
- let price = ref(399)
-
- provide(/* 注入名 */ 'bookShop', /* 值 */ '开心图书商店')
- </script>
-
- <style scoped>
- </style>
在子组件 Book.vue 中注入使用 inject
- <template>
- <div>
- <h1>{{bookShop}}</h1>
- <p>书名:{{bookName}}</p>
- <p>作者:{{author}}</p>
- <p>价格:{{price}}</p>
- </div>
-
- </template>
- <script setup lang="ts">
- import { inject } from 'vue'
- const bookShop = inject('bookShop')
- //父传子定义props
- const props = defineProps({
- bookName: String,
- author: {
- type: String,
- //必传
- required: true
- },
- price: Number
- })
- </script>
运行效果
依赖注入更详细讲解请阅读笔者文章《vue 依赖注入使用教程》
至此完
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。