vue3基础知识

vue3的备战

虚拟dom树对比

虚拟

1通讯方式

0 props :

js 复制代码
在父组件定义的响应式数据,通过在组件上写上属性进行传递
parent:
<child a="date">
 
    
    
    
child:
//子组件需要以这样的形式进行接收父组件传递的数据,同时可以接收多个数据,他返回一个对象,里面包含所有传来的数据,仍然是响应式的
let x=defineProps(['a'])    

1:v-model形式

js 复制代码
对于v-model的传递形式用在父子 子父都可以
用在自定义组件时
例如
parent
<fuzujian v-model='name'>
    
 child
首先需要声明接收
defineProps(['modelValue'])
const emit=defineEmits([update:modelValue])
触发的时候需要写
emit('update:modelValue',date)
注意,在这种情况下,modelvalue无法改变,如果在父元素 v-model:qw  这样写,则后面的modelValue可以更替

2:props形式

js 复制代码
父传子:
//父亲
  <child :obj="obj"></child>
//子组件
let b= defineProps(["obj"]);   父亲通过props的形式传递给了子组件数据,子组件需要使用defineProps来接收 

子传父:
需要父亲提前准备一个方法
父亲:
<child :name='a'></child>
function a(val){
    console.log(val)
}
子:
<button @click='test'>
let s=defineProps(['name'])  //注意,这里接收的名字应当为:name而非='a'里面的名字
function test(){
    s.name(val)
}

自定义事件:

js 复制代码
\\通过给子组件添加自定义事件来实现传参 只能实现子串夫
子传父亲:
父亲:
 <child @test="test1"></child>    //@test:时间名     test1 触发时调用哪个函数
function test1(val){console.log(val)}
子组件:
const emit=defineEmits(["test"]);
可以在适当的时候调用emit('test')参数作为emit的第二个参数传入
onMounted(()=>{
    emit('test',666)
})

mitt 任意组件的传递

js 复制代码
与vue2的 $bus一样
消息订阅与发布:
npm i mitt
新建utills文件
xx.ts
import mitt from 'mitt'
export const emitter=mitt()

main :  import xx.ts
基础语法
import mitt from "mitt";
const mitter = mitt()

// 绑定事件
mitter.on('a1', (val) => {
    console.log('a1')
})
//触发事件
mitter.emit('a1')
//解绑
mitter.off('a1', () => {
    console.log('a1解绑')
})
export default mitter

只需要在相应的组件导入使用即可
父亲:
import mitter from "../utills/mit";
 <button @click="mit">点我触发mit的子串夫</button>
mitter.on("all", (val) => {
  console.log("all", val);   //用于绑定一个事件all,当子组件触发all时,可以实现子组件的数据传递到父组件中来
});
function mit(val) {
  mitter.emit("allchild", obj);    //用于触发allchild事件,通过父组件点击触发事件,将父组件的数据传递过去
}
子组件:
<button @click="act">传递给父亲</button>
import mitter from "../utills/mit";
mitter.on("allchild", (val) => {
  console.log("allchild", val);    //用于绑定allchild事件,通过父组件点击触发事件,将父组件的数据传递过去
});
function act() {
  fn.have(a.value);
  emit("test", a.value);
  mitter.emit("all", a.value);  //用来触发all事件,当子组件点击时,触发all事件,并且将数据传递给父组件
}

$attrs 祖孙组件通讯

js 复制代码
$attrs 其实就是props传递的另一种形式,借助子组件传递给孙子,当然也可以传递方法,让孙组件将数据传递过来
父亲:
 <child :aaa="aaa" :bbb="bbb" :ccc="ccc" :test='test'></child>
function test(val){
    console.log(val)
}
子组件:
 <sun :attr="$attrs"></sun>   //子组件一个都不接受,直接传递给孙组件
孙组件:
let attr = defineProps(["attr"]);
let active=defineProps(['test'])
onMounted(() => {
  console.log("@@@@",attr.attr);
    active.test('val')
});//孙组件拿到数据

$refs

$parent

js 复制代码
$refs 用于父传子,$parent 用于子传父
refs类似于ref拿到子组件的实例
父组件:
<child1 ref='r1'>
<child2 ref='r2'>
<button @click='test($refs)'>
let r1=ref()
let r2=ref()

