vue3学习记录

在这里插入图片描述
注意:在vue3中setup中不要使用this,this未指向当前的组件实例,在setup被调用之前,data,methods,computed等都没有被解析,但是组件实例确实在执行setup函数之前就已经被创建好了。照理来说通过new Vue()创建vue实例后应该进入beforeCreate生命周期,但是setup的执行时机是在beforeCreate之前的,此时this是undefined

setup函数只会在组件初始化的时候执行一次。
setup函数在beforeCreated生命周期钩子执行之前执行,后期根据组件的声明周期或者自己写的逻辑执行对应的方法更新
vue中用instanceof判断props类型是否一致

vue3增加的新特性汇总
(1)ref reactive computed watch
(2)新的生命周期
(3)自定义钩子函数
(4)Teleport(组件位置替换)
(5)Suspense(异步加载组件实现)
(6)全局Api、配置项的优化
(7)更好的支持typescript

vue3 响应式核心:ref reactive computed watch等
1、vue3中ref的使用(一般定义简单类型)
(1)定义响应式数据,通过.value来访问
(2)ref 获取元素

定义响应式数据

// 当setup写在script标签中的时候
<template>
  <div ref="dom"></div>
</template>

<script setup>
import { ref, onMounted } from "vue";
const dom = ref();  // 声明响应式数据,通过.value获取值
onMounted(() => {
  console.log(dom.value);
});
</script>

// 当setup是单独写的时候,以函数的形式
import { ref, onMounted } from "vue";
export default {
	setup() {
		const dom = ref();   // 声明响应式数据,通过.value获取值
		onMounted(() => {
			 console.log(dom.value);
		});
		return { dom }  // 需要返回
	}
}
</script>

获取元素

// 获取子组件实例
<template>
  <div>
  <button @cilck="show"><button>
  <Dialog ref="dialog"></Dialog>  // 子组件
  </div>
</template>

<script>
import { reactive, toRefs,ref } from "vue";
import Dialog from "../components/Dialog.vue";
export default {
  components: {
    Dialog,
  },
  setup() {
    const state = reactive({
      count: 0,
    });
     const dialog=ref(null); //不要忘记return出去,要么获取不到值(子组件实例)
     //该方法显示子组件
    const show = () => {
    //调用dialog组件里面的showDialog方法
      dialog.value.showDialog(); //dialog.value.show=true;不能自己修改组件里面的值
      console.log(dialog.value)
    };
    return {
      ...toRefs(state),
      show,
      dialog
    };
  },
};
</script>
<style lang="scss" scoped></style>

// 获取标签元素
<template>
  <h2>App</h2>
  <input type="text">---
  <input type="text" ref="inputRef">
</template>

<script lang="scss" scoped>
import { onMounted, ref } from 'vue'
/* 
功能需求: 让输入框自动获取焦点
*/
export default {
  setup() {
    const inputRef = ref(null)
    onMounted(() => {
      inputRef.value && inputRef.value.focus()
    })

    return {
      inputRef
    }
  },
}
</script>

2、vue3中reactive及与ref的区别
reactive:返回对象的响应式代理, 是 Vue3 中提供的实现响应式数据的方法,参数是对象,基本类型无法被创建成proxy,无法实现监听(输出的值确实发生的变化但不会响应在界面上),它返回源对象的代理,与源对象是不相等的。只有代理是响应式的,更改源对象不触发更新

// 定义及访问
setup() {
    let msg = reactive([1, 2, 3])
    function c() {
      console.log(msg); // 直接访问,无需跟ref一样加.value
      msg[0] += 1;
      msg[1] = 5;
    }
    return {
      msg,
      c
    };
  }

相同:都是用来定义响应式数据
区别:
1)ref更推荐定义简单类型的响应式,reactive推荐定义复杂类型
2)ref定义的数据使用时通过.value,模板中不需要,reactive中模板与实例中都不需要,直接访问
3)用 reactive() 创建的响应式对象,整个对象是响应式的,而对象里的每一项都是普通的值,当你把它用展开运算符展开后,整个对象的普通值都不是响应式的。而用 ref() 创建的响应式的值本身就是响应式的,并不依赖于其他对象,所以需要展开 reactive() 创建的响应式对象,又不想让他们失去响应式特点的时候,就需要用 toRefs() 将它进行转换
…toRefs(state) // state是react定义的数据

