Shinkai005

V1

2022/05/05阅读:20主题:红绯

Vue动态组件/异步demo

没产出我也压力大啊!

Vue动态组件/异步demo

image-20220505221427773
image-20220505221427773

需求:

把需求想清楚,不然写完还得优化好几遍很麻烦

image-20220505225901421
image-20220505225901421

top-bar 是可以切换的.

slide-bar 是在posts组件里,可以切换的.

要求切换选项卡到第二个,回到第一个后,依旧可以显示原来的.

实现如下

整体代码实现如下:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="../js/vue.js"></script>
    <style>
      .tab-button {
        padding6px 10px;
        border-top-left-radius3px;
        border-top-right-radius3px;
        border1px solid #ccc;
        cursor: pointer;
        background#f0f0f0;
        margin-bottom: -1px;
        margin-right: -1px;
      }
      .tab-button:hover {
        background#e0e0e0;
      }
      .tab-button.active {
        background#e0e0e0;
      }
      .tab {
        border1px solid #ccc;
        padding10px;
      }
      .posts-tab {
        display: flex;
      }
      .posts-sidebar {
        max-width40vw;
        margin0;
        padding0 10px 0 0;
        list-style-type: none;
        border-right1px solid #ccc;
      }
      .posts-sidebar li {
        white-space: nowrap;
        text-overflow: ellipsis;
        overflow: hidden;
        cursor: pointer;
      }
      .posts-sidebar li:hover {
        background#eee;
      }
      .posts-sidebar li.selected {
        background: lightblue;
      }
      .selected-post-container {
        padding-left10px;
      }
      .selected-post > :first-child {
        margin-top0;
        padding-top0;
      }
    
</style>
  </head>
  <body>
    <!-- 根组件 -->
    <div id="dynamic-component-demo">
      <!-- top 按钮 -->
      <button
        v-for="tab in tabs"
        v-bind:key="tab"
        v-bind:class="['tab-button', { active: currentTab === tab }]"
        v-on:click="currentTab = tab"
      >

        {{ tab }}
      </button>
      <!-- 根据选项卡显示对应组件 -->
      <keep-alive>
        <component v-bind:is="currentTabComponent" class="tab"></component>
      </keep-alive>
    </div>

    <script>
      /* tab-posts组件 */
      Vue.component('tab-posts', {
        data: function () {
          return {
            posts: [
              {
                id: 1,
                title: 'First',
                content:
                  '<p>Life isn\'t about waiting for the storm to pass, it\'s about learning to dance in the rain.</p>',
              },
              {
                id: 2,
                title: 'Second',
                content:
                  '<p>努力就好,每个人都有自己的节奏,只能尽力就行,别想的太多.</p>',
              },
              {
                id: 3,
                title: 'Third',
                content:
                  '<p>别放弃,坚持一下.难受就歇会不差这么一会.</p>',
              },
            ],
            selectedPost: null,
          };
        },
        template: 
        /* html */
        `
   <div class="posts-tab">
      <ul class="posts-sidebar">
        <li
          v-for="post in posts"
          v-bind:key="post.id"
          v-bind:class="{ selected: post === selectedPost }"
     v-on:click="selectedPost = post"
        >

          {{ post.title }}
        </li>
      </ul>
      <div class="selected-post-container">
       <div
         v-if="selectedPost"
          class="selected-post"
        >

          <h3>{{ selectedPost.title }}</h3>
          <div v-html="selectedPost.content"></div>
        </div>
        <strong v-else>
          Click on a blog title to the left to view it.
        </strong>
      </div>
    </div>
  `,
      });

      Vue.component('tab-archive', {
        template: '<div>Archive component</div>',
      });

      new Vue({
        el: '#dynamic-component-demo',
        data: {
          currentTab: 'Posts',
          tabs: ['Posts', 'Archive'],
        },
        computed: {
          currentTabComponent: function () {
            return 'tab-' + this.currentTab.toLowerCase();
          },
        },
      });
    
</script>
  </body>
</html>

keep-alive

概念

keep-alive 是 Vue 的内置组件,当它包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。和 transition 相似,keep-alive 是一个抽象组件:它自身不会渲染成一个 DOM 元素,也不会出现在父组件链中。

作用

在组件切换过程中将状态保留在内存中,防止重复渲染DOM,减少加载时间及性能消耗,提高用户体验性

原理

在 created 函数调用时将需要缓存的 VNode 节点保存在 this.cache 中/在 render(页面渲染) 时,如果 VNode 的 name 符合缓存条件(可以用 include 以及 exclude 控制),则会从 this.cache 中取出之前缓存的 VNode 实例进行渲染。

VNode:虚拟DOM,其实就是一个JS对象

Props

include - 字符串或正则表达式。只有名称匹配的组件会被缓存。 exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存。 max - 数字。最多可以缓存多少组件实例。

生命周期函数

1. activated

在 keep-alive 组件激活时调用       该钩子函数在服务器端渲染期间不被调用

2. deactivated

在 keep-alive 组件停用时调用       该钩子在服务器端渲染期间不被调用

被包含在 keep-alive 中创建的组件,会多出两个生命周期的钩子: activated 与 deactivated

使用 keep-alive 会将数据保留在内存中,如果要在每次进入页面的时候获取最新的数据,需要在 activated 阶段获取数据,承担原来 created 钩子函数中获取数据的任务。

注意: 只有组件被 keep-alive 包裹时,这两个生命周期函数才会被调用,如果作为正常组件使用,是不会被调用的,以及在 2.1.0 版本之后,使用 exclude 排除之后,就算被包裹在 keep-alive 中,这两个钩子函数依然不会被调用!另外,在服务端渲染时,此钩子函数也不会被调用。

缓存所有页面--在 App.vue 里面

