30

概述

--

  1. 项目中会用到的插件 vue-router vue-resource

  2. 打包工具 webpack

  3. 依赖环境 node.js


start 安装vue开发的模板

# 全局安装 vue-cli

$ npm install -g vue-cli
# 创建一个基于 "webpack" 模板的新项目
$ vue init webpack my-project
# 安装依赖,走你
$ cd my-project
$ npm install
$ npm run dev
  1. 文件解释:

    • build中配置了webpack的基本配置、开发环境配置、生产环境配置

    • config中配置了路径端口值等

    • node_modules为依赖的模块

    • src放置组件和入口文件

    • static放置静态资源文件

    • index.html文件入口

  2. webpack中的一些解释:

    • new HtmlWebpackPlugin 这个插件的作用是把output输出的文件自动插入到html里

    • 这里不使用elint检查代码


step1 配置路由

clipboard.png

  • 这里使用vue-router 中文基本用法可参见http://router.vuejs.org/zh-cn...

  • 把原来脚手架中的new Vue换成了路由实现,最容易忘记的一点是Vue.use(VueRouter);

  • 这里的Vue.extend()暂时先定义两个临时的组件,main.vue为入口文件,组件内要添加路由视图标签<router-view></router-view>

  • 这里的router.start(app,"#app")的app是require进来main,'#app'是添加从index.html的id为app入口

现在的效果是:

clipboard.png

-this is bar 上面那部分是main.vue里面的,

-this is bar 则是有router-view渲染出来的


step2 提取路由

  1. app中要切换多个路由为了不代码耦合将map映射部分提取到一个router.js文件中

  2. 这里要后续要引用zepto开发,所以这里要在webpack.base.conf.js中做一个配置

    externals: {
        'zepto': 'Zepto'
    },

clipboard.png

*上截图解释这个参数,所以要加html中加如zepto的链接,然后在其他地方就可以引用了*

3.只是简单地把组件和映射放到router.js中,然后在app.js中传入router

router.js
clipboard.png

app.js增加的代码
clipboard.png

clipboard.png

现在页面还是和之前一样没有变化,基本框架和路由搭建完成,然后就可以开始封装组件


step3 main.vue组件编写

1.app的主页底部一般都有几个tab键是固定不变的,这里实现四个tab键分别是首页,发现,通知,我
2.这里使用mobile sui搭建ui,在main.vue<style>中引用sui样式

@import './assets/css/sm.css';
@import './assets/css/sm-extend.min.css';

clipboard.png

这样已经能呈现一个底部导航,但是不太符合vue组件化的概念,毕竟重复了四次的tab代码,所有这里要用slot进行内容分发

3.这里要理解slot元素,先上一张官方的解释

clipboard.png

step4 slot的使用

<slot> 就是外部调用时,标签中的内容。如果外部调用时没有提供内容的话,那么它就会使用自己默认提供的内容,非常方便。

-这个字面意思确实难以理解,用代码解释
-首先定义Bar.vue组件替代最外层的nav

Paste_Image.png

  • 然后在main.vue import 进来引用

  • 原来的nav标签就会变成这样写

Paste_Image.png

  • 先看现在的效果

Paste_Image.png

  • 一切正常,但是如果把Bar.vue中的slot注释,就没有这些导航图标了,所以我可以理解为使用了slot可以把不在Bar.vue的template中的代码引进来,不使用就直接使用Bar.vue的template模板了

Paste_Image.png

  • 现在可以把里面的item元素也弄成一个BarItem.vue组件

  • 这里要知道一个新的指令v-link和它的activeClass配合

v-link 是一个用来让用户在 vue-router 应用的不同路径间跳转的指令。http://router.vuejs.org/zh-cn... 详情看这里

先上代码BarItem.vue

Paste_Image.png

  • script中的props是在main.vue传进来的参数,v-link中的replcace:true 是用了router.replace()而不是router.go()也就是不能后退(首页标签页切换就不让用户有后退的功能了),activeClass是当路由激活时加上的类

  • main.vue现在的代码

Paste_Image.png

  • 对了,记得把BarItem.vue引进来喔

  • 现在的效果还是像之前一样,但是已经实现组件化

step5 HomeTab 路由切换

  • 新建search.vue、message.vue、me.vue、home.vue,然后在router.js中做相应的配置

Paste_Image.png

  • 这里动态组件载入就是常说的懒加载组件

