【问题标题】:Using Vue to enhance existing Multi page / server rendered / classic web app使用 Vue 增强现有的多页面/服务器渲染/经典 Web 应用程序
【发布时间】:2018-09-09 12:34:50
【问题描述】:

我已经搜索了好几个小时,但没有找到任何与我的用例相近的东西。

  • 我有一个用 JAVA 制作的经典/多页面/服务器渲染的电子商务网站
  • 我有一个页面,服务器在其中呈现带有分页的产品列表
  • 今天我用jQuery做分页,给用户更好的加载体验
  • 在我的服务器上,如果请求是 AJAX 发送一个 json 响应,否则我呈现一个普通的 html 视图
  • 使用 jQuery 和 vanilla 真的很简单,使用 Vue 似乎不起作用,因为 Vue 的 v-for 和其他模板绑定直接替换了服务器呈现的模板……
  • 服务器会渲染这个:

body {
  background: #20262E;
  padding: 20px;
  font-family: Helvetica;
}

#app {
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  transition: all 0.2s;
}

article {
  margin: 8px 0;
  background: #eee;
  padding: 20px;
}

h2 {
  font-weight: bold;
  margin-bottom: 15px;
}

del {
  color: rgba(0, 0, 0, 0.3);
}
<!-- server rendered -->
<div id="app">
  <h2>Freelance list</h2>
  
  <article>
    <h3>louane</h3>
    <p>
      City : <strong>courbevoie</strong>
      <br> Phone : <strong>05-36-23-51-89</strong>
    </p>
  </article>
  <article>
    <h3>indra</h3>
    <p>
      City : <strong>rheden</strong>
      <br> Phone : <strong>(354)-415-2419</strong>
    </p>
  </article>
  <article>
    <h3>angelo</h3>
    <p>
      City : <strong>montpreveyres</strong>
      <br> Phone : <strong>(883)-474-9314</strong>
    </p>
  </article>
  
  <a href="/prev-link">prev</a>
  <a href="/next-link">next</a>
</div>
<!-- server rendered -->
  • 我希望能够使用 Vue 做这样的事情:

// fake url link, normally this would be taken from the href or something
var url = 'https://randomuser.me/api/?seed=abc&results=3&page=';
var page = 1;
var $articles = $('.articles');
var tpl = $articles.children().eq(0).clone();

$('.prev').click(function(e) {
	e.preventDefault();
  
  if (page <= 1) {
  	return
  }

  page--;
  $.getJSON(url + page)
  	.done(onReqDone);
});

$('.next').click(function(e) {
	e.preventDefault();

  page++;
  $.getJSON(url + page)
  	.done(onReqDone);
});

function onReqDone(res) {
  $articles.html('');

  res.results.forEach(function(user) {
    var $node = tpl.clone();
    $node.find('h3').text(user.name.first);
    $node.find('strong:eq(0)').text(user.location.city);
    $node.find('strong:eq(1)').text(user.phone);
    $articles.append($node);
    window.scroll(0, 0);
  });

}
body {
  background: #20262E;
  padding: 20px;
  font-family: Helvetica;
}

#app {
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  transition: all 0.2s;
}

article {
  margin: 8px 0;
  background: #eee;
  padding: 20px;
}

h2 {
  font-weight: bold;
  margin-bottom: 15px;
}

