前段时间用vue3搭建完成了一个小项目,可喜的是领导在设计之初同意使用vue3进行新项目的开发,在此从技术角度记录一下vue3与vue2不同的点。

在main.js中的全局挂载方式

vue2:$mount

import Vue from "vue";
import App from "./App.vue";
import store from "./store/";
import router from "./router";new Vue({router,store,render: h => h(App)
}).$mount("#app");

vue3:createApp:返回一个提供应用上下文的应用实例,应用实例挂载的整个组件树共享同一个上下文

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'const app = createApp(App)
app.use(router)
app.use(store)
app.mount('#app')

createApp方法扩展

  • component:注册或检索全局组件
// 注册一个名为my-component的组件
app.component('my-component', {/* ... */
})// 检索注册的组件(始终返回构造函数)
const MyComponent = app.component('my-component')
  • config:包含应用配置的对象。以下是config的可配置项:

    globalProperties:添加一个可以再应用的任何组件实例中访问的全局property
    相当于vue2中的: Vue.prototype.$aa = '....'
    在vue3中的使用: app.config.globalProperties.$aa = '...'
    关于globalProperties的用法在下文有扩展~

  • directive:注册或检索全局指令

  • mixin:将一个mixin应用在整个应用范围内。

  • mount:所提供 DOM 元素的 innerHTML 将被替换为应用根组件的模板渲染结果。

  • provide:设置一个可以被注入到应用范围内所有组件中的值

  • unmount:卸载用用实例的根组件

  • use:安装vue.js插件。如果插件是一个对象,它必须报漏一个 install 方法;若它本身是一个函数,将被视为安装方法

  • version:以字符串形式提供已安装的vue的版本号

组合式API-setup

vue2中,我们书写逻辑部分,需要这样写:

export default {components: {...},props: {...},data () {return {num: ''}},computed: { },watch: {},mounted () {},methods: {}
}

当组件开始变大时,逻辑关注点的列表也会增长,后期不便于维护。
vue3使用setup:

  1. setup在组件创建之前执行(即在beforeCreated钩子之前)
  2. setup中不再使用this, 因为它不会找到组件实例
  3. setup接收两个参数:props & context的函数, context包含三个参数{attrs / slots / emit}
