1.Vue简介
2020年9月18日,Vue.js发布版3.0版本,代号:One Piece
1.1.性能的提拔
打包巨细镌汰41%。
初次渲染快55%, 更新渲染快133%。
内存镌汰54%。
1.2.源码的升级
使用Proxy代替defineProperty实现相应式。
重写假造DOM的实现和Tree-Shaking。
1.3.拥抱TypeScript
Vue3可以更好的支持TypeScript。
1.4 新的特性
ref和reactive
新的生命周期函数
2.创建Vue3工程
基于 vite 创建(保举)- ## 1.创建命令
- npm create vue@latest
- ## 2.具体配置
- ## 配置项目名称
- √ Project name: vue3_test
- ## 是否添加TypeScript支持
- √ Add TypeScript? Yes
- ## 是否添加JSX支持
- √ Add JSX Support? No
- ## 是否添加路由环境
- √ Add Vue Router for Single Page Application development? No
- ## 是否添加pinia环境
- √ Add Pinia for state management? No
- ## 是否添加单元测试
- √ Add Vitest for Unit Testing? No
- ## 是否添加端到端测试方案
- √ Add an End-to-End Testing Solution? » No
- ## 是否添加ESLint语法检查
- √ Add ESLint for code quality? Yes
- ## 是否添加Prettiert代码格式化
- √ Add Prettier for code formatting? No
复制代码 3.Vue3核心语法
3.1.OptionsAPI 与 CompositionAPI
Vue2的APi操持是Options(设置)风格,Vue3则是Composition(组合)风格,让相干功能的代码更加有序的组合在一起
3.2.setup
概述
注意:setup函数中访问this是undefined
全部值和函数都可以界说到setup函数中,但须要通过return“交出去”出来让外界使用- <template>
- <div class="person">
- <h2>姓名:{{name}}</h2>
- <h2>年龄:{{age}}</h2>
- <button @click="changeName">修改名字</button>
- <button @click="changeAge">年龄+1</button>
- <button @click="showTel">点我查看联系方式</button>
- </div>
- </template>
- <script lang="ts">
- export default {
- name:'Person',
- setup(){
- // 数据,原来写在data中(注意:此时的name、age、tel数据都不是响应式数据)
- let name = '张三'
- let age = 18
- let tel = '13888888888'
- // 方法,原来写在methods中
- function changeName(){
- name = 'zhang-san' //注意:此时这么修改name页面是不变化的
- console.log(name)
- }
- function changeAge(){
- age += 1 //注意:此时这么修改age页面是不变化的
- console.log(age)
- }
- function showTel(){
- alert(tel)
- }
- // 返回一个对象,对象中的内容,模板中可以直接使用
- return {name,age,tel,changeName,changeAge,showTel}
- }
- }
- </script>
复制代码 setup返回值
如果时返回一个对象则正常被调用
如果返回是一个函数,则会自动挂载,无关模板- setup(){
- return ()=> '你好啊!'
- }
复制代码 setup语法糖
一样寻常来说须要使用vite插件简化
step1:npm i vite-plugin-vue-setup-extend -D
step2:修改vite.config.ts- import { defineConfig } from 'vite'
- import VueSetupExtend from 'vite-plugin-vue-setup-extend'
- export default defineConfig({
- plugins: [ VueSetupExtend() ]
- })
复制代码 step3:直接<script setup lang="ts" name=" erson">
3.3.ref 创建:根本范例的相应式数据
语法:let age = ref(初始值),
返回值:是RefImpl的实例对象,简称ref对象,ref对象的value属性是相应式的
注意
1.js中利用相应式对象须要用 xx.value ,在模板中直接用即可,无需.value
2.对于 let age = ref(18)来说,age不是相应式的,age.value是相应式的
3.4.reactive 创建:对象范例的相应式数据
- import { reactive } from 'vue'
复制代码 语法:iet 相应式对象 = reactive(源对象)
返回值:proxy的实例对象,简称相应式对象
注意.reactive不能界说根本数据范例
3.5.ref 创建:对象范例的相应式数据
若ref吸收的是对象范例,内部实在也是调用了reactive函数。
注意:调用对象的属性应该是 xx.value.属性
代码能证明齐备- <template>
- <div class="person">
- <h2>汽车信息:一台{{ car.brand }}汽车,价值{{ car.price }}万</h2>
- <h2>游戏列表:</h2>
- <ul>
- <li v-for="g in games" :key="g.id">{{ g.name }}</li>
- </ul>
- <h2>测试:{{obj.a.b.c.d}}</h2>
- <button @click="changeCarPrice">修改汽车价格</button>
- <button @click="changeFirstGame">修改第一游戏</button>
- <button @click="test">测试</button>
- </div>
- </template>
- <script lang="ts" setup name="Person">
- import { ref } from 'vue'
- // 数据
- let car = ref({ brand: '奔驰', price: 100 })
- let games = ref([
- { id: 'ahsgdyfa01', name: '英雄联盟' },
- { id: 'ahsgdyfa02', name: '王者荣耀' },
- { id: 'ahsgdyfa03', name: '原神' }
- ])
- console.log(car)
- function changeCarPrice() {
- car.value.price += 10
- }
- function changeFirstGame() {
- games.value[0].name = '流星蝴蝶剑'
- }
- function test(){
- obj.value.a.b.c.d = 999
- }
- </script>
复制代码 3.6.ref 对比 reactive
使用原则:
- 若须要一个根本范例的相应式数据,必须使用ref。
- 若须要一个相应式对象,层级不深,ref、reactive都可以。
- 若须要一个相应式对象,且层级较深,保举使用reactive。
注意:reactive重新分配一个新对象,会失去相应式(可以使用Object.assign去团体更换)- let stu = reactive({name:'张三',age:18})
- stu = {name:'王五',age:19}//这样会失去响应式,
- //可以使用Object.assign整体替换
- Object.assign(stu,{'王五' ,19})
复制代码 3.7 toRefs 与 toRef
作用:将一个相应式对象中的全部属性转化为ref对象
注意:toRefs与toRef功能划一,但toRefs可以批量转换。- <template>
- <div class="person">
- <h2>姓名:{{person.name}}</h2>
- <h2>年龄:{{person.age}}</h2>
- <h2>性别:{{person.gender}}</h2>
- <button @click="changeName">修改名字</button>
- <button @click="changeAge">修改年龄</button>
- <button @click="changeGender">修改性别</button>
- </div>
- </template>
- <script lang="ts" setup name="Person">
- import {ref,reactive,toRefs,toRef} from 'vue'
- // 数据
- let person = reactive({name:'张三', age:18, gender:'男'})
-
- // 通过toRefs将person对象中的n个属性批量取出,且依然保持响应式的能力
- let {name,gender} = toRefs(person)
-
- // 通过toRef将person对象中的gender属性取出,且依然保持响应式的能力
- let age = toRef(person,'age')
- // 方法
- function changeName(){
- name.value += '~'
- }
- function changeAge(){
- age.value += 1
- }
- function changeGender(){
- gender.value = '女'
- }
- </script>
复制代码 3.8.computed
只有待盘算的值发生改变,computed才可以重新盘算一次(比力智慧)
默认盘算的值是只读的,只有当写上get()和set()方法,盘算出来的值才是可读可写的- <script setup lang="ts" name="App">
- import {ref,computed} from 'vue'
- let firstName = ref('zhang')
- let lastName = ref('san')
- // 计算属性——只读取,不修改
- /* let fullName = computed(()=>{
- return firstName.value + '-' + lastName.value
- }) */
- // 计算属性——既读取又修改
- let fullName = computed({
- // 读取
- get(){
- return firstName.value + '-' + lastName.value
- },
- // 修改
- set(val){
- console.log('有人修改了fullName',val)
- firstName.value = val.split('-')[0]
- lastName.value = val.split('-')[1]
- }
- })
- //因为前面有了set方法,所以现在可读可写
- fullName.vaule = 'li-si'
-
- </script>
复制代码 3.9. watch
情况一:监督ref界说的根本范例数据
监督函数watch中直接写根本范例,无需加 ‘.value’- <template>
- <div class="watch">
- <h1>{{sum}}</h1>
- <button @click="changeSum">加一</button>
- </div>
- </template>
- <script lang="ts" setup name="watch">
- import{ref,watch} from 'vue'
- let sum = ref(0)
- function changeSum(){
- sum.value+=1
- }
- //直接监视
- watch(sum,(newValue,oldValue)=>{
- console.log('变化了')
- })
- //有返回值,返回值是一个结束监视函数
- const stopWatch = watch(sum ,(newValue,oldValue)=>{
- if(sum.value>10){
- stopWatch()
- }
- })
- </script>
复制代码 情况2: 监督ref界说的对象范例数据
默认当对象的某一个属性厘革时,不会触发监督函数回调,只有当整个对象厘革(所在值厘革)才会相应,但是可以开启深度监督- /*
- 监视,情况一:监视【ref】定义的【对象类型】数据,监视的是对象的地址值,若想监视对象内部属性的变化,需要手动开启深度监视
- watch的第一个参数是:被监视的数据
- watch的第二个参数是:监视的回调
- watch的第三个参数是:配置对象(deep、immediate等等.....)
- */
- watch(person,(newValue,oldValue)=>{
- console.log('person变化了',newValue,oldValue)
- },{deep:true})
复制代码 情况3:监督reactive界说的对象范例数据
和情况2类似,但是默认开启深度监督
情况4:监督ref和reactive界说的对象范例中的某个属性
1.若该属性不是对象范例,须要写成函数情势
2.是对象范例,可以直接写- // 监视,情况四:监视响应式对象中的某个属性,且该属性是基本类型的,要写成函数式
- watch(()=> person.name,(newValue,oldValue)=>{
- console.log('person.name变化了',newValue,oldValue)
- })
- // 监视,情况四:监视响应式对象中的某个属性,且该属性是对象类型的,可以直接写,也能写函数,更推荐写函数
- watch(()=>person.car,(newValue,oldValue)=>{
- console.log('person.car变化了',newValue,oldValue)
- },{deep:true})
复制代码 情况5:监督多个属性- // 监视,情况五:监视上述的多个数据
- watch([()=>person.name,person.car],(newValue,oldValue)=>{
- console.log('person.car变化了',newValue,oldValue)
- },{deep:true})
复制代码 3.10. watchEffect
不消指明监督的属性,函数用到哪个监督哪个- const stopWtach = watchEffect(()=>{
- // 室温达到50℃,或水位达到20cm,立刻联系服务器
- if(temp.value >= 50 || height.value >= 20){
- console.log(document.getElementById('demo')?.innerText)
- console.log('联系服务器')
- }
- // 水温达到100,或水位达到50,取消监视
- if(temp.value === 100 || height.value === 50){
- console.log('清理了')
- stopWtach()
- }
- })
复制代码 3.11.标签的ref属性
差别组件的纵然有雷同的ref也不会辩说
定位到组件上,如果想要取消掩护机制,袒露属性可以用defineExpose- <!-- 子组件Person.vue中要使用defineExpose暴露内容 -->
- <script lang="ts" setup name="Person">
- import {ref,defineExpose} from 'vue'
- // 数据
- let name = ref('张三')
- let age = ref(18)
- /****************************/
- /****************************/
- // 使用defineExpose将组件中的数据交给外部
- defineExpose({name,age})
- </script>
复制代码 增补:接口界说和使用
界说- // 定义一个接口,限制每个Person对象的格式
- export interface PersonInter {
- id:string,
- name:string,
- age:number
- }
-
- // 定义一个自定义类型Persons
- export type Persons = Array<PersonInter>
复制代码 使用- import {type PersonInter} from './types'
- import {type Persons} from './types'
- let person:PersonInter = {id:'asyud7asfd01',name:张三',age:60}
- let personList:Array<PersonInter> = [
- {id:'asyud7asfd01',name:'张三',age:60},
- {id:'asyud7asfd02',name:"李四',age:18},
- {id: asyud7asfd03',name:'王五',age:5}]
- let persons = reactive<Persons>([
- {id:'e98219e12',name:'张三',age:18},
- {id:'e98219e13',name:'李四',age:19},
- {id:'e98219e14',name:'王五',age:20}
- ])
复制代码 3.12. props
让父组件可以大概向子组件通报数据。
<h2 a="1+1" :b="1+1" c="x" :d="x" ref="qwe"></h2>- <template>
- <div class="person">
- <ul>
- <li v-for="item in list" :key="item.id">
- {{item.name}}--{{item.age}}
- </li>
- </ul>
- </div>
- </template>
-
- <script lang="ts" setup name="Person">
- import {defineProps} from 'vue'
- import {type PersonInter} from '@/types'
-
- // 第一种写法:仅接收
- const props = defineProps(['list'])
-
- // 第二种写法:接收+限制类型
- const props = defineProps<{list:Persons}>()
-
- // 第三种写法:接收+限制类型+指定默认值+限制必要性
- let props = withDefaults(defineProps<{list?:Persons}>(),{
- list:()=>[{id:'asdasg01',name:'小猪佩奇',age:18}]
- })
- console.log(props)
- </script>
复制代码 3.12.生命周期
注意:使用时间须要从vue中impoort
创建阶段:setup
挂载阶段:onBeforeMount、onMounted
更新阶段:onBeforeUpdate、onUpdated
卸载阶段:onBeforeUnmount、onUnmounted
3.13.自界说hook
可以在src下创建hooks文件夹存放hook文件,定名一样寻常为“useXxx",把具有接洽的值和函数存放到一个文件夹
比方
useSum.ts- import {ref,onMounted} from 'vue'
- export default function(){
- let sum = ref(0)
- const increment = ()=>{
- sum.value += 1
- }
- const decrement = ()=>{
- sum.value -= 1
- }
- onMounted(()=>{
- increment()
- })
- //向外部暴露数据
- return {sum,increment,decrement}
- }
复制代码 useDog.ts- import {reactive,onMounted} from 'vue'
- import axios,{AxiosError} from 'axios'
- export default function(){
- let dogList = reactive<string[]>([])
- // 方法
- async function getDog(){
- try {
- // 发请求
- let {data} = await axios.get('https://dog.ceo/api/breed/pembroke/images/random')
- // 维护数据
- dogList.push(data.message)
- } catch (error) {
- // 处理错误
- const err = <AxiosError>error
- console.log(err.message)
- }
- }
- // 挂载钩子
- onMounted(()=>{
- getDog()
- })
-
- //向外部暴露数据
- return {dogList,getDog}
- }
复制代码 详细使用- <template>
- <h2>当前求和为:{{sum}}</h2>
- <button @click="increment">点我+1</button>
- <button @click="decrement">点我-1</button>
- <hr>
- <img v-for="(u,index) in dogList.urlList" :key="index" :src="(u as string)">
- <span v-show="dogList.isLoading">加载中......</span><br>
- <button @click="getDog">再来一只狗</button>
- </template>
- <script setup lang="ts">
- import useSum from './hooks/useSum'
- import useDog from './hooks/useDog'
-
- let {sum,increment,decrement} = useSum()
- let {dogList,getDog} = useDog()
- </script>
复制代码
4.路由
4.1.设置
router文件下设置- 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
-
- },
- {
- }
- ]
- })
复制代码 main.ts文件- import router from './router/index'
- app.use(router)
- app.mount('#app')
复制代码 App.vue文件
注意<RouterLink to=' '></RouterLink> 和<RouterView><RouterView>
- 路由组件通常存放在pages 或 views文件夹,一样寻常组件通常存放在components文件夹。
- 通过点击导航,视觉效果上“消散” 了的路由组件,默认是被卸载掉的,须要的时间再去挂载。
- <template>
- <div class="app">
- <h2 class="title">Vue路由测试</h2>
- <!-- 导航区 -->
- <div class="navigate">
- <RouterLink to="/home" active-class="active">首页</RouterLink>
- <RouterLink to="/news" active-class="active">新闻</RouterLink>
- <RouterLink to="/about" active-class="active">关于</RouterLink>
- </div>
- <!-- 展示区 -->
- <div class="main-content">
- <RouterView></RouterView>
- </div>
- </div>
- </template>
- <script lang="ts" setup name="App">
- import {RouterLink,RouterView} from 'vue-router'
- </script>
复制代码 4.2.路由器工作模式
history模式
长处:URL更加雅观,不带有#,更靠近传统的网站URL。
缺点:后期项目上线,须要服务端共同处理惩罚路径题目,否则革新会有404错误。- const router = createRouter({
- history:createWebHistory(), //history模式
- /******/
- })
复制代码 hash模式
长处:兼容性更好,由于不须要服务器端处理惩罚路径。
缺点:URL带有#不太雅观,且在SEO优化方面相对较差- const router = createRouter({
- history:createWebHashHistory(), //hash模式
- /******/
- })
复制代码 4.3.to的两种写法
- <RouterLink to="/home">主页</RouterLink>
- <RouterLink to="{path:'/home'}">主页</RouterLink>
复制代码 4.4.定名路由
在路由表中添加name属性
可以通过<RouterLink to="{name:'zhuye'}"><RouterLink>直接跳转
4.5.嵌套路由(子路由)
在一个路由下面增长children属性,再写入其他路由- const router = createRouter({
- history:createWebHistory(),
- routes:[
- {
- name:'zhuye',
- path:'/home',
- component:Home
- },
- {
- name:'xinwen',
- path:'/news',
- component:News,
- children:[
- {
- name:'xiang',
- path:'detail',
- component:Detail
- }
- ]
- },
- {
- name:'guanyu',
- path:'/about',
- component:About
- }
- ]
- })
- export default router
复制代码 4.6.路由传参
query参数
- 通报参数
- <!-- 跳转并携带query参数(to的字符串写法) -->
- <router-link to="/news/detail?a=1&b=2&content=欢迎你">
- 跳转
- </router-link>
-
- <!-- 跳转并携带query参数(to的对象写法) -->
- <RouterLink
- :to="{
- //name:'xiang', //用name也可以跳转
- path:'/news/detail',
- query:{
- id:news.id,
- title:news.title,
- content:news.content
- }
- }"
- >
- {{news.title}}
- </RouterLink>
复制代码 - 吸收参数:
- import{useRoute} from 'vue-router'
- const route = useRoute()
- console.log(route.query)
复制代码 params参数
- 通报参数
- <!-- 跳转并携带params参数(to的字符串写法) -->
- <RouterLink :to="`/news/detail/001/新闻001/内容001`">{{news.title}}</RouterLink>
-
- <!-- 跳转并携带params参数(to的对象写法) -->
- <RouterLink
- :to="{
- name:'xiang', //用name跳转
- params:{
- id:news.id,
- title:news.title,
- content:news.title
- }
- }"
- >
- {{news.title}}
- </RouterLink>
复制代码 - 吸收参数:
- import {useRoute} from 'vue-router'
- const route = useRoute()
- // 打印params参数
- console.log(route.params)
复制代码 3.占位- [name:'xinwen',
- path:'/news',
- component:News,
- children:[
- name:'xiang',
- //占位!!!!
- path: 'detail/:id/:title/:content',
- component:Detail[
- ]
复制代码 注意
备注1:通报params参数时,若使用to的对象写法,必须使用name设置项,不能用path。
备注2:通报params参数时,须要提前在规则中占位。
4.7. 路由的prop设置
使传参更优雅- {
- name:'xiang',
- path:'detail/:id/:title/:content',
- component:Detail,
- // props的对象写法,作用:把对象中的每一组key-value作为props传给Detail组件
- // props:{a:1,b:2,c:3},
- // props的布尔值写法,作用:把收到了每一组params参数,作为props传给Detail组件
- // props:true
-
- // props的函数写法,作用:把返回的对象中每一组key-value作为props传给Detail组件
- props(route){
- return route.query
- }
- }
复制代码 4.8.replace属性
控制路由跳转时利用欣赏器汗青记载模式
- 欣赏器的汗青记载有两种写入方式:分别为push和replace:
- push是追加汗青记载(默认值)。
- replace是更换当前记载。
- 开启replace模式:
- <RouterLink replace .......>News</RouterLink>
复制代码
4.9.编程式导航
在vue3中,$route和$router变成了两个hook,就是在导航区以外的地方通过一些利用实现路由跳转- <template>
- <div>
- 当前路由路径: {{ route.path }}
- </div>
- </template>
- <script setup>
- import { useRoute } from 'vue-router';
- const route = useRoute();
- </script>
复制代码- <template>
- <button @click="goToHome">前往首页</button>
- </template>
- <script setup>
- import { useRouter } from 'vue-router';
- const router = useRouter();
- const goToHome = () => {
- router.push('/home');
- };
- </script>
复制代码 增补知识点,定时使命
- //挂载后,页面三秒自动跳转
- onMounted(()=>{
- router.push('/home')
- },3000)
复制代码 4.10.重定向
将特定的路径,重新定向到已经有的路由,通过redirect- [
- path:'/',
- redirect:'/about'
- ]
复制代码 5.pinia
5.1.搭建情况
step1:npm install pinia
step2:利用 src/main.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')
复制代码 5.2.存储+读取数据
store是pinia的一个功能部件,我的明白是可以提取公共的数据大概函数供其他组件使用
在src/store中创建文件
详细编码src/store/count.ts- //引入defineStore用于创建store
- import {defineStore} from 'pinia'
- //定义并暴露一个store
- export const useCountstore = defineStore('count',{
- //动作
- actions:{},
- //状态
- state(){
- return {
- sum:6
- }
- },
- //计算
- getters:{}
- })
复制代码 使用 - <template>
- <h1>当前的求和为:{{countStore.sum}}</h1>
- </template>
- <script setup lang="ts" name="count">
- //引入对应的useXxxStore
- import {useCountStore} from '@/src/store/count'
- //调用对应的useXxxStore得到对应的store
- const countStore = useCountStore()
- </script>
复制代码 5.3.修改数据
1.直接在调用的地方加(vue3特性)
countStore.sum = 666
2.批量修改,通过patch函数
countStore.$patch({
sum:999
age:111
})
3.通过store内里的action创建修改store数据的方法- import {defineStore} from 'pinia'
- export const useCountStore = defineStore('count' ,{
- actions: {
- //加
- increment(value:number) {
- if (this.sum < 10) {
- //操作countStore中的sum
- this.sum += value
- }
- },
- //减
- decrement(value:number){
- if(this.sum > 1){
- this.sum -= value
- }
- }
- },
- /*************/
- })
复制代码 5.4.storeToRefs
借助storeToRefs会将store中的数据转化为ref对象,而store中的函数稳定
通过toRefs会将store的全部都转化为ref对象,包罗函数- <template>
- <div class="count">
- <h2>当前求和为:{{sum}}</h2>
- </div>
- </template>
- <script setup lang="ts" name="Count">
- import { useCountStore } from '@/store/count'
- /* 引入storeToRefs */
- import { storeToRefs } from 'pinia'
- /* 得到countStore */
- const countStore = useCountStore()
- /* 使用storeToRefs转换countStore,随后解构 */
- const {sum} = storeToRefs(countStore)
- </script>
复制代码 5.5.getters
当state中的数据,须要颠末处理惩罚后再使用时,可以使用getters设置。{感觉有点鸡肋,在state不是也可以吗},,记得在使用时和state一起import- // 引入defineStore用于创建store
- import {defineStore} from 'pinia'
- // 定义并暴露一个store
- export const useCountStore = defineStore('count',{
- // 动作
- actions:{
- /************/
- },
- // 状态
- state(){
- return {
- sum:1,
- school:'atguigu'
- }
- },
- // 计算
- getters:{
- bigSum:(state):number => state.sum *10,
- upperSchool():string{
- return this. school.toUpperCase()
- }
- }
- })
复制代码 5.6.$subscribe
监督厘革,和watch类似
mutate:关于状态厘革的信息
state:最新的state对象- talkStore.$subscribe((mutate,state)=>{
- console.log('LoveTalk',mutate,state)
- })
复制代码 5.7.store组合式写法
将数据和方法写在一个函数内里,别忘了末了须要return交出去- import {defineStore} from 'pinia'
- import { ref } from 'vue';
- export const useSumStore = defineStore('sum' ,()=>{
- const sum = ref(999)
- function add(){
- sum.value++
- }
- return {sum,add}
- })
复制代码 6.组件通讯
6.1.props
父传子,引入子组件的时间直接附带须要通报的值
父传子- <template>
- <div class="father">
- <h3>父组件,</h3>
- <Child :car="car" :getToy="getToy"/>
- </div>
- </template>
- <script setup lang="ts" name="Father">
- import Child from './Child.vue'
- import { ref } from "vue";
- // 数据
- const car = ref('奔驰')
- </script>
复制代码 子担当,通过defineProps
子传父,须要通过父通报的函数把值通报给父,就像父界说了一个构造函数,但是由子来调用这个构造函数从而获取详细的值
子调用父的函数- <template>
- <div class="child">
- <h3>子组件</h3>
- <h4>我的玩具:{{ toy }}</h4>
- <button @click="getToy(toy)">玩具给父亲</button>
- </div>
- </template>
- <script setup lang="ts" name="Child">
- import { ref } from "vue";
- const toy = ref('奥特曼')
-
- defineProps(['getToy'])
- </script>
复制代码 父通过子调用函数获取值- <template>
- <div class="father">
- <h3>父组件,</h3>
- <h4>儿子给的玩具:{{ toy }}</h4>
- <Child :getToy="getToy"/>
- </div>
- </template>
- <script setup lang="ts" name="Father">
- import Child from './Child.vue'
- import { ref } from "vue";
- // 数据
- const toy = ref()
- // 方法
- function getToy(value:string){
- toy.value = value
- }
- </script>
复制代码 6.2 自界说变乱
我以为这又有点像一种高级的监听机制,父组件在子组件绑定一个变乱并声明一个回调函数,只要子组件发生这个变乱,父组件就会调用这个回调函数,而且还会受到子组件的值
重要是注意一些格式,子组件在担当变乱的时间 通过const emits = defineEmits(['send-toy'])
使用时通过emit('send-toy' ,toy)来相应变乱,从而时父组件担当变乱附带的值并实行回调函数
6.3mitt
好像一个第三方的自界说变乱功能,吸收数据者界说变乱,提供数据者触发变乱,相比自界说变乱,更“自由机动”
step1:npm i mitt
step2:在util文件夹中创建emitter.ts- //引入emit
- import mitt from "mitt"
- //创建emitter
- const emitter = mitt()
- //创建并暴露mitt
- export default emitter
复制代码 绑定变乱
emitter.on('send-toy' (value)=>{ console.log("担当value并实行回调函数")})
触发变乱
emitter.emit('send-toy' ,666)
注意,在组件卸载时间,只管解绑当前组件绑定的变乱,制止斲丧内存
onUmMounted(()=>{ emitter.off('send-toy')})
6.4.v-model
v-model的本质- <!-- 使用v-model指令 -->
- <input type="text" v-model="userName">
- <!-- v-model的本质是下面这行代码 -->
- <input
- type="text"
- :value="userName"
- @input="userName =(<HTMLInputElement>$event.target).value"
- >
复制代码
6.5.$attrs
通常用于父与孙的通报,但是中心的桥梁是子组件,
详细分析:$attrs是一个对象,包罗全部父组件传入的标签属性。(个人明白:动态的资源聚集)
注意:$attrs会打扫props中已经声明的属性(相当于已经被用了),但把剩下的仍在$attrs中
父组件- <template>
- <div class="father">
- <h3>父组件</h3>
- <Child :a="a" :b="b" :c="c" :d="d" v-bind="{x:100,y:200}" :updateA="updateA"/>
- </div>
- </template>
- <script setup lang="ts" name="Father">
- import Child from './Child.vue'
- import { ref } from "vue";
- let a = ref(1)
- let b = ref(2)
- let c = ref(3)
- let d = ref(4)
- function updateA(value){
- a.value = value
- }
- </script>
复制代码 子组件- <template>
- <div class="child">
- <h3>子组件</h3>
- <GrandChild v-bind="$attrs"/>
- </div>
- </template>
- <script setup lang="ts" name="Child">
- import GrandChild from './GrandChild.vue'
- </script>
复制代码 孙组件- <template>
- <div class="grand-child">
- <h3>孙组件</h3>
- <h4>a:{{ a }}</h4>
- <h4>b:{{ b }}</h4>
- <h4>c:{{ c }}</h4>
- <h4>d:{{ d }}</h4>
- <h4>x:{{ x }}</h4>
- <h4>y:{{ y }}</h4>
- <button @click="updateA(666)">点我更新A</button>
- </div>
- </template>
- <script setup lang="ts" name="GrandChild">
- defineProps(['a','b','c','d','x','y','updateA'])
- </script>
复制代码 6.6.$refs,$parent
- 概述:
- $refs用于 :父→子。
- $parent用于:子→父。
- 原理如下:
属性分析$refs值为对象,包罗全部被ref属性标识的DOM元素或组件实例。$parent值为对象,当前组件的父组件实例对象。
但是须要用 defineExpose暴袒露去,使用时间不须要显式通报,直接用
6.7. provide和inject
- 在先人组件中通过provide设置向后代组件提供数据
- 在后代组件中通过inject设置来声明吸收数据
在父组件中,通过provide提供数据- <template>
- <div class="father">
- <h3>父组件</h3>
- <Child/>
- </div>
- </template>
- <script setup lang="ts" name="Father">
- import Child from './Child.vue'
- import { ref,reactive,provide } from "vue";
- // 数据
- let money = ref(100)
- let car = reactive({
- brand:'奔驰',
- price:100
- })
- // 用于更新money的方法
- function updateMoney(value:number){
- money.value += value
- }
- // 提供数据
- provide('moneyContext',{money,updateMoney})
- provide('car',car)
- </script>
复制代码 后代中通过inject担当数据,只管代码有默认值- <template>
- <div class="grand-child">
- <h3>我是孙组件</h3>
- <h4>资产:{{ money }}</h4>
- <h4>汽车:{{ car }}</h4>
- <button @click="updateMoney(6)">点我</button>
- </div>
- </template>
- <script setup lang="ts" name="GrandChild">
- import { inject } from 'vue';
- // 注入数据
- let {money,updateMoney} = inject('moneyContext',{money:0,updateMoney:(x:number)=>{}})
- let car = inject('car')
复制代码
6.7.solt
1.默认插槽
父组件在声明子组件时会通报一些供以表现的数据,这时间用插槽为这些数据的表现提供位置- 父组件中:
- <Category title="今日热门游戏">
- <ul>
- <li v-for="g in games" :key="g.id">{{ g.name }}</li>
- </ul>
- </Category>
- 子组件中:
- <template>
- <div class="item">
- <h3>{{ title }}</h3>
- <!-- 默认插槽 -->
- <slot></slot>
- </div>
- </template>
复制代码 2.具名插槽
指定插在哪个位置,通过v-slot大概#slot- 父组件中:
- <Category title="今日热门游戏">
- <template v-slot:s1>
- <ul>
- <li v-for="g in games" :key="g.id">{{ g.name }}</li>
- </ul>
- </template>
- <template #s2>
- <a href="">更多</a>
- </template>
- </Category>
- 子组件中:
- <template>
- <div class="item">
- <h3>{{ title }}</h3>
- <slot name="s1"></slot>
- <slot name="s2"></slot>
- </div>
- </template>
复制代码 3.作用域插槽
数据在组件的自身,但根据数据天生的结构须要组件的使用者来决定
子组件通过在插槽声明的位置声明数据,由使用者利用- 父组件中:
- <Game v-slot="params">
- <!-- <Game v-slot:default="params"> -->
- <!-- <Game #default="params"> -->
- <ul>
- <li v-for="g in params.games" :key="g.id">{{ g.name }}</li>
- </ul>
- </Game>
- 子组件中:
- <template>
- <div class="category">
- <h2>今日游戏榜单</h2>
- <slot :games="games" a="哈哈"></slot>
- </div>
- </template>
- <script setup lang="ts" name="Category">
- import {reactive} from 'vue'
- let games = reactive([
- {id:'asgdytsa01',name:'英雄联盟'},
- {id:'asgdytsa02',name:'王者荣耀'},
- {id:'asgdytsa03',name:'红色警戒'},
- {id:'asgdytsa04',name:'斗罗大陆'}
- ])
- </script>
复制代码 7.其他API
7.1.shallowRef 与 shallowReactive
绕开深度相应,提拔性能
shallowRef
- 作用:创建一个相应式数据,但只对顶层【属性】举行相应式处理惩罚。
- 用法:
- let myVar = shallowRef(initialValue);
复制代码 - 特点:只跟踪引用值的厘革,不关心值内部的属性厘革。
shallowReactive
- 作用:创建一个浅层相应式对象,只会使对象的【最顶层属性】变成相应式的,对象内部的嵌套属性则不会变成相应式的
- 用法:
- const myObj = shallowReactive({ ... });
复制代码 - 特点:对象的顶层属性是相应式的,但嵌套对象的属性不是。
7.2.readonly 与 shallowReadonly
readonly
- 作用:用于创建一个对象的深只读副本。
- 用法:
- const original = reactive({ ... });
- const readOnlyCopy = readonly(original);
复制代码 - 特点:
- 对象的全部嵌套属性都将变为只读。
- 任何实验修改这个对象的利用都会被制止(在开辟模式下,还会在控制台中发出告诫)。
- 应用场景:
- 创建不可变的状态快照。
- 掩护全局状态或设置不被修改。
shallowReadonly
- 作用:与 readonly 类似,但只作用于对象的顶层属性。
- 用法:
- const original = reactive({ ... });
- const shallowReadOnlyCopy = shallowReadonly(original);
复制代码 - 特点:
- 只将对象的顶层属性设置为只读,对象内部的嵌套属性仍然是可变的。
- 实用于只需掩护对象顶层属性的场景。
7.3. toRaw 与 markRaw
toRaw
作用:用于获取一个相应式对象的原始对象, toRaw 返回的对象不再是相应式的,不会触发视图更新。
markRaw
作用:标记一个对象,使其永久不会变成相应式的。- import { reactive,toRaw,markRaw,isReactive } from "vue";
- /* toRaw */
- // 响应式对象
- let person = reactive({name:'tony',age:18})
- // 原始对象
- let rawPerson = toRaw(person)
- /* markRaw */
- let citysd = markRaw([
- {id:'asdda01',name:'北京'},
- {id:'asdda02',name:'上海'},
- {id:'asdda03',name:'天津'},
- {id:'asdda04',name:'重庆'}
- ])
复制代码 7.4.customRef
自界说的ref,可以实现特别的逻辑,一样寻常把自界说的ref封装为一个hook- import {customRef } from "vue";
- export default function(initValue:string,delay:number){
- let msg = customRef((track,trigger)=>{
- let timer:number
- return {
- get(){
- track() // 告诉Vue数据msg很重要,要对msg持续关注,一旦变化就更新
- return initValue
- },
- set(value){
- clearTimeout(timer)
- timer = setTimeout(() => {
- initValue = value
- trigger() //通知Vue数据msg变化了
- }, delay);
- }
- }
- })
- return {msg}
- }
复制代码 8.Vue3新组件
8.1.Teleport
将窗口移动到指定位置- <teleport to='body' >
- <div class="modal" v-show="isShow">
- <h2>我是一个弹窗</h2>
- <p>我是弹窗中的一些内容</p>
- <button @click="isShow = false">关闭弹窗</button>
- </div>
- </teleport>
复制代码
8.2.Suspense
- 等候异步组件时渲染一些额外内容,让应用有更好的用户体验
- <template>
- <div class="app">
- <h3>我是App组件</h3>
- <Suspense>
- //异步组件加载完成后显示内容
- <template v-slot:default>
- <Child/>
- </template>
-
- //异步组件加载过程中显示内容,加载之后消失
- <template v-slot:fallback>
- <h3>加载中.......</h3>
- </template>
- </Suspense>
- </div>
- </template>
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
|