概述

--

  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文件入口

  1. webpack中的一些解释:

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

  • 这里不使用elint检查代码


step1 配置路由

vue+webpack项目实战_ico

  • 这里使用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入口

现在的效果是:

vue+webpack项目实战_数据_02

-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'
},

vue+webpack项目实战_ico_03

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

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

router.js 

vue+webpack项目实战_ico_04

app.js增加的代码

vue+webpack项目实战_数据_05

vue+webpack项目实战_ico_06

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


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';

vue+webpack项目实战_数组_07

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

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

vue+webpack项目实战_数组_08

step4 slot的使用


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


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

vue+webpack项目实战_数组_09

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

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

vue+webpack项目实战_数据_10

  • 先看现在的效果

vue+webpack项目实战_数组_11

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

vue+webpack项目实战_数组_12

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

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


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


先上代码BarItem.vue

vue+webpack项目实战_数组_13

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

  • main.vue现在的代码

vue+webpack项目实战_数组_14

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

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

step5 HomeTab 路由切换

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

vue+webpack项目实战_数组_15

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


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


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

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

vue+webpack项目实战_数组_16

  • 想要达到这种效果

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

  • 赞一下vue的错误提示,一开始死活显示不了,这错误提示还是很明显的
    vue+webpack项目实战_数据_17

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

  • 还有这个提示,注册了变量没
    vue+webpack项目实战_数据_18

step6 HomeTab 内容+下拉刷新

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

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


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


pullToRefresh.js


用到的钩子函数:
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里
    vue+webpack项目实战_数据_19

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

vue+webpack项目实战_ico_20

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

vue+webpack项目实战_ico_21

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

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

vue+webpack项目实战_ico_22

vue+webpack项目实战_ico_23

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

vue+webpack项目实战_数据_24

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

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

vue+webpack项目实战_数据_25那个$.showIndicator的效果其实就是这个

vue+webpack项目实战_数据_26

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

vue+webpack项目实战_数据_27

vue+webpack项目实战_数据_28

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

vue+webpack项目实战_数据_29

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

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

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

    • main.vuevue+webpack项目实战_ico_30

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

    • PullToRefresh.js

vue+webpack项目实战_数据_31vue+webpack项目实战_数据_32

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

vue+webpack项目实战_ico_33

vue+webpack项目实战_数据_34

step8不再操作dom只关心数据

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

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

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

vue+webpack项目实战_数组_35

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

vue+webpack项目实战_ico_36

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

vue+webpack项目实战_数组_37

  • html中用v-for输出数组

vue+webpack项目实战_数据_38

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

vue+webpack项目实战_数据_39

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

vue+webpack项目实战_数据_40

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

    • 先来了解一下动态组件
      vue+webpack项目实战_ico_41

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

vue+webpack项目实战_ico_42

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

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

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

step9 个人中心布局

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

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

  • 总体预览

vue+webpack项目实战_ico_43

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

vue+webpack项目实战_数组_44

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

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

vue+webpack项目实战_数据_45

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

vue+webpack项目实战_数组_46

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

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

vue+webpack项目实战_数据_47

vue+webpack项目实战_数据_48

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

vue+webpack项目实战_数据_50

  • 第三部分的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

vue+webpack项目实战_数据_51

vue+webpack项目实战_数据_52

step10 利用vue-resource 获取数据

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

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

  • news.json

vue+webpack项目实战_数据_53

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

import VueResource from 'vue-resource'

vue+webpack项目实战_数组_54

  • 参数解释:

  • 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

vue+webpack项目实战_ico_55

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

vue+webpack项目实战_数组_56

vue+webpack项目实战_数组_57

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

点击进入详情页面

  • 希望每个列表都能点击去看文章详情,那么要监听一下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
    vue+webpack项目实战_数据_58

vue+webpack项目实战_数据_59

  • 在父组件中的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切换钩子会在每次路由切换的时候调用,保证了当前的参数是最新的

vue+webpack项目实战_数据_60