I am working on an isomorphic javascript app with express + react. We started out using jade for server side templates for static content, but combining the two is quickly becoming unwieldy. We have ended up with something like this:
In the express routes:
router.get("/", function(req, res) {
var webpackStats = require('../../config/webpack-stats.json');
var reactHtml = React.renderToString(HiwApp({}));
var slideshowHtml = React.renderToString(slideshowApp({}));
var config = {
webpackStats: webpackStats,
reactOutput: reactHtml,
slideshowHtml: slideshowHtml
};
res.render("how_it_works/howitworks", config);
});
In Jade:
body
.company-logo.center
#react-main-mount
!= reactOutput
include ./content_block_1.jade
include ./content_block_2.jade
#slideshow-main-mount
!= slideshowHtml
This is very brittle-if we want jsx then a jade template then more jsx, we have to make sure we get the order right.
My idea is to do it all with jsx. I know there is React.renderToStaticMarkup for this sort of thing, but that doesn't solve the problem of mixing dynamic with static pages.
The big questions: if we decide to do all of this with jsx (say layout.jsx which contains all components), then call React.renderToString(App({});, will this be a major performance hit? If so, is there a better way to do it to easily combine static and dynamic blocks?
Although this may be a tiny bit off topic: We stuck with jade templates.
Basically we wanted the flexibility to use a non-react + flux architecture for areas of the site when and if that need arose. Our site is basically made up of a number of smaller SP apps: Site, UserAccount, Team and Admin.
Why did we do this?
Smaller filesize and overhead for users who are not accessing all sections of the site.
Option to "opt out" of React and flux if and when the need arises.
Simpler, server side authentication.
The way we have done it successfully was to render a JSX shell template (Html.jsx) on the server using React.renderToStaticMarkup() and then send it as the response to every server-side express route request that is meant to deliver some HTML to the browser. Html.jsx is just a shell containing html head information and GA scripts etc. It should contain no layout.
// Html.jsx
render(){
return (
<html>
<head>
// etc.
</head>
<body>
<div
id="app"
dangerouslySetInnerHTML={{__html: this.props.markup}}>
</div>
</body>
<script dangerouslySetInnerHTML={{__html: this.props.state}</script>
<script>
// GA Scripts etc.
</script>
</html>
)
}
Remember it is totally fine and even recommended to use dangerouslySetInnerHTML on the server when hydrating your app.
Dynamic layout should be done with your your isomorphic components through a hierarchy of components based on their state/props configuration. If you happen to be using React Router, then your router will render view handlers based on the routes you provide it so that means you don't need to manage that yourself.
The reason we use this technique is to architecturally separate our "App" which is isomorphic and responds to state from our server-side template shell which is just a delivery mechanism and is effectively boiler plate. We even keep the Html.jsx template amongst all the express components within our app and do not let it mix with the other isomorphic React components.
One of the most helpful resources I found for working out React/isomorphic architecture was https://github.com/yahoo/flux-examples/tree/master/react-router which is where we stole this technique from.
We explored the idea of integrating handlebars as a templating engine for client's devs using our products in the future but decided that it was less complex to write our own DSL in JSX and use some simple parsing routines to parse our HTML-like DSL to JSX by adding things like export default (ES6 module syntax) at the start of the template and then import the template to a rendering component.
You could of course follow that line of thought and use a jade compiler to spit out the template and then add module syntax around that if you think separate jade files are essential. I also noticed this project as well although I have not explored it in anger: https://github.com/jadejs/react-jade.
Related
As a thought exercise, I've been trying to create a blogging application using Isomorphic React. After some google searching I've come across these three tutorials:
Medium: A Modern Isomorphic stack:
https://medium.com/#MoBinni/a-modern-isomorphic-stack-6609c7c9d057
blog.lunarlogic.org: How to create a universal Reactjs application
http://blog.lunarlogic.io/2015/how-to-create-a-universal-reactjs-application-with-bare-reactjs/
One part that I'm confused in particular is with how the html-webpack-plugin works on the server side.
Both of these tutorials have templates that look like:
<html>
<head>
...
</head>
<body>
...
<%- reactOutput %>
</body>
</html>
However, when I try to run this template through webpack, I get errors that reactOutput is undefined.
On a pure client-side app, I would use html-webpack-plugin to generate my index.html file to be served to the clients. This generation would make it easier for me because I have different configurations in webpack for dev and production. (Common chunks and all of that)
Do I even need to consider that when talking about server side rendering? Or am I going about this all wrong?
I dont think html-webpack-plugin figures in the equation, except to pre-generate the template.
Once you have the template content you provide it to the renderer along with the value of reactOutput.
You make a call like shown here in line 42 to grab the rendered content:
var reactOutput = ReactDOMServer.renderToString(<RoutingContext {...renderProps} />);
Then you will be giving this var along with the template. In the tutorial they use ejs for templating though you can plug in your own:
res.render('index', {
reactOutput: reactOutput)
});
I love vue.js because of its simplicity, which means I can hack a quick one-page SPA with modern, intuitive data-binding syntax and no complex toolchain.
I also love the idea of single-file components which means there is a single place (*.vue file) where each component stores DOM, styling and scripted functionality.
However, I want to use single-file components without wasting time on managing a build process every time I put an app together. In short, I want the benefits of component management without the overhead of a build toolchain, which means letting the browser do the heavy lifting for bootstrapping each *.vue file via XMLHttpRequest and DOM rendering. Making sure that we replace module.exports and import calls with corresponding Vue.component() functionality.
I'd love to know if anyone has come across a client-side (only) solution for using *.vue files on the browser. Surely this has been done already?
I'm absolutely certain this doesn't exist yet, because while it might seem relatively easy, certain functionalities would make it quite difficult to implement. For example:
You don't necessarily import just other .vue components, you can import random external dependencies. Which means that the browser now needs to download and parse npm modules, handle their dependencies, etc.
Different sections of your .vue component (template, logic and style) can be written in languages other than HTML, JS and CSS. Which means the browser now also needs to download a compiler/transpiler for Jade, CoffeeScript, LESS or whatever else you're using and run your code through it. Mind, there's no guarantee that such a transpiler written in JavaScript actually exists, because a node module used in a regular build process could be just a wrapper for some external library which can't be run in a browser.
Styling in a .vue component can be scoped, which means that you now need to parse the template of a component to insert randomly generated IDs as element attributes AND parse the styling of the same component to insert those same IDs in your CSS selectors so that your styling ends up being scoped.
And those are just the most obvious ones off the top of my head. Sure, you could severely limit yourself and not use any of those features, but then it's not really a .vue component anymore, is it?
If you really want to avoid a build process at all costs and are willing to accept the limitations of not using any of the features above, why not just use a single JS file:
$(body).append(`<style>
// styling goes here
</style>`);
var myTemplate = `
// template goes here
`;
Vue.component('my-component', {
template: myTemplate
// component logic goes here
})
You have to load them in the correct order, but there you have it, a poor man's single file component.
Another way is use: http-vue-loader
Load .vue files directly from your html/js. No node.js environment, no build step.
https://cdn.jsdelivr.net/npm/http-vue-loader#1.4.1/src/httpVueLoader.min.js
Same to in unpkg cdn
https://unpkg.com/http-vue-loader
Here a example
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/http-vue-loader"></script>
<script>
new Vue({
el: '#app',
components: {
'header': httpVueLoader('/components/header.vue'),
'nav-bar': httpVueLoader('/components/navbar.vue'),
'aside': httpVueLoader('/components/aside.vue'),
'content': httpVueLoader('/components/content.vue'),
'footer': httpVueLoader('/components/footer.vue')
}
});
</script>
Or you can load your components from external like
'MyHelloWorldComponent': httpVueLoader('https://my-cdn-or.github.io/path/HelloWorld.vue'),
See the example at : https://codepen.io/mikechen2017/pen/wEdbBN/
It's 2020 and Evan You wrote https://github.com/vuejs/vite just last week.
I'd love to know if anyone has come across a client-side (only) solution...
Vite has a server, but it feels like the old days of Web when we just had Notepad. I had run the demo in less than 5 minutes, it's that easy.
it covers or aims to cover the finer details that #mzgajner mentions
For now, I would say that it's only gotcha is that you are in Vue 3 beta realm right away if you use it. No Vue 2.x.
However, I want to use single-file components without wasting time on managing a build process every time I put an app together. In short, I want the benefits of component management without the overhead of a build toolchain
I share the sentiment and decided to solve this problem with vue-blocks. Just a single script tag to get going, no build tools required, completely client-side.
It can load vue files (with some limitations though) from the server jsfiddle example:
<template src="path/to/vue-file.vue"></template>
Vue Blocks allows you to write multiple vue components in the html document, like so:
<template component="sample-component">
<div>
<h1>Sample component</h1>
</div>
<style>
</style>
<script>
export default {
data() {
return {}
},
mounted() {},
methods: {
xx() {
}
}
}
</script>
</template>
A working demo in jsfiddle: https://jsfiddle.net/o48L0y9j/
Quick Summary:
I need to allow two script files to handle different operations for the same angular app. One needs to initialize the app, the other needs to assign a templateCache object to a piece of JSON in localStorage.
Context:
I have several python files which compile/generate html and I have constructed an angular app with this emitted html for my site (which uses CGIs).
The basic construct of the site comes pieces of HTML, which fit together like so:
|------------Header---------------|
|-Navigation-|------Content-------|
|-Navigation-|------Content-------|
|-Navigation-|------Content-------|
|------------Footer---------------|
My Header creates the <head> tag, instantiates my ng-app and uses $templateCache to set up a template that I call from my Navigation code. I had to go with templateCache instead of ngView and ngRoute due to some limitations with how the CGIs emit the html, and the order in which this happens.
My "Navigation" python/html sets up my app with JS like so:
<script>
var responsiveCatalog = angular.module('responsiveCatalog', ['ngStorage']);
....controllers...
....config, etc....
</script>
This Navigation also includes my default templateCache object:
<div ng-include=" 'responsiveItems.html' "></div>
This is all working to show my first templateCache object in the Content section. However, I need to grab many pieces of information from the python generator for the "Content" section (a totally separate file from the "Navigation"), store this data as JSON in localstorage (hence the inclusion of the ngStorage module), and call that as my second templateCache option.
I am not actually sure that I can use two separate instances of Javascript to reference and influence the same Angular app. I know this seems like bad practice, but I am trying to prevent the need to tear down a huge piece of legacy architecture to influence the angular app from two Javascript files in harmony.
Thanks in advance.
You can do
angular.module('myAppName').controllers.... in different files, just make sure the myAppName the same. Bug I don't feel like it work with config, no time to test. If you need any communication between them, check $emit and $broadcast.
Disclaimer - I am very new to making web apps, I just went through a few meteor js tutorials over the weekend, it's pretty cool.
I have this project I want to make, which involves embedding tabular data into websites. I want to do this with a iFrame.
I can see how this could be done with meteor, route to a template to render out the html I want, but I'm concerned with performance. Having the whole app load up to just display a few lines of html seems excessive, it certainly doesn't need all the javascript.
The html can be a static file, only changing when the web app user saves a change, it doesn't have to dynamically load every-time somebody sees the embed.
So what I'm really asking is, how can I use meteor js to write a static html file?
What you need is server side rendering (SSR), because you are intending to serve html directly from your meteor server.
Meteor does not support SSR, yet.
While I haven't myself tried to get SSR working in meteor, Arunoda has, ableit some rather severe limitations. Basically you cannot have much reactivity there. This is probably due to the use of fibers on the server. But you said you do not need reactivity anyway in your iframes, so that should not stop you.
You can combine Arunoda's solution with Iron Router's server routes.
Enable the SSR package:
meteor add meteorhacks:ssr
Put your template into the private directory:
<!-- private/embed.html -->
<template name="embed">
<ul>
{{#each posts}}
<li>{{title}}</li>
{{/each}}
</ul>
</template>
On the server, compile it with:
SSR.compileTemplate('embed', Assets.getText('embed.html'));
And declare your template manager:
// server/embed.js
Template.posts.getPosts = function(category) {
return Posts.find({category: category}, {limit: 20});
}
And then add a server-route as follows:
Router.route('/embed/:owner', function () {
var html = SSR.render('embed', {owner: this.params.owner});
this.response.end(html);
}, {where: 'server'});
Now you just need to point your iframe to http://localhost:3000/embed/myusername.
At this stage I would not recommend using meteor in this way, but instead create a simple backend that connects to DDP or mongo directly and renders your iframe's html.
I set up a node.couchapp.js CouchApp and pushed it to my CouchDB. There is Sammy and jQuery included by default. Now, I'd like to add Mustache for templates, but don't exactly know where? I could use it as Node.js module or jQuery plugin.
I started with some sample code, just to see if it works, but it doesn't:
ddoc.lists = {
"basic": "function (head, req) { var that = this; provides('html', function () { var to_html = require('modules/mustache').to_html; var members = []; var row; while (row = getRow()) { members.push({club: row.key[0], name: row.key[1]}); } return to_html(that.templates['roster.mustache'], { members: members }); }) }"
};
Results in:
{"error":"invalid_require_path","reason":"Object has no property \"modules\". ...
Project structure is:
. Project
| - app.js
| - attachments
| - - scripts
| ` - index.html
| - modules
| | mustache
| - ` - mustache.js
` - templates
Update
I modified the code a bit and excluded the template folder as possible cause. This snippet is from a solved question here on SO, so it should actually work. But it's still the same error. I installed mustache.js with npm install mustache and renamed the folder from node_modules to modules (didn't work with node_modules either).
"basic": "function (head, req) { provides('html', function () { var template = '{{%IMPLICIT-ITERATOR}}{{name}}: <ul> {{#items}}<li>{{.}}</li>{{/items}} </ul>'; var mustache = require('./modules/mustache/mustache.js'); return mustache.to_html(template,view);});}"
But it's still the same error.
I'm not sure about the specific error you're getting, but to your question:
"Now, I'd like to add Mustache for templates, but don't exactly know where? I could use it as Node.js module or jQuery plugin."
You can use it in both.
As Node.js runs on the server-side this effectively gives you a way to define your template once (using Mustache in this case) and run it both server-side (using Node.js) and client-side (using the jquery-plugin).
Just for the overview (you may well already know this):
Imagine rendering a search-result of products:
The traditional way is to render the products with some server-side templating engine.
Now, let's say you want to enable filtering / sorting of the search-results. Generally speaking you have 3 options:
do a normal (non-ajax call) to the server, do server-side templating and spit out the new page
do an ajax-call to the server, do server-side templating and spit out html, pick it up client-side and insert it into a dom-element.
do an ajax-call to the server, generate a json-object, which you render client-side using a client-side templating engine (such as Mustache) into a dom-element.
Option 3 is arguably the best solution from a usability standpoint. (quick, low-bandwidth, no page flickering or page jumping, etc. ) . However think about a fallback/non-js way for SEO if you need it.
When implementing option 3 however you now have the situation of needing a server-side templating language to render products on the initial page load, and a client-side templating language to render products returned from subsequent ajax-calls. Both result in the exact same html. Not very DRY.
Here node.js comes into play, which eliminates the need for writing a seperate server-side template. Instead, just let node.js spit out the initial page using the exact same mustache template you use on the client-side.
Of course you could go the only client-side route: spitting out json on the intial page load as well, and render that on the client. This of course if terrible from a seo-standpoint. Some apps don't the search engine indexing however, in which case the 'everything on the client' approach is a good alternative.
Some related questions:
Client-side templating language with java compiler as well (DRY templating)
Good DRY approach to rendering and AJAX updating of page