Vue 3还没有正式发布,可是维护者已经发布了Beta版本,供咱们的参与者尝试并提供反馈。javascript
若是你想知道Vue 3的主要功能和主要变化是什么,那么我将在这篇文章中重点介绍一下,告诉你使用Vue 3 beta 9建立一个简单的应用程序。html
我将介绍尽量多的新内容,包括fragments,teleport,Composition API以及其余一些晦涩的更改。我将尽力解释该功能或更改的原理。前端
咱们将构建一个带有模式窗口功能的简单应用。我之因此选择它,是由于它能够方便地展现Vue 3的许多变化。vue
这是该应用在打开和关闭状态下的外观,所以你能够在脑海中描绘出咱们正在作什么:java
与其直接安装Vue 3,不如克隆一个项目 vue-next-webpack-preview
,这将为咱们提供一个包括Vue 3在内的最小的Webpack设置。webpack
$ git clone https://github.com/vuejs/vue-next-webpack-preview.git vue3-experiment $ cd vue3-experiment $ npm i
一旦克隆好了,安装好了NPM模块,咱们须要作的就是删除样板文件,而后建立一个新的 main.js
文件,这样咱们就能够从头开始建立咱们的Vue 3 app了。git
$ rm -rf src/* $ touch src/main.js
如今,咱们将运行开发服务器:github
$ npm run dev
咱们启动一个新的Vue应用程序的方式改变了,咱们如今须要导入新的 createApp
方法,而不是使用新的 Vue()
。web
咱们调用这个方法,传递咱们的Vue实例定义对象,并将返回对象分配给一个变量 app
。npm
接下来,咱们将在 app
上调用 mount
方法,并传递一个CSS选择器来指示咱们的mount元素,就像在Vue 2中使用 $mount
实例方法同样。
// src/main.js import { createApp } from "vue"; const app = createApp({ // 根实例定义 }); app.mount("#app");
与旧的API同样,咱们添加的任何全局配置(plugins,mixins,原型属性等)都将永久更改全局状态。例如:
// src/main.js // 影响两个实例 Vue.mixin({ ... }) const app1 = new Vue({ el: '#app-1' }) const app2 = new Vue({ el: '#app-2' })
在单元测试中,这确实是一个问题,由于要确保将每一个测试都与上一个测试隔离是很棘手的。
在新的API下,调用 createApp
将返回一个新的app实例,该实例不会被应用于其余实例的任何全局配置污染。
了解更多: Global API change RFC。
咱们的模态窗口能够处于两种状态之一——打开或关闭。让咱们用一个布尔状态属性 modalOpen
来管理它,咱们将给它一个初始值 false
。
在Vue 2下,咱们能够经过在咱们的应用实例上建立一个 data
属性并将一个对象分配给该对象来声明 modalOpen
属性,例如:
// src/main.js const app = createApp({ data: { modalOpen: false } });
再也不容许这样作。相反,必须为数据分配一个返回状态对象的工厂函数。
// src/main.js const app = createApp({ data: () => ({ modalOpen: false }) });
使用对象而不是工厂函数来存储数据的优势是,首先,它在语法上更简单;其次,你能够在多个根实例之间共享顶级状态,例如:
// src/main.js const state = { sharedVal: 0 }; const app1 = new Vue({ state }); const app2 = new Vue({ state }); // 影响两个实例 app1._data.sharedVal = 1;
这种用例不多,可使用。由于有两种类型的声明是不适合初学者的,因此决定删除这个特性。
了解更多: Data object declaration removed RFC
在继续以前,咱们还添加一个方法来切换 modalOpen
值。这与Vue 2没什么不一样。
// src/main.js const app = createApp({ data: () => ({ modalOpen: true }), methods: { toggleModalState() { this.modalOpen = !this.modalOpen; } } });
若是你如今进入浏览器并检查控制台,则会看到警告“Component is missing render function”,由于咱们还没有为根实例定义模板。
Vue 2的最佳实践是为根实例建立一个最小的模板,并建立一个app组件,其中将声明主app标记。
让咱们在这里也这样作。
$ touch src/App.vue
如今咱们能够获取根实例来渲染该组件。区别在于,对于Vue 2,咱们一般会使用render函数来执行此操做:
// src/main.js import App from "./App.vue"; const app = createApp({ ... render: h => h(App) }); app.mount("#app");
咱们仍然能够作到这一点,可是Vue 3有一个更简单的方法——使 App
成为根组件。为此,咱们能够删除根实例定义,而是传递 App
组件。
// src/main.js import App from "./App.vue"; const app = createApp(App); app.mount("#app");
这意味着 App
组件不只由根实例渲染,并且是根实例。
在此过程当中,咱们经过删除 app
变量来简化语法:
// src/main.js createApp(App).mount("#app");
如今移至根组件,让咱们向该组件从新添加状态和方法:
// src/App.vue <script> export default { data: () => ({ modalOpen: true }), methods: { toggleModalState() { this.modalOpen = !this.modalOpen; } } }; </script>
咱们还为模态功能建立一个新组件:
$ touch src/Modal.vue
如今,咱们将提供一个最小的模板,其中包括内容插槽。这确保了咱们的模态是可重用的。稍后咱们将向此组件添加更多内容。
// src/Modal.vue <template> <div class="modal"> <slot></slot> </div> </template>
如今让咱们为咱们的根组件建立模板。咱们将建立一个按钮来打开模态,它将触发 toggleModalState
方法。
咱们还将使用咱们刚刚建立的modal组件,它将根据 modalState
的值来渲染。让咱们也在槽中插入一段文字做为内容。
// src/App.vue <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中,因为有了一个叫作片断(fragments)的功能,它再也不强制要求有一个单一的根元素!
Vue 3的旗舰功能是Composition API。这个新的API容许你使用 setup
功能而不是使用添加到组件定义对象的属性来定义组件功能。
如今,让咱们重构App组件以使用Composition API。
在解释代码以前,请清楚咱们所作的只是重构——组件的功能将相同。还要注意,模板没有更改,由于Composition API仅影响咱们定义组件功能的方式,而不影响咱们渲染它的方式。
src/App.vue
<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>
首先,请注意,咱们导入了 ref
函数,该函数容许咱们定义响应式变量 modalState
。此变量等效于this.modalState
。
toggleModalState
方法只是一个普通的JavaScript函数。可是,请注意,要更改方法主体中的 modalState
值,咱们须要更改其子属性 value
。 这是由于使用 ref
建立的响应式变量被封装在一个对象中。这对于保留它们的响应式是很是必要的,由于它们在被传递的过程当中会被保留下来。
最后,咱们从 setup
方法返回 modalState
和 toggleModalState
,由于这些是在呈现模板时传递给模板的值。
请记住,Composition API并非更改,由于它纯粹是可选的。主要动机是容许更好的代码组织和组件之间的代码重用(由于mixin本质上是一种反模式)。
若是你认为在这个例子中重构App组件以使用Composition API是没有必要的,那你的想法是正确的。可是,若是这是一个更大的组件,或者咱们须要与其余组件共享其功能,那么你就会发现它的用处。
若是你曾经建立过模态功能,你会知道它一般被放置在关闭的 </body>
标签以前。
<body> <div> <!--main page content here--> </div> <!--modal here--> </body>
这样作是由于模式一般具备覆盖页面的背景,要使用CSS来实现,您不须要处理父元素定位和z-index堆栈上下文,所以最简单的解决方案是将模式放在DOM的最底部。
但这在Vue.js中产生了一个问题,它假定UI将做为一个单一的组件树来构建。为了容许将树的片断移动到DOM中的其余位置,在Vue 3中添加了一个新的 teleport
组件。
要使用teleport,首先要在页面上添加一个元素,咱们要将模态内容移动到该页面。咱们将转到 index.html
,并将ID为 modal-wrapper
的 div
放在Vue的安装元素旁边。
index.html
<body> ... <div id="app"></div><!--Vue mounting element--> <div id="modal-wrapper"> <!--modal should get moved here--> </div> </body>
如今,回到 App.vue
,咱们将模态内容包装在 teleport
组件中。咱们还须要指定一个 to
属性,为该属性分配一个查询选择器,以标识目标元素,在本例中为 #modal-wrapper
。
src/App.vue
<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>
就是这样,teleport
中的任何内容都将渲染在目标元素中。
如今,让咱们在modal中添加一个按钮,让它能够被关闭。要作到这一点,咱们要在modal 模板中添加一个按钮元素,并添加一个点击处理程序,该处理程序会发出一个 close
事件。
src/Modal.vue
<template> <div class="modal"> <slot></slot> <button @click="$emit('close')">Dismiss</button> </div> </template>
而后,该事件将由父组件捕获,并将切换 modalState
的值,从逻辑上将其设置为 false
并致使窗口关闭。
src/App.vue
<template> ... <modal v-if="modalOpen" @click="toggleModalState" > <p>Hello, I'm a modal window.</p> </modal> </teleport> </template>
到目前为止,此功能与Vue 2中的功能相同。可是,如今在Vue 3中,建议您使用新的 emits
组件选项显式声明组件的事件。就像props同样,你能够简单地建立一个字符串数组来命名组件将发出的每一个事件。
src/Modal.vue
<template>...</template> <script> export default { emits: [ "close" ] } </script>
想象一下,打开别人写的组件的文件,看到它的prop和event明文声明。立刻,你就会明白这个组件的界面,也就是它要发送和接收什么。
除了提供自说明代码外,你还可使用事件声明来验证你的事件有效载荷,虽然我在这个例子中找不到理由来验证。
了解更多: Emits Option RFC
为了使模态可重用,咱们提供了一个内容插槽。让咱们开始经过为组件添加 style
标签来为内容设置样式。
在咱们的组件中使用 scoped
CSS是一种很好的作法,以确保咱们提供的规则不会对页面中的其余内容产生意外影响。
让咱们把任何被放入插槽中的段落文字变成斜体。要作到这一点,咱们将使用 p
选择器建立一个新的CSS规则。
src/Modal.vue
<template>...</template> <script>...</script> <style scoped> p { font-style: italic; } </style>
若是你尝试一下,你会发现这一点并不奏效。问题是,在编译时,当插槽内容仍属于父对象时,Scoped styling是在编译时肯定的。
Vue 3提供的解决方案是提供一个伪选择器 ::v-slotted()
,容许你在提供插槽的组件中使用范围化规则来针对插槽内容。
这是咱们的用法:
<style scoped> ::v-slotted(p) { font-style: italic; } </style>
Vue 3还包含了其余一些新的Scoped Styling选择器:::v-deep
和 ::v-global
,你能够在这里了解更多:Scoped Styles RFC。
好吧,这就是我能够在一个简单示例中涵盖的全部新功能。主要的我基本都有了,但这里有一些我认为很重要的,在总结文章以前,我以为足够重要,能够本身研究一下。
添加的:
移出的:
更改的:
关于Vue Router也有各类变化,但我将专门用一篇文章来介绍这些变化!
本文首发于公众号《前端外文精选》,关注后私信回复:大礼包,送某网精品视频课程网盘资料,准能为你节省很多钱!