vue3 整理再学习

听到ref、reactive,脑子就6个字ref一把梭,哈哈哈

听到watch结束,现在脑子里就只有五个字函数一把梭,哈哈哈,vue3官网也是函数一把梭!

还是规范用比较好。

npm create vue@latest

一、文件目录

1. env.d.ts

官方用来引入声明文件的配置文件。

vue
/// <reference types="vite/client" />

比如在 App.vue 中引入 componentsxxx.vue 模块能让 vue识别。

2. index.html

入口文件

vue


<script type="module" src="/src/main.ts"></script>

设置 id为 app是为了后把创建渲染好来自 main.ts的文件挂载到 入喉文件上。 <script type="module" src="/src/main.ts"></script> @SpringBootApplication 整个程序的入口和配置文件引入。

3. vite.config.ts

安装插件....... 后面再补充。

4.src

1.main.ts

ts
// 花盆
import { createApp } from 'vue'
// 根 其他.vue 文件都叫组件
import App from './App.vue'

// 把 花插在花盆里 创建一个应用把应用的跟组件传入并挂载到 id为app的容器里,也就是idnex.html 中的 div容器中
// 把VDOM 插入到真实的 DOM 里
createApp(App).mount('#app')

2.App.vue

重点是要有导出有路由就导路由

vue

<script lang="ts"> export default { name: "App" } </script> <template>
hello,bocai!
</template> <style lang="scss" scoped> </style>

3.components

一般组件

4.hooks

封装ts代码的

1.useSum.ts
2.useGetDogs.ts

5.router

用于SPA页面的导航路由。

6.views

路由组件

二、Vue3核心语法

1.OptionsAPI 与 CompositionAPI

Options API(选项式) 的弊端

Options 类型的 API ,数据,方法,计算属性等,是分散在: data、methods、computed中的,若想新增或者修改一个需求,就需要分别修改 data、methods、computed,不便于维护和复用。

Composition API(组合式) 的优势

用函数的方式把所有功能都缝合在一起,我喜欢这种模块化。封装封装再封装!

2. setup

语法糖罢了,省的return了。要注意的地方是执行的生命周期和设置name名称的时候要安装vite-plugin-vue-setup-extend

3. ref 基本、对象类型的响应式创建

现在对于ref的理解就是 ----> 1.响应式 2.里面有很多个_xxx的值和一个value,你只需要用asd.value就行了

4.reactive (只能定义)对象类型的响应式创建

这个也是响应式但是如果声明对象car = reactive({brand: "大奔",price: 100})用这个就行了,取值直接 car.price 就好了。

5. ref 和 reactive的区别

宏观看:

ref 用来定义: 基本数据类型、对象数据类型

reactive 用来定义: 对象数据类型

区别:

1.ref 创建变量必须使用.value(可以使用volar插件自动添加.value)。

2.reactive重新分配一个新对象(a: reactive = {a1: 1,a2:2} b: {b1: 1,b2:2},如果把b的值给a,直接 =赋值的话,会失去响应式),会失去响应式。(可以使用Object.assign(a,b)去整体换)。

3.ref可以整体替换这个b: {b1: 1,b2:2}吗?统一回答,可以!因为改的是ref的value值

使用原则:

1.若需要一个基本数据类型的响应式数据,必须使用ref

2.若需要一个响应式对象,层级不深,ref、reactive都可以。

3。若需要一个响应式对象,且层级较深,推荐使用reactive

6. toRefs 和 toRef

在解构ref和reactive时使他们的属性具有响应式。

7.computed

computed函数默认是只有 get(),也就是只读,如果想要修改computed的值记得写set()方法。

8.watch

1.作用

监视数据的变化

2.特点

在vue3中watch只能见识一下四种数据:

1.ref定义的数据

2.reactive 定义的数据

3.函数返回的一个值(getter函数)

4.一个包含上述内容的数组

vue
import { watch } from "vue"
// watch的回调函数是结束监听 如果value是对象类型那么坚挺的是他的地址值,如果深度检测那么就要写deep: true
const myWatch = watch(value,(newValue,oldValue)=>{
// 写逻辑
console.log("变了",newValue,oldValue)
if(newValue > 10) {
// 结束监视
myWatch();
}
},{deep: true})

