pjax使用小结(二)

简介: pjax使用小结(二)

pjax失效情况



会有一些情况导致 pjax 失效,下面结合源码分析下(省略部分无关代码)


function handleClick(event, container, options) {
    ...
    // 1. 点击事件的事件源不是a标签。使用a标签可以做到对旧版本浏览器的兼容,所以不建议使用其他标签注册事件
    if (link.tagName.toUpperCase() !== 'A')
        throw "$.fn.pjax or $.pjax.click requires an anchor element"
    // 2. 使用鼠标滚轮点击(新标签页打开)
    // 点击超链接的同时按下Shift、Ctrl、Alt和Meta(在Windows键盘中是Windows键,在苹果机中是Cmd键)
    // 作用分别代表新窗口打开、新标签打开(不切换标签)、下载、新标签打开(切换标签)
    if (event.which > 1 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey)
        return
    // 3. 跨域(网络通讯协议,域名不一致)
    if (location.protocol !== link.protocol || location.hostname !== link.hostname)
        return
    // 4. 当前页面的锚点定位
    if (link.href.indexOf('#') > -1 && stripHash(link) == stripHash(location))
        return
    // 5. 已经阻止元素发生默认的行为(url跳转)
    if (event.isDefaultPrevented())
        return
    ...
    var clickEvent = $.Event('pjax:click')
    $(link).trigger(clickEvent, [opts])
    // 6. pjax:click事件回调中已经阻止元素发生默认的行为(url跳转)
    if (!clickEvent.isDefaultPrevented()) {
        pjax(opts)
        event.preventDefault()// 阻止url跳转
        $(link).trigger('pjax:clicked', [opts])
    }
}


除了上述情况之外,还有下列几种情况:


ajax 请求失败,或者 timeout 后请求被中止

当前页面的 X-PJAX-Version 和请求的新页面版本不一致

请求得到完整的页面(包含 html 标签)却没设置 fragment 参数


事件



1. 点击链接后触发的一系列事件, 除了 pjax:click 和 pjax:clicked 的事件源是点击的按钮,其他事件的事件源都是要替换内容的容器。可以在 pjax:start 事件触发时开始过度动画,在 pjax:end 事件触发时结束过度动画。


image.png

image.png


  • 注意:


pjax:beforeReplace 事件前 pjax 会调用 extractContainer 函数处理页面内容,即以 script[src] 的形式引入的 js 脚本不会被重复加载,有必要可以改下源码。


2. 浏览器前进/后退导航时触发的事件(暂时没做过多研究)


image.png

image.png


服务端配置


我的项目是 Spring MVC + velocity 的组合,这里就以此为例子,其他语言和框架的服务端可以参考下这里的思路。

项目中使用的视图解析器是


org.springframework.web.servlet.view.velocity.VelocityLayoutViewResolver 这个类,好处是可以使用模版技术,每个页面可以只写主体内容,公共部分统一写在模版里面,是不是和 pjax 绝配哈!pjax.js 默认会在请求头加入 X_PJAX 字段,并置为 true,所以以此来判断是否 pjax 请求。对于普通的请求使用常规的模版,pjax 请求则使用空模版或者特定的模版。


常规模版内容:


<!doctype html>
<html>
    #set($basePath = "screen/contain")
    <head>
        <meta http-equiv="x-pjax-version" content="$!{X-PJAX-Version}"/>
        #parse("$basePath/html-head.vm")
    </head>
    <body>
        <section id="container">
            #parse("$basePath/frame-head.vm")
            #parse("$basePath/frame-left.vm")
            <section id="main-content">
                <section class="wrapper">
                    $screen_content ##页面内容
                </section>
            </section>
            #parse("$basePath/frame-bottom.vm")
        </section>
    </body>
</html>


添加 SpringMVC 中的 Interceptor 拦截器,用于后端渲染前插入 pjax 处理


