API 和数据响应式的变化

去掉了 Vue 构造函数

  1. 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>
    
  2. 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

  1. vue2 api

  2. vue3 api

对比数据响应式

vue2vue3 均在相同的生命周期完成数据响应式,但做法不一样

  1. vue2 数据响应式原理

  2. vue3 数据响应式原理

模板中的变化

v-model

vue2 比较让人诟病的一点就是提供了两种双向绑定:v-model.sync,在 vue3 中去掉了 .sync 修饰符,只需要使用 v-model 进行双向绑定即可;

为了让 v-model 更好的针对多个属性进行双向绑定,vue3 作出了以下修改;

  1. 当对自定义组件使用 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" />
    
  2. 去掉了 .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" />
    
  3. 允许自定义 v-model 修饰符(新增);

  4. 案例(附件下载))

    HTML
    HTML
    <!-- 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

  • vue2v-if 的优先级低于 v-for

  • vue3v-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

  1. asyncComponent 的两种使用方式;

    JavaScript
    JavaScript
    import { 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
    })
    
  2. 关键代码(附件下载)

    JavaScript
    HTML
    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>
    
打赏作者
您的打赏是我前进的动力
微信
支付宝
评论

中午好👏🏻,我是 ✍🏻   疯狂 codding 中...

粽子

这有关于前端开发的技术文档和你分享。

相信你可以在这里找到对你有用的知识和教程。

了解更多

目录

  1. 1. API 和数据响应式的变化
    1. 1.1. 去掉了 Vue 构造函数
    2. 1.2. 组件实例中的 API
    3. 1.3. 对比数据响应式
  2. 2. 模板中的变化
    1. 2.1. v-model
    2. 2.2. v-if v-for
    3. 2.3. key
    4. 2.4. Fragment
  3. 3. 组件的变化
    1. 3.1. Teleport、Fragment
    2. 3.2. asyncComponent