3、vue3生命周期钩子(setup在beforeCreate之前调用,调用时组件实例已创建,但获取不到this)
在这里插入图片描述

<script setup>
import { onMounted } from 'vue'
onMounted(() => {
  console.log(`the component is now mounted.`) // 已经挂载
})
</script>

setup也是组件的选项,它是组合式api的入口,在 setup() 函数中返回的对象会暴露给模板和组件实例。其他的选项也可以通过组件实例来获取 setup() 暴露的属性,setup() 自身并不含对组件实例的访问权,即在 setup() 中访问 this 会是 undefined。你可以在选项式 API 中访问组合式 API 暴露的值,但反过来则不行

注意:在【options API】选项型api中,生命周期钩子是被暴露在vue实例上的选项,我们只需要调用使用即可。在【composition API】组合式api中,我们需要将生命周期钩子导入项目,然后才能使用。

import {onMounted} from 'vue'

创建 ---- 在组建创建时执行
挂载 ---- DOM被挂载时执行
更新 ---- 当响应数据被修改时执行
销毁 ---- 在元素被销毁之前立即执行

在vue3中beforecreate和created被setup方法本身所替代,我们在在setup中将会访问到9个生命周期

onBeforeMount:在挂载之前被调用,渲染函数render首次被调用
onMounted:组件挂载时调用
onBeforeUpdate:数据更新时调用,发生在虚拟DOM打补丁之前。
onUpdated:因数据更改导致的虚拟DOM重新渲染和打补丁时调用
onBeforeUnmount:在卸载组件实例之前调用,此阶段的实例依旧是正常的。
onActivated:被keep-alive缓存的组件激活时调用
onDeactivated:被keep-alive缓存的组件停用时调用
onErrorCaptured:当捕获一个来自子孙组件的错误时被调用,有三个参数:错误对象、发生错误的组件实例、一个包含错误来源信息的字符串;此钩子会返回false来阻止改错误继续向上传播。

<script>
import { onMounted } from 'vue'
 export default {
 	setup(){
 		onMounted(()=>{
 			console.log('生命周期----onMounted')
 		})
 	}
 }
</script>

在组合API中,使用setup()方法替换了beforeCreate和created,那么在这两个生命周期中的方法将放在setup中执行
beforeMount()和onBeforeMount(),在组件DOM实际渲染之前调用,此时根元素还不存在,在选项API中,可使用this.$el来 访问;在组合API中,想访问的话就必须在根元素上使用ref。
(1)beforeMount()和onBeforeMount()在渲染前调用,这个钩子被调用时,组件已经完成了其响应式状态的设置,但还没有创建 DOM 节点。它即将首次执行 DOM 渲染过程
(2)mounted和onMounted 钩子可以用来在组件完成初始渲染并创建 DOM 节点后运行代码,当调用 onMounted 时,Vue 会自动将回调函数注册到当前正被初始化的组件实例上。这意味着这些钩子应当在组件初始化时被同步注册,允许直接操作dom
(3)beforeUpdate()和onBeforeUpdate()数据更新时调用,发生在虚拟DOM打补丁之前。beforeUpdate对于跟踪对组件的编辑次数,甚至跟踪创建撤销功能的操作很有用。
(4)updated()和onUpdated()DOM更新后,updated的方法就会调用
(5)beforeUnmount()和onBeforeUnmounted()在卸载组件实例之前调用
(6)unmounted()和onUnmounted()卸载组件实例后调用,调用此钩子时,组件实例的所有指令都被解除绑定,所有事件侦听器都被移除,所有子组件实例被卸载。
(7)actived()和onActivated()被keep-alive缓存的组件激活时调用
(8)deactivated()和onDeactivated()被keep-alive缓存的组件停用时调用
(9)onRenderTracked和onRenderTriggered调试钩子

export default {
    onRenderTriggered(e) {
       debugger
       // 检测什么依赖造成了组件的重新渲染
    }
}

4、setup():setup() 钩子是在组件中使用组合式 API 的入口.在 setup() 函数中返回的对象会暴露给模板和组件实例,setup() 自身并不含对组件实例的访问权,即在 setup() 中访问 this 会是 undefined。你可以在选项式 API 中访问组合式 API 暴露的值,但反过来则不行。

// setup第一参
export default {
  props: {
    title: String
  },
  setup(props) { // 第一个参数是组件的 props,是响应式的,变更后同步更新。不要用扩展运算符否则丢失响应特性 
    console.log(props.title)
  }
}
// 解决解构带来的响应式问题
import { toRefs, toRef } from 'vue'

export default {
  setup(props) {
    // 将 `props` 转为一个其中全是 ref 的对象,然后解构
    const { title } = toRefs(props)
    // `title` 是一个追踪着 `props.title` 的 ref
    console.log(title.value)
    // 或者,将 `props` 的单个属性转为一个 ref
    const title = toRef(props, 'title')  // 此时title同样追踪props.title
  }
}

//setup 第二参:参数是一个 Setup 上下文对象。上下文对象暴露了其他一些在 setup 中可能会用到的值
export default {
  setup(props, context) {
    // 透传 Attributes(非响应式的对象,等价于 $attrs)透传属性指的是传递给一个组件,却没有被该组件声明为 props 或 emits 的 attribute 或者 v-on 事件监听器。最常见的例子就是 class、style 和 id
    console.log(context.attrs)

    // 插槽(非响应式的对象,等价于 $slots)
    console.log(context.slots)

    // 触发事件(函数,等价于 $emit)
    console.log(context.emit)

    // 暴露公共属性(函数)
    console.log(context.expose)
  }
}

暴露公共属性:expose 函数用于显式地限制该组件暴露出的属性,当父组件通过模板引用访问该组件的实例时,将仅能访问 expose 函数暴露出的内容

export default {
  setup(props, { expose }) {
    // 让组件实例处于 “关闭状态”
    // 即不向父组件暴露任何东西
    expose()

    const publicCount = ref(0)
    const privateCount = ref(0)
    // 有选择地暴露局部状态
    expose({ count: publicCount })
  }
}

5、(1)computed:接受一个 getter 函数,返回一个只读的响应式 ref 对象。该 ref 通过 .value 暴露 getter 函数的返回值。它也可以接受一个带有 get 和 set 函数的对象来创建一个可写的 ref 对象

// 创建一个只读得计算属性
const count = ref(1)
const plusOne = computed(() => count.value + 1)
console.log(plusOne.value) // 2
plusOne.value++ // 错误,因只读

// 创建一个可写的计算属性
const count = ref(1)
const plusOne = computed({
  get: () => count.value + 1,
  set: (val) => {
    count.value = val - 1
  }
})

plusOne.value = 1
console.log(count.value) // 0

(2)watch监听:vue3提供了在setup(包含beforeCreat created)中使用的数据监听函数watch,因为setup只是在初始化的时候调用一次,无法根据数据变化实时调用,所以提供了watch方法解决这个问题

setup(){
  //监听单个的ref
  const  a = ref(0)
  watch(a,(newval,oldval)=>{
   console.log(newval,oldval)
  })

  //监听多个ref
  const  b = ref(0)
  watch([a,b],(newval,oldval)=>{
   console.log(newval,oldval)
  })

  //监听reactive
  const reactiveData = reactive({
   a:1,
   b:2,
   c:3
  })
  watch(data,(newVal,oldVal)=>{
   console.log(newVal,oldVal)
  })
  
  //监听reactive下的某一个属性
  watch([()=>data.a,()=>data.b],(newVal,oldVal)=>{
   console.log(newVal,oldVal)
  })
}

// 监听props中某个属性
watch(
			() => props.bgUrl,
			(value) => {
				if (value?.includes('/')) {
					background.url = value;
				} else {
					background.url = getImageUrl('card', value);
				}
			},
			{ immediate: true }
		);