function test($refs){
    console.log($refs)  //拿到子组件希望父组件看到的数据
//c1:Proxy(Object) {child1: RefImpl, , __v_skip: true}
//c2:Proxy(Object) {child2a: RefImpl, __v_skip: true}
}
子组件1
let child1 = ref("child1");
let child1a=ref('child1a')
defineExpose({ child1 });   //使用此方法需要子组件进行对外暴露自身的数据,采用difineExprose的方法传入对象的形式
子组件2
let child2a = ref("child2");
defineExpose({ child2a });


$parent:
在子组件的方法中传入¥parent   !!不允许写错  可以拿到父组件向外暴露的数据
子组件2
 <button @click="haveParent($parent)">拿到父组件的数据</button>
let child2a = ref("child2");
defineExpose({ child2a });
function haveParent($parent) {
  console.log($parent);  //Proxy(Object) {aaa: RefImpl, __v_skip: true}
}
父组件:
let aaa=ref(123)
defineExprose({aaa})

provide与inject 实现爷->孙传递

js 复制代码
provide 向后代(注意,后代不仅仅是儿子和孙子,向下传递)提供数据,inject,孙子接收爷爷提供的数据
爷爷组件:
let sundate = ref(4);
provide("sundate", sundate);   //provide('name',date)  
注意,这里不可以写成sundate.value,否则会丢失响应式  //否则会是  sundate 4
function updateSundate() {
  sundate.value += 1;
}

孙子组件:
let sundate = inject("sundate");   //inject('name')
onMounted(() => {
  console.log('sundate',sundate)  
    //RefImpl {__v_isShallow: false, dep: undefined, __v_isRef: true, _rawValue: 4, _value: 4}
});


如果需要传递多个值或者方法,则需要写成对象形式
爷爷:
provide("sundate", { sundate, updateSundate });   //第二个参数需要传递成对象的形式,名字唯一就可以
孙子:
let sundate = inject("sundate");  //当然可用结构出来
onMounted(() => {
  console.log('sundate',sundate)   //接收的时候仍然需要保存到变量中去
  sundate.updateSundate() 
  console.log(sundate.sundate) 
    //sundate {sundate: RefImpl, updateSundate: ƒ}
//sun.vue:13 RefImpl {__v_isShallow: false, dep: undefined, __v_isRef: true, _rawValue: 5, _value: 5}
});

生命周期:

vue 复制代码
创建:setup()   --->beforecreate  created
挂在前:onBeforeMount(()=>{})
挂载:onMounted( async()=>{ await...})
更新:onBeforeUpdate(()=>{})    onUpdated(()=>{})
卸载{销毁}  onBeforeMount(()=>{})   onUnMounted(()=>{})

路由:

js 复制代码
vue3的路由区别于vue2的路由

新建 router:文件夹
路由创建与暴露
import {createRouter,createWebHistory,createWebHashHistory} from 'vue-router'
const router=createRouter({
    history:createWebHistory()  //工作模式 createWebHashHistory() 哈希模式
    routes:[
        //路由
        {
            path:''
            component:.vue
    		name:
        }
    ]
})
export default router  //对外暴露


main:
//进行挂在
createApp(App).use(router).mount('#app')

放在哪:
<RouterView>//展示用的 
<routerLink :to=`路径\name?a=${date}&b=${date}` active-class='激活样式'>  //标签跳转 针对link组件,里面内置了一个active-class的属性,用来方便设置激活时候的样式,动态路由传参
<routerLink :to={path\name:'',query:{参数}} active-class='激活样式'> 

params参数:
需要在路径里面进行占位
在路由组件中 path:'/news/:id/:text'
这样在<routerLink :to=`/news/id=${1}/text=${2}' active-class='激活样式`>  1 2为参数
而在对象方法中
<routerLink :to={ name:xxx,params:{date}} active-class='激活样式`> 使用对象的写法时,只能使用name作为导航的地址



接受参数
const route=useRoute()
 //let {query}=route  会丢失响应式,需要包裹上torefs
 let {query}=toRefs(route)
 //使用parmas的时候
 let {params}=toRefs(route)


 
··· 路由规则的props方法
当使用params方法时候,
 {
            path:''
            component:.vue
    		name:
     		props:true   //开启props方法
        }
那么在后续接受参数时
使用defineProps(['id','text'])  通过这样的形式将传递的参数形成一个单独的prop,可以直接在模板使用 


replace:
写在<routerLink replace>  //无法浏览器后退 

    
编程式路由导航
import { useRouter} from 'vue-router'

