Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use Vuejs for progressive enhancement - parse existing HTML

Tags:

vue.js

vuejs2

I would like to use Vue (v2) to manage parts of an HTML page, but do it in such a way that if the user does not have javscript, they still get a nice page.

e.g. The server might output:

<div id="rootAppContainer">
  ...
  <article is="foo" >
     <h1>Something cool</h1>
     <p>Here's a great article.</p>
  </article>
  ...
</div>

Which is fine as a fallback. But I'd like Vue to mount the article and replace it with something better, e.g.

<article>
   <p v-show="showTeaser" >{{teaser}}</p>
   <div v-show="!showTeaser" >
      <h1>{{title}}</h1>
      <p>Here you go:</p>
      <p>{{body}}</p>
   </div>
</article>

To do this, I'd like to be able to parse the pre-vue content of the element that's getting mounted to extract the view-model's data that will then be formatted by its template.

I thought I could do this with lifecycle hooks, or the component's data method, but I can't find any way to get a reference to the soon-to-be-mounted node; until it's too late (i.e. in mounted when it's already been replaced).

https://codepen.io/artfulrobot/pen/GOGBWQ?editors=1010

like image 768
artfulrobot Avatar asked Nov 24 '17 15:11

artfulrobot


1 Answers

First, I should say that you should look into server side rendering.

However, if you are stuck without SSR, you could parse the DOM before the Vue is created, and replace the articles with components.

Here is an example.

console.clear()

let root = document.querySelector("#rootAppContainer")
for (let article of root.querySelectorAll('article')){
  let title = article.querySelector("h1").textContent
  let body = article.querySelector("p").textContent
  let foo = document.createElement("foo")
  foo.setAttribute("title", title)
  foo.setAttribute("body", body)
  root.replaceChild(foo, article)
}

const app = new Vue({
  el: '#rootAppContainer',
  components: {
    foo: {
      props:["title", "body"],
      data: function() {
        return {
          showTeaser: true,
        };
      },
      computed: {
        teaser: function() {
          return "ooh, you won't believe this..." + this.title;
        }
      },
      template: `
        <article>
           <p v-show="showTeaser" >{{teaser}}</p>
           <div v-show="!showTeaser" >
              <h1>{{title}}</h1>
              <p>Here you go:</p>
              <p>{{body}}</p>
           </div>
        </article>
      `,
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.3/vue.min.js"></script>
<div id="rootAppContainer">
  <h2>some content before articles</h2>
  <article>
    <h1>Something cool</h1>
    <p>Here's a great article.</p>
  </article>
  <h2>some content between articles</h2>
  <article>
    <h1>Another cool thing</h1>
    <p>Here's a great article.</p>
  </article>
  <article>
    <h1>And lastly something cool</h1>
    <p>Here's a great article.</p>
  </article>
  <h2>some content after articles</h2>

</div>
like image 50
Bert Avatar answered Oct 13 '22 00:10

Bert