导航


HTML

CSS

JavaScript

浏览器 & 网络

版本管理

框架

构建工具

TypeScript

性能优化

软实力

算法

UI、组件库

Node

业务技能

针对性攻坚

AI


🔥 案例说明

登录 tab 的 demo

Untitled

keep-alive 缓存组件

<keep-alive></keep-alive>

实现原理

在具体实现上,keep-alive 在内部维护了一个 key数组 和一个 缓存对象

//keep-alive 内部声明周期函数
  created () {
    this.cache = Object.create(null)
    this.keys = []
  },
 render () {
    const slot = this.$slots.default; //获取默认插槽
    const vnode = getFirstComponentChild(slot); //得到插槽中第一个组件的vnode
    const name = getComponentName(vnode.componentOptions); //获取组件名字

    const { cache, keys } = this; //获取当前的缓存内对象和key数组
    const key: ?string = vnode.key == null
        ? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
        : vnode.key; //获取组件的key值,如果没有key值,会按照规则自动生成
      if (cache[key]) {
      //有缓存
      //重用组件实例
        vnode.componentInstance = cache[key].componentInstance
        remove(keys, key); //删除key值
        //将key值加入到数组末尾,这样是为了保证最近使用的组件在数组中靠后,主要是为了方便设置的max值删除很久没使用的组件
        keys.push(key)
      } else {
      //没有缓存的则进行缓存
        cache[key] = vnode
        keys.push(key)
        // prune oldest entry
        if (this.max && keys.length > parseInt(this.max)) {
        //超过最大缓存数量,移除第一个key对应的缓存
          pruneCacheEntry(cache, keys[0], keys, this._vnode)
        }
      }

      vnode.data.keepAlive = true
    }
    return vnode || (slot && slot[0])
  }

来源:https://segmentfault.com/a/1190000040105722

动态组件

<component :is="DynamicComponent"></component>

异步组件

🔥 Vue2 引入

const AsyncComponent = () => import('./组件路径');

export default {
	components: {
  	AsyncComponent
  }
}

或者:

export default {
  components: {
    QrcodeLogin: () => import('./QrcodeLogin.vue')
  },
}

Vue3 引入

AsyncComponent = defineAsyncComponent(() =>
	new Promise((resolve, reject) => {
		resolve({
    	data() {
      	return {...}
      },
      template: ``
    });
	});
);

或者有大括号:

AsyncComponent = defineAsyncComponent(() => {
	return new Promise((resolve, reject) => {
		resolve({
    	data() {
      	return {...}
      },
      template: ``
    });
	});
});

或者:

// npm install Vue 的方式
import { defineAsyncComponent } from 'Vue';

// cdn 引入 Vue 的方式
const { defineAsyncComponent } = Vue;

AsyncComponent = defineAsyncComponent(() => import('./组件路径'));

引入后使用 - 局部

const AsyncComponent = defineAsyncComponent(() => import('./组件路径'));

export default {
	components: {
  	AsyncComponent
  }
}

// or

export default {
	components: {
  	AsyncComponent: defineAsyncComponent(() => import('./组件路径'));
  }
}

引入后使用 - 全局

const AsyncComponent = defineAsyncComponent(() => import('./组件路径'));

app.component('async-comp', AsyncComponent);

起别名

Untitled

案例代码

MainLogin.vue

keep-alive + compoment 动态组件 + import 异步组件

<template>
  <div class="login-tab">
    <div class="login-nav">
      <div
        v-for="tab of tabData"
        :key="tab"
        :class="['nav-item', { active: currentTab === tab }]"
        @click="changeTab(tab)"
      >{{ tab }}</div>
    </div>
    <div class="login-component">
      <keep-alive>
        <component :is="currentTabComponent"></component>
      </keep-alive>
    </div>
  </div>
</template>

Untitled

完整代码

<template>
  <div class="login-tab">
    <div class="login-nav">
      <div
        v-for="tab of tabData"
        :key="tab"
        :class="['nav-item', { active: currentTab === tab }]"
        @click="changeTab(tab)"
      >{{ tab }}</div>
    </div>
    <div class="login-component">
      <keep-alive>
        <component :is="currentTabComponent"></component>
      </keep-alive>
    </div>
  </div>
</template>

<script>
import AccountLogin from './AccountLogin.vue';
const QrcodeLogin = Vue.defineAsyncComponent(() => import('./QrcodeLogin.vue'));
const MobileLogin = Vue.defineAsyncComponent(() => import('./MobileLogin.vue'));
export default {
  name: 'MainLogin',
  components: {
    AccountLogin,
    QrcodeLogin,
    MobileLogin,
    // vue2用下面形式:
    // QrcodeLogin: () => import('./QrcodeLogin.vue')
  },
  data() {
    return {
      currentTab: 'Account',
      tabData: ['Account', 'Qrcode', 'Mobile'],
    }
  },
  computed: {
    currentTabComponent() {
      // <account-login></account-login>
      return this.currentTab.toLowerCase() + '-login';
    }
  },
  methods: {
    changeTab(tab) {
      this.currentTab = tab;
    }
  }
}
</script>

<style lang="scss">
.login-tab {
  width: 500px;
  margin: 50px auto;
  border: 1px solid #000;

  .login-nav {
    height: 50px;
    border-bottom: 1px solid #000;

    .nav-item {
      float: left;
      width: 33.33%;
      text-align: center;
      line-height: 50px;

      &.active {
        background-color: #000;
        color: #fff;
      }
    }
  }
}
</style>