6、响应式api(工具)

// isRef 检查某个值是否为ref
let foo: unknown
if (isRef(foo)) {
  // foo 的类型被收窄为了 Ref<unknown>
  foo.value
}

//unref() 如果参数是 ref,则返回内部值,否则返回参数本身。val = isRef(val) ? val.value : val
function useFoo(x: number | Ref<number>) {
  const unwrapped = unref(x)
  // unwrapped 现在保证为 number 类型
}

// toRef 基于响应式对象上的一个属性,创建一个对应的 ref。这样创建的 ref 与其源属性保持同步:改变源属性的值将更新 ref 的值,反之亦然。
const state = reactive({
  foo: 1,
  bar: 2
})
const fooRef = toRef(state, 'foo')
// 更改该 ref 会更新源属性
fooRef.value++
console.log(state.foo) // 2
// 更改源属性也会更新该 ref
state.foo++
console.log(fooRef.value) // 3

// toRefs:将一个响应式对象转换为一个普通对象,这个普通对象的每个属性都是指向源对象相应属性的 ref。每个单独的 ref 都是使用 toRef() 创建的。此时解构不会丢失响应性
const state = reactive({
  foo: 1,
  bar: 2
})

const stateAsRefs = toRefs(state)
// 这个 ref 和源属性已经“链接上了”
state.foo++
console.log(stateAsRefs.foo.value) // 2

stateAsRefs.foo.value++
console.log(state.foo) // 3
//注意:toRefs 在调用时只会为源对象上可以枚举的属性创建 ref。如果要为可能还不存在的属性创建 ref,请改用 toRef

//isProxy() 检查一个对象是否是由 reactive()、readonly()、shallowReactive() 或 shallowReadonly() 创建的代理
//isReactive()检查一个对象是否是由 reactive() 或 shallowReactive() 创建的代理
// isReadonly()检查传入的值是否为只读对象。只读对象的属性可以更改,但他们不能通过传入的对象直接赋值



// 响应式api进阶
//1、shallowRef() 是ref() 的浅层作用形式,和 ref() 不同,浅层 ref 的内部值将会原样存储和暴露,并且不会被深层递归地转为响应式。只有对 .value 的访问是响应式的
const state = shallowRef({ count: 1 })
// 不会触发更改
state.value.count = 2
// 会触发更改
state.value = { count: 2 }

// 2、triggerRef()

// 3、customRef()

// 4、shallowReactive() 和 reactive() 不同,这里没有深层级的转换:一个浅层响应式对象里只有根级别的属性是响应式的。属性的值会被原样存储和暴露,这也意味着值为 ref 的属性不会被自动解包了
const state = shallowReactive({
  foo: 1,
  nested: {
    bar: 2
  }
})
// 更改状态自身的属性是响应式的
state.foo++
// ...但下层嵌套对象不会被转为响应式
isReactive(state.nested) // false
// 不是响应式的
state.nested.bar++

// shallowReadonly() 和 readonly() 不同,这里没有深层级的转换:只有根层级的属性变为了只读。属性的值都会被原样存储和暴露,这也意味着值为 ref 的属性不会被自动解包了
const state = shallowReadonly({
  foo: 1,
  nested: {
    bar: 2
  }
})
// 更改状态自身的属性会失败
state.foo++
// ...但可以更改下层嵌套对象
isReadonly(state.nested) // false
// 这是可以通过的
state.nested.bar++

//toRaw() 根据一个 Vue 创建的代理返回其原始对象,toRaw() 可以返回由 reactive()、readonly()、shallowReactive() 或者 shallowReadonly() 创建的代理对应的原始对象
const foo = {}
const reactiveFoo = reactive(foo)
console.log(toRaw(reactiveFoo) === foo) // true

//markRaw()

// effectScope()

//getCurrentScope()

//onScopeDispose()

7、组合式api依赖注入:允许后代访问

// provide
<script setup>
import { ref, provide } from 'vue'
import { fooSymbol } from './injectionSymbols'
// 提供静态值
provide('foo', 'bar')

