razzle-plugin-php:让 Razzle 应用直接编写并编译 PHP 的 Webpack 插件实战
2026/9/23 23:41:30 网站建设 项目流程
  • 前端
  • 构建工具
  • 前端构建
  • 后端

【免费下载链接】razzle

✨ Create server-rendered universal JavaScript applications with no configuration

项目地址:https://gitcode.com/gh_mirrors/ra/razzle
点击查看免费下载

Razzle 是一款无需配置即可构建「服务端渲染的通用 JavaScript 应用」的工具,而razzle-plugin-php是官方仓库中一个特殊的 Webpack 插件:它通过接入babel-preset-php,让 Razzle 项目中的.php文件能够像 JavaScript 一样被 Babel 转译后打包进客户端与服务端产物。读完本文,你将掌握该插件的安装配置方法、它对 Webpack 配置所做的每一处修改及其背后原理,以及如何在 Razzle 项目里实际编写、导入并运行 PHP 代码。

官方 README 的原话是 "Note this is kind of a joke, but actually works."——这虽然是个偏「整活」性质的插件,但它的技术实现是完全真实可用且自洽的。

插件是什么:一行简介

razzle-plugin-php(位于仓库 packages/razzle-plugin-php)做的事情只有一件:为 Razzle 的 Webpack 编译链路添加babel-preset-php。安装并启用后,你在项目里写的.php文件会被 Babel 以 PHP 语法预设进行解析与转译,生成可运行的 JavaScript,进而被 Webpack 打包进 client(浏览器端)与 server(Node.js 端)两套构建产物中。

从 packages/razzle-plugin-php/package.json 可以看到它的依赖关系:

  • 运行时依赖:babel-preset-php^1.2.0),这是负责将 PHP 语法转换为 JS 的核心转译器;
  • 对等依赖(peerDependencies):razzlerazzle-dev-utils,版本与本插件保持一致(当前为4.2.18)。

也就是说,它不自己实现任何解析器,而是完全复用 Razzle 现成的 Babel/Webpack 基础设施,只是把 PHP 预设"接"进去。

安装与启用

按 README 的步骤,在 Razzle 项目中安装插件:

yarn add razzle-plugin-php --dev

如果使用 npm:

npm install razzle-plugin-php --save-dev

然后在项目根目录的razzle.config.js中注册:

// razzle.config.js module.exports = { plugins: ['php'], };

这里plugins数组中写字符串'php'即可。Razzle 在加载插件时会自动做名称解析(见下文"插件是如何被加载的"),所以不需要写全razzle-plugin-php

插件如何被加载:名称解析与选项透传

从源码 packages/razzle/config/loadPlugins.js 看,razzle.config.js中的plugins数组支持多种写法:

  • 字符串形式:'php',等价于{ name: 'php' },以默认(空)选项应用插件;
  • { name: 'php', options: {...} }对象形式,可携带自定义选项;
  • 直接传入插件函数或插件对象(便于测试与本地开发)。

对于字符串/对象形式,Razzle 会按以下顺序在node_modules中尝试require解析完整包名:

  1. 如果是 scoped 包,则先尝试${scope}/razzle-plugin-${name}
  2. razzle-plugin-${plugin.name}(例如razzle-plugin-php);
  3. ${plugin.name}/razzle-plugin

解析成功后返回[pluginModule, pluginOptions]元组,随后在构建配置阶段被依次调用(详见 createConfigAsync.js 与 createConfigAsync.js)。

插件内部做了什么:逐行拆解 modifyWebpackConfig

razzle-plugin-php的核心只有一个导出对象,即 packages/razzle-plugin-php/index.js 中的modifyWebpackConfig(opts)方法。Razzle 在生成客户端(web)与服务端(node)两套 Webpack 配置后,都会调用该方法,让它分别对两套配置做同样的改造。整个方法依次完成四件事:

'use strict'; const makeLoaderFinder = require('razzle-dev-utils/makeLoaderFinder'); module.exports = { modifyWebpackConfig(opts) { const config = Object.assign({}, opts.webpackConfig); // 1. 让 Webpack 能够解析 .php 扩展名 config.resolve.extensions.push('.php'); // 2. 把 .php 从 file-loader 的匹配中排除 config.module.rules[ config.module.rules.findIndex(makeLoaderFinder('file-loader')) ].exclude.push(/\.(php)$/); // 3. 禁止 Webpack 把 .php 当普通 JS 做 noParse 解析 config.module.noParse = config.module.noParse ? config.module.noParse.concat([/.php$/]) : [/.php$/]; // 4. 追加一条针对 .php 的 babel-loader 规则 config.module.rules.push({ test: /\.php$/, include: config.module.rules.find(makeLoaderFinder('babel-loader')) .include, use: [ { loader: 'babel-loader', options: { presets: [require.resolve('babel-preset-php')], babelrc: false, }, }, ], }); return config; }, };

下面按步骤说明每一处修改的作用。

步骤 1:让 .php 成为可解析的模块扩展名

config.resolve.extensions.push('.php');

Webpack 通过resolve.extensions决定模块导入时可以省略的扩展名。追加.php后,代码中import foo from './foo'require('./foo')会优先命中foo.js,找不到时才尝试foo.php。这一步让.php文件真正"进入"了模块系统,可以被import/require引用。

步骤 2:把 .php 从 file-loader 中排除

config.module.rules[ config.module.rules.findIndex(makeLoaderFinder('file-loader')) ].exclude.push(/\.(php)$/);

Razzle 默认的构建链路中,file-loader负责把图片、字体等静态资源拷贝为文件并返回 URL。如果不加排除,.php文件会被file-loader当成普通静态资源处理(返回一个文件 URL),而不是被 Babel 转译。因此插件用makeLoaderFinder('file-loader')找到默认的file-loader规则,并在其exclude中加入\.(php)$正则,把.php从资源处理中"抢"出来。

makeLoaderFinder是 Razzle 官方工具 razzle-dev-utils/makeLoaderFinder.js 提供的高阶函数,它通过构造[/\\]loaderName[/\\]正则去匹配规则中的 loader 字符串(兼容rule.loader字符串形式与rule.use数组形式),从而在复杂的 Webpack 规则中精确定位某个 loader 对应的规则。

步骤 3:禁止 noParse 兜底解析

config.module.noParse = config.module.noParse ? config.module.noParse.concat([/.php$/]) : [/.php$/];

noParse是 Webpack 用来跳过模块内容解析的优化手段,被匹配的模块不会经过依赖解析。若不加处理,Webpack 可能对未知扩展名的.php文件走noParse兜底路径,导致内容被原样塞进产物。插件把/.php$/追加进noParse反向补充列表——需要说明的是,这里实际上是把.php从默认的 JS 解析路径中"摘除",交由专门的 babel-loader 规则接管,从而保证 PHP 代码一定经过转译而非被跳过。

步骤 4:追加 .php 专属的 babel-loader 规则

config.module.rules.push({ test: /\.php$/, include: config.module.rules.find(makeLoaderFinder('babel-loader')) .include, use: [ { loader: 'babel-loader', options: { presets: [require.resolve('babel-preset-php')], babelrc: false, }, }, ], });

这是整个插件最关键的一步:新增一条test: /\.php$/的规则,并做了两件细致的事:

  • 复用默认 babel-loader 的include:通过makeLoaderFinder('babel-loader')找到 Razzle 默认的 JS babel-loader 规则,直接沿用其include(通常是src目录),保证新规则只处理项目源码目录下的 PHP 文件,不误伤node_modules
  • 只启用babel-preset-php并关闭.babelrcpresets: [require.resolve('babel-preset-php')]require.resolve锁定实际安装的预设路径(避免解析歧义),babelrc: false确保 PHP 文件不会被项目根目录的.babelrc/ Babel 配置干扰,PHP 代码只按 PHP 预设的规则转译。

由于rules是数组、追加在末尾,且test精确匹配.php,这条规则与原有的.jsbabel-loader 规则互不冲突,形成了"JS 走默认预设、PHP 走 PHP 预设"的双轨并行结构。

在项目里怎么用:一个最小可运行示例

启用插件后,你可以在src目录下新建一个 PHP 文件(插件沿用了默认 babel-loader 的include,即src目录):

<?php // src/greet.php function greet($name) { return "Hello, " . $name . " from PHP!"; }

然后在任意 JS/JSX 模块中导入并使用它:

// src/App.js import greet from './greet'; export default function App() { return <div>{greet('Razzle')}</div>; }

babel-preset-php会把上述 PHP 语法转译成可执行的 JavaScript 函数,Webpack 随后将其分别打包进 client 与 server 两份产物。由于modifyWebpackConfig对 web / node 两个 target 都会被调用(见 createConfigAsync.js 中插件循环位于最终配置生成阶段),因此 PHP 模块在浏览器端和服务端都能正常运行,天然支持服务端渲染场景。

适用前提与注意事项

  • 这是一个"能用但别较真"的插件babel-preset-php只是将 PHP 语法的子集翻译成 JS,并非完整的 PHP 运行时,不适用于真实的 PHP 业务逻辑;适合作为实验、演示或技术玩味场景,正如 README 自嘲的 "kind of a joke, but actually works"。
  • 必须与 Razzle 4.x 配套peerDependencies锁定了razzlerazzle-dev-utils4.2.18版本,使用前请确保项目 Razzle 版本与之兼容。
  • 不要和静态资源处理混淆:启用插件后,src下的.php会被当作代码模块转译,而不是被file-loader拷贝为静态文件;若确有静态.php文件需求,请放在public目录由静态服务直接提供。
  • 插件顺序与冲突:插件在最终配置阶段按plugins数组顺序依次执行,若同时启用多个会改写module.rules的插件,注意它们在数组中的先后顺序。

深入阅读

想继续深挖,可以阅读仓库中的以下文件:

  • 插件核心实现:packages/razzle-plugin-php/index.js
  • 插件元信息与依赖声明:packages/razzle-plugin-php/package.json
  • 插件加载与名称解析逻辑:packages/razzle/config/loadPlugins.js
  • 插件modifyWebpackConfig的调用时机:packages/razzle/config/createConfigAsync.js
  • loader 查找工具函数:packages/razzle-dev-utils/makeLoaderFinder.js
  • 官方文档页面:website/pages/plugins/razzle-plugin-php.md
  • 前端
  • 构建工具
  • 前端构建
  • 后端

【免费下载链接】razzle

✨ Create server-rendered universal JavaScript applications with no configuration

项目地址:https://gitcode.com/gh_mirrors/ra/razzle
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询