vuejs3 第4页
#概览 对内联特性的支持已被移除。 #2.x 语法 在 2.x 中,Vue 为子组件提供了 inline-template attribute,以便将其内部内容用作模板,而不是将其作为分发内容。 <my-component inline-template> <div> <p>它们被编译为组件自己的模板</p> <p>不是父级所包含的内容。</p> </div> </my-component> #3.x 语法 将不再支持此功能。 #迁移策略 inline-template 的大多数用例都假设没有构建工具设置,所有模板都直接写在 HTML 页面中 #选项 #1:使用 <script> 标签 在这种情况下,最简单的解决方法是将 <script> 与其他类型一起使用: <script type="text/html" id="my-comp-template"> <div>{{ hello }}</div> </script> 在组件中,使用选择器将模板作为目标: const MyComp = { template: '#my-comp-template' // ... } 这不需要任何构建设置,可以在所有浏览器中工作,不受任何 DOM HTML 解析警告的约束 (例如,你可以使用 camelCase prop 名称),并且在大多数 ide 中提供了正确的语法高亮显示。在传统的服务器端框架中,可以将这些模板拆分为服务器模板部分 (包括在主 HTML 模板中),以获得更好的可维护性。 #选项 #2:默认 Slot 以前使用 inline-template 的组件也可以使用默认 slot——进行重构,这使得数据范围更加明确,同时保留了内联编写子内容的便利性: <!-- 2.x 语法 --> <my-comp inline-template :msg="parentMsg"> {{ msg }} {{ childState }} </my-comp> <!-- 默认 Slot 版本 --> <my-comp v-slot="{ childState }"> {{ parentMsg }} {{ childState }} </my-comp> 子级现在应该渲染默认 slot*,而不是不提供模板: <!-- 在子模板中,在传递时渲染默认slot 在必要的private状态下。 --> <template> <slot :childState="childState" /> </template> 提示:在 3.x,slot 可以渲染为具有原生 fragments 支持的根目录!
#2.x 语法 如果你曾经在 Vue 中手动操作过 DOM,你可能会遇到以下模式: import Vue from 'vue' Vue.nextTick(() => { // 一些和DOM有关的东西 }) 或者,如果你一直在对涉及 async components 的应用程序进行单元测试,那么很可能你编写了以下内容: import { shallowMount } from '@vue/test-utils' import { MyComponent } from './MyComponent.vue' test('an async feature', async () => { const wrapper = shallowMount(MyComponent) // 执行一些DOM相关的任务 await wrapper.vm.$nextTick() // 运行你的断言 }) Vue.nextTick() 是一个全局的 API 直接暴露在单个 Vue 对象上——事实上,实例方法 $nextTick() 只是一个方便的包装 Vue.nextTick() 为方便起见,回调的 this 上下文自动绑定到当前实例。 模块捆绑程序,如 webpack 支持 tree-shaking,这是“死代码消除”的一个花哨术语。不幸的是,由于代码是如何在以前的 Vue 版本中编写的,全局 API Vue.nextTick() 不可摇动,将包含在最终捆绑中不管它们实际在哪里使用。 #3.x 语法 在 Vue 3 中,全局和内部 API 都经过了重构,并考虑到了 tree-shaking 的支持。因此,全局 API 现在只能作为 ES 模块构建的命名导出进行访问。例如,我们之前的片段现在应该如下所示: import { nextTick } from 'vue' nextTick(() => { // 一些和DOM有关的东西 }) and import { shallowMount } from '@vue/test-utils' import { MyComponent } from './MyComponent.vue' import { nextTick } from 'vue' test('an async feature', async...
Vue 2.x 有许多全局 API 和配置,这些 API 和配置可以全局改变 Vue 的行为。例如,要创建全局组件,可以使用 Vue.component 这样的 API: Vue.component('button-counter', { data: () => ({ count: 0 }), template: '<button @click="count++">Clicked {{ count }} times.</button>' }) 类似地,使用全局指令的声明方式如下: Vue.directive('focus', { inserted: el => el.focus() }) 虽然这种声明方式很方便,但它也会导致一些问题。从技术上讲,Vue 2 没有“app”的概念,我们定义的应用只是通过 new Vue() 创建的根 Vue 实例。从同一个 Vue 构造函数创建的每个根实例共享相同的全局配置,因此: 在测试期间,全局配置很容易意外地污染其他测试用例。用户需要仔细存储原始全局配置,并在每次测试后恢复 (例如重置 Vue.config.errorHandler)。有些 API 像 Vue.use 以及 Vue.mixin 甚至连恢复效果的方法都没有,这使得涉及插件的测试特别棘手。实际上,vue-test-utils 必须实现一个特殊的 API createLocalVue 来处理此问题: import { createLocalVue, mount } from '@vue/test-utils' // 建扩展的 `Vue` 构造函数 const localVue = createLocalVue() // 在 “local” Vue构造函数上 “全局” 安装插件 localVue.use(MyPlugin) // 通过 `localVue` 来挂载选项 mount(Component, { localVue }) 全局配置使得在同一页面上的多个“app”之间共享同一个 Vue 副本非常困难,但全局配置不同。 // 这会影响两个根实例 Vue.mixin({ /* ... */ }) const app1 = new Vue({ el: '#app-1' }) const app2 = new Vue({ el: '#app-2' }) 为了避免这些问题,在 Vue 3 中我们引入…...
#概览 就变化而言,属于高等级内容: 在 3.x 中,函数式组件 2.x 的性能提升可以忽略不计,因此我们建议只使用有状态的组件 函数式组件只能使用接收 props 和 context 的普通函数创建 (即:slots,attrs,emit)。 非兼容变更:functional attribute 在单文件组件 (SFC) <template> 已被移除 非兼容变更:{ functional: true } 选项在通过函数创建组件已被移除 更多信息,请继续阅读! #介绍 在 Vue 2 中,函数式组件有两个主要用例: 作为性能优化,因为它们的初始化速度比有状态组件快得多 返回多个根节点 然而,在 Vue 3 中,有状态组件的性能已经提高到可以忽略不计的程度。此外,有状态组件现在还包括返回多个根节点的能力。 因此,函数式组件剩下的唯一用例就是简单组件,比如创建动态标题的组件。否则,建议你像平常一样使用有状态组件。 #2.x 语法 使用 <dynamic-heading> 组件,负责提供适当的标题 (即:h1,h2,h3,等等),在 2.x 中,这可能是作为单个文件组件编写的: // Vue 2 函数式组件示例 export default { functional: true, props: ['level'], render(h, { props, data, children }) { return h(`h${props.level}`, data, children) } } 或者,对于喜欢在单个文件组件中使用 <template> 的用户: // Vue 2 函数式组件示例使用 <template> <template functional> <component :is="`h${props.level}`" v-bind="attrs" v-on="listeners" /> </template> <script> export default { props: ['level'] } </script> #3.x 语法 #通过函数创建组件 现在在 Vue 3 中,所有的函数式组件都是用普通函数创建的,换句话说,不需要定义 { functional: true } 组件选项。 他们将接收两个参数:props 和 context。context 参数是一个对象,包含组件的 attrs,slots,和 emit property。 此外,现在不是在 render 函数中隐式提供 h,而是全局导入 h。 使用前面提到的 <dynamic-heading> 组件的示例,下面是它现在的样子。...
#概览 在 Vue 3 中,组件现在正式支持多根节点组件,即片段! #2.x 语法 在 2.x 中,不支持多根组件,当用户意外创建多根组件时会发出警告,因此,为了修复此错误,许多组件被包装在一个 <div> 中。 <!-- Layout.vue --> <template> <div> <header>...</header> <main>...</main> <footer>...</footer> </div> </template> #3.x 语法 在 3.x 中,组件现在可以有多个根节点!但是,这确实要求开发者明确定义属性应该分布在哪里。 <!-- Layout.vue --> <template> <header>...</header> <main v-bind="$attrs">...</main> <footer>...</footer> </template> 有关 attribute 继承如何工作的详细信息,见非 Prop Attributes。
#概览 从 Vue 3.0 开始,过滤器已删除,不再支持。 #2.x 语法 在 2.x,开发者可以使用过滤器来处理通用文本格式。 例如: <template> <h1>Bank Account Balance</h1> <p>{{ accountBalance | currencyUSD }}</p> </template> <script> export default { props: { accountBalance: { type: Number, required: true } }, filters: { currencyUSD(value) { return '$' + value } } } </script> 虽然这看起来很方便,但它需要一个自定义语法,打破大括号内表达式是“只是 JavaScript”的假设,这不仅有学习成本,而且有实现成本。 #3.x 更新 在 3.x 中,过滤器已删除,不再支持。相反地,我们建议用方法调用或计算属性替换它们。 使用上面的例子,这里是一个如何实现它的例子。 <template> <h1>Bank Account Balance</h1> <p>{{ accountInUSD }}</p> </template> <script> export default { props: { accountBalance: { type: Number, required: true } }, computed: { accountInUSD() { return '$' + this.accountBalance } } } </script> #迁移策略 我们建议用计算属性或方法代替过滤器,而不是使用过滤器。 #全局过滤器 如果在应用中全局注册了过滤器,那么在每个组件中用计算属性或方法调用来替换它可能就没那么方便了。 相反地,你可以通过全局属性在所有组件中使用它: // main.js const app = createApp(App) app.config.globalProperties.$filters = { currencyUSD(value) { return '$' + value } } 然后,你可以通过 $filters 对象修改所有的模板,像下面这样: <template> <h1>Bank Account Balance</h1> <p>{{ $filters.currencyUSD(accountBalance) }}</p>...
#概览 非兼容:data 组件选项声明不再接收纯 JavaScript object,而需要 function 声明。 当合并来自 mixin 或 extend 的多个 data 返回值时,现在是浅层次合并的而不是深层次合并的(只合并根级属性)。 #2.x Syntax 在 2.x 中,开发者可以定义 data 选项是 object 或者是 function。 例如: <!-- Object 声明 --> <script> const app = new Vue({ data: { apiKey: 'a1b2c3' } }) </script> <!-- Function 声明 --> <script> const app = new Vue({ data() { return { apiKey: 'a1b2c3' } } }) </script> 虽然这对于具有共享状态的根实例提供了一些便利,但是由于只有在根实例上才有可能,这导致了混乱。 #3.x Update 在 3.x,data 选项已标准化为只接受返回 object 的 function。 使用上面的示例,代码只有一个可能的实现: <script> import { createApp } from 'vue' createApp({ data() { return { apiKey: 'a1b2c3' } } }).mount('#app') </script> #Mixin 合并行为变更 此外,当来自组件的 data() 及其 mixin 或 extends 基类被合并时,现在将浅层次执行合并: const Mixin = { data() { return { user: { name: 'Jack', id: 1 } } } } const CompA =...
信息 这是一个低级的内部 API 更改,不会影响大多数开发人员。 #概览 下面是对这些变化的高层次总结: 删除枚举 attribute 的内部概念,并将这些 attribute 视为普通的非布尔 attribute 重大改变:如果值为布尔值,则不再删除 attribute false。相反,它被设置为 attr=“false”。移除 attribute,使用 null 或者 undefined。 如需更深入的解释,请继续阅读! #2.x 语法 在 2.x,我们有以下策略来强制 v-bind 的值: 对于某些 attribute/元素对,Vue 始终使用相应的 IDL attribute(property):比如 value 的 ,,“,等等。 对于“布尔 attribute”和 xlinks,如果它们是 falsy 的,Vue 会移除它们 (undefined,null or false) 另外加上它们 (见这里和这里)。 对于“枚举 attribute” (目前 contenteditable,draggable 和 spellcheck),Vue 会尝试强制将它们串起来 (目前对 contenteditable 做了特殊处理,修复 vuejs/vue#9397)。 对于其他 attribute,我们移除了 falsy 值 (undefined,null,or false) 并按原样设置其他值 (见这里)。 下表描述了 Vue 如何使用普通非布尔 attribute 强制“枚举 attribute”: 绑定表达式 foo 正常 draggable 枚举 :attr="null" / draggable="false" :attr="undefined" / / :attr="true" foo="true" draggable="true" :attr="false" / draggable="false" :attr="0" foo="0" draggable="true" attr="" foo="" draggable="true" attr="foo" foo="foo" draggable="true" attr foo="" draggable="true" 从上表可以看出,当前实现 true 强制为 'true' 但如果 attribute 为 false,则移除该 attribute。这也导致了不一致性,并要求用户在非常常见的用例中手动强制布尔值为字符串,例如 aria-* attribute 像 aria-selected,aria-hidden,等等。 #3.x 语法 我们打算放弃“枚举 attribute”的内部概念,并将它们视为普通的非布尔 HTML attribute。 这解决了普通非布尔...
#概览 以下是对变化的高层次概述: 新的 defineAsyncComponent 助手方法,用于显式地定义异步组件 component 选项重命名为 loader Loader 函数本身不再接收 resolve 和 reject 参数,且必须返回一个 Promise 如需更深入的解释,请继续阅读! #介绍 以前,异步组件是通过将组件定义为返回 Promise 的函数来创建的,例如: const asyncPage = () => import('./NextPage.vue') 或者,对于带有选项的更高阶的组件语法: const asyncPage = { component: () => import('./NextPage.vue'), delay: 200, timeout: 3000, error: ErrorComponent, loading: LoadingComponent } #3.x 语法 现在,在 Vue 3 中,由于函数式组件被定义为纯函数,因此异步组件的定义需要通过将其包装在新的 defineAsyncComponent 助手方法中来显式地定义: import { defineAsyncComponent } from 'vue' import ErrorComponent from './components/ErrorComponent.vue' import LoadingComponent from './components/LoadingComponent.vue' // 不带选项的异步组件 const asyncPage = defineAsyncComponent(() => import('./NextPage.vue')) // 带选项的异步组件 const asyncPageWithOptions = defineAsyncComponent({ loader: () => import('./NextPage.vue'), delay: 200, timeout: 3000, errorComponent: ErrorComponent, loadingComponent: LoadingComponent }) 对 2.x 所做的另一个更改是,component 选项现在被重命名为 loader,以便准确地传达不能直接提供组件定义的信息。 import { defineAsyncComponent } from 'vue' const asyncPageWithOptions = defineAsyncComponent({ loader: () => import('./NextPage.vue'), delay: 200, timeout: 3000, error: ErrorComponent, loading: LoadingComponent...
在 Vue 2 中,在 v-for 里使用的 ref attribute 会用 ref 数组填充相应的 $refs property。当存在嵌套的 v-for 时,这种行为会变得不明确且效率低下。 在 Vue 3 中,这样的用法将不再在 $ref 中自动创建数组。要从单个绑定获取多个 ref,请将 ref 绑定到一个更灵活的函数上 (这是一个新特性): <div v-for="item in list" :ref="setItemRef"></div> 结合选项式 API: export default { data() { return { itemRefs: [] } }, methods: { setItemRef(el) { this.itemRefs.push(el) } }, beforeUpdate() { this.itemRefs = [] }, updated() { console.log(this.itemRefs) } } 结合组合式 API: import { ref, onBeforeUpdate, onUpdated } from 'vue' export default { setup() { let itemRefs = [] const setItemRef = el => { itemRefs.push(el) } onBeforeUpdate(() => { itemRefs = [] }) onUpdated(() => { console.log(itemRefs) }) return { itemRefs, setItemRef } } } 注意: itemRefs 不必是数组:它也可以是一个对象,其 ref 会通过迭代的 key 被设置。 如果需要,itemRef 也可以是响应式的且可以被监听。