phpwordpressvite

How to properly create wp enqueue and functions script to run vite frontend


I am running twenty-twentythreetheme of WordPress at the moment I have created a default vite Vue project inside my themes folder. I have not found much tutorials online that match my use case.

I want to run the vite frontend on my WordPress website. The following is my enqueue script and functions.php

Functions.php

    <?php
/**
 * Vue Twenty Seventeen Child Theme Functions and Definitions.
 * Requires Twenty Seventeen and only works in WordPress 4.7 or later.
 *
 * @package WordPress
 */

 // includes for the callbacks.
include_once( get_stylesheet_directory() . '/enqueue-scripts.php' );

/* hooks and filters */

?>

enqueue-scripts.php


function enqueue_vue_assets() {
 wp_enqueue_script( 'app', get_template_directory_uri() . '/example/dist/assets/index-ec461b82.js', array(), '1.0', true );
 wp_enqueue_style( 'app', get_template_directory_uri() . '/example/dist/assets/index-fc5f319f.css', array(), '1.0' );
}
add_action( 'wp_enqueue_scripts', 'enqueue_vue_assets' );

?> 

These files are inside my twenty-twentythreetheme folder. The example is my vite project. I have not made any configurations in vite. Do I need to make any? What changes do I need to make in my enqueue script and functions script.

Also the twentytwentythree theme has mostly .html files on its frontend which I have wiped and added div id=app that should come from the app.vue.


