【珍惜时间】vuepro

老规矩放上大大的github开源地址:https://github.com/goodheart222/vuepro
我们再来看看项目的效果,初步根据效果做到心中有数

看到效果的话,我们会发现,肯定是有路由切换,懒加载,后端交换数据这些
一起分析代码
main.js入口文件,引用了vue-lazyload懒加载的方式加载图片

//main.js
import Vue from 'vue'
import App from './App'
import router from './router'
//引入图片懒加载模块
import VueLazyload from 'vue-lazyload'
//懒加载配置
Vue.use(VueLazyload, {
  preLoad: 1.3,
  error: require('../static/images/mo.jpg'),
  loading: require('../static/images/minloading.gif'),
  attempt: 1
})

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  render: h => h(App)
})

App.vue中的代码写的很高级啊,就是你会想象不到,居然可以这样写出来,作者大大功力很深啊
下面的tab切换是直接写在router-link中的,还有切换中颜色的改变,很有意思啊

//app.vue
<template>
  <div id="app">
  	<nav class="logo">
  		<img src="../static/images/cnodejs_light.svg"/>
  		<div class="input">
  			<span class="iconfont icon-gouwuche"></span>
  			<input type="text" />
  		</div>
  	</nav>
	  <!-- tab切换部分写在了App.vue中,有意思 而且还添加了tag有意思-->
    <nav class="bottom">
    	<ul>
    		<router-link to="/index" tag="li" :class="res=='index'?'selected':''">首页</router-link>
    		<router-link to="/me" tag="li" :class="res=='me'?'selected':''">关于我们</router-link>
    		<router-link to="/login" tag="li" :class="res=='login'?'selected':''">登录</router-link>
    	</ul>
    </nav>
    <router-view/>
    <!--{{res}}-->
  </div>
</template>

<script>
	export default {
	  name: 'App',
	  computed:{
	  	res:function(){
	  		var arr = this.$route.path.split('/');
	  		return arr[1];
	  	}
	  },
	  mounted:function(){
	  	var div1=document.getElementsByClassName('input')[0];
			var input1=document.getElementsByTagName('input')[0];
			input1.onfocus=function(){
				this.style.background='#fff';
				div1.style.background='#fff';
			}
			input1.onblur=function(){
				this.style.background='#888';
				div1.style.background='#888';
			}
		}
	}
</script>

<style lang="scss">
	@import './assets/sass/base.scss';
	nav.logo{
		rem(730px);
		height:rem(160px);
		background: #444;
		margin:0 auto;
		padding-top:rem(10px);
		img{
			display: block;
			rem(243px);
			margin:0 auto;
		}
		div.input{
			rem(470px);
			height:rem(54px);
			border-radius: rem(30px);
			background: #888;
			margin:rem(20px) auto 0;
			display: flex;
			align-items: center;
			padding-left:rem(15px);
			span{
				font-size: rem(40px);
				font-weight: bold;
				color:#5c5c5c;
			}
			input{
				background: transparent;
				height:98%;
				border-radius: 0 rem(10px) rem(10px) 0;
				border:none;
				font-size: rem(34px);
				
			}
		}
	}
	nav.bottom{
		100%;
		height:rem(80px);
		background: #8cba48;
		position: fixed;
		bottom: 0;
		left:0;
		ul{
			100%;
			height:rem(80px);
			display: flex;
			li{
				font-size: rem(38px);
				33.33%;
				height:100%;
				line-height: rem(80px);
				text-align: center;
				color:#fff;
			}
			.selected{
				background: #009606;
			}
		}
	}
</style>
//src
outerindex.js
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
import Index from '@/components/Index'
import Me from '@/components/Me'
import Login from '@/components/Login'
//主题组件
import All from '@/components/topics/All'
import Ask from '@/components/topics/Ask'
import Share from '@/components/topics/Share'
import Job from '@/components/topics/Job'
import Good from '@/components/topics/Good'
import Test from '@/components/topics/Test'
import Detail from '@/components/topics/Detail'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/index',
      name: 'Index',
      component: Index,
      children:[
      	{
      		path:'/',
      		component:All
      	},
      	{
      		path:'good',
      		component:Good
      	},
      	{
      		path:'ask',
      		component:Ask
      	},
      	{
      		path:'share',
      		component:Share
      	},
      	{
      		path:'test',
      		component:Test
      	},
      	{
      		path:'job',
      		component:Job
      	},
      	{
      		path:'detail/:id',
      		component:Detail
      	},
      ]
    },
    {
      path: '/me',
      name: 'Me',
      component: Me
    },
    {
      path: '/login',
      name: 'Login',
      component: Login
    },
    {
    	path:'/*',
    	redirect:'/index'
    }
  ]
})

跟后端请求接口的文件也写的很高级,作者的功力很深厚

//srcgetdata	opic.js
//引入axios模块
import axios from 'axios';
//基本配置,创建axios对象
var $http = axios.create({
  baseURL: 'https://cnodejs.org/api/v1',
//timeout: 1000,
  headers: {'X-Custom-Header': 'foobar'}
});
//获取栏目列表
function getItemList(options){
//	console.log(options);
	//设置默认参数,Object.assign是对象属性的合并,权重大的放在后面。例如options
	var newOptions=Object.assign({
		page:1,
		limit:9
	},options)
	
	return $http({
		method: 'get',
		//具体的地址
		url: '/topics',
		params:newOptions
	})
}

//获取文章
function getArticle(id){
	return $http.get('/topic/'+id)
}
//向外暴露,主要是暴露axios对象
export {getItemList,getArticle}

接下来我们分析页面

//srccomponentsIndex.vue
<template>
  <div class="index">
	  <nav class="top">
	  	<ul>
	  		<router-link to="/index/" tag="li" :class="(res==''||res==undefined)?'bj':''">全部</router-link>
	  		<router-link to="/index/good" tag="li" :class="res=='good'?'bj':''">精华</router-link>
	  		<router-link to="/index/share" tag="li" :class="res=='share'?'bj':''">分享</router-link>
	  		<router-link to="/index/ask" tag="li" :class="res=='ask'?'bj':''">问答</router-link>
	  		<router-link to="/index/job" tag="li" :class="res=='job'?'bj':''">招聘</router-link>
	  		<router-link to="/index/test" tag="li" :class="res=='test'?'bj':''">测试</router-link>
	  	</ul>
	  </nav>
	  <!--{{res}}-->
	  <router-view/>
  </div>
</template>

