A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

其实vuejs有一个叫做动态组件的东西,效果和路由有点相似,根据需求显示不同的组件,但是在实际使用上,很小气,就给人玩不爽,没办法像路由一样编程式导航(push,replace),也没有浏览器路径的语义引导,动态组件更适合小规模的局部tab,给大家秀一波用法
<template>    <div>        ul>li>tab1+tab2     @click=""  <!-- >>>此处改变下面data中的tab值就可以切换了 -->        <!-- 此处相当于router-link效果,虽然没有浏览器的url引导,但是可以自己写面包屑 -->        <component :is="tab"></component>  <!-- 这里就是router-view显示区了 -->    </div></template><script>import tab1 from 'path/to/tab1.vue' //相比vue-router要引入很多文件  麻烦import tab2 from 'path/to/tab2.vue'//其实vue-router也引入文件,但是在.vue文件里面引入那么多就很难看export default {    data(){        tab:'tab1' //默认显示tab1    },    components:{        tab1,        tab2   //es6语法   对象中,键值相同可以这样写    }}</script>复制代码我们不提动态组件了,开始router了。常用的api,官网介绍的很详细了。记得看官方教程,别忘了官方的api
路由的配置不管是什么路由的配置 还是vuex的配置等等,都有相应的规范,在普通js文件,不按规范也不会怎么样,在ts文件,就会报错了。前提是有兴趣玩ts。
//  router/index.js// 此处省略一堆 importconst router = new Router({    base:'/',    mode:'history',//default-->hash    routes:[],    scrollBehavior:()=>{        return {x:0,y:0}    }})复制代码其实配置很简单,就这么一点。
base浏览器url的前缀,默认为'/',如果设置为'/some/',则运行项目,浏览器url都是'/some/...',不会对静态文件的引用产生影响,一般写网站都会有域名,都可以把域名指向某个服务器目录,所以默认'/'即可,如果是写公司内部后台管理,那么不一定会用域名,可能就是某个ip下的某个目录,比如打包后的文件在111.22.33.44/admin,这个时候路由的配置匹配浏览器路径的时候,会从这个/admin开始算,如果base还是默认的/,那么路由配置的routes的path就要全部加上/admin/前缀,并且router-link和push方法也要加上这个/admin,很麻烦,但是只要设置base为'/admin/',路由内部配置以及所有相关的方法都可以忽视服务器ip下的目录名。这种情况,同样也要配置webpack的publicPath也为/admin/,这里不细说了。
mode这个太简单了,一共三种模式,
hash:浏览器会有‘#’符号,参考锚点效果,缺点很丑,但是兼容性棒棒
history:去除‘#’符号,让url变好看,下面会讲服务端配置。
abstract:非浏览器环境,会强制使用这个模式,例如weex
history模式服务端配置
我个人和公司都是用nginx,这里就讲nginx,配置这个的原因是当你进入某个路由之后,再次刷新页面时(或者是浏览器直接输入某个路由路径时),当刷新页面,浏览器就会重新dns解析,tcp协议,这个时候会根据浏览器的url去服务器找对应资源,当然我们vue-router是为单页面服务的,对应的url在服务端是肯定没有静态资源的,就会出现404,当配置了以下url重写语句,注意是重写,不是重定向,不改变url的情况重写浏览器内容,重写到index.html,因为这个index.html使我们项目的入口,index.html里面会读取当时打包好的app.js,就可以读取到路由配置,以实现我们浏览器的url对应的路由页面。
hash模式不需要配置,因为浏览器会忽略#和?后面的参数
打包文件在根目录时,
location / {   try_files $uri $uri/ /index.html;}复制代码打包文件在非根目录时,
location /admin {  try_files $uri $uri/ /admin/index.html;}复制代码routes核心路由配置,官网讲的很详细,我就讲几个注意点,所有自定义的配置,例如是否需要鉴权或者对应的icon等,需要规范化的话都写在meta对象中,不规范的话 ,就和path同级咯,随意玩,玩坏了我不背锅。
path是必须指定的,name需要唯一,不是必须。
routes的配置遵循顺序匹配,当url成功匹配,就不会再往下匹配,所以像403,404的页面应当写在最后。
alias别名的使用,当需要在指定router-view显示某个组件,并且希望浏览器url是自己想要的语义时使用
redirect重定向,参数可以是路径,也可以是对象(重定向到某个name),注意重定向是改变内容+改变url
scrollBehavior这个滚动行为,老实说,感觉很蹩脚,本人基本没有使用过,他控制的是body的滚动,很多需求都是局部滚动。如果的确是需要控制body滚动,参考官方文档即可。
路由的使用路由的配置一开始只有根目录/,每写一层children就要写一层router-view,否则组件不显示。每一个嵌套children的层级和router-view的层级都是一一对应的。
// router/index.jsconst router = new Router({    routes:[        {            path:'/',   //此时配置数组的第一层级,即对应app.vue中的router-view            component:Home,//   <<<----------------------------            children:[//                                       |                {   //                                         |                    //此时第一层级出现children第二层级,------------                    //那么在这个第二层级所属的第一层级 Home组件中,就要写一层router-view,以此类推                    path:'user',                     component:User,                }            ]        }    ]})复制代码路由配置生成menu菜单menu其实就是路由的导航,就是router-link,有很多小伙伴会另外建一个叫做menu.js的文件,然后模拟路由配置的结构去写一个数组,然后根据这个文件遍历生成dom,是不是有多此一举的感觉,并且当使用第三方的menu的ui组件时,这种静态文件是没有状态 (当前访问路由的对应menu高亮,刷新页面或者输入路由的url进入页面会丢失高亮) 的。
menu有很多种情况,
  • 固定层级,比如固定2层,或者3层,一般后台管理系统,固定层级较多,因为美观,整洁。这种就比较方便了。template的dom结构也比较方便。
  • “个性”的产品经理,“定制化”的menu菜单,未知的层级,这里就要用到render函数去写dom。