const router=useRouter()
router.push('/path')  // router.replace('/path)
router.push({
    path:'/path',
    query:{[参数]}
})


重定向:
{
    path:'/',
    redirect
}

pinia

js 复制代码
main:
import {createPinia} from 'pinia'
const pinia=createPinia()
app.use(pinia)


创建store文件夹
xxx.ts:
import {defineStore} from 'pinia'
export const usexxxStore=defineStore('xxx',{
    state(){
        return{
            a:1
        }
    },
    actions:{
        function a(){
            this.a=this.a+1
        }
    },
      getters:{                                     
    		big(state){
    		return state.a*10
}
                                     
                      }                                  
                                   
                                     
})


//组合式
export const usexxxStore=defineStore('xxx',()=>{
    let a=ref(1)
    //方法
    //actions:
    function a2(){
            this.a=this.a+1
        },
     //getters:
            let b=computed(()=>{
                return ???
            })
            
    
    return{
        a,
        a2,
        b
    }
})


vue中读取
import { usexxxStore} from ''

const store= usexxxStore()
const store2=storeToRefs(store)  //保持数据!!!的响应式,store的方法不会包裹响应式
let {a,big}=store2

1 toRef与toRefs

js 复制代码
toRef 与toRefs
toRef是针对单个数据做响应式处理,而toRefs是针对全部做响应式处理,对于结构时,一定要注意响应式是否丢失
例子:
let obj=ref({
    a:1,
    b:2
})
let {a}=obj   //此时响应式就已经丢失了,你拿到的只是里面的一个值
toRef:  let {a}=toRef(obj,'a')
toRefs: let {a}=toRefs(obj)

2组合式api

两张图对比:

js 复制代码
对于rective定义的属性,无法整体替换
例如 let obj=rective(a:1,b:2)
let obj={a:2,b:3}
不可这样写,需要 Object,assgin(obj,{??})这样
computed在vue3中变成了组合式api

let a=computed(()=>{
    return ??  应该写成这样的形式
})

let op=watch(()=>{return??},(new,old)=>{
    //watch的用法,监视的数据要注意有时需要用函数返回的形式来写
    op() //达到条件可用关闭监视
})
watch([()=>{return??},()=>{return}],(new,old)=>{
    //当要监视多个属性时需要这样
})
watch(rective对象,(new,old)=>{
    //当监视的是rective的对象时,需要开启deep深度监视
},{
      deep:ture
      })
  
watchEffect  //区别于watch,他不需要写监视谁,在里面用到的数据他默认会给你监视
watchEffect(()=>{
   里面用到的数据都自动进行了监视
})

3 ref属性

js 复制代码
在vue3中
在标签上写ref属性可以拿到dom操作
<button ref="a">
    let a=ref()
那么这个a就是哪个标签
当ref写在组件上时,拿到的就是组件实例
例如
<BUTTON ref='btn'>
    let btn=ref()
此时,就拿到了这个组件的实例,当拿到组件实例的时候可以看到组件里面的数据,当使用setup的时候
我们需要在子组件用defineExpose进行暴露出去,这样才能看到
如果没有使用setup语法糖的时候,则不需要这么做

例
child :
<template><h1>这里是stu1</h1></template>
<script lang="ts">
export default {};
</script>
<script lang="ts" setup>
import { ref } from "vue";
let ss1 = ref("asdasd");
defineExpose({ ss1 });

parent:
<child ref='ch'>
<script lang="ts" setup>
let ch=ref()

插槽:

js 复制代码
默认插槽
默认插槽需要在子组件使用双标签的形式
parent:
<chacao>
    <ul>
      <li>1</li>
      <li>2</li>
      <li>3</li>
    </ul>
  </chacao>
  <chacao>
  <img src="https://img0.baidu.com/it/u=2565809048,1400176152&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=500" alt="">
  </chacao>
  父组件使用了两个一样的插槽,但里面的内容是不同的,展示到页面上的效果也不同
  
  
  子组件:
   <h1>这里是插槽</h1>   而子组件只需要写上slot标签告诉父组件插槽的内容往哪里展示即可
  <slot></slot>


具名插槽
当我们要向插槽里面添加的样式和结构不是单一的结构时,我们就需要使用具名插槽,来告诉具体的结构展示在具体的哪个位置