<script>
export default {
  name: 'Index',
  data () {
    return {
      msg: 'Welcome to Your Vue.js App'
    }
  },
  //计算属性
  computed:{
  	res:function(){
  		var arr=this.$route.path.split('/');
		console.log(arr[2]);
  		return arr[2];
  		
  	}
  },
//   这个时候用这个是什么意思?
  mounted:function(){
		var swiper = new Swiper('.swiper-container', {
			// 显示 slide 的个数
			slidesPerView: 5,
			spaceBetween: 15,
		});  	
	}
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
	@import '../assets/sass/base.scss';
	nav.top{
		rem(730px);
		height:rem(70px);
		background: #f6f6f6;
		border-radius: rem(15px) rem(15px) 0 0;
		margin:rem(40px) auto 0;
		ul{
			display:flex;
			100%;
			height: 100%;
			align-items: center;
			justify-content: space-around;
			li{
				font-size: rem(34px);
				color:#90b910;
			}
			.bj{
				background: #8dbd3e;
				color:#fff;
				padding:rem(5px) rem(10px);
				border-radius:rem(10px)
			}
		}
	}
</style>
//srccomponents	opicsGood.vue
<template>
  <div class="good quan">
  	<!--1-->
  	<topic-list :items="items"></topic-list>
  	<!--分页-->
  	<infinite-loading @infinite="infiniteHandler" v-if="isLoadingShow" class="box"></infinite-loading>
  </div>
</template>

<script>
	//引入axios模块
	//import axios from 'axios';
	//引入取数据函数
	import {getItemList} from '@/getdata/topic.js';
	//2、引入公共的主题列表组件
	import topicList from '@/components/topics/Topiclist';
	//引入分页组件
	import InfiniteLoading from 'vue-infinite-loading';
	
	export default {
	  name: 'Good',
	  data () {
	    return {
	      items:[],
	      //控制圆圈是否显示
	      isLoadingShow:true,
	      page:1
	    }
	  },
	   methods: {
	    infiniteHandler($state) {
        setTimeout(() => {
        	//更新数据
        	getItemList({tab:'good',page:++this.page}).then((data)=> {
				    console.log('aaaa',data.data.data);
				    this.items=this.items.concat(data.data.data);
				    if(data.data.data.length==0){
				    	this.isLoadingShow=false;
				    }else{
				    	 $state.loaded();
				    }
				  })
         
	      }, 1000);
	    },
	  },
	  //3、加载组件(类似局部组件)
	  components:{
	  	InfiniteLoading,
	  	topicList
	  },
	  //4.钩子函数
	  mounted:function(){
	  	//分类这里之所以要传对象,是因为axios的语法就是这样params:{}
	  	getItemList({tab:'good'}).then((data)=> {
		    //console.log(data.data.data);
		    this.items=data.data.data;
		  })
	  },
	}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
	@import '../../assets/sass/base.scss';
	.box{
		margin-bottom:rem(100px);
	}
</style>

上面的vue-infinite-loading插件主要是实现下拉数据加载更多的效果,实际数据的请求,以及点击回到顶部代码为

//srccomponents	opicsTopiclist.vue
<template>
  <div class="all quan">
  	<!--<loading v-if="items.length==0"></loading>-->
  	<ul class="items">
  		<li class="item" v-for="item in items">
  			<img v-lazy="item.author.avatar_url"/>
  			<div class="tab">
  				<div class="bj" v-if="item.top==true">置顶</div>
  				<div class="bj" v-else-if="item.good==true">精华</div>
  				<div class="bj1" v-else>{{tab[item.tab]}}</div>
  			</div>
  			<router-link :to="'/index/detail/'+item.id" tag="div" class="title">{{item.title}}</router-link>
  		</li>
  	</ul>
  	<div class="back">回<br>到<br>顶<br>部</div>
  </div>
</template>

<script>
	//引入axios模块
	//import axios from 'axios';
	//引入取数据模块 ,数据和组件分开,所以这里不引入数据模块
	//import {getItemList} from '@/getdata/topic.js';
	//引入loading组件
	import Loading from '@/components/Loading';
	
	export default {
	  name: 'All',
	  data () {
	    return {
	      tab:{
					share:'分享',
					ask:'问答',
					dev:'测试',
					job:'招聘'
				},
	    }
	  },
	  //加载loading组件、分页组件
	  components:{
	  	Loading
	  },
	  //接收数据,这个items是在子组件中绑定的属性名称
	  props:['items'],
	  //钩子函数,这里不取数据,所以不用钩子函数
		mounted:function(){
			$(window).scroll(function(){
				var top=$(window).scrollTop();
				if(top>200){
					$('.back').css('display','block');
				}else if(top<200){
					$('.back').css('display','none');
				}
			})
			$('.back').click(function(){
				$('html').animate({scrollTop:0},500);
			})
		}
	}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
	@import '../../assets/sass/base.scss';
	.quan{
		rem(730px);
		margin:0 auto;
		background:#fff;
		.items{
			100%;
			.item{
				display: flex;
				height:rem(100px);
				align-items: center;
				border-bottom: 1px solid #ccc;
				img{
					display: block;
					rem(60px);
					height:rem(60px);
					border-radius: rem(10px);
					margin:0 rem(15px);
				}
				.tab{
					.bj{
						rem(65px);
						height:rem(40px);
						font-size: rem(26px);
						background: #9cba48;
						color:#fff;
						text-align: center;
						line-height: rem(40px);
						border-radius: rem(10px);
						margin-right:rem(15px);
					}
					.bj1{
						rem(65px);
						height:rem(40px);
						font-size: rem(26px);
						background: #e5e5e5;
						color:#999;
						text-align: center;
						line-height: rem(40px);
						border-radius: rem(10px);
						margin-right:rem(15px);
					}
				}
				.title{
					rem(500px);
					line-height: rem(100px);
					overflow: hidden;
					text-overflow: ellipsis;
					white-space: nowrap;
					font-size: rem(30px);
				}
			}
		}	
		.back{
			display: none;
			position: fixed;
			top:60%;
			right:0;
			padding:rem(15px);
			background: #f5f5f5;
			border:1px solid #ccc;
			border-right:none;
			color:grey;
			font-size:rem(28px);
			border-radius: rem(20px) 0 0 rem(20px);
		}
	}
	
</style>

//srccomponents	opicsShare.vue
<template>
  <div class="share quan">
  	<!--1-->
  	<topic-list :items="items"></topic-list>
  	<!--分页-->
  	<infinite-loading @infinite="infiniteHandler" v-if="isLoadingShow" class="box"></infinite-loading>
  </div>
</template>

<script>
	//引入axios模块
	//import axios from 'axios';
	//引入取数据函数
	import {getItemList} from '@/getdata/topic.js';
	//2、引入公共的主题列表组件
	import topicList from '@/components/topics/Topiclist';
	//引入分页组件
	import InfiniteLoading from 'vue-infinite-loading';
	
	export default {
	  name: 'Share',
	  data () {
	    return {
	      items:[],
	      //控制圆圈是否显示
	      isLoadingShow:true,
	      page:1
	    }
	  },
	   methods: {
	    infiniteHandler($state) {
        setTimeout(() => {
        	//更新数据
        	getItemList({tab:'share',page:++this.page}).then((data)=> {
//				    console.log(data.data.data);
				    this.items=this.items.concat(data.data.data);
				    if(data.data.data.length==0){
				    	this.isLoadingShow=false;
				    }else{
				    	 $state.loaded();
				    }
				  })
         
	      }, 1000);
	    },
	  },
	  //3、加载组件(类似局部组件)
	  components:{
	  	InfiniteLoading,
	  	topicList
	  },
	  //4.钩子函数
	  mounted:function(){
	  	//分类这里之所以要传对象,是因为axios的语法就是这样params:{}
	  	getItemList({tab:'share'}).then((data)=> {
		    //console.log(data.data.data);
		    this.items=data.data.data;
		  })
	  },
	}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
		@import '../../assets/sass/base.scss';
	.box{
		margin-bottom:rem(100px);
	}
</style>
//srccomponents	opicsTopiclist.vue
<template>
  <div class="all quan">
  	<!--<loading v-if="items.length==0"></loading>-->
  	<ul class="items">
  		<li class="item" v-for="item in items">
  			<img v-lazy="item.author.avatar_url"/>
  			<div class="tab">
  				<div class="bj" v-if="item.top==true">置顶</div>
  				<div class="bj" v-else-if="item.good==true">精华</div>
  				<div class="bj1" v-else>{{tab[item.tab]}}</div>
  			</div>
  			<router-link :to="'/index/detail/'+item.id" tag="div" class="title">{{item.title}}</router-link>
  		</li>
  	</ul>
  	<div class="back">回<br>到<br>顶<br>部</div>
  </div>
</template>

<script>
	//引入axios模块
	//import axios from 'axios';
	//引入取数据模块 ,数据和组件分开,所以这里不引入数据模块
	//import {getItemList} from '@/getdata/topic.js';
	//引入loading组件
	import Loading from '@/components/Loading';
	
	export default {
	  name: 'All',
	  data () {
	    return {
	      tab:{
					share:'分享',
					ask:'问答',
					dev:'测试',
					job:'招聘'
				},
	    }
	  },
	  //加载loading组件、分页组件
	  components:{
	  	Loading
	  },
	  //接收数据,这个items是在子组件中绑定的属性名称
	  props:['items'],
	  //钩子函数,这里不取数据,所以不用钩子函数
		mounted:function(){
			$(window).scroll(function(){
				var top=$(window).scrollTop();
				if(top>200){
					$('.back').css('display','block');
				}else if(top<200){
					$('.back').css('display','none');
				}
			})
			$('.back').click(function(){
				$('html').animate({scrollTop:0},500);
			})
		}
	}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
	@import '../../assets/sass/base.scss';
	.quan{
		rem(730px);
		margin:0 auto;
		background:#fff;
		.items{
			100%;
			.item{
				display: flex;
				height:rem(100px);
				align-items: center;
				border-bottom: 1px solid #ccc;
				img{
					display: block;
					rem(60px);
					height:rem(60px);
					border-radius: rem(10px);
					margin:0 rem(15px);
				}
				.tab{
					.bj{
						rem(65px);
						height:rem(40px);
						font-size: rem(26px);
						background: #9cba48;
						color:#fff;
						text-align: center;
						line-height: rem(40px);
						border-radius: rem(10px);
						margin-right:rem(15px);
					}
					.bj1{
						rem(65px);
						height:rem(40px);
						font-size: rem(26px);
						background: #e5e5e5;
						color:#999;
						text-align: center;
						line-height: rem(40px);
						border-radius: rem(10px);
						margin-right:rem(15px);
					}
				}
				.title{
					rem(500px);
					line-height: rem(100px);
					overflow: hidden;
					text-overflow: ellipsis;
					white-space: nowrap;
					font-size: rem(30px);
				}
			}
		}	
		.back{
			display: none;
			position: fixed;
			top:60%;
			right:0;
			padding:rem(15px);
			background: #f5f5f5;
			border:1px solid #ccc;
			border-right:none;
			color:grey;
			font-size:rem(28px);
			border-radius: rem(20px) 0 0 rem(20px);
		}
	}
	
</style>

//srccomponents	opicsAsk.vue
<template>
  <div class="ask quan">
  	<!--1-->
  	<topic-list :items="items"></topic-list>
  	<!--分页-->
  	<infinite-loading @infinite="infiniteHandler" v-if="isLoadingShow" class="box"></infinite-loading>
  </div>
</template>

<script>
	//引入axios模块
	//import axios from 'axios';
	//引入取数据函数
	import {getItemList} from '@/getdata/topic.js';
	//2、引入公共的主题列表组件
	import topicList from '@/components/topics/Topiclist';
	//引入分页组件
	import InfiniteLoading from 'vue-infinite-loading';
	
	export default {
	  name: 'Ask',
	  data () {
	    return {
	      items:[],
	      page:1,
	      //控制圆圈是否显示
	      isLoadingShow:true,
	    }
	  },
	   methods: {
	    infiniteHandler($state) {
        setTimeout(() => {
        	//更新数据
        	getItemList({tab:'ask',page:++this.page}).then((data)=> {
//				    console.log(data.data.data);
				    this.items=this.items.concat(data.data.data);
				    if(data.data.data.length==0){
				    	this.isLoadingShow=false;
				    }else{
				    	 $state.loaded();
				    }
				  })
         
	      }, 1000);
	    },
	  },
	  //3、加载组件(类似局部组件)
	  components:{
	  	InfiniteLoading,
	  	topicList
	  },
	  //4.钩子函数
	  mounted:function(){
	  	//分类这里之所以要传对象,是因为axios的语法就是这样params:{}
	  	getItemList({tab:'ask'}).then((data)=> {
		    //console.log(data.data.data);
		    this.items=data.data.data;
		  })
	  },
	}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
		@import '../../assets/sass/base.scss';
	.box{
		margin-bottom:rem(100px);
	}
</style>

//srccomponents	opicsJob.vue

<template>
  <div class="job quan">
  	<!--1-->
  	<topic-list :items="items"></topic-list>
  	<!--分页-->
  	<infinite-loading @infinite="infiniteHandler" v-if="isLoadingShow" class="box"></infinite-loading>
  </div>
</template>

<script>
	//引入axios模块
	//import axios from 'axios';
	//引入取数据函数
	import {getItemList} from '@/getdata/topic.js';
	//2、引入公共的主题列表组件
	import topicList from '@/components/topics/Topiclist';
	//引入分页组件
	import InfiniteLoading from 'vue-infinite-loading';
	
	export default {
	  name: 'Job',
	  data () {
	    return {
	      items:[],
	      //控制圆圈是否显示
	      isLoadingShow:true,
	      page:1
	    }
	  },
	   methods: {
	    infiniteHandler($state) {
        setTimeout(() => {
        	//更新数据
        	getItemList({tab:'job',page:++this.page}).then((data)=> {
//				    console.log(data.data.data);
				    this.items=this.items.concat(data.data.data);
				    if(data.data.data.length==0){
				    	this.isLoadingShow=false;
				    }else{
				    	 $state.loaded();
				    }
				  })
         
	      }, 1000);
	    },
	  },
	  //3、加载组件(类似局部组件)
	  components:{
	  	InfiniteLoading,
	  	topicList
	  },
	  //4.钩子函数
	  mounted:function(){
	  	//分类这里之所以要传对象,是因为axios的语法就是这样params:{}
	  	getItemList({tab:'job'}).then((data)=> {
		    //console.log(data.data.data);
		    this.items=data.data.data;
		  })
	  },
	}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
		@import '../../assets/sass/base.scss';
	.box{
		margin-bottom:rem(100px);
	}
</style>

//srccomponents	opicsTest.vue
<template>
  <div class="dev quan">
  	<!--1-->
  	<topic-list :items="items"></topic-list>
  	<!--分页-->
  	<infinite-loading @infinite="infiniteHandler" v-if="isLoadingShow" class="box"></infinite-loading>
  </div>
</template>

<script>
	//引入axios模块
	//import axios from 'axios';
	//引入取数据函数
	import {getItemList} from '@/getdata/topic.js';
	//2、引入公共的主题列表组件
	import topicList from '@/components/topics/Topiclist';
	//引入分页组件
	import InfiniteLoading from 'vue-infinite-loading';
	
	export default {
	  name: 'Dev',
	  data () {
	    return {
	      items:[],
	      //控制圆圈是否显示
	      isLoadingShow:true,
	      page:1
	    }
	  },
	   methods: {
	    infiniteHandler($state) {
        setTimeout(() => {
        	//更新数据
        	getItemList({tab:'dev',page:++this.page}).then((data)=> {
//				    console.log(data.data.data);
				    this.items=this.items.concat(data.data.data);
				    if(data.data.data.length==0){
				    	this.isLoadingShow=false;
				    }else{
				    	 $state.loaded();
				    }
				  })
         
	      }, 1000);
	    },
	  },
	  //3、加载组件(类似局部组件)
	  components:{
	  	InfiniteLoading,
	  	topicList
	  },
	  //4.钩子函数
	  mounted:function(){
	  	//分类这里之所以要传对象,是因为axios的语法就是这样params:{}
	  	getItemList({tab:'dev'}).then((data)=> {
		    //console.log(data.data.data);
		    this.items=data.data.data;
		  })
	  },
	}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
	@import '../../assets/sass/base.scss';
	.box{
		margin-bottom:rem(100px);
	}
</style>

//srccomponentsMe.vue
<template>
  <div class="me">
  	<div class="head">
  		<ul>
  			<li><a href="/index">主页</a>&nbsp;/&nbsp;</li>
  			<li class="active">关于</li>
  		</ul>
  	</div>
  	<div class="shuru">
  		<h2>关于</h2>
  		<div class="con">
  			<p>CNode 社区为国内最大最具影响力的 Node.js 开源技术社区,致力于 Node.js 的技术研究。</p>
  			<p>CNode 社区由一批热爱 Node.js 技术的工程师发起,目前已经吸引了互联网各个公司的专业技术人员加入,我们非常欢迎更多对 Node.js 感兴趣的朋友。</p>
  			<p>CNode 的 SLA 保证是,一个9,即 90.000000%。</p>
  		</div>
  	</div>
  </div>
</template>

<script>
export default {
  name: 'Login',
  data () {
    return {
      msg: 'Welcome to Your Vue.js App'
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
	@import '../assets/sass/base.scss';
	
	.me{
		margin:rem(40px) auto 0;
		rem(730px);
		height:rem(686px);
		background: #fff;
		border-radius: rem(10px);
		.head{
			border-radius: rem(10px) rem(10px) 0 0;
			100%;
			height:rem(80px);
			background:#f6f6f6;
			ul{
				display: flex;
				padding-left:rem(20px);
				li{
					font-size:rem(28px);
					line-height: rem(80px);
					color:#999;
					a{
						color:#80bd01;
					}
				}
			}
		}
		.shuru{
			padding:rem(20px) rem(20px) 0;
			h2{
				font-size: rem(40px);
				line-height:rem(80px);
				border-bottom:1px solid #ccc;
			}
			.con{
				padding-top:rem(30px);
				p{
					font-size: rem(30px);
					line-height: rem(50px);
					margin-bottom:rem(30px);
				}
			}
		}
	}
</style>

//srccomponentsLogin.vue
<template>
  <div class="login">
  	<div class="head">
  		<ul>
  			<li><a href="/index">主页</a>&nbsp;/&nbsp;</li>
  			<li class="active">登陆</li>
  		</ul>
  	</div>
  	<div class="shuru">
  		<p>用户名</p>
  		<input type="text"  />
  		<p>密码</p>
  		<input type="password" /><br />
  		<a href="#" class="btn">登录</a>
  		<a href="#"  class="btn1">通过GitHub登录</a>
  	</div>
  </div>
</template>

<script>
export default {
  name: 'Login',
  data () {
    return {
      msg: 'Welcome to Your Vue.js App'
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
	@import '../assets/sass/base.scss';
	
	.login{
		margin:rem(40px) auto 0;
		rem(730px);
		height:rem(686px);
		background: #fff;
		border-radius: rem(10px);
		.head{
			border-radius: rem(10px) rem(10px) 0 0;
			100%;
			height:rem(80px);
			background:#f6f6f6;
			ul{
				display: flex;
				padding-left:rem(20px);
				li{
					font-size:rem(28px);
					line-height: rem(80px);
					color:#999;
					a{
						color:#80bd01;
					}
				}
			}
		}
		.shuru{
			height:rem(526px);
			padding-top:rem(80px);
			padding-left:rem(20px);
			p{
				font-size: rem(30px);
				color:#333;
				margin-bottom: rem(15px);
			}
			input{
				rem(568px);
				height:rem(60px);
				border:rem(2px) solid #ccc;
				border-radius: rem(10px);
				margin-bottom: rem(40px);
				font-size: rem(30px);
			}
			a{
				display: inline-block;
				padding:rem(15px);
				color:#fff;
				border-radius: rem(10px);
				font-size: rem(28px);
				margin:rem(30px) rem(5px);
			}
			.btn{
				background:#0088cc;
				margin-left:rem(20px);
			}
			.btn1{
				background: #5bc0de;
				
			}
		}
	}
</style>
原文地址:https://www.cnblogs.com/smart-girl/p/11386973.html