当你在使用 Webpack 或者 Browserify 时,在基于异步组件编写的 Vue 项目时,也可以较为容易的实现惰性加载组件。不再是之前所述的直接引用一个组件,现在需要像下面这样通过定义一个函数返回一个组件:

  • resolve这个参数有点难理解,实际就是用异步加载,用AMD风格的写法是

require(['./MyComponent.vue'], 
     function (MyComponent) { 
    // code here runs after MyComponent.vue is asynchronously loaded
.})
  • 五个路由都写好就可以随意切换tab了

Paste_Image.png

  • 想要达到这种效果

  • homeTab这部分也是可以提取出组件作为各个tab的头部

  • 赞一下vue的错误提示,一开始死活显示不了,这错误提示还是很明显的
    Paste_Image.png

  • 用组件记得在js components中注册

  • 还有这个提示,注册了变量没
    Paste_Image.png

step6 HomeTab 内容+下拉刷新

  • 这里的ui用的是sui,包括下拉刷新也是用sui的组件,详情:http://http://m.sui.taobao.or...

  • 下拉刷新会有dom元素的操作,不能用jq的思想,所以要用到自定义指令

自定义指令提供一种机制将数据的变化映射为 DOM 行为

pullToRefresh.js

Paste_Image.png

用到的钩子函数:
bind:只调用一次,在指令第一次绑定到元素上时调用。
unbind:只调用一次,在指令从元素上解绑时调用。
在注册之后,便可以在 Vue.js 模板中这样用(记着添加前缀 v-),当作为属性指令的时候


用到的指令实例属性:

  • el: 指令绑定的元素。

  • vm: 拥有该指令的上下文 ViewModel。

  • expression: 指令的表达式,不包括参数和过滤器。

  • params:自定义指令可以接收一个 params 数组,指定一个特性列表,Vue 编译器将自动提取绑定元素的这些特性。this.params[key]会自动保持更新。


data-ptr-distance="55"可以配置下拉刷新的下拉距离,sui的配置


  • 在app.js中注册自定义指令

import pullToRefresh from './directives/pullToRefresh'
//directive
Vue.directive('pullToRefresh', pullToRefresh);
  • 自定义指令完成

  • 然后是要将这部分的ui封装成组件放在两个tab里
    Paste_Image.png

  • 就是封装官网上的这段代码

Paste_Image.png

-前面已经说过怎么去拆分组件了,只要你觉得合理,子父组件之间能通信想怎么拆都可以,当然以复用为原则,直接上我自己的拆分方式,可以对比一下跟第二篇的源码,都会同步到github,无论怎么定义记得要在components里面注册
--vue函数中通过this.$el来获取当前元素

Paste_Image.png

  • 千万要注意一个概念叫做片段实例

    • 就是模板中只有一个顶级元素,否则会对你下面获取当前元素产生影响,如果你在函数中想要获取当前元素用this.$el得到的不是一个节点,而是一个空文本元素,那么你就要去检查你的模板是不是有问题了

Paste_Image.png

Paste_Image.png

  • 这里最重要的是把刚刚的指令引入进来了

Paste_Image.png

  • 前面有提到作为属性要加v-前缀

  • 现在可以去书写你下拉刷新是要执行的函数了,在methods方法中定义

Paste_Image.png
那个$.showIndicator的效果其实就是这个

Paste_Image.png

  • 之前有一点没有提到,就是sui需要执行一个init()方法才能有上面的效果,而且是在ready方法中init,这是基本配置,所以要在入口文件main.vue加个.page,否则会报找不到id的错误

Paste_Image.png

Paste_Image.png

  • 现在的效果是下拉可以增加一个.card,但是切换路由的时候再回到页面就不见了。所以要绑一个变量存数据。今天这个自定义指令挺难理解的,先keep一份代码吧

Paste_Image.png

step7解决(3)中刷新在两个tab中都添加元素的问题

  • (3)中的情况是无论在动态tab下拉刷新还是在前端tab下拉刷新同时会增加dom元素

  • 主要是因为这一句话,这里的this是指向整个template的根目录,这也正好可以解释片段实例为什么获取不到元素了

    • main.vue
      Paste_Image.png

  • 那就必须是添加到当前指令下的.card-container,所以要获取到当前指令元素,可以在自定义指令中获得并传参

    • PullToRefresh.js

