一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情。
组件化和模块化思维在我们大前端的发展过程中已经是不可或缺的存在,在我们自己封装一下组件的时候需要频繁的引入和注册 当组件和模块越来越多的时候,长期的维护频繁的改动需要大量的精力,这时候充分利用好webpack的Tree-shaking和自动化代码来实现更便捷和更灵活的组件注册方案,同时又能兼顾性能。 废话不多说了,直接上源码:喜欢的话点个小手手吧
先上一波基础介绍
组件注册
组件名
在注册一个组件的时候,我们始终需要给它一个名字。比如在全局注册的时候我们已经看到了:
const app = Vue.createApp({...})
app.component('my-component-name', {
/* ... */
})
复制代码
该组件名就是 app.component
的第一个参数,在上面的例子中,组件的名称是“my-component-name”。
对组件的命名可能与你打算在哪使用它有关。当直接在 DOM 中 (而不是在字符串模板或单文件组件中) 使用一个组件的时候,我们强烈推荐遵循 W3C 规范来给自定义标签命名:
- 全部小写
- 包含连字符 (及:即有多个单词与连字符符号连接)
这样会帮助我们避免与当前以及未来的 HTML 元素发生冲突。
你可以在风格指南中查阅到关于组件名的其它建议。
#组件名大小写
在字符串模板或单文件组件中定义组件时,定义组件名的方式有两种:
#使用 kebab-case
app.component('my-component-name', {
/* ... */
})
复制代码
当使用 kebab-case (短横线分隔命名) 定义一个组件时,你在引用这个自定义元素时也必须使用 kebab-case,例如 <my-component-name>
。
#使用 PascalCase
app.component('MyComponentName', {
/* ... */
})
复制代码
当使用 PascalCase (首字母大写命名) 定义一个组件时,你在引用这个自定义元素时两种命名法都可以使用。也就是说 <my-component-name>
和 <MyComponentName>
都是可接受的。注意,尽管如此,直接在 DOM (即非字符串的模板) 中使用时只有 kebab-case 是有效的。
自动化注册
-src
--components
---CommonComponents
----autpModules
---index.js
首先通过require的context方法匹配基础组件文件
const requireComponent = require.context(
// 其组件目录的相对路径
"../components",
// 是否查询其子目录
true,
// 匹配基础组件文件名的正则表达式
// /V[A-Z]\w+\.(vue|js)$/
/\.(vue)$/,
// 配置是否异步加载
"sync"
);
复制代码
然后通过requireComponent.keys().forEach方法获取所有components文件夹下的公共组件
requireComponent.keys().forEach((fileName) => {
// 获取组件配置
const componentConfig = requireComponent(fileName);
});
});
复制代码
然后通过条件判断来获取组件对象和相关配置
此处判断有两个条件,满足任一都可:
- 读取组件配置中是否开启自动化注册
- 在默认自动注册的文件夹下符合校验条件的相关文件
if (componentConfig.default.isAutoRegister ||
(componentConfig.default.__file.indexOf(dirpath) != -1 && new RegExp(/\.(vue|js)$/).test(componentConfig.default.__file)) ) {
...
}
复制代码
获取组件的 PascalCase 命名
const componentName = upperFirst()
复制代码
获取和目录深度无关的文件名
camelCase(
// 获取和目录深度无关的文件名
fileName
.split("/")
.pop()
.replace(/\.\w+$/, "")
)
复制代码
全局注册组件
如果这个组件选项是通过 export default
导出的,
那么就会优先使用 .default
,
否则回退到使用模块的根。
Vue.component(
componentName,
componentConfig.default || componentConfig
);
复制代码
main.js自动化注册插件模块
// main.js
const setUseModule = (moduleName) => {
if (moduleName.constructor === Object) {
// 遍历注册第三方组件
for (let key in moduleName) {
if (Object.hasOwnProperty.call(moduleName, key)) {
let element = moduleName[key];
app.use(element);
}
}
} else {
app.use(moduleName);
}
};
复制代码
然后在main.js中注册
// main.js
import component from "./component-expand";
...
setUseModule(component);
复制代码
接下来上源码
<template>
<div id="KayCard">
<el-card class="box-card" :body-style="bodyStyle" :style="cardStyle">
<slot></slot>
</el-card>
</div>
</template>
<script>
import {
reactive,
toRefs,
onBeforeMount,
onMounted,
getCurrentInstance,
} from "vue";
export default {
// isAutoRegister: true, // 是否开启组件自动注册(全局);在componts文件夹下开发组件使用
// isVuex: true, // 是否开启store动态导入
// fitVuexModuleName: 'KayCard', // 配置所需要的store模块按需动态导入
name: "KayCard",
components: {},
props: {
bodyStyle: {
// 自定义卡片body样式{ padding: '20px' }
type: Object,
default: () => {
return {};
},
},
cardStyle: {
// 自定义卡片body样式{ padding: '20px' }
type: Object,
default: () => {
return {};
},
},
},
setup() {
console.log("1-开始创建组件-setup");
const thisVue = getCurrentInstance().proxy; // 获取当前组件实例
const data = reactive({
// 定义响应式数据
});
const methods = {
// 定义方法
};
const conputeds = {
// 定义计算属性
};
onBeforeMount(() => {
console.log("2.组件挂载页面之前执行----onBeforeMount", thisVue);
});
onMounted(() => {
console.log("3.-组件挂载到页面之后执行-------onMounted");
});
return {
...toRefs(data),
...methods,
...conputeds,
};
},
};
</script>
<style scoped lang="scss">
#KayCard {
.box-card:hover {
--el-box-shadow-light: 0 10px 12px 5px rgba(0, 0, 0, 0.1);
}
}
</style>
复制代码
// components/index.js
// 此autoRegisterDirectory配置需要自动化注册组件的目录
export const autoRegisterDirectory = ["/CommonComponents/autoModules"];
复制代码
// component-expand.js
import upperFirst from "lodash/upperFirst";
import camelCase from "lodash/camelCase";
import { autoRegisterDirectory } from "../components"; // 用来控制自动化注册的文件夹路径
// 此文件配置公共基础组件自动化注册
export default {
install(Vue) {
const requireComponent = require.context(
// 其组件目录的相对路径
"../components",
// 是否查询其子目录
true,
// 匹配基础组件文件名的正则表达式
// /V[A-Z]\w+\.(vue|js)$/
/\.(vue)$/,
// 配置是否异步加载
"sync"
);
requireComponent.keys().forEach((fileName) => {
// 获取组件配置
const componentConfig = requireComponent(fileName);
autoRegisterDirectory.forEach((dirpath) => {
if (
componentConfig.default.isAutoRegister ||
(componentConfig.default.__file.indexOf(dirpath) != -1 &&
new RegExp(/\.(vue|js)$/).test(componentConfig.default.__file))
) {
// 获取组件的 PascalCase 命名
const componentName = upperFirst(
camelCase(
// 获取和目录深度无关的文件名
fileName
.split("/")
.pop()
.replace(/\.\w+$/, "")
)
);
// 全局注册组件
Vue.component(
componentName,
// 如果这个组件选项是通过 `export default` 导出的,
// 那么就会优先使用 `.default`,
// 否则回退到使用模块的根。
componentConfig.default || componentConfig
);
}
});
});
},
};
复制代码