Webpack - 前端自动化导入

一、require.context

在阅读vue-element-admin项目过程中,作者采用如下操作,引入了全局使用到的svg矢量图

const req = require.context('./svg', false, /\.svg$/)
const requireAll = requireContext => requireContext.keys().map(requireContext)
requireAll(req)

心中不免有些疑惑,短短两行即可实现上百文件的导入,其作用原理是什么呢?

不难看出关键在于require.context这个对象

查阅官方文档可知,这个对象必须使用了 webpack构建项目 (或在内部使用了 webpack 的 Vue CLI 3+),那么就可以使用 require.context

分析一下require.context这个函数

require.context(
  directory: String,
  includeSubdirs: Boolean /* 可选的,默认值是 true */,
  filter: RegExp /* 可选的,默认值是 /^\.\/.*$/,所有文件 */,
  mode: String  /* 可选的, 'sync' | 'eager' | 'weak' | 'lazy' | 'lazy-once',默认值是 'sync' */
)

这个函数一共接受四个参数

  1. directory 导入文件路径 string 类型
  2. includeSubdirs 是否递归子文件夹 boolean类型
  3. filter filter过滤 传 正则表达式
  4. mode 模式 有以下四种模式 'sync' | 'eager' | 'weak' | 'lazy' | 'lazy-once'

代码中require.context返回一个req,将其在console输出


webpackContext.png

返回值是一个名为webpackContext的函数

那么它的作用原理是什么呢?那必须通过查阅源码才可得知.

__webpack_require__.o = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop)

var map = {
    "./404.svg": "./src/icons/svg/404.svg",
    "./account.svg": "./src/icons/svg/account.svg",
    "./admin.svg": "./src/icons/svg/admin.svg",
    "./bug.svg": "./src/icons/svg/bug.svg",
};


function webpackContext(req) {
    var id = webpackContextResolve(req);
    return __webpack_require__(id);
}
function webpackContextResolve(req) {
    if(!__webpack_require__.o(map, req)) {
        var e = new Error("Cannot find module '" + req + "'");
        e.code = 'MODULE_NOT_FOUND';
        throw e;
    }
    return map[req];
}
webpackContext.keys = function webpackContextKeys() {
    return Object.keys(map);
};
webpackContext.resolve = webpackContextResolve;
module.exports = webpackContext;
webpackContext.id = 1;

这是webpack源码中md的例子,一目了然.

当调用require.context函数时,webpack模块内部会生成一个map对象,

返回值webpackContext对象上有一个属性keys,对应一个返回模块内部对象map键数组的函数

内部还做了key是否在map对象本身身上的错误处理
Object.prototype.hasOwnProperty.call(obj, prop)

在通过 _webpack_require_(原始 require 函数。这个表达式不会被解析器解析为依赖)导入

到此习得通过require.context 寥寥几行代码即可实现了过去数百行的导入

二 、Vue全局注册组件

既然require.context可用于批量导入模块,那么是否可用于批量注册全局模块呢?

答案是肯定的

Vue官方已经给出了一个很好的例子

  
// Globally register all base components for convenience, because they
// will be used very frequently. Components are registered using the
// PascalCased version of their file name.

import Vue from 'vue'

// https://webpack.js.org/guides/dependency-management/#require-context
const requireComponent = require.context(
  // Look for files in the current directory
  '.',
  // Do not look in subdirectories
  false,
  // Only include "_base-" prefixed .vue files
  /_base-[\w-]+\.vue$/
)

// For each matching file name...
requireComponent.keys().forEach((fileName) => {
  // Get the component config
  const componentConfig = requireComponent(fileName)
  // Get the PascalCase version of the component name
  const componentName = fileName
    // Remove the "./_" from the beginning
    .replace(/^\.\/_/, '')
    // Remove the file extension from the end
    .replace(/\.\w+$/, '')
    // Split up kebabs
    .split('-')
    // Upper case
    .map((kebab) => kebab.charAt(0).toUpperCase() + kebab.slice(1))
    // Concatenated
    .join('')

  // Globally register the component
  Vue.component(componentName, componentConfig.default || componentConfig)
})

只需在forEach迭代过程中,过滤map 的key,得到注册组件名
调用Vue.component注册即可

这里需要注意, .vue文件通常是通过 export default 导出的,那么就会优先使用 componentConfig.default,否则回退到使用模块的根。

并且注册组件需在Vue实例化以前.

如果文章对你有所帮助,点赞一下呗

发现错误?联系我 谢谢

你可能感兴趣的:(Webpack - 前端自动化导入)