项目实例:https://github.com/bailicangdu/vue2-elm
对于普通的网站,所有的超链接都是URL地址,所有的URL地址都对应服务器上对应的资源;
对于单页面应用程序来说,主要通过URL中的hash(#号)来实现不同页面之间的切换,同时,hash有一个特定:HTTP请求中不会包含hash相关的内容;所以,单页面程序中的页面跳转主要用hash实现;
在单页面应用程序中,这种通过hash改变来切换页面的方式,称作前端路由(区别于后端路由);
2.创建VueRouter对象,该对象的构造函数参数里routes表示一组路由,path为正则表达式这里面写的是hash之后的路径,component属性指定的即为组件模板对象,注意是组件的模板对象,并不是组件(组件名称)。
3.使用vue-router提供的占位符,表示如果路由匹配到了某一个组件模板的path,就将该组件模板渲染到此处。
<title>Document</title> <script src="../lib/vue-2.4.0.js"></script> <!-- 依赖于vue --> <script src="../lib/vue-router-3.0.1.js"></script> </head> <body> <div id="app"> <a href="#/login">登陆</a> <a href="#/register">注册</a> <!-- 3.使用vue-router提供的占位符,表示如果路由匹配到了某一个组件 模板的path,就将该组件模板渲染到此处 --> <router-view></router-view> </div> <script> // 1.创建组件模板对象 var login = { template:"<h1>登陆模块</h1>" } var register = { template:"<h1>注册模块</h1>" } // 只要创建了VueRouter实例,在浏览器地址里里就会出现hash,即#/ // 2.创建VueRouter对象,该对象的构造函数参数里 // routes表示一组路由,path为正则表达式这里面写的是hash之后的路径,component属性指定的 // 即为组件模板对象,注意是组件的模板对象,并不是组件(组件名称) var routerobj = new VueRouter({ routes:[ {path:'/login',component:login}, {path:'/register',component:register} ] }) var vm = new Vue({ el: '#app', data: { name:'123' }, methods: { }, // 4.将路由对象绑定给vue实例 router:routerobj }) </script>
<div id="app">
<!-- <a href="#/login">登陆</a>
<a href="#/register">注册</a> -->
<!-- vue-router不推荐使用a链接,因为使用a链接每次还都要使用#,vue-router提供了router-link来替代a链接 -->
<router-link to="/login" tag="span" class="loginspan">登陆</router-link>
<router-link to="/register">注册</router-link>
<!-- 3.使用vue-router提供的占位符,表示如果路由匹配到了某一个组件
模板的path,就将该组件模板渲染到此处 -->
<router-view></router-view>
</div>
var routerobj = new VueRouter({ routes:[ // 如果一进入首页就默认给他login页面该怎么做? // 虽然能实现但不推荐,因为这样/和/login就表示了同一个组件在页面时看着很别扭 // {path:'/',component:login}, // 还有一种方式我们使用redirect {path:'/',redirect:'/login'}, {path:'/login',component:login}, {path:'/register',component:register} ] })
1.直接使用默认的active类名:router-link-active。
2.在创建VurRouter实例的时候,设置linkActiveClass属性,指定自己设置的active类名。
<style> /* 高亮设置方式1:直接使用vue-router提供了类名router-link-active设置样式 */ /* 高亮设置方式2:在定义VueRouter实例时,使用linkActiveClass设置active类名 */ .router-link-active,.myActive { font-style: italic; font-weight: 700; font-size: 70px; color: red; } </style> var routerobj = new VueRouter({ routes:[ // 还有一种方式我们使用redirect {path:'/',redirect:'/login'}, {path:'/login',component:login}, {path:'/register',component:register} ], linkActiveClass:'myActive' })
使用query传递参数时直接将参数以?方式开头追加到链接末尾,而且不需要修改路由正则匹配规则。直接通过组件自身的$route属性即可拿到参数
<!-- 路由传参 --> <!-- 如果在路由中,使用 查询字符串 ,给路由传递参数,则不需要修改 路由规则的path属性 --> <router-link to="/login?id=123&name=毛毛">登陆</router-link> <router-link to="/register">注册</router-link> <script> // 2.定义组件模板 var login = { // 既然在生命周期created钩子里能拿到,说明data和methods都已实例化完成,也能拿到$route对象里的相关内容 template: "<h1>登陆---{{ $route.query.name }}</h1>", created() { // 可以直接在组件的生命周期钩子里获取:$route对象 console.log(this.$route.query); } } // 3.创建路由实例 var router = new VueRouter({ routes: [ { path: '/login', component: login }, { path: '/register', component: register }, ] });
使用params传递参数,需要修改路由正则匹配规则,要使用参数占位符的形式
<!-- 路由传参 --> <!-- 除了使用query传参的方式外,还可以使用params方式传参 --> <router-link to="/login/12/毛毛">登陆</router-link> <router-link to="/register">注册</router-link> <!-- 5.留坑 --> <router-view></router-view> </div> <script> // 2.定义组件模板 var login = { // 既然在生命周期created钩子里能拿到,说明data和methods都已实例化完成,也能拿到$route对象里的相关内容 template: "<h1>登陆---{{ $route.params.id }}---{{ $route.params.name }}</h1>" } var router = new VueRouter({ routes: [ // 注意如果是使用params方式传递参数,需要指明参数占位符 { path: '/login/:id/:name', component: login }, { path: '/register', component: register }, ] });
<script src="../lib/vue-2.4.0.js"></script> <script src="../lib/vue-router-3.0.1.js"></script> </head> <body> <div id="app"> <router-link to="/account">account</router-link> <!-- 这里留的时account的坑 --> <router-view></router-view> </div> <template id="account"> <div> <h1>Account ABC</h1> <router-link to="/account/login">登陆</router-link> <router-link to="/account/register">注册</router-link> <!-- 这里留的时login和register的坑 --> <router-view></router-view> </div> </template> <script> var account = { template:'#account' } var login = { template:'<h3>登陆</h3>' } var register = { template:'<h3>注册</h3>' } var router = new VueRouter({ routes:[ { path:'/account', component:account, // 使用children属性指定其下的嵌套路由,注意嵌套路由里的path属性不要以/开头 // 如果以/开头的话,依旧是以根路径来直接匹配的即:#/login而不是#/account/login // 虽然也能达到相同的效果,但是地址栏里的地址很别扭,因为login本来是account的子 // 路由里的,但是却没带上/account children:[ {path:'login',component:login}, {path:'register',component:register}, ] }, // 注意:按理说login和register是属于account的,但是我们的path却以/开头, // 以/开头表示以根路径开头去访问一个新的地址,虽然我们下面地址也是以/account开头 // 的,但是vue并不是这样认为的,vue眼中你的login和account是平级的,所以即使你 // path里指定的是login属于account但是真正解析时还是把login当作一个新的不属于account // 的地址来解析。vue-router提供了路由嵌套来解决这个问题。 // {path:'/account/login',component:login}, // {path:'/account/register',component:register}, ] }); var vm = new Vue({ el: '#app', data: { msg: '欢迎学习Vue' }, methods: { }, router, }) </script>
除了使用<router-link>创建一个标签来定义导航链接,我们还可以借助router的实例方法,通过编写代码来实现。
this.$router.push(location, onComplete?, onAbort?)
想要导航到不同的URL,则使用router.push方法。这个方法会向history栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,则回到之前的URL。
你当点击<router-link>时,这个方法会在内部调用,所以说,点击<router-link :to="...">等同于调用router.push(...)。
// literal string path router.push('home') // object router.push({ path: 'home' }) // named route:你需要在router.js中的routes数组里的某一项指定name属性、 // 例如: // routes: [{ path: "/home/shopping", component: shopping, name: user }], router.push({ name: 'user', params: { userId: '123' } }) // with query, resulting in /register?plan=private router.push({ path: 'register', query: { plan: 'private' } })
注意:如果提供了path,params会被忽略,例子上述的中query并不属于这种情况取而代之的的英文下面例子的做法,你需要提供路由的。name或手写完整的带有参数的path:
const userId = '123' router.push({ name: 'user', params: { userId }}) // -> /user/123 router.push({ path: `/user/${userId}` }) // -> /user/123 // 这里的 params 不生效 router.push({ path: '/user', params: { userId }}) // -> /user
这个方法的参数是一个整数,意思是在历史记录中向前或者后退多少步,类似window.history.go(n)。
// 在浏览器记录中前进一步,等同于 history.forward() router.go(1) // 后退一步记录,等同于 history.back() router.go(-1) // 前进 3 步记录 router.go(3) // 如果 history 记录不够用,那就默默地失败呗 router.go(-100) router.go(100)
有时你需要同时显示多个视图,而不是嵌套,例如创建一个布局 侧边栏 视图和一个 主要 视图。 这就是命名视图派上用场。 而不是一个单一的出口在你的视图中,可以有多个,给他们每个人一个名字。 一个 router-view 将没有一个名字 默认的 正如它的名字。
<script src="../lib/vue-2.4.0.js"></script> <script src="../lib/vue-router-3.0.1.js"></script> <style> html,body,h1 { margin: 0; padding: 0; } .header { background-color: orange; height: 80px; } .container { /* 使用css3实现水平布局 */ display: flex; height: 600px; } .left { background-color: gold; flex: 1; } .main { background-color: green; flex: 9; } </style> </head> <body> <div id="app"> <!-- <router-view></router-view> <router-view></router-view> <router-view></router-view> --> <router-view></router-view> <div class="container"> <router-view name="left"></router-view> <router-view name="main"></router-view> </div> </div> <script> var header = { template: '<h1 class="header">头部</h1>' } var leftBox = { template: '<h1 class="left">左边区域</h1>' } var main = { template: '<h1 class="main">main主体</h1>' } var router = new VueRouter({ routes: [ // {path:'/',component:header}, // {path:'/left',component:leftBox}, // {path:'/main',component:main}, { path: '/', components: { default: header, left: leftBox, main: main } } ] }); var vm = new Vue({ el: '#app', router }) </script>
watch常用来监听一非DOM元素的事件,只要是vue实例中的属性或对象发生变化都可以被watch。
<script src="../lib/vue-2.4.0.js"></script> <script src="../lib/vue-router-3.0.1.js"></script> </head> <body> <div id="app"> <router-link to="/login">登陆</router-link> <router-link to="/register">注册</router-link> <router-view /> </div> <script> var login = { template: "<h1>登陆</h1>" } var register = { template: "<h1>注册</h1>" } var router = new VueRouter({ routes: [ { path: "/", redirect:"/login" }, { path: "/login", component: login }, { path: "/register", component: register }, ] }); var vm = new Vue({ el: '#app', data: { msg: '欢迎学习Vue' }, methods: { }, router, watch:{ // 如果要想监听到路由的变化,使用keyup事件以及model等形式是不行的,因为你根本不知道该 // 把这些东西加给谁,所以只有使用watch,watch常用来监听一些非DOM元素的事件 // 只要是vue实例中的属性或对象发生变化都可以被watch '$route.path':function(newVal,oldVal){ // this.$route是实例上的属性,可以省略this console.log(newVal,oldVal); } } }) </script>
计算属性,该属性声明的变量(虽然使用方法表示,但是在vue中它被当作变量),是用来表示那些常用的改变的量。在computed中,可以定义一些属性,这些属性,叫做【计算属性】,计算属性的本质就是一个方法,只不过,我们在使用这些计算属性的时候,是把它们的名称,直接当作属性来使用的;并不会把计算属性,当作方法去调用;
<script src="../lib/vue-2.4.0.js"></script> </head> <body> <div id="app"> <input type="text" v-model="firstname">+ <input type="text" v-model="middlename">+ <input type="text" v-model="lastname">= <input type="text" v-model="fullname"> <p>{{ fullname }}</p> <p>{{ fullname }}</p> <p>{{ fullname }}</p> </div> <script> var vm = new Vue({ el: '#app', data: { firstname: '', middlename: '', lastname: '' }, methods: {}, computed: { // 在computed中,可以定义一些属性,这些属性,叫做【计算属性】,计算属性的本质就是 // 一个方法,只不过,我们在使用这些计算属性的时候,是把它们的名称,直接当作属性来 // 使用的;并不会把计算属性,当作方法去调用; // 注意1:计算属性,在引用的时候,一定不要加() 去调用,直接把它当作普通属性去使用就好了。 // 注意2:只要 计算属性,这个function内部,所用到的任何data中的数据发送了变化,就会 // 立即重新计算 这个 计算属性的值 // 注意3:计算属性的求值结果,会被缓存起来,方便下次直接调用;如果 计算属性方法中, // 所有引用的数据都没有发生过变化,则,不会重新对 计算属性求值; 'fullname': function () { console.log('ok'); return this.firstname + this.lastname; } } }) </script>
1、computed 属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算,主要当作属性来使用。
2、methods方法表示一个具体的操作,主要书写业务逻辑。
3、watch 一个对象,键是需要观察的表达式,值是对应回调函数,主要用来监听某些特定数据的变化,从而进行某些具体的业务逻辑操作;可以看作是computed和methods的结合体;
webpack
即可,它会在根目录下生成一个dist目录,该目录下即为打包好的文件。你可以直接将其丢到服务器上运行。
.v-enter:这是组件执行动画开始时的位置,一般设定为:
.v-enter { opacity:0; transform:transformX(100%); // 即:在该组件出现的位置是在屏幕最右侧【隐藏】 }
.v-leave-to:这是组件执行完动画后的位置,一般设定为:
.v-leave-to { opacity:0; transform:transformX(-100%); // 即:该组件结束位置在屏幕最左侧【隐藏】 position: absolute; // 不占位置,就不会出现先出后进【进去的占用位置,离开的不占位置(absolute)】 }
二者结合在一起就表示,从屏幕的最右边(看不见的位置)移动到屏幕的最左边。
.v-enter-active, .v-leave-active { transition: all 0.3s ease; }
除此之外,你还要设置执行动画元素的父元素overflow-x:hidden,如果你不设置,header区域也会跟着动画执行而晃动,这是因为,当开始执行动画的时候,下一个动画切片已经在右侧等待了,但是我们的起始位置并没有设置position:absolute,所以就会占有位置,这是header区域没有设置宽度,页面被撑宽,header区域也自然就晃动了。
在mui中组件(文字太长被hidden后)切换动画出现“滚动条”
在使用mui时,在切换子组件时,我遇到这样一个问题,我是在上面一个问题已经解决时出现了这个问题,这个问题,是由于我p元素内的内容过长,在mui中
<p class="mui-ellipsis">能和心爱的人一起睡觉,是件幸福的事情;可是,打呼噜怎么办?</p> .mui-table-view-cell p { margin-bottom: 0; } .mui-ellipsis { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
可以看到即使设置了overflow等等都没用,后来发现是由于p元素的内容超出,在切换组件执行动画时,不知道怎么回事,又出现了header区域轻微晃动【研究后发现晃动的幅度大概也就是超出的文本的宽度】,其他组件切换都没问题,就这块组件切换有这个现象,我尝试把文字删短到一两个字就没这个问题了,我又尝试在其父元素上设定一个小一点的宽度也没不出现这个问题了。那么解决方案有了,文字是不可能删了,只有设定一个具体的宽度才行了,由于是自适应的宽度,所以我大概测了一下:
console.log(this.$refs.ulref.offsetWidth); console.log(this.$refs.ulref.clientWidth); console.log(this.$refs.ulref.style.width); console.log(this.$refs.ulref.scrollWidth); //console.log(document.getElementsByTagName("body")[0].offsetWidth);
如果我单独切换到这个有问题的子组件然后刷新页面,你会发现打印出来的结果和你从首页切换到该子组件的结果不一致,从首页切过来的这一批值偏大,直接刷的值刚好,我又测了一下首页body的宽度值,发现首页body的宽度值和直接刷子组件的宽度一致,那简单了,直接这样干:
// 1.ul打ref属性 <ul class="mui-table-view" ref="ulref"> // 2.mounted挂载后设置当前宽度为body宽度 mounted() { console.log(this.$refs.ulref.offsetWidth); console.log(this.$refs.ulref.clientWidth); console.log(this.$refs.ulref.style.width); console.log(this.$refs.ulref.scrollWidth); console.log(document.getElementsByTagName("body")[0].offsetWidth); this.$refs.ulref.style.width = document.getElementsByTagName("body")[0].offsetWidth + "px"; },
<div id="app"> <ul> <li v-for="(todo, index) in todos" v-on:click="addClass(index)" v-bind:class="{ blue:index==current}">{{ todo.text }}</li> </ul> </div> <script> new Vue({ el: '#app', data: { current:0, todos: [ { text: '选项一' }, { text: '选项二' }, { text: '选项三' } ] }, methods:{ addClass:function(index){ this.current=index; } } })