export default {components: {...},props: {...},setup(props, {attrs, slots, emit}) {console.log(props)//在这里可以写生命周期钩子函数onMounted(() => {....})//  在return返回所有用于DOM的变量和方法return {}}
}

结构简单了许多,写起来特别香~

生命周期钩子

如图是vue2和vue3的生命周期钩子对比:

vue3中给钩子函数都加上了“on”来访问

在单页面的引入

import {onMounted, onUpdated...} from 'vue'

这些函数接收一个回调函数,使用方法如上面例子
注意:钩子函数在使用之前必须要在单页面引入

响应式引用 ref & 响应式状态reactive

vue2中,双向绑定是基于Object.defineProperty()方法实现,且用this可以指向当前实例,所以一些简单的变量赋值就很容易的渲染到DOM上,完成响应。
vue3中,双向绑定是通过ES6的proxy方法实现,且页面不再使用this,响应式变量的定义就发生了变化:

ref

通过 ref 函数为变量创建了一个响应式引用。 使任何响应式变量在任何地方起作用

import { ref } from 'vue'const counter = ref(0)  // 括号里面是给counter赋初始值

因绑定方式发生变化,若打印counter 是如下结果:

所以,如果要对counter进行操作,需要用 counter.value。但是在DOM中双向绑定的时候不用.value,因为它会自动浅层次解包内部值,直接绑定就行。
ref一般用来定义基本数据类型的变量(Number,String,Boolean,Null, Undefined)

reactive

官方文档上对reactive的解释是:返回对象的响应式副本为 JavaScript 对象创建响应式状态;该响应式转换是“深度转换”——它会影响传递对象的所有嵌套 property。

所以,reactive一般用来定义引用数据类型的变量(Object,Array)

const obj = reactive({ count: 0 })

打印obj的结果如下:

操作count值: obj.count

以下关于解包:

  • reactive 将解包所有深层的 refs,同时维持 ref 的响应性
    ex1:
const count = ref(1)
const obj = reactive({ count })// ref 会被解包
console.log(obj.count === count.value) // truecount.value++
console.log(count.value) // 2
console.log(obj.count) // 2

ex2:当将 ref 分配给 reactive property 时,ref 将被自动解包。

const count = ref(1)
const obj = reactive({})obj.count = countconsole.log(obj.count) // 1
console.log(obj.count === count.value) // true
  • reactive定义变量可以使用 for…of 进行循环

响应式状态解构

当定义了一个响应式对象,我们想要采用ES6解构的方式获取其中的参数进行操作时:

import { reactive } from 'vue'const obj = reactive({name: 'wang',age: 18,sex: '女',height: 160
})// 解构
const { name, age } = objconsole.log('name: ', name, name.value)
console.log('age: ', age, age.value)


结果如上,解构出的两个property失去响应性。
所以,vue3里引入了api: toRefs,保留与源对象的响应式关联

import { reactive, toRefs } from 'vue'const obj = reactive({name: 'wang',age: 18,sex: '女',height: 160
})// 解构
const { name, age } = toRefs(obj)console.log('name: ', name, name.value)
console.log('age: ', age, age.value)

结果如下:

我们做进一步的测试,修改解构之后的name值,是否会反应到源对象obj上呢?

name.value = '小王'
console.log('name: ', name.value, obj)


如上,修改name值之后,源对象obj的值会同步更新,完美~

一个小栗子

基于上面的生命周期和响应式引用,我们来看一个较完整的小栗子:

<template><div><p>{{ num }}</p><p v-for="item in arr" :key="item">{{ item }}</p><button @click="getData">点击事件</button></div>
</template><script>
import { ref, reactive, onMounted } from 'vue'export default {setup() {// 响应式const num = ref(1)const arr = reactive(['red', 'yellow'])// 不具备响应式,可用于中间逻辑操作,但无法在dom中使用let testNum = 0// methods:const getData = () => {console.log('getData')}// 生命周期onMounted(() => {getData()})// DOM中使用到的响应式引用和方法需要returnreturn {num,arr,getData}}
}
</script>

计算属性和侦听器

computed

在vue2中,computed是这么使用的:

computed: {variable() {const path = this.$route.pathreturn path }
}

计算属性内部存在缓存,当返回值发生变化才会触发
在vue3中,computed这样使用:

import { computed } from 'vue'setup() {const variable = computed(() => localStorage.getItem('userid'))
}

写法简化
注意,vue3中的承载计算属性的变量 variable 同样具备响应式引用,使用时:variable.value

watch

在vue2中, watch这样使用:

 watch: {// 监听变量  watcherVal(newValue,oldValue) {//  处理逻辑}}

watch 需要侦听特定的数据源,并在单独的回调函数中执行副作用。默认情况下,它也是惰性的——即回调仅在侦听源发生变化时被调用。

vue3中,这么用:
侦听单一源:

import { watch } from 'vue'setup(){const count = ref(0)watch(count, (newValue, oldValue) => {/* ... */})
}

同时侦听多个源,采用数组形式:

import { watch } from 'vue'setup(){const var1 = ref(0)const var2 = ref(1)watch([var1, var2], ([newVar1, newVar2], [oldVar1, oldVar2]) => {/* ... */})
}

ref获取DOM

在vue使用ref获取DOM之前,我们一般使用js的原生api,用ref获取就方便了许多。尤其是在画echarts图时
而ref不止是可以获取DOM,同时可以担任起父子组件的传参~
我们先看vue2中ref的使用:

<template>// 子组件<test-component ref="testComp" /><div ref="divDom"></div>
</template>// methods:
methods:{someFunction() {// this.$refs.testComp 即可得到子组件DOM, initFunc:子组件方法this.$refs.testComp.initFunc(params)// 获取div的DOMconsole.log(this.$refs.divDom)}
}

在vue3中,使用组合式api(setup)时,响应式引用和模板引用的概念是统一的,即上面提到的定义响应式引用的ref和DOM中的ref是一个概念,那么ref获取DOM如下使用:

<template> <div ref="root">hello world</div>
</template><script>import { ref, onMounted } from 'vue'export default {setup() {const root = ref(null)onMounted(() => {// DOM 元素将在初始渲染后分配给 refconsole.log(root.value) // <div>hello world</div>})return {root}}}
</script>

如上,定义一个与ref同名的变量,return之后,就可以使用 root.value 来获取DOM。
同时,若ref定义在子组件上,可以调用子组件方法进行传参,甚至修改子组件的变量值:

补充上面例子:<template> <div ref="root">This is a root element</div>// 子组件<test-component ref="testComp" />
</template><script>import { ref, onMounted } from 'vue'export default {setup() {const root = ref(null)const testComp = ref(null)const someFunction = () => {// 执行子组件的initFunc方法,并传参paramstestComp.value.initFunc(params)}onMounted(() => {// DOM 元素将在初始渲染后分配给 refconsole.log(root.value) // <div>This is a root element</div>console.log(testComp.value)someFunction()})return {root,testComp}}}
</script>

globalProperties方法扩展

上文也简单提到了globalProperties,可以定义全局property,在DOM中的使用和vue2一样没有变化:

app.config.globalProperties.$aa = '...'
$aa  // DOM中用

但是在setup-methods中,因为缺少this指向,vue3文档中提到一个新的api:getCurrentInstance
getCurrentInstance:支持访问内部组件实例

不能滥用!

博主当时遇到的情况是:全局引入并定义了echarts之后,在方法中使用echarts来绘图,发现无法拿到全局的echarts,然后查到了该api

使用如下:

import { getCurrentInstance } from 'vue'
export default {setup() {const internalInstance = getCurrentInstance()internalInstance.appContext.config.globalProperties // 访问 globalProperties}
}

getCurrentInstance 只能在setup或生命周期钩子中调用
我们可以看一下,在我的项目中,internalInstance.appContext.config.globalProperties的打印:

nextTick

nextTick: 等待DOM更新之后执行
vue2:

this.$nextTick(() => {// do something
})

vue3:

setup() {const someFunc= async () => {await nextTick()console.log('DOM is updated')
}

prop&emit

来到父子组件传值啦~
先回想下vue2中的prop&emit的使用:

// 子组件:<template> <div><p> {{ vari1 }} </p><button @click="handleClick">触发</button></div>
</template><script>
export default {props: {vari1: {type: String,default: 'hi'}},data(){return{}},methods: {handleClick() {this.$emit('handleClickFunc')}}
}
</script>
// 父组件<template> <div>// 子组件<test-component :vari1="variable"  @handleClickFunc="handleClickFunc" /></div>
</template><script>
export default {data() {return {variable: 'hello'}},methods: {handleClickFunc() {console.log('hello world')}}
}
</script>

如上,是不是非常熟悉~
那么,vue3有什么改变呢?
我们通过上文也知道了api setup的两个参数setup(props, {attrs, slots, emit }){}。
props 对象将仅包含显性声明的 prop,并且所有声明了的prop,不论父组件是否向其传递,都会出现在props对象。
emit作用没变,不过vue3提供了一个emits选项,emits可以用来定义一个组件可以向其父组件触发的事件。
栗子如下:

// 子组件:<template> <div><p> {{ vari1 }} </p><button @click="handleClick">触发</button></div>
</template><script>
export default {props: {vari1: {type: String,default: 'hi'}},// 定义组件可触发的事件emits: ['handleClickFunc'],setup(props, { emit }) {console.log(props.vari1)const handleClick = () => {emit('handleClickFunc')}return {handleClick}}
}
</script>
// 父组件<template> <div>// 子组件<test-component :vari1="variable"  @handleClickFunc="handleClickFunc" /></div>
</template><script>
export default {setup() {const variable = 'hello'const handleClickFunc = () =>  {console.log('hello world')}return {variable,handleClickFunc}}
}
</script>

父组件中除了新api之外没有特殊变化,还是子组件里增加了变化props&emits

vuex

创建一个store

想想vue2是怎么创建store的呢?

import Vue from "vue";
import Vuex from "vuex";Vue.use(Vuex);export default new Vuex.Store({state: {},mutations: {},actions: {}
});

如上,创建成功之后在main.js中进行挂载
那么,vue3呢?
vue3引入了一个新api:createStore
如下:

import { createStore } from 'vuex'// 创建一个新的 store 实例
const store = createStore({state () {},mutations: {},actions: {}
})

创建成功之后在main.js中将store实例作为插件安装(.use)
这样就创建成功了。

useStore()

vue3中,通过调用函数useStore来在setup中访问store,相当于vue2中使用的this.$store
调用方式如下:

import { useStore } from 'vuex' // 引入export default {setup () {const store = useStore()}
}

如上,获取到store之后,就可以访问State、Getter、Mutation和Action
为了访问state和getter,需要computed引用来保留响应式:
要使用 mutation 和 action 时,只需要在 setup 钩子函数中调用 commit 和 dispatch 函数:

import { computed } from 'vue'
import { useStore } from 'vuex'export default {setup () {const store = useStore()return {// 在 computed 函数中访问 statecount: computed(() => store.state.count),// 在 computed 函数中访问 getterdouble: computed(() => store.getters.double)// 使用 mutationincrement: () => store.commit('increment'),// 使用 actionasyncIncrement: () => store.dispatch('asyncIncrement')}}
}

题外话:
vue2中有些辅助函数:mapState、mapGetters、mapMutations、mapActions,但是vue3里面是没有这些辅助函数的。目前只有useStore函数。

router

this.$router & useRouter()

vue2中,我们通过this.$router来访问路由器,同时作用于路由跳转

this.$router.push('page')
this.$router.push({name:'Page', params: { name:'xiaowang' }})

vue3中, setup 里面没有访问 this,使用useRouter函数来代替this.$router

import { useRouter } from 'vue-router'export default {setup() {const router = useRouter()const someFunction = () => {router.push({ name: 'Page', params: {  name: 'xiaowang'  } })}return {someFunction}}
}

this.$route & useRoute()

vue2中,我们通过this.$route来访问当前的路由,并作用于接收从this.$router跳转页面携带的参数

// 接上面this.$router代码
this.$route.params.name // 'xiaowang'

vue3中,采用useRoute函数代替this.$route

import { useRoute } from 'vue-router'export default {setup() {const route = useRoute()const userData = ref()// 当参数更改时获取用户信息watch(() => route.params,(newParams) => {userData.value = newParams.name})}
}

小结: 在模板中我们仍然可以访问 $router 和 $route,在 setup 中就需要使用函数。所以不必在setup中返回 router 和 route

总结

用vue3完成了一个历时近两月的小项目,一开始写有很多不理解的地方,但是随着熟练加深,发现vue3写起来是真香~代码逻辑结构更加清晰了,而且新的响应式原理,比2版本强大很多。有幸看到最后的朋友们可以尝试一波。
目前对于vue2和vue3不同点的总结就先更新到这,以后如果再遇到会持续更新。
最后。vue3不支持IE浏览器! 在实际项目中请谨慎!

vue3和vue2不同点总结相关推荐

  1. Vue3比Vue2有什么优势/区别

    优势 性能更好 体积更小 更好的ts支持 更好的代码组织 更好的逻辑抽离 更多新功能 Vue3生命周期 Options API 生命周期 Composition API 生命周期 Options AP ...

  2. Windows 切换node版本开发Vue3和Vue2

    Windows 切换node版本开发Vue3和Vue2 第一步:先清空本地安装的node.js版本 第二步:安装nvm管理工具(先关掉360等软件,不然会弹出警告!) 1.从官网下载安装包 https ...

  3. vue3相比vue2效率提升在哪些方面?

    vue3相比vue2效率提升在哪些方面? 静态提升 预字符串化 缓存事件处理函数 Block Tree PatchFlag 静态提升 相比vue2,vue3对以下静态节点进行提升: 元素节点 没有绑定 ...

  4. 在 Vue3 成为默认版本后,盘点了 Vue3 与 Vue2 的区别

    目录 前言 正文 一.Vue3 与 Vue2 区别概览 二.Vue3 与 Vue2 区别详述 生命周期 多根节点 Composition API 异步组件(Suspense) Teleport 响应式 ...

  5. 一个 Java 猿眼中 Vue3 和 Vue2 的差异

    随着 TienChin 项目视频的录制,松哥终于也要静下心来,认真捋一捋 Vue3 中的各种新特性了,然后再和小伙伴们进行分享,其实 Vue3 中还是带来了很多新鲜的玩意,今天我们就不卷 Java 了 ...

  6. vue - vue3与vue2.x的区别(一) :目录结构不一致

      今天总结一下vue3与vue2.x的区别 -- 目录结构不一致.由于目录结构的不一致相对的也造成了一些问题的存在,比如打包项目之后打开出现白屏现象. 一.目录结构不一致   通过上图可以发现 vu ...

  7. Vue3和Vue2的区别

    目录 前言 概览 一.新特性 二.差异 详情 一.vue3新特性 1.组合式API---setup 2.ref创建响应式数据 3.Teleport---"传送门" 4.多根节点 5 ...

  8. 使用 Vue3 重构 Vue2 项目(长文)

    前言 2020年9月18日,vue3正式版发布了,前几天把文档整体读了一遍,感触很深,可以解决我项目中的一些痛点,于是就决定重构之前那个vue2的开源项目. 本篇文章就记录下重构vue2项目的过程,欢 ...

  9. vue3与vue2的详细区别

    1. vue实例化方法改变 vue2通过new Vue(),手动生成.vue3通过createApp实例化 // vue2 import Vue from 'vue':import App from ...

  10. VUE3对比VUE2的优势及新特性原理

    1.Vue3.0新特性 性能比Vue2.x快1.2~2倍 原因1: diff方法优化: vue2中的虚拟dom是全量的对比(每个节点不论写死的还是动态的都会比较) vue3新增了静态标记(patchf ...

最新文章

  1. maya表情blendshape_Maya的形状融合变形器Blend Shape | 学步园
  2. effective C++ 条款 21:必须返回对象时别妄想返回其reference
  3. Python 应用领域
  4. Java黑皮书课后题第5章:**5.34(游戏:石头、剪刀、布)编程练习题3.17给出玩石头-剪刀-布游戏的程序。修改这个程序,让用户可以连续玩这个游戏,直到用户或者计算机赢对手两次以上为止
  5. java 数组的冒泡排序
  6. java 中 正则 正则表达式 匹配 url
  7. java不同项目加token访问_利用JWT实现前后端分离的Token验证
  8. 快速了解Linux ps命令
  9. 用设计解决问题 ——访小米科技、小米路由器事业部总经理 唐沐
  10. 开关电源设计书籍推荐
  11. 汇编语言学习笔记(【汇编语言】小甲鱼零基础汇编)
  12. 移动端用户设置字体放大导致的问题
  13. Mac制作启动U盘解决重启按option不能识别的问题
  14. 【SpringBoot】63、SpringBoot中教你手把手封装自己的starter(xxl-job-spring-boot-starter)
  15. 如何批量调整Word中mathtype公式的大小
  16. 基金经理研究所 | 从兴全合润看谢治宇的攻守道
  17. adb安装报错情形以及解决办法
  18. JAVA学习笔记五---函数
  19. python输入百分制成绩输出成绩等级_switch实现成绩打等级
  20. U盘制作-BGA焊接练习

热门文章

  1. 思迈特软件Smartbi:公安大数据的3个发展阶段
  2. w10运行游戏计算机中丢失xinput1-3.dll,WIN10启动游戏由于找不到xinput13dll无法运行如何修复...
  3. Py第四问 from test import test1 ImportError:cannot import name 'test1'
  4. HTB打靶日记:Soccer
  5. IBM Platform LSF在IC行业内的使用
  6. VC++公安指纹识别系统
  7. spring boot毕业设计选题及程序开发功能说明
  8. 通过搜狗蜘蛛池,让网站收录排名提升
  9. TensorFlow中的Shape如何理解
  10. thinkphp使用migration/Seeder