предисловие
Как мы все знаем, по мере того, как бизнес-функции клиентских приложений становятся все более сложными, пользователи предъявляют все более высокие требования к пользовательскому опыту.SPA
) стала основной формой интерфейсных приложений. Одной из наиболее примечательных особенностей крупномасштабных одностраничных приложений является используемая внешняя система маршрутизации и перехода на подстраницы.URL
, который обновляет представление страницы без повторного запроса страницы.
Обновите представление, но браузер не перерисовывает всю страницу, просто перерисовывает некоторые подстраницы, скорость загрузки высокая, страница отзывчива, этоSPA
преимущество, которое также является ядром принципа внешней маршрутизации, который даст людям ощущениеAPP
С таким же чувством на данный момент существует два основных способа реализации этой функции в среде браузера:
- использовать
URL
изhash(#)
- использовать
H5
новый методHistory interface
использоватьURL
изHash(#)
существуетH5
Когда это еще не популярно, обычноSPA
оба используютurl
изhash(#)
В качестве привязки получите значение после #, отслеживайте его изменения, а затем визуализируйте соответствующую подстраницу.Официальный сайт NetEase Cloud Musicявляется использование этой технологии.
Например, ваш адресhttp://localhost:8888/#/abc
затем используйтеlocation.hash
Выход#/abc
.
Тогда я начну сlocation
об этом объекте.
Первый взглядlocation
Каковы официальные атрибуты
Атрибуты | описывать |
---|---|
hash | Установите или верните URL-адрес, начинающийся с # (якорь) |
host | Установите или верните имя хоста и номер порта текущего URL-адреса |
hostname | Установить или вернуть имя хоста текущего URL |
href | Установить или вернуть полный URL |
pathname | Задает или возвращает часть пути текущего URL-адреса |
port | Установить или вернуть номер порта текущего URL-адреса |
protocol | Установить или вернуть протокол текущего URL |
search | Задает или возвращает часть URL-адреса, начинающуюся с ? |
Как видно из приведенной выше таблицы, мы можем легко получить часть после #, так как же мы отслеживаем ее изменения и соответствующие подстраницы, чтобы вносить изменения, когда мы получаем эту часть?
window
В объекте происходит событие, которое специально отслеживаетсяhash
измениться, то естьonhashchange
, сначала нам нужно прослушать это событие:
<body>
<h1 id="id"></h1>
<a href="#/id1">id1</a>
<a href="#/id2">id2</a>
<a href="#/id3">id3</a>
</body>
<script>
window.addEventListener('hashchange', e => {
e.preventDefault()
document.querySelector('#id').innerHTML = location.hash
})
</script>
Видно, что мы полностью мониторили в это время.URL
изменяется, содержание на странице также меняется соответственно.
Итак, как загружать разные страницы, на данный момент существует три способа:
- Найдите содержимое узла и измените его (это то, что мы продемонстрировали выше).
-
import
ОдинJS
файл, внутри файлаexport
строка шаблона - использовать
AJAX
загрузить соответствующийHTML
трафарет
Первый способ был продемонстрирован, но этот метод слишком ограничен, два других способа загрузки страницы я продемонстрирую ниже.
import
Способ
определитьJS
файл с именемdemo1.js
, введите в него содержимое:
const str = `
<div>
我是import进来的JS文件
</div>
`
export default str
в основном файлеimport
зайди и протестируй (используяChrome
Обязательно используйте сервер для открытия или откройте напрямую с помощью Firefox):
<body>
<h1 id="id"></h1>
<a href="#/id1">id1</a>
<a href="#/id2">id2</a>
<a href="#/id3">id3</a>
</body>
<!-- 在 HTML 导入文件记得要加上 type="module" -->
<script type="module">
import demo1 from './demo1.js'
document.querySelector('#id').innerHTML = demo1
window.addEventListener('hashchange', e => {
e.preventDefault()
document.querySelector('#id').innerHTML = location.hash
})
</script>
Видно, что файл импорта вступил в силу.В настоящее время большинство фреймворков обрабатываются аналогичным образом после компиляции.
Например,vue
Рамка,.vue
файл — это пользовательский тип файла, использующий классHTML
Грамматика описываетVue
компоненты. каждый.vue
Файл содержит три типа языковых блоков верхнего уровня.<template>
,<script>
а также<style>
,vue-loader
будет анализировать файл, извлекая каждый языковой блок, передавая другиеloader
обрабатывать и, наконец, собирать их вCommonJS
модуль,module.exports
из одногоVue.js
компонентный объект. .
AJAX
Способ
В этой статье подробно объясняется механизм маршрутизации.AJAX
непосредственно использоватьJQuery
Это колесо.
определитьHTML
файл с именемdemo2.html
, напишите в нем какой-нибудь контент (поскольку на главной странице уже естьhead
,body
т.д. корневые теги, этот файл просто записывает теги, которые необходимо заменить):
<div>
我是AJAX加载进来的HTML文件
</div>
Пишем в основной файл и тестируем:
<body>
<h1 id="id"></h1>
<a href="#/id1">id1</a>
<a href="#/id2">id2</a>
<a href="#/id3">id3</a>
</body>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<script type="module">
// import demo1 from './demo1.js'
// document.querySelector('#id').innerHTML = demo1
$.ajax({
url: './demo2.html',
success: (res) => {
document.querySelector('#id').innerHTML = res
}
})
window.addEventListener('hashchange', e => {
e.preventDefault()
document.querySelector('#id').innerHTML = location.hash
})
</script>
видно, использоватьAJAX
Загруженный файл также вступил в силу.
Теперь, когда содержимое загрузки разных страниц вступило в силу, нам нужно только обернуть наши слушатели и использовать шаблон наблюдателя для инкапсуляции изменений маршрутизации:
<body>
<h1 id="id">我是空白页</h1>
<a href="#/id1">id1</a>
<a href="#/id2">id2</a>
<a href="#/id3">id3</a>
</body>
<script type="module">
import demo1 from './demo1.js'
// 创建一个 newRouter 类
class newRouter {
// 初始化路由信息
constructor() {
this.routes = {};
this.currentUrl = '';
}
// 传入 URL 以及 根据 URL 对应的回调函数
route(path, callback = () => {}) {
this.routes[path] = callback;
}
// 切割 hash,渲染页面
refresh() {
this.currentUrl = location.hash.slice(1) || '/';
this.routes[this.currentUrl] && this.routes[this.currentUrl]();
}
// 初始化
init() {
window.addEventListener('load', this.refresh.bind(this), false);
window.addEventListener('hashchange', this.refresh.bind(this), false);
}
}
// new 一个 Router 实例
window.Router = new newRouter();
// 路由实例初始化
window.Router.init();
// 获取关键节点
var content = document.querySelector('#id');
Router.route('/id1', () => {
content.innerHTML = 'id1'
});
Router.route('/id2', () => {
content.innerHTML = demo1
});
Router.route('/id3', () => {
$.ajax({
url: './demo2.html',
success: (res) => {
content.innerHTML = res
}
})
});
</script>
Эффект следующий:
Пока что используйтеhash(#)
Реализовано внешнее управление маршрутизацией.
использоватьH5
новый методHistory interface
Используется вышеhash
Хорошо реализовать маршрутизацию по методу, но проблема в том, что это слишком некрасиво ~ Если это не отображается в WeChat или другомURL
изAPP
Неважно, используете ли вы его в обычном браузере, но вы столкнетесь с проблемами, если будете использовать его в обычном браузере.
таким образом,H5
изHistory
режим решает эту проблему.
существуетH5
До,History
Только с несколькимиAPI
:
API | иллюстрировать |
---|---|
back() |
откат к последнему посещенномуURL (так же, как браузер, нажав кнопку «Назад») |
forward() |
вперед назадURL (то же самое, что и браузер, нажав кнопку «Вперед») |
go(n) |
n Получить целое число, перейти на страницу, указанную целым числом, напримерgo(1) эквивалентноforward() ,go(-1) эквивалентноback() ,go(0) Эквивалентно обновлению текущей страницы |
Если перемещенная позиция превышает границу истории доступа, три вышеуказанных метода не сообщают об ошибке, а молча завершают работу.
Однако пришелH5
эпоха, новыйH5
Это дает ему больше новых возможностей:
кеш туда и обратно
По умолчанию браузер кэширует текущую страницу сеанса, поэтому, когда следующая страница нажимает кнопку «Назад» или предыдущая страница нажимает кнопку «Вперед», браузер извлекает и загружает страницу из кэша. кэширование поездки».
PS: этот кеш сохраняет данные страницы, состояние DOM и js, фактически оставляя всю страницу нетронутой.
Добавить запись в стек истории: pushstate (состояние, заголовок, URL)
Поддержка браузера:IE10+
- состояние: а
JS
Объекты (не более 640кБ), в основном используемые вpopstate
получается как параметр в событии. Если вам не нужен этот объект, вы можете заполнить его здесьnull
- title: заголовок новой страницы, некоторые браузеры (например, Firefox) игнорируют этот параметр, поэтому обычно
null
- url: адрес новой истории,Может быть адресом страницы, но также и значением привязки,новый
url
должен соответствовать текущемуurl
В том же домене, иначе будет выброшено исключение.Если этот параметр специально не отмечен, он будет установлен на текущий документurl
Каштан:
// 现在是 localhost/1.html
const stateObj = { foo: 'bar' };
history.pushState(stateObj, 'page 2', '2.html');
// 浏览器地址栏将立即变成 localhost/2.html
// 但!!!
// 不会跳转到 2.html
// 不会检查 2.html 是否存在
// 不会在 popstate 事件中获取
// 不会触发页面刷新
// 这个方法仅仅是添加了一条最新记录
Помимо этого, следует отметить несколько моментов:
- Буду
url
Не срабатывает, если установлено значение привязкиhashchange
- В соответствии с политикой того же происхождения, если установленоразныеАдрес доменного имени сообщит об ошибке. Цель этого состоит в том, чтобы пользователи не думали, что они являются одним и тем же веб-сайтом. Если такого ограничения нет, это будет легко выполнить
XSS
,CSRF
и т. д. атака
Изменить текущую историю: replaceState(state, title, url)
Поддержка браузера:IE10+
- Параметры имеют тот же смысл
pushstate
- Изменить текущую запись истории вместо добавления новой
- тоже не заводится
popstate
history.state
Поддержка браузера:IE10+
- возвращает текущую историю
state
.
popstate
Определение: Всякий раз, когда история просмотров одного и того же документа (т.history
объект) изменится, он вызоветpopstate
мероприятие.
Примечание: если только звонитьpushState
метод илиreplaceState
method , событие не будет запущено, только пользователь щелкнет браузеротступатькнопка ивперед, продолжатькнопку или используйтеJavaScript
передачаback
,forward
,go
метод срабатывает. Кроме того, это событие предназначено только для одного и того же документа, если переключение истории просмотров приводит к загрузке другого документа, это событие не будет запущено.
Каштан:
window.onpopstate= (event) => {
  console.log(event.state) //当前历史记录的state对象
}
выполнить
Зная так много, давайте начнемHistory
Маршрутизация шаблонов сейчас!
Ставим вышеперечисленноеHTML
После небольшой модификации, пожалуйста, терпеливо проанализируйте его:
<body>
<h1 id="id">我是空白页</h1>
<a class="route" href="/id1">id1</a>
<a class="route" href="/id2">id2</a>
<a class="route" href="/id3">id3</a>
</body>
import demo1 from './demo1.js'
// 创建一个 newRouter 类
class newRouter {
// 初始化路由信息
constructor() {
this.routes = {};
this.currentUrl = '';
}
route(path, callback) {
this.routes[path] = (type) => {
if (type === 1) history.pushState( { path }, path, path );
if (type === 2) history.replaceState( { path }, path, path );
callback()
};
}
refresh(path, type) {
this.routes[this.currentUrl] && this.routes[this.currentUrl](type);
}
init() {
window.addEventListener('load', () => {
// 获取当前 URL 路径
this.currentUrl = location.href.slice(location.href.indexOf('/', 8))
this.refresh(this.currentUrl, 2)
}, false);
window.addEventListener('popstate', () => {
this.currentUrl = history.state.path
this.refresh(this.currentUrl, 2)
}, false);
const links = document.querySelectorAll('.route')
links.forEach((item) => {
// 覆盖 a 标签的 click 事件,防止默认跳转行为
item.onclick = (e) => {
e.preventDefault()
// 获取修改之后的 URL
this.currentUrl = e.target.getAttribute('href')
// 渲染
this.refresh(this.currentUrl, 2)
}
})
}
}
// new 一个 Router 实例
window.Router = new newRouter();
// 实例初始化
window.Router.init();
// 获取关键节点
var content = document.querySelector('#id');
Router.route('/id1', () => {
content.innerHTML = 'id1'
});
Router.route('/id2', () => {
content.innerHTML = demo1
});
Router.route('/id3', () => {
$.ajax({
url: './demo2.html',
success: (res) => {
content.innerHTML = res
}
})
});
Демонстрационная диаграмма показана ниже:
Суммировать
При нормальных обстоятельствах,hash
а такжеhistory
Вы можете, если вас больше не волнует внешний вид,#
Символический смесительURL
Выглядит чуть менее красиво.
Кроме того, согласноMozilla Develop Networkзнакомство, звонокhistory.pushState()
по сравнению с прямой модификациейhash
, имеются следующие преимущества:
-
pushState()
установить новыйURL
может быть таким же, как текущийURL
гомологичный произвольныйURL
;а такжеhash
только модифицируемый#
последняя часть, поэтому может быть установлена только с текущимURL
тот же документURL
-
pushState()
установить новыйURL
можно совместить с текущимURL
Точно, это также добавит запись в стек; иhash
Новый набор значений должен отличаться от исходного, чтобы инициировать действие по добавлению записи в стек. -
pushState()
пройти черезstateObject
Параметры могут добавлять в запись данные любого типа;hash
Можно добавлять только короткие строки; -
pushState()
Доступны дополнительные настройкиtitle
Свойства для последующего использования.
Посмотрите на это с другой стороныhistory
Режим полон счастья, и он кажется полностью заменимымhash
режим, а на самом делеhistory
Не все хорошо, хотя это легко сделать в браузере, но пройти действительно необходимоURL
Перейти к серверной частиHTTP
По запросу появляется разница между ними. Особенно когда пользователь вручную вводитURL
После нажатия Enter или при обновлении (перезапуске) браузера.
-
hash
режим, толькоhash
Содержимое перед символом будет включено в запрос, напримерhttp://www.qqq.com
, так что для бэкенда, даже если маршрут не полностью пройден, он не вернется404
ошибка. -
history
режим, интерфейсURL
должени фактический запрос к бэкендуURL
последовательно, какhttp://www.qqq.com/book/id
. Если в бэкенде отсутствует пара/book/id
Обработчик маршрута вернется404
ошибка.Vue-Router
Официальный сайт описывает это так: «Однако, чтобы хорошо играть в этот режим, ему также нужна поддержка фоновой конфигурации... Итак, вам нужно добавить ресурс-кандидат, который охватывает все ситуации на стороне сервера: еслиURL
не соответствует никаким статическим ресурсам, он должен возвращать то же самоеindex.html
страница, эта страница - тыapp
зависимые страницы. " - должен быть в бэкенде (
Apache
илиNginx
) для простой настройки маршрутизации и с интерфейсной маршрутизацией404
Поддержка страницы.
Наконец, я сожалею, что продвигаю свою работу на основеTaro
Библиотека компонентов, написанная фреймворком:MP-ColorUI.
Я очень рад, что могу сыграть главную роль, спасибо.