当前位置: 首页 > news >正文

门户网站开发步骤博客彩视网站建设策划

门户网站开发步骤博客,彩视网站建设策划,网站停留时间,php伪静态网站破解本文由浅入深观摩vue-router源码是如何通过hash与History interface两种方式实现前端路由#xff0c;介绍了相关原理#xff0c;并对比了两种方式的优缺点与注意事项。最后分析了如何实现可以直接从文件系统加载而不借助后端服务器的Vue单页应用。随着前端应用的业务功能越来…本文由浅入深观摩vue-router源码是如何通过hash与History interface两种方式实现前端路由介绍了相关原理并对比了两种方式的优缺点与注意事项。最后分析了如何实现可以直接从文件系统加载而不借助后端服务器的Vue单页应用。随着前端应用的业务功能越来越复杂、用户对于使用体验的要求越来越高单页应用(SPA)成为前端应用的主流形式。大型单页应用最显著特点之一就是采用前端路由系统通过改变URL在不重新请求页面的情况下更新页面视图。“更新视图但不重新请求页面”是前端路由原理的核心之一目前在浏览器环境中这一功能的实现主要有两种方式利用URL中的hash(“#”)利用History interface在 HTML5中新增的方法vue-router是Vue.js框架的路由插件下面我们从它的源码入手边看代码边看原理由浅入深观摩vue-router是如何通过这两种方式实现前端路由的。模式参数在vue-router中是通过mode这一参数控制路由的实现模式的const router new VueRouter({mode: history,routes: [...]})创建VueRouter的实例对象时mode以构造函数参数的形式传入。带着问题阅读源码我们就可以从VueRouter类的定义入手。一般插件对外暴露的类都是定义在源码src根目录下的index.js文件中打开该文件可以看到VueRouter类的定义摘录与mode参数有关的部分如下export default class VueRouter {mode: string; // 传入的字符串参数指示history类别history: HashHistory | HTML5History | AbstractHistory; // 实际起作用的对象属性必须是以上三个类的枚举fallback: boolean; // 如浏览器不支持history模式需回滚为hash模式constructor (options: RouterOptions {}) {let mode options.mode || hash // 默认为hash模式this.fallback mode history !supportsPushState // 通过supportsPushState判断浏览器是否支持history模式if (this.fallback) {mode hash}if (!inBrowser) {mode abstract // 不在浏览器环境下运行需强制为abstract模式}this.mode mode// 根据mode确定history实际的类并实例化switch (mode) {case history:this.history new HTML5History(this, options.base)breakcase hash:this.history new HashHistory(this, options.base, this.fallback)breakcase abstract:this.history new AbstractHistory(this, options.base)breakdefault:if (process.env.NODE_ENV ! production) {assert(false, invalid mode: ${mode})}}}init (app: any /* Vue component instance */) {const history this.history// 根据history的类别执行相应的初始化操作和监听if (history instanceof HTML5History) {history.transitionTo(history.getCurrentLocation())} else if (history instanceof HashHistory) {const setupHashListener () {history.setupListeners()}history.transitionTo(history.getCurrentLocation(),setupHashListener,setupHashListener)}history.listen(route {this.apps.forEach((app) {app._route route})})}// VueRouter类暴露的以下方法实际是调用具体history对象的方法push (location: RawLocation, onComplete?: Function, onAbort?: Function) {this.history.push(location, onComplete, onAbort)}replace (location: RawLocation, onComplete?: Function, onAbort?: Function) {this.history.replace(location, onComplete, onAbort)}}可以看出作为参数传入的字符串属性mode只是一个标记用来指示实际起作用的对象属性history的实现类两者对应关系如下modehistoryhistoryHTML5HistoryhashHashHistoryabstractAbstractHistory在初始化对应的history之前会对mode做一些校验若浏览器不支持HTML5History方式(通过supportsPushState变量判断)则mode强制设为hash若不是在浏览器环境下运行则mode强制设为abstractVueRouter类中的onReady(), push()等方法只是一个代理实际是调用的具体history对象的对应方法在init()方法中初始化时也是根据history对象具体的类别执行不同操作在浏览器环境下的两种方式分别就是在HTML5HistoryHashHistory两个类中实现的。他们都定义在src/history文件夹下继承自同目录下base.js文件中定义的History类。History中定义的是公用和基础的方法直接看会一头雾水我们先从HTML5HistoryHashHistory两个类中看着亲切的push(), replace()方法的说起。HashHistory看源码前先回顾一下原理hash(“#”)符号的本来作用是加在URL中指示网页中的位置http://www.example.com/index.html#print#符号本身以及它后面的字符称之为hash可通过window.location.hash属性读取。它具有如下特点hash虽然出现在URL中但不会被包括在HTTP请求中。它是用来指导浏览器动作的对服务器端完全无用因此改变hash不会重新加载页面可以为hash的改变添加监听事件window.addEventListener(hashchange, funcRef, false)每一次改变hash(window.location.hash)都会在浏览器的访问历史中增加一个记录利用hash的以上特点就可以来实现前端路由“更新视图但不重新请求页面”的功能了。HashHistory.push()我们来看HashHistory中的push()方法push (location: RawLocation, onComplete?: Function, onAbort?: Function) {this.transitionTo(location, route {pushHash(route.fullPath)onComplete onComplete(route)}, onAbort)}function pushHash (path) {window.location.hash path}transitionTo()方法是父类中定义的是用来处理路由变化中的基础逻辑的push()方法最主要的是对window的hash进行了直接赋值window.location.hash route.fullPathhash的改变会自动添加到浏览器的访问历史记录中。那么视图的更新是怎么实现的呢我们来看父类History中transitionTo()方法的这么一段transitionTo (location: RawLocation, onComplete?: Function, onAbort?: Function) {const route this.router.match(location, this.current)this.confirmTransition(route, () {this.updateRoute(route)...})}updateRoute (route: Route) {this.cb this.cb(route)}listen (cb: Function) {this.cb cb}可以看到当路由变化时调用了History中的this.cb方法而this.cb方法是通过History.listen(cb)进行设置的。回到VueRouter类定义中找到了在init()方法中对其进行了设置init (app: any /* Vue component instance */) {this.apps.push(app)history.listen(route {this.apps.forEach((app) {app._route route})})}根据注释app为Vue组件实例但我们知道Vue作为渐进式的前端框架本身的组件定义中应该是没有有关路由内置属性_route如果组件中要有这个属性应该是在插件加载的地方即VueRouter的install()方法中混合入Vue对象的查看install.js源码有如下一段export function install (Vue) {Vue.mixin({beforeCreate () {if (isDef(this.$options.router)) {this._router this.$options.routerthis._router.init(this)Vue.util.defineReactive(this, _route, this._router.history.current)}registerInstance(this, this)},})}通过Vue.mixin()方法全局注册一个混合影响注册之后所有创建的每个 Vue 实例该混合在beforeCreate钩子中通过Vue.util.defineReactive()定义了响应式的_route属性。所谓响应式属性即当_route值改变时会自动调用Vue实例的render()方法更新视图。总结一下从设置路由改变到视图更新的流程如下$router.push() -- HashHistory.push() -- History.transitionTo() -- History.updateRoute() -- {app._route route} -- vm.render()HashHistory.replace()replace()方法与push()方法不同之处在于它并不是将新路由添加到浏览器访问历史的栈顶而是替换掉当前的路由replace (location: RawLocation, onComplete?: Function, onAbort?: Function) {this.transitionTo(location, route {replaceHash(route.fullPath)onComplete onComplete(route)}, onAbort)}function replaceHash (path) {const i window.location.href.indexOf(#)window.location.replace(window.location.href.slice(0, i 0 ? i : 0) # path)}可以看出它与push()的实现结构上基本相似不同点在于它不是直接对window.location.hash进行赋值而是调用window.location.replace方法将路由进行替换。监听地址栏以上讨论的VueRouter.push()和VueRouter.replace()是可以在vue组件的逻辑代码中直接调用的除此之外在浏览器中用户还可以直接在浏览器地址栏中输入改变路由因此VueRouter还需要能监听浏览器地址栏中路由的变化并具有与通过代码调用相同的响应行为。在HashHistory中这一功能通过setupListeners实现setupListeners () {window.addEventListener(hashchange, () {if (!ensureSlash()) {return}this.transitionTo(getHash(), route {replaceHash(route.fullPath)})})}该方法设置监听了浏览器事件hashchange调用的函数为replaceHash即在浏览器地址栏中直接输入路由相当于代码调用了replace()方法HTML5HistoryHistory interface是浏览器历史记录栈提供的接口通过back(), forward(), go()等方法我们可以读取浏览器历史记录栈的信息进行各种跳转操作。从HTML5开始History interface提供了两个新的方法pushState(), replaceState()使得我们可以对浏览器历史记录栈进行修改window.history.pushState(stateObject, title, URL)window.history.replaceState(stateObject, title, URL)stateObject: 当浏览器跳转到新的状态时将触发popState事件该事件将携带这个stateObject参数的副本title: 所添加记录的标题URL: 所添加记录的URL这两个方法有个共同的特点当调用他们修改浏览器历史记录栈后虽然当前URL改变了但浏览器不会立即发送请求该URL(the browser wont attempt to load this URL after a call to pushState())这就为单页应用前端路由“更新视图但不重新请求页面”提供了基础。我们来看vue-router中的源码push (location: RawLocation, onComplete?: Function, onAbort?: Function) {const { current: fromRoute } thisthis.transitionTo(location, route {pushState(cleanPath(this.base route.fullPath))handleScroll(this.router, route, fromRoute, false)onComplete onComplete(route)}, onAbort)}replace (location: RawLocation, onComplete?: Function, onAbort?: Function) {const { current: fromRoute } thisthis.transitionTo(location, route {replaceState(cleanPath(this.base route.fullPath))handleScroll(this.router, route, fromRoute, false)onComplete onComplete(route)}, onAbort)}// src/util/push-state.jsexport function pushState (url?: string, replace?: boolean) {saveScrollPosition()// try...catch the pushState call to get around Safari// DOM Exception 18 where it limits to 100 pushState callsconst history window.historytry {if (replace) {history.replaceState({ key: _key }, , url)} else {_key genKey()history.pushState({ key: _key }, , url)}} catch (e) {window.location[replace ? replace : assign](url)}}export function replaceState (url?: string) {pushState(url, true)}代码结构以及更新视图的逻辑与hash模式基本类似只不过将对window.location.hash直接进行赋值window.location.replace()改为了调用history.pushState()和history.replaceState()方法。在HTML5History中添加对修改浏览器地址栏URL的监听是直接在构造函数中执行的constructor (router: Router, base: ?string) {window.addEventListener(popstate, e {const current this.currentthis.transitionTo(getLocation(this.base), route {if (expectScroll) {handleScroll(router, route, current, true)}})})}当然了HTML5History用到了HTML5的新特特性是需要特定浏览器版本的支持的前文已经知道浏览器是否支持是通过变量supportsPushState来检查的// src/util/push-state.jsexport const supportsPushState inBrowser (function () {const ua window.navigator.userAgentif ((ua.indexOf(Android 2.) ! -1 || ua.indexOf(Android 4.0) ! -1) ua.indexOf(Mobile Safari) ! -1 ua.indexOf(Chrome) -1 ua.indexOf(Windows Phone) -1) {return false}return window.history pushState in window.history})()以上就是hash模式与history模式源码的导读这两种模式都是通过浏览器接口实现的除此之外vue-router还为非浏览器环境准备了一个abstract模式其原理为用一个数组stack模拟出浏览器历史记录栈的功能。当然以上只是一些核心逻辑为保证系统的鲁棒性源码中还有大量的辅助逻辑也很值得学习。此外在vue-router中还有路由匹配、router-view视图组件等重要部分关于整体源码的阅读推荐滴滴前端的这篇文章两种模式比较在一般的需求场景中hash模式与history模式是差不多的但几乎所有的文章都推荐使用history模式理由竟然是# 符号太丑...0_0 如果不想要很丑的 hash我们可以用路由的 history 模式 ——官方文档当然严谨的我们肯定不应该用颜值评价技术的好坏。根据MDN的介绍调用history.pushState()相比于直接修改hash主要有以下优势pushState设置的新URL可以是与当前URL同源的任意URL而hash只可修改#后面的部分故只可设置与当前同文档的URLpushState设置的新URL可以与当前URL一模一样这样也会把记录添加到栈中而hash设置的新值必须与原来不一样才会触发记录添加到栈中pushState通过stateObject可以添加任意类型的数据到记录中而hash只可添加短字符串pushState可额外设置title属性供后续使用history模式的一个问题我们知道对于单页应用来讲理想的使用场景是仅在进入应用时加载index.html后续在的网络操作通过Ajax完成不会根据URL重新请求页面但是难免遇到特殊情况比如用户直接在地址栏中输入并回车浏览器重启重新加载应用等。hash模式仅改变hash部分的内容而hash部分是不会包含在HTTP请求中的http://oursite.com/#/user/id // 如重新请求只会发送http://oursite.com/故在hash模式下遇到根据URL请求页面的情况不会有问题。而history模式则会将URL修改得就和正常请求后端的URL一样http://oursite.com/user/id在此情况下重新向后端发送请求如后端没有配置对应/user/id的路由处理则会返回404错误。官方推荐的解决办法是在服务端增加一个覆盖所有情况的候选资源如果 URL 匹配不到任何静态资源则应该返回同一个 index.html 页面这个页面就是你 app 依赖的页面。同时这么做以后服务器就不再返回 404 错误页面因为对于所有路径都会返回 index.html 文件。为了避免这种情况在 Vue 应用里面覆盖所有的路由情况然后在给出一个 404 页面。或者如果是用 Node.js 作后台可以使用服务端的路由来匹配 URL当没有匹配到路由的时候返回 404从而实现 fallback。直接加载应用文件Tip: built files are meant to be served over an HTTP server.Opening index.html over file:// wont work.Vue项目通过vue-cli的webpack打包完成后命令行会有这么一段提示。通常情况无论是开发还是线上前端项目都是通过服务器访问不存在 Opening index.html over file:// 但程序员都知道需求和场景永远是千奇百怪的只有你想不到的没有产品经理想不到的。本文写作的初衷就是遇到了这样一个问题需要快速开发一个移动端的展示项目决定采用WebView加载Vue单页应用的形式但没有后端服务器提供所以所有资源需从本地文件系统加载// AndroidAppWrapperpublic class MainActivity extends AppCompatActivity {private WebView webView;Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);webView new WebView(this);webView.getSettings().setJavaScriptEnabled(true);webView.loadUrl(file:///android_asset/index.html);setContentView(webView);}Overridepublic boolean onKeyDown(int keyCode, KeyEvent event) {if ((keyCode KeyEvent.KEYCODE_BACK) webView.canGoBack()) {webView.goBack();return true;}return false;}}此情此景看来是必须 Opening index.html over file:// 了为此我首先要进行了一些设置在项目config.js文件中将assetsPublicPath字段的值改为相对路径 ./调整生成的static文件夹中图片等静态资源的位置与代码中的引用地址一致这是比较明显的需要改动之处但改完后依旧无法顺利加载经过反复排查发现项目在开发时router设置为了history模式(为了美观...0_0)当改为hash模式后就可正常加载了。为什么会出现这种情况呢我分析原因可能如下当从文件系统中直接加载index.html时URL为file:///android_asset/index.html而首页视图需匹配的路径为path: / :export default new Router({mode: history,routes: [{path: /,name: index,component: IndexView}]})我们先来看history模式在HTML5History中ensureURL (push?: boolean) {if (getLocation(this.base) ! this.current.fullPath) {const current cleanPath(this.base this.current.fullPath)push ? pushState(current) : replaceState(current)}}export function getLocation (base: string): string {let path window.location.pathnameif (base path.indexOf(base) 0) {path path.slice(base.length)}return (path || /) window.location.search window.location.hash}逻辑只会确保存在URLpath是通过剪切的方式直接从window.location.pathname获取到的它的结尾是index.html因此匹配不到 / 故 Opening index.html over file:// wont work 。再看hash模式在HashHistory中export class HashHistory extends History {constructor (router: Router, base: ?string, fallback: boolean) {...ensureSlash()}// this is delayed until the app mounts// to avoid the hashchange listener being fired too earlysetupListeners () {window.addEventListener(hashchange, () {if (!ensureSlash()) {return}...})}getCurrentLocation () {return getHash()}}function ensureSlash (): boolean {const path getHash()if (path.charAt(0) /) {return true}replaceHash(/ path)return false}export function getHash (): string {const href window.location.hrefconst index href.indexOf(#)return index -1 ? : href.slice(index 1)}我们看到在代码逻辑中多次出现一个函数ensureSlash()当#符号后紧跟着的是/则返回true否则强行插入这个/故我们可以看到即使是从文件系统打开index.htmlURL依旧会变为以下形式file:///C:/Users/dist/index.html#/getHash()方法返回的path为 / 可与首页视图的路由匹配。故要想从文件系统直接加载Vue单页应用而不借助后端服务器除了打包后的一些路径设置外还需确保vue-router使用的是hash模式。
http://www.pierceye.com/news/774672/

