I love vue, one of the problems that I have with it or that I can’t figure out is how build on the fly with hugo? With react this is possible with ‘js.Build’ which works great as shown below.
The Problem
I would like to use Vue with ‘hugo -D server’ for experimentation, not a production build. The
problem is that I have to change all my ‘export default’ in vue code. While in react I could just
import whatever component I want and it works.
The Solution
I have came up with 4 solutions to my problem.
- Don’t use hugo. Build your app first in npm then move the code to hugo.
- Concat the files. Although this kinda works things will get messy fast
when you have several components with html templates,javascript, and css. - Esbuild plugins?
- Vue Render functions - Which eliminates the use of templates then I could in theory concat javascript and css. The problem with this is that if I wanted to test someone else’s code I would have to use it in npm or change it to render functions.
What Works Now
Basically, I break apart all the vue into files and append them (overkill). This is a hugo partial.
The html gets injected twice. The first html get’s appended to the dom as expected without the
template tag. The second code appends a gray ‘template’ html tag, not sure why but it works. All the files are in assets/ of the root directory.
<script src="https://unpkg.com/vue@next"></script>
{{ $calculator := resources.Get "vue/calculator.html" }}
{{ $heading := resources.Get "vue/components/heading.js" }}
{{ $main := resources.Get "vue/calculator.js" }}
{{ $style := resources.Get "vue/calculator.css" }}
{{$js := slice $main $heading | resources.Concat "vue/bundle.js" | minify | fingerprint "sha512"}}
{{$calculator.Content | safeHTML}}
<script src='{{$js.RelPermalink}}'></script>
<link rel='stylesheet' href='{{$style.RelPermalink}}' type='text/css' media='all' />
Vue Code
This probably breaks all the functionality of how vue was intended to be used because when you
use a package manager like npm, vue cli will compile all the html templates into javascript render
functions. Here are some basic examples.
Calculator.js uses pure javascript. This works, I’m not sure how to use
export default
on the fly without npm/vue cli. The delimiters are not needed
since we are sourcing this.
const app = Vue.createApp({
template: '#calc',
delimiters: ['${', '}'],
data() {
return {
previous: null,
current: '',
operator: null,
operatorClicked: false,
count: 0,
name: 'Calculator',
message: "Hello",
};
},
methods: {
//more code here
Small component example heading.js. Mount is here because I concat the files (export hack).
app.component('heading', {
delimiters: ['${', '}'],
props: ['htitle','name'],
data(){
return{
name: this.name,
}
},
template: '<h3 @click="clear" class="heading">${htitle}${name}</h3>',
})
app.mount("#app")
The vue html template which gets injected twice with {{$calculator.Content | safeHTML}}
<template id="calc">
<div class="calculator">
<heading htitle="Mode: " :name="name"></heading>
<!-- More divs here -->
</div>
</template>
React Code
This works fine with hugo server. I think the reason for it is because react uses
‘js.build’. In react I can import fine, everything works.
This is the react code in a hugo partial which works great. The only problem is the transferring of css and images which is not a big deal. I ended up doing drag and drop for the images.
baseof.html
{{/* xfer css/images - Esbuild won't do import */}}
{{partial "react/react.html" (dict "jsx" "react/index.jsx" "css" "react/App.css" "page" .) }}
react/react.html
<script src="https://unpkg.com/react@17/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js" crossorigin></script>
{{$react_file := .jsx }}
{{ $externals := slice "react" "react-dom" }}
{{ $defines := dict "process.env.NODE_ENV" `"development"` }}
{{ $opts := dict "targetPath" "out.js" "externals" $externals "defines" $defines }}
{{ $built := resources.Get $react_file | js.Build $opts }}
{{/* Could not figure out how to xfer images with ExecuteAsTempalate solution: drag and drop.*/}}
{{/* $built := resources.Get $react_file | ExecuteAsTemplate "out.jsx" . | js.Build $opts */}}
<script type="text/javascript" src="{{ $built.RelPermalink }}" defer></script>
{{with .css}}
{{$react_css := resources.Get .}}
{{ $css := slice $react_css | resources.Concat "out.css" }}
<link rel='stylesheet' href='{{$css.RelPermalink}}' type='text/css' media='all' />
{{end}}
These is an example app.jsx.
import Welcome from './Welcome';
let useState = React.useState
function Example() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
<Welcome name="Mr. Rick your count is: " count={count}/>
</div>
);
}
ReactDOM.render(React.createElement(Example),
document.getElementById("app"));
Here is the react component.
function Welcome(props) {
return <p>Hello, {props.name}{props.count}</p>
}
export default Welcome;
Thanks