The WordPress coreCoreCore is the set of software required to run WordPress. The Core Development Team builds WordPress. development team builds WordPress! Follow this site for general updates, status reports, and the occasional code debate. There’s lots of ways to contribute:
Found a bugbugA bug is an error or unexpected result. Performance improvements, code optimization, and are considered enhancements, not defects. After feature freeze, only bugs are dealt with, with regressions (adverse changes from the previous version) being the highest priority.?Create a ticket in the bug tracker.
A new filterFilterFilters are one of the two types of Hooks https://codex.wordpress.org/Plugin_API/Hooks. They provide a way for functions to modify data of other functions. They are the counterpart to Actions. Unlike Actions, filters are meant to work in an isolated manner, and should never have side effects such as affecting global variables and output. runs for each Script Module that is enqueued or in the dependency graph. This filter allows arbitrary data to be associated with a given Script Module and added to the rendered page.
Script Module data is embedded in the page as JSONJSONJSON, or JavaScript Object Notation, is a minimal, readable format for structuring data. It is used primarily to transmit data between a server and web application, as an alternative to XML. in a <script type="application/json">tagtagA directory in Subversion. WordPress uses tags to store a single snapshot of a version (3.6, 3.6.1, etc.), the common convention of tags in version control systems. (Not to be confused with post tags.).
Script Modules are responsible for reading the data and performing their own initialization.
This is a proposal. Everything in this post is open to feedback and discussion. Please, share your thoughts and ideas.
The feedback period will end 2024-05-24 (Friday, May 24). Please provide any feedback before then.
This post will use “scripts” to refer to WP Scripts and “modules” to refer to WP Script Modules.
Scripts or modules?
Most JavaScriptJavaScriptJavaScript or JS is an object-oriented computer programming language commonly used to create interactive effects within web browsers. WordPress makes extensive use of JS for a better user experience. While PHP is executed on the server, JS executes within a user’s browser. https://www.javascript.com/. for WordPress is probably using scriptsunless it was specifically compiled as a module. Modern JavaScript is often authored using import apiFetch from '@wordpress/api-fetch', which is module syntax. Until very recently, WordPress build tooling has always removed the module syntax and compiled a script.
Only the most recent WordPress version 6.5 introduced support for modules. WordPress CoreCoreCore is the set of software required to run WordPress. The Core Development Team builds WordPress. includes exactly two modules to expose functionality at this time: @wordpress/interactivity and @wordpress/interactivity-router. JavaScript that uses the Interactivity APIAPIAn API or Application Programming Interface is a software intermediary that allows programs to interact with each other and share data in limited, clearly defined ways. is probably a module.
This post will talk about a common script dependency, wp-api-fetch. The @wordpress/api-fetchmodule mentioned in this post is hypothetical; it does not exist at this time.
A note about modules
It’s important to know a few things about scripts versus modules in the context of this post. A few key differences:
Scripts and their dependencies will be executed as the page is parsed even if a dependency is never directly used.
Script “exports” are attached to the window object, e.g. wp-api-fetch is available as window.wp.apiFetch.
Modules will execute after the page has finished parsing.
Module dependencies can be loaded on demand.
Modules have true encapsulation, using import and export to share values.
More about scripts and modules…
Scripts that are either enqueued or are dependencies of enqueued scripts will be added to the page as script tags, like <script src="path/to/script.js">. The browser will fetch and execute these scripts before it continues to parse the page. There are attributes like async and defer that can change how the browser executes scripts.
Modules that are enqueued will appear on the page as script tags of type module, like <script src="path/to/module.js" type="module">. Processing of modules is deferred, they will be executed after the whole document has been parsed. The async attribute will cause a module to execute in parallel as the document is parsed. WordPress Script Modules do not use async at this time.
Modules that are in the dependency graph of enqueued modules will appear in an importmap. This is a mapping of names to URLs so that a browser knows what module to fetch when it sees an import. It’s what associates the module used in a statement like import "a-module"; with a URLURLA specific web address of a website or web page on the Internet, such as a website’s URL www.wordpress.org{ "imports": { "a-module": "path/to/a-module.js" } }.
The problem
Scripts often require some initialization or other data to work correctly in WordPress. The wp-api-fetch script is a good example, it requires a significant amount of configuration. For example, Core uses the following inline script to initialize wp-api-fetch so it can send requests to the appropriate place:
This snippet uses PHPPHPThe web scripting language in which WordPress is primarily architected. WordPress requires PHP 5.6.20 or higher to create a string of JavaScript code with some data from PHP embedded. This will be included in a script tag that appears immediately after the script tag for wp-api-fetch, something like this:
<script src="path/to/wp-api-fetch.js"></script>
<script>
// The JavaScript code is printed here:
wp.apiFetch.use( wp.apiFetch.createRootURLMiddleware( "…/index.php?rest_route=/" ) );
// More code may follow from other calls to `wp_add_inline_script`…
</script>
Notice that the JavaScript depends on wp-api-fetch, it imperatively calls wp.apiFetch.use(…). That same approach with a hypothetical @wordpress/api-fetch module would look something like this:
In this approach, the initialization module would fetch and execute the @wordpress/api-fetch module just to initialize it! That eliminates one of the important advantages of modules, their ability to load on-demand. 🤔 It looks like this imperative initialization isn’t a good fit for modules. Let’s see if there’s a better solution…
The proposal
Here are some requirements that arise from the naive implementation:
Modules should be fetched and initialized on demand.
Modules should be responsible for their own initialization when they’re executed.
Variables should be scoped to modules and not pollute the global namespace.
The data should introduce minimal overhead.
Add server data via filters
Filters provide a nice method to collect the data needed by modules. The WP_Script_Modules class introduces a new filter that runs for each module that is enqueued or present in the dependency graph. Adding or modifying data for a module looks like this:
Multiple filters can be added to add or modify the data exposed to the script, and if no data is added, nothing will be serialized on the page for the client. It’s also worth mentioning that no JavaScript code is written in PHP, a nice improvement over wp_add_inline_script which requires valid JavaScript to be added.
A drawback to this approach is that all the data passed must pass through JSON. Data without a valid JSON representation is not supported by default.
Use an inert script tag to expose data on the client
The data is embedded it in the HTMLHTMLHyperText Markup Language. The semantic scripting language primarily used for outputting content in web browsers. in a script tag:
<script id="scriptmoduledata_@wordpress/api-fetch" type="application/json">
{ "theData": "JSON Serializable data can be shared" }
</script>
Because modules are always deferred, it should be safe to print these script tags at the bottom of the page. They should have limited impact on page load because the browser will not parse or execute the contents.
Modules read data from the script tag
This script tag doesn’t do anything on its own. The module is responsible for getting the data and performing its initialization when it executes:
if ( typeof document !== 'undefined' ) {
const serializedData = document.getElementById(
'scriptmoduledata_@wordpress/api-fetch'
)?.textContent;
if ( serializedData ) {
let config = null;
try {
config = JSON.parse( serializedData );
} catch {
// there was a problem parsing the serialized data
}
performInitialization( config );
}
}
function performInitialization( config ) {
if ( config?.rootURL ) {
registerMiddleware( createRootURLMiddleware( config.rootURL ) );
}
// etc.
}
I’ve prototyped this proposal in the following PRs:
WordPress-develop PR 6433 applies the filters and adds the necessary filters to expose data to @wordpress/api-fetch. The proposal in this post is contained in this PR.
Gutenberg PR 60952 builds and registers the @wordpress/api-fetch module. This PR is helpful for testing, but is beyond the scope of this post.
Try it in the WordPress Playground here. If you run the following JavaScript in the inspector console —make sure you pick the JavaScript context, something like wp (scope:abc123)— you’ll see the @wordpress/api-fetch module log some initialization when it’s imported and then work as expected:
You must be logged in to post a comment.