【问题标题】:Enhance parts of multi page application with react or vue使用 react 或 vue 增强多页面应用程序的某些部分
【发布时间】:2018-10-02 14:54:44
【问题描述】:

我正在开发一个企业应用程序,在服务器端使用 javahibernatespring mvc,在客户端使用 jquery(不是 SPA)。

现在在搜索页面中,我使用 ajax 并仅获得 json 响应,但我不想在每个搜索或分页请求中写下类似的内容。

function(ajaxData) {
    ....
    $('#search').html('' +
        '<div class="search-container">' + 
          '<div class="search-item">' +
             '<div class="title-item">'+ajaxData.title+'</div>' + 
             ...
             ...
          '</div>' + 
        '</div>'
    )
    ....
}

我认为在此页面中使用jsxreactvue 组件 很容易刷新结果。

我还想重用一些 html 块,我认为使用 reactvue 会很容易

我曾经构建了一个小型 SPA 项目,它都是关于 npmwebpack 以及捆绑,但我真的不想使用它们,因为我有一个 multi页面应用程序,非常适合我的项目。

我认为 facebook 正在做同样的事情,他们使用 react 但 facebook 不是 SPA。

我怎样才能实现这种混合方法?

【问题讨论】:

  • 没有 JSX 很难使用 react。并且使用 JSX 需要 webpack。如果你不想使用 webpack,看起来 Vue 对你来说是一个更简单的选择
  • 做一个简单的 ajax 请求,jQuery 似乎是一个不错的选择。您显然已经拥有它,只需使用它。 $.ajax()。请参阅文档:api.jquery.com/jquery.ajax
  • 您可以在 CDN 上找到生产就绪的 vuejs 文件,只需一个脚本标签并在 mvvm 中开始您的代码。 angular1.x 也可以,但建议使用 vue。 react 也可以不编译运行,但仅供练习
  • 你可能不需要 React/Vue,你看过 fetch 吗?实现AJAX请求的一种非常简单的方法davidwalsh.name/fetch
  • @Pixelomo 这与 ajax 无关,我更新了我的问题

标签: javascript jquery reactjs vue.js


【解决方案1】:

我过去做过类似的事情。我在 DOM 中注入了一个小的 react 组件。

我是这样做的:

在 JSX 中创建一个 React 组件,我们称之为Demo

export class Demo extends React.Component {
    render() {
        return <h1>This is a dummy component.</h1>
    }
}

现在使用renderToStaticMarkup 函数获取静态html。

const staticMarkup = renderToStaticMarkup(<Demo PASS_YOUR_PROPS/>);

您拥有 HTML,现在您可以使用 innerHTML 在所需位置插入此标记。

如果我误解了您的问题,我们深表歉意。

更新

我们也可以为此使用render()。示例:

document.getElementById("button").onclick = () => {
  render(
    <Demo PASS_YOUR_PROPS/>,
    document.getElementById("root")
  );
};

使用 render() 和 renderToStaticMarkup 的工作解决方案:https://codesandbox.io/s/w061xx0n38

渲染()

在提供的容器中将 ReactElement 渲染到 DOM 中,然后 返回对组件的引用。

如果 ReactElement 先前已渲染到容器中,则此 将对它执行更新,并且只在必要时改变 DOM 反映最新的 React 组件。

renderToStaticMarkup()

这不会创建额外的 DOM 属性,例如 data-react-id, React 在内部使用。如果您想将 React 用作 简单的静态页面生成器,因为剥离了额外的属性 可以节省很多字节。

