VueRouter底层实现-Pjax技术
介绍
Pjax ,即 pushState + ajax ,用于无刷新页面资源加载,pjax 的工作原理是通过 ajax 从服务器端获取 HTML,在页面中用获取到的 HTML 替换指定容器元素中的内容。然后使用pushState技术更新浏览器地址栏中的当前地址。pjax 可以避免渲染页面重复内容,做到页面局部更新,从而达到性能提升。
实现
首先封装一个 ajax
1 2 3 4 5 6 7 8 9 10 function request (url ) { fetch (url).then (response => { if (!response.ok ) { throw new Error ('Network response was not ok' ); } return response.json (); }) .then (data => console .log (data)) .catch (error => console .error ('There was a problem with your fetch operation:' , error)); }
页面链接事件绑定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 function convertLinks (opt: Options ) { const links = document .querySelectorAll ('a' ) for (const link of links) { if ( link.host === window .location .host && link.pathname !== window .location .pathname && link.target !== '_blank' ) { bindLink (link, opt) } } } function bindLink (el, opt ) { el.onclick = (e ) => { e.preventDefault () handlePopState (el.href , opt, el.hash ) } }
lastTrigger互斥变量防止链接多次跳转
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 let lastTrigger = null export function handlePopState ( url, opt, hash, push = true , ) { if (lastTrigger === url) return opt.beforeSend && opt.beforeSend () lastTrigger = url requestPage (url, opt.container ) .then ((res ) => { if (!res.targetContent ) { throw new Error ('targetContent is empty' ) } push && history.pushState ({ url : window .location .href }, '' , url) scollToHash (hash) if (opt.container && lastTrigger === url) { const container = document .querySelector (opt.container ) container && (container.innerHTML = res.targetContent ) document .title = res.title lastTrigger = null convertLinks (opt) opt.complete && opt.complete (true ) } }) .catch ((err ) => { console .error ('PJAX:' , err) if (lastTrigger === url) { lastTrigger = null opt.complete && opt.complete (false ) } }) }
导出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 const pjax = { version : '__VERSION__' , initialed : false , connect (opt?: Options ) { const option : Options = { container : opt?.container , beforeSend ( ) { history.replaceState ( { url : window .location .href }, '' , window .location .href , ) opt?.beforeSend && opt.beforeSend () }, complete (success ) { opt?.complete && opt.complete (success) }, } convertLinks (option) if (!this .initialed ) { window .addEventListener ('popstate' , (e ) => { handlePopState (e.state .url , option, '' , false ) }) } this .initialed = true }, }