Vue3
[TOC]
学习
vue3重写的动机
- 应用新的js原生个性
解决设计和体系架构的缺点
在vue3中新增了es6的 proxy,proxy打消了vue2的所有限度(比方新对象属性的减少、数组元素的间接批改不会触发响应式机制,这是很多老手所谓的bug),而且有着更好的性能。
Proxy就是真正意义给一个对象包上一层代理从而去实现数据侦听劫持的操作,所以总的来说这个复杂度是间接少了一个数量级的。只有你对这个代理后的对象拜访或批改外面的货色都能被这层代理所感知到,进而去动静地决定返回什么货色给你,并且也不再须要把选项的这些货色再反复挂到组件实例的this下面,因为你拜访的时候是有这个信息晓得你拜访的货色是属于props还是data还是其余的,vue只须要依据这个信息去对应的数据结构外面拿进去就能够了,单就这一点而言你就能够感觉到组件的内存占用曾经少了一半。
vue3的改良及特点
1.性能的晋升:打包大小缩小 41%,首次渲染快 55%,更新快 133%,内存应用缩小 54%。
2.新推出的Composition API 使组件更易保护,缩小无用数据绑定页面更晦涩。
4.更好TypeScript反对,能够在创立命令里间接配置,页面集成畅通无阻。
5.Teleport(瞬移组件)、Suspense(解决异步加载组件问题)和全局 API 的批改和优化。
6.Vue3兼容大部分Vue2的个性,用Vue2代码开发Vue3都能够。
Vue3.x中的新增点
1.多根节点组件
在vue3中,组件正式反对多根节点组件,即片段!
<template>
<header>...</header> <main>...</main> <footer>...</footer>
</template>
2.setup
要开始应用Composition API,咱们首先须要一个能够理论应用它的中央。在Vue组件中,咱们将此地位称为setup。
setup执行时尚未创立组件实例,所以不能应用this,此时this 为 undefined。除了props,无法访问组件中申明的任何data、computed、methods。
注:setup是为了优化性能让程序按需引入全局对立
参数 (props, context)
// props 父组件传过来的props props是具备反馈性的(传入新的props时会自动更新)// context {attrs, emit, slots}setup(props, context) { console.log(context) /** * attrs: Proxy * emit: (event, ...args) => instance.emit(event, ...args) * slots: Proxy */}
setup 生命周期钩子
钩子函数 | setup钩子 |
---|---|
beforeCreate | 没有 |
created | 没有 |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeUnmount | onBeforeUnmount |
unmounted | onUnmounted |
errorCaptured | onErrorCaptured |
renderTracked | onRenderTracked |
renderTriggered | onRenderTriggered |
因为setup是围绕beforeCreate和created生命周期挂钩运行的,因而您无需显式定义它们。换句话说,应该在这些钩子中编写的任何代码都应间接在setup函数中编写。
用法一:
<template> <div class="home"> <div>名字:{{ name }}</div> <ul> <li v-for="item in list" :key="item" @click="show(item)">{{ item }}</li> </ul> </div></template><script lang="ts">import { defineComponent, ref } from "vue";// 注:defineComponent 在TypeScript下,给予了组件正确的参数类型推断export default defineComponent({ name: "Home", components: {}, props:['msg'], setup(props,context) { // 注:setup函数是处于生命周期函数 beforeCreate 和 Created 两个钩子函数之间的函数 也就说在 setup 函数中是无奈应用 data 和 methods 中的数据和办法,而methods等能够应用setup中return进来的数据。 /* 一.函数的第一个参数是 props 用于接管 props.msg 这个props是一个响应式的Proxy对象,不能够解构,解构后会失去响应,如果要用解构的形式,要用toRefs let { msg } = toRefs(props) //然而解析成ref了要用msg.value,所以间接用props.msg更简略 二.context对象在setup()中裸露三个属性 attrs 、slots 和 emit 因为在setup函数中还没有创立Vue实例,是无奈应用vm.$attrs、vm.$slots和vm.$emit的,所以这三个属性充当了这样的作用,应用办法雷同。 留神: context.attrs和vm.$attrts蕴含的是在实例vm.props中没有被申明辨认的attribute(class和style除外)。所以setup()中参数props中裸露的变量,就不会在context.attrs中裸露。 context.slots和vm.$slots只能拜访具名插槽,没有命名的插槽或者v-slot:default的是没有裸露的。 context的attrs和slots是有状态的,当组件更新时也会实时更新,所以也不要解构。但与props不同的是,它们不是响应式的,在setup()中的应用应放弃只读的状态,如果要扭转能够在onUpdated的周期函数中进行。 context.emit和vm.$emit能够触发实例上的监听事件。 */ const list = ref(["深圳", "北京", "上海"]); const name = ref(""); //注:用ref是为了转换成援用类型,让全局援用保持一致,而之前原始类型是不行的,所以要name.value的方示赋值 const show = (index: string) => { name.value = index; }; // 注:不return进来的数据,模板是无奈应用的。 return { list, name, show }; },});</script>
用法二: reactive() 优化
<template> <div class="home"> <div>名字:{{ data.name }}</div> <ul> <li v-for="item in data.list" :key="item" @click="data.show(item)">{{ item }}</li> </ul> </div></template><script lang="ts">import { defineComponent, reactive } from "vue";export default defineComponent({ name: "Home", components: {}, setup() { const data = reactive({ list: ["深圳", "北京", "上海"], name: "", show: (index: string) => { data.name = index; }, }); return { data }; },});</script>
用法三: toRefs() 优化
<template> <div class="home"> <div>名字:{{ name }}</div> <ul> <li v-for="item in list" :key="item" @click="show(item)">{{ item }}</li> </ul> </div></template><script lang="ts">import { defineComponent,reactive,toRefs } from "vue";export default defineComponent({ name: "Home", components: {}, setup() { const data = reactive({ list: ["深圳", "北京", "上海"], name: "", show: (index: string) => { data.name = index; }, }); const refData = toRefs(data); //不能间接解析 ...data 必须用 toRefs() return { ...refData }; },});</script>
jsx
setup() { const root = ref(null) return () => <div ref={root} />}
在setup获取示例this的办法
setup外面this
指向window
,composition的文档中也没有提到怎么获取组件实例。
咱们能够通过getCurrentInstance()
这个接口获取组件实例:
setup() { // getCurrentInstance()能够获取组件实例 const instance = getCurrentInstance() console.log(instance); onMounted(()=>{ // 组件实例的上下文才是咱们相熟的this console.log(instance.ctx.foo); // 'foo' console.log(instance.ctx.bar()); // '我是bar办法' }) return {}},
然而很快咱们又蒙圈了,这个组件实例和咱们以前相熟的this
不一样,间接拜访this.foo
还是找不到数据。
vue3中组件实例构造如下,各个选项中的this
实际上是ctx
或proxy
当然坑还是有的,你仔细观察这个ctx
,发现它不是一个Proxy对象,也就是这位兄台只有值却没有响应性,所以如果要利用响应个性,还得用proxy
这个属性返回上下文对象,如果只是想要数据,上图中不是有个data
也是Proxy类型的嘛。
setup() { const { proxy, data } = getCurrentInstance() // 想要利用响应能力,就要应用proxy这个上下文 watchEffect(() => { console.log(proxy.foo) // foo变动会有输入 console.log(data.foo) // foo变动会有输入 })},
最初大家还要留神,setup()执行的工夫点是很早的,甚至早于created,因而foo和bar的拜访如果没有特意放到onMounted外面还真没有。
setup() { const instance = getCurrentInstance() console.log(instance.ctx.foo); // undefined console.log(instance.ctx.bar()); // undefined},
罕用解决篇:
setup函数太长了怎么办?
setup(){ let { val, todos, addTodo } = useTodo() let {count,double,add} = useCounter() let {x, double:doubleX} = useMouse() return { val, todos, addTodo, count,double,add, x,doubleX }}
return的上下文太长了,咱们能够应用vue3的setup script
性能,把setup这个配置也优化掉,一个性能export一次
<script setup>import useCounter from './counter'import useMouse from './mouse'import useTodo from './todos'let { val, todos, addTodo } = useTodo()export {val, todos, addTodo}let {count,double,add} = useCounter()export {count,double,add}let {x, double:doubleX} = useMouse()export {x,doubleX}</script>
我的属性怎么就不响应了
上面是我第一次写的代码
setup({ foo, bar }) { watchEffect(() => { console.log(foo, bar) // foo,bar发生变化,也不会有输入 })}
props是一个Proxy对象,间接解构就失去了响应能力,所以看待props要温顺,不能动不动就劈开了
setup(props) { watchEffect(() => { console.log(props.foo, props.bar) // foo,bar发生变化,会有输入 })}
真想劈开也行,看你喜爱什么姿态了
setup(props) { const { foo, bar } = toRefs(props) watchEffect(() => { console.log(foo.value, bar.value) // foo,bar发生变化,会有输入 })}
怎么派发自定义事件?
忽然之间没有this了之后,如同忽然生存不能自理了,再也不能用this.$emit()
派发事件了。
其实通过组件实例是能够派发事件的,比方:
setup() { getCurrentInstance().emit('ooxx') }//然而这样比拟麻烦,所以咱们要用到setup函数的第二个参数:setup(props, ctx) { ctx.emit('ooxx')}//当然,还能把emit间接解构进去应用更不便:setup(props, { emit }) { emit('ooxx')}
vue3获取以后页面路由
1.
import { getCurrentInstance } from 'vue'setup() { const { ctx } = getCurrentInstance() console.log(ctx.$router.currentRoute.value)}
2.
import { useRoute } from 'vue-router'import { toRaw } from 'vue'setup () { const route = useRoute() console.log(toRaw(route))}
父组件调用子组件办法
//父组件 <Listmodify :isShow="isShow" ref="child"></Listmodify> setup(props, context) { const child = ref(); const handleEdit = () => { child.value.str(); }; return { handleEdit, child }; }//子组件 setup(props, context) { const str = () => { console.log("我被父组件调用了"); }; return { str }; }
3.Teleport 传送
(解决款式等抵触问题不挂载到app下)
Teleport提供了一种办法,是咱们能够管制要在Dom中哪个父对象下出现HTML,而不用求助于全局状态或将其拆分为两个局部(有点像哆啦A梦的任意门,集体了解)
Teleport 的应用
示例一:
1、先搭建一个vue3的我的项目
2、开始应用 示例:
<div id="app"></div> <div id="teleport-target"></div> <div id="modal-container"></div>
<button @click="showToast" class="btn">关上 toast</button> <!-- to 属性就是指标地位 --> <teleport to="#teleport-target"> <div v-if="visible" class="toast-wrap"> <div class="toast-msg">我是一个 Toast 文案</div> </div> </teleport>
import { ref } from 'vue';export default { setup() { // toast 的封装 const visible = ref(false); let timer; const showToast = () => { visible.value = true; clearTimeout(timer); timer = setTimeout(() => { visible.value = false; }, 2000); } return { visible, showToast } }}
3、与 Vue components 一起应用 —— modal封装的组件
<div id="app"></div> <div id="teleport-target"></div> <div id="modal-container"></div> <script type="module" src="/src/main.js"></script>
<teleport to="#modal-container"> <!-- use the modal component, pass in the prop --> <modal :show="showModal" @close="showModal = false"> <template #header> <h3>custom header</h3> </template> </modal> </teleport>
import { ref } from 'vue';import Modal from './Modal.vue';export default { components: { Modal }, setup() { // modal 的封装 const showModal = ref(false); return { showModal } }}
在这种状况下,即便在不同的中央渲染 Modal
,它仍将是以后组件(调用 Modal
的组件)的子级,并将从中接管 show
prop
示例二:
- index.html 页面新加插入点(会挂载到 #headTitie DOM下)
<div id="headTitie"></div><div id="app"></div>
- 在components目录下新建 headTitle.vue
<template> <teleport to="#headTitie"> <div class="head"> <h1>{{ title }}</h1> </div> </teleport></template><script lang="ts">import { defineComponent, ref } from "vue";export default defineComponent({ name: "headTitie", setup() { const title = ref("Vue3 新个性示例"); return { title, }; },});</script>
- 在 App.vue 加
<template> <headTitle /> <router-view /></template><script lang="ts">import headTitle from "./components/headTitle.vue"; export default { name: "App", components: { headTitle, },};</script>
4.watch监听
从 vue 中引入 watch 能够参考 第4点 Reactivity(反馈性)
- 监听 ref 申明的 反馈性数据
- 监听 reactive 申明的反馈性数据
- 监听 props 外面的数据
<script>//肯定应用之前引入import {ref, watch} from 'vue'setup(props) { //监听 ref 数据 let count = ref(0) watch(count, (val, old) => { // 新数据、老数据 }) //监听 reactive 数据 对象 let person = reactive({ age: 18 }) watch(() => person.age, (val, old) => { //新数据、老数据 }) //监听 reactive 数据 数组 let arr = reactive([1, 2, 3]) watch(arr, (val, old) => { //新数据、老数据 }) //监听 props 的数据 退出props传了一个 testkey watch(() => props.testkey, () => { }) //要留神 return return { count, person, arr }}</script>
<script lang="ts">import { defineComponent, ref, reactive, toRefs, watch } from "vue";export default defineComponent({ name: "Home", components: {}, setup() { const text = ref("测试单个值"); const data = reactive({ list: ["深圳", "北京", "上海"], name: "", show: (index: string) => { data.name = index; }, }); //watch(text, 单个用法,watch([text,()=>data.name], 多个用法,注:()=>data.name 为了兼容vue2 watch([text,()=>data.name], (newValue, oldValue) => { console.log(`new--->${newValue}`); console.log(`old--->${oldValue}`); }); const refData = toRefs(data); return { ...refData, }; },});</script>
watchEffect和watch有啥不同?
这俩办法很类似,都是察看响应式数据,变动执行副作用函数,但有如下不同:
- watch须要明确指定监督指标
watch(() => state.counter, (val, prevVal) => {})//watchEffect不须要watchEffect(() => {console.log(state.counter)})
- watch能够获取变动前后的值
- watch是懒执行的,它等效于vue2中的
this.$watch()
,watchEffect为了收集依赖,要立刻执行一次
生命周期钩子能不能写多个?
当然能够写多个,最初它们会按注册程序顺次执行:
setup() { onMounted(() => {}) onMounted(() => {}) onMounted(() => {})}
甚至还能拆分出多个雷同生命周期钩子到独立函数中
function fun1() { // 这里能够用onMounted执行代码 onMounted(() => {})}function fun2() { // 这里还能够用onMounted执行代码 onMounted(() => {})}setup() { fun1() fun2() onMounted(() => {})}
5. computed 计算属性
从 vue 中引入 computed
<script>import {ref, computed} from 'vue'setup(props) { let count = ref(0) //以后组件的计算属性 let countCom = computed(() => { return count.value * 2 }) //props传进来的计算属性 let countCom2 = computed(() => { return props.propsKey * 2 })}</script>
扩大——
- computed创立只读计算属性
给 computed()
传入一个函数,能够失去一个只读的计算属性:
const count = ref(1)// 创立一个计算属性,使其值比 count 大 1const bigCount = computed(() => count.value + 1)console.log(bigCount.value) // 输入 2bigCount.value++ // error 不可写
- computed创立可读可写计算属性
const count = ref(1)// 创立一个 computed 计算属性,传入一个对象const bigCount = computed({ // 取值函数 get: () => (count.value + 1), // 赋值函数 set: val => { count.value = val - 1 }})// 给计算属性赋值的操作,会触发 set 函数bigCount.value = 9// 触发 set 函数后,count 的值会被更新console.log(count.value) // 8
6. setup 生命周期钩子应用
- setup 的 onMounted 执行 在 整个组件的 mounted 之前
- setup 的 onMounted 执行 在 setup 同步组件执行之后
- setup 的 onMounted 外面以及 setup 不能应用this
<script>import {onMounted} from 'vue'mounted () { console.log(4)},setup () { onMounted(() => { console.log(1) }) console.log(2) setTimeout(() => { console.log(3) }, 0) //打印程序 // 2 1 4 3 onMounted(() => { console.log(this) }) //打印 //undefined}</script>
7.provide和inject
<!--一句话介绍:provide能够向所有子孙组件提供数据以及提供批改数据的办法,子孙组件用inject应用数据。-->
从 vue 中引入父组件 provide
<script>import {provide} from 'vue'export default { setup () { provide('person', { name: 'Bob', age: 18 }) provide('city', '杭州') }}</script>
子孙组件 inject
<script>import {inject} from 'vue'export default { setup () { //父辈组件 provide 的数据 let person = inject('person') let city = inject('city') //父辈组件 没有提供的值,给个默认值 不给默认为为 undefined 而且会有Vue warn揭示 let noProvide = inject('noProvide', 'noProvide hah') }}</script>
provide 带有反馈性的数据 父组件值批改,子孙组件值会对应的批改
<script>import {provide} from 'vue'export default { setup () { let city = ref('滨江') provide('city', city) }}</script>
provide 一个 函数
<script>import {provide} from 'vue'export default { setup () { let provideFun = () => { console.log('provide fun') } provide('provideFun', provideFun) }}</script>
// 父组件import { ref,provide } from 'vue';// provide()承受两参数(name: string,data: 传递的数据)setup(){ let init = ref(1); function change(){ init.value += 1 } provide('init',init) provide('update',change) // 数据响应式的要害,批改数据的办法也要一应传递上来,否则会不响应 return { change,init }}// 子组件import { inject,readonly } from 'vue';// inject()接管两个参数(name: 要承受provide的name,data: 可不填,默认的数据)setup(){ const oneInit = inject('init') // 接管上方传递下来的数据 const updateUser = inject('updatea1',readonly(oneInit)) // 接管办法,readonly只读,免得外在起因批改了数据 return { oneInit, updateUser }}
8.Reactivity(反馈性)
Vue最独特的性能之一是不引人注目的反馈零碎。
当您将纯JavaScript对象作为data选项传递给应用程序或组件实例时,Vue将遍历其所有属性,并应用带有getter和setter的处理程序将它们转换为Proxies
- 跟踪更改它的函数:在代理的getter中进行此操作 effect
- 触发函数,以便它能够更新最终值:在代理中的setter中进行操作 trigger
申明反馈性数据
个别应用 ref、reactive、readonly 来申明数据
- ref 申明根本类型
- reactive 申明援用类型
- readonly 申明只读援用类型
import { ref, reactive, readonly } from 'vue'export default { setup () { // 个别用 ref 申明 根本 类型 // 用 reactive 申明 援用 类型 let count = ref(0) // 在 script 应用 count.value,在 template 应用 {{count}} let state = reactive({ name: 'Bob' }) // 在 script 应用 state.name, 在 template 应用 {{state.name}} // 用 ref 申明 援用类型 let obj1 = ref({ count: 1 }) // 用 reactive 申明 根本 类型 正告⚠️ 能够失常应用,然而没有反馈性 let num1 = reactive(10) //value cannot be made reactive: 10 // readonly // readonly 和 reactive 一样,然而申明的是只读数据 申明根本类型和 reactive 一样正告揭示 let person = readonly({age: 18}) setTimeout(() => { // 定时器 批改 正告 ⚠️ person.age = 10 // Set operation on key "age" failed: target is readonly }); // 肯定要return 才能够在 template 和 script 其余中央应用 return { count, state, obj1 } }}
count打印出的数据
RefImpl {_rawValue: 0, _shallow: false, __v_isRef: true, _value: 0} __v_isRef: true _rawValue: 0 _shallow: false _value: 0 value: 0
state打印出的数据
Proxy {name: "Bob"} [[Handler]]: Object deleteProperty: ƒ deleteProperty(target, key) get: ƒ (target, key, receiver) has: ƒ has(target, key) ownKeys: ƒ ownKeys(target) set: ƒ (target, key, value, receiver) [[Target]]: Object name: "Bob" [[IsRevoked]]: false
obj1打印出的数据
RefImpl {_rawValue: {…}, _shallow: false, __v_isRef: true, _value: Proxy} __v_isRef: true _rawValue: {count: 1} _shallow: false _value: Proxy {count: 1} value: Proxy [[Handler]]: Object [[Target]]: Object count: 1 [[IsRevoked]]: false
num1打印的数据
10
template中应用
<!-- 应用ref定义的根本类型间接应用 --><!-- 应用reactive定义的援用类型间接.对应的属性 --><div class="vue3_pro"> count: {{count}} state: {{state.name}}</div>
9. Vue3 生命周期函数用法
须要引入 (注:vue2 生命周期函数不影响)
<script lang="ts">import { defineComponent,ref,reactive,toRefs,onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted,onActivated,onDeactivated,onErrorCaptured,onRenderTracked,onRenderTriggered} from "vue";export default defineComponent({ name: "Home", components: {}, setup() { //setup() 开始创立组件之前,在beforeCreate和created之前执行。 const data = reactive({ list: ["深圳", "北京", "上海"] }); onBeforeMount(()=>{ //组件挂载到节点上之前执行的函数。 }) onMounted(()=>{ //组件挂载实现后执行的函数。 }) onBeforeUpdate(()=>{ //组件更新之前执行的函数 }) onUpdated(()=>{ //组件更新实现之后执行的函数。 }) onBeforeUnmount(()=>{ //组件卸载之前执行的函数。 }) onUnmounted(()=>{ //组件卸载实现后执行的函数。 }) onActivated(()=>{ //被蕴含在<keep-alive>中的组件,会多出两个生命周期钩子函数。被激活时执行。 }) onDeactivated(()=>{ //比方从 A 组件,切换到 B 组件,A 组件隐没时执行。 }) onErrorCaptured(()=>{ //当捕捉一个来自子孙组件的异样时激活钩子函数。 }) //< 调试用生命函数 onRenderTracked((event)=>{ //跟踪所有状态触发 console.log(event); }); onRenderTriggered((event) => { //跟踪以后状态触发 console.log(event); //key 那边变量产生了变动 //newValue 更新后变量的值 //oldValue 更新前变量的值 //target 目前页面中的响应变量和函数 }); // 调试用生命函数 /> const refData = toRefs(data); return { ...refData }; }, mounted(){ console.log("vue2 生命周期"); }});</script>
10.Vue3 模块化重用性能 (优化 mixins)
1.新建useTime.ts文件
import { ref } from "vue";const time = ref("00:00:00");const getTime = () => { const now = new Date(); const h= now.getHours() < 10 ? "0" + now.getHours() : now.getHours(); const m = now.getMinutes() < 10 ? "0" + now.getMinutes() : now.getMinutes(); const s= now.getSeconds() < 10 ? "0" + now.getSeconds() : now.getSeconds(); time.value = h + ":" + m + ":" + s; setTimeout(getTime, 1000);};export { time, getTime }
2.引入
<template> <div class="home"> <div>工夫:{{time}} <button @click="startTime">开始</button></div> </div></template><script lang="ts">import { defineComponent, ref } from "vue";import { time, getTime } from './useTime';export default defineComponent({ name: "Home", components: {}, setup() { const startTime = () => { getTime(); }; return { startTime, time }; },});</script>
11.Suspense 异步申请组件
- 新建Demo.vue
<template> <div class="Demo"> <div>名字:{{ name }}</div> </div></template><script lang="ts">import { defineComponent } from "vue";export default defineComponent({ name: "Demo", components: {}, setup() { return new Promise((resolve, reject) => { setTimeout(() => { return resolve({ name: "我是 Suspense 异步申请组件" }); }, 2100); }); },});</script>
- 应用引入 home.vue
<template> <div class="home"> <Suspense> <template #default> <Demo /> </template> <template #fallback> <p>加载中...</p> </template> </Suspense> </div></template><script lang="ts">import { defineComponent } from "vue";import Demo from "./Demo.vue";export default defineComponent({ name: "Home", components: {Demo}});</script>
Composition-Api:
12.ref()
ref()
函数能够依据给定的值来创立一个响应式的数据对象,返回值是一个对象,且只蕴含一个 .value
属性。
- 用 ref 创立响应式对象
// 引入 refimport { ref } from '@vue/composition-api'setup() { // 创立响应式对象 const count = ref(0); return { count }}
- 应用响应式对象
<p>以后的count的值为:{{count}}</p><button @click="count++">点击减少count</button>
在JS中批改这个值要额定加上value
:
- ref 的注意事项
在
setup()
函数内,由ref()
创立的响应式数据返回的是对象,所以须要用.value
来拜访;而在
setup()
函数内部则不须要.value
,间接拜访即可。- 能够在
reactive
对象中拜访ref()
函数创立的响应式数据。 - 新的
ref()
会笼罩旧的ref()
。
ref数据响应式监听,和react-hook如同差不多。ref 函数传入一个值作为参数,返回一个基于该值的响应式Ref对象,该对象中的值一旦被扭转和拜访,都会被跟踪到,就像咱们改写后的示例代码一样,通过批改 count.value 的值,能够触发模板的从新渲染,显示最新的值
13.reactive()
reactive是用来定义更加简单的数据类型,然而定义后外面的变量取出来就不在是响应式Ref对象数据了
reactive()
函数接管一个一般的对象,返回出一个响应式对象。
在Vue2.x的版本中,咱们只须要在data()
中定义一个数据就能将它变为响应式数据,在 Vue3.0 中,须要用reactive
函数或者ref
来创立响应式数据。
- 用reactive创立响应式对象
// 在组件库中引入 reactiveimport { reactive } from '@vue/ composition-api'setup() { // 创立响应式对象 const state = reactive({ count:0 }); // 将响应式对象return进来,裸露给模板应用 return state;}
- 应用响应式对象
<p>以后的count的值为:{{count}}</p><button @click="count++">点击减少count< button>
composition-api引入了独立的数据响应式办法reactive
,它能够将传入的对象做响应式解决:
const state = reactive({ foo: 'foo',})watchEffect(() => { console.log(state.foo)})state.foo = 'foooooo' // 输入 'foooooo'
这个形式相似于咱们设置的data
选项,可能解决咱们大部分需要。然而也有以下问题:
- 当咱们间接导出这个state时,咱们在模板中就要带上state这个前缀
setup() {const state = reactive({})return { state }}<div>{{ state.foo }}</div>//为了解决这个问题又要引入toRefssetup() {const state = reactive({})return { ...toRefs(state) }}<div>{{ foo }}</div>
ref和reactive之间的抉择
如果是单值,倡议ref,哪怕是个单值的对象也能够
一个业务关注点有多个值,倡议reactive
14. readonly()
传入一个响应式对象、一般对象或 ref ,返回一个只读的对象代理。这个代理是深层次的,对象外部的数据也是只读的。
const state = reactive({ count: 0 })const copy = readonly(state)watchEffect(() => { // 依赖追踪 console.log(copy.count)})// state 上的批改会触发 copy 上的侦听state.count++// 这里只读属性不能被批改copy.count++ // warning!
15. watchEffect()
watchEffect()
会立刻执行传入的函数,并响应式侦听其依赖,并在其依赖变更时从新运行该函数。
- 根本用法
const count = ref(0)// 首次间接执行,打印出 0watchEffect(() => console.log(count.value))setTimeout(() => { // 被侦听的数据发生变化,触发函数打印出 1 count.value++}, 1000)
- 进行侦听
watchEffect()
应用时返回一个函数,当执行这个返回的函数时,就进行侦听。
const stop = watchEffect(() => { /* ... */})// 进行侦听stop()
Composition-Api 依赖工具
1. isRef()
isRef()
顾名思义,是用来判断某个值是否为 ref()
创立进去的响应式的值。
当你须要开展某个可能为 ref()
创立的响应式的值的时候,会用到它:
import { isRef } from '@vue/composition-api'const unwrapper = isRef(foo) ? foo.value : foo
2.toRefs()
toRefs()
能够将 reactive()
创立进去的响应式对象转换成内容为 ref 响应式的值的一般对象
在搞清楚
toRefs()
的用法之前,咱们须要先理解一下用reactive()
和ref()
创立进去的响应式对象的区别:
- 用
reactive()
创立的响应式对象,整个对象是响应式的,而对象里的每一项都是一般的值。当你把它用开展运算符开展后,整个对象的一般值都不是响应式的;- 而用
ref()
创立的响应式的值,自身就是响应式的,并不依赖于其余对象。
所以当你须要开展 reactive()
创立的响应式对象,又不想让他们失去响应式特点的时候,就须要用 toRefs()
将它进行转换:
mport { toRefs } from '@vue/composition-api'setup() { // 定义响应式数据对象 const state = reactive({ count: 0 }) // 定义简略的函数,使count每次+1 const add = () => { state.count++ } // 将setup函数的内容return进来,供外界应用 return { // 将 state 开展导出,同时将其属性都转化为 ref 模式的响应式数据 ...toRefs(state), add }}
<template> <div> <p>以后的count值为:{{count}}</p> <button @click="add">点击+1</button> </div></template>
vue3与vue2的变动
这都是本人试出来的坑
router-link
改良如下:
- 删除
tag
prop - 应用作用域插槽代替 - 删除
event
prop - 应用作用域插槽代替 - 减少
scoped-slot
API - 进行主动将
click
事件调配给外部锚点 - 增加
custom
prop 以齐全反对自定义的router-link
渲染
在 vue2-router 中,想要将 <roter-link>
渲染成某种标签,例如 <button>
,须要这么做:
<router-link to="/" tag="button">按钮</router-link>!-- 渲染后果 --><button>按钮</button>
当前可能须要这样做:
<router-link to="/foo" v-slot="{ href, route, navigate, isActive, isExactActive }"> <li :class="[isActive && 'router-link-active', isExactActive && 'router-link-exact-active']" > <a :href="href" @click="navigate">{{ route.fullPath }}</a> </li></router-link>
插槽 prop 的对象蕴含上面几个属性:
1、href:解析后的 URL。将会作为一个 a 元素的 href attribute。
2、route:解析后的规范化的地址。
3、navigate:触发导航的函数。会在必要时主动阻止事件,和 router-link 同理。
4、isActive:如果须要利用激活的 class 则为 true。容许利用一个任意的 class。
5、isExactActive:如果须要利用准确激活的 class 则为 true。容许利用一个任意的 class。
API变更
对于 Composition API 的阐明:
1、useRouter
2、useRoute
3、onBeforeRouteLeave
4、onBeforeRouteUpdate
5、useLink