【讨论】:

  • renderToStaticMarkup 不仅仅在节点服务器上与ReactDOMServer 一起使用?特别是服务器端渲染 SSR .... 我正在使用 spring-mvc 我想在客户端而不是在服务器中实现这一点
  • 它的使用不限于服务器。我在客户端用过。
  • 根据您添加的 sn-p,这将完全适合图片。为您的 HTML 元素/JSX 创建一个 React 组件,并将成功返回的数据作为 props 传递。
  • 为什么不直接使用render()ReactDOM 包?
  • @Youssef 我创建了一个示例,展示了如何使用 render() 和 renderToStaticMarkup() 方法实现此目的。请看一看。 codesandbox.io/s/w061xx0n38。我之前从未为此目的使用 render() ,但既然你提到了它,我就尝试了 render 方法。我已经更新了我的答案。
【解决方案2】:

由于您有一个现有的多页面应用程序没有构建步骤(即没有 webpack/babel),我相信实现您想要的一种非常简单的方法是使用 Vue.js。

您可以定义模板并仅更新数据。

这是一个演示,您将如何执行问题中显示的代码:

new Vue({
  el: '#app',
  data: {
    ajaxDataAvailable: false,
    ajaxData: {
      title: '',
      results: []
    }
  },
  methods: {
    fetchUsers() {
      this.ajaxDataAvailable = false; // hide user list
      $.getJSON("https://jsonplaceholder.typicode.com/users", (data) => {
        this.ajaxData.title = 'These are our Users at ' + new Date().toISOString();
        this.ajaxData.results = data;
      	this.ajaxDataAvailable = true; // allow users to be displayed
      });
    }
  }
})
/* CSS just for demo, does not affect functionality could be existing CSS */
.search-container { border: 2px solid black; padding: 5px; }
.title-item { background: gray; font-weight: bold; font-size: x-large; }
.result-item { border: 1px solid gray; padding: 3px; }
<script src="https://unpkg.com/vue"></script>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>

<div id="app">
  <button @click="fetchUsers">Click to fetch the Users</button><br><br>

  <div class="search-container" v-if="ajaxDataAvailable">
    <div class="search-item">
      <div class="title-item"> {{ ajaxData.title }}</div>
      <div class="result-item" v-for="result in ajaxData.results">
        Name: {{ result.name }} - Phone: {{ result.phone }} - Edit name: <input v-model="result.name">
      </div>
    </div>
  </div>

</div>

