【问题标题】:multiple vue js instances vs components , ajax tabs多个 vue js 实例与组件,ajax 选项卡
【发布时间】:2018-08-03 15:14:40
【问题描述】:

我想使用 vue js 渲染 ajax 选项卡。对于每个选项卡,ajax 请求从 api 获取模板 + 数据。

这些是标签:

<div id="tabs">
  <ul class="nav nav-tabs">
    <li class="active"><a href="#tab1" role="tab" data-toggle="tab">Tab1</a></li>
    <li><a href="#tab2" ajax-url="/tab2" role="tab" data-toggle="tab">Tab2</a></li>
    <li><a href="#tab3" ajax-url="/tab3" role="tab" data-toggle="tab">Tab3</a></li>
    <li><a href="#tab4" ajax-url="/tab4" role="tab" data-toggle="tab">Tab4</a></li>
  </ul>

  <div class="tab-content">
    <div role="tabpanel" class="tab-pane active" id="tab1"></div>
    <div role="tabpanel" class="tab-pane" id="tab2"></div>
    <div role="tabpanel" class="tab-pane" id="tab3"></div>
    <div role="tabpanel" class="tab-pane" id="tab4"></div>
  </div>
</div>

<script>
$('#tabs').on('shown.bs.tab', function (event) {
   var url = event.target.getAttribute("ajax-url");
   // fetch template and data..
   // init vue instance for the new tab
})
</script>

我应该如何将 vue 集成到其中?每个选项卡或组件的单独 vue 实例?我还需要支持预加载一些选项卡。
由于我需要按需初始化 vue 实例,我不确定管理它的好方法是什么。对于角度,我会为每个选项卡定义 ng-controller。但是在 vue.js 中似乎没有一个正确的项目架构。

