API 和数据响应式的变化
去掉了 Vue 构造函数
-
在
vue2
中,如果遇到一个页面有多个vue
应用时,往往会遇到一些问题:<!-- vue2 --> <div id="app1"></div> <div id="app2"></div> <script> Vue.use(...); // 全局挂载插件,会影响所有的 vue 应用 Vue.mixin(...); // 全局混入,会影响所有的 vue 应用 Vue.component(...); // 全局组件,会影响所有的 vue 应用 new Vue({ // 配置 }).$mount("#app1"); new Vue({ // 配置 }).$mount("#app2"); </script>
-
在
vue3
中,去掉了Vue
构造函数,转而使用createApp
创建vue
应用实例;更多 vue 应用的 api:https://v3.vuejs.org/api/application-api.html
<!-- vue3 --> <div id="app1"></div> <div id="app2"></div> <script> createApp(根组件).use(...).mixin(...).component(...).mount("#app1"); // 给 vue 实例上 挂载插件、混入、声明全局组件,实例之间互不影响 createApp(根组件).mount("#app2"); </script>
组件实例中的 API
对比数据响应式
vue2 和 vue3 均在相同的生命周期完成数据响应式,但做法不一样
-
vue2 数据响应式原理
-
vue3 数据响应式原理
模板中的变化
v-model
vue2
比较让人诟病的一点就是提供了两种双向绑定:v-model
和.sync
,在vue3
中去掉了.sync
修饰符,只需要使用v-model
进行双向绑定即可;为了让
v-model
更好的针对多个属性进行双向绑定,vue3
作出了以下修改;
-
当对自定义组件使用
v-model
指令时,绑定的属性名由原来的value
变为modelValue
,事件名由原来的input
变为update:modelValue
;<!-- vue2 --> <ChildComponent :value="pageTitle" @input="pageTitle = $event" /> <!-- 简写为 --> <ChildComponent v-model="pageTitle" /> <!-- vue3 --> <ChildComponent :modelValue="pageTitle" @update:modelValue="pageTitle = $event" /> <!-- 简写为 --> <ChildComponent v-model="pageTitle" />
-
去掉了
.sync
修饰符,它原本的功能由v-model
的 参数 替代;<!-- vue2 --> <ChildComponent :title="pageTitle" @update:title="pageTitle = $event" /> <!-- 简写为 --> <ChildComponent :title.sync="pageTitle" /> <!-- vue3 --> <ChildComponent :title="pageTitle" @update:title="pageTitle = $event" /> <!-- 简写为 --> <ChildComponent v-model:title="pageTitle" />
-
允许自定义
v-model
修饰符(新增);
-
HTMLHTML
<!-- app.vue --> <template> <div class="container"> <div class="list"> <strong>编辑:</strong> <div class="list"> <CheckEditor v-for="item in products" :key="item.id" v-model="item.sell" v-model:title="item.title"/> </div> </div> <div class="list"> <strong>销售中:</strong> <div> <template v-for="(item, index) in sells" :key="item.id"> <span>{{ index + 1 }}.</span> <strong>{{ item.title }}</strong> </template> </div> </div> </div> </template> <script> import CheckEditor from "./components/CheckEditor.vue"; import { ref, computed } from "vue"; const defaultSells = [ { id: 1, sell: true, title: "iphone12", }, { id: 2, sell: false, title: "xiaomi" }, { id: 3, sell: true, title: "huawei" }, { id: 4, sell: true, title: "vivo" }, ]; export default { components: { CheckEditor, }, setup() { const productsRef = ref(defaultSells); const sellsRef = computed(() => productsRef.value.filter((it) => it.sell)); const isAccountRef = ref(false); return { products: productsRef, sells: sellsRef, isAccount: isAccountRef, }; }, }; </script> <style scoped> </style>
<!-- CheckEditor.vue --> <template> <div class="check-editor"> <div class="check-editor-inner"> <div class="checkbox" :class="{ checked: modelValue }" @click="handleChecked"></div> <input type="text" class="editor" :value="title" @input="handleTextChange"/> </div> </div> </template> <script> export default { props: { modelValue: Boolean, title: String, titleModifiers: { default: () => ({}), }, }, setup(props, ctx) { const handleChecked = () => { ctx.emit("update:modelValue", !props.modelValue); }; const handleTextChange = (e) => { let value = e.target.value; if (props.titleModifiers.trim) { value = value.trim(); } ctx.emit("update:title", value); }; return { handleChecked, handleTextChange, }; }, }; </script> <style scoped> </style>
v-if v-for
vue2 中
v-if
的优先级低于v-for
;vue3 中
v-if
的优先级高于v-for
;
key
当使用
<template>
进行v-for
循环时,需要把key
值放到<template>
中,而不是它的子元素中;当使用
v-if v-else-if v-else
分支的时候,不再需要指定key
值,因为vue3
会自动给予每个分支一个唯一的key
;即便要手工给予key
值,也必须给予每个分支唯一的key
,不能因为要重用分支而给予相同的 key;
Fragment
vue2
只允许存在一个根节点;
vue3
允许组件出现多个根节点;
组件的变化
Teleport、Fragment
asyncComponent
-
asyncComponent 的两种使用方式;
JavaScriptJavaScriptimport { defineAsyncComponent } from 'vue' // 第一种:不带选项的异步组件 const asyncModal = defineAsyncComponent(() => import('./Modal.vue'))
import { defineAsyncComponent } from 'vue' import ErrorComponent from './components/ErrorComponent.vue' import LoadingComponent from './components/LoadingComponent.vue' // 第二种:带选项的异步组件 const asyncModalWithOptions = defineAsyncComponent({ loader: () => import('./Modal.vue'), delay: 200, timeout: 3000, errorComponent: ErrorComponent, loadingComponent: LoadingComponent })
-
JavaScriptHTML
import { defineAsyncComponent, h } from "vue"; import Error from "../components/Error.vue"; import Loading from "../components/Loading.vue"; import "nprogress/nprogress.css"; import NProgress from "nprogress"; NProgress.configure({ trickleSpeed: 50, showSpinner: false, }); export function delay(duration) { if (!duration) { duration = random(1000, 5000); } return new Promise((resolve) => { setTimeout(() => { resolve(); }, duration); }); } export function random(min, max) { return Math.floor(Math.random() * (max - min)) + min; } // 得到一个异步页面 export function getAsyncPage(path) { return defineAsyncComponent({ loader: async () => { NProgress.start(); await delay(); const comp = await import(path); NProgress.done(); return comp; }, loadingComponent: Loading, // 当promise在pending状态时,将显示这里的组件 }); } // 得到一个异步组件 export function getAsyncComponent(path) { return defineAsyncComponent({ loader: async () => { await delay(); if (Math.random() < 0.5) { throw new TypeError(); } return import(path); }, loadingComponent: Loading, // 当promise在pending状态时,将显示这里的组件 errorComponent: { render() { return h(Error, "出错了!!!"); }, }, }); }
<template> <div class="container"> <div class="block"> <h2>区域1</h2> <p> <button @click="modalVisible = true">打开朦层</button> </p> <Teleport to="body"> <Modal v-if="modalVisible"> <button @click="modalVisible = false">关闭朦层</button> </Modal> </Teleport> </div> <div class="block mid"> <h2>区域2</h2> </div> <div class="block big"><Block3 /></div> <div class="block big"><h2>区域4</h2></div> <div class="block mid"><Block5 /></div> <div class="block"><h2>区域6</h2></div> </div> </template> <script> import { ref } from "vue"; import Modal from "../components/Modal.vue"; import { getAsyncComponent } from "../util"; const Block3 = getAsyncComponent("../components/Block3.vue"); const Block5 = getAsyncComponent("../components/Block5.vue"); export default { components: { Block3, Block5, Modal, }, setup() { const modalVisibleRef = ref(false); return { modalVisible: modalVisibleRef, }; }, }; </script> <style scoped> </style>
vue3🛫 Vue3 概览
上一篇