// 提供响应式的值
const count = ref(0)
provide('count', count)

// 提供时将 Symbol 作为 key
provide(fooSymbol, count)
</script>


// inject
<script setup>
import { inject } from 'vue'
import { fooSymbol } from './injectionSymbols'

// 注入值的默认方式
const foo = inject('foo')
// 注入响应式的值
const count = inject('count')
// 通过 Symbol 类型的 key 注入
const foo2 = inject(fooSymbol)
// 注入一个值,若为空则使用提供的默认值
const bar = inject('foo', 'default value')
// 注入一个值,若为空则使用提供的工厂函数
const baz = inject('foo', () => new Map())
// 注入时为了表明提供的默认值是个函数,需要传入第三个参数
const fn = inject('function', () => {}, false)
</script>

8、内置特殊组件:不是组件,、 和 具有类似组件的特性,也是模板语法的一部分。但它们并非真正的组件,同时在模板编译期间会被编译掉。因此,它们通常在模板中用小写字母书写
用于渲染动态组件或元素的‘元组件’,要渲染的实际组件由 is prop 决定
(1)当 is 是字符串,它既可以是 HTML 标签名也可以是组件的注册名。
(2)is 也可以直接绑定到组件的定义

<script setup>
import Foo from './Foo.vue'
import Bar from './Bar.vue'
</script>
<template>
  <component :is="Math.random() > 0.5 ? Foo : Bar" />
</template>

// 渲染标签
<component :is="href ? 'a' : 'span'"></component>

表示模板中的插槽内容出口
当我们想要使用内置指令而不在 DOM 中渲染元素时, 标签可以作为占位符使用

9、Typrscript 声明类型的关键字 interface type

// type进行声明
type ColorConfig = {
  [key in Type]: Colors; // 可以是复合类型
};

// interface 声明
interface ColorConfig {
  [key: string]: Colors; // interface 的索引只能是基础类型,类型别名也不可以。而 type 的索引可以是复合类型
}

10、全局config
vue2是将配置挂在构造函数上,vue3是将挂在组件实例上(使得应用之间的配置互不影响)

//vue2的全局config配置是直接挂在Vue构造函数上的
//例如
Vue.config.errorHandler = (err)=>console.log(err)

//vue3的全局api是在当前应用实例上修改的,不会影响其他应用
//例如
const app1 = createApp(AppComponent1)
const app2 = createApp(AppComponent2)

app1.config.errorHandler.(err)=>console.log(err,'app1')
app2.config.errorHandler.(err)=>console.log(err,'app2')

11、路由相关

import {useRoute,useRouter} from ''vue-router
setup(){
 const route = useRoute()  //等同于vue2的this.$route
 const router = useRouter() //等同于vue2的this.$router
}

vue3 新方法及内置组件使用记录
(1)defineComponent使用

// 1、defineComponent,没有实现任何的逻辑,只是把接收的 Object 直接返回,它的存在是完全让传入的整个对象获得对应的类型,它的存在就是完全为了服务 TypeScript 而存在的.我们都知道一个组件就是一个普通对象,既然是普通对象就不会获得提示,使用该方法后给予组件正确的参数类型推断
import { defineComponent} from 'vue';
export default defineComponent({
name: 'MapParamsEdit',
	components: {},
	setup(props, { emit }) {
	}
})

(2)Vue3JsonEditor使用,可以实现json编辑器,需要快速编辑json数据,还需要支持全屏编辑,以及json校验

// 首先安装  npm install json-editor-vue3 --save
import { Vue3JsonEditor } from 'vue3-json-editor/dist/vue3-json-editor.cjs.js';

  <Vue3JsonEditor 
     v-model="content" 
     key="1" 
     :mode="'text'" 
     :showBtns="false" 
     :expandedOnStart="true"
      class="redis-extraction-rule-script-json" />

(3)内置组件的使用:内置组件无需注册便可在模板中使用

// 1、<Transition> 为单个组件或元素提供过渡效果
<Transition @after-enter="onTransitionComplete">
  <div v-show="ok">toggled content</div>
</Transition>

// 2、<TransitionGroup> 为多个元素或组件增加过渡效果
// 3、<KeepAlive> 缓存包裹在其中的动态切换组件
<KeepAlive>
  <comp-a v-if="a > 1"></comp-a>
  <comp-b v-else></comp-b>
</KeepAlive>
// 4、<Teleport> 将其插槽内容渲染到dom另一位置
// 5、<Suspense> 用于协调对组件树中嵌套的异步依赖的处理

问题:vue-router 4.x(用在vue3项目中)
vue-router 4.x在vue3中使用的区别:https://router.vuejs.org/zh/guide/advanced/navigation-guards.html
vue2中使用,全局配置使用

// 在router下配置路由及守卫,在跟组件中全局注册使用
import App from './App.vue';
import router from './router';
import auth from '@/components/auth/auth.vue';
// 3中
const app = createApp(App); // vue2 3的组件创建也有区别
app.use(router).component('Auth', auth)
// 2中
new Vue({
  render: h => h(App),
}).$mount('#app')
app.use(router).mount('#app');

// 在项目中各个组件中通过this.$router的形式访问路由配置及操作,并且以 this.$route 的形式访问当前路由

在vue3中,路由在组件中的使用有区别

// 要在 setup 函数中访问路由,请调用 useRouter 或 useRoute 函数
// 请注意,在模板中我们仍然可以访问 $router 和 $route,所以不需要在 setup 中返回 router 或 route
import { useRoute, useRouter, onBeforeRouteUpdate } from 'vue-router';
const route = useRoute();
const router = useRouter();
router.push({ name: arr[arr.length - 1].name, params: arr[arr.length - 1].params });
if (route.path === '/iframes') element.style.height = `100vh`;
// 虽然你仍然可以通过 setup 函数来使用组件内的导航守卫,但 Vue Router 将更新和离开守卫作为 组合式 API 函数公开
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
import { ref } from 'vue'

export default {
  setup() {
    // 与 beforeRouteLeave 相同,无法访问 `this`
    onBeforeRouteLeave((to, from) => {
      const answer = window.confirm(
        'Do you really want to leave? you have unsaved changes!'
      )
      // 取消导航并停留在同一页面上
      if (!answer) return false
    })

    const userData = ref()

    // 与 beforeRouteUpdate 相同,无法访问 `this`
    onBeforeRouteUpdate(async (to, from) => {
      //仅当 id 更改时才获取用户,例如仅 query 或 hash 值已更改
      if (to.params.id !== from.params.id) {
        userData.value = await fetchUser(to.params.id)
      }
    })
  },
}

问题:使用组合式api的好处:一系列api集合而不是固定的选项入口书写
(1)组合式 API 并不是函数式编程(抽象的映射关系,相同的输入必须有相同的输出)
(2)更好的逻辑复用(vue2逻辑复用 mixins 选项,多个mixins时数据来源不清晰。命名冲突等。组合api直接引入setup导出的即可,解决了命名冲突(不同文件下拿的重新起名即可),)
(3)更灵活的代码组织(选项式api在单组件的逻辑复杂到一定程度时,会面临一些无法忽视的限制,例如多个逻辑关注点写在不同的选项中,需要反复上下滚动查看,片段分散 )组合式api将一个逻辑关注点写在一块方便查看等
(4)更好的类型推理(ts)
(5)更小的生产包体积(都在setup函数中变量名可以被压缩。而选项式中this对象属性不可)

vue3 hooks
ref reactive computed watch等都是
Vue 3 中的 Hooks 是一些函数,它们可以让开发者在组件中处理逻辑。设计目的是为了让开发者在不借助 class 的情况下复用组件逻辑(vue2中通过class定义组件,new Vue() 创建实例并渲染)
vue3将组件的定义方式改为基于函数的 API,而使用 Vue 3 的 Hooks 可以让我们在不借助 class 的情况下,轻松地复用组件逻辑。在 Hooks 中,我们可以将逻辑封装在一个函数中,并返回所需的数据和方法。这样,我们就可以在多个组件中引入这个函数,并在组件的 setup 函数中调用它,从而达到复用逻辑的目的。

