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中是难以做到的。
相关推荐
MarcoPage2 分钟前
第十九课 Vue组件中的方法
前端·javascript·vue.js
.net开发3 分钟前
WPF怎么通过RestSharp向后端发请求
前端·c#·.net·wpf
工业互联网专业12 分钟前
Python毕业设计选题:基于Hadoop的租房数据分析系统的设计与实现
vue.js·hadoop·python·flask·毕业设计·源码·课程设计
**之火25 分钟前
Web Components 是什么
前端·web components
顾菁寒25 分钟前
WEB第二次作业
前端·css·html
前端宝哥26 分钟前
10 个超赞的开发者工具,助你轻松提升效率
前端·程序员
你好龙卷风!!!28 分钟前
vue3 怎么判断数据列是否包某一列名
前端·javascript·vue.js
程序员清风1 小时前
浅析Web实时通信技术!
java·后端·面试
Ljw...1 小时前
Vue.js组件开发
vue.js
兔老大的胡萝卜1 小时前
threejs 数字孪生,制作3d炫酷网页
前端·3d