An HTML templating engine that uses Vue's template syntax. Parses HTML, evaluates JavaScript expressions, and returns rendered output.
cargo add prevuepub fn render(template: impl AsRef<str>, data: impl serde::Serialize) -> prevue::Result<String>data can be any value that implements Serialize.
use prevue::render;
use serde_json::json;
fn main() -> prevue::Result<()> {
let template = r#"
<div>
<a :id="id">link</a>
<p v-if="user.age >= 18">{{ user.name }} is adult</p>
<ul>
<li v-for="item in list">{{ item }}</li>
</ul>
</div>
"#;
let data = json!({
"id": "link-id",
"user": { "name": "James", "age": 28 },
"list": ["a", "b", "c"],
});
let output = render(template, data)?;
// <html><head></head><body><div>
// <a id="link-id">link</a>
// <p>James is adult</p>
// <ul>
// <li>a</li>
// <li>b</li>
// <li>c</li>
// </ul>
// </div>
// </body></html>
Ok(())
}render builds a fresh JavaScript engine on every call. Renderer keeps one
alive across renders and caches compiled expressions:
use prevue::Renderer;
use serde_json::json;
let mut renderer = Renderer::new()?;
let template = "<p>{{ name }}</p>";
for name in ["Ada", "Grace"] {
println!("{}", renderer.render(template, json!({ "name": name }))?);
}Small templates render 5-25x faster this way; large loop-heavy ones barely change, since the engine setup was already a rounding error there.
Render data and setup script declarations do not carry over between renders, but
globals a template creates deliberately do — var inside {{ }}, an undeclared
assignment, a write to globalThis, a mutated built-in. Use a fresh Renderer
when you need a clean realm.
Renderer is not Send; use one per thread.
The HTML is still parsed on every call above, and parsing is most of the cost of
a small render. Template parses once:
use prevue::{Renderer, Template};
use serde_json::json;
let mut renderer = Renderer::new()?;
let template = Template::new("<p>{{ name }}</p>");
for name in ["Ada", "Grace"] {
println!("{}", renderer.render_template(&template, json!({ "name": name }))?);
}That is roughly twice as fast; loop-heavy templates gain little, since
evaluation dominates them instead. Template is not Send either, but cloning
one is cheap.
| Syntax | Notes |
|---|---|
{{ }} |
Text interpolation |
v-bind, :attr |
Attribute binding |
v-if |
Conditional rendering |
v-else, v-else-if |
Conditional branches |
v-show |
Hides the element with display: none |
v-for |
List rendering |
v-text |
Text replacement |
v-html |
Raw HTML replacement; inserted HTML is not compiled |
v-model |
Fills a form control's value, checked or selected |
v-pre |
Skip rendering logic |
v-on/@, v-once, v-cloak, v-memo, v-slot/# |
Recognized, then dropped from the output |
<template> |
Structural wrapper |
<script type="prevue"> |
Render-order setup script |
- Output is serialized as a complete HTML document with
<html>,<head>, and<body>. - Attribute names are lowercased by html5ever, so
:MyAttrbecomes:myattrand:[dynamicKey]looks updynamickey. On SVG and MathML, where case matters,.camelrestores it::view-box.camelbindsviewBox. - When two sources set the same attribute, the one written last wins;
classandstylemerge in that order. - A static
styleattribute is rewritten like a binding, sostyle="marginTop: 1px"becomesmargin-top: 1px;. - An attribute spelled like a directive Vue does not define, such as a misspelled
v-els, is an error.
Object data fields are available as top-level variables. The original data value is also available as $.
{{ user.name }}
{{ $.user.name }}$ is reserved for the full data value. If your data contains a top-level "$" field, access it with $["$"].
<script type="prevue"> runs when rendering reaches it. Helpers defined by a setup script are available to following template expressions, and executed setup scripts are removed from the rendered HTML.
<script type="prevue">
function fullName(user) {
return `${user.first} ${user.last}`;
}
</script>
<p>{{ fullName(user) }}</p>Only type="prevue" scripts are executed by prevue. Regular <script> tags are preserved.
prevue uses Boa to evaluate JavaScript expressions and setup scripts.
- Never use untrusted templates.
- Accessing undeclared identifiers fails expression evaluation instead of returning
undefined. thisis not Vue-compatible and may expose internal scope objects. Avoid usingthisin templates.
MIT