Vue-router
箭头函数
箭头函数的定义
-
箭头函数也是一种定义函数的方式。
-
定义函数的方式:
// 1.通过 Function 定义函数。 const aaa = function(){ // 函数体 } // 2.在对象字面量中定义函数 const obj = { bbb: function(){ // 函数体 }, ccc() { // 函数体 } } // 3.ES6 中的箭头函数 const ddd = () => { // 函数体 }
箭头函数的参数问题
-
传入两个参数:
const sum = (num1,num2) => { return num1 + num2 // 返回的值是 num1 和 num2 的和。 }
-
传入一个参数:
const power = (num) => { return num * num // 返回的值是 num 的二次方。 } // 当传入的参数是一个的时候,() 是可以省略的。 const power = num => { return num * num }
-
函数中代码语句
-
函数体中有多行代码时
const log = () => { console.log('hello world!') // 输出hello world! console.log('hello Vuejs!') // 输出hello Vuejs! }
-
函数体中只有一行代码时:他会把箭头后面的代码结果作为函数的返回值。
const sum = (num1,num2) => num1 + num2 console.log(sum(20,30)) // 输出的结果是:50 // 当箭头后面的代码没有返回值时,函数会返回一个 undefind const f1 = () => console.log('hello vuejs!') console.log(f1())
-
箭头函数的 this 指向
-
一般要把一个函数的返回值作为参数传入另外一个函数的时候,会使用箭头函数:
setTimeout(() => { console.log(this) },1000)
-
this指向问题:
setTimeout(function() = { console.log(this) // this 指向的是 window 对象 },1000) setTimeout(() => { console.log(this) // this 指向的是 window 对象 },1000) const obj = { aaa() { setTimeout(function() = { console.log(this) // this 指向的是 window 对象 },1000) setTimeout(() => { console.log(this) // this 指向的是 obj 对象 },1000) } }
- 结论:箭头函数中的 this 引用的是最近作用域中的 this 。
- 箭头函数会向外层一层一层地查找,直到找到 this 的定义为止。
升级版 this 指向判断
const obj = {
aaa() {
setTimeout(function() = {
setTimeout(function() = {
console.log(this) // this 指向的是 window 对象
},1000)
setTimeout(() => {
console.log(this) // this 指向的是 window 对象
},1000)
},1000)
setTimeout(() => {
setTimeout(function() = {
console.log(this) // this 指向的是 window 对象
},1000)
setTimeout(() => {
console.log(this) // this 指向的是 obj 对象
},1000)
},1000)
}
}
了解知识点
什么是路由
- 路由是网络工程里面的一个术语。
- **路由(routing):**就是通过互联的网络把信息从源地址传输到目的地址的活动。
- 路由器就是生活中的路由。
- 路由器提供了两种机制:路由和传送。
- 路由是决定数据包从来源到目的地的路径。
- 传送将输入端的数据转移到合适的输出端。
- 路由中有一个非常重要的概念叫路由表。
- 路由表本质上就是一个映射表,决定了数据包的指向。
- 路由器提供了两种机制:路由和传送。
后端渲染和后端路由
- **后端渲染:**后端渲染也叫服务端渲染。当请求 url 发送时,前端页面向服务器请求页面,此时动态页面在后端形成,再传输给前端,这种渲染方式为后端渲染。
- 早期的网站开发整个 HTML 页面是由服务器来渲染的。
- 服务器直接生产渲染好对应的 HTML 页面,返回给客户端进行展示。
- 早期的网站开发整个 HTML 页面是由服务器来渲染的。
- 后端路由:后端处理URL和页面之间的映射关系,就叫后端路由。
- 服务器是怎么处理这么多页面的?
- 一个页面有自己对应的网址,也就是 URL 。
- URL 会发送到服务器,服务器会通过正则对该 URL 进行匹配,并且最后交给一个 Controller 进行处理。
- Controller 进行各种处理,最终生成 HTML 或者数据,返回给前端。
- 这就完成了一个 IO 操作。
- 上面的这种操作就是后端路由。
- 当我们页面中需要请求不同的路径内容时,交给服务器来进行处理,服务器渲染好整个页面,并且将页面返回给客户端。
- 这种情况下渲染好的页面,不需要单独加载任何的 js 和 css ,可以直接交给浏览器展示,这样也就有利于 SEO 的优化。
- 服务器是怎么处理这么多页面的?
- 后端路由的缺点:
- 一种情况是整个页面的模块由后端人员来编写和维护的。
- 另一种情况是前端开发人员如果要开发页面,需要通过 PHP 和 Java 等语言来编写页面代码。
- 而且通常情况下 HTML 代码和数据以及对应的逻辑会混在一起,编写和维护都是非常糟糕的事情。
前端渲染和前端路由
- **前端渲染:**当请求url发送时,前端页面向服务器请求页面,服务器传输给前端页面为(html+css+js)的页面,页面在前端游览器进行渲染的方式为前端渲染。
- 前端路由:前端处理URL和页面之间的映射关系,就叫前端路由。
前后端分离阶段
- 随着 Ajax 的出现,有了前后端分离的开发模式。
- 后端只提供 API 来返回数据,前端通过 Ajax 或取数据,并且可以通过 JavaScript 将数据渲染到页面中。
- 这样做最大的优点就是前后端责任的清晰,后端专注于数据上,前端专注于交互和可视化上。
- 并且当移动端(IOS/Android)出现后,后端不需要进行任何处理,依然使用之前的一套 API 即可。
- 目前很多的网站依然采用这种开发模式。
单页面富应用阶段
-
**单页Web应用(single page web application,简称SPA):**就是只有一张Web页面的应用,是加载单个HTML 页面并在用户与应用程序交互时动态更新该页面的Web应用程序。
-
其实 SPA 最主要的特点就是在前后端分离的基础上加了一层前端路由。
-
也就是前端来维护一套路由规则。
-
单页面应用的优缺点:
-
优点:
- 用户操作体验好,用户不用刷新页面,整个交互过程都是通过 Ajax 来操作。
- 适合前后端分离开发,服务端提供 http 接口,前端请求 http 接口获取数据,使用 JS 进行客户端渲染。
-
缺点:
-
首页加载慢:
- 单页面应用会将 js 、 css 打包成一个文件,在加载页面显示的时候加载打包文件,如果打包文件较大或者网速慢则用户体验不好,首屏时需要请求一次 html ,同时还要发送一次 js 请求,两次请求回来了,首屏才会展示出来。相对于多页应用
-
SEO 不友好:
- SEO 效果差,因为搜索引擎只认识 html 里的内容,不认识 js 的内容,而单页应用的内容都是靠 js 渲染生成出来的,搜索引擎不识别这部分内容,也就不会给一个好的排名,会导致单页应用做出来的网页在百度和谷歌上的排名差。使用单页面应用将大大减少搜索引擎对网站的收录。
-
-
URL 的 hash
-
URL 的 hash 也就是锚点(#),本质上是改变 window.location 的 herf 属性。
-
我们可以通过直接赋值 location.hash 来改变 herf ,但是页面不发生刷新。
-
用法:
location.hash = 'aaa'
HTML5 的 history 模式:
pushState
-
用法:
history.pushState(data,title,?url) // 距离 history.pushState({},'','home')
back()
-
用法:
history.back()
-
back():返回上一层的 url 。
replaceState
-
用法:
history.replaceState(data,title,?url) // 距离 history.replaceState({},'','home')
-
replaceState 改变的时候是没有办法返回的。
go
-
用法:
history.go(?data)
-
go():跳转到某一个页面。
-
补充:
- history.back() 等价于 history.go(-1) 。
- history.forward() 则等价于 history.go(1) 。
认识 Vue-router
- 目前前端流行的三大框架,都有自己的路由实现。
- Angular 的 ngRouter。
- react 的 ReactRouter
- Vue 的 Vue-router。
- Vue-router
- Vue-router 是 Vue.js 官方的路由插件,他和 vue.js 是深度集成的,适合用于构建单页面应用。
- Vue-router
- Vue-router 是基于路由和组件的。
- 路由用于设定访问路径,将路径和组件映射起来。
- 在 Vue-router 的单页面应用中,页面的路径的改变就是组件的切换。
安装和使用 Vue-router
-
安装:
npm install vue-router --save
-
在模块化工程中使用它(因为是一个插件,所以可以通过 Vue.use() 来安装路由功能)。
- 第一步:导入路由对象,并且**调用 Vue.use(VueRouter) 。
- 第二步:创建路由实例,并且传入路由映射配置。
- 第三步:在 Vue 实例中挂载创建的路由实例。
-
使用 Vue-router 的步骤:
- 第一步:创建路由组件。
- 第二步:配置路由映射:组件和路径映射关系。
- 第三步:使用路由:通过
<router-link></router-link>
和<router-view></router-view>
。
-
使用:
// App.vue 文件 <template> <div id="app"> <router-link to="/home">首页</router-link> <router-link to="/about">关于</router-link> // <router-link> 最终会被渲染成为一个 a 标签。 <router-view></router-view> // <router-view> 该组件会根据当前的路径,动态渲染出不同的组件。 </div> </template> <script> export default { name: 'App' } </script> <style> </style>
// home 组件 <template> <div> <h2>我是首页</h2> <p>我是首页内容,哈哈哈</p> </div> </template> <script> export default { name: 'Home' } </script> <style scoped> </style>
// about 组件 <template> <div> <h2>我是关于</h2> <p>我是关于内容,呵呵呵</p> </div> </template> <script> export default { name: 'About' } </script> <style scoped> </style>
// main.js 文件 // 利用 import 导入所需要的模块 import Vue from 'vue' import App from './App' import router from './router' Vue.config.productionTip = false // 关闭默认提示 // 创建 Vue 实例 new Vue({ el: '#app', router, render: h => h(App) })
// ./router/index.js 文件 // 配置路由相关的信息 import VueRouter from 'vue-router' import Vue from 'vue' import Home from '../components/Home' import About from '../components/About' // 通过 Vue.use(插件)安装插件 Vue.use(VueRouter) // 创建 VueRouter 对象 const routes = [ { path: '/home', component: Home },{ path: '/about', component: About } ] const router = new VueRouter({ // 配置路由和组件之间的应用关系 routes }) // 将 router 对象传入到 Vue 实例 export default router
-
网页的其他内容,比如顶部的标题/导航,或者底部的一些版权信息等会和 处于同一个等级。
-
在路由切换时,切换的是 挂载的组件,其他内容不会发生改变。
路由的默认路径
-
一个不太好实现的页面:
- 默认情况下,进入网站的首页,让 渲染首页的内容。
- 但是实际上,默认并没有显示首页组件,必须让用户点击才可以。
-
如何可以让路径默认跳到首页,并且渲染组件?
-
在 router 配置里面多一个映射就可以了:
const routes = [ { path: '/', redirect: '/home' } ]
-
配置解析:
- 在 router 中配置了新的映射。
- path 配置的是根路径 ‘/’ 。
- redirect 是重定向,也就是将根路径重定向到 /home 的路径下,就可以得到想要的结果了。
-
HTML5 的 History 模式
-
改变路径的方式:
- URL 的 hash。
- HTML5 的 history。
- 默认情况下,路径的改变使用的 URL 的 hash 。
-
使用 HTML5 的 history 模式:
// 创建 router 实例 const router = new VueRouter({ routes, mode: 'history' })
router-link 补充
-
<router-link>
的属性:-
to :可以跳转到对应的组件。
<router-link to="/home">首页</router-link> <router-link to="/about">关于</router-link>
-
tag :tag 可以指定 之后渲染什么组件。
<router-link tag='li'></router-link> // 在页面中会渲染一个 li 标签。 <router-link tag='div'></router-link> // 在页面中会渲染一个 div 标签。
-
replace :replace 不会留下 history 记录,所以指定 replace 的情况下,后退键返回不能返回到上一个页面中。
-
active-class:当 对应的路由匹配成功时,会自动给当前元素设置一个 router-link-active 的 class ,设置 linkActiveClass 可以修改默认的名称。
- 在进行高亮显示的导航菜单或者底部 tabbar 时,会使用到该类。
- 但是通常不会修改类的属性,会直接使用默认的 router-link-active 即可。
-
exact-active-class:
- 类似于 active-class ,只是在精确匹配下才会出现的 class 。
- 后面的嵌套路由再具体讲解。
-
通过代码修改 Vue-router
// App.vue 文件
<template>
<div id="app">
// <router-link to="/home">首页</router-link>
// <router-link to="/about">关于</router-link>
// <router-link> 最终会被渲染成为一个 a 标签。
<button @click="homeClick">首页</button>
<button @click="aboutClick">关于</button>
<router-view></router-view>
// <router-view> 该组件会根据当前的路径,动态渲染出不同的组件。
</div>
</template>
<script>
export default {
name: 'App',
methods: {
homeClick() {
this.$router.push('/home')
// 不希望有返回键也可以使用 replace 方法。
// this.$router.replace('/home')
},
aboutClick() {
this.$router.push('/about')
// this.$router.replace('/about')
}
}
}
</script>
<style>
</style>
动态路由
-
在某些情况下,一个页面的 path 路径可能是不确定的,比如用户页面的路径是:
- /user/aaa 或者 /user/bbb 。
- 除了有前面的 /user 之外,后面还跟上了用户的 ID 。
- 这种 path 和 component 的匹配关系,就是动态路由(也是路由传递数据的一种方式)。
{ path: '/user/:id', component: User }
// App.vue 文件 <div> <h2>{{$route.params.id}}</h2> // 这里的 $route.params.id 会渲染成123 </div> <router-link to="/user/123">用户</router-link>
-
总结
-
router:是在路 router 文件夹下面的 index.js 文件中创建的router 对象。
-
route:当前哪个路由处于活跃状态就是拿到的哪个路由。
-
params:参数。
-
路由的懒加载
- 官方解释:
- 当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。
- 如果能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。
- 它在说什么?
- 首先,路由中通常会定义很多不同的页面。
- 这个页面打包的时候一般情况下是放在一个 js 文件中。
- 但是,这么多页面放在一个 js 文件中,必然会造成这个页面非常大。
- 如果一次性从服务器请求下来这个页面,可能需要花费一定的时间,甚至用户的电脑还会出现短暂空白的情况。
- 使用路由的懒加载就可以避免这种情况。
- 路由懒加载做了什么?
- 路由懒加载的主要作用就是将路由对应的组件打包成一个个的 js 代码块。
- 只要在这个路由被访问到的时候,才加载对应的组件。
路由懒加载的效果
路由懒加载的方式
-
方式一:
// 结合 Vue 的异步组件和 Webpack 的代码分析。 const Home = resolve => { require.ensure(['../components/Home.vue'],() => {resolve(require('../components/Home.vue'))} )}
-
方式二:
// AMD 写法。 const About = resolve => require(['../components/About.vue'],resolve)
-
方式三:
// 在 ES6 中,有更简洁的写法来组织 Vue 异步组件和 Webpack 的代码分割。 const Home = () => import('../components/Home.vue')
认识嵌套路由
-
嵌套路由是一个很常见的功能。
- 比如在 home 页面中,希望通过 /home/news 和 /home/message 访问一些内容。
- 一个路径映射一个组件,访问这两个路径也会分别渲染两个组件。
-
路径和组件的关系:
-
实现嵌套路由有两个步骤:
-
创建对应的子组件,并且在路由映射中配置对应的子路由。
-
在组件内部使用 标签。
// 子路由配置 const routes = [ { path: '/home', component: Home, children: [ { // 嵌套的默认路径 path: '', redirect: 'message' }, { // 子路由中的 path 不用加“/” path: 'news' component: News }, { path: 'message' component: 'Message' } ] },{ path: '/about', component: About } ] // 在 home 组件中使用 <router-view> <router-link to="/home/news">新闻</router-link> <router-link to="/home/message">消息</router-link> <router-view></router-view>
-
获取参数
-
统一资源定位符(Uniform Resource Locator,常缩写为:URL),俗称网页地址(网址)。
协议类型:[服务器地址[:端口号]][/资源层级UNIX路径]文件名[?查询][#片段ID]
完整格式:
协议类型:[//[访问资源需要的凭证信息@][服务器地址[:端口号]][/资源层级UNIX路径]文件名[?查询][#片段ID]
-
获取参数通过 $router 对象获取的。
- 在使用了 vue-router 的应用中,路由对象会被注入到每个组件中,赋值为 this.$route ,并且当路由切换时,路由对象会被更新。
-
传递参数主要有两种类型:params 和 query (使用的方式都是采用 和 JavaScript 代码方式)。
-
params 的类型:
- 配置路由格式:/router/:id 。
- 传递的方式:在 path 后面跟上对应的值。
- 传递后形成的路径:/router/123 , /router/abc 。
- 用于传递一个参数。
-
query 的类型:
- 配置路由格式:/router ,也就是普通配置。
- 传递的方式:对象中使用 query 的 key 作为传递方式。
- 传递后形成的路径:/router?id=123 , /router?id=abc 。
- 用于传递大量参数。
-
通过query(查询)传递参数:
-
<router-link>
内部传递:<router-link to="{path:'/profile',query:{name:'why',age:18,height:1.88}}">档案</router-link>
-
<button>
内部传递:<button @click="profileClick">档案</button> // js 代码 methods:{ profileClick() { this.$router.push({ path: '/profile', query: { name: 'kobe', age: 19, height: 1.87 } }) } }
router 和 route 的区别
- 组件中的 $router 就是 router 对象。而 route 就是当前处于活跃的路由对象。
- 所有的组件都继承自 Vue 的原型。
- $router 为 VueRouter 实例,想要导航到不同 URL ,则使用 $router.push 方法。
- $router 为当前 router 跳转对象里面可以获取 name、path、query、params 等。
-
导航守卫
-
在一个 SPA 应用中,如何改变网页的标题呢?
-
网页标题是通过
显示的,但是 SPA 只有一个固定的 HTML ,切换不同的页面时,标题并不会改变。 -
但是可以通过 JavaScript 来修改
的内容。 window.document.title = '新的标题'
-
那么在 Vue 项目中,在哪里修改?什么时候修改比较合适呢?
// 首先在每个路由中加入一个 meta 。 const routes = [ { path: '/home', component: Home, meta: { title: '首页' }, children: [ { // 嵌套的默认路径 path: '', redirect: 'message' }, { // 子路由中的 path 不用加“/” path: 'news' component: News }, { path: 'message' component: 'Message' } ] },{ path: '/about', component: About, meta: { title: '关于' }, } ]
// 使用 beforeEach 方法改变每个页面的标题 // 前置钩子(hook) router.beforeEach((to,from,next) => { document.title = to.meta.title next() // next 是必须调用的 }) // 因为首页有路由的嵌套,所以会出现 undefined 的情况。解决办法就是使用 matched 中的 meta。 // document.title = to.matched[0].meta.title
-
-
普通的修改方式:
- 一般想到的修改标题的位置是每一个路由对应的组件
.vue
文件中。 - 通过 mounted 生命周期函数,执行对应的代码进行修改即可。
- 但是当页面比较多时,这种方式不容易维护(因为需要在多个页面执行类似的代码)。
- 一般想到的修改标题的位置是每一个路由对应的组件
-
使用导航守卫就是一个更好的办法。
什么是导航守卫呢?
- 导航守卫也可以成为路由守卫。
- vue-router 提供的导航守卫主要是用来监听路由的进入和离开的。
- vue-router 提供了 beforeEach 和 afterEach 的钩子函数,他们会在路由即将改变前和改变后触发。
导航守卫钩子函数的参数解析
- to:即将要进入的目标的路由守卫。
- from:当前导航守卫即将要离开的路由对象。
- next:调用该方法后,才能进入下一个钩子。
导航守卫补充
- 补充一:如果是后置钩子函数,也就是 afterEach ,不需要主动调用 next() 函数。
- 补充二:上面使用的导航守卫也被称之为全局守卫。还有两个特殊的导航守卫。
- 路由独享的守卫。
- 组件内的守卫。
keep-alive 遇见 vue-router
-
keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。
- 他有两个非常重要的属性:
- include:字符串或正则表达,只有匹配的组件会被缓存。
- exclude:字符串或正则表达式,任何匹配的组件都不会被缓存。
- 可以通过 create 声明周期函数来验证。
- 他有两个非常重要的属性:
-
router-view 也是一个组件,如果直接被包在 keep-alive 里面,所有的路径直接匹配到的视图组件都会被缓存。
-
在组件跳转之后,返回还是显示跳转钱的组件。
// 此时可以使用路由守卫中的组件守卫。 <template> <router-link to="/home">首页</router-link> <router-link to="/about">关于</router-link> <button @click="userClick">用户</button> <button @click="profileClick">档案</button> <keep-alive> <router-view></router-view> </keep-alive> </template> export default { name: 'home', data:{ path: '/home/news' }, activated(){ this.$router.push(this.path) }, beforeRouterLeave(to,from,next){ this.path = this.$router.path next() } }
-
-
activated 和 deactivated 这两个函数只有在该组件使用 保持了状态才会有效。
TabBar 实现思路
- 如果在下方有一个单独的 TabBar 组件,你如何分封装。
- 自定义 TabBar 组件,在 App 中使用。
- 让 TabBar 出于底部,并且设置相关的样式。
- TabBar 中显示的内容由外界决定。
- 定义插槽。
- flex 布局平分 TabBar 。
- 自定义 TabBarItem ,可以传出图片和文字。
- 定义 TabBarItem ,并且定义两个插:图片、文字。
- 给两个插槽外层包装 div ,用于设置样式。
- 填充插槽,实现底部 TabBar 的效果。
- 传入高亮图片。
- 定义另外一个插槽,插入 active-icon 的数据。
- 定义一个变量 isActive ,通过 v-show 来决定是否显示对应的 icon 。
- TabBarItem 绑定路由数据。
- 安装路由:npm install vue-router --save
- 完成 router/index.js 的内容,以及创建对应的组件。
- main.js 中注册 router。
- APP 中加入 组件。
- 点击 item 跳转到对应路由,并且动态决定 isActive 。
- 监听 item 的点击,通过 this.$router.replace() 替换路由路径。
- 通过 this.$route.path.indexOf(this.link) !== -1来判断是否是 active 。
- 动态计算 active 的样式。
- 封装新的计算属性:this.isActive ? {‘color’:'red}:{} 。
补充:
vertical-align: middle;
可以消除图片与 div 之间默认的 3px 间距。