del {
  color: rgba(0, 0, 0, 0.3);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!-- server rendered -->
<div id="app">
  <h2>Freelance list</h2>

  <div class="articles">
    <article>
      <h3>louane</h3>
      <p>
        City : <strong>courbevoie</strong>
        <br> Phone : <strong>05-36-23-51-89</strong>
      </p>
    </article>
    <article>
      <h3>indra</h3>
      <p>
        City : <strong>rheden</strong>
        <br> Phone : <strong>(354)-415-2419</strong>
      </p>
    </article>
    <article>
      <h3>angelo</h3>
      <p>
        City : <strong>montpreveyres</strong>
        <br> Phone : <strong>(883)-474-9314</strong>
      </p>
    </article>
  </div>

  <a href="/prev-link" class="prev">prev</a>
  <a href="/next-link" class="next">next</a>
</div>
<!-- server rendered -->

关于如何做到这一点的任何想法?这是我的尝试: https://jsfiddle.net/7270zft3/2/ : 问题,它不会删除旧的 dom

PS: 在有人用 Vue 谈论 SSR 之前,你只是在做一个 SPA,这就是为什么我不能:

  • 这个电子商务网站不能完全用单页应用程序重新制作,它会给我们带来的好处会花费太多的时间和金钱
  • 这个电子商务需要 SEO 来继续推动流量,就像任何电子商务一样
  • 如果 Vue 真的可以像 jQuery 一样使用(这就是我们押注 Vue 的原因),我们应该能够做到这一点而无需完全重写
  • 如果我们有时间重写 SPA,我们将无法使用 SSR,因为我们的后端是用 JAVA 制作的,而 SSR 似乎只能用于 node 和带有 v8js 模块的 PHP

【问题讨论】:

  • 它正在为我移除旧的 dom 元素。除了你硬编码的前三个。
  • “除了你硬编码的前三个” - 那是旧的 dom 哈哈,检查 jQuery 版本以了解我的期望。
  • 我想你基本上是在找hydration
  • @RoyJ 你能详细说明一下吗?为了清楚起见,我需要的是服务器为 google-bot 呈现实际数据,以便让我的 SEO 继续运行,但是因为我不希望用户在每次分页时体验完全重新加载,我处理分页与ajax si 我看不出水合作用在哪里
  • Hydration 就是这样:您从一个包含数据的服务器渲染页面开始。 Vue 的初始状态匹配渲染的页面而不是替换它,但是所有的 Vue 动作都是实时的,因此它可以根据用户输入更新页面。在您的情况下,这只会使分页生效。

标签: javascript jquery vue.js vuejs2 server-rendering


【解决方案1】:

您可以将 DOM 引用附加到服务器呈现的内容,然后就像在 jQuery 中一样,只需将内容清除到 DOM 元素。

您只需要执行一次此操作,因此您可以添加检查以查看您的 DOM 引用是否为空 page === 1

new Vue({
  el: "#app",
  data: {
    users: null,
    page: 1
  },
  methods: {
    loadData: function(prev) {
      var page = this.page

      if (prev) {
        page--
      } else {
        page++
      }

      fetch('https://randomuser.me/api/?seed=abc&results=3&page=' + page)
        .then(res => res.json())
        .then(data => {
          this.$refs.serverContent.innerHTML = '';
          this.users = data.results
          this.page = page
          window.scroll(0, 0)
        })
    }
  }
})
body {
  background: #20262E;
  padding: 20px;
  font-family: Helvetica;
}

#app {
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  transition: all 0.2s;
}

article {
  margin: 8px 0;
  background: #eee;
  padding: 20px;
}

h2 {
  font-weight: bold;
  margin-bottom: 15px;
}

del {
  color: rgba(0, 0, 0, 0.3);
}
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app">
  <h2>Freelance list</h2>
  <div ref="serverContent">
    <article>
      <h3>louane</h3>
      <p>
        City : <strong>courbevoie</strong>
        <br> Phone : <strong>05-36-23-51-89</strong>
      </p>
    </article>
    <article>
      <h3>indra</h3>
      <p>
        City : <strong>rheden</strong>
        <br> Phone : <strong>(354)-415-2419</strong>
      </p>
    </article>
    <article>
      <h3>angelo</h3>
      <p>
        City : <strong>montpreveyres</strong>
        <br> Phone : <strong>(883)-474-9314</strong>
      </p>
    </article>
  </div>

  <!-- Vue part -->
  <!-- how to plug Vue to handle the pagination ? -->
  <article v-for="user in users">
    <h3>{{ user.name.first }}</h3>
    <p>
      City : <strong>{{ user.location.city }}</strong>
      <br> Phone : <strong>{{ user.phone }}</strong>
    </p>
  </article>
  <!-- Vue part -->

  <button v-show="page > 1" @click="loadData(true)">prev</button>
  <button @click="loadData()">next</button>
</div>
<!-- server rendered -->

但是要做到 Vue 风格,建议你从服务器中移除首页的渲染,让 Vue 自己处理数据获取,以便 Vue 可以使用它存储的data 作为事实源操作 DOM。

【讨论】:

  • 我觉得你没看够我的帖子,如果我做的是 Vue 风格,那么当一个机器人来的时候,他看到没有数据,所以没有 SEO,流量更少,这是我没有的问题'在使用 jQuery 时没有。目标是让服务器为机器人渲染初始数据,然后继续使用 ajax
  • 很好的解决方案,尽管添加一个 ref 并清空它,我会等着看是否有人看到别的东西