【问题讨论】:

    标签: javascript ajax vue.js bootstrap-tabs


    【解决方案1】:

    对于标签,使用vue-router

    每条路由在激活时都会加载一个单独的视图/组件。但是,这是预加载的,因为 vue.js 包文件包含渲染所需的所有代码。

    Vue Lifecycle hooks,例如createdmounted,通常用于在每个视图中执行设置或获取附加数据。

    看起来您的示例足够同质,单个 vue 实例可以处理所有工作(不需要 jQuery/AJAX)。

    如果您的用例差异很大,有时构建多个单独的组件会更有效,每个组件都编译到自己的 JS 文件中(通常使用 Webpack 的多个 'entry' 设置完成)。

    更新

    有一种使用 CLI 捆绑 Vue 组件的方法,请参阅:Compile .vue file into .js file without webpack or browserify

    请注意,每个组件都可以使用类似于 Angular 'ng-controller' 指令的 el 属性挂载一个单独的选项卡:

    new Vue({
      el: '#tab1',
      data () {
        return {
          obj
        }
      }
    })
    

    但是,我认为您要做的是从 Vue 外部控制 Vue 实例(即:使用 jQuery)。可能不建议这样做。

    如果你决定使用 Webpack,你仍然可以使用 Bootstrap。有多种集成 Bootstrap 的方法,我个人建议导入 SCSS。但是,您的里程可能会有所不同。但不要被推迟,有很多在线资源可以让您继续前进。

    【讨论】:

    • 我必须为每个标签使用一个组件吗?对于这个项目,我不能使用 webpack 或其他构建器。我更喜欢逐步集成 vue.js,所以我还不能放弃 jQuery。我需要 jQuery 进行引导。我想要的是为每个选项卡加载一个模板 + 数据。 vue-router 看起来有些复杂,大多数文档都假设我使用 webpack/bundler
    • 查看我的更新以获取此问题的答案。到目前为止,您尝试过什么?
    • CLI 也不是我的选择,因为我说过我不使用像 webpack 这样的构建器并且包括 vue cli。尝试了 vue 路由器,但它似乎对我的目的来说太重了(我不需要 hashbang,链接状态管理,只需在单击时加载 ajax 选项卡)。我尝试像 vue router 一样在 JS 代码上定义标签,然后在
    • 元素上使用 v-for。但是有什么方法可以像 bootstrap 一样在 HTML 中定义选项卡吗?
    【解决方案2】:

    您必须使用单独的 Vue 实例,以防您不想要 vue-router.. 但它会导致一些内存泄漏一些问题所以对一些这个.. 当您在单击选项卡时发出 ajax 请求时,您必须首先销毁当前的 Vue 实例..然后将数据添加到 dom

     let app =  new Vue({
                el:  '#app',
    
            })
    // doo other stuff
        $.ajax({
          // ajax settings
          success: function(data){
           // here you should destroy the Old Vue instance otherwise it will make issues
            app.$destroy()
            $('body').html(data);
          }
        })
    

    我建议你制作一个小的 javascript 插件.. 单击链接时,它将获取链接的 url 执行 ajax 请求,并在每次请求时将数据提供给页面,它将触发回调..您破坏旧的新实例.. 对于某种加载器,您可以在 ajax 请求之前使用另一个回调来显示页面加载器或其他东西..

    我在我的一个项目中使用了这种技术。我用 jquery ajax 制作了一个插件... 在这里……

        import $ from 'jquery';
    import anime from 'animejs';
    export let Loader = (function(window , document , undefined){
    
        class Loader{
    
            constructor(options = null){
                this.options = Object.assign({ pageProgressBar : false , pageProgressBarDuraion: 2000 } , options);
                this.functions = [];
                this.xhr = null;
                this.routes = this.options.routes || [];
    
                window.addEventListener('load' , this.init.bind(this));
            }        
            init(){
    
                if(this.options.container)
                    this.$container = $(this.options.container).length ? $(this.options.container) : $(document.body)
                else            
                    this.$container = $(document.body)    
    
    
                this.functions['popstate'] =  this.onPopState.bind(this);
                window.addEventListener('popstate' , this.functions['popstate'] );
    
                this.functions['handleLinks'] = this.handleLinks.bind(this);
                window.addEventListener('click' , this.functions['handleLinks']);
            }
            pushHistory(type , url){
                var state = {url : url , type : type};
                window.history.pushState(state , '' , url);
            }
            onPopState(e){
                var state = e.state;
    
                if(state == null)
                    return true;
    
                this.loadUrl(state.url)
            }
            handleLinks(e){
                if(!e.target.matches('[ajax-link]'))
                    return ;
                e.preventDefault();
    
                var url = e.target.href;
                var self = this;
    
                this.loadUrl(url).then(function(){            
                    self.pushHistory('get' , url);
                });
            }
    
            load(url)
            {
                let self = this;
                return new Promise(function(resolve , reject){
                    self.loadUrl(url).then(function(){
    
                        self.pushHistory('get' , url);
                        resolve();
                    });    
                });
            }
    
    
            loadUrl(url){
                var isError = true;
                if(this.xhr)
                {
                    isError = false;
                    if(this.options.onAbort)
                        this.options.onAbort();
                    this.xhr.abort();
                    if(this.options.pageProgressBar)
                        restartProgressBar();
                    this.xhr = null;
                }
    
                var route = this.getMatchingRoute(url);
                if(route)
                {
                    if(route.beforeLoad)
                        route.beforeLoad();
                }
    
                if(this.beforeLoading)
                    this.beforeLoading();
    
    
                var self = this;
                return new Promise(function(resolve , reject){
                   self.xhr = $.ajax({
                        url : url,
                        type: 'get',
                        cache : false,
                        beforeSend (xhr){
    
                            if(self.options.onLoading)
                                self.options.onLoading(xhr);
    
                            if(self.options.pageProgressBar)
                                startProgressBar( self.options.pageProgressBarDuration );
                        },
                        success (data){
    
                            self.$container.html(data);
                            self.xhr = null;
                            resolve();
    
                            if(route && route.afterLoad)
                                route.afterLoad();
    
                            if(self.options.onLoaded)
                                self.options.onLoaded(data);
                            if(self.options.pageProgressBar)
                                endProgressBar();
                        },
                        error (response){
                            if(response.response)
                                window.location = url;
                        },
                    });
                });
            }
            reload(){
                this.loadUrl(window.location.href);
            }
            getMatchingRoute(url){
                if(this.routes)
                {   
                    for(let i = 0; i< this.routes.length ; i++)
                    {
                        var route = this.routes[i];
                        if(url.indexOf(route.route) >= 0 ){
                            return route;
                        }
                    };
                }
                return null;
            }
            destory(){
                window.removeEventListener('popstate' , this.functions['popstate'] );
            }
        }
        return Loader;
    
    
    
        // Provate progressbar methods
        var $pageProgressBar = null;
    
        function restartProgressBar(){
            $pageProgressBar.remove();
            anime.remove($pageProgressBar[0]);
        }
        function startProgressBar( duration = 3000){
            $pageProgressBar = $('<div>').addClass('page-progressbar')
            $('body').append($pageProgressBar)
    
            anime({
                targets: $pageProgressBar[0],
                width: '70%',
                easing : 'easeInOutQuad',
                duration : 3000
            });
        }
        function endProgressBar(){
            anime.remove($pageProgressBar[0]);
            var $prog = $('.page-progressbar');
            $prog.css({ width: '100%' , transition :".35s all" , 'webkit-transition' : ".35s all" });
            setTimeout(()=>{
                $prog.css({ opacity: '0'});
                setTimeout( () => $pageProgressBar[0].remove() , 200);
            },350);
        }
    
    
    }(window , document , undefined));
    

    它有一些进度条和其他可以删除的东西来修改它 现在你要做的就是

    window.Loader = new Loader({
    // tell it the tag you want to put data in 
    container :"#app-data",
    })
    

    然后将ajax-link 属性添加到您的任何链接中,它的选项卡链接 它将拉取 url 并将内容放入您指定的容器中 还有一些回调 您可以添加Loader.beforeLoading = function(){ app.$destroy(); } 就是这样

    【讨论】:

      猜你喜欢
      相关资源
      最近更新 更多
      热门标签