前言
本文是个人学习实践过程中的记录及理解,如有错漏欢迎指出。
相关知识来源于
哲玄前端(抖音ID:44622831736)大前端全栈实践课程
目标
使用方引入 npm 包后,调用暴露出去的 API 快速启动项目,将本地文件一并加载到该系统中运行。
要点
Loader 解析异常问题
npm 包提供了一些内置的 Loader,由于 Webpack 默认会在本地项目根目录找 loader,由于本地目录中不一定安装了该 Loader 导致报错,所以我们应该 告诉 Webpack 如何找到这些 Loader。
这里可以有 两种方式:
-
使用 Node.js 的原生方法
require.resolve()。require查找的起点是当前执行代码的文件所在目录 (即__dirname),从而找到npm包中相关的 Loader,找到后会返回该loader的绝对路径。Webpack拿到这个绝对路径后,就不会再去本地的 node_modules 里瞎找,直接加载该路径文件。 -
在
webpack.config.js中修改resolveLoader配置。提供数组告诉Webpack有哪些兜底策略,相比于前一种方式更方便,一劳永逸,但解析顺序取决于数组中定义的顺序。resolveLoader: { modules: [ 'node_modules', path.resolve(__dirname, 'node_modules') // npm 包中的 node_modules 做兜底 ] }
资源加载问题
由于该包中暴露的 API 运行时会将本地项目文件一起打包到系统中运行,而本地项目或许涉及到某些依赖未安装的问题。为了解决这种情况的发生,可以通过 Webpack 配置一份别名供用户使用,同时也是告诉 Webpack 从哪里获取依赖,也能避免重复安装依赖。
如 vue:
resolve: {
alias: {
vue: require.resolve("vue"),
moment: require.resolve("moment")
},
},
需要注意的是,为了避免直接写死 vue 始终指向自身 node_modules 目录,路径应该先用 动态探测 方式查看。
// 获取本地项目里的 Vue 路径
const localProjectVuePath = path.resolve(process.cwd(), 'node_modules/vue');
// 动态判断应该用哪个 Vue
const vuePathToUse = fs.existsSync(localProjectVuePath)
? localProjectVuePath // 优先使用宿主项目的 Vue
: require.resolve('vue'); // 兜底:使用 npm 包自身的 Vue
文件拓展
借助前文提到的文件解析内核 elpis-core ( 类 egg 理念,约定大于配置 ) 的能力,将约定位置的文件全部解析挂在到系统上,这能轻松解决大部分系统文件在本地完成拓展的问题。
可是,当涉及到"识别前端文件并提供给 npm 包中的其他文件使用" 的情况,光靠内核是无法实现的,还需要借助 Webpack 的能力。
这会有三种方式可以实现:
-
约定固定位置放置:告诉
npm包使用方,如需为前端组件、页面添加拓展文件,固定放置在某种格式的文件列表中(如app/pages/dashboard/components/[componentName]),以供系统读取。const businessModules = require.context( "$businessPath/app/pages", // $businessPath 已在 webpack 中添加别名 process.cwd() false, /.js$/, ); -
约定统一
config文件:与上一中方法不同的是,约定放置的不再是每个文件固定的位置,而是通过约定位置的config文件告诉运行方:“需要拓展的文件放置在哪里”。const businessModules = require.context( "$businessPath/app/pages", // $businessPath 已在 webpack 中添加别名 process.cwd() false, /page.config.js$/, );以便系统直接读取文件,而不再需要为了添加一个文件嵌套过多的文件夹,将文件放置的格式交还给使用方决定。
-
暴露拓展 API:类似于
app.use的实现,在系统启动前通过暴露给使用方的 API 添加拓展文件。对于使用方而言,更加方便快捷,但需要更多的前置操作,每个有可能拓展的位置都需留下钩子。