Solution

  • If you want Vite to compile assets and automatically render the browser via the hot file method in any local Wordpress theme build. Here is a working method of doing this using Chris Page's custom php class to handle Vite in Wordpress.

    Follow steps below to get Vite running properly in Wordpress theme...

    (example below for twentytwentythree theme but this will work in any wordpress theme)

    Implement PHP into theme

    See Vite php class Vite.lib.php code below...

    Gist version by Chris Page

    <?php
    
    class Vite {
    
        /**
         * Flag to determine whether hot server is active.
         * Calculated when Vite::initialise() is called.
         *
         * @var bool
         */
        private static bool $isHot = false;
    
        /**
         * The URI to the hot server. Calculated when
         * Vite::initialise() is called.
         *
         * @var string
         */
        private static string $server;
    
        /**
         * The path where compiled assets will go.
         *
         * @var string
         */
        private static string $buildPath = 'build';
    
        /**
         * Manifest file contents. Initialised
         * when Vite::initialise() is called.
         *
         * @var array
         */
        private static array $manifest = [];
    
        /**
         * To be run in the header.php file, will check for the presence of a hot file.
         *
         * @param  string|null  $buildPath
         * @param  bool  $output  Whether to output the Vite client.
         *
         * @return string|null
         * @throws Exception
         */
        public static function init(string $buildPath = null, bool $output = true): string|null
        {
    
            static::$isHot = file_exists(static::hotFilePath());
    
            // have we got a build path override?
            if ($buildPath) {
                static::$buildPath = $buildPath;
            }
    
            // are we running hot?
            if (static::$isHot) {
                static::$server = file_get_contents(static::hotFilePath());
                $client = static::$server . '/@vite/client';
    
                // if output
                if ($output) {
                    printf(/** @lang text */ '<script type="module" src="%s"></script>', $client);
                }
    
                return $client;
            }
    
            // we must have a manifest file...
            if (!file_exists($manifestPath = static::buildPath() . '/manifest.json')) {
                throw new Exception('No Vite Manifest exists. Should hot server be running?');
            }
    
            // store our manifest contents.
            static::$manifest = json_decode(file_get_contents($manifestPath), true);
    
            return null;
        }
    
        /**
         * Enqueue the module
         *
         * @param string|null $buildPath
         *
         * @return void
         * @throws Exception
         */
        public static function enqueue_module(string $buildPath = null): void
        {
            // we only want to continue if we have a client.
            if (!$client = Vite::init($buildPath, false)) {
                return;
            }
    
            // enqueue our client script
            wp_enqueue_script('vite-client',$client,[],null);
    
            // update html script type to module wp hack
            Vite::script_type_module('vite-client');
    
        }
    
        /**
         * Return URI path to an asset.
         *
         * @param $asset
         *
         * @return string
         * @throws Exception
         */
        public static function asset($asset): string
        {
            if (static::$isHot) {
                return static::$server . '/' . ltrim($asset, '/');
            }
    
            if (!array_key_exists($asset, static::$manifest)) {
                throw new Exception('Unknown Vite build asset: ' . $asset);
            }
    
            return implode('/', [ get_stylesheet_directory_uri(), static::$buildPath, static::$manifest[$asset]['file'] ]);
        }
    
        /**
         * Internal method to determine hotFilePath.
         *
         * @return string
         */
        private static function hotFilePath(): string
        {
            return implode('/', [static::buildPath(), 'hot']);
        }
    
        /**
         * Internal method to determine buildPath.
         *
         * @return string
         */
        private static function buildPath(): string
        {
            return implode('/', [get_stylesheet_directory(), static::$buildPath]);
        }
    
        /**
         * Return URI path to an image.
         *
         * @param $img
         *
         * @return string|null
         * @throws Exception
         */
        public static function img($img): ?string
        {
    
            try {
    
                // set the asset path to the image.
                $asset = 'resources/img/' . ltrim($img, '/');
    
                // if we're not running hot, return the asset.
                return static::asset($asset);
    
            } catch (Exception $e) {
    
                // handle the exception here or log it if needed.
                // you can also return a default image or null in case of an error.
                return $e->getMessage(); // optionally, you can retrieve the error message
    
            }
    
        }
    
        /**
         * Update html script type to module wp hack.
         *
         * @param $scriptHandle bool|string
         * @return mixed
         */
        public static function script_type_module(bool|string $scriptHandle = false): string
        {
    
            // change the script type to module
            add_filter('script_loader_tag', function ($tag, $handle, $src) use ($scriptHandle) {
    
                if ($scriptHandle !== $handle) {
                    return $tag;
                }
    
                // return the new script module type tag
                return '<script type="module" src="' . esc_url($src) . '" id="' . $handle . '-js"></script>';
    
            }, 10, 3);
    
            // return false
            return false;
    
        }
    
    }
    

    Place this Vite.lib.php class file inside a lib folder in your local twentytwentythree theme directory, then require Vite.lib.php in your functions.php.

    You then want to create a Theme php class Theme.lib.php to handle enqueuing your scripts and css to work with Vite...

    <?php
    
    class Theme {
    
        public function __construct()
        {
            
            // enqueue admin styles scripts
            add_action('wp_enqueue_scripts', [ $this, 'enqueue_styles_scripts' ], 20);
            
        }
        
        /**
         * @return void
         * @throws Exception
         */
        public function enqueue_styles_scripts(): void
        {
    
            // enqueue the Vite module
            Vite::enqueue_module();
    
            // register theme-style-css
            $filename = Vite::asset('resources/scss/theme.scss');
    
            // enqueue theme-style-css into our head
            wp_enqueue_style('theme-style', $filename, [], null, 'screen');
    
            // register theme-script-js
            $filename = Vite::asset('resources/js/theme.js');
    
            // enqueue theme-script-js into our head (change false to true for footer)
            wp_enqueue_script('theme-script', $filename, [], null, false);
    
            // update html script type to module wp hack
            Vite::script_type_module('theme-script');
    
        }
    
    }
    
    new Theme();
    

    Then in your function.php, require Theme.lib.php after Vite.lib.php like this...

    <?php
    
    // require libs
    require_once(__DIR__ . '/lib/Vite.lib.php');
    require_once(__DIR__ . '/lib/Theme.lib.php');
    

    Install Vite with NPM into theme

    Once you've implemented the above php, let's start a fresh package.json in your local theme directory.

    When you get Vite running, you can re-add Vue and other dependencies later 👍🏼

    Here is the basic package.json boilerplate to get your project started with Vite in Wordpress...

    {
      "private": true,
      "scripts": {
        "dev": "vite",
        "watch": "npm run dev",
        "build": "vite build",
        "production": "vite build"
      },
      "devDependencies": {
        "sass": "^1.63.6",
        "vite": "^4.4.3",
        "laravel-vite-plugin": "^0.7.8"
      },
      "dependencies": {
    
      }
    }
    

    You will notice we are using laravel-vite-plugin in our package.json, this Laravel Vite plugin makes it super easy to configure the resources and build directory in our vite.config.js, and watches for php file changes in your entire theme!

    See working vite.config.js below for compiling our theme.css and theme.js resources to the build folder. This vite.config.js file also goes in your local theme directory, the same as your package.json...

    import {defineConfig} from "vite";
    import laravel from 'laravel-vite-plugin';
    
    export default defineConfig(() => ({
        base: '',
        build: {
            emptyOutDir: true,
            manifest: true,
            outDir: 'build',
            assetsDir: 'assets'
        },
        plugins: [
            laravel({
                publicDirectory: 'build',
                input: [
                    'resources/js/theme.js',
                    'resources/scss/theme.scss'
                ],
                refresh: [
                    '**.php'
                ]
            })
        ],
        resolve: {
            alias: [
                {
                    find: /~(.+)/,
                    replacement: process.cwd() + '/node_modules/$1'
                },
            ]
        }
    }));
    

    Before we install NPM and install Vite build, you need to create a resources folder in your local theme directory. Then inside the resources folder, create these img, scss, js directories and then create empty theme.scss, theme.js files, structured like this...

    enter image description here

    Now we are ready to run NPM install on our package.json configuration.

    Open terminal in your theme directory and run this command...

    npm install
    

    This command install's our dev dependencies and creates our node_modules library root vendor files, plus creates a package-lock.json file, so our theme directory now looks like this...

    enter image description here

    Initial Vite project build

    Now Vite is installed, we are ready to build our Vite project.

    Before we run Vite build, if you visit your site locally you will notice you get a fatal error. This is because we have not generated a Vite manifest json file yet. The Vite manifest.json handles linking between our resources files and our cache busted build/assets files.

    OK, let's build our resources with Vite so you can start making some magic!

    In your local theme directory, run this command first...

    npm run build
    

    Command should return this log...

    enter image description here

    You only need to initially run npm run build once if you are starting a brand new Vite project. This command initially creates your build folder, build/manifest.json and build/assets/.. cache busted files.

    Please note you will need to use npm run build again every time you want to build production minified build assets and production manifest.json.

    Your theme directory will look like this after initially running npm run build for the first time...

    enter image description here

    If you visit your local site you will notice the fatal error has gone and the production build theme.css and theme.js will be working!

    If your local theme is working with no errors, you can inspect the source code and you will see the theme.css and theme.js are enqueued like this in the html...

    <link href="http://localhost/wp-content/themes/vite-wordpress/build/assets/theme-716126ae.css" rel="stylesheet" id="theme-style-css" type="text/css" media="screen"> 
    
    <script src="http://localhost/wp-content/themes/vite-wordpress/build/assets/theme-13161a16.js" type="module" id="theme-script-js"></script>
    

    The above enqueued output is exactly how you want this to look on your staging and production environments. This is the expected output when our Vite local development hot file does not exist!

    Start Vite local dev js server

    Finally, we are ready to start the epic Vite local js server and hot file for super fast dev compiling and automatic browser asset rendering without refreshing the browser!

    Provided you've initially built your Vite build files, you can now run this command below to start your local Vite js server and hot file to watch for any save changes in your theme.

    npm run dev
    

    This command should return a log like this, and shows you that the Vite dev local js server is running...

    enter image description here

    And you will notice a hot file now exists in your build directory...

    enter image description here

    If you now refresh your local Wordpress site, will notice our enqueued theme.css and theme.js html has changed to this...

    <link href="http://127.0.0.1:5173/resources/scss/theme.scss" rel="stylesheet" id="theme-style-css" type="text/css" media="screen">
    
    <script src="http://127.0.0.1:5173/resources/js/theme.js" type="module" id="theme-script-js"></script>
    

    And you will also notice an additional script loaded in the <head> which is our Vite client js module. This script is handling the hot file watcher for any changes made to files in your theme.

    <script src="http://127.0.0.1:5173/@vite/client" type="module" id="vite-client-js"></script>
    

    So now we are running Vite dev server, you can start adding code to your theme.css and theme.js resource files.

    Every time you hit save on your resource files, Vite will compile your project and changes will automatically run/update in your browser without refreshing! The same will happen if you edit and save PHP.

    Installing NPM packages and dependancies

    While your local Vite dev js server is still running, you can install Vue and other packages via a new terminal window without having to stop the current Vite dev js server watcher.

    Production build and deployment

    Once you are ready to build for production, make sure you've exited the Vite dev js server and is no longer running...

    Then re-run npm run build in your theme directory to build cache busted production files in your build directory. The sync build directory to staging or production environments.

    Make sure functions.php, Vite.lib.php and Theme.lib.php are also deployed to your staging or production environments for the build/assets to work in your theme.

    Using theme images located in resources/img

    To use images from the theme build folder which are images compiled from the resources/img directory to the build/assets directory.

    The php usage is...

    <img src="<?=Vite::img('example.png')?>" alt="Example" />
    

    And the scss usage is...

    BODY {
      background-image: url(../img/example.png);
    }