前面一篇博客介绍了从零开始准备Vue.js所需的一些环境和工具。这片博客就来跟大家探讨一下Vue.js 2.0中的一些特性,以及一个小实例,通过实例来跟大家分享,想必更容易理解。
先来看一下,看完这篇博客,你会做出什么样的效果吧。

就是这样的一个小的demo,其中主要用到了vue-router 2.0 和vue的单文件组件,这里再细分一下如下

1. vue-router 2.0 定义路由配置
2. router-view 和 router-link 来控制路由
3. transition 控制页面之间的跳转动画
4. .vue后缀名的单文件组件
5. 简单的ES6语法

修改项目目录结构

先来看一下项目的机构,我在原项目目录结构上稍作调整,让目录结构更清晰一些。如下图

在 src 目录下,删除 App.vue ,增加 pages 文件夹,该文件夹用来用来放置我们创建的“页面”(比如,Home.vue)。其实,这里说的“页面”也是组件,只是它变现为一个“页面”而已,跟components 目录下的组件没有本质的区别,我们分开目录放置主要是更语义化,结构更清晰易懂。
可能有的刚接触的同学还不太了解组件(.vue后缀结尾的文件),不要着急,接着往下看。

.vue后缀名的单文件组件

这里先说一下我对组件的理解。组件,顾名思义就是一组元素组成的一个原件(理解的比较浅显、直白),在Vue.js中,表现为一个自定义元素。开篇展示的图中,首页的的列表中的每一项就可以看成一个组件(事实上,在demo中,我也是这么做的),这个组件由一张图片,一个显示价格的元素,一个显示名称的元素组成,我就可以先把它定义为一个list组件。
首先,我们先来分析一下两个页面中组件,如下图


主页可以看做由两个组件组成,homeHeader和list组件,而详情页则可以看成是有detailHeader跟下边的图文内容组成(这里的图文内容也可以写成组件,但是博主这里想偷懒一下,有兴趣的,可以自己试着写成组件)。
分析完两个页面的组件构成之后,我们修改项目目录如下

这样的目录结构,就一目了然了吧。两个页面,Home和detail和其中用到的三个组件。ps:assets目录下新建的img目录,里边放的一张图片是测试用的。
做了这么一大堆铺垫,现在来简单说一说单文件组件.vue文件。拿List.vue为例,说说单文件组件的结构,如下图

单文件组件,由三大部分组成,template、style、script,想必看到这三个标签元素,大家也都对其作用大概了解了吧。

template

template是放置组件的组成部分——html元素的地方,是整个组件的模板构成。不过有一点需要特别注意,template下只能存在一个根html元素作为wrapper,不能存在两个并列的跟元素,否则会报错。

style

style下放置组件的样式,可以用css预处理器less或者sass等,前提是需要安装这些依赖包,和设置lang属性,博主比较懒,直接用css了。这个style里的样式表在项目运行的时候会生成一个style标签,插入到index.html的head标签里,如果组件里的style标签为空,则会在index.html的head里插入一个空的style标签,所以,建议大家,这个组件没有用到css,就不要写一个空的style,直接省略就好。

既然每个组件的样式都会生成一个style插入到index.html中,我们做的又是单页面应用,所有的代码都基于index.html的,那如果我们的项目比较大或者是多人协作开发的,难免会在写组件的时候会命名相同的class,这样的话,具有相同class的不同组件的样式就会收到影响,产生不可预估的样式问题,那岂不是很头疼。其实,vue早就替我想到了这个问题,可以给style设置一个scoped的属性,意思是该style里的样式只对这个组件起作用,不会影响其他组件中含有相同class的元素。那vue是怎么做到的呢?这里先卖个关子,后边再探究竟。

script

script里自然是放的js代码。这里会用到一些ES6的语法,大家可以去 http://es6.ruanyifeng.com/
学习ES6的一些新的语法特性。在List.vue的script中,大致意思就是导出一个对象(这个组件),其中设置可以通过属性price和title传递数据。看下边的示意图

具体的怎样利用props传递数据,请看 https://vuefe.cn/v2/guide/components.html#使用Props传递数据

.vue单文件组件的三大组成部分大概就是这些,具体的内容,主要是script,整个组件的功能代码全在这里边,大家还要多去学习和了解,由于在下才疏学浅,也只能介绍这些了。

vue-router 2.0

由于用vue主要开发单页面应用,没有页面之间的跳转,在vue中,一个所谓的“页面”实则是一个看起来很像“页面”的一个组件(这个组件大部分情况下包含其他子组件)而已。既然没有页面,那怎样实现页面之间的切换呢?那就是我们现在要介绍的主角——vue-router 2.0。
vue-router是在vue中控制路由的。ps:如果你不太理解路由这个概念,可以简单的理解为url重的hash部分,只不过vue做了一些封装和完善。要控制路由,还需要借助两个vue-router自带的两个组件router-view和router-link。

安装vue-router 2.0

打开命令行,cd到当前项目的目录,运行 cnpm install vue-router –save ,f等待安装完成即可。

router-view

router-view是现实路由内容的地方,即如果有多个“页面”需要切换,显示当前“页面“的地方。需要注意的是,使用vue-router控制路由则必须router-view作为容器。

router-view还可以嵌套,即嵌套路由,详情去https://router.vuejs.org/zh-cn/essentials/nested-routes.html学习哟~

router-link

router-link有一个to属性,其属性值是目标路由,在运行项目的时候,router-link表现为a标签,to属性则表现为a标签的href属性。至于,为什么不直接用a标签,我也不知道呢疑问。如果你知道的话,可以留言告诉我,不胜感激的呢~

这是List组件中用到的router-link组件。