在这个例子中,我们有:

  • 将执行 ajax 调用的方法 fetchUsers
  • fetchUsers 方法通过@click="methodName" 绑定到&lt;button&gt;click 事件(v-on:click="methodName" 的简写形式)。
  • 一个v-if (v-if="ajaxDataAvailable") 使.search-container div 隐藏,直到ajaxDataAvailable 属性为true
  • 使用插值法渲染模板中的一些数据:{{ ajaxData.title }},注意这是从下面的 Vue 实例的 data: 部分(new Vue({... 代码)中声明的对象中选取的值,并且是 自动ajaxData.title 更改时更新(Ajax 调用完成时fetchUsers 方法内部发生的情况。
  • 使用v-for 呈现列表:v-for="result in ajaxData.results"。这在 ajaxData.results 数组中进行迭代,该数组在 fetchUsers 方法中更新得太快了。
  • &lt;input&gt; 元素与v-model 指令结合使用,这允许我们直接编辑result.name 值(这也会自动更新模板)。

Vue 还有很多,这只是一个例子。如果需要,可以制作更详细的演示。

至于集成到现有应用程序中,您可以将这段代码粘贴到任何 HTML 页面中,它已经可以工作了,不需要任何 webpack/babel。

【讨论】:

  • 在反应文档React is unaware of changes made to the DOM outside of React. It determines updates based on its own internal representation, and if the same DOM nodes are manipulated by another library, React gets confused and has no way to recover.vuejs 的情况是否相同?
  • 是的,虽然如果你知道哪些节点会具体改变,你可以稍微补救一下,一般来说,这也适用于 Vue。我能问一下为什么会这样吗?
  • 啊,Vue 不会“感到困惑,无法恢复”。事实上,如果其他东西改变了 Vue 之外的 DOM,如果程序员没有手动处理,它会在下一个更新周期被 Vue 简单地覆盖(每当 Vue 实例的某些数据发生变化时都会发生这种情况)。
【解决方案3】:

因此,您可以做两件事来重用您的代码:

  • 我建议 React 将代码作为组件共享。来自官方docs 的这个页面解释了如何使用 react 和 jquery。 用于集成 react 和 jquery 的其他资源jquery-ui with reactUsing react and jquery togetherreact-training
  • 或者,使用一些模板引擎,这样您就无需费心集成新库并使其与 jquery 一起工作。这个 SO answer 提供了很多这样做的选项。

除非您计划迁移您的 jquery 应用程序以从长远来看做出反应,否则我不建议您只在一页上使用反应。使用模板引擎路线会更容易。

【讨论】:

    【解决方案4】:

    从需求来看,你只需要:

    1. 动态、数据驱动的 HTML 块
    2. 哪些需要可重复使用

    在这种情况下,由于我们不需要状态,因此拥有一个像 react/vue/angular 这样的完整框架可能是矫枉过正。

    我的建议是使用 jQuery Templates PluginHandlebars 这样的模板引擎

    您甚至可以将 HTML 块存储为单独的可重用模块,您可以根据需要跨页面调用这些模块。

    使用把手的示例:

    var tmpl = Handlebars.compile(document.getElementById("comment-template").innerHTML);
    
    function simulatedAjax(cb){
      setTimeout(function(){
        cb(null, {title:'First Comment', body: 'This is the first comment. Be sure to add yours too'});
      },2000);
    }
    
    
    simulatedAjax(function(err, data){
      if(err){
        // Handle error
        return;
      }
      document.getElementById('target').innerHTML = tmpl(data);
    })
    <script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.11/handlebars.min.js"></script>
    
    <div id="target">
    Loading...
    </div>
    
    <script id="comment-template" type="text/x-handlebars-template">
      <div class="comment">
        <h1>{{title}}</h1>
        <div class="body">
          {{body}}
        </div>
      </div>
    </script>

    注意:

    使用这种方法的一个缺点(与 react/vue/angular 之类的框架相反)是您需要手动处理事件绑定和解除绑定。您可以通过将模板包装在一个自动安装和卸载它的函数中来在一定程度上缓解这种情况。

    // Single reuable JS file for each component
    (function(namespace) {
      var _tmpl = Handlebars.compile('<div class="comment"><h1>{{title}}</h1><p>{{body}}</p></div>');
    
      function titleClickHandler(ev) {
        alert(ev.target.innerHTML)
      }
    
      function CommentWidget(target, data) {
        var self = this;
        self.data = data;
        self.target = document.querySelector(target);
      }
      CommentWidget.prototype.render = function() {
        var self = this;
        self.target.innerHTML = _tmpl(self.data);
    
        // Register Event Listeners here
        self.target.querySelector('h1').addEventListener('click', titleClickHandler)
    
      }
      CommentWidget.prototype.unmount = function() {
        var self = this;
        // Unregister Event Listeners here
        self.target.querySelector('h1').removeEventListener('click', titleClickHandler);
    
        self.target.innerHTML = '';
    
      }
    
      window._widgets.CommentWidget = CommentWidget;
    
    })(window._widgets = window._widgets || {})
    
    
    // Usage on your page 
    var w = new _widgets.CommentWidget('#target', {
      title: 'Comment title',
      body: 'Comment body is remarkably unimaginative'
    });
    
    // Render the widget and automatically bind listeners
    w.render();
    
    // Unmount and perform clean up at a later time if needed 
    //w.unmount();
    <script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.11/handlebars.min.js"></script>
    
    <div id="target"></div>

    【讨论】:

      猜你喜欢
      • 2018-09-09
      • 2012-01-05
      • 2016-05-17
      • 2021-05-29
      • 2015-11-03
      • 1970-01-01
      • 1970-01-01
      • 2017-06-26
      • 2016-07-28
      相关资源
      最近更新 更多