【解决方案2】:

hydration 的示例。我无法让 Vue 停止警告我生成的 HTML 与原始 HTML 不匹配;这并不重要。在开发中,Vue 将“保释并进行完整渲染”,但在生产中它将保留预渲染。你只是想确保它们匹配,这样当它更新时,它就是你所期望的。

我将 jQuery 留在了 getJSON 中。除此之外,它是免费的 jQuery。

// fake url link, normally this would be taken from the href or something
var url = 'https://randomuser.me/api/?seed=abc&results=3&page=';
var page = 1;
$.getJSON(url + page).done((res) => {
  const articles = res.results;

  new Vue({
    el: '#app',
    template: `
  <div id="app">
    <h2>Freelance list</h2>
    <div class="articles">
      <article v-for="article in articles">
        <h3>{{article.name.first}}</h3>
        <p>
          City : <strong>{{article.location.city}}</strong>
          <br> Phone : <strong>{{article.phone}}</strong>
        </p>
      </article>
    </div>
    <a href="/prev-link" class="prev" @click.prevent="--page">prev</a>
    <a href="/next-link" class="next" @click.prevent="++page">next</a>
  </div>
  `,
    data: {
      page,
      url,
      articles
    },
    methods: {
      getPage() {
        $.getJSON(this.url + this.page)
          .done((res) => {
            this.articles = res.results;
          });
      }
    },
    watch: {
      page() {
        this.getPage();
      }
    }
  });
});
body {
  background: #20262E;
  padding: 20px;
  font-family: Helvetica;
}

#app {
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  transition: all 0.2s;
}

article {
  margin: 8px 0;
  background: #eee;
  padding: 20px;
}

h2 {
  font-weight: bold;
  margin-bottom: 15px;
}

del {
  color: rgba(0, 0, 0, 0.3);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//unpkg.com/vue@latest/dist/vue.js"></script>
<div id="app" data-server-rendered="true">
  <h2>Freelance list</h2>
  <div class="articles">
    <article>
      <h3>louane</h3>
      <p>
        City : <strong>courbevoie</strong>
        <br> Phone : <strong>05-36-23-51-89</strong>
      </p>
    </article>
    <article>
      <h3>indra</h3>
      <p>
        City : <strong>rheden</strong>
        <br> Phone : <strong>(354)-415-2419</strong>
      </p>
    </article>
    <article>
      <h3>angelo</h3>
      <p>
        City : <strong>montpreveyres</strong>
        <br> Phone : <strong>(883)-474-9314</strong>
      </p>
    </article>
  </div>

  <a href="/prev-link" class="prev">prev</a>
  <a href="/next-link" class="next">next</a>
</div>
<!-- server rendered -->

【讨论】:

  • mmmh 这增加了很多模板重复 mmmh 因为这里的标记当然不是真正的标记,所以对于真正的标记,这看起来真的很混乱...... mmmmh 并且不太可维护
  • 您只有一个模板。您还拥有服务器生成的 HTML。他们必须匹配。没有办法解决这个问题。如果您没有服务器生成的 HTML,那么您的模板将完全相同。如果您不想拥有模板,请坚持使用 jQuery 操作 DOM。
  • 这不是我不想有一个模板,而是在你的例子中,如果我的服务器模板发生变化,我将不得不在 Vue 模板中复制更改,这是主要的这里有问题(它不可扩展)。
  • 您可能想查看Vue on the server,以便您可以在两个地方使用相同的模板。
  • 我会再看一次(我已经为其他用例做过),我真的很想看看 Vue 是否可以在这些基本用例中替代 jQuery,但它似乎有点复杂。 ..
【解决方案3】:

Vue 论坛上的某个人发现了一个与此处发布的内容相近的好方法,但更适合我的需要:https://forum.vuejs.org/t/using-vue-to-enhance-existing-multi-page-server-rendered-classic-web-app/30934/20

【讨论】:

    猜你喜欢
    • 2018-10-02
    • 2019-03-07
    • 2011-07-15
    • 2019-11-30
    • 2015-10-22
    • 2018-07-05
    • 1970-01-01
    • 2010-09-05
    相关资源
    最近更新 更多