不使用Node/Express时如何从node_modules导入Bootstrap JS文件
如何在不使用Node/Express的前提下,从node_modules/目录向JS文件中导入Bootstrap?
我在Yarn monorepo项目中安装了Bootstrap5,不希望使用CDN资源,认为将完整Bootstrap包放在node_modules/目录下更便于结合SASS做灵活配置。安装依赖命令如下:
yarn add bootstrap @popperjs/core
我已经完成Bootstrap scss文件的编译,并将编译产物和自定义样式表合并,编写了如下HTML代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Company</title> <link rel="stylesheet" href="css/styles.css" /> </head> <body> <nav class="navbar navbar-expand-lg navbar-dark bg-primary"> <div class="container-fluid"> <a class="navbar-brand" href="#"> <img src="../assets/images/logo/logo-transparent.png" alt="Company" width="40" height="40" /> </a> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation" > <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarNav"> <div class="navbar-nav"> <a class="nav-link active" aria-current="page" href="#">Home</a> <a class="nav-link" href="#">About</a> <a class="nav-link" href="#">Careers</a> <a class="nav-link" href="#">Contact</a> </div> </div> </div> </nav> </body> </html>
如何不使用CDN引入Bootstrap JS模块?
Bootstrap官方文档给出的全量JS引入方式为CDN引入,代码如下:
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-pprn3073KE6tl6bjs2QrFaJGz5/SUsLqktiwsUTF55Jfv3qYSDhgCecCxMW52nD2" crossorigin="anonymous" ></script>
由于我已经通过Yarn将Bootstrap安装到项目中(用于自定义样式),我原本计划创建自定义main.js文件从node_modules导入全量Bootstrap JS:
- index.html中添加引入代码:
<script type="module" src="js/main.js"></script>
- js/main.js中编写导入代码:
import "bootstrap/dist/js/bootstrap.min.js"; // Just import everything from bootstrap
但上述写法触发了如下报错:
Uncaught TypeError: Failed to resolve module specifier "bootstrap". Relative references must start with either "/", "./", or "../".
如果我直接在index.html中写入如下引入代码:
<script src="../../../node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
功能可以正常运行,但我不希望使用../../../node_modules/这类冗长的多层相对路径写法,该如何妥善处理这个问题?
注:我的项目未使用node/express,仅使用HTML、JavaScript和SCSS开发。
解决方案
浏览器原生ES模块不识别bootstrap这类裸模块标识符,只支持/、./、../开头的路径,不想写冗长相对路径的话,选下面任意一种方案即可,都不需要额外搭建Node/Express服务:
方案1:原生Import Maps(零工具成本,最推荐)
所有现代浏览器均原生支持Import Maps特性,可直接给裸模块配置路径映射,完全不影响现有开发流程。在HTML中引入自定义JS之前,加一段映射配置即可:<!-- 路径从站点根目录/开头,不需要写多层../ --> <script type="importmap"> { "imports": { "bootstrap": "/node_modules/bootstrap/dist/js/bootstrap.esm.min.js", "@popperjs/core": "/node_modules/@popperjs/core/dist/esm/index.js" } } </script> <script type="module" src="js/main.js"></script>配置完成后
main.js里的导入代码不需要修改,注意要引入esm版本的Bootstrap JS,不要用UMD格式的bootstrap.min.js,前者原生支持ES模块规范,会自动按映射加载Popper依赖,不会报路径错误。方案2:构建时复制依赖到自有静态目录
你已经有SCSS编译流程,只需要加一步文件复制逻辑,把需要的Bootstrap JS文件拷贝到自己项目的js/vendor这类静态目录下,后续直接引用自有目录的文件即可,完全不需要碰node_modules路径。
可以直接在package.json里加个简单脚本,不需要额外安装工具:{ "scripts": { "build:css": "sass scss/styles.scss css/styles.css", "build:vendor": "mkdir -p js/vendor && cp node_modules/bootstrap/dist/js/bootstrap.bundle.min.js js/vendor/", "build": "yarn build:css && yarn build:vendor" } }构建完成后HTML里直接写短路径引入即可:
<script src="js/vendor/bootstrap.bundle.min.js"></script>这个方案的额外好处是部署时不需要上传整个node_modules目录,只需要传拷贝出来的静态资源,路径稳定,移动HTML文件位置也不会导致路径失效。
方案3:复用现有构建工具的模块解析能力
如果你是用Vite、Parcel这类工具来编译SCSS,这类工具本身自带node_modules裸模块解析能力,不需要额外配置,直接在JS里写import 'bootstrap'就能自动定位到依赖文件,构建时还会自动把依赖打包到输出的JS文件中,连单独引入vendor文件的步骤都能省掉。
你之前写多层相对路径能正常运行,本质是直接加载了UMD格式的Bootstrap文件,它会自动把API挂到全局window对象上,不需要走ES模块解析逻辑,但这种写法维护性差,一旦调整HTML文件的目录层级路径就会失效,不建议长期使用。
内容的提问来源于stack exchange,提问作者Raul

