在NextJs项目A中引入NextJs项目B模块的技术问题咨询
背景
我创建了一个作为独立模块/框架的Next.js项目B,通过Webpack将其组件/UI及CSS/SCSS文件打包为bundle.js。将其安装到Next.js项目A后,期望项目A的通用CSS样式能覆盖项目B的样式,若无则使用项目B的默认样式。目前已完成项目B的打包,但遇到两个问题:
Q1) 如何在项目A中导入项目B的组件?
操作步骤:
安装项目B
本地开发阶段,在项目A根目录执行:npm install ../path/to/projectB # 或用yarn yarn add ../path/to/projectB若已发布到npm仓库,直接安装包名:
npm install projectB-package-name配置项目B的package.json
确保main字段指向打包后的入口文件,建议同时添加module字段支持ES模块:{ "main": "./dist/bundle.js", "module": "./dist/bundle.esm.js", "files": ["dist"] // 指定发布时包含的目录 }在项目A中导入组件
直接从项目B的包名导入目标组件:// 项目A的页面/组件文件 import { Button, Card } from 'projectB-package-name'; export default function Home() { return ( <div> <Button>项目B的按钮</Button> <Card>内容区域</Card> </div> ); }样式引入与优先级控制
在项目A的pages/_app.js/_app.tsx中全局导入样式,将项目A的全局样式放在项目B样式之后,利用CSS优先级覆盖默认样式:// 项目A的pages/_app.js import 'projectB-package-name/dist/main.[contenthash].css'; import '../styles/globals.css'; // 项目A全局样式,放在后面实现覆盖 function MyApp({ Component, pageProps }) { return <Component {...pageProps} />; } export default MyApp;
Q2) 构建日志中的警告(已翻译为中文)
根据截图内容,警告为:
windowis not defined
(注:这是Next.js服务端渲染场景下的常见问题,服务端执行代码时不存在浏览器的window对象)
解决方法:
修改项目B的组件代码
避免在顶层作用域直接使用window,将依赖window的逻辑放在useEffect钩子(仅客户端执行)或添加环境判断:// 项目B的组件示例 import { useEffect, useState } from 'react'; export function ResponsiveComponent() { const [width, setWidth] = useState(0); useEffect(() => { setWidth(window.innerWidth); const handleResize = () => setWidth(window.innerWidth); window.addEventListener('resize', handleResize); return () => window.removeEventListener('resize', handleResize); }, []); return <div>窗口宽度:{width}</div>; }或者添加环境判断:
const isBrowser = typeof window !== 'undefined'; if (isBrowser) { // 执行依赖window的逻辑 }优化Webpack配置
若项目B依赖第三方库使用window,可在Webpack配置中注入全局变量:// webpack.development.config.js const webpack = require('webpack'); module.exports = { // ... 原有配置 plugins: [ // ... 原有插件 new webpack.DefinePlugin({ 'typeof window': JSON.stringify('object'), }), ], };项目A的Next.js配置调整
若项目B仅用于客户端,可在项目A的next.config.js中配置忽略服务端渲染警告:/** @type {import('next').NextConfig} */ const nextConfig = { reactStrictMode: true, webpack: (config) => { config.module.rules.push({ test: /projectB-package-name/, use: 'null-loader', // 服务端不加载项目B代码 }); return config; }, }; module.exports = nextConfig;
额外Webpack配置修复
你的webpack.development.config.js中,css-loader的options被重复定义(会导致前者被覆盖),需合并为一个对象:
// 修改前 { loader: 'css-loader', options: { publicPath: ... }, options: { modules: ... }, } // 修改后 { loader: 'css-loader', options: { publicPath: (resourcePath, context) => { return path.relative(path.dirname(resourcePath), context) + "/"; }, esModule: true, modules: { namedExport: true, localIdentName: "[name]__[contenthash]__[local]", }, importLoaders: 1, }, }
内容的提问来源于stack exchange,提问作者Kapil Panchal