固定层级
<template>    <div class="menu">        <div class="first-menu" v-for="item,index in menus" :key="item.name">            <router-link :to="item.name">{{ item.meta.zhName }}</router-link>            <template v-if="item.children.length">                <div class="sub-menu" v-for="subItem.subIndex in item.children"                        :key="subItem.name">                    <img :src="subItem.meta.icon" />                    <router-link :to="subItem.name">                    {{subItem.meta.zhName}}                    </router-link>                </div>            </template>        </div>    </div></template><script>export default {    computed:{        menus(){            return this.$router.options.routes            //获取路由配置        }    }}</script><style>.router-link-active{    /* 激活样式 */}</style>复制代码这里我只写了2层,并且没有写样式,如果使用了像element-ui这样的menu组件,他底层没有渲染成router-link,那就要编程式导航了,click的时候,push到item.name,并且当前路由的导航样式需要根据$route.name(当前路由配置)来判断
未知层级
因为不知道是几层菜单,所以在template的html标签没办法自由的遍历,所以需要js去操作路由配置的递归,因为render函数式js操作,所以需要用render函数渲染。
<script>export default {    computed:{        menus(){            return this.$router.options.routes            //获取路由配置        }    },    // 使用render函数就不需要写template标签的dom了    render(h){         //这个 h 参数,原来的语义是createElement ,这里使用 h 是方便操作,也是官方建议        // ...... 这里是操作 menus 的递归代码,再通过render函数循环出来        return h('div','这是一个div')        //这里必须return 对应的语法    }}</script>复制代码
render函数的语法参见官方文档即可 render函数
这里有个注意点,如果router做了权限控制,那么路由配置不能通过$router.options.routes获取,因为addRoutes方法不改变原先的配置,需要在addRoutes的同时,把完整的路由配置保存在vuex,再获取。权限控制不细讲了,有很多相关文章,但是不建议copy,建议加入自己的理解,没有绝对的方案。
路由的缓存和过渡动画过渡动画应该都用了,一般使用opacity过渡一下。
<template>    <transition mode="out-in" name="fade">        <!-- 这里如果在行内直接用$route.meta去v-if判断缓存 dom结构会变复杂 -->        <keep-alive :include="aliveRoutes">            <router-view :key="$route.fullPath" />            <!-- 此处通过路由的完整路径加上key,当为动态路由时,触发路由组件重新渲染 -->        </keep-alive>    </transition>    <!-- 都是些官方用法 不细说了--></template><script>export default {    computed:{        aliveRoutes(){            // this.$router.options.routes  获取路由配置            //可以配置meta 是否缓存 根据 meta 字段  push到数组里            return [/* ... */]        }    }}</script>复制代码路由的钩子官网讲的超详细的。触发顺序也讲清楚了,官方的说法叫导航守卫
注意点:要搞清楚哪些是全局钩子,哪些是组件内的钩子。并且所有的前置钩子,需要调用next() 才会正常进入路由,在写钩子函数内部逻辑时,需要注意,不能形成死循环
例如
router.beforeEach((to,from,next)=>{    if (from.name === 'login'){        next({            name:'login'  //死循环        })        //更多next回调方法可以查看官方文档    }})复制代码组件内钩子使用情况,要看业务需求,比如动态路由↓↓↓。
动态路由动态路由,使用场景是一个固定的路由视图组件,配合一个动态的浏览器url,在视图中显示不同的数据,同时希望url中有当前详情的语义,例如详情页,用户信息页。
先看下路由如何配置动态,这里用详情页举例
// router/index.jsconst router = new Router({    routes:[        {            path:'/list',            name:'list',            component:List        },        {            path:'/detail/:id',//此处id对应params.id            name:'detail',            component:Detail        }    ]})复制代码<!-- List.vue --><template>    <div>        <ul>            <li @click="goDetail(item.id)" v-for="item in someData" :key="item.id">            </li>        </ul>    </div></template><script>export default{    methods:{        goDetail(_id){            this.$router.push({                name:'detail',                //此处注意只有通过name跳转路由,params才会生效                //query不受影响                params:{                    id:_id  //此处对应routes配置的/:id                }            })        }    }}</script>复制代码<!-- Detail.vue --><script>export default {    beforeRouteEnter (to, from, next) {        //此处为路由组件内的钩子,可以通过判断 to.params.id 是否 undefined,        //进行一些操作,因为即使 to.params.id 是 undefined,路由也能匹配成功        //因为这些id可能是用来获取数据的,不会在页面显示,不一定能察觉是 undefined    }}</script>复制代码关于路由传参
  • params就如上面动态路由所讲,只有通过路由配置的name跳转,才会生效,即使没有使用动态路由,也可以通过params传参
这里有注意点:
  • 如果使用动态路由,那么在动态路由页面刷新路由,params参数依然存在,
  • 如果没有使用动态路由,params参数只在跳转路由时有效,刷新页面则会丢失params

  • query参数,可以通过path或name跳转都可以传参,并且参数会和ajax的get请求一样,附加到浏览器的url上,刷新页面依然保留
路由的懒加载路由组件打包单独的js,当访问路由时,加载对应js文件,加快首页加载时间,会增加项目的总体积。
  • npm或yarn安装babel-plugin-syntax-dynamic-import
  • .babelrc或babel.config.js配置
module.exports = {    plugins:["syntax-dynamic-import"]}复制代码
  • 路由文件使用
// router/index.jsconst router = new Router({    routes:[        {            path:'/list',            name:'list',            component:()=>import(/* webpackChunkName:"SOME_NAME" */'path/to/List.vue')            //此处 import 是方法  和 es6 的 import 不一样            //特殊注释语法 相同chunkName 打包到一个js中  可省略        }    ]})复制代码路由的命名视图我从来没用到过命名视图,不给大家误导了,我能解释的就是命名视图的作用是一个浏览器url,匹配多个router-view。有需求的移步官方文档。
部分api
  • .vue文件中this.$router是指挂载的路由实例,可以使用push等方法。
  • .vue文件中this.$route当前路由信息对象,包括path,query等,只读属性。
  • router.app指router所挂载的vue实例,可以通过router.app.$store访问vuex,前提是main.js中,要先引入vuex,后引入router,否则报错。
  • router.push()常用路由跳转方法,参数为{path?:string,name?:string,params?:object,query?:object},需要注意的是,push为方法名,与数组没有联系。
  • router.replace()替换当前路由,参数和push一致,区别是替换当前路由,即点击浏览器的返回,不会回到之前被替换的路由。
  • router.go()和router.back(),参数为整数,前进或后退几步。
  • router.addRoutes()参数是需要符合routes配置的数据,动态添加路由配置到原有配置。



作者:chavesgu
链接:https://juejin.im/post/5b5a7780f265da0f455a10d3



3 个回复

倒序浏览
回复 使用道具 举报
奈斯,很赞
回复 使用道具 举报
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马