1.参照
ref, isRef, shallowRef, triggerRef, customRef
- ref は es6 のクラスを返します。値と変更には .value を追加する必要があります。
- Ref とshallowRef を一緒に書き込むことはできません。その場合、 shallowRef のビューが更新されます。
- ref = 浅い参照 + トリガー参照
<template>
<div class="home">
Ref: {
{
student1.name}}<br>
ShallowRef: {
{
student2.name}}<br>
CustomRef: {
{
student3}}<br>
<div ref="myDiv">我是一个div</div>
<button @click="updateStudentName">修改</button>
</div>
</template>
<script setup lang="ts">
import {
ref, isRef, shallowRef, triggerRef, customRef } from 'vue'
const student1 = ref({
name: '小花1' })
const student2 = shallowRef({
name: '小花2' })
function myRef<T> (value:T) {
return customRef((track, trigger) => {
let timer:any
return {
get () {
track() // 收集依赖
return value
},
set (newValue:T) {
clearInterval(timer)
timer = setTimeout(() => {
timer = null
console.log('触发一次调用一次')
value = newValue // 触发依赖
trigger()
}, 500)
}
}
})
}
const student3 = myRef<string>('小花3')
const myDiv = ref<HTMLDivElement>() // 变量名需要与dom一致
// console.log(myDiv.value?.innerText) dom未渲染,打印无数据
const updateStudentName = () => {
// student.value.name = '小明1'
student2.value.name = '小明2' // 这里如果响应函数里有ref,则shallowRef也会同时刷新 (student,注释掉就不会更新)
// student2.value = { name: '小明2' } // 如果想要更新视图需要在value层修改
triggerRef(student2) // 强制更新shallowRef
student3.value = '小明3'
console.log(myDiv.value?.innerText)
// console.log(student1)
// console.log('判断是否是ref', isRef(student1))
// console.log(student2)
}
</script>
二、Reactive
reactive, readonly, shallowReactive
- ref はすべての型をサポートし、reactive は参照型のみをサポートします配列オブジェクト マップ セット
- リアクティブな値の割り当てには.valueは必要ありません
- reactive はプロキシであるため、直接割り当てることはできません。そうでないと、応答性の高いオブジェクトが破棄されます。
- readonly は読み取り専用であり、readonly によって割り当てられた変数値を変更できません。元の変数は変更できます。変更すると、readonly によって割り当てられた変数の値のみが変更されます。
- ShallowReactive 浅い複数レベルのオブジェクトは最初のレベルにのみ影響します
<template>
<div class="about">
<form>
姓名:<input v-model="student1.name">
</form>
<ul>
<li :key="index" v-for="(item,index) in student2">{
{
item}}</li>
</ul>
shallowReactive:{
{
student5}}
<button @click="submitStudent">修改</button>
</div>
</template>
<script setup lang="ts">
import {
ref, reactive, readonly, shallowReactive } from 'vue'
const student1 = reactive({
name: '小花' })
const student2 = reactive<string[]>([])
const student3 = reactive<{
arr:string[]}>({
arr: [] })
const student4 = reactive({
name: '小红' })
const readonlyS4 = readonly(student3)
// readonlyS4.name = ''
const student5 = shallowReactive({
obj1: {
obj2: {
name: '小花'
}
}
})
const submitStudent = () => {
// student1.name = '小明'
// setTimeout(() => {
// const res = ['1', '2', '3']
// // student2 = res //不是响应式对象,视图不会更新
// student2.push(...res) // 响应式对象,视图更新
// student3.arr = res // 响应式对象,视图更新
// }, 2000)
student5.obj1.obj2.name = '小明' // 视图不会改变(不能和reactive一起使用)
student5.obj1 = {
obj2: {
name: '小明' } } // 视图改变
console.log(student5)
}
</script>
3.シリーズへ
toRaw, toRef, toRefs
- toRef は応答性のあるオブジェクトの値のみを変更でき、非応答性のビューは変更されません。
- toRefs の分解
- toRaw 応答性から非応答性まで
<template>
<div class="about">
<h1>{
{
student1}}</h1>
<h1>{
{
name}}--{
{
age}}--{
{
sex}}</h1>
<button @click="updateStudent">修改</button>
</div>
</template>
<script setup lang="ts">
import {
reactive, toRaw, toRef, toRefs } from 'vue'
// toRef 只能修改响应式对象的值,非响应式视图不会变化
// toRefs 解构
// toRaw 响应式改成非响应式
const student1 = reactive({
name: '小花', age: 12, sex: '女' })
const age = toRef(student1, 'age')
const {
name, sex } = toRefs(student1)
const student2 = reactive({
name: '小花', age: 12, sex: '女' })
const updateStudent = () => {
age.value = 13
name.value = '小明'
sex.value = '男'
console.log(student1, toRaw(student2))
}
</script>
四、Computed、Watch、WatchEffect
計算された
<input v-model="name1"><br>
<input v-model="name2"><br>
<input v-model="name3.obj1.obj2.name"><br>
<input v-model="name4.obj1.obj2.name"><br>
const one = ref('')
const tow = ref('')
const split1 = computed(() => {
return `${
one.value}--${
tow.value}`
})
const split2 = computed({
get () {
return `${
one.value}--${
tow.value}`
},
set () {
//
}
})
時計
- watch はリアクティブなオブジェクトのみを監視できます
- 監視監視参照型で監視する新値と旧値が同じである
- watch は、reactive でラップされた深いオブジェクトを監視します。深いオブジェクトを追加することはできません
const name1 = ref('小花1')
const name2 = ref('小花2')
const name3 = ref({
obj1: {
obj2: {
name: '小花3'
}
}
})
const name4 = reactive({
obj1: {
obj2: {
name: '小花4'
}
}
})
// 监听单个
watch(name1, (n, o) => {
console.log('watch', n, o)
})
// 监听多个 newValue oldValue 也是数组对应监听数组
watch([name1, name2], (n, o) => {
console.log('watch', n, o)
})
// 深度监听 立即执行
watch(name3, (n, o) => {
console.log('watch', n, o)
},
{
deep: true, // 深度监听
immediate: true // 立即执行
})
// 监听对象单一属性(回调函数)
watch(() => name4.obj1.obj2.name, (n, o) => {
console.log('watch', n, o)
})
// 监听对象单一属性 (toRef)
const toName = toRef(name4.obj1.obj2, 'name')
watch(toName, (n, o) => {
console.log('watch', n, o)
})
ウォッチエフェクト
- watchEffect コールバック関数内に書き込まれた内容はすべて監視されます。
- watchEffect はリスニングを停止する関数を返します
<input id="message1" v-model="message1"><br>
<input v-model="message2"><br>
<button @click="stopWatchEffect">停止监听</button>
const message1 = ref('watchEffect1')
const message2 = ref('watchEffect2')
const stopWatchEffect = watchEffect((oninvalidate) => {
oninvalidate(() => {
console.log('我最先执行,一开始不执行,监听值变化才执行')
})
const message1Dom:HTMLInputElement = document.getElementById('message1') as HTMLInputElement
console.log('message1Dom', message1Dom)
console.log('message1', message1.value)
console.log('message2', message2.value)
}, {
flush: 'post', // pre 组件更新前执行 sync强制效果始终同步触发 post组件更新后执行
onTrigger (e) {
console.log('进入调试模式', e)
}
})
5. 親コンポーネントと子コンポーネント間で値を渡す
defineProps、defineEmits、defineExpose、および withDefaults を使用すると、コンパイラ マクロ
ESLint は、「defineEmits' が定義されていません。(no-undef)」というエラーを報告します。
最初に ESlint のバージョンを確認してください。npm list eslint-plugin-vue
module.exports = {
// 版本为 v8.0.0或以上
env: {
node: true,
'vue/setup-compiler-macros': true
},
// 版本为 v8.0.0以下
globals: {
defineProps: "readonly",
defineEmits: "readonly",
defineExpose: "readonly",
withDefaults: "readonly",
}
}
親コンポーネント
<template>
<div>
<h1>我是父亲</h1>
<button @click="getChild">获取子组件暴露的方法和属性</button>
<hr>
<Child ref="childRef" :sendChildVal="sendChildVal" @sendFather="sendFather" :arr="[1,2,3]"></Child>
</div>
</template>
<script setup lang="ts">
import Child from '@/components/Child.vue'
import {
nextTick, onMounted, ref } from 'vue'
const sendChildVal = '我是父组件定义的数据'
const sendFather = (value:string) => {
console.log(value)
}
// 获取子组件暴露的方法和属性
const childRef = ref<InstanceType<typeof Child>>() // 必须先定义
const getChild = () => {
console.log(childRef.value.childName)
console.log(childRef.value.open())
}
onMounted(() => {
// nextTick onMounted 中调用
console.log(childRef.value.childName)
console.log(childRef.value.open())
})
</script>
サブアセンブリ
<template>
<div>
<h1>我是儿子</h1>
<h1>{
{
sendChildVal }}</h1>
<h1>{
{
arr}}</h1>
<h1>{
{
defaultValue}}</h1>
<button @click="sendFather">传值给父组件</button>
</div>
</template>
<script setup lang="ts">
// 接收值
// const props = defineProps({ //普通写法
// sendChildVal: {
// type: String,
// default: '默认值'
// }
// })
// let props = defineProps<{ //ts写法
// sendChildVal:string,
// arr:number[],
// defaultValue:string
// }>()
// console.log(props.sendChildVal)
withDefaults(defineProps<{
sendChildVal:string,
arr:number[],
defaultValue:string
}>(), {
defaultValue: () => '默认值'
})
// 传值
// const emit = defineEmits(['sendFather']) //普通写法
const emit = defineEmits<{
(e:'sendFather', message:string):void
}>()
const sendFather = () => {
emit('sendFather', '我是子组件发送过来的值')
}
// 暴露属性方法给父组件
defineExpose({
childName: '我是子组件属性',
open: () => console.log('我是子组件方法')
})
</script>
6. 再帰コンポーネント、動的コンポーネント、非同期コンポーネント
再帰的なコンポーネント
// 父组件定义传值并使用
<Tree :data="TreeData"></Tree>
interface TreeInterface {
name:string,
checked:boolean,
children?:TreeInterface[]
}
const TreeData = reactive<TreeInterface[]>([
{
name: '1-1',
checked: false,
children: [
{
name: '1-1-1',
checked: false
},
{
name: '1-1-2',
checked: false,
children: [{
name: '1-1-2-1',
checked: false
}]
}
]
},
{
name: '1-2',
checked: false
},
{
name: '1-3',
checked: false,
children: [
{
name: '1-3-1',
checked: false
}
]
}
])
//子组件递归
<template>
<div>
<div class="tree" v-for="(item,i) in data" :key="i">
<input type="checkbox" v-model="item.checked"> <span>{
{
item.name}}</span>
<Tree v-if="item?.children?.length" :data="item.children"></Tree>
</div>
</div>
</template>
<script setup lang="ts">
interface TreeInterface {
name:string,
checked:boolean,
children?:TreeInterface[]
}
withDefaults(defineProps<{
data:TreeInterface[]
}>(), {
data: () => [
{
name: '1-1',
checked: false
}
]
})
</script>
<style>
.tree{
margin-left: 30px;
}
</style>
動的コンポーネント
<div @click="handleClick(item.component)" v-for="(item,i) in tabList" :key="i">
<div>{
{
item.name}}</div>
</div>
<component class="active-tab" :is="activeTab"></component>
const activeTab = shallowRef(Child) // 节约性能
const tabList = reactive([
{
name: '普通组件',
component: markRaw(Child) // 不做Proxy代理
},
{
name: '递归组件',
component: markRaw(Tree)
}
])
const handleClick = (com) => {
activeTab.value = com
}
非同期コンポーネントの
アプリケーション シナリオ: スケルトン画面には、データを非同期でロードする必要がある
ナレッジ ポイントdefineAsyncComponent
とコンポーネントが含まれます。Suspense
<template>
<div>
<div style="width: 100px;height: 20px;">{
{
data}}</div>
</div>
</template>
<script setup lang="ts">
import {
ref } from 'vue'
const axios = {
get <T> (url:string) {
return new Promise((resolve) => {
const xhr = new XMLHttpRequest()
xhr.open('GET', url)
xhr.onreadystatechange = () => {
if (xhr.readyState == 4 && xhr.status == 200) {
// JSON.parse(xhr.responseText)
setTimeout(() => {
resolve('成功')
}, 1000)
}
}
xhr.send()
})
}
}
const data = ref('')
await axios.get('https://img2.baidu.com/it/u=3202947311,1179654885&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1678035600&t=3bc1ca76995dd635b10b7e1d5d648cfa')
data.value = 'hello'
</script>
<style>
</style>
ロード中にデータが表示されたままのコンポーネント
<template>
<div>
<div style="width: 100px;height: 20px;background-color: red"></div>
</div>
</template>
<script setup lang="ts">
</script>
<style>
</style>
非同期コンポーネントを呼び出す
<template>
<div>
<!-- vue3内置组件 -->
<Suspense>
<template #default>
<Sync></Sync> //需要异步加载数据的组件
</template>
<template #fallback>
<Show></Show> // 数据还在加载中显示的组件
</template>
</Suspense>
</div>
</template>
<script setup lang="ts">
const Sync = defineAsyncComponent(() => import('@/components/Sync.vue')) // 需要异步加载数据的组件
// const SyncVue = defineAsyncComponent({
// loadingComponent: () => import('@/components/Sync.vue'),
// errorComponent:'',
// timeout:''
// })
</script>
七、TypeScript
构造函数是大写的,小写是构造函数的实例化对象
ベースタイプ
number : NaN、Infiniy (無限大)、さまざまな基数を定義できます
void : null (TS の厳密モードでエラーが報告されます)、未定義を定義できます。一般に関数に使用されます。
nullと未定義には代入を散在させることができます (厳密モードでは、オフになります)
いかなるタイプ
any: 任意の型に割り当てることができます。
不明(unknown): それ自体または any にのみ割り当てることができ、属性を読み取る方法がなく、メソッドを呼び出すことができません。
インターフェースとオブジェクトタイプ
インターフェイス
1. 重複した名前が見つかった場合、属性はマージされます
2. 継承によって属性もマージされます。
interface Test{
name:string
age:number
sex?:string //可选属性
readonly cb:()=>boolean // 只读
readonly id:number // 只读
[propName:string]:any // 索引签名,用于未确定的属性
}
// 约束函数
interface Fu{
(name:string):number[]
}
let fu:Fu = (name:string)=>{
return [1]
}
残りのパラメータは配列の型を定義します
function test (...args:number[]){
console.log(args) // [1,2,3]
}
test(1,2,3)
関数の種類
// 默认参数和可选参数不可在同一参数只使用
let test = ( a:number = 10, b?:number)=> a + b
test()
// typeScript可以定义this的类型 在js中无法使用 必须是第一个参数定义this类型
interface Obj{
num:number
add:(this:Obj,num:number)=>void
}
let obj:Obj = {
num:1,
add(this:Obj, val:number){
this.num += val
}
}
obj.add(1)
共用体型
interface People{
name:string,
age:number
}
interface Student{
sno:number
}
let fu = (student:People & Student)=> ''
fu({
name:'小明',age:12,sno:18023569})
DOM を定義する
let div:NodeListOf<HTMLInputElement | HTMLDivElement> = document.querySelectorAll('div')
8. ピニア
npm install --save pinia
mian.js のインストールで導入される
import {
createApp } from 'vue'
import './style.css'
import App from './App.vue'
import Antd from 'ant-design-vue';
import 'ant-design-vue/dist/antd.css';
import {
createPinia } from "pinia";
const pinia = createPinia();
let app = createApp(App)
app.use(Antd)
app.use(pinia);
app.mount('#app')
userStoreの作成(/src/store/user.js)
import {
defineStore } from 'pinia'
export const useUserStore = defineStore("user",{
state:()=>{
return{
name:'小明',
age:21,
sex:'男',
}
},
getters:{
getAge:(state)=>{
return (num)=> state.age + num
}
},
actions:{
updateName(name){
this.name = name
}
}
})
コンポーネントA
<script setup lang="ts">
import {
useUserStore } from './store/user.js'
import Child from "./view/ComponentB.vue";
let userStore = useUserStore()
</script>
<template>
<div>组件A</div>
<span>{
{
userStore.name}}</span>-
<span>{
{
userStore.age}}</span>-
<span>{
{
userStore.sex}}</span>-
<!-- 计算属性 -->
<span>{
{
userStore.getAge(20)}}</span>
<ComponentB></ComponentB>
</template>
<style scoped>
</style>
コンポーネントB
<script setup>
import {
useUserStore} from "../store/user.js";
let userStore = useUserStore()
let handleClick = ()=>{
userStore.name = '张三'
console.log(userStore.name)
}
// 全部重置
let reset = ()=>{
userStore.$reset()
}
// 批量修改
let batchUpdate = ()=>{
userStore.$patch({
name:'粒子',
age:100,
sex:'女',
})
}
let updateName = ()=>{
userStore.updateName('小花')
}
</script>
<template>
<div>组件B</div>
<a-button @click="handleClick">点击修改</a-button>
<a-button @click="reset">重置数据</a-button>
<a-button @click="batchUpdate">批量修改数据</a-button>
<a-button @click="updateName">修改名字</a-button>
</template>
<style scoped>
</style>