美文网首页前端实际项目视野拓展H5
vue-router 使用装饰器进行按需加载与页面加载动画

vue-router 使用装饰器进行按需加载与页面加载动画

作者: 书SHU | 来源:发表于2017-04-10 13:43 被阅读3497次

    1. 在 app.vue 中添加 div

    这里假设 div 为 .page-loading-loader 。使用相应的 css3 动画进行显示,这里 css 代码略。

    //- app.vue
    
    #app
        .page-loading-loader
              .my-css-animation-element
    
        .page-header
    
        .page-main
            router-view
    
        .page-footer
    

    我们用 js 来动态控制 .page-loading-loader 的显示与隐藏。
    为了方便,示例中使用 jQuery 来处理。

    2. 原有的组件加载方式

    webpack 中有两种加载方式,一种是直接加载,一种是按需加载:

    # router.coffee
    [
        {
            path: '/load'
            name: 'load'
            # 直接加载:
            component: require('./views/load.vue')
        },
        {
            path: '/lazy-load'
            name: 'lazy_load'
            # 按需加载:
            component: (resolve) ->
                require(['../views/lazy-load.vue'], resolve)
        },
        ...
    ]
    

    3. 页面加载动画

    直接加载模式,由于已经加载到浏览器中了,无需使用加载动画。
    故,只有按需加载时需要使用页面加载动画。

    3.1 处理动画

    # router.coffee
    [
        {
            path: '/lazy-load'
            name: 'lazy_load'
            # 按需加载:
            component: (resolve) ->
                # 加载前显示动画
                $('.page-loading-loader').show()
    
                require(['../views/lazy-load.vue'], (component) ->
                    # 加载完成后渲染并隐藏动画
                    resolve(component)
                    $('.page-loading-loader').hide()
                )
        },
        ...
    ]
    

    但是这样会有一个问题,比如有两个 url,对应两个按需加载的组件。
    假设组件 1 加载需要 10 秒,组件 2 加载需要 2 秒。
    用户点击组件 1 的 router-link,然后在 10 秒内(即组件 1 还没有加载完成时)点击组件 2 的 router-link,此时加载组件 2。

    由于组件 2 加载速度快,先返回,先 resolve,resolve 后又 hide() 了动画 div。
    之后已经没有动画了。
    但是组件 1 还在加载,当几秒过后加载完成,再次 resolve,此时页面变成了组件 1。

    这样用户的意愿是要查看组件 1,后来又变更了想法,要访问组件 2。但是我们的程序先展示组件 2,然后隐藏动画,过一会又展示了组件 1。导致逻辑混乱。

    3.2 解决加载顺序混乱

    问题的关键在于 resolve 时没有检查当前的路由是否与要 resolve 的路由一致。故,每次加载时保存到一个全局变量。每次 resolve 时,进行对比。

    # router.coffee
    
    # 值仅用于每次获取时作为唯一标记,值没有任何用途。
    # 也可以用 Math.random() 或者 new Date().getTime() 等代替
    uuid = 1
    unique = ->
        uuid += 1
        return uuid
    
    [
        {
            path: '/lazy-load'
            name: 'lazy_load'
            # 按需加载:
            component: (resolve) ->
                # 加载前显示动画
                $('.page-loading-loader').show()
                _uuid = unique()
    
                require(['../views/lazy-load.vue'], (component) ->
                    # 如果加载完成的组件就是当前用户最后点击的那个组件,那么才 resolve
                    if _uuid == uuid
                        # 加载完成后渲染并隐藏动画
                        resolve(component)
                        $('.page-loading-loader').hide()
                )
        },
        ...
    ]
    

    3.3 封装

    3.3.1 封装 require

    webpack 中的 require 不能使用变量的形式来加载组件,这就导致不能使用传递变量的方法来封装。
    即,如下代码是行不通的:

    # router.coffee
    
    # 值仅用于每次获取时作为唯一标记,值没有任何用途。
    # 也可以用 Math.random() 或者 new Date().getTime() 等代替
    uuid = 1
    unique = ->
        uuid += 1
        return uuid
    
    
    fetchComponent = (path, resolve) ->
            # 加载前显示动画
            $('.page-loading-loader').show()
            _uuid = unique()
    
            # 这是的 require 会报错的!!!
            require([path], (component) ->
                # 如果加载完成的组件就是当前用户最后点击的那个组件,那么才 resolve
                if _uuid == uuid
                    # 加载完成后渲染并隐藏动画
                    resolve(component)
                    $('.page-loading-loader').hide()
            )
    
    
    router: [
        {
            path: '/lazy-load'
            name: 'lazy_load'
            # 按需加载:
            component: (resolve) ->
                fetchComponent('../views/lazy-load.vue', resolve)
        },
        ...
    ]
    
    3.3.2 使用装饰器

    我们不过是想在 require 前后做一些初始化及收尾的工作,用 装饰器 来解决再好不过。

    # 值仅用于每次获取时作为唯一标记,值没有任何用途。
    # 也可以用 Math.random() 或者 new Date().getTime() 等
    uuid = 1
    unique = ->
        uuid += 1
        return uuid
    
    
    setLoadingVisibility = (func) ->
        $('.page-loading-loader')[func]()
    
    
    # require 之前及之后,做一些初始化及收尾工作。装饰器。
    decorator = (cb) ->
        (resolve) ->
            setLoadingVisibility('show')
    
            current_component = unique()
    
            # hideLoadingResolve 相当于 webpack 的 component: (resolve) -> 中的 resolve,
            # 只是多做一些收尾工作(隐藏 loading 动画),最终还是返回了 resolve
            hideLoadingResolve = (fetched_component) ->
                (component) ->
                    # 如果获取完成的不是当前正在获取的组件,那么什么也不做。
                    # 如,用户连续点击两个导航菜单:
                    # 第一个还没有获取成功,第二个就被点击了。
                    # 此时此 resolve 最新的那个
                    if current_component == fetched_component
                        setLoadingVisibility('hide')
                        return resolve(component)
    
            cb(hideLoadingResolve(current_component))
    
    
    router: [
        {
            path: '/'
            name: 'dashboard'
            component: decorator (resolve) ->
                require(['./list.vue'], resolve)
        },
        {
            path: '/group'
            name: 'group'
            component: decorator (resolve) ->
                require(['./group.vue'], resolve)
        },
        ...
    ]
    

    如上所示加载组件就方便了,用 decorator 装饰一下就好了:

    之前为:

    component: (resolve) ->
        require(['./list.vue'], resolve)
    

    现在加一个单词 decorator 即可:

    component: decorator (resolve) ->
        require(['./list.vue'], resolve)
    

    以上。

    相关文章

      网友评论

      • 积小流:你那么极简的写法是怎么编译成功的

      本文标题:vue-router 使用装饰器进行按需加载与页面加载动画

      本文链接:https://www.haomeiwen.com/subject/wmesattx.html