[置顶] Vue.js 2.0从入门到放弃---入门实例(二)
Vue.js入门实例,Vue.js 2.0教程,vuejs单文件组件,vue-router2016-11-23
前面一篇博客介绍了从零开始准备Vue.js所需的一些环境和工具。这片博客就来跟大家探讨一下Vue.js 2.0中的一些特性,以及一个小实例,通过实例来跟大家分享,想必更容易理解。
先来看一下,看完这篇博客,你会做出什么样的效果吧。
就是这样的一个小的demo,其中主要用到了vue-router 2.0 和vue的单文件组件,这里再细分一下如下
先来看一下项目的机构,我在原项目目录结构上稍作调整,让目录结构更清晰一些。如下图
在 src 目录下,删除 App.vue ,增加 pages 文件夹,该文件夹用来用来放置我们创建的“页面”(比如,Home.vue)。其实,这里说的“页面”也是组件,只是它变现为一个“页面”而已,跟components 目录下的组件没有本质的区别,我们分开目录放置主要是更语义化,结构更清晰易懂。
可能有的刚接触的同学还不太了解组件(.vue后缀结尾的文件),不要着急,接着往下看。
<!-- index.html -->
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
    <title>first-vue</title>
    <style type="text/css">
    	* { margin: 0; padding: 0; }
    	html,body { background: #eee; }
    	ul,li { list-style: none; }
    	a { text-decoration: none; }
    	img { vertical-align: middle; }
    	/* 跳转页面动画 */
    	.slide-enter,
    	.slide_back-enter {
    		position: absolute;
    		width: 100%;
    	}
    	.slide-leave,
    	.slide_back-leave {
    		position: absolute;
    		width: 100%;
    	}
    	.slide-enter-active,
    	.slide_back-enter-active {
    		transition: all 0.3s linear;
    	}
    	.slide-leave-active {
    		position: absolute;
    		transition: all 0.3s linear;
    		transform: translate(-100%);
    	}
    	.slide-enter{
    		transform: translateX(100%);
    	}
    	.slide_back-leave-active {
    		position: absolute;
    		transition: all 0.3s linear;
    		transform: translate(100%);
    	}
    	.slide_back-enter {
    		transform: translateX(-100%);
    	}
    </style>
  </head>
  <body>
    <div id="app">
    	<transition :name="transitionName">
    		<router-view></router-view>
    	</transition>
    </div>
    <script type="text/javascript">
    	// 计算html的font-size
    	(function(){
    		function resizeBaseFontSize(){
    			var rootHtml = document.documentElement,
    				deviceWidth = rootHtml.clientWidth;
    			if(deviceWidth > 640){
    				deviceWidth = 640;
    			}
    			rootHtml.style.fontSize = deviceWidth / 7.5 + "px";
    		}
    		resizeBaseFontSize();
    		window.addEventListener("resize", resizeBaseFontSize, false);
    		window.addEventListener("orientationchange", resizeBaseFontSize, false);
    	})();
    </script>
  </body>
</html>
其中 transition组件是用来控制页面切换的动画用的,transitionName绑定到的是main.js中的data中的transitionName字段。// main.js
// 导入Vue,这个是必需的,在使用Vue之前,必须先导入
import Vue from 'vue'
// 导入 vue-router,并使用
import VueRouter from 'vue-router'
Vue.use(VueRouter)
// 导入 pages 下的 Home.vue 
import Home from './pages/Home'
import Detail from './pages/Detail'
// 定义路由配置
const routes = [
	{
		path: '/',
		component: Home
	},
	{
		path: '/detail',
		component: Detail
	}
]
// 创建路由实例
const router = new VueRouter({
	routes
})
// 创建 Vue 实例
new Vue({
  el: '#app',
  data(){
  	return {
  		transitionName: 'slide'
  	}
  },
  router, // 在vue实例配置中,用router
  watch: {
  	// 监视路由,参数为要目标路由和当前页面的路由
  	'$route' (to, from){
  		const toDepth = to.path.substring(0, to.path.length-2).split('/').length
  		// 官方给出的例子为 const toDepth = to.path.split('/').length 由于现在只有两个路由路径'/'和'/detail'
  		// 按照官方给的例子,这两个路由路径深度都为 2 ,所以,这里稍作调整,不知道有什么不妥
  		// 但目前在这个demo中能正常运行,如果知道更好的方法,欢迎留言赐教
  		const fromDepth = from.path.substring(0, from.path.length-2).split('/').length
  		this.transitionName = toDepth < fromDepth ? 'slide_back' : 'slide'
  		// 根据路由深度,来判断是该从右侧进入还是该从左侧进入
  	}
  }
})<!-- HomeHeader.vue -->
<template>
	<header class="header">
		<div class="header_inner">
			<div class="header_cont">主页</div>
		</div>
	</header>
</template>
<style>
	.header {
		height: 0.88rem;
	}
	.header_inner {
		position: fixed;
		top: 0;
		left: 0;
		right: 0;
		z-index: 99;
		max-width: 640px;
		height: 0.88rem;
		box-sizing: border-box;
		margin: 0 auto;
		padding: 0 0.24rem;
		border-bottom: 0.02rem solid #80ccd6;
		background-color: #fff;
	}
	.header_cont {
		text-align: center;
		padding: 0 0.4rem;
		line-height: 0.86rem;
		font-size: 15px;
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
	}
</style><!-- List.vue -->
<template>
	<li class="sec_li">
		<router-link to="/detail" class="lp_li_a">
			<div class="lp_li_imgWrap">
				<img src="../assets/img/lp_01.jpg" alt="">
			</div>
			<p class="lp_li_name">{{ title }}</p>
			<p class="lp_li_price">¥{{ price }}元</p>
		</router-link>
	</li>
</template>
<style scoped>
	.sec_li {
		float: left;
		width: 50%;
		margin-bottom: 0.1rem;
	}
	.lp_li_a {
		display: block;
		padding: 0.3rem 0;
		margin: 0 0.05rem;
		text-align: center;
		background: #fff;
	}
	.lp_li_imgWrap {
		padding: 0.24rem 0;
	}
	.lp_li_imgWrap > img {
		width: auto;
		height: 2.3rem;
	}
	.lp_li_name {
		height: 0.5rem;
		line-height: 0.5rem;
		font-size: 16px;
		color: #333;
	}
	.lp_li_price {
		height: 0.5rem;
		line-height: 0.5rem;
		font-size: 16px;
		color: #fb3b3b;
	}
</style>
<script>
	export default {
		props: ['price', 'title']
	}
</script><!-- DetailHeader.vue -->
<template>
	<header class="header">
		<div class="header_inner flexWrap">
			<div 
				id="header_btn_nav" 
				class="header_btn header_btn_back"
				v-on:click="goBack"
				>返回</div>
			<div class="header_cont flex">详情</div>
			<div class="header_btn header_btn_cart"></div>
		</div>
	</header>
</template>
<style>
	.flexWrap {
		display: -webkit-flex;
		display: flex;
	}
	.flex {
		flex: 1;
	}
	.header {
		height: 0.88rem;
	}
	.header_inner {
		position: fixed;
		top: 0;
		left: 0;
		right: 0;
		z-index: 99;
		max-width: 640px;
		height: 0.88rem;
		box-sizing: border-box;
		margin: 0 auto;
		padding: 0 0.24rem;
		border-bottom: 0.02rem solid #80ccd6;
		background-color: #fff;
	}
	.header_btn {
		width: 0.5rem;
		height: 100%;
		background-repeat: no-repeat;
	}
	.header_btn_back {
		line-height: 0.86rem;
	}
	.header_cont {
		text-align: center;
		padding: 0 0.4rem;
		line-height: 0.86rem;
		font-size: 15px;
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
	}
	.header_btn:active {
		opacity: 0.7;
	}
</style>
<script>
	export default {
		methods: {
			goBack(){
				window.history.back();
			}
		}
	}
</script><!-- Home.vue -->
<template>
	<div class="container">
		<!-- 由于html不区分大小写,所以js中驼峰命名方式在html中要改成用短横线连接的形式 -->
		<home-header></home-header>
		<div class="content">
			<ul class="cont_ul">
				<list
					v-for="item in items"
					:price="item.price"
					:title="item.title">
				</list>
			</ul>
		</div>
	</div>
</template>
<style>
	.container {
		max-width: 640px;
		margin: 0 auto;
		overflow-x: hidden;
	}
	.cont_ul {
		padding-top: 0.05rem;
		margin: 0 -0.12rem;
	}
	.cont_ul:after {
		content: "";
		display: block;
		width: 0;
		height: 0;
		clear: both;
	}
</style>
<script>
	// 导入要用到的子组件
	import HomeHeader from '../components/HomeHeader'
	import List from '../components/List'
	export default {
		data () {
			return {
				items: [
					{ price: "129.00", title: "大学" },
					{ price: "256.00", title: "中庸" },
					{ price: "399.00", title: "论语" },
					{ price: "998.00", title: "孟子" },
					{ price: "99.00", title: "道德经" },
					{ price: "89.00", title: "老子" },
					{ price: "188.00", title: "金刚经" },
					{ price: "209.00", title: "易筋经" },
				]
			}
		},
		// 在components字段中,包含导入的子组件
		components: {
			HomeHeader,
			List
		}
	}
</script><!-- Detail.vue -->
<template>
	<div class="detail">
		<detail-header></detail-header>
		<img src="../assets/img/lp_01.jpg" alt="">
		<p>崇贤馆始置唐代太宗朝。1999年,李克先生及志同道合者复兴其宗旨。以积累、传播中华优秀传统文化,提供全新国学体验馆为宏愿。</p>
		<p>其间,在季羡林、冯其庸等国学大师及著名文史学家傅璇琮、毛佩琦先生指导下,耕注先贤原典,以宣纸线装精品形式呈奉世人。作为一家国学传播机构,崇贤馆始终致力于中华传统文化的传承和推广,以古籍线装宣纸书的形式,对浩繁的史海巨著进行经典复刻。不仅如此,崇贤馆还延请了傅璇琮、毛佩奇等诸位在国学界内享有盛誉的专家和学者担纲学术顾问,以精益求精的治学态度面对每一部崇贤馆的作品,使之成为学术史中无尚的精品。</p>
	</div>
</template>
<style>
	.detail {
		padding: 0.24rem;
		font-size: 12px;
	}
	img {
		display: block;
		width: 80%;
		margin: 0 auto 0.2rem;
	}
	p {
		font-size: 14px;
		line-height: 0.5rem;
		text-align: justify;
		padding-bottom: 0.24rem;
	}
</style>
<script>
	import DetailHeader from '../components/DetailHeader'
	export default {
		components: {
			DetailHeader
		}
	}
</script>