代码地址:

Hash 模式和 History 模式的区别

2种方式都是客户端路由的实现方式,也就是当路径发生变化,不会向服务器发生请求。使用js监视路径的变化,不同的地址渲染不同的内容,如果需要服务端内容发送ajax。

  • 表现形式的区别:

Hash:携带#,#之后是路由地址,可以通过?携带参数。很丑。 https://music.163.com/#/paylist?id=3102977778
History: 一个正常的url. https://music.163.com/paylist/3102977778.

  • 原理的区别:
    Hash:模式是基于锚点,以及onhashchange事件。通过锚点的值作为路由地址,当地址发生变化后,触发onhashchange事件。根据路径决定页面的内容。
    History:模式是基于html5中的History api
    1. history.pushState(). IE10以后才支持 。
    2. history.replaceState()

pushState和push方法的区别是:当我们调用history.push方法时,路径会发生变化,这个时候要向服务器发送请求,而我们调用history.pushState时,不会向服务器发送请求,只会改变我们浏览器中地址栏中的地址,并且把我们的地址就录到历史路径中,因此通过history.pushState可以实现客户端路由,但是 history.pushState(). IE10以后才支持,也就是使用history有兼容性问题。

  • History 模式 - Node.js

  • History 模式 - nginx
    默认80端口
    启动: start nginx
    重启:nginx -s reload
    停止:nginx -s stop

请添加图片描述

html: 用来存储网站。打包好的前端代码,放到html文件夹中,替换默认的index.html为最新的。

nginx端口:默认是80端口,如果80端口被占用,无法启动,也不会报错。
验证nginx启动是否成功:在浏览器中输入:localhost回车:
显示如下:则启动成功。
请添加图片描述

nginx.conf

server {
        # 静态资源根路径变量
        set $root /home/admin;
        listen 8080; // 监听的端口,默认是80端口,如果80端口被占用,可以重新设置
        server_name localhost; // 绑定的域名,默认是localhost,指的是本地
        charset utf-8;
        # 静态资源根路径,(后面相关前端静态资源都放单该路径下)
        root /home/admin;
        # 在location中指定了当前网站的根目录,root :默认的根目录,index: 默认的首页,也就是当我们访问localhost按回车的时候,在根目录中找index.html,或者index.htm
        location / {
          try_files $uri $uri/ @router; // try_files:配置vue-route histroy模式:try_files:表示试着访问,$uri:当前请求的路径,会找这个路径对应的文件,如果找到就把这个路径返回,如果找不到接着找$uri/,$uri/表示文件目录,找这个$uri/下面的首页,index.html,indx.htm,如果找到返回,找不到继续匹配
          index  index.html index.htm;
        }
        location @router {
          rewrite ^.*$ /index.html last;
        }
 
        location /api {
        // 配置的反向代理
          proxy_pass http://backend;
        }
        location /innerapi {
          proxy_pass http://backend;
        }
    }

2vue-router实现原理

使用到的知识库:
history模式:

  1. 插件
  2. 混入
  3. Vue.observable()
  4. 插槽
  5. render函数
  6. 运行时和完整版的Vue

vue-router实现原理:vue-router是前端路由,当路径切换的时候浏览器判断当前路径并加载当前路径对应的组件

hash模式:是把url中#后面的内容作为路径地址,直接可以通过location.url来切换浏览器中的地址,如果只改变了路径中#后面的内容,浏览器不会向服务器请求这个地址,但是会把这个地址记录到浏览器的访问历史中,当hash改变后,我们需要监听hash的变化,并做相应的处理,我们只需要监听hashchange事件,当hash发生变化后,触发hashchange事件,在hashchange事件中记录当前的路由地址,并找到该路径对应的组件,然后重新渲染。

