Vue.js 2.0从入门到放弃---入门实例(二)

来源:互联网 发布:数据分析助理干啥的 编辑:程序博客网 时间:2024/06/08 06:41

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

先来看一下,看完这篇博客,你会做出什么样的效果吧。


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


  • vue-router 2.0 定义路由配置

  • router-view 和 router-link 来控制路由

  • transition 控制页面之间的跳转动画

  • .vue后缀名的单文件组件

  • 简单的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的语法,大家可以去 这里 学习ES6的一些新的语法特性。在List.vue的script中,大致意思就是导出一个对象(这个组件),其中设置可以通过属性price和title传递数据。看下边的示意图


具体的怎样利用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还可以嵌套,即嵌套路由,详情去 这里学习哟~

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的奥秘之处。

这下应该没有什么遗漏了吧,好累,心好累~~~

哦,对了,如果用sublime开发,建议安装vue的插件,支持语法高亮哟~

应有些朋友的需求,把项目源码托管到github上,项目网址 https://github.com/Angewell/firstVue。


感谢分享http://blog.qianduanchina.cn/post/59704ab47838a71273eb4da7


阅读全文
'); })();
0 0
原创粉丝点击
热门IT博客
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 我懂得了母爱作文 珍惜母爱作文600字 母爱作文600字记叙文 伟大的母爱400字 母爱作文开头 关于母爱的名言名句 母爱的伟大作文 母爱的作文600字 母爱日记300字 关于母爱的作文400字 母爱如山作文 母爱作文结尾 有关母爱的句子 关于母爱的作文800字 母爱500字作文 母爱300字作文 母爱作文300字左右 母爱400字作文 有关母爱的作文 母爱名言大全 母爱作文600字初中 关于母爱的开头 写母爱的句子 赞美母爱的名言 我读懂了母爱 母爱450字作文 关于母爱的作文600字 母爱高中作文800字 母爱600字作文 母爱的作文300个字 母爱作文100字 关于母爱的作文结尾 母爱依然作文 父爱母爱的名言 父爱如山母爱如水 父爱母爱的作文 母爱的名言名句 父爱母爱的散文 母爱让我感动 母爱伴我成长 有关母爱古诗