- 前端
- 构建工具
- 前端构建
- 后端
【免费下载链接】razzle
✨ Create server-rendered universal JavaScript applications with no configuration
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):
razzle与razzle-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解析完整包名:
- 如果是 scoped 包,则先尝试
${scope}/razzle-plugin-${name}; razzle-plugin-${plugin.name}(例如razzle-plugin-php);${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并关闭.babelrc:presets: [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锁定了razzle与razzle-dev-utils的4.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
相关推荐
Razzle 插件 razzle-plugin-php 实战:在 Universal React 应用中用 Babel 编译 PHP 代码
Razzle 插件 razzle plugin php 实战:在 Universal React 应用中用 Babel 编译 PHP 代码 razzle plu
前端构建工具前端构建后端在 Razzle 中通过 razzle-plugin-php 使用 babel-preset-php 编写「PHP 版」通用 JavaScript 应用
在 Razzle 中通过 razzle plugin php 使用 babel preset php 编写「PHP 版」通用 JavaScript 应用 Raz
前端构建工具前端构建后端razzle-plugin-bundle-analyzer:为 Razzle 应用接入 webpack-bundle-analyzer 包体积分析
razzle plugin bundle analyzer:为 Razzle 应用接入 webpack bundle analyzer 包体积分析 本指南围绕
前端构建工具前端构建后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考