history模式:就是一个普通的url,通过使用history.pushState()方法改变浏览器的地址栏, 注意:pushState方法仅仅是改变地址栏,并把当前地址记录到浏览器的访问历史中,并不会真正的跳转到指定的路径,也就是浏览器不会向服务器发送请求。通过监听popstate事件,可以监听到浏览器历史操作的变化。 在popstate事件的处理函数中,可以记录改变后的地址。要注意的是:当调用的是pushState,或者是replacestate的时候,并不会触发该事件,当点击浏览器的前进和后退按钮的时候,或者调用history.back,history.forword方法时,该事件才会触发,最后当地址改变之后,要根据当前的地址,找到对应的组件,进行重新渲染。

2.3实现自己的vue-router history模式

回顾核心代码:
请添加图片描述

类图:用来描述类中的所有成员。
请添加图片描述
类属性:
option:记录构造函数中传递的对象
data:是一个对象,当中有一个属性current,用来记录当前的路由地址。我们需要一个响应式的对象,data就是一个响应式的对象。因为地址发生变化,组件也要自动改变,我们使用Vue.observable()这个方法。
routeMap:是一个对象记录:路由地址和组件的对应关系。

类方法:
+ 对外公开的方法
- 静态方法

_install:静态方法:用来实现vue的插件机制。
inti:调用下面的3个方法
initEvent: 用来注册popState事件,监听浏览器历史的变化。
createRouteMap:初始化routeMap,把我们传递过来的路由规则转化为键值对的形式存储到routeMap对象中。键:路由地址; 值:对应的组件。
initCompoents:创建route-link,route-view组件。

2.4 VueRouter-install
  • 运行时版本:
    不支持template模版,需要打包的时候提前编译。

  • 完整版本:
    包含运行时和编译器,体积比运行时版大10k左右,程序运行的时候把模版转换成render函数。

Vue-Router.js

console.dir(Vue)
let _Vue = null
class VueRouter {
    static install(Vue){
    //1 判断当前插件是否被安装
    //2 把Vue的构造函数记录在全局
    //3 把创建Vue的实例传入的router对象注入到Vue实例


        //1 判断当前插件是否被安装
        if(VueRouter.install.installed){
            return; // 如果已经安装直接return
        }
        
        VueRouter.install.installed = true // 插件已经被安装
        //2 把Vue的构造函数记录在全局
        _Vue = Vue
        //3 把创建Vue的实例传入的router对象注入到Vue实例
        // _Vue.prototype.$router = this.$options.router
        _Vue.mixin({
            beforeCreate(){
                if(this.$options.router){
                    _Vue.prototype.$router = this.$options.router
                }
               
            }
        })
    }
    constructor(options){
        this.options = options
        this.routeMap = {}
        // observable
        this.data = _Vue.observable({
            current:"/"
        })
        this.init()

    }
    init(){
        this.createRouteMap()
        this.initComponent(_Vue)
        this.initEvent()
    }
    createRouteMap(){
        //遍历所有的路由规则 把路由规则解析成键值对的形式存储到routeMap中
        this.options.routes.forEach(route => {
            this.routeMap[route.path] = route.component
        });
    }
    initComponent(Vue){
        // 创建router-link组件
        Vue.component("router-link",{
            props:{
                to:String
            },
            render(h){
                return h("a",{
                    attrs:{
                        href:this.to
                    },
                    on:{
                        click:this.clickhander
                    }
                },[this.$slots.default])
            },
            methods:{
                clickhander(e){
                    history.pushState({},"",this.to)
                    this.$router.data.current=this.to
                    e.preventDefault()
                }
            }
            // 第2种创建组件的方法:运行时版本:
            // template:"<a :href='to'><slot></slot><>"
        })
        const self = this
        // 创建router-view组件
        Vue.component("router-view",{
            render(h){
                // self.data.current
                const cm=self.routeMap[self.data.current]
                return h(cm)
            }
        })
        
    }
    initEvent(){
        //
        window.addEventListener("popstate",()=>{
            this.data.current = window.location.pathname
        })
    }
}
Logo

前往低代码交流专区

更多推荐