前端路由(vue-router/react-router-dom)
单页面应用& 多页面应用
「SPA(single page application)单页面应用」:一个项目只有一个页面,我们基于路由,控制页面展示不同的片段(或组件),当展示内容改变的时候,页面并不会刷新!!
「MPA(multi page application)多页面应用」:一个项目有很多页面,我们做的是页面之间的跳转,每一次跳转都是打开新的页面(相当于页面刷新了)。
单页面应用的项目:移动端大部分都是(追求原生app的操作 体验)、PC端的管理系统 多页面应用的项目:PC端非管理系统的产品。
基于JS动态绑定的内容(vue框架都是这样处理的),在页面的源代码中是看不到内容的,不利于SEO搜索引擎优化!!想做SEO优化,需要服务器渲染(前后端不分离、「现在主流的服务器渲染SSR:node+vue(nuxt.js)+react(next.js)」)!!
首屏服务器渲染【骨架屏】
其余屏幕还是交给客户端渲染
前端路由的模式
「哈希路由(hash router):监听URL地址后面的hash值」
○ 点击按钮改变url地址栏中的hash值改变,控制#app中的内容改变
「浏览器路由(brower/history router):基于H5中的history api实现的」
○ 可以改变URl地址,但是页面不刷新;
○ 如果手动刷新页面,这个地址不存在,报404;需要服务器支持,如果地址不存在,不要返回404,依然返回主页面。
「区别:」
○ hash路由地址不好看,brower路由好看一些;
○ 实现机制不同
❝hash路由每次跳转修改的是URL的hash值(页面不刷新),基于监听onhashchange事件,获取最新的hash值,去路由表中,找到对应的组件拿过来渲染;
brower路由每次跳转,修改的是URL地址,(页面不刷新),基于history.pushState事件,根据地址找到对应的组件。
❞
○ brower跳转的路由并不真实存在,所以页面手动刷新,会出现404,此时需要后台服务器的配合【后台:如果地址不存在,返回主页的内容】
vue脚手架基于webpack-dev-server启动服务(开发环境),已经完成了服务器应该对history模式支持的相关操作;但是打包到服务器上(生产环境),没有webpack-dev-server,此时需要服务器基于nginx做相同的配置支持。
vue路由
安装
npm i vue-router
步骤
「创建路由规则表」 src/router/index.js
import Vue from "vue";
import VueRouter from "vue-router";
import routes from "./routes"
Vue.use(VueRouter);
const router = new VueRouter({
// 设置路由模式:hash、history
mode: "hash",
// 设置路由的匹配规则
routes
})
export default router;
src/router/routes.js 管理项目路由表
// 管理项目路由表
import Home from "@/pages/Home.vue";
import Analyse from "@/pages/Analyse.vue";
import Order from "@/pages/Order.vue";
const routes = [{
path: "/",//HASH值(pathname值)
component: Home//渲染的组件
}, {
path: "/analyse",
component: Analyse
}, {
path: "/order",
component: Order
}, {//以上都不是,重定向到首页
path: "*",
redirect: "/"
}];
export default routes
main.js注册使用路由表
import router from '@/router';
new Vue({
router,//=>this.$router & this.$route
render: h => h(App),
}).$mount('#app');
实现路由跳转(改变URL地址或者哈希值)
设定一个容器,可以在指定的位置,把基于路由规则匹配的组件进行渲染
「router-view」
router-view内置组件(路由容器):渲染"基于路由规则匹配的组件"的
当页面刷新或者路由跳转后,vue都会拿URL最新的地址(或hash值)去路由表匹配,把匹配到的组件,放到router-view容器中渲染!
每一次的路由切换都是把上一个渲染的组件释放(销毁beforeDestroy->destroyed),把新匹配的组件进行渲染(beforCreate->created->beforeMonut->mounted)
「router-link to="/"」
router-link内置组件(路由跳转、切换组件)
目的:点击实现路由跳转,基于"to"属性指定跳转地址
页面渲染的时候,会把router-link渲染为A标签
页面刷新或路由切换,都会拿最新的地址(或哈希值)和每一个router-link中to的值(或者path属性值)进行匹配,完全匹配""精准"匹配,会给A标签设置router-link-exact-active router-link-active类;非""精准"匹配,只设置router-link-active类;一点都没匹配,啥样式都不设置。==>我们后期可以基于这个特点,给当前匹配的导航设置选中样式。
页面地址:/order
to的地址:
/ 非精准匹配(包含一个完整的)
■ / 任何地址都包含一个完整的斜杠
■ /home2 VS /home 不算
■ /home/list VS /home 算
/analyse 完全不匹配
/order 精准匹配{一毛一样}
<router-link to="/">首頁</router-link>
<router-link to="/analyse">分析頁</router-link>
<router-link :to="{path:'/order'}">訂單頁</router-link>
vue性能优化方案
路由懒加载
如果在编写路由表的时候,事先导入了所有的组件,根据规则渲染不同的组件,这样最后build的时候,会把所以导入的组件打包到一个JS中,JS文件较大,页面第一次加载请求JS的时间过长,延长了白屏等待的时间。
路由懒加载,依托于ES6中提供的import函数
webpack打包的时候,实现代码切割:在路由表中已经导入的组件(一般只导入默认展示的)打包到JS中,其余的组件根据情况,分隔成为一个 或多个JS文件 最开始加载页面的时候,只把主JS文件导入,当路由匹配了某个规则,需要渲染某个组件,再把这个组件所在的JS文件导入进来
route router
当我们在new Vue({})的时候,会给每个组件(vue 实例)注入两个私有属性
● this.$route:存储路由匹配的信息
● this.$router:VueRouter的实例,可以调用
VueRouter.prototype上的方法实现路由跳转
● $route
❝fullPath:完整的路由地址(含问号参数信息)
path:不含问号传参信息
query:以键值对的方式存储了问号传参信息
params:{} 存储的是“路径传参”信息或者“隐式传参”信息
name:路由名字
meta:{} 存储设置的路由元的信息
meached:[...] 记录了路由具体的匹配规则记录
hash: 存储除哈希路由内容外的内容,单独设置的hash值
❞
● $router
❝私有属性方法
■ mode路由模式
公有属性方法
■ push 跳转到指定的路由 ● --> this.$router.push("/analyse")等同于 router-link to="/analyse" ● -->this.$router.push({path:'/analyse',...}) ■ replace 也是跳转到指定的路由,区别在于,push是新增一条历史记录,而replace 是修改本条记录
■ go(n): 以当前历史记录为标准,前进或后退,基于N指定步数 go(-1)后退一步 go(1)前进一步
■ back -->go(-1)
■ forward-->go(1)
■ addRoute addRoutes 动态向路由表中加入新的规则记录(动态路由)
■ getRoutes 获取现有的路由表
❞
路由切换/路由跳转的传参方式
路由切换/路由跳转的传参方式:每一次路由切换,想把一些信息传递给下一个组件 A组件跳转到B组件
问号传参
#/xxx?xxx==xxx 【丑&能在地址栏中看到传递的信息】因为在地址栏中有,即使手动刷新B页面,信息还可以获取到
隐式传参
● #/xxx 【地址栏中看不见,从内部把信息传递过去&&需要基于路由名字跳转,基于path跳转不可以】建议大家后期在编写路由表的时候,给每一个路由设置一个唯一的名字-->"命名路由",后期实现路由跳转可以不基于path,基于name也可以跳转(可以隐式传参)
因为传递的信息在地址栏中不存在,所以在B页面刷新,传递的信息都没有了
路径参数
● #/xxx/数据 把需要传递的数据当作路由地址的一部分【常用的方案】 ○ 第一步:设置路由表 :xxx设置动态的规则
○ 第二步:跳转 this.$router.push('/home/customass/100/aa')
○ 第三步:获取 this.$router.params --->{id:100,name:'aa'}
特殊情况:跳转前后是同一个组件
路由切换的时候,要释放上一个组件和即将渲染的下一个组件,是同一个组件,此时这个组件既不会被释放也不会被重新渲染,第一次渲染的逻辑不会触发了,例如:created就不会执行了
● 解决方法1:基于watch监听路由变化,从而做一些事情
watch: {
$route: {
hadler() {
...
},
immediate: true,//重新渲染组件(第一次渲染逻辑),让监听器的处理也执行
}
}
● 解决方法2:computed 把需要依赖的路由由变化而变化的信息,都设置为计算属性
computed: {
title() {
let { id } = this.$route.params;
return id ? "修改客户" : "新增客户";
},
}
路由导航守卫
在每一次页面刷新(或者路由跳转),路由进行规则匹配的时候,也会触发一些钩子函数==>导航守卫函数
「完整的导航解析流程 A组件-->B组件」
释放A组件,触发A组件的beforeRouteLeave 钩子函数。 a. A组件释放,即将开启B组件的路由解析和渲染。 触发全局前置守卫函数 beforeEach 守卫【写在创建路由出】。 如果A和B是相同的组件,则会触发组件里 beforeRouteUpdate 守卫 (2.2+)。 再触发匹配的路由配置的独享守卫 beforeEnter【写在路由表中】。 a. B组件被激活,开始进入到B组件中进行解析。 触发B组件的 beforeRouteEnter函数。 触发全局解析守卫函数 beforeResolve 守卫 (2.5+)。 调用全局后置守卫函数 afterEach 钩子。 真实项目中,我们最常用的是 "全局前置守卫 beforeEach":每一次页面刷新(或路由跳转)、也不论从哪跳到哪,beforeEach一定会触发执行。
一般会在 "全局前置守卫 beforeEach"中 ,做登陆态的校验【只要不是进入登陆页,都需要校验是否登陆,如果登陆了,正常进行后面的步骤;如果没登陆,直接让其返回登录页!】
路由元信息
meta:{} 路由元信息:记录每一个路由表的一些基础信息【自定义】
● this.$route.mate 获取
● 根据每个路由表中的记录的不同的元信息,做不同的事情