Node报错
报错:NodeJS.Timer,ts报错Cannot find namespace 'NodeJS'.
解决方法:在tsconfig.app.json中增加types:["node"]
如何访问路由
访问当前路由:useRoute()
访问router对象:useRouter
import { useRouter, useRoute } from 'vue-router'
export default {
setup() {
const router = useRouter()
const route = useRoute()
function pushWithQuery(query) {
router.push({
name: 'search',
query: {
...route.query,
...query,
},
})
}
},
}
动态路由
使用场景:看上去是不同的页面,但实际上有着同样的页面组件,只是数据不一样。比如不同景区的详情页
语法:动态字段【路径参数】用:开头
对外暴露:$route.params.xxx
注意点:可设置多个路径参数,都会映射到$route.params
const User = {
template: '<div>User</div>',
}
const routes = [
{ path: '/users/:id', component: User },
]
效果:/u/jo和/u/ju不同的url会被映射到同一个路由
动态路由对于页面渲染的影响
当动态路由切换的时候,只有url会变化,但组件实例则会被复用,这会导致组件的生命周期钩子函数不会被调用。
解决方案
方案1:watch监听指定属性,比如治理监听路由参数的变化
watch(
() => $route.params,
(toParams, previousParams) => { // 对路由变化做出响应... }
);
方案2:导航守卫
import {onBeforeRouteUpdate } from 'vue-router'
import { ref } from 'vue'
export default {
setup() {
const userData = ref()
onBeforeRouteUpdate(async (to, from) => {
//仅当 id 更改时才获取用户,例如仅 query 或 hash 值已更改
if (to.params.id !== from.params.id) {
userData.value = await fetchUser(to.params.id)
}
})
},
}
匹配任意路径的路由
方法:使用正则
const routes = [
// 将匹配所有内容并将其放在 `$route.params.pathMatch` 下
{ path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound },
// 将匹配以 `/user-` 开头的所有内容,并将其放在 `$route.params.afterUser` 下
{ path: '/user-:afterUser(.*)', component: UserGeneric },
]
vue的watch
作用:根据状态变化执行一些effect。
语法:watch(监听对象,回调函数)
// 可以直接侦听一个 ref
watch(question, async (newQuestion, oldQuestion) => {
if (newQuestion.includes('?')) {
loading.value = true
answer.value = 'Thinking...'
try {
const res = await fetch('https://yesno.wtf/api')
answer.value = (await res.json()).answer
} catch (error) {
answer.value = 'Error! Could not reach the API. ' + error
} finally {
loading.value = false
}
}
})
监听对象可以是一个ref或者computed,一个响应式对象,一个getter函数或者多个数据源组成的数组,但不能直接监听响应式对象的属性值
const obj = reactive({ count: 0 })
// 错误,因为 watch() 得到的参数是一个 number
watch(obj.count, (count) => {
console.log(`Count is: ${count}`)
})
此时可以用一个getter函数作为数据源
watch(
() => obj.count,
(count) => {
console.log(`Count is: ${count}`)
}
)
普通监听器只监听响应式对象或引用本身的变化,而不深入监听对象内部的属性变化。 比如下面这段代码,watc监听的是obj本身,只有当obj的引用被替换为另外一个对象,才会触发回调函数。所以这里并不会执行console
//不会触发回调函数
const obj = ref({ count: 0 })
watch(obj, (newValue, oldValue) => {
console.log('普通监听器:', newValue, oldValue)
})
const handleClick=()=>{
obj.value.count++
}
//会触发回调函数
const obj = ref({ count: 0 })
watch(obj, (newValue, oldValue) => {
console.log('普通监听器:', newValue, oldValue)
})
const handleClick=()=>{
obj.value={count:obj.value.count+1}
}
可以通过deep:true将第一段中的watch强制转换为转成深层侦听器。
watch默认懒执行,只有当数据源变化才会执行回调函数,但如果希望创建的时候就执行一次可以使用immediate:true来强制执行
vue的watchEffect
作用:自动跟踪回调函数中的响应式依赖
//watch实现
watch(
todoId,
async () => {
const response = await fetch(
`https://jsonplaceholder.typicode.com/todos/${todoId.value}`
)
data.value = await response.json()
},
{ immediate: true }
)
//watchEffect简化版
watchEffect(async () => {
const response = await fetch(
`https://jsonplaceholder.typicode.com/todos/${todoId.value}`
)
data.value = await response.json()
})
适用场景:需要同时监听多个数据源,这个函数可以让开发者不需要手动维护依赖
动态加载组件Component
component组件不是真正的组件,和<slot>以及<template>一样有类似组件的特性,可以使用模板语法,但会在编译期间被编译掉
<component is="xxx"/>
interface DynamicComponentProps {
is: string | Component
}
v-slot指令
插槽
组件内部的slot元素是一个插槽出口,表明父元素的插槽内容渲染在哪里,即子组件只负责渲染子组件外层的标签和样式,而插槽的内容则由父组件提供。
<button class="fancy-btn">
<slot></slot> <!-- 插槽出口 -->
</button>
具名插槽
在某个组件有多个插槽出口,可以通过设置name属性给各个插槽分配唯一的id来确定每个地方渲染什么,如果没有设置则默认name为default
<header>
<slot name="header"></slot>
</header>
在调用该组件的时候,用v-slot:插槽名称或者缩写#插槽名称指令传递插槽名称,并且只能使用<template>元素
<BaseLayout>
<template v-slot:header>
<!-- header 插槽的内容放这里 -->
</template>
<template #default>
<!-- header 插槽的内容放这里 -->
</template>
</BaseLayout>
条件插槽
只有在父组件传入指定插槽内容的时候才渲染该插槽,在子组件内用$slots和v-if结合实现
场景:比如期望对插槽内容统一添加某些样式,但如果没有传入该插槽内容则不希望渲染这些样式
<div v-if="$slots.header" class="card-header">
<slot name="header" />
</div>
作用域插槽
目前缺陷:插槽内容无法访问子组件的数据
场景:插槽的内容需要同时使用父组件域内和子组件域内的数据
解决方法:让子组件在渲染时将一部分数据提供给插槽。即向一个插槽的出口上<slot>传递 attributes:,父组件v-slot:插槽名称="headerProps"
//App.vue
<template>
<FancyList api-url="url" :per-page="10">
<template #item="{item}">
<div class="item">
<p>{{ item.body }}</p>
<p class="meta">by {{ item.username }} | {{ item.likes }} likes</p>
</div>
</template>
</FancyList>
</template>
<!-- FancyList.vue -->
<template>
<ul>
<template v-if="!items.length">
<li>Loading...</li>
</template>
<template v-else>
<li v-for="(item, index) in items" :key="index">
<slot name="item" :item="item"/>
</li>
</template>
</ul>
</template>
【注意】插槽 prop 是一个对象,而不是单个值。因此要解构这个对象以便使用传递的值