异步组件
在大型应用中,我们可能需要将应用分割成小一些的代码块,并且只在需要的时候才从服务器加载一个模块。为了简化,Vue 允许你以一个工厂函数的方式定义你的组件,这个工厂函数会异步解析你的组件定义。Vue 只有在这个组件需要被渲染的时候才会触发该工厂函数,且会把结果缓存起来供未来重渲染。
2.0中 我们这么用??
1 2 3 4 5 6 7 8 9 | const asyncPage = () => import('./NextPage.vue') //return a promise const asyncPage = { component: () => import('./NextPage.vue'), delay: 200, timeout: 3000, error: ErrorComponent, loading: LoadingComponent } |
3.0用法(由于组件被定义为纯函数,我们需要引入defineAsyncComponent实现异步)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | import { defineAsyncComponent } from 'vue' import ErrorComponent from './components/ErrorComponent.vue' import LoadingComponent from './components/LoadingComponent.vue' // Async component without options const asyncPage = defineAsyncComponent(() => import('./NextPage.vue')) // Async component with options const asyncPageWithOptions = defineAsyncComponent({ loader: () => import('./NextPage.vue'), delay: 200, timeout: 3000, errorComponent: ErrorComponent, loadingComponent: LoadingComponent }) |
自定义指令
2.0中 我们这么用??
1 2 3 4 5 6 7 8 | // 注册一个全局自定义指令 `v-focus` Vue.directive('focus', { // 当被绑定的元素插入到 DOM 中时…… inserted: function (el) { // 聚焦元素 el.focus() } }) |
钩子函数:
3.0用法(添加了更加丰富的生命周期钩子函数)
1 2 3 4 5 6 7 8 | const MyDirective = { beforeMount(el, binding, vnode, prevVnode) {}, mounted() {}, beforeUpdate() {}, updated() {}, beforeUnmount() {}, // new unmounted() {} } |
1 2 3 4 5 6 7 | const app = Vue.createApp({}) app.directive('highlight', { beforeMount(el, binding, vnode) { el.style.background = binding.value } }) |
自定义元素互操作更改(破坏性改变)
2.0中 我们这么用??
1. ignoredElements
1 2 3 4 5 6 7 | Vue.config.ignoredElements = [ 'my-custom-web-component', 'another-web-component', // 用一个 `RegExp` 忽略所有“ion-”开头的元素 // 仅在 2.5+ 支持 /^ion-/ ] |
使 Vue 忽略在 Vue 之外的自定义元素 (e.g. 使用了 Web Components APIs)。否则,它会假设你忘记注册全局组件或者拼错了组件名称,从而抛出一个关于 Unknown custom element 的警告。
举例:
假如我使用了未定义的
报错:(这个检查在vue3.0中在编译时就会进行,而2.0是在运行时进行的)
使用场景:有时候我们会再引入其他第三方库的组件,但是vue会抛出错误,我们要避免这个错误,可以在Vue.config.ignoredElements配置里面配置下。
3.0用法
1 2 3 4 5 6 7 8 9 10 11 12 13 | // in webpack config rules: [ { test: /\\.vue$/, use: 'vue-loader', options: { compilerOptions: { isCustomElement: tag => tag === 'plastic-button' } } } // ... ] |
改变原因:自定义ignoredElements现在在模板编译期间执行,应通过编译器选项(而不是运行时配置)进行配置。
2.is属性的使用
2.0中 我们这么用??
1 | <button is="plastic">Click Me!</button> |
它被解释为使用name渲染Vue组件plastic,等同于:
1 | <plastic> Click Me!</plastic> |
或者
1 | <component v-bind:is="currentTabComponent" class="tab"></component> |
这里的用法是动态组件渲染
不了解is具体使用的看这里:用于动态组件且基于 DOM 内模板的限制来工作。
在3.0中,仅将Vue对 is prop 的特殊处理限制在标签上。
1 | <component v-bind:is="currentTabComponent"></component> |
也即是说在上面button标签例子中的is在vue3.0只会呈现一个普通prop的效果
3.is进行In-DOM模板解析的变通办法
这个不太常见,只在HTML中使用vue会出现这个问题,.vue文件没有这个情况
2.0中 我们这么用??
1 2 3 4 5 | <ul> <li></li> <li></li> <li></li> </ul> |
总所周知,ul里面嵌套li的写法是html语法的固定写法(还有如table,select等)
1 2 3 4 | <ul> <my-component></my-component> <my-component></my-component> </ul> |
my-component是我们自己写的组件,但是html在渲染dom的时候,my-component对ul来说并不是有效的dom,甚至会报错。
解决方法:
1 2 3 | <ul> <li is='my-component'></li> </ul> |
在3.0中
1 2 3 | <ul> <li v-is='"my-component"'></li> </ul> |
用v-is取代了is,在写法上和上面一种is的功能也区分开了
data定义(破坏性改变)
2.0中 我们这么用??
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <!-- Object Declaration --> <script> const app = new Vue({ data: { apiKey: 'a1b2c3' } }) </script> <!-- Function Declaration --> <script> const app = new Vue({ data() { return { apiKey: 'a1b2c3' } } }) </script> |
Data支持两种格式(Function/Object)的定义
vue3.0中只支持Function这一种定义方法
Fragment
2.0中 我们这么用??
1 2 3 4 5 6 7 8 | <!-- Layout.vue --> <template> <div> <header>...</header> <main>...</main> <footer>...</footer> </div> </template> |
如果不加这个
Component template should contain exactly one root element. If you are using v-if on multiple elements, use v-else-if to chain them instead
3.0中支持不止一个根标签:
1 2 3 4 5 6 | <!-- Layout.vue --> <template> <header>...</header> <main v-bind="$attrs">...</main> <footer>...</footer> </template> |
渲染API
2.0中 我们这么用??
1 2 3 4 5 | export default { render(h) { return h('div') } } |
3.0中h 函数现在已全局导入,而不是传递给渲染函数作为参数
1 2 3 4 5 6 | import { h } from 'vue' export default { render() { return h('div') } } |
v-model
2.0中 我们这么用??
1 2 3 4 5 | <ChildComponent v-model="pageTitle" /> <!-- 上下等效 --> <ChildComponent :value="pageTitle" @input="pageTitle = $event" /> |
3.0中变化为这样:
1 2 3 4 5 | <ChildComponent v-model="pageTitle" /> <!-- 上下等效 --> <MyBook :modelValue="pageTitle" @update:modelValue="pageTitle = $event" /> |
1.一个组件支持多个v-model
1 2 3 4 5 6 7 8 9 | <ChildComponent v-model:title="pageTitle" v-model:content="pageContent" /> <ChildComponent :title="pageTitle" @update:title="pageTitle = $event" :content="pageContent" @update:content="pageContent = $event" /> |
2.v-model自定义修饰符
类似于2.0的.tirm修饰符
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | <div id="app"> <my-component v-model.capitalize="myText"></my-component> {{ myText }} </div> const app = Vue.createApp({ data() { return { myText: '' } } }) app.component('my-component', { props: { modelValue: String, modelModifiers: { default: () => ({}) } }, methods: { emitValue(e) { let value = e.target.value if (this.modelModifiers.capitalize) { value = value.charAt(0).toUpperCase() + value.slice(1) } this.$emit('update:modelValue', value) } }, template: `<input type="text" v-bind:value="modelValue" v-on:input="emitValue">` }) app.mount('#app') |
Vue实例
2.0中 我们这么用??
1 2 3 4 5 6 7 8 9 10 11 | Vue.component('button-counter', { data: () => ({ count: 0 }), template: '<button @click="count++">Clicked {{ count }} times.</button>' }) Similarly, this is how a global directive is declared: Vue.directive('focus', { inserted: el => el.focus() }) |
从技术上讲,Vue 2没有app的概念。我们定义为app的只是通过创建的根Vue实例new Vue()从同一个Vue构造函数创建的每个根实例都共享相同的全局配置。用起来很方便,但结果是不可避免的造成了全局污染,并且全局配置使测试过程中意外污染其他测试案例变得容易。
vue3.0:
调用createApp返回一个新的vue实例,这是Vue 3中的一个新概念,每个实例拥有了自己的配置项。
1 2 3 | import { createApp } from 'vue' const app = createApp() |
1.挂载app实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | import { createApp } from 'vue' import MyApp from './MyApp.vue' const app = createApp(MyApp) app.mount('#app') const app = createApp(MyApp) app.component('button-counter', { data: () => ({ count: 0 }), template: '<button @click="count++">Clicked {{ count }} times.</button>' }) app.directive('focus', { mounted: el => el.focus() }) app.mount('#app') |
2.在app之间共享配置
1 2 3 4 5 6 7 8 9 10 11 12 13 | import { createApp } from 'vue' import Foo from './Foo.vue' import Bar from './Bar.vue' const createMyApp = (VueInstance) => { const app = createApp(VueInstance) app.directive('focus' /* ... */) return app } createMyApp(Foo).mount('#foo') createMyApp(Bar).mount('#bar') |
现在,该focus指令将在Foo和Bar实例及其后代中可用。
相关参考文档:https://v3.vuejs.org/