【vue3.x】vue3.x基础知识学习

 

1.vue2.x和vue3.x项目的区别 

1.1.创建应用实例的方式

vue2.x:

通过new Vue()创建vue根实例,从同一个vue构造函数创建的每一个根实例共享相同的全局配置

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

Vue.config.productionTip = false

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

 vue3.x:

 vue3.x引入createApp一个新的全局API,调用该API返回一个应用实例

import { createApp } from 'vue';
import App from './App.vue'
import router from './router'
import store from './store'

createApp(App).use(router).use(store).mount('#app')

1.2.全局API的迁移

由于vue3.x不存在全局根实例,那么伴随根实例的全局API就被迁移到了应用实例上。应用程序实例暴露当前全局API的子集,任何改变vue行为的API现在都被迁移到应用实例上

以下是全局API及其相应实例API的表:

2.x全局API 3.x实例API
Vue.config app.config
Vue.component app.component
Vue.directive app.directive
Vue.mixin app.mixin
Vue.use app.use

 如上图所示,vue2.x的main.js中的这段代码:

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

Vue.config.productionTip = false

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

在vue3.x中这样呈现:

import { createApp } from 'vue';
import App from './App.vue'
import router from './router'
import store from './store'


const app = createApp();//创建app实例
app.use(router); //注入router
app.use(store);
app.mount('#app);//挂载页面


//等同于该链式调用
createApp(App).use(router).use(store).mount('#app')

1.3.vue3.x允许多个根节点的出现 

 vue2.x:

多个节点时提示错误

vue3.x时,不再提示:

组件可以没有根标签,内部会将多个标签包含在一个Fragment虚拟元素中(Vue3.x中Fragment(片断)的内容)

好处:减少标签层级,减小内存占用

1.4.vue3中过滤器不再被支持

建议使用方法调用或计算属性代替过滤器(呵呵。。。)

1.5.v-if和v-for优先级的改变

vue2.x中同时使用v-if和v-for,时v-for优先级更高;但是vue3.x中v-if优先级高于v-for ,则更容易促进该写法(虽然平时也是这样写的。。)

<div v-for="item in list" :key="item" >
    <template v-if="item > 0">
        {
   
   { item }}
    </template>
</div>

1.6.nextTick的使用改变

vue2.x一直都是这样使用nextTick的

Vue.nextTick(() => {});

this.nextTick(() => {});

但是vue3.x中,全局API现在只能作为ES模块构建的命名导出进行访问,如下面所示:

 据我所知,setup(){}中是没有this的;至于setup外部,。。。暂未实验过,晚点试试

//先导出
import { nextTick } from 'vue'

//再使用
nextTick(() => {});

//或者
this.nextTick(() => {});//该方式依然可行,有点懵

1.7.v-for中的ref数组的变化

  vue2.x中: 

<div>
  <button ref="btns" v-for="(item,index) in list" :key="index">{
   
   { item }}</button>
</div>


//vue2.x中,this.$refs.btns的取值会是一个数组,数组的内容是循环的每一个button的dom元素

 vue3.x中,ref的值则不在是数组,而是让你传一个函数,通过接收该函数的形参得到一个数组,如下:

<div>
  <button :ref="setBtnDoms" v-for="(item,index) in list" :key="index">{
   
   { item }}</button>
</div>


//接收的形参未循环的每一个button的dom
setBtnDoms(dom){
   this.list.push(dom);
}

1.8.自定义指令的生命周期

vue2.x vue3.x
bind beforeMount
inserted mounted
beforeUpdate(元素本身更新之前调用)
update 无(update被移除)
componentUpdated updated
beforeUnmount(卸载元素之前调用)
unbind unmounted

1.9.VueRouter的使用变化

Vue Router4.0提供了vue3支持 ,VueRouter追随vue3抛弃了之前new VueRouter的写法,提供了createRouter的创建函数

import { createRouter,createWebHashHistory } from 'vue-router'

const router = createRouter({
  //负责管理历史的属性,有三种历史可选
  //hash,history,memory
  history: createWebHashHistory(),
  routes
});

export default router

//代码中编程式导航也是通过createRouter获取router的,route也一样

1.10.vuex的引入变化

vuex4.0提供了vue3支持,唯一的变化是初始化

import { createStore } from 'vuex'

const store = createStore();

export default store

2.创建vue3.x项目的方式

cdn方式:

<script src="https://unpkg.com/vue@next"></script>

 vite脚手架:

npm init vite-app demo

vue-cli脚手架:

vue create demo

tips:若使用vue create demo方式创建项目未跳转到vue3选项,则可先创建vue2.x项目,然后cd到项目根目录,再进行 vue add vue-next 即可将vue2.x新建项目转化为vue3.x项目

在vue2.x中使用vue3.x的API

npm install @vue/composition-api

在main.js文件中:

import VueCompositionAPI from '@vue/composition-api';
Vue.use(VueCompositionAPI);

使用时导入:

import { ref,reactive } from '@vue/composition-api';

3.常用Composition API

1.setup

组合API的入口,只在初始化时执行一次;函数中若返回对象,对象中的属性或方法,可在模板中直接使用

export default {
  setup(){

    // 非响应式数据(响应式数据:数据变化,页面跟着渲染变化)
    let count = 0;
    
    // 方法
    let updateCount = () => {
      console.log('updateCount');
      count++;
    }
    
    // 返回一个对象
    return{
      //属性
      count,
      updateCount
    }
  }
}

2.setup细节问题

2.1.setup执行时机 

  • 在beforeCreate执行一次,此时组件并未创建 
  • 因组件并未创建,则this为undefined;不能用this来访问data/methods/props;所以setup函数中不能使用this
  • 其余所有compositionAPI相关回调函数中也不可以使用this

示例:

export default {
  beforeCreate(){
    console.log('beforeCreate',this);
  },
  created(){
    console.log('created',this); 
  },
  setup(){
    console.log('setup',this);
    return{

    }
  }
}

 执行结果:

可以看到setup中没有this;且vue3.x中的this做了处理,是一个proxy对象

tips:网上还有setup执行时机是在beforeCreate和created之间这种说法?

从上图打印效果最直观的讲,setup的执行在beforeCreate和created之前

其次,beforeCreate和created内部是有this存在的,而setup中this为undefined,这也验证了setup的执行在beforeCreate和created之前

在Vue3.x中生命周期函数发生了语义化的变化,比如beforeDestroy变更为了beforeUnmount;其他生命周期均小有变化,且其他生命周期均在setup中执行;

而beforeCreate和created还维持原本,这说明Vue3.x中本身并没考虑为beforeCreate和created留有位子,而是使用setup来代替这两者,之所以Vue3.x中还有这两个钩子函数,主要是为了向下兼容

2.2.setup的返回值 

  • 一般都返回一个对象
  • 返回对象中的属性会与data函数返回对象合并成为组件对象的属性
  • 返回对象中的方法会与methods中的方法合成为组件对象的方法
  • 如有重名,setup优先
  • 不推荐混用,methods中可以访问setup提供的属性余方法;而setup中却不能访问data/methods
  • setup不推荐是一个async函数:因为返回只不再是return的对象,而是promise,模板看不到return对象中的属性数据 

tips:setup可不可以是一个async函数?

可以是一个async函数,但是由于是异步,使得数据不会立即给到模板,可以使Suspense标签解决该问题,代码示例如下:

在异步组件中的setup中使用async,在未得到数据前会有一个加载中的过渡效果

<template>
    <h1>Suspense的使用</h1>
    <Suspense>
       <template #default>
           <!--异步组件-->
           <HelloWorld />
       </template>
       <template v-slot:fallback>
           <h2>加载中...</h2>  
       </template>
    </Suspense>  
</template>

 参考:https://zhuanlan.zhihu.com/p/137364605

2.3.setup中的参数

  • setup(props,context) / setup(props,{attrs,slots,emit}) 
  • props:包含props配置中声明的属性的对象,可用于父传子传值
  • attrs:包含没有在props配置中声明的属性的对象,相当于this.$attrs
  • slots:包含所有传入的插槽内容的对象,相当于this.$slots
  • emit:用来分发自定义事件的函数,相当于this.$emit,可用于子传父传值

tips:setup中接收的props是响应式,当传入新的props时,会及时更新,由于是响应式的,所以不可以使用ES6解构,解构会消除响应式 

同理,setup中声明的响应式对象也不可解构,而是返回的时候使用toRefs来进行转换 

示例:

export default {
  name: 'HelloWorld',
  props: {
    msg: String
  },
  setup(props,context){
    console.log('props:',props);
    console.log('props.msg:',props.msg);
    console.log('context:',context);
    return {

    }
  }
}

执行结果:

3.ref

  • 定义一个数据的响应式 ;创建一个包含响应式数据的引用(reference)对象 / ref对象
  • const xxx = ref(initVal);
  • js中操作:xxx.value
  • 模板中操作:xxx;不需要.value
  • 通常用来定义一个基本类型的响应式数据

示例:

<template>
  <div class="about">
    <h1>This is an about page</h1>
  </div>
  <div>
    <h3>{
   
   { count }}</h3>
    <button @click="updateCount">更新数据</button>
  </div>
</template>

<script>
import { ref } from 'vue';
export default {
  setup(){

    // 响应式数据
    let count = ref(0);

    
    // 方法
    let updateCount = () => {
      console.log('updateCount');
      count.value++;
    }
    
    // 返回一个对象
    return{
      //属性
      count,
      updateCount
    }
  }
}
</script>

ref另一个作用:可以获取页面中的元素 

html代码: 

<h1 ref="h1">This is an about page</h1>

js代码:

import { ref,reactive, computed,watch,watchEffect, onMounted } from 'vue';
export default {
  setup(){
    //页面加载完毕,组件已经存在,获取元素
    const h1 = ref(null);

    onMounted(() => {
      console.log('h1',h1.value.innerText);
    });
    
    return{
      h1
    }
  }
}

4.reactive

  • 定义多个数据的响应式
  • const proxy = reactive(obj) :接收一个普通对象然后返回该普通对象的响应式代理对象
  • 响应式转换是‘深层的’:会影响对象内部所有嵌套的属性
  • 内部基于ES6的proxy实现,通过代理对象操作源对象内部数据都是响应式的

示例:

<template>
  <div class="about">
    <h1>This is an about page</h1>
  </div>
  <div>
    <h3>{
   
   { user }}</h3>
    <button @click="updateUser">更新数据</button>
  </div>
</template>

<script>
import { ref,reactive } from 'vue';
export default {
  setup(){
  
    const user = reactive({
      name:'xxxx',
      age:19,
      bro:{
        name:'yyy',
        age: 18,
        likes:['swimming']
      },
      likes:['sing','dance']
    });

    const updateUser = () => {
      user.name += '----------'
      user.age += 1;
      user.bro.name += '**********';
      user.bro.likes.push('haha');
    }
    
    return{
      user,
      updateUser
    }
  }
}
</script>

执行结果:

执行前显示:

执行后显示:

5.ref与reactive细节

  • 是vue3 compositionAPI中两个最重要的响应式API
  • ref通常用来处理基本类型数据,reactive用来处理对象(递归深层响应式)
  • 若用ref对象/数组,内部则自动将对象/数组转换为reactive的代理对象
  • ref内部:给value属性添加getter/setter来实现对数据的劫持
  • reactive内部:使用proxy来实现对对象内部所有数据的劫持,并通过Reflect(反射)操作对象内部数据
  • ref数据操作:在js中.value;在模板中在不需要(内部解析模板时会自动添加.value

tips:ref内部value的劫持是如何来实现的? 

示例:

<template>
  <div class="about">
    <h1>This is an about page</h1>
  </div>
  <div>
    <h3>{
   
   { user }}</h3>
    <button @click="updateUser">更新数据</button>
  </div>
</template>

<script>
import { ref,reactive } from 'vue';
export default {
  setup(){
  
    const user = ref({
      name:'xxxx',
      age:19,
      bro:{
        name:'yyy',
        age: 18,
        likes:['swimming']
      },
      likes:['sing','dance']
    });

    const updateUser = () => {
      console.log('user ref:',user);
      user.value.name += '----------'
      user.value.age += 1;
      user.value.bro.name += '**********';
      user.value.bro.likes.push('haha');
    }
    
    return{
      user,
      updateUser
    }
  }
}
</script>

执行结果:

ref若是对象,则内部经过reactive的处理,形成了proxy类型对象

tips:ref和reactive有什么区别?

6.toRefs

  • 把一个响应式对象转换成普通对象,该普通对象的每个property都是一个ref
  • 应用:当从合成函数返回响应式对象时,使用toRefs可以在不丢失响应式的情况下对返回的对象进行分解
  • 由于reactive对象取出的所有属性值都是非响应式的;利用toRefs可以将一个响应式reactive对象的所有原始属性转换为响应式的ref属性

示例:

<template>
  <div class="about">
    <h1 ref="h1">This is an about page</h1>
  </div>
  <div>

    <fieldset>
      <legend>toRefs</legend>
      <!-- 直接返回reactive对象并使用点语法来获取属性,依然是响应式的 -->
      <!-- <div>{
   
   {state.name}}</div>
      <div>{
   
   {state.age}}</div> -->
      
      <!--解构后,则不再是响应式数据,返回时使用toRefs转换为响应式数据-->
      <div>{
   
   {name}}</div>
      <div>{
   
   {age}}</div> 
    </fieldset>
  </div>
</template>

<script>
import { ref,reactive, computed,watch,watchEffect, onMounted,toRefs } from 'vue';
export default {
  setup(){

    const state = reactive({
      name:'小明',
      age: 18
    });
    
    setInterval(() =>{
      state.name += '*';
    },1000);
    
    return{
      // 直接返回完整的reactive对象时,模板中通过点语法获取该对象的属性时,数据依然是响应式的
      // state

      // 解构后不是响应式
      // ...state 

      // 使用toRefs
      ...toRefs(state)
    }
  }
}
</script>

7.计算属性computed

代码书写形式与vue2.x不同,其余区别不大

示例:

<template>
  <div class="about">
    <h1>This is an about page</h1>
  </div>
  <div>
    <fieldset>
      <legend>计算属性</legend>
      {
   
   { fullName }}
    </fieldset>
  </div>
</template>

<script>
import { ref,reactive, computed } from 'vue';
export default {
  setup(){
  
    const user = reactive({
      name:'xxxx',
      age:19,
      bro:{
        name:'yyy',
        age: 18,
        likes:['swimming']
      },
      likes:['sing','dance']
    });

    
    // 计算属性的函数中若只传入一个回调函数,表示get
    //返回的是一个ref对象 
    const fullName = computed(() => {
      return user.name + '==' + user.bro.name;
    });
    
    //若使用其中的set和get,则外部是一个对象
    const fuleName2 = computed({
      get(){
        return user.name + '+' + user.bro.name;
      },
      set(val){
        console.log('set val',user.name);
      }
    });

    
    return{
      user,
      fullName
    }
  }
}
</script>

执行结果:

8.监听watch

代码书写形式与vue2.x不同,其余区别不大

//监听一个值
watch(user,(val) => {
      fullName3.value = user.name + '++' + user.bro.name;
});

深监听/初始化执行

watch(user,(val) => {
      fullName3.value = user.name + '++' + user.bro.name;
    },{
      immediate: true,//默认执行一次
      deep: true //深监听
    });

监听多个数据

当监听非响应式数据时,该数据需以函数返回值的形式呈现 

watch([() => user.name,() => user.age],() => {
      console.log('zzzz');
    },{
      immediate: true,
      deep: true
    });

watchEffect

本身默认执行一次;无需指定监听对象,有数据变动则自动监听 

watchEffect(() => {
   fullName3.value = user.name + '$$' + user.bro.name;
});

9.生命周期

与vue2.x生命周期相对应的组合式API:

vue2.x vue3.x
beforeCreate setup
created setup
beforeMount onBeforeMount
mounted onMounted
beforeUpdate onBeforeUpdate
updated onUpdated
beforeDestory onBeforeUnmounted
destoryed onUnmounted
errorCaptured onErrorCaptured

新增钩子函数:

用于调试

  • onRenderTracked
  • onRenderTriggered

4.不常用Composition API

1.shallowReactive与shallowRef

  • shallowReactive:只处理对象内最外层的响应式(浅响应式)
  • shallowRef:只处理value的响应式,不对对象进行reactive处理  

示例:

<template>
  <fieldset>
      <legend>shallowReactive与shallowRef</legend>
      <div>obj1(reactive):{
   
   { obj1 }}</div>
      <div>obj2(shallowReactive):{
   
   { obj2 }}</div>
      <div>obj3(ref):{
   
   { obj3 }}</div>
      <div>obj4(shallowRef):{
   
   { obj4 }}</div>
      <button @click="update">更新数据</button>
    </fieldset>
</template>

<script>
import { reactive,ref,shallowReactive,shallowRef } from 'vue'

export default {
  name: 'Home',
  setup(){

    const obj1 = reactive({
      name:'小明',
      age:18,
      car:{
        name:'奔驰',
        color:'black'
      }
    });
    const obj2 = shallowReactive({
      name:'小黑',
      age:19,
      car:{
        name:'大众',
        color:'blue'
      }
    });
    const obj3 = ref({
      name:'小白',
      age:20,
      car:{
        name:'丰田',
        color:'white'
      }
    });
    const obj4 = shallowRef({
      name:'小华',
      age:21,
      car:{
        name:'宝马',
        color:'red'
      }
    });


    const update = () => {
      
      //reactive
      // obj1.car.name+='*';

      //shallowReactive
      obj2.car.name+='*';

      //ref  
      // obj3.value.car.name+='*';

      //shallowRef 
      obj4.value.name+='*';
      // obj4.value.car.name+='*';
    }

    return{
      obj1,
      obj2,
      obj3,
      obj4,
      update
    }
  }
}
</script>

tips:单独修改数据时,shalowReactive和shallowRef如上述所言;可多数据修改时,shalowReactive和shallowRef依然深响应式,为什么?

2.readonly与shallowReadonly

readonly

  • 深度只读数据
  •  获取一个对象(响应式或纯对象)或ref对象并返回原始代理的只读代理
  • 只读代理是深层的:访问的任何嵌套property也是只读的

shallowReadonly

  • 浅只读数据
  • 创建一个代理,使其自身的property为只读,但不执行嵌套对象的深度只读转换

示例:

<template>
  <fieldset>
      <legend>readonly与shallowReadonly</legend>
      <!-- <div>objReadonly:{
   
   { objReadonly }}</div> -->
      <div>objShallowReadonly:{
   
   { objShallowReadonly }}</div>
      <button @click="update">更新数据</button>
    </fieldset>
</template>

<script>
import { reactive,ref,readonly,shallowReadonly } from 'vue'

export default {
  name: 'Home',
  setup(){

    const obj1 = reactive({
      name:'小明',
      age:18,
      car:{
        name:'奔驰',
        color:'black'
      }
    });
    
    
    const objReadonly = readonly(obj1);
    const objShallowReadonly = shallowReadonly(obj1);
    console.log('objReadonly:',objReadonly);
    console.log('objShallowReadonly:',objShallowReadonly);

    const update = () => {
      // objReadonly.name += '*';
      // objReadonly.car.name += '*';

      // objShallowReadonly.name += '*';
      objShallowReadonly.car.name += '****';
    }

    return{
      // objReadonly,
      objShallowReadonly,
      update
    }
  }
}
</script>

tips:单独修改数据时,readonly与shallowReadonly如上述所言;可多数据修改时,shallowReadonly依然无法深度修改,为什么?

3.toRow与markRow

toRow

  • 返回reactive或readonly方法转换成响应式代理的普通对象
  • 这是一个还原方法,可用于临时读取,访问不会被代理/追踪,写入时也不会触发界面更新

markRow

  • 标记一个对象,使其永远不会转换为代理;返回对象本身
  • 应用场景:有些值不应被设置为响应式,例如复杂第三方类实例或Vue组件对象;当渲染具有不可变数据源的大列表时,跳过代理转换可以提高性能

示例:

<template>
  <fieldset>
      <legend>toRow与markRow</legend>
      <div>obj1:{
   
   { obj1 }}</div>
      <button @click="testToRow">testToRow</button>
      <button @click="testMarkRow">testMarkRow</button>
    </fieldset>
</template>

<script>
import { reactive,ref,toRaw,markRaw } from 'vue'

export default {
  name: 'Home',
  setup(){

    const obj1 = reactive({
      name:'小明',
      age:18,
      car:{
        name:'奔驰',
        color:'black'
      }
    });

    const testToRow = () => {
      //代理对象变为普通对象
      //这里返回原始对象
      const toRowObj = toRaw(obj1);
      console.log('toRowObj:',toRowObj);
    };

    const testMarkRow = () => {
      const likes = ['吃','喝'];
      const markRowLikes= markRaw(likes);
      // 标记后,该数据不再是响应式数据
      obj1.likes = markRowLikes;
      console.log('markRowLikes:',markRowLikes);
      console.log('obj1:',obj1);
    };

    setInterval(() => {
      if(obj1.likes){
        // 界面不会更新
        obj1.likes.push('玩');
        console.log('setInterval obj1:',obj1);
      }
    },1000);
    
    
    return{
      testToRow,
      testMarkRow,
      obj1
    }
  }
}
</script>

4.toRef

 为源响应式对象上的某个属性创建一个ref对象,二者内部操作的是同一个数据值,更新时二者是同步的(ref不会同步

应用:当要将某个prop的ref传递给复合函数时,toRef很有用

示例:

<template>
  <fieldset>
      <legend>toRef</legend>
      <div>obj:{
   
   { obj }}</div>
      <div>age:{
   
   { age }}</div>
      <div>name:{
   
   { name }}</div>
      <button @click="update">更新数据</button>
    </fieldset>
</template>

<script>
import { reactive,ref,toRef } from 'vue'
export default {
  name: 'Home',
  setup(){
    const obj = reactive({
      name:'小明',
      age:18,
      car:{
        name:'奔驰',
        color:'black'
      }
    });

    // 把响应式数据obj1对象中的属性变成ref对象
    const age = toRef(obj,'age');

    const name = ref(obj.name);

    const update = () => {
      //obj.age+=1;
      age.value+=1;
      
      //obj.name+= '*';
      //name.value+='*';
    }

    return{
      obj,
      age,
      name,
      update
    }
  }
}
</script>

5.customRef

创建一个自定义的ref,并对其依赖项跟踪和更新触发显示控制 

示例:

这里使用了自定义hook函数(Vue3.x中的概念),这里穿插一下自定义hook的东西:

自定义hook函数

  • 使用Vue3.x组合API封装的可复用的功能函数
  • 自定义hook类似于Vue2.x中的mixin(简单来说,其实就是封装公共的函数方法或者可以分离提取的功能,自定义hook叫做功能模块感觉更适合一点)
  • 好处:代码可复用,简洁
<template>
    <h1>customRef</h1>
    <input type="text" v-model="keyword"/>
    <div>{
   
   { keyword }}</div>
</template>

<script>

import { reactive,ref,customRef } from 'vue';
// 自定义hook
function useDebounceRef(value,delay=200){
  let timerId = null;

  return customRef((track,trigger) => {
    return{
      get(){
        // 告诉Vue追踪数据
        track();
        return value;
      },
      set(newVal){
        clearTimeout(timerId);
        timerId = setTimeout(() => {
          value = newVal;
          // 告诉Vue更新界面
          trigger();
        },delay);
      }
    }
  })
}

export default {
  name: 'Home',
  setup(){
    const keyword = useDebounceRef('abc',500);

    return{
      keyword
    }
  }
}
</script>

6.provide与inject

provide和inject提供依赖注入,实现跨层级组件(祖孙)间通信

示例:

这里以父子间传值为例

父组件:

<template>
    <h1>provide与inject</h1>
    <div>当前颜色:{
   
   {color}}</div>
    <button @click="color='red'">红色</button>
    <button @click="color='pink'">粉色</button>
    <button @click="color='blue'">蓝色</button>

    <HelloWorld msg="Welcome to Your Vue.js App"/>
</template>

<script>

import { reactive,ref,provide } from 'vue';
import HelloWorld from '@/components/HelloWorld.vue'
export default {
  name: 'Home',
  setup(){
    const color = ref('red');
    
    // 提供数据
    provide('color',color);

    return{
      color
    }
  },
  components: {
    HelloWorld
  }
}
</script>

子组件:

<template>
  <div class="hello">
    <h1 :style={color}>{
   
   { msg }}</h1>
    <p>
      For a guide and recipes on how to configure / customize this project,<br>
      check out the
      <a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
    </p>
    <h3>Installed CLI Plugins</h3>
    <ul>
      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-router" target="_blank" rel="noopener">router</a></li>
      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-vuex" target="_blank" rel="noopener">vuex</a></li>
    </ul>
    <h3>Essential Links</h3>
    <ul>
      <li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
      <li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
      <li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
      <li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
      <li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
    </ul>
    <h3>Ecosystem</h3>
    <ul>
      <li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
      <li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
      <li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
      <li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
      <li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
    </ul>
  </div>
</template>

<script>
import { inject } from 'vue';
export default {
  name: 'HelloWorld',
  props: {
    msg: String
  },
  setup(props,context){
    console.log('props:',props);
    console.log('props.msg:',props.msg);
    console.log('context:',context);
    
    // 注入
    const color = inject('color');
    return {
      color
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
h3 {
  margin: 40px 0 0;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
</style>

效果:

7.响应式数据的判断

  • isRef:检查一个值是否为一个ref对象
  • isReactive:检查一个对象是否由reactive创建
  • isReadonly:检查一个对象是否是由readonly创建的只读代理
  • isProxy:检查一个对象是否是由reactive或者readonly方法创建的代理

8.Teleport(瞬移)

Teleport提供了一种干净的方法,让组件的html在父组件界面外的特定标签(很可能是body)下插入显示;Teleport上的to属性指定特定标签

通俗来讲,就是给组件指定父元素

示例:

子组件:

//HelloWorld组件
<template>
    <!--to属性指定将Teleport中的代码瞬移到body标签下-->
    <Teleport to="body">
        <button @click="modalOpen = true">打开一个弹窗</button>
        <!-- 弹窗 -->
        <div v-if="modalOpen" >
          弹窗本体
          <button @click="modalOpen = false">关闭弹窗</button>
        </div>
    </Teleport>
</template>

<script>
import { reactive,ref} from 'vue';
export default {
  name: 'Home',
  setup(){
    const modalOpen = ref(false);
    return{
      modalOpen
    }
  }
}
</script>

父组件:

<template>
    <HelloWorld />  
</template>

<script>
import HelloWorld from '@/components/HelloWorld';
export default {
  setup(){
   
  },
  components:{
     HelloWorld
  }
}
</script>

 效果:

由于该html代码本身位于body中,效果不明显,也可尝试将该标签瞬移到html中

9.Suspense

允许应用程序在等待异步组件时渲染一些后备内容,可以得到一个平滑的用户体验 

通俗来讲,就是用Suspense这个标签来实现过渡异步请求的效果 

示例:

子组件:

//HelloWorld组件
<template>
    <h1>AsyncComponent 子组件</h1>
    <h1>{
   
   { msg }}</h1>
</template>

<script>
import { reactive,ref } from 'vue';
export default {
  setup(){
    return new Promise((resolve,reject) => {
      setTimeout(() => {
        resolve({
          msg:'啦啦啦啦啦啦啦'
        });
      },3000);
    });
  }
}
</script>

父组件:

<template>
    <h1>Suspense的使用</h1>
    <Suspense>
       <template #default>
           <HelloWorld />
       </template>
       <template v-slot:fallback>
           <h2>加载中...</h2>  
       </template>
    </Suspense>  
</template>

<script>
import HelloWorld from '@/components/HelloWorld';
export default {
  setup(){
   
  },
  components:{
    HelloWorld
  }
}
</script>

5.核心API(Object.defineProperty与Proxy)对比

6.vue2.x项目迁移到vue3.x 

猜你喜欢

转载自blog.csdn.net/THcoding_Cat/article/details/114155707