情况一、二

监视【ref】定义的【对象类型】数据,监视的是对象的地址值,若想监视对象内部属性的变化,需要动手开启深度监视 (deep: true)

watch的第一个参数是:被监视的数据

watch的第二个参数是:监视的回调

watch的第三个参数是:配置对象(deep、immediate等等……)

情况三

监视reactive定义的【对象类型】数据,且默认开启了深度监视。

情况四

监视ref或reactive定义的【对象类型】中的某个属性,注意点如下:

函数式的本质是 getter()

1.若该属性值不是【对象类型】,需要写成函数式。

vue
watch(()=>{return person.name},()=>{
console.log("proson变化了",newValue,oldValue)
})

2.若该属性式 依然是【对象类型】,可以直接变,也可以写成函数,建议写成函数。

结论:

函数一把梭!

监视的数据不能是字符串!如果必须要监听他就要写成函数式。

监视的是对象里的属性,那么最好写函数式,注意点:若是对象监视的式地址值,需要关注对象内部,需要手动开启深度监视,也就是说你如果监视reactive里的某个属性式个对象,如果你要监视这个对象里的属性,尽管这个最外层的数据是reactive类型(reactive默认开启深度监视),那你还得加deep:true。

情况五

监视上述多个数据

数组里放 很多个 函数式或地址。

函!数!一!把!梭!

9.watchEffect

官网: 立即执行一个函数,同时响应式的追踪其依赖,并在以来更改时重新执行这个函数。(人话就是函数里用到谁就监视谁!)

watch 和watchEffect

1.都能监听响应式数据的变化,不同的时监听数据变化的方式不同。

watch:要明确指出监视的数据

watchEffect:不用明确指出监视的数据(函数中用到的哪些属性,那就监视哪些属性)。

watch

没有watchEffect用watch写,你就得一个一个监视。

vue
watch([temp,height],(value)=>{
let [newTemp,newHeight] = value
if(newTemp >= 60 || newHeight >= 80){
console.log("给服务器发请求!")
}
})

watchEffect

vue
watchEffect(()=>{
if(temp.value >= 60 || height.value >= 80){
console.log("给服务器发请求!")
}
})

10.标签的ref属性

作用:用于注册模板引用

  • 用在普通DOM标签上,获取的是DOM节点。
  • 用在组件标签上,获取的是组件标签的实例对象。

通常还可以用来取标签的DOM元素时解决与其他的标签id冲突。

11. props

看vue3官网

12.生命周期

  • 概念:vue组件在创建时要经历一系列初始化步骤,在此过程中vue会在合适的时机,调用特定的函数,从而让开发者有机会在特定的阶段运行自己的代码,这些特定的函数统称为:生命周期狗子。

  • 规律:

    神明周期整体氛围四个阶段,分别是:创建、挂载、更新、销毁,每个阶段都有自己的两个钩子,一前一后。

  • vue3的生命周期

    创建阶段:setup

    挂载阶段:onBeforeMount、onMounted

    更新阶段:onBeforeUpdate、onUpdated

    销毁阶段:onBeforeUnmount、onUnmounted

13.hooks

理解为java中的封装,也是在vue3中组合式api的重点!

三、路由

1. 【基本切换】

1.index.ts

ts
// 创建一个路由器,并暴露出去

// 第一步:引入createRouter
import {createRouter,createWebHistory} from 'vue-router'
// 引入可能要呈现的组件
import Home from '@/components/Home.vue'
import News from '@/components/News.vue'
import About from '@/components/About.vue'

// 第二部: 创建路由
const router = createRouter({
history: createWebHistory(),
routes:[
// 一个一个的路由规则

复制代码
]

})

// 暴露出去router
export default router

2.修改@/main.ts挂载路由

ts
// 引入 createApp 用于创建应用
import {createApp} from 'vue'
// 引入App根组件
import App from './App.vue'
// 引入路由器
import router from './router'

