I found a simple way to enable HMR with Livewire.
- Start a watch process to monitor file savings
- Send the filename to the browser via a socket server
- Add javascript in the browser to listen for the websocket, and emit an event
- Add event listener to the Livewire component
- Start the watch process
Part 1 and 2:
a) Install node-watch
and ws
from node:
yarn add node-watch
yarn add ws
b) Create a file called watch.js
const watch = require('node-watch');
const WebSocket = require('ws');
const wss = new WebSocket.Server({port: 8080});
watch([
'./resources/views/livewire',
'./app/Http/Livewire'
], {recursive: true}, (evt, name) => {
console.log('%s changed.', name);
wss.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(name);
}
});
});
Part 3: Add the following to you app.blade.php
@if( config('app.debug') )
<script type="text/javascript">
if ("WebSocket" in window) {
// Let us open a web socket
var ws = new WebSocket("ws://localhost:8080/");
ws.onopen = function () {
console.log("HMR open");
};
ws.onmessage = function (evt) {
var received_msg = evt.data;
console.log("Message is received...", received_msg);
window.dispatchEvent(new CustomEvent('hmr', {detail: received_msg}));
window.livewire.emit('hmrupdated' + (received_msg));
};
ws.onclose = function () {
console.log("HRM Connection is closed...");
};
} else {
console.log("HMR WebSocket NOT supported by your Browser!");
}
</script>
@endif
Part 4:
Make your Livewire components inherit from a BaseComponent, and add an event listener:
<?php
namespace App\Http\Livewire;
use Illuminate\Support\Str;
use Livewire\Component;
class BaseComponent extends Component
{
protected function getEventsAndHandlers()
{
$eventsAndhandlers = collect($this->getListeners())
->mapWithKeys(function ($value, $key) {
$key = is_numeric($key) ? $value : $key;
return [$key => $value];
})->toArray();
if(config('app.debug')){
// Hot module reloading
$nameBladeFile = Str::of($this->getName())
->replace('.','/')
->append('.blade.php')
->prepend('resources/views/livewire/')
;
$eventsAndhandlers['hmrupdated'.$nameBladeFile] = '$refresh';
$nameClass = Str::of(static::class)
->replaceFirst('App', 'app')
->replace('\\', '/') . ".php";
$eventsAndhandlers['hmrupdated'.$nameClass] = '$refresh';
}
return $eventsAndhandlers;
}
}
Part 5: Start watching your files:
node watch.js
Then the browser should be able to autoupdate the changed components.