【问题标题】:Create loader that maps URL path to a component using Webpack in Next JS在 Next JS 中使用 Webpack 创建将 URL 路径映射到组件的加载器
【发布时间】:2025-12-18 21:10:02
【问题描述】:

我想在 Next.js 中创建一个加载器,它使用 Webpack underhood 它是 next.config.js 配置文件,它将加载 Blog.js 用于 /blog 路由和 Tutorial.js 用于 /tutorial 路由。

MDX 数据位于pages/ 目录中。 pages/ 包含 blog//tutorial,它们有自己的 .mdx 文件。

我的文件夹结构如下:

.
├── README.md
├── components
│   ├── Blog.js
│   ├── Image.js
│   └── Tutorial.js
├── jsconfig.json
├── next.config.js
├── package-lock.json
├── package.json
├── pages
│   ├── blog
│   │   ├── hello-world
│   │   │   ├── Rustin_Cohle.jpg
│   │   │   └── index.mdx
│   │   └── shit-world
│   │       └── index.mdx
│   ├── blog.js
│   ├── index.js
│   ├── tutorial
│   │   └── console-log-in-javascript
│   │       └── index.mdx
│   └── tutorial.js
├── prettier.config.js
├── src
└── utils
    ├── blog
    │   ├── getAllBlogPreviews.js
    │   ├── getStaticPaths.js
    │   └── getStaticProps.js
    ├── common
    │   ├── getAllPreviews.js
    │   ├── getStaticFilePaths.js
    │   └── getStaticFileProps.js
    ├── date.js
    ├── mdxUtils.js
    └── tutorial
        ├── getAllTutorialPreviews.js
        ├── getStaticPaths.js
        └── getStaticProps.js

我的next.config.js 文件如下所示:

const { createLoader } = require('simple-functional-loader')
const rehypePrism = require('@mapbox/rehype-prism')
const withBundleAnalyzer = require('@next/bundle-analyzer')({
    enabled: process.env.ANALYZE === 'true',
})

module.exports = withBundleAnalyzer({
    pageExtensions: ['js', 'jsx', 'ts', 'tsx', 'md', 'mdx'],
    experimental: {
        modern: true,
    },
    webpack: (config, options) => {
        config.module.rules.push({
            test: /\.(svg|png|jpe?g|gif|mp4)$/i,
            use: [
                {
                    loader: 'file-loader',
                    options: {
                        publicPath: '/_next',
                        name: 'static/media/[name].[hash].[ext]',
                    },
                },
            ],
        })

        const mdx = [
            options.defaultLoaders.babel,
            {
                loader: '@mdx-js/loader',
                options: {
                    rehypePlugins: [rehypePrism],
                },
            },
        ]

        config.module.rules.push({
            test: /\.mdx$/,
            oneOf: [
                {
                    resourceQuery: /preview/,
                    use: [
                        ...mdx,
                        createLoader(function (src) {
                            console.log('src ????')
                            console.log(src)
                            console.log('src ????')
                            if (src.includes('<!--more-->')) {
                                const [preview] = src.split('<!--more-->')
                                return this.callback(null, preview)
                            }

                            const [preview] = src.split('<!--/excerpt-->')
                            return this.callback(null, preview.replace('<!--excerpt-->', ''))
                        }),
                    ],
                },
                {
                    test: /blog/,
                    use: [
                        ...mdx,
                        createLoader(function (src) {
                            const content = [
                                'import Blog from "@/components/Blog"',
                                'export { getStaticProps } from "@/utils/blog/getStaticProps"',
                                'export { getStaticPaths } from "@/utils/blog/getStaticPaths"',
                                'console.log("/blog")',
                                src,
                                'export default (props) => <Blog meta={meta} {...props} />',
                            ].join('\n')

                            if (content.includes('<!--more-->')) {
                                return this.callback(null, content.split('<!--more-->').join('\n'))
                            }

                            return this.callback(null, content.replace(/<!--excerpt-->.*<!--\/excerpt-->/s, ''))
                        }),
                    ],
                },
                {
                    test: /tutorial/,
                    use: [
                        ...mdx,
                        createLoader(function (src) {
                            const content = [
                                'import Tutorial from "@/components/Tutorial"',
                                'export { getStaticProps } from "@/utils/tutorial/getStaticProps"',
                                'export { getStaticPaths } from "@/utils/tutorial/getStaticPaths"',
                                'console.log("/tutorial")',
                                src,
                                'export default (props) => <Tutorial meta={meta} {...props} />',
                            ].join('\n')

                            if (content.includes('<!--more-->')) {
                                return this.callback(null, content.split('<!--more-->').join('\n'))
                            }

                            return this.callback(null, content.replace(/<!--excerpt-->.*<!--\/excerpt-->/s, ''))
                        }),
                    ],
                },
            ],
        })

        return config
    },
})

我正在使用test: /blog/test: /tutorial/,但它不起作用。我不确定如何让它发挥作用?

我已将我的完整代码上传到 Github 的 tailwind 分支 → https://github.com/deadcoder0904/blog-mdx-next/tree/tailwind 改编自 https://github.com/tailwindlabs/blog.tailwindcss.com

如何使blog/ 使用Blog.jstutorial/ 使用Tutorial.js

【问题讨论】:

    标签: javascript webpack next.js mdxjs


    【解决方案1】:

    我不得不使用 Webpack 的包含功能,例如:

    include: [path.resolve(__dirname, 'pages/blog')]
    

    而不是

    test: /blog/
    

    & 与 tutorial 相同。

    这是提交 → https://github.com/deadcoder0904/blog-mdx-next/commit/84ad0042de3ec229dd130ef2491511c5e3090110

    【讨论】: