Одностраничные приложения используют JavaScript для динамического преобразования содержимого веб-страницы, чтобы избежать перезагрузки страницы; маршрутизация обеспечивает изменение адреса браузера и соответствующее изменение содержимого веб-страницы. Сочетание этих двух факторов дает нам одностраничное веб-приложение с хорошим интерфейсом.
Реализация внешней маршрутизации
Маршрутизация должна выполнять три функции:
①Адрес браузера меняется, и страница переключается;
②Нажмите кнопки [Назад] и [Вперед] в браузере, и содержимое веб-страницы изменится соответствующим образом;
③ Обновите браузер, веб-страница загрузит соответствующее содержимое текущего маршрута.
На одностраничной веб-странице простое изменение адреса браузера не приведет к перезагрузке веб-страницы. Например, если веб-страница изменяется с помощью простого URL-хэша, веб-страница не изменится. Поэтому наша маршрутизация в основном осуществляется путем отслеживания событий и использование js для динамического изменения содержимого веб-страницы.Есть две реализации:
hash
Маршрутизация: отслеживайте изменение хеш-значения адреса браузера, выполняйте соответствующие js для переключения веб-страницы.history
Маршрутизация: используйте API истории, чтобы изменить URL-адрес и изменить содержимое веб-страницы.
хэш-маршрутизация
Сначала определитеRouter
Добрый
class Router {
constructor(obj) {
// 路由模式
this.mode = obj.mode
// 配置路由
this.routes = {
'/index' : 'views/index/index',
'/index/detail' : 'views/index/detail/detail',
'/index/detail/more' : 'views/index/detail/more/more',
'/subscribe' : 'views/subscribe/subscribe',
'/proxy' : 'views/proxy/proxy',
'/state' : 'views/state/stateDemo',
'/state/sub' : 'views/state/components/subState',
'/dom' : 'views/visualDom/visualDom',
'/error' : 'views/error/error'
}
this.init()
}
}
инициализация маршрутаinit()
мониторинг времениload
,hashchange
Два события:
window.addEventListener('load', this.hashRefresh.bind(this), false);
window.addEventListener('hashchange', this.hashRefresh.bind(this), false);
Изменение хеш-значения адреса браузера осуществляется напрямую через ссылку тега a
<nav id="nav" class="nav-tab">
<ul class='tab'>
<li><a class='nav-item' href="#/index">首页</a></li>
<li><a class='nav-item' href="#/subscribe">观察者</a></li>
<li><a class='nav-item' href="#/proxy">代理</a></li>
<li><a class='nav-item' href="#/state">状态管理</a></li>
<li><a class='nav-item' href="#/dom">虚拟DOM</a></li>
</ul>
</nav>
<div id="container" class='container'>
<div id="main" class='main'></div>
</div>
После изменения хеш-значения метод обратного вызова:
/**
* hash路由刷新执行
*/
hashRefresh() {
// 获取当前路径,去掉查询字符串,默认'/index'
var currentURL = location.hash.slice(1).split('?')[0] || '/index';
this.name = this.routes[this.currentURL]
this.controller(this.name)
}
/**
* 组件控制器
* @param {string} name
*/
controller(name) {
// 获得相应组件
var Component = require('../' + name).default;
// 判断是否已经配置挂载元素,默认为$('#main')
var controller = new Component($('#main'))
}
Одноклассник оставил сообщение, чтобы понятьОтложенная загрузка маршрута, обратитесь к реализации vue и выложите сюда, надеюсь, вы дадите больше комментариев:
/**
* 懒加载路由组件控制器
* @param {string} name
*/
controller(name) {
// import 函数会返回一个 Promise对象,属于es7范畴,需要配合babel的syntax-dynamic-import插件使用
var Component = ()=>import('../'+ name);
Component().then(resp=>{
var controller = new resp.default($('#main'))
})
}
Учитывая существование многоуровневой вложенной маршрутизации страниц, необходимо обработать вложенную маршрутизацию:
- При прямой маршрутизации дочерних страниц загружать страницы в порядке от родительского маршрута к дочернему маршруту.
- Родительская страница была загружена, и когда дочерняя страница загружается снова, родительская страница остается, и загружается только дочерняя страница.
- Загрузка одноуровневой страницы, сохранение той же родительской страницы, загрузка только разных страниц
Измененный метод обновления маршрута:
hashRefresh() {
// 获取当前路径,去掉查询字符串,默认'/index'
var currentURL = location.hash.slice(1).split('?')[0] || '/index';
// 多级链接拆分为数组,遍历依次加载
this.currentURLlist = currentURL.slice(1).split('/')
this.url = ""
this.currentURLlist.forEach((item, index) => {
// 导航菜单激活显示
if (index === 0) {
this.navActive(item)
}
this.url += "/" + item
this.name = this.routes[this.url]
// 404页面处理
if (!this.name) {
location.href = '#/error'
return false
}
// 对于嵌套路由和兄弟路由的处理
if (this.oldURL && this.oldURL[index]==this.currentURLlist[index]) {
this.handleSubRouter(item,index)
} else {
this.controller(this.name)
}
});
// 记录链接数组,后续处理子级组件
this.oldURL = JSON.parse(JSON.stringify(this.currentURLlist))
}
/**
* 处理嵌套路由
* @param {string} item 链接list中当前项
* @param {number} index 链接list中当前索引
*/
handleSubRouter(item,index){
// 新路由是旧路由的子级
if (this.oldURL.length < this.currentURLlist.length) {
// 相同路由部分不重新加载
if (item !== this.oldURL[index]) {
this.controller(this.name)
}
}
// 新路由是旧路由的父级
if (this.oldURL.length > this.currentURLlist.length) {
var len = Math.min(this.oldURL.length, this.currentURLlist.length)
// 只重新加载最后一个路由
if (index == len - 1) {
this.controller(this.name)
}
}
}
Таким образом реализуется компонент хеш-маршрутизации.
При использовании просто создайте новый экземпляр маршрутизатора:new Router({mode:'hash'})
исторический маршрут
window.history
Атрибут указывает на объект History, который является атрибутом браузера и представляет собой историю просмотров текущего окна.Объект History сохраняет адреса всех страниц, посещенных текущим окном. Для получения дополнительной информации об объекте «История» см. введение г-на Жуана Ифэна:Объект истории
В среде разработки webpack к объекту devServer необходимо добавить следующую конфигурацию:
historyApiFallback: { rewrites: [ { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') }, ], }
Исторический маршрут проходит в основном черезhistory.pushState()
Метод добавляет запись истории в запись просмотра и одновременно запускает обратный вызов js для загрузки страницы.
Когда [вперед], [назад], это сработаетhistory.popstate
событие, загрузкаhistory.state
путь, хранящийся в
Реализация маршрутизации по истории аналогична этапам маршрутизации по хэшу. Поскольку необходимо настроить переключение режима маршрутизации, все ссылки на странице используют ссылки типа хэш. Когда маршрутизация по истории инициализируется, переход тега по умолчанию должен быть перехваченным:
/**
* history模式劫持 a链接
*/
bindLink() {
$('#nav').on('click', 'a.nav-item', this.handleLink.bind(this))
}
/**
* history 处理a链接
* @param e 当前对象Event
*/
handleLink(e) {
e.preventDefault();
// 获取元素路径属性
let href = $(e.target).attr('href')
// 对非路由链接直接跳转
if (href.slice(0, 1) !== '#') {
window.location.href = href
} else {
let path = href.slice(1)
history.pushState({
path: path
}, null, path)
// 加载相应页面
this.loadView(path.split('?')[0])
}
}
Инициализация маршрута истории должна быть привязанаload
,popstate
мероприятие
this.bindLink()
window.addEventListener('load', this.loadView.bind(this, location.pathname));
window.addEventListener('popstate', this.historyRefresh.bind(this));
Запускается при просмотре [вперед] или [назад]popstate
событие, выполнить функцию обратного вызова
/**
* history模式刷新页面
* @param e 当前对象Event
*/
historyRefresh(e) {
const state = e.state || {}
const path = state.path.split('?')[0] || null
if (path) {
this.loadView(path)
}
}
Когда режим маршрутизации истории загружает страницу в первый раз, страница может быть задана по умолчанию, а затем вы можете использоватьhistory.replaceState
метод
if (this.mode === 'history' && currentURL === '/') {
history.replaceState({path: '/'}, null, '/')
currentURL = '/index'
}
Для обработки 404 страниц то же самое
history.replaceState({path: '/error'}, null, '/error')
this.loadView('/error')
Для получения дополнительных исходных кодов, пожалуйста, посетитеGithub