ctxkiwi / vue-pre
There is no license information available for the latest version (0.5.0) of this package.
Pre-render Vue.js templates in PHP
0.5.0
2019-11-14 11:58 UTC
Requires
- php: >=5.6.0
Requires (Dev)
- phpunit/phpunit: ^5.4.0|^6.0
- symfony/var-dumper: ^4.1
README
VuePre is a package to prerender vue templates. This is useful for SEO and avoiding blank pages on page load. What VuePre does, is translating the Vue template to a PHP template, then replaces the javascript expressions with PHP expressions and caches it. Once cached, you can render thousands of templates per second depending on your hardware.
PROS
- Very fast
- No dependencies
CONS
- Some javascript expressions are not supported (yet).
Installation
composer require ctxkiwi/vue-pre
Basic usage
$vue = new \VuePre\Engine(); $vue->setCacheDirectory(__DIR__ . '/cache'); // Method 1 $data = ["name" => "world"]; $html = $vue->renderHtml('<div>Hello {{ name }}!</div>', $data); // Method 2 - Using component directory (required if you use sub-components) $vue->setComponentDirectory(__DIR__ . '/components'); $html = $vue->renderComponent('my-page', $data);
Component directory
// If you set your directory like this $vue->setComponentDirectory(__DIR__ . '/components'); // It's going to look for any .php file and register the filename as a component // So, if you have components/pages/homepage.php // It will use this file for the <homepage> component
Component example
<?php return [ 'beforeRender' => function (&$data) { $data['counter'] = 0; }, ]; ?> <template> <div> <button v-on:click="min"> - </button> {{ counter }} <button v-on:click="plus"> + </button> </div> </template> <script> Vue.component('homepage', { template: '#vue-template-homepage', data: function () { return { counter: 0, }; }, methods: { plus: function(){ this.counter++; }, min: function(){ this.counter--; }, } }); </script>
Real world example
class View{ public static function render($view, $data = []){ // Normal PHP template engine ... return $html; } public static function renderComponent($name, $data = []){ $vue = new \VuePre\Engine(); $vue->setCacheDirectory(Path::get('tmp'). '/cache'); $vue->setComponentDirectory(Path::get('views') . '/components'); $html = $vue->renderComponent($name, $data); $templates = $vue->getTemplateScripts(); $js = $vue->getJsScripts(); $vueInstance = $vue->getVueInstanceScript('#app', $name, $data); $html = '<div id="app">'.$html.'</div>'.$templates.$js.$vueInstance; return static::render('layouts/main.html', ['CONTENT' => $html]; } } class ViewController{ public function homepage(){ $data = [ // Dont put private data in here, because it's shared with javascript 'layoutData' => [ 'authUser' => \AuthUser::getUser()->getPublicData(), ], 'featureProducts' => Product::where('featured', true)->limit(10)->get(); ]; // Render <homepage> component echo View::renderComponent('homepage', $data); } }
<!-- views/layouts/main.html --> <!DOCTYPE> <html> <head> <script src="https://cdn.jsdelivr.net/npm/vue"></script> </head> <body> {!! $CONTENT !!} <body> </html>
<?php // views/components/layout.php return [ 'beforeRender' => function (&$data) { $data = $data['layout-data']; }, ]; ?> <template> <div> <header>...</header> <main> <slot></slot> </main> <footer>...</footer> </div> </template> <script> Vue.component('layout', { props: ['layoutData'], template: '#vue-template-layout', data: function () { return this.layoutData; }, }); </script>
<?php // views/components/homepage.php ?> <template> <layout :layout-data="layoutData"> <div class="homepage"> <h1>Welcome</h1> <p>...</p> <h2>Featured products</h2> <div v-for="product in featuredProducts"><h3>{{ product.name }}</h3></div> </div> </layout> </template> <script> Vue.component('homepage', { props: ['layoutData', 'featuredProducts'], template: '#vue-template-homepage', data: function () { return {}; }, }); </script>
Generating <scripts>
You can generate scripts for your component templates and your component.js files.
// Based on your last render $vue->getScripts(); $vue->getTemplateScripts(); // only template scripts $vue->getJsScripts(); // only js scripts // By component name $vue->getTemplateScript('my-page'); $vue->getJsScript('my-page'); // Usefull $vue->getRenderedComponentNames();
API
->setCacheDirectory(String $path) ->setComponentDirectory(String $path) ->setGlobals(Array $globalVariables) // e.g. ['loggedIn' => true, 'user' => ['id'=>123, 'username'=>'TerryDavis']] ->renderHtml(String $html, Array $data) ->renderComponent(String $componentName, Array $data) // Optional settings ->ignoreAttributes(Array $attributeNames) ->unignoreAttributes(Array $attributeNames) ->getIgnoredAttributes() : Array $attributeNames // Generating scripts ->getScripts($idPrefix = 'vue-template-'); ->getTemplateScripts($idPrefix = 'vue-template-'); ->getTemplateScript(String $componentName, $default = null, $idPrefix = 'vue-template-'); ->getJsScripts(); ->getJsScript(String $componentName, $default = null); // Others ->getComponentAlias(String $componentName, $default = null) ->getRenderedComponentNames();
JS expressions | Supported
# Prototype functions
.indexOf()
.length
# JS Functions
typeof()
# Values: variables, strings, numbers, booleans, null, objects, arrays, functions
# Comparisons
myVar === 'Hello'
something ? 'yes' : false
# Nested expressions
(((5 + 5) > 2) ? true : false) ? (myBool ? 'Yes' : 'Yez') : 'No'
# Objects
product.active ? product.name : product.category.name
# Methods using $vuePre->setMethods(['myFunc'=> function(){ ... }])
product.active ? myFunc(product.name) : null
Todos
Note: Feel free to make an issue for these, so i can make them a prority. The only reason these are not implemented yet is because of low priority.
- Custom error handlers
- Options:
ignoreVariableNotFound
ignoreVariableNames
ignoreMethodNames
ignoreSubComponents
ignoreSubComponentNames
Contributors
The DOM iterator code was partially copied from wmde/php-vuejs-templating