parent:
<template>
  <chacao>
    <template v-slot:s1>
      <ul>
        <li>1</li>
        <li>2</li>
        <li>3</li>
      </ul>
    </template>
    <template v-slot:s2>
      <h1>这里是具名插槽s2</h1>
    </template>
  </chacao>
  <chacao>
    <template v-slot:s1>
      <img
        src="https://img0.baidu.com/it/u=2565809048,1400176152&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=500"
        alt=""
      />
    </template>
  </chacao>
</template>
子组件:
<template>
  <h1>这里是插槽</h1>
  <slot name="s1"></slot>
  <slot name="s2"></slot>
</template>
// 可用看到,子组件写了两个插槽,并且命名为s1和s2
//在父组件中,具体的结构使用了template进行包裹,并且在template上使用了v-slot:name 指令
//注意 v-solt只能写在组件标签或者template标签上,语法为v-slot:插槽名字
//这样,插槽就和结构一一对应起来


作用域插槽:
  作用域插槽用于结构由父组件决定,但是数据在子组件身上,从而形成的尴尬局面,当然具名插槽可以和作用域插槽一起使用
  例如:  <template v-slot:s1="data">
 父亲:
 <chacao>
    <template v-slot="data">
      <ul>
        <li v-for="(item, index) in data.youxi" :key="item.id">
          {{ item.name }}
        </li>
      </ul>
    </template>
  </chacao>
子组件
  <h1>这里是作用域插槽</h1>
  <slot :youxi="game"></slot>     //直接在slot标签上传递数据,可以传递多个数据,slot会将所有的数据打包成一个对象传递给父组件
父组件在template写语法为v-slot='??'格式的指令,拿到由子组件打包而来的数据,使用时只需要??.name使用即可
let game = reactive([
  { id: 1, name: "benghuai" },
  {id: 2,name: "LOL",},
  {id: 3, name: "yuanshen" },
  {id: 4,name: "王者",},
]);
效果:
//benghuai
//LOL
//yuanshen
//王者

一些面试题

面试题1:为什么vue3中去掉了vue构造函数?

js 复制代码
vue2的全局构造函数带来了诸多问题:
1. 调用构造函数的静态方法会对所有vue应用生效,不利于隔离不同应用
2. vue2的构造函数集成了太多功能,不利于tree shaking,vue3把这些功能使用普通函数导出,能够充分利用tree shaking优化打包体积
3. vue2没有把组件实例和vue应用两个概念区分开,在vue2中,通过new Vue创建的对象,既是一个vue应用,同时又是一个特殊的vue组件。vue3中,把两个概念区别开来,通过createApp创建的对象,是一个vue应用,它内部提供的方法是针对整个应用的,而不再是一个特殊的组件。

面试题2:谈谈你对vue3数据响应式的理解

js 复制代码
vue3不再使用Object.defineProperty的方式定义完成数据响应式,而是使用Proxy。
除了Proxy本身效率比Object.defineProperty更高之外,由于不必递归遍历所有属性,而是直接得到一个Proxy。所以在vue3中,对数据的访问是动态的,当访问某个属性的时候,再动态的获取和设置,这就极大的提升了在组件初始阶段的效率。
同时,由于Proxy可以监控到成员的新增和删除,因此,在vue3中新增成员、删除成员、索引访问等均可以触发重新渲染,而这些在vue2中是难以做到的。
相关推荐
GHUIJS5 分钟前
【vue3】vue3.5
前端·javascript·vue.js
&白帝&32 分钟前
uniapp中使用picker-view选择时间
前端·uni-app
谢尔登39 分钟前
Babel
前端·react.js·node.js
ling1s39 分钟前
C#基础(13)结构体
前端·c#
卸任1 小时前
使用高阶组件封装路由拦截逻辑
前端·react.js
计算机学姐1 小时前
基于python+django+vue的家居全屋定制系统
开发语言·vue.js·后端·python·django·numpy·web3.py
lxcw1 小时前
npm ERR! code CERT_HAS_EXPIRED npm ERR! errno CERT_HAS_EXPIRED
前端·npm·node.js
Ripple1111 小时前
Vue源码速读 | 第二章:深入理解Vue虚拟DOM:从vnode创建到渲染
vue.js
秋沐1 小时前
vue中的slot插槽,彻底搞懂及使用
前端·javascript·vue.js
这个需求建议不做1 小时前
vue3打包配置 vite、router、nginx配置
前端·nginx·vue