Vue 之 零散小知识

一、异步组件 

为什么要使用异步组件,首先我们要了解另一个概念

webpack的打包过程

  • 默认情况下,因为组件和组件之间是通过模块化直接依赖的,所以webpack打包时会将组件模块打包到一起,比如(app.js文件中)
  • 对于一些不需要立即使用的组件,我们可以对它们进行拆分,拆成小的代码块chunk.js
  • 这些chunk.js会在需要的时候才会去请求服务器加载,然后运行

webpack是如何进行分包的呢,是使用import('  ')函数

如果我们的项目过大,对于某些组件我们希望通过异步的方式来进行加载( 目的是进行分包处理 ),在vue中提供了一个函数,

defineAsyncComponent异步组件函数

defineAsyncComponent接受两种类型的参数

类型一 : 工厂函数,该工厂函数返回一个promise对象 ( 常用 )

// 导入vue中的方法
import { defineAsyncComponent } from 'vue';

// 普通组件
import profile from './pages/profile.vue';
// 异步组件
const profile = defineAsyncComponent(() => import('./pages/profile.vue'));

类型二 : 接受一个对象类型,对异步函数进行配置( 不常用 )

const profile = defineAsyncComponent({
  // 工厂函数
  loader: () => import('./pages/profile.vue'),
  // 加载过程中显示的组件Loading,用来占位的,用普通组件的方式导入即可
  loadingComponent: Loading,
  // 加载失败时显示的组件Error
  errorComponent: Error,
  // 在显示loadingComponent组件之前的延迟,等待多长时间 | 默认值200ms
  delay: 2000,
  // 如果提供了timerout,并且加载组件时超过了设定值,将显示错误组件,默认值 infinity == 永不超时 ms
  timeout: 3000,
  // 定义组件是否可挂起 默认true
  suspensible: true,
  /**
   * err : 错误信息
   * retry : 函数,调用retry尝试重新加载
   * fail : 函数,指示加载程序结束退出
   * attempts : 记录尝试的次数
   */
  onError(err, retry, fail, attempts) {
    if (err.message.match(/fetch/) && attempts <= 3) {
      // 请求发生错误时重试,最多尝试3次
      retry();
    } else {
      // 像promise的resove|reject一样,必须调用一个,否则就卡在这里了
      fail();
    }
  }
});

tip : 异步组件在路由中使用的很多 


二、Mixin

在开发中,我们会遇到各个组件重复的代码逻辑,可能有重复的字典数据,重复的用户信息,重复的接口请求,所以,Mixin就随之出现了

在Vue2和Vue3中都支持一种方式,就是使用Mixin来完成

  • Minin提供了一种非常灵活的方式,来分发Vue组件中的可复用功能
  • 一个Mixin对象可以包含任何组件选项
  • 当组件使用Mixin对象时,所有Mixin对象的选项将被 混合 进入该组件本身的选项中

1. Mixin的基本使用

01 - 创建demoMixin.js文件

export const demoMixin = {
  data() {
    return {
      message: 'demo message'
    }
  },
  created() {
    console.log('demo created 执行')
  },
  methods: {
    foo() {
      console.log('demo foo 执行')
    }
  }
}

02 - 在组件中导入并使用

<template>
  <div class="box">
    <!-- 可直接使用mixin中的数据 -->
    <h1>{{ message }}</h1>
    <!-- 可直接使用mixin中的方法 -->
    <button @click="foo">按钮</button>
  </div>
</template>
<script>
// 导入
import { demoMixin } from '../mixin/demoMixin'
export default {
  // 注册一下
  mixins: [demoMixin],
  data() {
    return {}
  },
  created() {},
  methods: {}
}
</script>

03 - 效果

2. Mixin的合并规则 

如果Mixin对象中的选项和组件对象中的选项发生了冲突,那么Vue会如何操作呢?

情况一 : data中的数据

  • data函数的返回值对象默认情况下会进行合并
  • 如果数据有冲突 ( 属性名相同 ),保留组件自身的数据

情况二 : 生命周期

  • 生命周期函数会被合并到一个数组中,mixin和组件的都会执行
  • Mixin的生命周期 ( created | mounted ) 会被先执行

情况二 : 值为对象的选项

  • 比如methods、components、directives,都会合并在一起
  • 如果有重复,保留组件自身的

3. 全局混入Mixin 

如果组件中的某些选项,是所有的组件都需要拥有的,那么这个时候可以使用全局的Mixin

  • 全局的Mixin可以使用 应用app的方法mixin 来完成注册
  • 一旦注册,那么全局混入的选项将会影响每一个组件

在main.js中进行代码修改