// 在hook.js文件在封装
import { reactive, computed } from "vue";
export function useCounter() {
  const state = reactive({
    count: 0,
  });
  const doubleCount = computed(() => state.count * 2);
  function increment() {
    state.count++;
  }
  return { state, doubleCount, increment };
}


// 在组件中调用
javascript
import { useCounter } from "./hooks";
export default {
  setup() {
    const { state, doubleCount, increment } = useCounter();
    return { state, doubleCount, increment };
  },
};

set在标签中与不在标签中写法主要区别
(1)setup位置写法
(2)props写法

// 在使用 <script setup> 的单文件组件中,props 可以使用 defineProps() 宏来声明
defineProps() 函数中的参数与vue2中props选项同
// 1、可以是字符串形式声明名称
defineProps(["foo"])
// 2、可以是对象
interface Props {
  message: string;
  count: number;
}
defineProps<Props >({
  message: {
    type: String,
    required: true
  },
  count: {
    type: Number,
    default: 0,
    validator: (value: number) => value >= 0 && value <= 10
  }
})

const props = defineProps({
  total: {
    type: Number,
    required: true
  },
  page: {
    type: Number,
    default: 1
  },
  limit: {
    type: Number,
    default: 20
  },
  pageSizes: {
    type: Array as () => number[],
    default: () => [10, 20, 30, 50] // 设置默认值
  },
  layout: {
    type: String,
    default: "total, sizes, prev, pager, next, jumper"
  },
  background: {
    type: Boolean,
    default: true
  },
  autoScroll: {
    type: Boolean,
    default: true
  },
  hidden: {
    type: Boolean,
    default: false
  },
  style: {
    type: [String, Object],
    default: ""
  }
});
// 结合withDefault函数定义父传过来的默认值(第一个参数是defineProps函数,第二个是对象参数定义默认值)
const props = withDefaults(defineProps<{ isShow: boolean, map: VisualMap | null }>(), {
    isShow: () => false,
    map: () => null
});
访问:模板中直接访问
js中props.__访问

(3)emit写法

// setup不写在标签中
使用 emits: ["successAdd"]声明,写法可以是字符串,对象,类似props,然后在setup函数第二个参数中使用 setup(props, { emit })

// setup写在标签中,利用defineEmit函数。
// 使用泛型
interface Events {
  add: { name: string; age: number };
  remove: string;
}
const emit = defineEmit<Events>();
// 直接使用字符串

(4)返回
写在标签中的不用特殊定义返回在模板中使用
(5)组件命名 可以在script标签上给组件命名

<script setup lang="ts" name="GroupEdit">

(6)组件中的方法属性供外部使用defineExpose:使用 defineExpose 函数将组件实例上的属性或方法暴露给父级组件。这个函数通常与 setup 函数一起使用,用于在组件中定义需要暴露给父组件的属性和方法
defineExpose({
count,
increment
});

(7)ref与shallowRef的区别

shallowRef是浅层 ref ,将ref的内部值将会原样存储和暴露,并且不会被深层递归地转为响应式。只有对 .value 的访问是响应式
shallowRef({ name: "xuehua", age: 1 })
test1.value.name = "xuehua2"; // 对value响应,.value 属性本身被访问时才是有响应性的,不关心内部的变化
  console.log(test1.value);  // 值虽然变更了但是没有触发视图的响应式更改
  可以给重新赋值,给一个新的对象
  
const test1 = ref({ name: "xuehua", age: 1 });  // ref会对整体是响应式的,改属性或变更整个对象都会触发视图响应变更

(8)reactive与shallowReactive的区别

shallowReactive是reactive()的浅层作用形式
shallowReactive只有根级别的属性是响应式的,值会原样存储和暴露,这也意味着值为 ref 的属性不会被自动解包了
const test1 = shallowReactive({ name: "xuehua", age: 1, nested: { bar: 1 } });
test1.nested.bar = 6; // 值更新了,但不会触发视图响应更新(因为变更的不是跟级别的属性,值虽然变更了但不响应)

