Vue v3.x 概述
Vue 3 引入了一些新功能和一系列重大更改。让我们看看这些更改将如何影响我们的应用程序。
Vue V3.x 重大更改
在 Vue 3 中,重大更改基本上分为七类:
- 全局 API
(负责 Vue 的行为方式) - 您很可能想要查看这些更改。 - 模板指令
(对 v- 指令的工作方式所做的更改) - 您很可能想要查看这些更改。 - 组件
(对组件工作方式的更改) - 如果您的应用程序使用组件,则您很可能要查看这些更改 - 呈现函数(允许您以编程方式创建 HTML 元素)
- 自定义元素(告诉 Vue 有关创建自定义 HTML 元素的信息)
- 次要更改(这些可能不会影响您,但您仍然需要查看这些更改)
- 删除了 API(在 Vue 3 中不再可用的内容)
在所有更改中,有一些是任何应用程序都会使用的,例如全局API和组件。因此,如果你想开始使用新的 Vue 版本,就需要考虑它们。
值得一提的是以下附加更改:
- 您创建 Vue 应用程序和组件实例的方式已更改(全局 API)
- 应始终将数据选项声明为函数(细微更改)
- 对同一元素使用 v-if 和 v-for 时的优先级更改(模板 Ddrectives)
- 应为组件事件(组件)声明发出选项
有关更改的完整列表,您可以转到文档
现在,让我们更详细地看一下其中的一些更改。
全局属性
vue2
- 对于一些第三方插件,Vue2通常使用原型原型来挂载到VUE对象上。
import Vue from 'vue'
Vue.prototype.$http=Axiox
Vue.prototype.$echart= Echart
vue3
- Vue3Global 属性配置中提供了一个名称,可以替换 Vue2 中的原型
globalProperties
app.config.globalProperties.$http = Axios
app.config.globalProperties.$echart = Echart
- 使用 $ http
import {getCurrentInstance} from 'vue'
setup () {
const { ctx } = getCurrentInstance();
onMounted(() => {
console.log(ctx.$http)
})
.......
}
REF 和 V-FOR用于生成
vue2
VUE2中的V-FOR与REF一起使用,当引用批量模板时,获得的REF是一个数组。
<div v-for = "i in 3" ref = "setItemref": key = "i"> {{i}} </ div> // Here are arrays
mounted() {
console.log(this.$refs.setItemRef)
},
vue3
VUE3 中的 REF 绑定是一个函数,
<div v-for = "item in 3": ref = "setItemRef"> </ div> // This is a function
setup(){
let itemRefs = []
const setItemRef = el => {
itemRefs.push(el)
}
onMounted(() => {
console.log(itemRefs)
})
}
REF 的 DOM 模式已更改,但结果是相同的
异步组件
在路由中,通常使用延迟加载的方式来引入组件。
vue2
component: () => import('@/views/homePage/index.vue'),
vue3
在 Vue3 中引入一个新方法--->定义异步组件来打包 Vue2 介绍的内容defineAsyncComponent
import { defineAsyncComponent } from 'vue'
component: defineAsyncComponent(() => import('./NextPage.vue'))
自定义说明
更改挂钩函数的命名
vue2
VUE2 中绑定的钩子函数是
- bind - 指令绑定到元素。仅发生一次。
- inserted - 将元素插入父 DOM 时。
- update - 当元素更新,但子元素尚未更新时,将调用此挂接。
- componentUpdate - 一旦组件和子级更新,将调用此挂接。
- unbind - 接触绑定。一旦指令被删除,将调用此钩子。只呼叫一次。
vue3
钩子函数的命名与生命周期的钩子函数一致
- bind → beforeMount
- inserted → mounted
- beforeUpdate:new!这是在元素本身更新之前调用的,非常类似于组件生命周期挂钩。
- componentUpdated → updated
- beforeUnmount:new!与组件生命周期挂钩类似,它将在卸载元素之前调用。
- unbind -> unmounted
挂接函数中的引用组件实例
某些实例需要获取组件中实例的某些属性
vue2
- 需要通过 vNod 获取实例
Vue.directive('has', {
inserted: (el, binding, vnode) => checkPermission(el, binding, vnode),
});
export const checkPermission = (el, binding, vnode) => {
...
Const permissionarr = vnode.context. $ store.state.PermissionID // All permission ID
...
}
vue3
- 从绑定中获取对象
export const checkPermission = (el, binding, vnode) => {
...
Const permissionarr = binding.instance. $ store.State.PermissionID // All permission ID
...
}
v-bind=“$attrs”
自定义元素元素交互
IS 用法
vue2
- 组件:
<component :is="currentTabComponent"></component>
- html
<table>
<tr is="blog-post-row"></tr>
</table>
vue3
- 子组件Assembly
<component is="currentTabComponent"></component>
- html
<table>
<TR V-IS = "'blog-post-rot"> </ tr> // V-IS is similar to binding a variable, and we need a component name, a string, so wrapped with single quotes
</table>
事件
- Vue3 移除、、,三种方法,只保留。
$on
$off
$once
$emit
滤波器
过滤器函数在 Vue3 中被删除。建议使用方法或计算代替,实际上,在 Vue2 中,你可以实现这两种方式。
部分过滤器
vue2
<p>{{message|toLower}}</p>
data() {
return {
message: 'ABC'
}
},
filters: {
toLower(value) {
return value.toLowerCase()
}
}
vue3
- Vue3 是用计算或方法定义的
<p>{{messageToLower}}</p>
import {
computed,
ref,
} from 'vue';
setup(){
let message = ref('ABC')
let messageToLower = computed(() => {
// console.log(message)
return message.value.toLowerCase()
})
return{
messageToLower,
}
}
全局过滤器
vue2
Vue.filter('toLower', (value)=> {
return value.toLowerCase()
})
vue3
- 在main.js中注册
const app =createApp(App)
app.config.globalProperties.$filter={
toLower(value){
return value.toLowerCase()
}
}
- 用
<p>{{$filters.toLower(message)}}</p>
根节点
vue2
- Vue2只有一个根节点只能存在
template
<template>
<div id="app">
...
</div>
</template>
vue3
- Vue3 中可以存在多个节点
<template>
<div>...</div>
<a>...</a>
<p>...</p>
</template>
template
<template>
<div class="wrap">
<div>{{ num }}</div>
<Button @ click = "getdata"> change data </ button>
</div>
</template>
<style lang="less" scoped>
.wrap {
width: 100%;
height: 100%;
border: 1px solid red;
}
</style>
vue2
<!-- vue2 -->
<script>
export default {
data() {
return {
num: 333,
};
},
//
mounted() {
alert('111');
this.getData();
},
methods: {
getData() {
this.num = '2222';
},
},
};
</script>
vue3.0
<!-- vue3.0 -->
<script>
import { defineComponent, ref } from 'vue';
export default defineComponent({
setup() {
let num = ref(1223);
function getSecondData(params) {
alert('222');
}
function getData(params) {
num.value = 'weruwiru';
getSecondData();
}
return {
num,
getData,
};
},
});
</script>
vue3.2
<!-- vue3.2 -->
<script setup>
import { ref } from 'vue';
let num = ref(1223);
function getSecondData(params) {
alert('222');
}
function getData(params) {
num.value = 'weruwiru';
getSecondData();
}
</script>
功能组件
全球原料药
内联模板
vue2
-
使用内联模板属性
-
Vue2 中的文档会提示这样一个词,所以我几乎用了
但是,
内联模板
会使模板的作用更难理解。因此,作为最佳实践,请选择组件模板
Option 或.vue
One中的file<template>
Elements来定义模板。>
vue3
删除此功能,
唯一键
输入 V-IF
vue2
- 在 Vue2 中,V-IF 中的关键是 V-Else 控制组件或元素。
- 如果您没有密钥,您将被重复使用。<你好世界>组件仅创建一个
<!---->
<template v-if="loginType === 'username'">
<hello-world title="username"></hello-world>
</template>
<template v-else>
<hello-world title="email"></hello-world>
</template>
<Button @ Click = "ChangeType"> Switch </ button>
- 使用 Key,每个开关将呈现组件(在 KEY 中提供)
<template v-if="loginType === 'username'">
<hello-world title="username" key="a"></hello-world>
</template>
<template v-else>
<hello-world title="email" key="b"></hello-world>
</template>
<Button @ Click = "ChangeType"> Switch </ button>
vue3
Vue3 默认为键,此键始终是唯一的,不同于其他键,不能使用相同的键
来强制重用分支。
<div v-if="condition">Yes</div>
<div v-else>No</div>
在模板中的 V-for 中 KEY
vue2
在 Vue2 的 Template 选项卡上,你可以使用 V-FOR 指令,但不能绑定 Key,只能在子节点上绑定唯一的 KEY。
<template v-for="item in list">
<div :key="item.id">...</div>
</template>
vue3
键可以绑定到 Vue3 中的模板
<template v-for="item in list" :key="item.id">
<div>...</div>
</template>
设置数据
这就是主要区别所在 —— Vue2 Options API 与 Vue 3 Composition API。
选项 API 将我们的代码分为不同的属性:数据、计算属性、方法等。同时,组合API允许我们按函数而不是属性类型对代码进行分组。
对于窗体组件,假设我们只有两个数据属性:a 和 a。username password
Vue2 代码看起来像这样 – 我们只是在数据属性中折腾了两个值。
export default {
props: {
title: String,
},
data() {
return {
username: "",
password: "",
};
},
};
在 Vue 3 中,我们必须通过使用一种新方法来投入更多的精力,其中所有组件初始化都应该进行。setup()
此外,为了让开发人员能够更好地控制什么是反应式的,我们可以直接访问 Vue 的反应性 API。
创建反应式数据涉及三个步骤:
-
从 vue 导入
reactive
-
使用反应式方法声明我们的数据
-
让我们的方法返回反应式数据,以便我们的模板可以访问它
setup
就代码而言,它看起来有点像这样。
import { reactive } from "vue";
export default {
props: {
title: String,
},
setup() {
const state = reactive({
username: "",
password: "",
});
return { state };
},
};
然后,在我们的模板中,我们像和state.username state.password
在 Vue 2 与 Vue 3 中创建方法
Vue2 选项 API 有一个单独的方法部分。在其中,我们可以定义我们所有的方法并以我们想要的任何方式组织它们。
Vue 3 Composition API中的 setup 方法也处理方法。它的工作方式有点类似于声明数据——我们必须首先声明我们的方法,然后返回它,以便我们组件的其他部分可以访问它。
创建我们的模板(根目录)
对于大多数组件,Vue 2 和 Vue 3 中的代码即使不相同,也会非常相似。但是,Vue 3 支持 Fragments,这意味着组件可以有多个根节点。
这在渲染列表中的组件以删除不必要的包装器 div 元素时特别有用。但是,在这种情况下,我们将为表单组件的两个版本保留一个根节点。
唯一真正的区别是我们如何访问我们的数据。在 Vue 3 中,我们的响应式数据都包装在一个响应式状态变量中——因此我们需要访问这个状态变量来获取我们的值。
生命周期挂钩
在 Vue2 中,我们可以直接从我们的组件选项中访问生命周期钩子。对于我们的示例,我们将等待mounted 事件。
现在使用 Vue 3 组合 API,几乎所有内容都在 setup() 方法中。这包括已安装的生命周期挂钩。
但是,默认情况下不包含生命周期钩子,因此我们必须导入onMounted
在 Vue 3 中调用的方法。这看起来与之前导入响应式的方法相同。
然后,在我们的 setup 方法中,我们可以通过将 onMounted 方法传递给我们的函数来使用它。
计算属性
让我们添加一个计算属性,将我们的用户名转换为小写字母。
为了在 Vue2 中实现这一点,我们向选项对象添加了一个计算字段。从这里,我们可以像这样定义我们的属性……
Vue 3 的设计允许开发人员导入他们使用的东西,并且在他们的项目中没有不必要的包。本质上,他们不希望开发人员必须包含他们从未使用过的东西,这在 Vue2 中正成为一个日益严重的问题。
因此,要在 Vue 3 中使用计算属性,我们首先必须将计算属性导入到我们的组件中。
然后,与我们之前创建响应式数据的方式类似,我们可以将一段响应式数据作为计算值
访问props
访问 props 带来了 Vue 2 和 Vue 3 之间的重要区别——this
意味着完全不同的东西。
在 Vue2 中,this
几乎总是引用组件,而不是特定的属性。虽然这在表面上使事情变得容易,但它使类型支持变得痛苦。
然而,我们可以很容易地访问道具——让我们添加一个简单的例子,比如title
在挂载的钩子中打印出我们的道具:
但是在 Vue 3 中,我们不再使用this
来访问 props、发出事件和获取属性。相反,该setup()
方法采用两个参数:
-
props
– 对组件道具的不可变访问
-
context
– Vue 3 公开的选定属性(emit、slots、attrs)
使用 props 参数,上面的代码看起来像这样。
Emitting事件
同样,在 Vue 2 中Emitting事件非常简单,但 Vue 3 让您可以更好地控制如何访问属性/方法。
假设在我们的例子中,当按下“提交”按钮时,我们想要向父组件发出登录事件。
Vue 2 代码只需要调用this.$emit
并传入我们的有效负载对象。
然而,在 Vue 3 中,我们现在知道这this
不再意味着同样的事情,所以我们必须以不同的方式来做。
幸运的是,上下文对象暴露emit
给了我们同样的东西this.$emit
我们所要做的就是将context
第二个参数添加到我们的设置方法中。我们将解构上下文对象以使我们的代码更简洁。
然后,我们只需调用 emit 来发送我们的事件。然后,就像以前一样,emit 方法接受两个参数:
-
我们的活动名称
-
与我们的事件一起传递的有效负载对象
如何在 Vue 3 中创建应用程序和组件实例
在 Vue 3 中,你创建应用的方式发生了变化。Vue 应用程序现在使用新方法来创建应用程序实例。.createApp()
Vue 应用程序现在被视为根组件,因此您定义其数据选项的方式也发生了变化。
HTML 根元素尚未更改,因此在索引.html文件中,您仍将看到如下内容:
<div id="app"></div>
在 JavaScript 文件中,您需要了解一个重要的更改:您将不再用于创建新的应用程序实例,而是使用一个名为:new Vue()
createApp()
// Vue 3 syntax
const app = Vue.createApp({
// options object
})
app.mounth('#app') // Vue Instance - Root component
// Vue 2 syntax
const app = new Vue({
// options object
el: '#app'
})
如何在 Vue 3 中定义组件
要在 Vue 3 中定义组件,请不再使用 。相反,您现在使用应用程序根组件,如下所示:Vue.component()
/* Vue 3 syntax */
const app = Vue.createApp({
// options here
})
app.component('componenet-name', {
// component code here
})
/* Vue 2 syntax*/
Vue.component('component-name', {
// component code here
})
如何在 Vue 3 中使用数据选项对象
假设主应用实例现在被视为根组件,则无法再将数据属性指定为对象。相反,您需要将其定义为返回对象的函数,就像通常在组件中所做的那样。
// Vue 3
const app = Vue.createApp({
// options object
data(){
return {
message: 'hi there'
}
}
})
app.mounth('#app') // Vue Instance - Root component
// Vue 2 syntax
const app = new Vue({
// options object
el: '#app'
data: {
message: 'hi there'
}
})
Vue 3 中 v-if/v-for 的优先级更改
在 Vue 2 中,如果在同一元素上使用了两个指令,则 v-for 指令将优先于 v-if。但是在 Vue 3 中,v-if 总是优先。
但是,使用这两个指令并不是一个好主意。请务必访问此处的文档以了解更多信息。
如何在 Vue 3 中的组件事件上使用 Emits 属性(重大更改/新功能)
与该属性类似,现在在 Vue 3 中,还有一个属性,组件可以使用该属性来声明它可以向父组件发出的事件。props
emits
我强烈建议使用此属性,以避免在需要重新发出本机事件(如单击事件)的组件中两次发出事件。
以下是官方文档中的示例:
<template>
<div>
<p>{{ text }}</p>
<button v-on:click="$emit('accepted')">OK</button>
</div>
</template>
<script>
export default {
props: ['text'],
emits: ['accepted']
}
</script>
emits 属性也可以接受对象。
我现在还不会深入探讨这个问题,但我保证迟早会在专门的视频系列中解决每个功能/变化。
Vue 路由器 v4.x 概述
随着 Vue.js 的新版本发布,我们也有了新版本的 Vue Router。新版本 v4.x 有一些重大更改,如果你想将项目迁移到新的 Vue 版本,你需要考虑这些更改。
Vue 路由器 V4 重大更改
两个重大更改特别值得一提,因为它们位于 Vue 路由器应用程序的基础。稍后需要了解它们才能迁移应用程序。
- Vue 路由器实例已更改
- 有一个新的历史记录选项
此处提供了更改的完整列表。
让我们深入看看这两个变化。
Vue 路由器 4 实例已更改
要创建新的 Vue 路由器实例,请不再使用 VueRuter 函数构造函数。
以下是 Vue Router v.3x 文档,因此您可以进行比较。
代码由以下原因更改:
// 3. Create the router instance and pass the `routes` option
// You can pass in additional options here, but let's
// keep it simple for now.
const router = new VueRouter({
routes // short for `routes: routes`
})
// 4. Create and mount the root instance.
// Make sure to inject the router with the router option to make the
// whole app router-aware.
const app = new Vue({
router
}).$mount('#app')
对此:
// 3. Create the router instance and pass the `routes` option
// You can pass in additional options here, but let's
// keep it simple for now.
const router = VueRouter.createRouter({
// 4. Provide the history implementation to use. We are using the hash history for simplicity here.
history: VueRouter.createWebHashHistory(), // <-- this is a new property and it is mandatory!
routes, // short for `routes: routes`
})
// 5. Create and mount the root instance.
const app = Vue.createApp({})
// Make sure to _use_ the router instance to make the
// whole app router-aware.
app.use(router)
app.mount('#app')
在上面的代码中,要创建新的 Vue 路由器实例,您现在必须使用 VueRouter 对象并调用该方法。createRouter()
此外,新的历史记录属性是必需的 – 。您必须定义它,否则将收到控制台错误。history: VueRouter.createWebHashHistory()
接下来,您将使用该方法创建 Vue 实例,并使用变量来调用该方法。您将您在上一步中创建的路由器实例传递到该位置。createApp()
app
.use()
最后,您可以使用 在应用程序实例上挂载根 DOM 元素。app.mount('#app')
你可以阅读 Vue 路由器 v4.x 文档了解更多详情。
路由的变化:
main.js
// create and mount the Vue instance
const app = new Vue({
router
}).$mount('#app')
对此:
// create and mount the Vue instance
const app = Vue.createApp({
router
})
app.mount('#app')
Vue 路由器的实例化方式已更改
它从这个变化:
// create the router instance
const router = new VueRouter({
routes
})
对此:
// create the router instance
const router = VueRouter.createRouter({
// Provide the history implementation to use. We are using the hash history for simplicity here.
history: VueRouter.createWebHashHistory(),
routes, // short for `routes: routes`
})
上面的代码处理两个主要更改:已替换为 ,新选项现在将替换 。new VueRouter()
VueRouter.createRouter()
history
mode
请访问 Vue 路由器 4 的文档以了解更多信息。
最后,让我们的应用程序知道我们正在使用 Vue 路由器。如果我们在 Vue 应用程序中注入了路由器实例,现在我们需要指示它使用 Vue 路由器,使用该方法执行此操作,并将路由器实例传递给它。.use()
由此更改:
// create and mount the Vue instance
const app = Vue.createApp({
router
})
app.mount('#app')
对此:
// create and mount the Vue instance
const app = Vue.createApp({})
app.use(router)
app.mount('#app')