一、使用
require.context
实现前端工程自动化require.context
是一个Webpack
提供的Api,通过执行require.context
函数获取一个特定的上下文,主要是用于实现自动化导入模块。什么时候用? 当一个js文件里面需要手动引入过多的其他文件时,就可以使用。
在Vue项目开发过程中,我们可能会遇到这些可能会用到require.context的场景
1、当页面内容较多,我们把页面细化为多个组件,然后再通过 import 引入到 index.js 主入口文件中的时候。
2、开发了一些基础组件,然后把所有组件都导入到 index.js 中,然后再将所有组件进行全局注册的时候。
3、vuex 分割模块后,将各模块引入到 index.js 主文件中的时候。
对于上述的几个场景,如果我们要导入的文件比较少的情况下,通过 import 一个一个去导入还可以接受,但对于量比较大的情况,就变成了纯体力活,并且每次修改都需要在主入口文件内进行调整。这样做一点都不优雅,这时候我们就可以通过 require.context 去简化这个过程。
以第2条为例,讲一下 require.context 的用法
常规用法
组件通过常规方式全局注册(组件越多越繁琐)
// index.js import Vue from 'vue' import BaseIcon from './BaseIcon.vue' import BaseButton from './BaseButton.vue' import BaseInput from './BaseInput.vue' import BaseLoading from './BaseLoading.vue' const components = [ BaseIcon, BaseButton, BaseInput, BaseLoading ] components.forEach((component) => { Vue.component(component.name, component) }) // --------------分割线-------------- // 而更多的同学可能会像下面这样进行注册 Vue.component('base-icon', BaseIcon) Vue.component('base-button', BaseButton) Vue.component('base-input', BaseInput)使用 require.context
require.context 基本语法
require.context(directory, useSubdirectories, regExp);directory:要搜索的文件夹目录
useSubdirectories:是否还去搜索它的子级目录
通过 require.context 注册组件
import Vue from 'vue' // 这里暂不考虑 JSX 和 TSX const context = require.context('./', false, /\.vue$/) context.keys().forEach((key) => { const component = context(key).default Vue.component(component.name, component) })context.keys() 会返回一个数组,包含所有匹配到的文件的路径
context(key)可以获取到对应的文件 .default表示 export default 导出的内容
现在代码简洁了很多,也更加优雅。
二、 v-model 原来可以这么用
v-model 属于语法糖
在用Vue开发前端时,不论使用原生还是封装好的UI库,对于表单组件,一般都会使用到 v-model ,而为了更好的表达 value 属性的用处(类似于单选框、复选框),也会进行适当的自定义。但是对于自定义组件同样可以使用 v-model 。
vue 对 v-model 的基本定义
一个组件上的 v-model 默认是在组件上面定义一个名为 value 的 prop ,同时对外暴露一个名为 input 的事件。
像这样:
<template> <div class="base-input"> <input type="text" :value="value" @change="handleChange" /> </div> </template> <script> export default { name: 'BaseInput', props: { // 名为value 的属性 value: { type: String, default: '', }, }, methods: { handleChange(e) { // 暴露一个input时间 this.$emit('input', e.target.value) }, }, } </script>用起来就是这样:
<BaseInput v-model="something" />对 v-model 进行自定义
通过设置 model 选项, 以自定义开关组件为例
<template> <div :class="['base-switch', active && 'base-switch-active']" @click="handleClick"> <div class="base-switch-pie"></div> </div> </template> <script> export default { name: 'BaseSwitch', // 通过model属性自定义 属性名和事件名 model: { event: 'change', prop: 'active', }, props: { // 虽然自定义了 active属性,仍然要在 props 选项里面声明 active: { type: Boolean, default: false, }, }, methods: { handleClick() { // 暴露change事件 this.$emit('change', !this.active) }, }, } </script>这么用:
<BaseSwitch v-model="isActive" />这样的开关组件是不是更简洁了。
三、使用 .sync ,更优雅的实现数据“双向绑定”
在 Vue 中,props 属性是单向数据传输的,父级的 prop 的更新会影响到子组件,但是反过来不行。可是有些情况,我们需要对prop进行“双向绑定”。
上面,我们提到了使用 v-model 实现“双向绑定”。但有时候我们希望一个组件可以实现多个数据的“双向绑定”,而在 Vue2.x 中 v-model 一个组件只能有一个,这时候就需要使用到 .sync 。
.sync 与 v-model 的异同
相同点:
两者的本质都是语法糖,目的都是实现组件与外部数据的“双向绑定”
两者都是通过 属性+事件 来实现的
一个组件只能定义一个 v-model ,但可以定义多个.sync
定义方式不同,v-model 默认事件为 input ,可以通过配置 model 选项来修改,.sync事件名称固定为update:属性名
使用 .sync
比如写一个遮罩层
<template> <div class="base-mask" v-if="showMask" @click.stop="handleClickMask"> <slot></slot> </div> </template> <script> export default { name: 'BaseMask', props: { showMask: { type: Boolean, default: false, }, }, methods: { handleClickMask() { this.$emit('update:showMask', !this.showMask) }, }, } </script>这样去使用
<template> <div class="about"> <BaseSwitch v-model="isActive" /> <BaseMask :show-mask.sync="isActive">这里可以加一些东西</BaseMask> </div> </template> <script> export default { data() { return { isActive: false, } }, } </script>代码简洁的同时,也更加优雅
四、动态组件,更优雅的按条件渲染页面
有这么一个场景,需要根据条件(多条件),在页面显示相应的内容,如果用 v-if 来判断要显示哪个组件,那就会是这个样子:
<template> <div> <ComponentA v-if="type === 'a'" /> <ComponentB v-else-if="type === 'b'" /> <ComponentC v-else-if="type === 'c'" /> <!-- 这里还有 v-else-if --> <ComponentG v-else /> </div> </template>条件越多,上面代码中那一堆 v-if,v-else-if 就越繁琐,时刻记得要优雅,这时候就该动态组件上场了。
<template> <div> <button v-for="type in types" :key="type" @click="currentType = type"> {{ type }} </button> <component :is="currentComponent"></component> </div> </template> <script> // 组件不多的情况下手动引入即可 const context = require.context('./', false, /\.vue$/) let components = {} context.keys().forEach((key) => { if (key.includes('index')) return const component = context(key).default components[component.name] = component }) export default { components, data() { return { typeComponents: components, types: {}, currentType: '', } }, computed: { currentComponent() { return this.typeComponents[this.currentType] }, }, created() { this.types = Object.keys(this.typeComponents) this.currentType = this.types[0] // 默认第一个选项 }, } </script>五、 mixins ,更高效的实现组件内容的复用
mixins 是 Vue 提供的一种混合混入机制,可以更高效的实现组件内容的复用。
场景示例
在h5页面开发中,安卓端可以通过app提供的api实现像IOS那样,软键盘弹起时不遮挡底部内容,但是并不是任何页面都适用这种情况,所以就可以在需要的页面使用这个功能,并在离开这个页面的时候,关闭这个功能。
但是如果在每个需要的组件里面都去实现一段监听代码,代码重复太多了,此时就可以使用混入来解决这个问题
// 混入代码 upward-mixins.js 简例 export default { beforeCreate() { // ...省略 this.$util.needUpwardOnAndroid(true) }, beforeDestroy() { // ...省略 this.$util.needUpwardOnAndroid(false) } }在需要使用这种功能的组件中使用即可
<template> <div></div> </template> <script> import upwardMixins from '../mixins/upward-mixins' export default { // mixins 传入一个数组,可以有多个混入对象 mixins: [upwardMixins], // ...省略 } </script>混入规则
1、在 Vue 中,一个混入对象可以包含任意组件选项,但是对于不同的组件选项,会有不同的使用策略。
data 选项,在混入时会进行递归合并,如果两个属性发生冲突,则以组件本身为主
const mixin1 = { data() { return { a: 1, b: 2 } } } export default { mixins: [mixin1], data() { return { a: 2 } }, created() { console.log(this.a, this.b) // 输出 2,2 } }2、生命周期钩子函数
对于生命周期钩子函数,混入时会依次执行。混入对象里面的钩子函数会优先于组件的钩子函数执行。如果一个组件混入了多个对象,对于混入对象里面的同名钩子函数,将按照数组顺序依次执行:
const mixin1 = { created() { console.log('第一个输出') } } const mixin2 = { created() { console.log('第二个输出') } } export default { mixins: [mixin1, mixin2], created() { console.log('第三个输出') } }3、其他选项,对于值为对象的选项,如methods,components,filter,directives,props 等等,会被合并为同一个对象。两个对象键名冲突时,取组件本身对象的键值对。
全局混入
混入也可以进行全局注册。一旦使用全局混入,那么混入的选项将在所有的组件内生效,代码简例:
// 仅作简单的示例 const someMixin = { // ...一些内容 } Vue.mixin(someMixin)请谨慎使用全局混入,因为它会影响每个单独创建的 Vue 实例 (包括第三方组件)。
文章来源:https://blog.csdn.net/AiHuanhuan110/article/details/89155643