【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】vue3.x基础知识学习_第1张图片

 

vue3.x时,不再提示:

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

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

【vue3.x】vue3.x基础知识学习_第2张图片

 

 

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

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

 

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

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

 

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中: 

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

 

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

//接收的形参未循环的每一个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方式:

 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项目;或是升级vue-cli脚手架,则在新建项目时有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对象

【vue3.x】vue3.x基础知识学习_第3张图片

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,在未得到数据前会有一个加载中的过渡效果

 参考: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 {

    }
  }
}

执行结果:

【vue3.x】vue3.x基础知识学习_第4张图片

 

 

3.ref

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

示例:



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

html代码: 

This is an about page

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实现,通过代理对象操作源对象内部数据都是响应式的

示例:




执行结果:

执行前显示:

【vue3.x】vue3.x基础知识学习_第5张图片

执行后显示:

【vue3.x】vue3.x基础知识学习_第6张图片

 

 

5.ref与reactive细节

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

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

 

 

示例:



执行结果:

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

【vue3.x】vue3.x基础知识学习_第7张图片

 

 

tips:ref和reactive有什么区别?

 

6.toRefs

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

示例:



 

 

 

7.计算属性computed

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

示例:



执行结果:

 

 

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.生命周期

【vue3.x】vue3.x基础知识学习_第8张图片

 

与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处理  

示例:



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

 

2.readonly与shallowReadonly

readonly

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

shallowReadonly

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

示例:




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

 

3.toRow与markRow

toRow

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

markRow

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

示例:



 

4.toRef

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

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

示例:



 

5.customRef

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

示例:

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

自定义hook函数

  • 使用Vue3.x组合API封装的可复用的功能函数
  • 自定义hook类似于Vue2.x中的mixin(简单来说,其实就是封装公共的函数方法或者可以分离提取的功能,自定义hook叫做功能模块感觉更适合一点)
  • 好处:代码可复用,简洁


 

6.provide与inject

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

示例:

这里以父子间传值为例

父组件:




子组件:






效果:

【vue3.x】vue3.x基础知识学习_第9张图片

 

 

7.响应式数据的判断

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

 

8.Teleport(瞬移)

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

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

示例:

子组件:

//HelloWorld组件


父组件:



 效果:

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

【vue3.x】vue3.x基础知识学习_第10张图片

 

 

9.Suspense

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

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

示例:

子组件:

//HelloWorld组件


父组件:



 

 

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

 

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

你可能感兴趣的:(前端,vue3,vue3.x,vue3与vue2的区别,vue3新特性)