// 创建一个应用
const app = createApp(App)
// 使用路由器
app.use(router)
// 挂载整个应用到app容器中
app.mount('#app')

来个dmeo

vue
<template>


// active-class="likebocai" 被激活时的样式
<RouterLink to="/home" active-class="likebocai">首页</RouterLink>
<RouterLink to="/about" active-class="likebocai">关于</RouterLink>
<RouterLink to="/news" active-class="likebocai">新闻</RouterLink>



<RouterView></RouterView>

</template>

<script lang="ts" setup> import {RouterView,RouterLink} from 'vue-router' </script>

2.【两个注意点】

1.路由组件通常存放在pages或 views文件夹,一般组件通常存放在components文件夹。

2.通过点击导航,视觉效果上”消失“了的路由组件,默认是被销毁的,需要的时候再去挂载。

3.【to的两种写法】

vue

<router-link active-class="likebocai" to="/home">主页</router-link>

<router-link active-class="likebocai" :to="{path: '/home'}">主页</router-link>
<router-link active-class="likebocai" :to="{name: 'zhuye'}">主页</router-link>

4.【路由器的工作模式】

1.history模式

优点:URL更加美观,不带有#,更加接近传统的网站URL。

缺点: 后期项目上线,需要后端服务端配合处理路径问题,否者刷新会有404错误

ts
const router = createRouter({
history: createWebHistory(),// history模式
....
})

如果用的nginx转发,解决404问题可以这样做

nginx
location / {
...
try_files uri uri/ /index.html;
}

2.hash模式

优点: 兼容性更好,因为不需要服务器端处理路径。

缺点: URL带有#不太美观,且在SEO优化方面略差。

ts
const router = createRouter({
history:createWebHashHistory(),//hash模式
...
})

5.【路由传参】

1.query传参

1.路由传值的两种写法

News.vue

vue
<RouterLink :to="/news/detail?id=${id}&title=${title}&content=${content}" /><RouterLink :to="{ // path:'news/detail' name:'xiang', query:{ id:id, title:title, content:content } }" />

2.接受值的注意事项

接收参数: 重点是结构后失去响应式,所以必须加toRefs

Detail.vue

vue

<script lang="ts" setup> import {useRoute} from 'vue-router' const route = useRoute() const {query} = toRefs(route) </script> <template>

{{query.id}}

</template>

2.param传参

1.修改路由添加占位

@/router/index.ts

ts
// 创建一个路由器,并暴露出去

// 第一步:引入createRouter
import {createRouter,createWebHistory} from 'vue-router'
// 引入可能要呈现的组件
import Home from '@/components/Home.vue'
import News from '@/components/News.vue'
import About from '@/components/About.vue'
import Detail from '@/components/Detail.vue'

// 第二部: 创建路由
const router = createRouter({
history: createWebHistory(),
routes:[
...
{
name:'xinwen',
path:'/news',
component: News,
children:[
{
name:'xiang',
// /:content? 意思是可传可不传
path:'detail/:id/:title/:content?',
component: Detail
}
]
}

复制代码
]

})

// 暴露出去router
export default router

2.路由传值的两种写法

News.vue

子路由:to里一定要根据 name 写 切不可用 path

vue
<RouterLink :to="/news/detail/${id}/${title}/${content}">{{title}}</RouterLink>
<RouterLink :to="{ name:'xiang', params:{ id:id, title:title, content:content } }" >{{title}}</RouterLink>

3.接收值

Detail.vue

vue

<script lang="ts" setup> import {useRoute} from 'vue-router' const route = useRoute() </script> <template>

{{route.params.id}}

</template>

备注1: 传递params参数时,若是使用to的三次想写法,必须使用name配置项,不能用path。

备注2: 传递params参数时,需要提前在规则(路由中)中占位。

6.【路由规则的props配置】

ts
// 创建一个路由器,并暴露出去

// 第一步:引入createRouter
import {createRouter,createWebHistory} from 'vue-router'
// 引入可能要呈现的组件
import Home from '@/components/Home.vue'
import News from '@/components/News.vue'
import About from '@/components/About.vue'
import Detail from '@/components/Detail.vue'