相关文章:

  • 个人备案做运营网站wordpress公众号源码
  • 营销网站服务器郑州金水区
  • 陕煤化建设集团铜川分公司网站大冶建设局网站
  • 网站如何制作django企业网站开发实例
  • 免费网站建设软件大全平面设计与网页设计
  • 建设网站 怀疑对方传销 网站制作 缓刑网站ip如何做跳转
  • 公司网站建设要求书网页怎么弄到桌面快捷方式
  • 做网站 公司 个体学校网站建设板块分析
  • 如何让百度更新网站收录wordpress图片处理类
  • 镜像网站能否做google排名企业做网站需要注意什么
  • 网站设计公司名称dz网站恢复数据库
  • 展示网站和营销网站的区别舆情分析师
  • 做网站用那一种语言最好网站推广指的是什么
  • 有哪些网站可以学做糕点的专业做网站建设公司怎么样
  • 广州网站排名怎么优化androidapp开发教程
  • 永顺网站建设网站建设免费建站
  • 建立一个网站需要多少钱?jquery网站后台模板
  • PHP网站开发工程师招聘营销型网站主机
  • 百度招聘 网站开发书画网站免费源码
  • 4s店网站建设贺贵江seo教程
  • 做网站的公司一般怎么培训销售wordpress引用php
  • 自己怎样做网站平台网页设计做网站首页
  • 费县做网站点石家装
  • 科技网站制作案例图片制作在线网页
  • 怀柔成都网站建设网络推广图片
  • 网站建设微信运营公司中国室内设计公司
  • app推广平台网站建设银行东营分行网站
  • 校园二手交易网站设计的原则群辉搭wordpress
  • 无锡网站建设网页制作seo网站优化培训要多少钱
  • 一个人可以做几个网站seo页面检测