### Vue.js
## 一、 课堂目标
1. 掌握前端单页应用的原理
2. 掌握vue-router工作机制
3. 手写vue-router
## 二、 知识点
### 2.1 什么是路由?前端有哪些路由?他们有 哪些特性?
#### 2.1.1 History API
#### 2.1.2 hash 模式
hash 路由模式是这样的:http://xxx.abc.com/#/xx。 有带#号,后面就是 hash 值的变化。 改变后面的 hash 值,它不会向服务器发出请求,因此也就不会刷新页面。并且每次 hash 值发生改变的时候,会触发 hashchange 事件。因此我们可以通过监听该事件,来知道 hash 值发生了哪些变化。比如我们可以如下简单的监听:
function hashAndUpdate () { // todo 匹配 hash 做 dom 更新操作 }window.addEventListener('hashchange', hashAndUpdate);
我们先来了解下 location 有哪些属性,如下
* location.href
完整的 url
* location.protocol
当前 URL 的协议,包括 :; 比如 https:
* location.host
主机名和端口号,如果端口号是 80(http)或 443(https), 那就会省略端口号,比如www.baidu.com:8080
* location.hostname
主机名:比如:www.baidu.com
* location.port
端口号;比如 8080
* location.pathname
url 的路径部分,从 / 开始; 比如 https://www.baidu.com/s?ie=utf-8,那么 pathname = '/s'了
* location.search
查询参数,从?开始;比如 https://www.baidu.com/s?ie=utf-8 那么 search = '?ie=utf-8'
* location.hash
hash 是页面中的一个片段,从 # 开始的,比如 https://www.baidu.com/#/a/b 那么 返回值就是:"#/a/b"
改变 hash 不会触发页面跳转,因为 hash 链接是当前页面中的某个片段,所以如果 hash 有变化,那么页面将会滚动到 hash 所连接的位置。但是页面中如果不存在 hash 对应的片 段,则没有任何效果。比如 a 链接。这和 window.history.pushState 方法类似,都是不 刷新页面的情况下更改 url。如下也可以看到操作并没有刷新 url,如下演示:
#### 2.1.3 hash 和 pushState 对比有如下缺点:
1. hash 只能修改 url 的片段标识符的部分。并且必须从#号开始,但是 pushState 且能修 改路径、查询参数和片段标识符。pushState 比 hash 更符合前端路由的访问方式,更加优 雅(因为不带#号)。
2. hash 必须和原先的值不同,才能新增会话浏览历史的记录,但是 pushState 可以新增相 同的 url 的记录,如下所示:
#### 2.1.4 如何实现简单的 hash 路由?
实现 hash 路由需要满足如下基本条件:
1. url 中 hash 值的改变,并不会重新加载页面。
2. hash 值的改变会在浏览器的访问历史中增加一条记录,我们可以通过浏览器的后退,前进按钮控制 hash 值的切换。
3. 我们可以通过 hashchange 事件,监听到 hash 值的变化,从而加载不同的页面显示。
触发 hash 值的变化有 2 种方法:
第一种是通过 a 标签,设置 href 属性,当点击 a 标签之后,地址栏会改变,同时会触发 hashchange 事件。比如如下 a 链接: <a href="#/test1">测试 hash1</a>
第二种是通过 js 直接赋值给 location.hash,也会改变 url,触发 hashchange 事件。 location.hash = '#/test1';
### 2.1 新建工程
// vue create RMrouter vue-cli 4
vue init webpack RMrouter vue-cli 2
### 2.2 熟悉vue自身插件的使用方式
### 2.3 了解访问器属性
```javascript
var obj = {};
obj._name='1'; //内部属性,不能被改变
Object.defineProperty(obj,'currentPath',{
get(){
return this._name
},
set(val){
this._name = val
}
})
```
### 2.3 了解构造函数的静态属性
```javascript
class Laney{
static test=1;
static fun1(){ console.log('opp')}
}
console.log(Laney.test)
Laney.fun1()
```
### 2.4 了解createElement创建虚拟dom
```javascript
//第三种写法:render模式,性能最优
new Vue({
el:'#app',
router:VueRouter,
data:{
hello:'hello',
msg: 'Welcome to ruanmou'
},
//创建虚拟Dom,不用组件
// render(createElement){
// return createElement('div',{
// id: "app1",
// style:{
// color:'red'
// }
// },[
// createElement('h1',this.msg),
// createElement('span','world')
// ])
// },
//使用组件, 利用render函数渲染
render:function(h){
return h(App);
},
// render:h => h(App)
mounted(){
console.log(this);
}
});
```
### 2.5了解编写vue插件机制
Vue.use
用户执行Vue.use的时候,实际执行的是模块的install方法,会把Vue的实例传递进去,比如咱们整个字符串
```javascript
let Vue;
class RMRouter {
static install(_Vue){
Vue=_Vue;
Vue.mixin({
beforeCreate() {
const options=this.$options;
console.log(options)
Vue.prototype.$rmrouter='晚上好'
}
})
}
}
export default RMRouter;
```
### 2.4 单页页面
#### 2.4.1 hash模式
使用url#后面的锚点来区分组件,hash改变的时候,页面不会重新加载,只会触发onhashchange事件
#### 2.4.2 hishtory模式
hash的url略丑,使用mode:history,这种模式充分利用了html5 history interface 中新增的 pushState() 和replaceState() 方法。这两个方法应用于浏览器记录栈,
在当前已有的 back、forward、go 基础之上,它们提供了对历史记录修改的功能。只是当它们执行修改时,虽然改变了当前的 URL ,但浏览器不会立即向后端发送请求。
### 2.5 上代码
```javascript
//封装插件
let Vue;
// var obj = {};
// obj._name='1'; //内部属性,不能被改变
// Object.defineProperty(obj,'currentPath',{
// get(){
// return this._name
// },
// set(val){
// this._name = val
// }
// })
class RMRouter {
static install(_Vue){
Vue = _Vue;
Vue.mixin({
beforeCreate() {
Vue.prototype.$brouter = '我是路由,我来了'
if(this.$options.router) {
//启动路由
//this 是Vue的实例化对象
Vue.prototype.$krouter = this.$options.router;
this.$options.router.init()
}
}
})
}
constructor(options){
// 方式一:
// options._curTest = '/';
// Object.defineProperty(options,'current',{
// get(){
// return this._curTest
// },
// set(val){
// this._curTest = val
// }
// });
this.$options = options;
this.routerMap={};
// 路由 -- 组件
// /test1 - test1.vue
// 使用vue相应机制,路由切换的时候, 做一些相应
//方式一:
this.app = new Vue({
data(){
return {
//当前路由
current:'/'
}
}
})
//方式二:
this.$options.current = '/';
}
init(){
//1. 监听hashchange事件
this.initEvents();
// 2. 处理路由 routerMap
this.createRouterMap();
// 3. 初始化组件 router-view, router-link
this.initComponent();
// 4. 路由守卫
}
//1. 监听hashchange事件
initEvents(){
window.addEventListener('hashchange',this.onHashChage.bind(this),false)
window.addEventListener('load',this.onHashChage.bind(this),false)
}
onHashChage(e){
// this --
console.log(e);
console.log('我监听到路由了');
//获取当前的hash
let hash = window.location.hash.slice(1) || '/';
this.app.current = hash; //方式一:
// this.$options.current = hash; //方式二:
}
createRouterMap(){
this.$options.routes.forEach(item=>{
this.routerMap[item.path] = item.component;
})
}
// 3. 初始化组件 router-view, router-link
initComponent(){
Vue.component('router-view',{
render:h=>{
console.log('this.app.current',this.app.current); //方式一:
// console.log('this.$options.current',this.$options.current); //方式二:
// 只要当前的路由hash变化, 这里可以坚挺到
const component = this.routerMap[this.app.current]; //方式一:
// const component = this.routerMap[this.$options.current].component; //方式二:
return h(component);
}
});
Vue.component('router-link',{
// props:['to'],
props:{
to:String
},
render:function(h){
// h-- createElemnt
// 3个参数
// dom节点名
// 属性
// 子元素
return h('a',{
attrs:{
href:'#'+this.to
}
},[this.$slots.default])
}
// template最终也是转换成render来执行
// 需要compile
// template:"<a :href='to'><slot></slot></a>"
});
}
}
export default RMRouter;
```
来源:https://www.cnblogs.com/laneyfu/p/12427548.html