Paste_Image.png
Paste_Image.png

  • 现在可以main.vue 中的函数中获取这个参数,利用它获取当前元素的.card-container

Paste_Image.png

Paste_Image.png

step8不再操作dom只关心数据

  • vue是为数据而生的,不该过多的操作dom,而之前的所有刷新都是append了一次元素

  • 为了模拟有真实是的数据时的情况,把将定义两个数组将分别对应两个tab里面的数组task1,task2,并在每次下拉时push进去,这时候我们就不在需要操作dom元素了,只需要关心data的变化

  • 为了让存放在不同的数组中在自定义指令的元素上加入了这些属性

Paste_Image.png

  • 然后在pullToRefresh.js中将它设置到自定义属性中,当然要记得在params中传入特性,(这里我总感觉我这方法有点麻烦,如果在指令中传参我还没有明确,希望有知道的大神指点一二)

Paste_Image.png

  • 每次下拉函数都会push一个数组

Paste_Image.png

  • html中用v-for输出数组

Paste_Image.png

  • 基本的home.vue已经全部完成了

Paste_Image.png

  • 当我很开心的以为结束了今天的任务的时候,我切换一下底部菜单,然后再回到home tab时,整个人都不好了,刚刚下拉的数据全部没了,回到刚开始的页面

Paste_Image.png

  • 我本想着要自定义方法来存储,可是参考资料里面是并不用这么麻烦的,寻寻觅觅了很久很久,终于遇到了神奇的它---keep-alive

    • 先来了解一下动态组件
      Paste_Image.png

      • 当用户关闭component时,将该component卸载,再次打开时重新加载。

