vue3项目:npm init vue@latest
vite.config.js-项目的配置文件 基于vite的配置
package.json-项目包文件 核心依赖项变成了vue3.x和vite
main.js-入口文件 createApp函数创建应用实例
app.vue-跟组件 SFC单文件组件 script-template-style
setup选项的执行时机:beforeCreate钩子之前,自动执行
setup写代码的特点是什么:定义数据+函数 然后以对象方式return
<script setup>解决了什么问题:经过语法糖的封装更简单的使用组合API
setup中的this还指向组件的实例吗:指向undefined
reactive():作用:接收对象类型数据的参数传入并返回一个响应式的对象
ref():作用:接收简单类型或者对象类型的数据传入并返回一个响应式的对象
<script setup>
// //1.导入函数
// import {reactive} from 'vue'
// //2.执行函数传入一个对象类型的参数 变量接收
// const state = reactive ({
// count:0
// })
// const setCount = () => {
// state.count++
// }
//1.导入ref函数
import {ref} from 'vue'
//2.执行函数 传入参数[简单类型+对象类型] 变量接收
const count = ref(0)
const setCount =()=>{
//脚本区域修改ref产生的响应式对象数据 必须通过.value属性
count.value++
}
</script>
<template>
<div>
<button @click="setCount">{{ state.count }}</button>
</div>
</template>
使用ref时button里面直接写count
reactive和ref函数的共同作用:用函数调用的方式生成响应式数据
reactive vs ref:reactive不能处理简单类型的数据;ref参数类型支持更好但是必须通过.value访问修改;ref函数的内部实现依赖于reactive函数
computed函数:
<script setup>
import {ref} from 'vue'
import { computed } from 'vue';
const list = ref([1,2,3,4,5,6,7,8])
const computedlist = computed (() =>{
return list.value.filter(item=>item>2)
})
</script>
<template>
<div>
原始响应式数据-{{ list }}
</div>
<div>
计算属性数值-{{ computedlist }}
</div>
</template>
计算属性只去计算;避免直接修改计算属性的值。
watch函数:侦听一个或者多个数据的变化,数据变化时执行回调函数。immediate(立即执行),deep(深度侦听)
<script setup>
import {ref, watch} from 'vue'
const count = ref(0)
const setCount =() => {
count.value++
}
watch(count,(newVal,oldVal)=>{
console.log('count变化了',newVal,oldVal)
})
</script>
<template>
<div>
<button @click="setCount">+{{ count }}</button>
</div>
</template>
<script setup>
import {ref, watch} from 'vue'
const count = ref(0)
const name = ref('cp')
const changeCount = () =>{
count.value++
}
const changeName = () =>{
name.value='pc'
}
watch([count,name],([newCount,newName],[oldCount,oldName])=>{
console.log('count或者name改变了',[newCount,newName],[oldCount,oldName])
})
</script>
<template>
<div>
<button @click="changeCount">count改变了--{{ count }}</button>
</div>
<div>
<button @click="changeName">name改变了--{{ name }}</button>
</div>
</template>
<script setup>
import { ref, watch } from 'vue';
const count = ref(0)
const setCount = () =>{
count.value++
}
watch(count,()=>{
console.log('count改变了');
},{
immediate:true
})
</script>
<template>
<div>
<button @click="setCount">{{ count }}</button>
</div>
</template>
<script setup>
import { ref, watch } from 'vue';
const state = ref({count:0})
const changeStateByCount = () =>{
state.value.count++
}
watch(state,()=>{
console.log('count改变了')
},{
deep:true
})
</script>
<template>
<div>
<button @click="changeStateByCount">{{ state }}</button>
</div>
</template>
精确监听:
<script setup>
import { ref, watch } from 'vue';
const count = ref({
name:'zhangsan',
age:18
})
const changeName = () =>{
count.value.name = 'lisi'
}
const changeAge = () => {
count.value.age = 20
}
watch(
()=>count.value.name,
()=>count.value.age
)
</script>
<template>
<div>
<button @click="changeName">{{ count.name }}</button>
</div>
<div>
<button @click="changeAge">{{ count.age }}</button>
</div>
</template>
作为watch函数的第一个参数,ref对象需要添加.value吗?不需要,watch会自动读取
watch可以监听单个或多个数据
不开启deep,不能直接修改嵌套属性触发回调
不开启deep,想在某个层次比较深的属性变化执行回调,可以把第一个参数写成函数的写法,返回要监听的具体属性。
组合式API中生命周期函数的格式:on+生命周期名字
组合式API中组件卸载完毕时执行onUnmounted函数
父传子:
<script setup>
//setup语法糖下局部组件无需注册直接可以使用
import SonCom from './son-com.vue'
</script>
<template>
<div class="father">
<h2>父组件App</h2>
<!--1.绑定属性-->
<SonCom message="father message"/>
</div>
</template>
<script setup>
//2.defineProps接收数据
defineProps({
message:String
})
</script>
<template>
<div class="son">
<h3>子组件Son</h3>
<div>
父组件传入的数据-{{ message }}
</div>
</div>
</template>
响应式数据:
<script setup>
//setup语法糖下局部组件无需注册直接可以使用
import SonCom from './son-com.vue'
import { ref } from 'vue';
const count = ref(100)
</script>
<template>
<div class="father">
<h2>父组件App</h2>
<!--1.绑定属性-->
<SonCom :count="count" message="father message"/>
</div>
</template>
<script setup>
//2.defineProps接收数据
defineProps({
message:String,
count:Number
})
</script>
<template>
<div class="son">
<h3>子组件Son</h3>
<div>
父组件传入的数据-{{ message }}-{{ count }}
</div>
</div>
</template>
子传父:
<script setup>
import SonCom from './son-com.vue'
const getMessage = (msg)=> {
console.log(msg)
}
</script>
<template>
<div class="father">
<h2>父组件App</h2>
<!--绑定事件-->
<SonCom @get-message="getMessage"/>
</div>
</template>
<script setup>
const emit = defineEmits(['get-message'])
const sendMsg = () => {
emit('get-message','this is a son message')
}
</script>
<template>
<div class="son">
<h3>子组件Son</h3>
<button @click="sendMsg">触发自定义事件</button>
</div>
</template>
<script setup>
import { onMounted, ref } from 'vue';
import TestCom from './test-com.vue'
const h1Ref = ref(null)
const comRef = ref(null)
onMounted(()=>{
console.log(h1Ref.value)
console.log(comRef.value)
})
</script>
<template>
<h1 ref="h1Ref">我是dom标签h1</h1>
<TestCom ref="comRef"/>
</template>
<script>
import { ref } from 'vue';
import { defineExpose } from 'vue';
const name = ref('test name')
const setName = () => {
name.value = 'test new name'
}
defineExpose({
name,
setName
})
</script>
<template>
<div>我是test组件</div>
</template>
获取模板引用的时机:组件挂在完毕
defineExpose编译宏的作用:显式暴露组件内部的属性和方法
实现跨层传递普通数据:顶层组件通过provide函数提供数据;底层组件通过inject函数获取数据
<script setup>
import { provide } from 'vue';
import RoomMsgItem from './room-msg-item.vue'
import { ref } from 'vue';
import roomMsgComment from './room-msg-comment.vue';
provide('data-key','this is room data')
const count = ref(0)
provide('count-key',count)
setTimeout(()=>{
count.value=100
},3000)
const setCount = () =>{
count.value++
}
provide('setCount-key',setCount)
</script>
<template>
<div class="page">
顶层组件
<RoomMsgItem/>
<roomMsgComment/>
</div>
</template>
<script setup>
</script>
<template>
<div class="item">
中间组件
</div>
</template>
<script setup>
import { inject } from 'vue';
const roomDate = inject('data-key')
const countData = inject('count-key')
const setCountDate = inject('setCount-key')
</script>
<template>
<div class="comment">
底层组件
<div>
来自顶层组件中的数据为:{{ roomDate }}
</div>
<div>
来自顶层组件的响应式数据为:{{ countData }}
</div>
<div>
<button @click="setCountDate">修改顶层组件的count</button>
</div>
</div>
</template>
provide和inject的作用是:跨层组件通信
如何在传递的过程中保持数据响应式:第二个参数传递ref对象
底层组件想要通知顶层组件做修改,需要传递方法,底层组件调用方法
一颗组件树可以存在多个顶层和底层的关系
列表渲染||删除功能||编辑功能
<script setup>
import Edit from './components/Edit.vue'
import {ref,onMounted} from 'vue'
import axios from 'axios';
// TODO: 列表渲染
//思路:声明响应式list -> 调用接口获取数据 -> 后端数据赋值给list -> 绑定到table组件
const list = ref([])
const getList = async () => {
//调用接口
const res = await axios.get('/list')
//赋值给list
list.value = res.data
}
onMounted(()=>getList())
// TODO: 删除功能
//思路:获取当前行的id -> 通过id调用删除接口 -> 更新最新的列表
const onDelete = async (id) => {
console.log(id)
await axios.delete(`/del/${id}`)
getList()
}
// TODO: 编辑功能
//打开弹窗 -> 回填数据 -> 更新数据
//1.打开弹窗(获取子组件实例 调用方法或者修改属性)(需要用到组件edit.vue,将弹窗开关改为true,但是需要在父组件中使用)
//2.回填数据(调用详情接口/当前行的静态数据)(调用open方法的同时直接将数据传下来)
const editRef = ref(null)//获取子组件的属性,用到ref标识
const onEdit = (row) => {
editRef.value.open(row)
}
</script>
<template>
<div class="app">
<el-table :data="list">
<el-table-column label="ID" prop="id"></el-table-column>
<el-table-column label="姓名" prop="name" width="150"></el-table-column>
<el-table-column label="籍贯" prop="place"></el-table-column>
<el-table-column label="操作" width="150">
<template #default="{row}">
<el-button type="primary" @click="onEdit(row)" link>编辑</el-button>
<el-button type="danger" @click="onDelete(row.id)" link>删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
<Edit ref="editRef" @on-Update="getList"/>
</template>
<style scoped>
.app {
width: 980px;
margin: 100px auto 0;
}
</style>
<script setup>
// TODO: 编辑
import { ref } from 'vue'
import axios from 'axios';
// 弹框开关
const dialogVisible = ref(false)
//准备form(利用形参来获取数据)
const form = ref({
name:'',
place:'',
id:''
})
const open = (row) => {//通常使用方法来打开弹窗
dialogVisible.value = true
console.log(row)
form.value.name = row.name
form.value.place = row.place
form.value.id = row.id
}
defineExpose({//子传父(抛出)
open
})
//更新
const emit = defineEmits(['on-Update'])
//1.收集表单数据 调用接口
const onUpdate = async () => {
await axios.patch(`/edit/${form.value.id}`, {
name: form.value.name,
place: form.value.place,
})
//2.关闭弹窗
dialogVisible.value = false
//3.通知父组件做列表更新(触发onUpdate函数)
emit('on-Update')
}
</script>
<template>
<el-dialog v-model="dialogVisible" title="编辑" width="400px">
<el-form label-width="50px">
<el-form-item label="姓名">
<el-input placeholder="请输入姓名" v-model="form.name"/><!--template会自动解包,所以不用写.value-->
</el-form-item>
<el-form-item label="籍贯">
<el-input placeholder="请输入籍贯" v-model="form.place"/>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="onUpdate">确认</el-button>
</span>
</template>
</el-dialog>
</template>
<style scoped>
.el-input {
width: 290px;
}
</style>