写给 vue2.0 开发者的 vue3.0 教程

Vue 3尚未正式发布,可是维护者已经发布了beta版本,以供咱们的用户尝试并提供反馈
html

若是您想知道Vue 3的主要特性和主要变化,我将在本文中经过使用Vue 3 beta 9建立一个简单的应用程序来强调它们vue

我将介绍尽量多的新内容,包括片断、传送、复合API和其余一些模糊的更改。我也会尽我所能来解释这个特性或变动的基本原理webpack

如何构建

咱们将构建一个带有模态窗口功能的简单应用程序。我选择这个是由于它方便地容许我展现一些Vue 3的更改。git

下面是这款应用在打开和关闭状态下的样子,这样你就能够在脑海中想象出咱们正在作的事情:github

图片

Vue3.0的安装与启动

与其直接安装Vue 3,不如克隆Vue -next- Webpack -preview项目,它将为咱们提供包括Vue 3在内的最小的Webpack设置。web

$ git clone https://github.com/vuejs/vue-next-webpack-preview.git vue3-experiment
$ cd vue3-experiment
$ npm i

一旦克隆并安装了NPM模块,咱们所须要作的就是删除样板文件并建立一个新的main.js文件,这样咱们就能够从头建立Vue 3应用程序了。npm

$ rm -rf src/*
$ touch src/main.js

如今咱们将运行开发服务器:数组

建立一个新的 vue3.0 app

立刻,咱们启动一个新的Vue应用程序的方式改变了。咱们如今须要导入新的createApp方法,而不是使用新的Vue()浏览器

而后咱们调用这个方法,传递咱们的Vue实例定义对象,并将返回对象分配给一个变量app服务器

接下来,咱们将在app上调用mount方法,并传递一个CSS选择器来指示咱们的mount元素,就像咱们在Vue 2中使用$mount实例方法同样

import { createApp } from "vue";

const app = createApp({
 // root instance definition
});

app.mount("#app");

改变的缘由

在旧的API中,咱们添加的任何全局配置(插件、混合、原型属性等)都会永久地改变全局状态。例如:

// Affects both instances
Vue.mixin({ ... })

const app1 = new Vue({ el: '#app-1' })
const app2 = new Vue({ el: '#app-2' })

这在单元测试中确实是一个问题,由于它使确保每一个测试与上一个测试隔离变得很棘手。

在新的API下,调用createApp将返回一个新的app实例,该实例不会被应用于其余实例的任何全局配置所污染。

Learn more:Global API change RFC.

添加状态属性

咱们的模式窗口能够处于两种状态之一——打开或关闭。让咱们用一个布尔状态属性modalOpen来管理它,咱们会给它一个初始值false

const app = createApp({
 data: {
   modalOpen: false
 }
});

这是不容许的。相反,必须为数据分配一个返回状态对象的工厂函数。

这是您必须为Vue组件作的事情,可是如今它也对Vue应用程序实例强制执行

const app = createApp({
 data: () => ({
   modalOpen: false
 })
});

Reason for change

使用对象而不是工厂函数的优势是,首先,它在语法上更简单,其次,你能够在多个根实例之间共享顶层状态,例如:

const state = {
 sharedVal: 0
};

const app1 = new Vue({ state });
const app2 = new Vue({ state });

// Affects both instances
app1._data.sharedVal = 1;

这种用例不多,可使用。由于有两种类型的声明是不适合初学者的,因此决定删除这个特性。

Learn more:Data object declaration removed RFC

在继续以前,让咱们添加一个方法来切换modalOpen值。这与Vue 2没有什么不一样。

const app = createApp({
 data: () => ({
   modalOpen: true  
 }),
 methods: {
   toggleModalState() {
     this.modalOpen = !this.modalOpen;
   }
 }
});

使用根组件

若是您如今转到浏览器并检查控制台,您将看到警告“组件缺乏呈现函数”,由于咱们尚未为根实例定义模板。

Vue 2的最佳实践是为根实例建立一个最小的模板,并建立一个应用程序组件,其中将声明主应用程序标记。

咱们在这里也作一下。

touch src/App.vue

如今咱们能够得到根实例来呈现该组件。不一样之处在于,在Vue 2中,咱们一般会使用渲染函数来完成如下操做:

import App from "./App.vue";

const app = createApp({
 ...
 render: h => h(App)
});

app.mount("#app");

咱们仍然能够这样作,可是Vue 3有一个更简单的方法——让应用程序成为一个根组件。为此,咱们能够删除根实例定义并传递App组件。

import App from "./App.vue";

const app = createApp(App);

app.mount("#app");

这意味着App组件不只由根实例呈现,并且是根实例。

在此过程当中,让咱们经过删除app变量来简化一下语法:

createApp(App).mount("#app");

如今移动到根组件,让咱们从新添加状态和方法到这个组件:

<script>
export default {
 data: () => ({
   modalOpen: true  
 }),
 methods: {
   toggleModalState() {
     this.modalOpen = !this.modalOpen;
   }
 }
};
</script>

让咱们也为模态特性建立一个新组件:

touch src/Modal.vue

如今,咱们将提供一个包含内容插槽的最小模板。这确保了咱们的模式是可重用的。稍后咱们将向该组件添加更多内容。

<template>
 <div class="modal">
   <slot></slot>
 </div>
</template>

Multi-root模板

如今让咱们为根组件建立模板。咱们将建立一个按钮来打开模态,它将触发toggleModalState方法

咱们还将使用刚刚建立的模态组件,它将根据modalState的值呈现。咱们还能够在内容槽中插入一段文本。

<template>
 <button @click="toggleModalState">Open modal</button>
 <modal v-if="modalOpen">
   <p>Hello, I'm a modal window.</p>
 </modal>
</template>
<script>
import Modal from "./Modal.vue";
export default {
 components: {
   Modal
 },
 ...
}
</script>

注意到这个模板有什么奇怪的地方吗?看一遍。我将等待。

没错,有两个根元素。在Vue 3中,因为一个称为fragment的特性,它再也不强制拥有单个根元素!

使用复合API重构

Vue 3的旗舰特性是复合API。这个新的API容许您使用setup函数定义组件功能,而不是使用添加到组件定义对象的属性。

如今,让咱们重构应用程序组件,以使用复合API。

在我解释代码以前,要清楚咱们所作的一切都是重构——组件的功能是相同的。还要注意,模板没有改变,由于复合API只影响咱们定义组件功能的方式,而不是咱们呈现它的方式。

<template>
 <button @click="toggleModalState">Open modal</button>
 <modal v-if="modalOpen">
   <p>Hello, I'm a modal window.</p>
 </modal>
</template>
<script>
import Modal from "./Modal.vue";
import { ref } from "vue";
export default {
 setup () {
   const modalState = ref(false);
   const toggleModalState = () => {
     modalState.value = !modalState.value;
   };
   return {
     modalState,
     toggleModalState
   }
 }
};
</script>

setup method

首先,请注意咱们导入了ref函数,该函数容许咱们定义一个反应变量modalState。这个变量等价于This . modalstate。

toggleModalState方法只是一个普通的JavaScript函数。可是,请注意,要更改方法体中的modalState的值,咱们须要更改它的子属性值。这是由于使用ref建立的反应变量被包装在一个对象中。这对于保持它们在传递过程当中的活性是必要的。

若是您想详细了解refs的工做方式,最好查阅Vue Composition API文档。

最后,咱们从setup方法返回modalState和toggleModalState,由于它们是在模板呈现时传递给模板的值。

Reason for change

请记住,组合API不是一个更改,由于它彻底是可选的。主要动机是考虑更好的代码组织和组件之间的代码重用(由于mixin本质上是一种反模式)

若是您认为在本例中重构应用程序组件以使用复合API是没必要要的,那么您是正确的。可是,若是这是一个更大的组件,或者咱们须要与其余组件共享它的特性,那么您就会看到它的有用性。

提供更深刻的示例超出了本文的范围,因此若是您有兴趣了解更多关于新API的使用,请参阅个人另外一篇文章,了解什么时候使用新Vue复合API(以及什么时候不使用)。


传送内容

若是您之前建立过模态特性,您就会知道它一般被放置在关闭的标记以前。

<body>
 <div>
   <!--main page content here-->
 </div>
 <!--modal here-->
</body>

这样作是由于情态动词一般有一个页面覆盖的背景(若是你不明白个人意思,请参阅开头的图片)。要使用CSS实现这一点,您不须要处理父元素定位和z-index叠加上下文,所以最简单的解决方案是将模态放在DOM的最底部。

这就与Vue产生了问题。不过,它假设UI将被构建为一个组件树。为了容许树的片断移动到DOM中的其余位置,Vue 3中添加了一个新的传送组件

要使用传送,让咱们首先向页面添加一个元素,咱们但愿将模态内容移动到该页面。咱们将转到index.html,并在Vue的挂载元素旁边放置一个带ID modal-wrapper的div。

<body>
 ...
 <div id="app"></div><!--Vue mounting element-->
 <div id="modal-wrapper">
   <!--modal should get moved here-->
 </div>
</body>

如今,回到App.vue,咱们将把模态内容包装在传送组件中。咱们还须要指定一个to属性,它将被分配一个用于标识目标元素的查询选择器,在本例中是#modal-wrapper。

<template>
 <button @click="toggleModalState">Open modal</button>
 <teleport to="#modal-wrapper">
   <modal v-if="modalOpen">
     <p>Hello, I'm a modal window.</p>
   </modal>
 </teleport>
</template>

就是这样。传送中的任何内容都将在目标元素中呈现。然而,它仍然会像它在层级中的最初位置同样工做(关于道具,事件等)。

所以,在您保存代码以后,从新加载页面,在开发工具中检查DOM,您会感到惊讶!

Learn more:Teleport RFC

发出一个事件

如今让咱们在模态中添加一个按钮来关闭它。为此,咱们将向modal tempate添加一个按钮元素,并使用一个发出事件close的click处理程序。

<template>
 <div class="modal">
   <slot></slot>
   <button @click="$emit('close')">Dismiss</button>
 </div>
</template>

而后父组件将捕捉此事件,并切换modalState的值,使其在逻辑上为假,并致使窗口关闭。

<template>
 ...
   <modal
     v-if="modalOpen"
     @click="toggleModalState"
   >
     <p>Hello, I'm a modal window.</p>
   </modal>
 </teleport>
</template>

到目前为止,这个特性与Vue 2中的特性彻底相同。可是,在Vue 3中,如今建议您使用新的component选项显式地声明组件的事件。就像使用道具同样,您能够简单地建立一个字符串数组来命名组件将发出的每一个事件

<template>...</template>
<script>
export default {
 emits: [ "close" ]
}
</script>

Reason for change

想象一下,打开别人编写的组件文件,并查看显式声明的组件的道具和事件。立刻,您就会理解这个组件的接口,即它要发送和接收什么。

除了提供自我记录的代码以外,您还可使用事件声明来验证事件负载,尽管在本例中我找不到这样作的理由。

Learn more:Emits Option RFC

样式槽内容

为了使咱们的模式可重用,咱们为内容提供了一个插槽。让咱们经过向组件添加样式标签来开始对该内容进行样式化。

在咱们的组件中使用限定范围的CSS是一个很好的实践,以确保咱们提供的规则不会对页面中的其余内容产生意外的影响

让咱们把任何段落文本放到槽里都改为斜体。为此,咱们将使用p选择器建立一个新的CSS规则。

<template>...</template>
<script>...</script>
<style scoped>
 p {
   font-style: italic;
 }
</style>

若是你试一下,你会发现它不起做用。问题是,当槽内容仍然属于父内容时,在编译时肯定了做用域样式。

Vue 3提供的解决方案是提供一个伪选择器::v- sloated(),容许您使用提供插槽的组件中的做用域规则来锁定插槽内容。

Here's how we use it:

<style scoped>
 ::v-slotted(p) {
   font-style: italic;
 }
</style>

Vue 3 also includes some other new scoped styling selectors::v-deepand::v-globalwhich you can learn more about here:Scoped Styles RFC

Other changes

好了,这就是我能够在一个简单的例子中介绍的全部新特性。我获得了大部分主要的,但这里有一些我认为重要到足以在结束文章以前提到,你能够本身研究:

Added:

Removed:

  • Filters

  • Inline templates

  • Event interface for components (no more event bus!)

Changed:

  • Async component API

  • Custom directive API

  • Render function syntax

There are also various changes regarding Vue Router but I'll be dedicating a separate article to those!

相关文章
相关标签/搜索