共计 19704 个字符,预计需要花费 50 分钟才能阅读完成。
导航
[[深刻 01] 执行上下文](https://juejin.im/post/684490…
[[深刻 02] 原型链](https://juejin.im/post/684490…
[[深刻 03] 继承](https://juejin.im/post/684490…
[[深刻 04] 事件循环](https://juejin.im/post/684490…
[[深刻 05] 柯里化 偏函数 函数记忆](https://juejin.im/post/684490…
[[深刻 06] 隐式转换 和 运算符](https://juejin.im/post/684490…
[[深刻 07] 浏览器缓存机制(http 缓存机制)](https://juejin.im/post/684490…
[[深刻 08] 前端平安](https://juejin.im/post/684490…
[[深刻 09] 深浅拷贝](https://juejin.im/post/684490…
[[深刻 10] Debounce Throttle](https://juejin.im/post/684490…
[[深刻 11] 前端路由](https://juejin.im/post/684490…
[[深刻 12] 前端模块化](https://juejin.im/post/684490…
[[深刻 13] 观察者模式 公布订阅模式 双向数据绑定](https://juejin.im/post/684490…
[[深刻 14] canvas](https://juejin.im/post/684490…
[[深刻 15] webSocket](https://juejin.im/post/684490…
[[深刻 16] webpack](https://juejin.im/post/684490…
[[深刻 17] http 和 https](https://juejin.im/post/684490…
[[深刻 18] CSS-interview](https://juejin.im/post/684490…
[[深刻 19] 手写 Promise](https://juejin.im/post/684490…
[[深刻 20] 手写函数](https://juejin.im/post/684490…
[[react] Hooks](https://juejin.im/post/684490…
[[部署 01] Nginx](https://juejin.im/post/684490…
[[部署 02] Docker 部署 vue 我的项目](https://juejin.im/post/684490…
[[部署 03] gitlab-CI](https://juejin.im/post/684490…
[[源码 -webpack01- 前置常识] AST 形象语法树](https://juejin.im/post/684490…
[[源码 -webpack02- 前置常识] Tapable](https://juejin.im/post/684490…
[[源码 -webpack03] 手写 webpack – compiler 简略编译流程](https://juejin.im/post/684490…
[[源码] Redux React-Redux01](https://juejin.im/post/684490…
[[源码] axios ](https://juejin.im/post/684490…
[[源码] vuex ](https://juejin.im/post/684490…
[[源码 -vue01] data 响应式 和 初始化渲染 ](https://juejin.im/post/684490…
[[源码 -vue02] computed 响应式 – 初始化,拜访,更新过程 ](https://juejin.im/post/684490…
前置常识
URL 和 URI
- <font color=red>URI:对立资源标识符(I => Identifier:标识符 Id)</font>
(Universal Resource Identifier
- <font color=red>URL:对立资源定位符(L => Locator:定位器)</font>
Uniform Resource Locator
-
区别:
- URL:强调的是地址,即 (<font color=red> 定位 </font>) 这个惟一的资源
- URI:强调的是 (<font color=red> 标识 </font>) 资源,资源具备的(<font color=red> 唯一性 </font>)
别离标识惟一资源和标识惟一地址很麻烦,所以用 URL 也充当 RUI 的角色,即标记惟一资源还标记该资源的惟一地址
URL 的组成
http://www.baidu.com:80/stu/index.html?name=xxx&age=25#teacher
- Protocol:协定
http://
,https://
- Domain:域名
www.baicu.com
-
Port:端口
:80
- http 协定的默认地址是:80
- https 自已的默认地址是:443
- Path:文件门路,=>
/ 开始 ? 之前的局部
,本例中是:/stu/index.html
- Query:查问字符串 =>
? 结尾到结尾,或者? 结尾到 #之前
,本例是:?name=xxx&age=25
- Hash:哈希值 =>
# 结尾到结尾
,本例是:teacher
- <font color=red>protocol,domain,port,path,query,hash</font>
DOMContentLoaded 事件,load 事件
- window.onload
window.addEventListener('load', ....)
- DOMContentLoaded
-
区别:
- <font color=red>DOMContentLoaded:DOM 加载实现时触发 </font>
- <font color=red>load:须要 DOM,款式,脚本,图片,视频等所有资源都加载实现时才会触发,即页面加载实现才触发 </font>
DOM 残缺解析过程:
- 解析 html
- 解析 css – 包含当 html 中的款式和内部引入的款式
- 解析并运行脚本 – 报错本 html 中的脚本和内部引入的脚本
- DOM 构建实现 —————————————————- DOM 加载实现,触发 DOMContentLoaded
- 加载图片,视频等其余资源
-
页面加载结束 ————————————————— 页面加载实现,触发 load
// 2021/07/25 更新 – 下面的过程不欠缺,这里再简略记录下
// – 详见 https://juejin.cn/post/698357… - parse-html
- parse-styleSheet
- evaluate-script
- layout
- paint
-
composite
字符串 slice 办法特例
- slice 可用于 (数组) 和 (字符串)
- 有返回值,不扭转原字符串
-
<font color=red>String.prototype.slice(开始地位,完结地位) 截取字符串,不包含完结地位 </font>
String.prototype.slice() 特例:''.slice(1) ----------- 返回'' 空字符串 案例:window.location.hash // 因为:当地址栏的 url 中的 hash 不存在时,window.location.hash 返回的是空字符串,这种状况如下 // 所以:window.location.hash.slice(1) => 返回空字符串
window.location 对象
window.location 对象
属性:pathname: 返回 url 的 path 局部,/ 开始 ? 之前 或者 / 开始到后果,如果没有 query 和 hash
origin:protocal + hostname + port 三者之和,相当于协定,域名,端口
protocal:协定 http:// https://
hostnme: 主机名
port:端口号
host:主机(hostname + port)search:查问字符串(? 结尾到 #之前,或者? 结尾到结尾)hash:片段字符串(哈希值,# 结尾到结尾)返回值:- window.location 返回的是一个只读的 Location 对象,你依然能够赋给它一个 DOMString
- 这意味着您能够在大多数状况下解决 location,就像它是一个字符串一样
- window.location = 'http://www.example.com',是 window.location.href = 'http://www.example.com' 的同义词
问题:- window.location.assign() 和 window.location.href 的区别?- location.assign(url)是函数式的形式
- window.location.href=url 比 location.assign(url) 更快
hash 路由
- url 中的 hash 以 #号结尾,本来用来作为锚点,从而定位到页面的特定区域
- 当 hash 产生扭转时,页面不会刷新,浏览器也不会向服务器发送申请
- 留神:<font color=red>hash 扭转时,能够触发 hashchange 事件,在监听函数中能够申请数据,实现页面的更新操作 </font>
作为锚点,定位页面特点区域
<a href="#anchor1"> 锚点 1 </a>
<a href="#anchor2"> 锚点 2 </a>
<div id="anchor1"> 锚点 1 的地位 </div>
<div id="anchor2"> 锚点 2 的地位 </div>
阐明:- 点击 a2,页面会跳转到 div2 的地位
- 并且页面的 hash 局部也会扭转,即 url 中以 #结尾的字符串会扭转
- anchor:是锚的意思
- 留神:a 标签的 name 属性曾经废除,用 id 代替(因为有的教程应用 name 属性实现的)
hashchange 事件
- <font color=red> 如果监听了 hashchange 事件,hash 扭转,地址栏的 url 中的 hash 局部就会扭转,同时 hashchange 也会触发 </font>
- 然而页面不会刷新,即浏览器的刷新按钮的圈圈不会转动
-
然而能够利用 hashchange 的回调函数更新页面的内容,留神不是页面刷新
<body> <a href="#anchor1"> 锚点 1 </a> <a href="#anchor2"> 锚点 2 </a> <script> window.addEventListener('hashchange', function() {console.log('111111111') }, false) </script> </body>
-
点击 a 标签,url 中的 hash 扭转,hash 扭转,hashchange 事件触发,则监听函数就会执行,输入 111111
手动实现一个 hash-router
hash-router
原理:(1) hash 扭转,地址栏 url 的 hash 字符串扭转,触发 hashchange 事件
(2) 在 hashchange 事件的回调函数中更新视图
代码:<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<a href="#/home">home</a>
<a href="#/other">other</a>
<div id="content"> 内容局部 </div>
<script>
const routes = [{
path: '/home',
component: '<h1>home 页面 </h1>'
}, {
path: '/other',
component: '<h1>other 页面 </h1>'
}]
class Router {constructor(routes) { // 构造函数
this.route = {} // 路由映射
this.createRouteMap(routes) // 创立路由映射
this.init() // 初始化}
createRouteMap = (routes) => {
routes.forEach(item => {this.route[item.path] = () => {document.getElementById('content').innerHTML = item.component
// 函数体的作用:将 id 是 content 的 div 中的内容换成 componet
}
// 循环配置
// key 是 path
// value 包装成一个更新 html 内容的函数,在 load 和 hahschange 中调用
})
}
init = () => {window.addEventListener('load', this.updateView, false) // 页面加载实现时触发,留神辨别 DOMContentLoaded
window.addEventListener('hashchange', this.updateView, false)
}
updateView = () => {const hash = window.location.hash.slice(1) || '/home'; // 首次加载 home 页面
// load 事件触发时,window.location.hash => 返回 '' 空字符串
// ''.slice(1) => 返回''
if (this.route[hash]) this.route[hash]()
// 存在,则执行函数
}
}
new Router(routes)
</script>
</body>
</html>
2020/12/24 温习 – hash 路由
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<a href="#/home">home 页面 </a>
<a href="#/other">other 页面 </a>
<div id="content"> 内容局部,即路由替换的局部 </div>
<div id="current-hash"></div>
<script>
const routes = [{
path: '/home',
component: 'home 页面的内容'
}, {
path: '/other',
component: 'other 页面的内容'
}]
class HashRouter {constructor(routes) {this.route = {} // 路由映射
this.createRouteMap(routes) // 创立路由映射,为 (this.route) 创立 (map) 映射;key=path;value=()=>{更新页面}
this.init() // 初始化}
createRouteMap = (routes) => {if (routes.length) {routes.forEach(({ path, component}) => {this.route[path] = () => {document.getElementById('content').innerHTML = component // 替换内容
}
})
}
}
createRouteMap = (routes) => {if (routes.length) {routes.forEach(({ path, component}) => {this.route[path] = () => {document.getElementById('content').innerHTML = component // 替换内容
}
})
}
}
init = () => {window.addEventListener('load', this.updateView, false)
window.addEventListener('hashchange', this.updateView, false)
}
// 更新视图
updateView = () => {// (1)
// 这里 (load 事件) 和 (hashchange 事件) 都会触发 (updateView 办法)
// (2)
// load 事件: (页面加载实现时触发),包含 (DOM,款式,图片,视频等所有资源都加载实现)
// DOMContentLoaded 事件: 是在 (DOM 加载实现时触发)
// (3)
// 当 load 事件触发时,hash 并没有扭转,即 window.location.hash = ''=>''.slice(1) => ''
const hash = this.getCurrentHash() // 获取 hash
// if (Object.keys(this.route).includes(hash)) { // 还有更简略的办法
// this.route[hash]()
// }
if (this.route[hash]) this.route[hash]() // 如果 this.route 对象中的 key 对应得值存在,就执行该函数}
// 获取以后地址栏的 hash
getCurrentHash = () => {const hash = window.location.hash.slice(1)
this.printHahToHtml(hash) // 该函数是用来在 html 中显示以后 hash 的
return hash ? hash : '/home'
// load 事件触发时,hash 就不存在,hash='',这种状况下即默认状况下返回'/home' 路由
// load 事件触发时,window.location.hash => 返回 '' 空字符串
// ''.slice(1) => 返回''
}
printHahToHtml = (hash) => {const DOM = document.getElementById('current-hash')
DOM.innerHTML = ` 以后页面的 hash 是:=> #${hash}`
DOM.style.setProperty('background', 'yellow')
DOM.style.setProperty('padding', '10px')
}
}
new HashRouter(routes)
</script>
</body>
</html>
2021/07/25 优化 – hash 路由
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<a href="#/home">home 页面 </a>
<a href="#/other">other 页面 </a>
<div id="content"> 内容局部,即路由替换的局部 </div>
<div id="current-hash"></div>
<script>
const routes = [
{
path: "/home",
component: "home 页面的内容",
},
{
path: "/other",
component: "other 页面的内容",
},
];
class HashRouter {constructor(routes) {this.route = {};
this.createRouteMap(routes);
this.init();}
init = () => {window.addEventListener("load", this.updateView, false);
window.addEventListener("hashchange", this.updateView, false);
this.DOM_CURRENT_HASH = document.getElementById("current-hash");
this.DOM_CONTENT = document.getElementById("content");
};
// createRouteMap = (routes) => {// if (routes.length) {// routes.forEach(({ path, component}) => {// this.route[path] = () => {// document.getElementById("content").innerHTML = component; // 替换内容
// };
// });
// }
// };
createRouteMap = (routes = []) => {
routes.forEach(({ path, component}) =>
(this.route[path] = () =>
(this.DOM_CONTENT.innerHTML = component))
);
};
// updateView = () => {// const hash = this.getCurrentHash(); // 获取 hash
// if (this.route[hash]) this.route[hash](); // 如果 this.route 对象中的 key 对应得值存在,就执行该函数
// };
updateView = () => {const path = this.getCurrentHash();
this.route?.[path]?.();};
// getCurrentHash = () => {// const hash = window.location.hash.slice(1);
// this.printHahToHtml(hash); // 该函数是用来在 html 中显示以后 hash 的
// return hash ? hash : "/home";
// };
getCurrentHash = () => {const path = window.location.hash.slice(1) || "/home";
this.printHahToHtml(path); // 该函数是用来在 html 中显示以后 hash 的
return path;
};
// printHahToHtml = (hash) => {// const DOM = document.getElementById("current-hash");
// DOM.innerHTML = ` 以后页面的 hash 是:=> #${hash}`;
// DOM.style.setProperty("background", "yellow");
// DOM.style.setProperty("padding", "10px");
// };
printHahToHtml = (hash) => {
const DOM = this.DOM_CURRENT_HASH;
DOM.innerHTML = ` 以后页面的 hash 是:=> #${hash}`;
DOM.style.setProperty("padding", "10px");
DOM.style.setProperty("background", "yellow");
};
}
new HashRouter(routes);
</script>
</body>
</html>
history 路由
window.history 对象
- window.history 对象的办法:
back()
,forward()
,go()
,pushState()
,replaceState()
- pushState()
- replaceState()
-
<font color=red>pushState() 和 replaceState()</font>
- <font color=red> 不会触发页面刷新,只能导致 History 对象发生变化,地址栏的 url 会变动 </font>
- <font color=red> 会扭转 url,不会触发 popstate 事件,地址栏的 url 有所变动 </font>
window.history.pushState(state, title, url)
- window.history.pushState(state, title, url)
- state:一个与增加的记录相关联的对象
- title:新页面的题目,当初所有浏览器都疏忽该参数,能够传入空字符串
- url:新的 url 地址,必须与以后页面同一个域,浏览器的地址栏显示这个网址
window.history.pushState({}, null, url)
-
留神:pushState 不会刷新页面,只会扭转 History 对象,地址栏 url 会变动
- <font color=red> 能够通过 History.state 读取状态对象 </font>
popstate
-
<font color=red>popstate 触发的条件 </font>
- <font color=red> 浏览器的后退后退按钮 </font>
- <font color=red>history.go(),history.back(),history.forward()</font>
- <font color=red> 留神:window.history.pushState() 和 window.history.replaceState()不会触发 popstate 事件 </font>
- 留神:pushState()和 replaceState()能够扭转 url,且实现不向服务器发送申请,不存在 #号,比 hash 路由更好看,然而 History 路由须要服务器的反对,并且需将所有的路由重定向到根页面
手动实现一个 history-router
-
原理剖析
- 第一步:给每个 a 标签都绑定一个 click 事件,click 事件触发时,再去触发 pushState()事件,将 url 的 path 局部扭转为 a 的 data-href 自定义属性的值即路由的 path,传入 window.history.pushState({}, null, path),这样地址栏的 url 就扭转了
- 第二步:扭转地址栏的 url 后,通过 window.location.pathname 获取更新的 url 的 path 局部
- 第三步:用 path 和 route 对象中的 key 去匹配,匹配上就去执行更新视图的函数
-
第四步:
这只是一条线:1- 3 步即点击 a 标签的状况
-
还有一条线:浏览器的后退后退,和函数式导航 go() back() forward() 则有 popstate 事件来解决,过程差不多
history-router 原理:(1) 封装一个办法,在 pushState()和 replaceState()扭转 url 后调用,在该办法中获取最新的 window.location.path,更置信页面 (2) 通过 go() back() forward() 浏览器后退后退等触发 popstate 事件 代码:<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <a href="javascript:void(0)" data-href="/home">home</a> // html 中的自定义属性 <a href="javascript:void(0)" data-href="/other">other</a> <div id="content">content 的内容 </div> <script> const routes = [{ path: '/home', component: '<h1>home 页面 </h1>' }, { path: '/other', component: '<h1>other 页面 </h1>' }] class Router {constructor(routes) {this.route = {} // key-value 键值对,key 是 path,value 是更新视图的函数 this.createRouteMap(routes) // 创立路由映射 this.bindEvent() // 绑定 a 标签的点击事件 this.init() // 绑定 load 和 popstate 事件} createRouteMap = (routes) => { routes.forEach(item => {this.route[item.path] = () => {document.getElementById('content').innerHTML = item.component } }) } bindEvent = () => {const a = document.getElementsByTagName('a') Array.prototype.forEach.call(a, item => { // 第二个参数,是 forEach 须要传入的回调函数 item.addEventListener('click', () => {const path = item.getAttribute('data-href') // 获取 data-herf 属性 this.pushStateFn(path) // 执行 History.pushState()办法 // 这里因为是箭头函数,this 指向父级所在的上下文环境,即 bindEvent 所在的上下文环境,即 Router }, false) }) } pushStateFn = (url) => {window.history.pushState({}, null, url) // 扭转 url 后,调用更新视图的函数 updateView this.updateView() // 更新视图} init = () => {window.addEventListener('load', this.updateView, false) // 页面加载实现时触发 window.addEventListener('popstate', this.updateView, false) // 浏览器后退后退,History.go() back() forward()时触发} updateView = () => { const path = window.location.pathname || '/'; // 获取 url 的 path 局部 if(this.route[path]) this.route[path]() // path 在 route 中存在,就执行对象的函数,key-value 键值对} } new Router(routes) </script> </body> </html> 留神:该 html 须要用 Live Server 启动,vscode 插件,来提供 server
2020/12/24 温习 – history 路由
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<a href="javascript:void(0)" data-href="/home"> 点击去 home 页面 </a>
<a href="javascript:void(0)" data-href="/other"> 点击去 other 页面 </a>
<div id="content"> 内容局部,即路由要替换的内容 </div>
<script>
const routes = [{
path: '/home',
component: '<h1>home 页面 </h1>'
}, {
path: '/other',
component: '<h1>other 页面 </h1>'
}]
class HistoryRouter {constructor(routes) {this.route = {} // 路由映射 key=path value=()=>{更新视图}
this.createRouteMap(routes) // 创立路由映射
this.bindEvent() // 绑定事件
this.init() // 初始化}
createRouteMap = (routes) => {if (routes.length) {routes.forEach(({ path, component}) => {this.route[path] = () => {document.getElementById('content').innerHTML = component
}
})
}
}
bindEvent = () => {const a = document.getElementsByTagName('a')
Array.prototype.forEach.call(a, aDom => {aDom.addEventListener('click', () => {const path = aDom.getAttribute('data-href')
this.triggerPushState(path) // 触发 pushState 事件
}, false)
})
}
triggerPushState = (path) => {window.history.pushState({}, null, path)
// pushState() 能够扭转地址栏的 url,然而不会触发页面更新,所以要执行上面的更新函数
// (1) 状况 1:这只是 (点击 a 标签) 的状况,应用的是 pushState() 函数
// (2) 状况 2:还有就是 (点击浏览器的后退后退按钮) 和 (函数式 k 导航 window.history.go() back() forward() 的状况 )
// (3) 状况 3:就是初始化时,在 (load) 事件触发是的状况,默认 path='/'
this.updateView()}
updateView = () => {// 因为:在执行该办法之前,曾经触发了 pushState() || popstate 事件 || load 事件
// 所以:能够用 window.location.pathname 获取最新的 url 中的 path 局部
const currentPath = window.location.pathname
? window.location.pathname
: '/'
if (this.route[currentPath]) this.route[currentPath]()}
init = () => {window.addEventListener('load', this.updateView, false) // 页面加载实现时的状况
window.addEventListener('popstate', this.updateView, false) // popstate 触发的状况,浏览器后退后退和函数式导航
}
}
new HistoryRouter(routes)
</script>
</body>
</html>
留神:该 html 须要用 Live Server 启动,vscode 插件,来提供 server
2021/07/25 优化 – history 路由
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<a href="javascript:void(0)" data-href="/home">home</a>
<a href="javascript:void(0)" data-href="/other">other</a>
<div class="root">content</div>
<script>
// history 路由
// 原理:// - 1. window.history.pushState(stateObj, title, url) 和 window.history.replaceState(stateObj, title, url) 不会触发 popstate,不会刷新浏览器,然而会扭转 history 对象,并且 url 地址栏会扭转
// - 2. popstate 事件,触发条件
// - window.history.go()/back()/forward()
// - 浏览器的后退,后退按钮
// history 路由实现
// - 1. 给每个 a 标签增加 (data-href 自定义属性),并且给每个 a 标签 (绑定 click 事件),点击后获取 data-href,而后触发 (window.history.pushState({}, null, data-href))
// - 2. 通过 1 后,触发了 window.history.pushState()后,地址栏的 url 会扭转,而后通过 ( window.location.pathname) 获取扭转后的 path
// - 3. 而后通过 path 去匹配 routes 数组中的 path,匹配上就去更新 ui
const routes = [
{
path: "/home",
component: "<h1>home 页面 </h1>",
},
{
path: "/other",
component: "<h1>other 页面 </h1>",
},
];
class HistoryRouter {constructor(routes) {this.route = {};
this.init();
this.createRouteMap(routes); // (path) 和 (ui 更新函数) 之间的 (映射)
this.bindEvent(); // a 标签绑定 click 事件}
createRouteMap = (routes = []) => {
routes.forEach(({ path, component}) =>
(this.route[path] = () =>
(this.DOM_CONTENT.innerHTML = component))
);
};
init = () => {window.addEventListener("load", this.updateView, false);
window.addEventListener("popstate", this.updateView, false);
this.DOM_CONTENT = document.getElementsByClassName("root")[0];
};
bindEvent = () => {const aList = document.getElementsByTagName("a");
[...aList].forEach((a) => {const path = a.getAttribute("data-href");
a.addEventListener(
"click",
(e) => {window.history.pushState({}, null, path); // window.history.pushState(state, title, url),会扭转地址栏 url
this.updateView(); // 更新视图},
false
);
});
};
updateView = () => {
const path = window.location.pathname; // 获取最新 path
this.route?.[path]?.();};
}
new HistoryRouter(routes);
</script>
</body>
</html>
留神:该 html 须要用 Live Server 启动,vscode 插件,来提供 server
手动实现一个 vue-router(hash 版)
-
原理
- 大体的原理和 hash 路由的 html 版本一样,渺小区别
- 都是利用 (a 标签) 的 (href) 中的 (‘#/xxx’) 这样的 hash 串
- 只有点击 a 标签 => 地址栏的 url 的 hash 局部就会扭转 => 同时触发 hashchange 事件 => 通过 window.location.hash 获取最新的 hash
- 获取到 hash 在和 routeMap 中的 path 匹配,匹配后就扭转视图
-
区别
- 区别就是 Vue 本人封装了
<router-link>
和<router-view>
组件 - 能够通过 Vue.component() 办法注册下面两个组件
- 通过
vm.name 能够拜访到 new Vue({data: {name: xx}})中的 name
手动实现一个 vue-router(hash 版)
- 区别就是 Vue 本人封装了
- <router-link to=”#/home”>home</router-link> // 点击会跳转到 ‘#/home’ 地址
-
<router-view></router-view> // 路由将显示的 DOM 地位
// 定义一个名为 button-counter 的新组件
Vue.component(‘button-counter’, {
data: function () {
return {count: 0
}
},
template: ‘<button v-on:click=”count++”>You clicked me {{count}} times.</button>’
}) - 因为组件是可复用的 Vue 实例,所以它们与 new Vue 接管雷同的选项
-
例如 data、computed、watch、methods 以及生命周期钩子等。
<!DOCTYPE html>
<html lang=”en”>
<head>
<meta charset=”UTF-8″>
<meta name=”viewport” content=”width=device-width, initial-scale=1.0″>
<meta http-equiv=”X-UA-Compatible” content=”ie=edge”>
<title>Document</title>
<!– 引入 Vue 通过 CDN 引入 –>
<script src=”https://cdn.jsdelivr.net/npm/vue/dist/vue.js”></script>
</head>
<body>
<div id=”app”>
<!– 留神 router-link 组件具备 to 属性 –>
<router-link to=”#/home”>home</router-link>
<router-link to=”#/other”>other</router-link>
<router-view></router-view>
</div><script>
// 创立两个 vue 组件
const Home = {template: ‘<h1>home 页面 </h1>’}
const Other = {template: ‘<h1>other 页面 </h1>’}// 创立 vue 路由数组
const routes = [{path: '/home', component: Home
}, {
path: '/other', component: Other
}]
class VueRouter {
constructor(Vue, option) { // 参数:// Vue:Vue 构造函数,通过 cdn 引入的 // option: 配置对象,蕴含 routes 路由数组属性 this.$options = option this.routeMap = {} // 路由映射,是这样的构造 { path: component} this.createRouteMap(this.$options) // 创立路由映射 this.app = new Vue({ data: {currentHash: '#/'} }) // this.app.currentHash => 能够拜访到 currentHash 的值 '#/' // 举例 // var data = {name: 'woow_wu7'} // var vm = new Vue({ // data: data // }) // vm.name === data.name => true this.init() // 初始化监听函数 this.initComponent(Vue) // 初始化 Vue 种的各种组件 } createRouteMap = (option) => {// 留神:option 是传入 VueRoute 的第二个参数,即 {routes: routes} // 所以:options 是一个对象 option.routes.forEach(item => {this.routeMap[item.path] = item.component // this.routeMap 是这样一个对象:{path: component} }) } init = () => {window.addEventListener('load', this.onHashChange, false) // 页面加载实现触发,留神区别 DOMContentLoaded // load:页面加载实现时触发,包含 DOM 加载实现,图片,视频等所有资源加载实现 // DOMContentLoaded:DOM 加载实现时触发 window.addEventListener('hashchange', this.onHashChange, false) // 监听 hashchange 事件 // 触发 hashchange 的条件:hash 扭转时候 } onHashChange = () => {this.app.currentHash = window.location.hash.slice(1) || '/' // (1) // 当 hahs 没有扭转时,load 事件触发时 // window.location.hash = ''=> window.location.hash.slice(1) ='' // 所以:此种状况:this.app.currentHash = '/' // (2) // hash 扭转时,window.location.hash 有值,是 '#/...' 这样的字符串 } initComponent = (Vue) => { // router-link 组件 // props to 属性 // template 实质上会被解决成 a 标签,href 属性是传入的 to 属性,内容是 slot 插入的内容 Vue.component('router-link', { props: { to: { type: String, value: '' } }, template: '<a :href="to"><slot/></a>' }) Vue.component('router-view', {render: (h) => {const component = this.routeMap[this.app.currentHash] // 拿到最新 hash 对应的组件 return h(component) // h(component) 相当于 createElement(component) // render: function(createElement) {return createElement(App); } } }) }
}
new VueRouter(Vue, {routes
})
new Vue({el: '#app'
})
</script>
</body>
</html>
材料
URI 和 URL:https://www.luyuqiang.com/uri…
URI 和 URL 的区别举例(很形象)https://juejin.im/post/684490…
URL 的组成(优良)https://www.jianshu.com/p/406…
DOMContentLoaded 和 load 的区别:https://www.jianshu.com/p/1a8…
window.location 对象:https://wangdoc.com/javascrip…
vue-router 模仿实现 https://juejin.im/post/684490…
hash history 路由 模仿实现 https://juejin.im/post/684490…
vue-router 源码记录 https://juejin.im/post/684490…
VueRouter 源码剖析 https://juejin.im/post/684490…