import { createApp } from 'vue'
import App from './App.vue'
import 'animate.css'
// 之前
// createApp(App).mount('#app');
// 现在
const app = createApp(App)

// 注册全局mixin组件,会被混入到每一个组件中
app.mixin({
  data() {
    return {}
  },
  methods: {},
  created() {
    console.log('app created')
  }
})

app.mount('#app')

三、extends 

extends在开发中用的比较少,因为可以用Mixin来代替,Mixin更灵活,简单了解下即可

1. 创建一个baseVue.vue文件

<template>
  <div></div>
</template>

<script>
export default {
  data() {
    return {
      title: 'base title'
    }
  },
  created() {
    console.log('base created')
  }
}
</script>

<style lang="scss" scoped></style>

2. 导入并使用

<template>
  <div class="box">
    <!-- 可直接使用继承的数据 -->
    {{ title }}
  </div>
</template>
<script>
// 导入
import baseVue from './baseVue.vue'
export default {
  // 继承一下
  extends: baseVue,
  data() {
    return {}
  },
  methods: {}
}
</script>

3. 推荐

在开发中extends用的非常少,在Vue2中推荐使用Mixin,而在Vue3中推荐使用Composition Api


四、Suspense组件 

Suspense是vue3正在新加的组件,正在实验的一个特性,可能以后都会存在,也可能会不存在~,可以和异步组件结合来使用

Suspense是一个内置的全局组件,该组件有两个插槽

  • default : 如果default可以显示,那么就显示default的内容
  • fallback : 如果default无法显示,那么会显示fallback插槽的内容
<suspense>
  <template #default>
    <profile />
  </template>
  <template #fallback>
    <loading />
  </template>
</suspense>

五、动态组件

举个栗子🌰,比如我们现在想要实现一个功能,点击一个tab-bar,切换不同的组件显示

方式一 : 通过v-if || v-show

1 - 创建 home, about, profile 三个测试组件

2 - 引用并写如下代码

<template>
  <div class="hello-world-layout">
    <ul class="nav">
      <li class="item" v-for="item in tabArr" :key="item" @click="itemClick(item)" :class="{ active: currentTab === item }">{{ item }}</li>
    </ul>

    <template v-if="currentTab === 'home'">
      <home />
    </template>
    <template v-if="currentTab === 'about'">
      <about />
    </template>
    <template v-if="currentTab === 'profile'">
      <profile />
    </template>
  </div>
</template>

<script>
// 引用测试组件
import home from './pages/home.vue';
import about from './pages/about.vue';
import profile from './pages/profile.vue';
export default {
  components: { home, about, profile },
  name: 'HelloWorld',
  data() {
    return {
      tabArr: ['home', 'about', 'profile'],
      // 指定默认组件
      currentTab: 'home'
    };
  },
  methods: {
    // 点击切换
    itemClick(item) {
      this.currentTab = item;
    }
  }
};
</script>

<style lang="scss" scoped>
.hello-world-layout {
  .nav {
    padding: 0;
    margin: 0;
    width: 100%;
    display: flex;
    align-items: center;
    list-style: none;
    margin-bottom: 100px;
  }
  .item {
    flex: 1;
    height: 30px;
    line-height: 30px;
    background-color: skyblue;
    &.active {
      background-color: red;
      color: #fff;
    }
  }
}
</style>

3 - 效果

方式二 : 使用动态组件

概念 : 动态组件是使用component组件,通过一个特殊的attribute is 来实现

is : 后面可以跟全局注册的组件名,也可以跟局部注册的组件名(不区分大小写)

tip : 所以,代码直接可以简化成这个样子!是不是很nice 


六、keep-alive 

一般会和 路由一起使用,其他地方也可用~

栗子🌰

在上面所创建的about.vue文件中我们写如下代码,就是增加一个计数器 

<template>
  <div>about组件</div>
  {{ count }}
  <button class="addClick" @click="count++">+1</button>
</template>
<script>
export default {
  data() {
    return {
      count: 0
    };
  }
};
</script>
<style lang="scss" scoped></style>

但是,不知道你们有没有思考一个问题,如果这个时候我们切换为home组件,然后再跳转回来,那么记得数还回存在吗~答案是否定,肯定没有了,因为动态组件内部是使用v-if来进行判定的

存在的问题

使用keep-alive解决该问题

如果想保留的话,就要使用keep-alive了

keep-alive : 在开发中某些情况我们希望继续保持组件的状态,而不是销毁掉,这个时候可以使用

<keep-alive>
   <component :is="currentTab" />
</keep-alive>

keep-alive常用的属性 