(9)ref与reactive的选择

1)简单数据类型使用ref,复杂的使用reactive
(2)简单的值进行包装,并且只关心该值的变化使用ref,对象或复杂数据结构:如果你需要包装一个对象或者一个复杂的数据结构,并且希望能够追踪其内部属性的变化,那么使用reactive更合适
(3)性能和细粒度控制:ref提供了更细粒度的控制,因为它只会追踪到被访问的属性。这意味着只有当你在模板或代码中显式地使用.value访问时,才会触发重新渲染。而reactive将对整个对象进行深度追踪,并在任何属性变化时触发重新渲染

综上:
使用ref时,主要关注单个简单值的变化,并且在模板或代码中直接通过.value访问包装的值。
使用reactive时,主要关注对象或复杂数据结构的整体变化,并且希望能够追踪对象内部属性的变化。

(10)响应式

1)副作用effect:会更改程序里的状态
(2)订阅者:例如每个副作用依赖某个状态,这个副作用就是这个状态的订阅者,状态变化后应该通知其所有订阅了的副作用重新执行。
(3)在模版中顶级的ref才会自动解包
const count = ref(0)
const object = { id: ref(1) }
模版使用中:{{object.id + 1}}  不会被解包
(4)断开连接:将reactive变量重新赋值后与之前的变量端来连接;当你将一个响应式对象的属性赋值或解构到一个本地变量时,访问或赋值该变量是非响应式的,因为它将不再触发源对象上的 get / set 代理。注意这种“断开”只影响变量绑定——如果变量指向一个对象之类的非原始值,那么对该对象的修改仍然是响应式的
(5)创建响应式副作用:
watchEffect(() => {
  // 追踪 A0 和 A1
  A2.value = A0.value + A1.value
})
A0.value = 2 // 将触发副作用,从而A2的值也会更新,在使用A2的地方也会触发渲染更新
const A2 = computed(() => A0.value + A1.value) // 同上(在内部computed 会使用响应式副作用来管理失效与重新计算的过程)6)Vue 的响应式系统基本是基于运行时的。追踪和触发都是在浏览器中运行时进行的(可以在没有构建步骤的情况下工作,而且边界情况较少)
(7)调试(开发模式下工作):组件调试钩子(依赖)、计算属性调试(利用computed第二个参数对应中的两个钩子:依赖被追踪、依赖更新)、侦听器调试
watch(source, callback, { // 可以写调试钩子,也可以配置deep等属性
  onTrack(e) {
    debugger
  },
  onTrigger(e) {
    debugger
  }
})8)watch和computed的区别
     监听方式不同:
     watch:允许监听特定的数据源,当监听的数据变化时执行自定义的回调函数,可以设置深度监听、立即触发回调的属性;
     computed:基于其依赖的响应式数据进行计算的属性,当依赖的数据发生变化时,computed属性会自动重新计算其值。computed属性是基于缓存的,只有在依赖的数据发生变化时才会重新计算;watch监听数据必须是data中声明过或者父组件传递过来的props中的数据,不缓存。
     使用场景不同:
     watch:watch适用于监听和响应数据的变化,并在变化时执行异步、复杂或耗时的操作。您可以根据需要执行任意的副作用操作,例如发送网络请求、手动操作DOM等。watch更加灵活,但也更容易导致代码冗长和复杂。
     computed:computed适用于基于现有数据衍生新的数据,并且这些衍生数据是响应式的。它非常适合计算和过滤列表、格式化日期、根据条件计算样式等纯粹的转换和计算操作。computed具有更好的性能,因为它使用了缓存机制来避免不必要的重复计算。
     计算方式不同:
     watch:watch提供了一个侦听函数,该函数接收新值和旧值作为参数,并且可以执行自定义的逻辑。
     computed:computed属性是一个被Vue自动追踪的响应式属性,它会根据其依赖的其他响应式数据自动进行计算,而不需要手动编写观察器。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值