Paste_Image.png

    • 这个项目里在router-view标签中加上keep-live就可以缓存组件了

      • 扩展:根据官方的这个案例,通过:is和keep-alive可以实现随意切换是否缓存这个组件

    • 现在无论怎么点tab键都能保留下拉刷新的那几个内容了

    step9 个人中心布局

    • 前面step1到step8已经将首页和基本菜单栏完成,现在先做个人中心页也就是底部菜单中‘我’这个tab*

    • 首页点击去看详情和写新动态加到列表中放到后面再完成

    • 总体预览

    Paste_Image.png

    • 之前做过一个HeaderTab的组件,现在可以拿来用了

    Paste_Image.png

    - 如果你遇到底部切换页面可以正常显示,但是直接刷新浏览器却不起作用,那么你看看是不是没有$init()
    • 第二部分是图片,sui中1rem等于20px

    • 第三部分是个人信息,为了模拟以后从后台获取数据,将这部分抽离出来定义一个UserDetail组件并将数据存储在一个数据中,这个数组存放在me.vue中

    Paste_Image.png

    • 子父之间的通信:虽然之前也有提到过,但是之前都是在父组件上传递一个字符串,当时的栗子是这样的

    Paste_Image.png

    • 然后在子组件的props中注册status就可以直接用了,但是今天要传递的是一个数组data 如果直接写成data="userData" vue是不能识别你这个是字符串还是变量数组的

    • 所以这里要用v-bind:user-data='userData',这里还有一个坑,如果你写的是v-bind:userData='userData'就会报错了。

    Paste_Image.png

    Paste_Image.png

    • 组件中的样式我定义的比较随便,直接用标签来定义样式,因为这里加了scope只作用在当前组件,vue会自动处理加一串标记数字,这样再也不用为命名担心了
      Paste_Image.png

    Paste_Image.png

    • 第三部分的tab与第二部分类似,也是抽离一个组件,这里定义数组将内容与结构分离,用v-for循环输出结构,如果要改内容直接改数组,不用在一大堆的html结构里面去找文字了

    //这里之所以数组套数组是因为考虑到sui的.row样式结构需要
    //path就是路由的路径了,都要添加到router.js文件中
    lists :[
              [{
                title:'动态',
                icon:'icon-app',
                path:'/me/moment'
              },
              {
                title:'访客',
                icon:'icon-friends',
                path:'/me/friends'  
              }],
              [{
                title:'文章',
                icon:'icon-menu',
                path:'/me/articles'  
              },
              {
                title:'最佳实现',
                icon:'icon-browser',
                path:'/me/practice'  
              }],
              [{
                title:'阅读',
                icon:'icon-code',
                path:'/me/read'  
              },
              {
                title:'收藏列表',
                icon:'icon-star',
                path:'/me/love'  
              }]
          ]
       <user-refer v-bind:lists='lists'>
          
       </user-refer>
        <div v-for="list in lists" class="row">
               <div class="col-50">
                 <a class="tab-item" v-link="{ path: list[0].path}">
                <!-- 这里不同于首页的tab标签切换,所以不需要replace这个参数,让路由可以后退 -->
                  <span class="icon"  v-bind:class="list[0].icon"></span><br>
                  <span class="tab-label">{{list[0].title}}</span>
                </a>
              </div>
              <div class="col-50">
                 <a class="tab-item" v-link="{ path: list[1].path}">
                  <span class="icon"  v-bind:class="list[1].icon"></span><br>
                  <span class="tab-label">{{list[1].title}}</span>
                </a>
              </div>
            </div> 
        </div>
    • 第三部分的tab键点击进去的详情还是用路由实现,下面这一部分可以用组件封装,路由不用加replace=true参数,返回按钮加上个人中心的路由,详细内容无非就是下拉列表,大致跟首页一样,可以看step1-step8

    Paste_Image.png

    Paste_Image.png

    step10 利用vue-resource 获取数据

    • 将首页的数据变为动态获取的

    • 首先定义一个json文件,注意格式,否则解析不了会为null,建议可以把自己的json文件在线检测一下

      • news.json

    Paste_Image.png

    • 这个文件放在static/data,要在app.js中做相应的配置

      import VueResource from 'vue-resource'

    Paste_Image.png

    • 参数解释:

    • proces.env.NODE_ENV 在vue-cli搭建的时候bulid文件里面配置好的可以去研究一下

    • Vue.http.options.emulateJSON = true;

    If your web server can't handle requests encoded as application/json , you can enable the emulateJSON option. This will send the request as application/x-www-form-urlencoded MIME type, as if from an normal HTML form.
    --- 给你个眼神自己领悟吧,相信我的翻译还不如自己百度,哈哈

    • 然后使用$http去获取数据,返回值response,利用$set设置 Vue 实例的属性值,也就是之前的假数据task1

    Paste_Image.png

    • task1数据改变就会引起视图的变化,现在效果是这样的,那么改变一下以前下拉push进去的假数据

    Paste_Image.png

    Paste_Image.png

    • 先每次刷新都能获取一次数据了,基本模拟了后端传输数据的效果

    点击进入详情页面

    • 希望每个列表都能点击去看文章详情,那么要监听一下click事件,在子组件中,找到CardCon.vue

      <div class="card" v-on:click="goDetail">
              <slot></slot>   
        </div>
    • 定义goDetail方法,并且使用dispatch冒泡的父组件,传入当前列表的索引,方便获取数据

           methods:{
                 goDetail (){
                    this.$dispatch('GoDetailRouter', $(this.$el).index())
                  }
            }
    • $emit $dispatch $brocast
      Paste_Image.png

    Paste_Image.png

    • 在父组件中的events中监听子组件的事件,msg就是传入的参数,利用路由go方法跳转到详情页

    events :{//监听子组件
          GoDetailRouter (msg){
               router.go({
                name: 'detail', 
                params: {
                  Lcontent:encodeURIComponent(this.$data.task1[msg].Lcontent)
    
                }
              });
          }
      }
    router.js
     '/home/detail/:Lcontent': {
                name:'detail',
                  component (resolve) {
                require(['./views/detail'], resolve)
              }
            }
    • 这里的Lcontent是传入的参数,我这里传入的是详情的文字,实际开发一般是传一个id然后从后台获取相应的数据

    • 增加detail.vue,要了解router钩子函数才能了解下面这一段代码

    route: {
              data: function (transition) {
    
               this.$set('Lcontent',decodeURIComponent(this.$route.params.Lcontent)); 
              }
            }

    切换钩子子函数

    • 由于如果我在ready中去获取参数,因为keep-alive将组件缓存所以只会执行一次,但是router的data切换钩子会在每次路由切换的时候调用,保证了当前的参数是最新的

    Paste_Image.png

    • 今天用到的关于router 和 resource,网上的资源真的很少,大多是直接翻译了文档,连个demo都没有,各种摸索去试,心好累,大家觉得有帮助的,请在github上给个小星星吧,https://github.com/sally2015/...


    Obeing
    665 声望108 粉丝

    努力地成为一只小牛