// 第二部: 创建路由
const router = createRouter({
history: createWebHistory(),
routes:[
...
{
name:'xinwen',
path:'/news',
component: News,
children:[
{
name:'xiang',
// /:content? 意思是可传可不传
path:'detail/:id/:title/:content?',
component: Detail,
// 第一种写法:将路由收到的所有params参数作为props传给props传给路由组件
props:true

复制代码
                // 第二种写法: 函数写法。可以自己决定讲什么作为props作为props给路由组件
                // 这种写法切记是用query,不然就采取第一种写法。
                /* props(route){
            		return route.query
            	} */
                
                // 第三种写法: 对象写法。可以自己决定讲什么作为props作为props给路由组件
                // 不推荐,死值!
                /* props(route){
            		a: 100,
            		b: 200,
            		c: 300
            	} */
            }
        ]
    }
    
]

})

// 暴露出去router
export default router

以上相当于

vue
props:true 之后 <Detail /> ==> <Detail id:=?? title=?? content=??/>

Detail.vue

vue

<script lang="ts" setup> defineProps(['id','title','content']) </script> <template>

{{id}}

{{title}}

{{content}}

</template>

7.【路由的replace属性】

1.作用: 控制路由跳转时操作浏览器历史记录的模式。

2.浏览器的历史记录有两种写入方式:分别是push和replace

  • push时追加历史记录(默认值)。

  • replace是替换当前记录。
    3.开启repalce模式:
    下面例子:
    vue
    <template>


    // active-class="likebocai" 被激活时的样式
    <RouterLink replace to="/home" active-class="likebocai">首页</RouterLink>
    <RouterLink replace to="/about" active-class="likebocai">关于</RouterLink>
    <RouterLink replace to="/news" active-class="likebocai">新闻</RouterLink>



    <RouterView></RouterView>

    </template>

<script lang="ts" setup> import {RouterView,RouterLink} from 'vue-router' </script>

加上replace就会消除历史记录,无法前进或后退

8.【编程式导航】

没什么好说的,怎么写to就怎么写push/replace。

9.【重定向】

vue
{
path:'/',
redirect:'/home'
}

四、pinia

1.搭建pinia环境

第一步:npm install pinia

第二步:操作src/main.ts

ts
import { createApp } from 'vue'
import App from './App.vue'

/* 引入createPinia,用于创建pinia */
import { createPinia } from 'pinia'

/* 创建pinia */
const pinia = createPinia()
const app = createApp(App)

/* 使用插件 */{}
app.use(pinia)
app.mount('#app')

2.组合式

ts
import {defineStore} from 'pinia'
import axios from 'axios'
import {nanoid} from 'nanoid'
import {reactive} from 'vue'

export const useTalkStore = defineStore('talk',()=>{
// talkList就是state
const talkList = reactive(
JSON.parse(localStorage.getItem('talkList') as string) || []
)

// getATalk函数相当于action
async function getATalk(){
// 发请求,下面这行的写法是:连续解构赋值+重命名
let {data:{content:title}} = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
// 把请求回来的字符串,包装成一个对象
let obj = {id:nanoid(),title}
// 放到数组中
talkList.unshift(obj)
}
return {talkList,getATalk}
})

五、组件通信

1.props

概述:props是使用频率最高的一种通信方式,常用与 :父 ↔ 子。

  • 若 父传子:属性值是非函数,直接传。
  • 若 子传父:属性值是函数,父先给子一个东西,子触发callback函数,又传回给了父组件。

父组件:

vue
<template>

父组件,

我的车:{{ car }}

儿子给的玩具:{{ toy }}

<Child :car="car" :getToy="getToy"/>
</template> <script setup lang="ts" name="Father"> import Child from './Child.vue' import { ref } from "vue"; // 数据 const car = ref('奔驰') const toy = ref() // 方法 function getToy(value:string){ toy.value = value } </script>

子组件

vue
<template>

子组件

我的玩具:{{ toy }}

父给我的车:{{ car }}