这个根据的是name属性 

这里涉及两个生命周期 activated || deactivated

对于缓存的组件来说,再次进入该组件,不会执行created或mounted等生命周期的,那么问题来了,如果我们确实希望监听到何时重新进入了该组件,何时离开了该组件

activated : 进入时触发

deactivated : 离开时触发

tip : 第一次进入该组件时,created、mounted、activated会依次执行


七、生命周期


八、v - model 

1. 元素上使用

解释

<input type="text" v-model="count" placeholder="请输入..." />

<!-- 相当于绑定value值,监听input方法改变值 -->

<input type="text" :value="count" @input="count = $event.target.value" />

整体代码 

<template>
  <div>about组件</div>
  <input type="text" v-model="count" placeholder="请输入..." />
  <div>{{ count }}</div>
</template>

<script>
export default {
  name: 'about',
  data() {
    return {
      count: ''
    };
  }
};
</script>

<style lang="scss" scoped></style>

2. 组件上使用 

默认情况下,会生成value属性和input事件

但是可以更改

默认情况 

解释

<abc v-model="num" />
<!-- 相当于 -->
<abc :value="num" @input="num = $event" />

父组件代码 

<template>
  <div class="index-layout">
    <abc v-model="num" />
    <!-- 相当于 -->
    <abc :value="num" @input="num = $event" />
  </div>
</template>
<script>
import abc from './abc.vue';
export default {
  name: 'index',
  components: {
    abc
  },
  data() {
    return {
      num: 2
    };
  }
};
</script>

子组件代码

<template>
  <div class="a-layout">
    <button type="button" @click="change(value + 1)">+1</button>
    <div>{{ value }}</div>
    <button type="button" @click="change(value - 1)">+1</button>
  </div>
</template>
<script>
export default {
  name: 'abc',
  props: {
    value: {
      type: [Number, String],
      default: 0
    }
  },
  methods: {
    change(newValue) {
      this.$emit('input', newValue);
    }
  }
};
</script>

效果

自定义

更改子组件变量名称和改变方法 

<template>
  <div class="a-layout">
    <button type="button" @click="change(number + 1)">+1</button>
    <div>{{ number }}</div>
    <button type="button" @click="change(number - 1)">+1</button>
  </div>
</template>
<script>
export default {
  model: {
    // 默认情况下,组件的 v-model 会把 value 作为 prop 且把 input 作为事件
    // prop: 'value',
    // event: 'input'
    //可以配置 model 选项来使用不同的 prop 和 event
    prop: 'number',
    event: 'change'
  },
  name: 'abc',
  props: {
    number: {
      type: [Number, String],
      default: 0
    }
  },
  methods: {
    change(newNumber) {
      this.$emit('change', newNumber);
    }
  }
};
</script>

九、snyc修饰符

v-model 只能用一次; .sync可以有多个

语法

  • 父组件
    • 属性值后加上sync修饰符
    • :attr.sync="attr" 
  • 子组件
    • 发射事件时,使用update:xxx作为事件名称
    • $emit('update:attr',xxx)

父组件

<template>
  <div class="index-layout">
    <abc v-model="n" :n1.sync="n1" :n2.sync="n2" :n3.sync="n3" />
  </div>
</template>
<script>
import abc from './abc.vue';
export default {
  name: 'index',
  components: {
    abc
  },
  data() {
    return {
      n: 99,
      n1: 11,
      n2: 22,
      n3: 33
    };
  }
};
</script>

子组件

<template>
  <div class="a-layout">
    <button type="button" @click="change(1)">+1</button>
    <button type="button" @click="change(-1)">+1</button>
    <hr />
    <div>n:{{ value }}</div>
    <hr />
    <div>n1:{{ n1 }}</div>
    <hr />
    <div>n2:{{ n2 }}</div>
    <hr />
    <div>n3:{{ n3 }}</div>
    <hr />
  </div>
</template>
<script>
export default {
  name: 'abc',
  props: {
    value: {
      type: [Number, String],
      default: 0
    },
    n1: {
      type: [Number, String],
      default: 0
    },
    n2: {
      type: [Number, String],
      default: 0
    },
    n3: {
      type: [Number, String],
      default: 0
    }
  },
  methods: {
    change(newValue) {
      // n : v-model
      this.$emit('input', newValue * Math.random());

      // n1 : sync
      this.$emit('update:n1', newValue * Math.random());
      // n2 : sync
      this.$emit('update:n2', newValue * Math.random());
      // n3 : sync
      this.$emit('update:n3', newValue * Math.random());
    }
  }
};
</script>

效果 

        

  • 31
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 32
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 32
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值