public class PjaxInterceptor extends HandlerInterceptorAdapter {
    @Value("${X-PJAX-Version}")
    private String X_PJAX_VERSION;
    /**
     * Controller 方法调用之后,页面渲染前执行
     * 
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        if (modelAndView != null) {
            boolean isPajx = Boolean.parseBoolean(request.getHeader("X-PJAX"));// 值为true表示pjax请求,这是重点
            ModelMap model = modelAndView.getModelMap();
            model.addAttribute("X-PJAX-Version", X_PJAX_VERSION);// 设置当前页面的pjax版本
            if (isPajx) {
                model.addAttribute("layout", "layout_pjax.vm");// 指定pjax请求时使用的模版
                // 在vm页面中通过 #set($layout = 'xxx.vm') 的方式指定模版
                response.setHeader("X-PJAX-Version", X_PJAX_VERSION);// 响应内容的pjax版本,有新模版发布时,通过配置文件修改版本来强制页面刷新
            }
        }
    }
}


xml 配置


<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <bean id="pjaxInterceptor" class="xxx.PjaxInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>


pjax 请求模版页面:layout_pjax.vm


<title>$!{title}</title>
$screen_content


模版中使用 title 标签,这样执行 pjax 请求时不仅地址栏 url 会变化,而且浏览器标签的标题内容也会变化。


针对没有服务端处理的方案如下:


// fragment一般同container一致
$(document).pjax('a[data-pjax]', '#main-content .wrapper', {fragment: '#main-content .wrapper'});


插件伴侣——NProgress



比较漂亮的一款进度条插件,用法十分简单,很适合做pjax的过度动画,详细用法在该项目 github 上有介绍


image.png


NProgress


  • 示例:


$(document).on('pjax:start', NProgress.start).on('pjax:end', NProgress.done);


结语



虽然个人还是比较喜欢造轮子(有成就感),不怎么喜欢用插件(一般插件使用复杂,文档少学习成本大,还不如自己写),但看了 pjax 的源码后感觉真要自己也使用 pushState + ajax 的方式简单的实现它的功能,还是要踩不少坑的,所以为什么要放着这么个易用又精致的小轮子不用呢?我的项目是一个管理系统,统一的 左侧菜单 + 右侧table 的布局,每个页面都需要一个独立访问的 url,非常适合使用 pjax。由于使用的 velocity 模版技术,集成 pjax 就是分分钟的事,不仅对原先的代码完全没影响,还提升了加载速度,页面过度效果更好,再用上了 NProgress,感觉逼格又上升不少,哈哈。


目录
相关文章
|
3月前
|
JavaScript 前端开发 CDN
jQuery学习记录--jQuery语法,选择器,事件及hide(),show(), toggle()
本文是关于jQuery的学习笔记,涵盖了jQuery的简介、语法、选择器、事件处理以及hide()、show()、toggle()等方法的使用。
jQuery学习记录--jQuery语法,选择器,事件及hide(),show(), toggle()
UITextView,UIWebView 直接显示html代码
UITextView,UIWebView 直接显示html代码
73 0
|
SQL 开发框架 移动开发
pjax使用小结(一)
pjax使用小结(一)
432 0
pjax使用小结(一)
|
移动开发 缓存 前端开发
PJAX实现
浏览器历史就像一堆卡片,如下所示:
PJAX实现
|
Web App开发 缓存 前端开发
|
数据安全/隐私保护 PHP
Yii2 Pjax 使用
参考:Yii2 Pjax Tutorial Pjax是JQuery的插件,结合ajax和Html5的pushState技术来实现异步刷新,每次通过a标签点击和form表单提交向服务器发送一个指定的请求之后,服务器都会返回需要更新的内容,Pjax会更新老旧内容以及向浏览记录中加入一条新的url而不需要更新刷新整个页面。
1846 0
|
PHP
如何在Laravel中使用pjax
在laravel中集成pjax动态加载引擎,加快页面访问速度。
2813 0