<template>
  <div id="app">
  	<keep-alive>
      <router-view/>
    </keep-alive>
  </div>
</template>

<script>
export default {
  name: 'App'
}
</script>

根据条件缓存页面---在 App.vue 里面

<template>
  <div id="app">
  	// 1. 将缓存 name 为 test 的组件
  	<keep-alive include='test'>
      <router-view/>
    </keep-alive>

	// 2. 将缓存 name 为 a 或者 b 的组件,结合动态组件使用
	<keep-alive include='a,b'>
	  <router-view/>
	</keep-alive>
	
	// 3. 使用正则表达式,需使用 v-bind
	<keep-alive :include='/a|b/'>
	  <router-view/>
	</keep-alive>	
	
	// 5.动态判断
	<keep-alive :include='includedComponents'>
	  <router-view/>
	</keep-alive>
	
	// 5. 将不缓存 name 为 test 的组件
	<keep-alive exclude='test'>
	  <router-view/>
	</keep-alive>
  </div>
</template>

<script>
export default {
  name: 'App'
}
</script>

结合Router,缓存部分页面--在 router 目录下的 index.js 文件里

import Vue from 'vue'
import Router from 'vue-router'
const Home = resolve => require(['@/components/home/home'], resolve)
const Goods = resolve => require(['@/components/home/goods'], resolve)
const Ratings = resolve => require(['@/components/home/ratings'], resolve)
const Seller = resolve => require(['@/components/home/seller'], resolve)

Vue.use(Router)

export default new Router({
  mode'history',
  routes: [
    {
      path'/',
      name'home',
      component: Home,
      redirect'goods',
      children: [
        {
          path'goods',
          name'goods',
          component: Goods,
          meta: {
         keepAlivefalse // 不需要缓存
         }
        },
        {
          path'ratings',
          name'ratings',
          component: Ratings,
          meta: {
         keepAlivetrue  // 需要缓存
         }
        },
        {
          path'seller',
          name'seller',
          component: Seller,
          meta: {
         keepAlivetrue  // 需要缓存
         }
        }
      ]
    }
  ]
})

在 App.vue 里面

<template>
  <div id="app">
   <keep-alive>
      <router-view v-if="$route.meta.keepAlive"></router-view>
    </keep-alive>
    <router-view v-if="!$route.meta.keepAlive"></router-view>
  </div>
</template>

<script>
export default {
  name'App'
}
</script>

把上面组件改成异步的

image-20220505233438993
image-20220505233438993

现在都是各种webpack搞得我是真滴烦.

几百行代码用啥这些玩意啊....

下面是我看vue3官方介绍视频这样引入的.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="../js/vue.js"></script>
    <style>
      .tab-button {
        padding6px 10px;
        border-top-left-radius3px;
        border-top-right-radius3px;
        border1px solid #ccc;
        cursor: pointer;
        background#f0f0f0;
        margin-bottom: -1px;
        margin-right: -1px;
      }
      .tab-button:hover {
        background#e0e0e0;
      }
      .tab-button.active {
        background#e0e0e0;
      }
      .tab {
        border1px solid #ccc;
        padding10px;
      }
      .posts-tab {
        display: flex;
      }
      .posts-sidebar {
        max-width40vw;
        margin0;
        padding0 10px 0 0;
        list-style-type: none;
        border-right1px solid #ccc;
      }
      .posts-sidebar li {
        white-space: nowrap;
        text-overflow: ellipsis;
        overflow: hidden;
        cursor: pointer;
      }
      .posts-sidebar li:hover {
        background#eee;
      }
      .posts-sidebar li.selected {
        background: lightblue;
      }
      .selected-post-container {
        padding-left10px;
      }
      .selected-post > :first-child {
        margin-top0;
        padding-top0;
      }
    
</style>
  </head>
  <body>
    <!-- 根组件 -->
    <div id="dynamic-component-demo">
      <!-- top 按钮 -->
      <button
        v-for="tab in tabs"
        v-bind:key="tab"
        v-bind:class="['tab-button', { active: currentTab === tab }]"
        v-on:click="currentTab = tab"
      >

        {{ tab }}
      </button>
      <!-- 根据选项卡显示对应组件 -->
      <keep-alive>
        <component v-bind:is="currentTabComponent" class="tab"></component>
      </keep-alive>
    </div>
    <script src="./components/TabPosts.js"></script>
    <script src="./components/TabArchive.js"></script>
    <script>
      new Vue({
        el'#dynamic-component-demo',
        data: {
          currentTab'Posts',
          tabs: ['Posts''Archive'],
        },
        computed: {
          currentTabComponentfunction ({
            return 'tab-' + this.currentTab.toLowerCase();
          },
        },
      });
    
</script>
  </body>
</html>

这里在写一个关于script标签同步异步加载

script标签默认是同步加载的,也就是说,当读到script标签是引入外部js文件的,会立即停止对页面的向下解析,然后下载脚本并且执行,如果说js文件相对来说比较大,而且网速什么的又不是很好,那么用户在浏览网页的时候,会有很长一段时间的等待,用户体验很差。

当js文件里面没有对DOM进行操作或者对css进行操作的时候,我们可以将这个js文件设置成异步加载的,这样子会让用户体验更好。

那么如何设置js文件的异步加载呢?

有两种方式:

<script defer>
<script async>

这两种方式的区别就是:

defer是在对页面解析的同时下载js文件,但是要等页面解析完,才开始执行js文件。

async是在对页面解析的同时,下载js文件,一旦有下载完了,就先停止对页面的解析,执行下载完的那个js文件,当有多个js文件使用async的时候,不能保证执行顺序。

分类:

前端

标签:

JavaScript

作者介绍

Shinkai005
V1

公众号:深海笔记Shinkai