这里终于明白了为什么一直有这个语法报错,就是在提示你哪里错的地方上方注释一行/*eslint-disable*/,之前一直警告这个错误感谢老师!
一、vue2和vue3生命周期
还有一个问题就是父组件和子组件哪个先挂载完毕呢?答案是子组件先挂载完毕。因为首先解析入口文件index.html=>main.ts=>App.vue=>Person.vue,所以当然是Person.vue先挂载完毕,才能继续解析App.vue。
<template>
<div class="person">
<h2>当前求和为:{{ sum }}</h2>
<button @click="changeSum">点我sum+1</button>
</div>
</template>
<!-- vue3写法 -->
<script lang="ts" setup name="Person">
import {
ref,
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
onBeforeUnmount,
onUnmounted
} from 'vue'
// 数据
let sum = ref(0)
// 方法
function changeSum() {
sum.value += 1
}
console.log('setup')
// 生命周期钩子
onBeforeMount(()=>{
console.log('挂载之前')
})
onMounted(()=>{
console.log('挂载完毕')
})
onBeforeUpdate(()=>{
console.log('更新之前')
})
onUpdated(()=>{
console.log('更新完毕')
})
onBeforeUnmount(()=>{
console.log('卸载之前')
})
onUnmounted(()=>{
console.log('卸载完毕')
})
</script>
二、自定义hooks
hook本质是一个函数,用use开头,把setup函数中使用的Composition API进行了封装。可以更方便我们去复用处理数据的逻辑.
有时候我们会写很多数据和方法,如果都写在Person.vue会很大很复杂也不好修改,所以我们可以在src下新建hooks文件夹 ,建立useDog.ts和useSum.ts,拆分Person.vue中的东西。
例如:
Person.vue
<template>
<div class="person">
<h2>当前求和为:{{ sum }},放大十倍{{ bigSum }}</h2>
<button @click="add">点我sum+1</button>
<hr>
<img v-for="(dog,index) in dogList" :src="dog" :key="index">
<hr>
<button @click=" getDog">再来一只小狗</button>
</div>
</template>
<script lang="ts" setup name="Person">
import useSum from '@/hooks/useSum'
import useDog from '@/hooks/useDog'
let { sum,add,bigSum} = useSum()
let { dogList, getDog } = useDog()
</script>
<style scoped>
.person {
background-color: skyblue;
border-radius: 0 0 10px;
box-shadow: 10px;
padding: 10px;
}
button {
margin: 5px;
}
img{
height: 100px;
}
</style>
useDog.ts
import { reactive, onMounted } from 'vue';
import axios from 'axios';
export default function () {
//数据
let dogList = reactive([
'https://images.dog.ceo/breeds/pembroke/n02113023_4373.jpg',
]);
//方法
async function getDog() {
try {
let result = await axios.get(
'https://dog.ceo/api/breed/pembroke/images/random'
);
dogList.push(result.data.message);
} catch (error) {
alert(error);
}
}
//钩子
onMounted(() => {
getDog();//千万别忘了在上面引入onMounted
});
//向外部提供数据
return { dogList, getDog };
}
useSum.ts
import { ref,onMounted,computed } from 'vue';
export default function () {
//数据
let sum = ref(0);
let bigSum = computed(()=>{
return sum.value*10
})
//方法
function add() {
sum.value += 1;
}
//钩子
onMounted(()=>{
add()
})
//向外传输
return {sum,add,bigSum}
}
效果:
但是可能遇到如下报错,'xxx' is never reassigned.use 'const' instead prefer-const,解决办法如下:
三、路由
1.理解:
2.路由的基本切换
其实大致和vue2是差不多的,
(1)首先要下载vue-router的依赖
(可能有些在创建项目的时候就已经选择了要配置router,那么这时候就不要下载了,可以去packge.json中查看自己的vue-router版本号如果有的话就不用下载了)
npm install vue-router@4(后面可以指定版本,或者不@直接下载就是最新版本)
(2)在src目录下新建一个router目录,在router目录下新建一个index.ts文件
//创建一个路由器并暴露粗去
//第一步:引入
import { createRouter, createWebHistory } from 'vue-router';
//引入一个个要呈现的组件
import Home from '../pages/Home.vue';
import News from '../pages/News.vue';
import About from '../pages/About.vue';
//第二步:创建路由器
const router = createRouter({
history: createWebHistory(),//路由器的工作模式
routes: [//引入路由规则
{
path: '/home',
component: Home,
},
{
path: '/news',
component: News,
},
{
path: '/about',
component: About,
},
],
});
export default router;
createRouter函数,新建一个路由器;
createWebHistory函数可以创建一个 HTML5 History 路由实例,从而能够支持浏览器的前进和后退按钮。
routes:存放一个一个的路由;在这里每一个路由都是一个对象,必须要指定的由两个参数:
path:指定路由的路径
component:指定路由对应的组件
(3)在main.ts中引入路由
import { createApp } from "vue"
import APP from './App.vue'
//引入路由
import router from './router'
//创建一个应用
const app = createApp(APP)
// 使用路由器
app.use(router)
//挂载整个应用到app中
.mount('#app')
(4)实现路由的跳转
在App.vue中这样写,利用RouterLink导航 和to=""路径active-class=""实现点哪哪亮效果
RouterLink标签会被渲染成一个 <a>
标签,点击它时会触发路由切换。
RouterView 标签是展示区,会根据当前路由的路径自动加载对应的组件,并将其渲染到页面中。
<template>
<div class="app">
<h2 class="title">Vue路由测试</h2>
<!-- 导航区 -->
<div class="navigate">
<RouterLink to="/home" active-class="xiaozhupeiqi">首页</RouterLink>
<RouterLink to="/news" active-class="xiaozhupeiqi">新闻</RouterLink>
<RouterLink to="/about" active-class="xiaozhupeiqi">关于</RouterLink>
</div>
<!-- 展示区 -->
<div class="main-content">
<RouterView></RouterView>
</div>
</div>
</template>
<script lang="ts" setup name="App">
import { RouterView, RouterLink } from 'vue-router'
</script>
<style>
/* App */
.title {
text-align: center;
word-spacing: 5px;
margin: 30px 0;
height: 70px;
line-height: 70px;
background-image: linear-gradient(45deg, gray, white);
border-radius: 10px;
box-shadow: 0 0 2px;
font-size: 30px;
}
.navigate {
display: flex;
justify-content: space-around;
margin: 0 100px;
}
.navigate a {
display: block;
text-align: center;
width: 90px;
height: 40px;
line-height: 40px;
border-radius: 10px;
background-color: gray;
text-decoration: none;
color: white;
font-size: 18px;
letter-spacing: 5px;
}
.navigate a.xiaozhupeiqi {
background-color: #64967E;
color: #ffc268;
font-weight: 900;
text-shadow: 0 0 1px black;
font-family: 微软雅黑;
}
.main-content {
margin: 0 auto;
margin-top: 30px;
border-radius: 10px;
width: 90%;
height: 400px;
border: 1px solid;
}</style>
(5)pages中的静态页面
分别有Home.vue,About.vue,News.vue 这里不再写明,可以去老师的笔记里面自取
(6)效果
3.两个注意点
4.两种工作模式
1. history模式
优点:`URL`更加美观,不带有`#`,更接近传统的网站`URL`。
缺点:后期项目上线,需要服务端配合处理路径问题,否则刷新会有`404`错误。
//创建一个路由器并暴露粗去
//第一步:引入
import { createRouter, createWebHistory } from 'vue-router';
//引入一个个要呈现的组件
import Home from '../pages/Home.vue';
//第二步:创建路由器
const router = createRouter({
history: createWebHistory(),//路由器的工作模式history
routes: [//引入路由规则
{
path: '/home',
component: Home,
}
],
});
//暴露出去
export default router;
2. hash模式
优点:兼容性更好,因为不需要服务器端处理路径。
缺点:`URL`带有`#`不太美观,且在`SEO`优化方面相对较差。
const router = createRouter({
history:createWebHashHistory(), //hash模式
/******/
})
5.to的两种写法
字符串写法和对象写法
<!-- 第一种:to的字符串写法 -->
<router-link active-class="active" to="/home">主页</router-link>
<!-- 第二种:to的对象写法 -->
<router-link active-class="active" :to="{path:'/home'}">Home</router-link>
其实对象写法也分为两种,在学完命名之后可以有name和path两种写法
6.命名路由
作用:可以简化路由跳转及传参
写法:name:'xxx'
这样写的话就可以在跳转路由的时候这样写:
7.嵌套路由
假如想在新闻的展示区再做一个导航区和显示区,如图所示,这时候就需要嵌套路由。
(1) 首先我们编写`News`的子路由,在pages下新建Detail.vue
<template>
<ul class="news-list">
<li>编号:xxx</li>
<li>标题:xxx</li>
<li>内容:xxx</li>
</ul>
</template>
<script setup lang="ts" name="About">
</script>
<style scoped>
.news-list {
list-style: none;
padding-left: 20px;
}
.news-list>li {
line-height: 30px;
}
</style>
(2)配置路由规则,使用children配置项
用法:children:[{ path:'xxxx', componnent:xxxx}]
{
name: 'xinwen',
path: '/news',
component: News,
children:[
{
path: 'detail',//嵌套路由路径不用加/
component: Detail,
}
]
},
(3) news.vue中跳转路由(记得要加完整路径)
<template>
<!-- 导航区 -->
<div class="news">
<ul>
<li v-for="news in newsList" :key="news.id">
<RouterLink to="/news/detail">{{ news.title }}</RouterLink>
</li>
</ul>
<!-- 展示区 -->
<div class="news-content">
<RouterView></RouterView>
</div>
</div>
</template>
<script setup lang="ts" name="News">
import { reactive } from 'vue';
import { RouterView,RouterLink } from 'vue-router';
const newsList = reactive([
{id:'cgp01',title:'今天周一',content:'嘻嘻'},
{id:'cgp02',title:'今天周二',content:'不嘻嘻'},
{id:'cgp03',title:'今天周三',content:'不不嘻嘻细'},
{id:'cgp04',title:'今天周四',content:'不不不嘻嘻'}
])
</script>
8.query参数
我们发现上方实现的效果newscontent的数据是写死的,那怎么把数据传过去呢?
(1)传递参数
用to传参有两种写法:
// News.vue
<ul>
<li v-for="news in newsList" :key="news.id">
<!-- 第一种写法 模板字符串写法 冗杂 -->
<!-- <RouterLink :to="`/news/detail?id=${news.id}`">{{ news.title }}</RouterLink> -->
<!-- 第二种写法 对象写法 -->
<RouterLink
:to="{
// path:'/news/detail',
name:'xiangqing',
query:{
id:news.id,
title:news.title,
content:news.content
}
}"
>
{{ news.title }}
</RouterLink>
</li>
</ul>
(2) 接收参数
<template>
<ul class="news-list">
<li>编号:{{query.id }}</li>
<li>标题:{{query.title}}</li>
<li>内容:{{query.content}}</li>
</ul>
</template>
<script setup lang="ts" name="About">
import { toRefs } from 'vue';
import { useRoute } from 'vue-router';
let route = useRoute()
// console.log(route);可以发现route上有我们需要的query参数
//解构让query参数等于route上面就不用写route.query.id
let { query } = toRefs(route)
// 直接解构query一定丢失响应式,导致点两次导航区就不更新了
// 加上torefs是不让query丢失响应式
</script>
需要注意的是 直接解构query一定会让其丢失响应式,导致点两次导航区就不更新了加上torefs是不让query丢失响应式。
9.params参数
第一种字符串:
<!-- params第一种 占位符+模板字符串 -->
<RouterLink :to="`/news/detail/${news.id}/${news.title}/${news.content}`">{{ news.title }}</RouterLink>
记得一定要占位符
这里有个注意点,就是万一不想传content怎么办?就是在占位符后面加个?就好了
第二种对象:
需要注意的是不能用path
<!-- 第二种 对象 params -->
<RouterLink
:to="{
name: 'xiangqing', //用name跳转
params: {
id: news.id,
title: news.title,
content: news.title
}
}"
>
{{ news.title }}
</RouterLink>
备注1:传递`params`参数时,若使用`to`的对象写法,必须使用`name`配置项,不能用`path`。
备注2:传递`params`参数时,需要提前在规则中占位。
Detail.vue
10.路由的props配置
之前上面写的detail.vue很冗杂,我们可以利用props配置
(1)props布尔值写法
props的布尔值写法,作用:把收到了每一组params参数,作为props传给Detail组件(只能和params配合)
props:true
(2) props的函数写法(常用)
作用:把返回的对象中每一组key-value作为props传给Detail组件
(3) 对象写法(少写)
作用:把对象中的每一组key-value作为props传给Detail组件
11.replace属性
1. 作用:控制路由跳转时操作浏览器历史记录的模式。
2. 浏览器的历史记录有两种写入方式:分别为push和replace:
push是追加历史记录(默认值)。
replace是替换当前记录。
3. 开启`replace`模式:
<RouterLink replace .......>News</RouterLink>
添加replace之后不能前进后退
12.编程式导航
先来实现一个效果,点击首页看三秒之后自动跳转到新闻页面。其实这就是实现了一个简单的编程式路由导航效果。
//home.vue
<script setup lang="ts" name="Home">
import { onMounted } from 'vue'
import { useRouter } from 'vue-router'
const router = useRouter()
onMounted(() => {
setTimeout(() => {
router.push('/news')
}, 3000)
})
</script>
那么接下来做一个复杂一点的:
在新闻页面的导航区增加按钮,通过点击按钮实现跳转。其实这也是现实开发中常用的编程时路由导航,而且一般都比routerlink使用的多。而且在vue2中用这个点击次数多可能会报错,但是在vue3中并不会。
//news.vue
<template>
<div class="news">
<!-- 导航区 -->
<ul>
<li v-for="news in newsList" :key="news.id">
<!-- 通过点击按钮实现路由跳转 -->
<button @click="showNewsDetail(news)">查看新闻</button>
<RouterLink :to="{
name: 'xiang',
query: {
id: news.id,
title: news.title,
content: news.content
}
}">
{{ news.title }}
</RouterLink>
</li>
</ul>
<!-- 展示区 -->
<div class="news-content">
<RouterView></RouterView>
</div>
</div>
</template>
<script setup lang="ts" name="News">
import { reactive } from 'vue'
import { RouterView, RouterLink, useRouter } from 'vue-router'
const newsList = reactive([
{ id: 'asfdtrfay01', title: '很好的抗癌食物', content: '西蓝花' },
{ id: 'asfdtrfay02', title: '如何一夜暴富', content: '学IT' },
{ id: 'asfdtrfay03', title: '震惊,万万没想到', content: '明天是周一' },
{ id: 'asfdtrfay04', title: '好消息!好消息!', content: '快过年了' }
])
const router = useRouter()
//写一个接口限制类型防止news报错
interface NewsInter {
id: string,
title: string,
content: string
}
function showNewsDetail(news: NewsInter) {
router.replace({
name: 'xiang',
query: {
id: news.id,
title: news.title,
content: news.content
}
})
}
效果:
13.重定向
1. 作用:将特定的路径,重新定向到已有路由。
2. 具体编码:
这样默认显示的教师你redirect后的页面。