[置顶] 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>