<button @click="getToy(toy)">玩具给父亲</button>
</template> <script setup lang="ts" name="Child"> import { ref } from "vue"; const toy = ref('奥特曼') defineProps(['car','getToy']) </script>

2.自定义

  1. 概述:自定义事件常用于:子 => 父。
  2. 注意区分好:原生事件、自定义事件。
  • 原生事件:
    • 事件名是特定的(click、mosueenter等等)
    • 事件对象$event: 是包含事件相关信息的对象(pageX、pageY、target、keyCode)
  • 自定义事件:
    • 事件名是任意名称
    • 事件对象$event: 是调用emit时所提供的数据,可以是任意类型!!!
  1. 示例:

    html
    <template>

    <Child @send-toy="saveToy"/>
    </template>

    <script> const toy = ref('') // 用于保存传递过来的玩具 function saveToy(value: string) { console.log('saveToy',value) toy.value = value } </script>

    vue
    <template>
    <button @click="emit('send-toy',toy)">按钮</button>
    </template>

    <script> import { ref } from "vue" //子组件中,触发事件: const toy = ref('奥特曼') const emit = defineEmits(['send-toy']) </script>

3.mitt

概述:与消息订阅与发布(pubsub)功能类似,可以实现任意组件间通信。

安装mitt

shell
npm i mitt

新建文件:src\utils\emitter.ts

javascript
// 引入mitt
import mitt from "mitt";

// 创建emitter
const emitter = mitt()

/*
// 绑定事件
emitter.on('abc',(value)=>{
console.log('abc事件被触发',value)
})
emitter.on('xyz',(value)=>{
console.log('xyz事件被触发',value)
})

setInterval(() => {
// 触发事件
emitter.emit('abc',666)
emitter.emit('xyz',777)
}, 1000);

setTimeout(() => {
// 清理事件
emitter.all.clear()
}, 3000);
*/

// 创建并暴露mitt
export default emitter

接收数据的组件中:绑定事件、同时在销毁前解绑事件:

typescript
import emitter from "@/utils/emitter";
import { onUnmounted } from "vue";

// 绑定事件
emitter.on('send-toy',(value)=>{
console.log('send-toy事件被触发',value)
})

onUnmounted(()=>{
// 解绑事件
emitter.off('send-toy')
})

【第三步】:提供数据的组件,在合适的时候触发事件

javascript
import emitter from "@/utils/emitter";

function sendToy(){
// 触发事件
emitter.emit('send-toy',toy.value)
}

注意这个重要的内置关系,总线依赖着这个内置关系

4.v-model

  1. 概述:实现 父↔子 之间相互通信。

  2. 前序知识 —— v-model的本质

    vue

    <input
    type="text"
    :value="userName"
    @input="userName =(<HTMLInputElement>$event.target).value"

  3. 组件标签上的v-model的本质::moldeValue + update:modelValue事件。

    vue

    <AtguiguInput v-model="userName"/>

    <AtguiguInput :modelValue="userName" @update:model-value="userName = $event"/>

    AtguiguInput组件中:

    vue
    <template>

    </template> <script setup lang="ts" name="AtguiguInput"> // 接收props defineProps(['modelValue']) // 声明事件 const emit = defineEmits(['update:model-value']) </script>
  4. 也可以更换value,例如改成abc

    vue

    <AtguiguInput v-model:abc="userName"/>

    <AtguiguInput :abc="userName" @update:abc="userName = $event"/>

    AtguiguInput组件中:

    vue
    <template>

    </template> <script setup lang="ts" name="AtguiguInput"> // 接收props defineProps(['abc']) // 声明事件 const emit = defineEmits(['update:abc']) </script>
  5. 如果value可以更换,那么就可以在组件标签上多次使用v-model

    vue
    <AtguiguInput v-model:abc="userName" v-model:xyz="password"/>

站点概览
菠菜的小窝

上帝不让狗狗讲话是为了让人们知道,
爱与忠诚是要靠行动表达的。

  • 8
    文章
  • 3
    分类
  • 8
    标签
  • 2595
    总浏览