基本的概念和准备工作都做好了,剩下的就是贴代码了,瞬间感觉轻松好多。

修改根目录下的index.html如下:

<!-- 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字段。

修改src目录下的main.js如下:

// 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代码

<!-- 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代码

<!-- 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代码

<!-- 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代码

<!-- 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代码

<!-- 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>  

style的scoped属性的问题

现在,我们把List.vue中的style加上scoped属性,然后在命令行cd到项目目录,运行 npm run dev ,在浏览器访问localhost:8080,然后审查列表标签,会发现在列表标签中多了一个自定义属性,如下

相应的css选择器也成了复合选择器,在原有选择器基础上复合了一个属性选择器,so~~这就是style scoped的奥秘之处。

Vue.js2.0 入门实例(二)(小试牛刀)相关推荐

  1. Vue.js2.0开发环境搭建(三)

    转载自  Vue.js2.0从入门到放弃---入门实例(三) 今天就来简单说一下vue-resource,这是vue的一个与服务器端通信的HTTP插件,用来从服务器端请求数据.话不多说,直接上干货吧. ...

  2. Vue.js2.0开发环境搭建(一)

    转载自  Vue.js2.0从入门到放弃---入门实例(一) 最近,vue.js越来越火.在这样的大浪潮下,我也开始进入vue的学习行列中,在网上也搜了很多教程,按着教程来做,也总会出现这样那样的问题 ...

  3. Vue学习(入门实例、常用指令)-学习笔记

    文章目录 Vue学习(入门实例.常用指令)-学习笔记 实例 常用指令 v-on v-bind v-for v-html v-if event v-model 双向数据绑定实现 - defineProp ...

  4. 耗时近一个月,终于录完了VUE.JS2.0前端视频教程!

    这次课录制的比较辛苦,圣诞节时原本已经快录制完成了,偶然的一次,播放了一下,感觉不满意,好几篇推倒重来,所以今天才结束. vue.js2.0是Vue.JS的最新版本,视频教程还不多,如果你看到了,学到 ...

  5. vue.js2.0完整视频教程12套

    0.1智能社vuejs(1-11章全套) 0.2英文版learing vuejs 0.3Vue.js实战小米阅读开发 0.4走进Vue.js2.0 0.5Vuejs教程45节课 0.6Vue.js+N ...

  6. Vue.js2.0开发环境搭建(二)

    转载自  Vue.js 2.0从入门到放弃---入门实例(二) 前面一篇博客介绍了从零开始准备Vue.js所需的一些环境和工具.这片博客就来跟大家探讨一下Vue.js 2.0中的一些特性,以及一个小实 ...

  7. vue.js2.0 新手开发_VueJs2.0建议学习路线

    最近VueJs确实火了一把,自从Vue2.0发布后,Vue就成了前端领域的热门话题,github也突破了三万的star,那么对于新手来说,如何高效快速的学习Vue2.0呢. 既然大家会看这篇文章,那么 ...

  8. Vue.js2.0开发环境搭建(四)

    转载自   vuejs2.0从入门到放弃--入门实例(四) 最近,很多小伙伴有疑惑,想学vuejs必须先了解复杂的构建工具和命令行操作吗!!答案是否定的! 对于很多做前端的同学,涉及到命令行和构建工具 ...

  9. 【转】最详细的Log4J使用教程一、入门实例二、Log4J基本使用方法三、Spring中使用Log4J四、实战经验总结

    原文地址:http://www.codeceo.com/article/log4j-usage.html 日志是应用软件中不可缺少的部分,Apache的开源项目log4j是一个功能强大的日志组件,提供 ...

最新文章

  1. 多才多艺的移动式人形机器人iPal,担当起小朋友的“好家教”
  2. java基础初步总结
  3. js escape,unescape解决中文乱码问题的方法
  4. 时序图 分支_BOOM微架构学习(1)——取指单元与分支预测
  5. Alpha冲刺Day5
  6. MyBatis 源码解读-配置解析过程
  7. 无源的nfc加传感_基于ON Semiconductor SPS无源温度标签,应用于冷链运输的 UHF 标签读取器方案...
  8. docker启动,重启,关闭命令
  9. 收集数据至泛型Dictionary
  10. eclipse PHP开发环境配置
  11. docker helowin 迁移_禅道在docker上部署与迁移
  12. 基于Python-turtle库绘制小猪佩奇、小猫咪
  13. 所有锁的unlock要放到try{}finally{}里,不然发生异常返回就丢了unlock了
  14. 如何批量从Excel文件中导入数据到数据库(二)
  15. Atitit 图像处理 公共模块 矩阵扫描器
  16. php b2c源码,Thinkphp5萤火商城B2C小程序源码
  17. 服务器pe系统ghost系统安装教程,U盘PE启动安装GHOST系统图文教程
  18. systrace抓取方式
  19. 微信 服务器参数错误 请重新填写,微信上登录验证出现参数错误怎么解决
  20. 服务容错 - Hystrix

热门文章

  1. python自动化干什么-自动化专业学习python需要到什么程度?
  2. jar包瘦身的方法和思考
  3. DRGs 工具可以从哪些纬度来衡量评价医院的医疗服务质量?
  4. mysql解决[HY000][1366] Incorrect string value: ‘\xE4\xB8\xAD\xE6\x96\x87‘ for column ‘title‘ at row 1
  5. 360浏览器自带控制台使用
  6. Java使用日期类实现打印输出各个年份的日历表
  7. 《Android移动应用基础教程》(Android Studio)(第二版)黑马教程 课后题答案第一章
  8. embedding embedding_lookup
  9. Python之批处理字符串(打开文件)
  10. 模拟算法-桶排-信号塔问题详解+代码——zzx的博客