vue+webpack项目实战

概述

--javascript

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

  2. 打包工具 webpackhtml

  3. 依赖环境 node.js前端


start 安装vue开发的模板

# 全局安装 vue-clivue

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

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

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

    • node_modules为依赖的模块git

    • src放置组件和入口文件github

    • static放置静态资源文件

    • index.html文件入口

  2. webpack中的一些解释:

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

    • 这里不使用elint检查代码


step1 配置路由

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

如今的效果是:

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

*上截图解释这个参数,因此要加html中加如zepto的连接,而后在其余地方就能够引用了* 

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

router.js 

app.js增长的代码

如今页面仍是和以前同样没有变化,基本框架和路由搭建完成,而后就能够开始封装组件


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组件化的概念,毕竟重复了四次的tab代码,全部这里要用slot进行内容分发

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

step4 slot的使用

<slot> 就是外部调用时,标签中的内容。若是外部调用时没有提供内容的话,那么它就会使用本身默认提供的内容,很是方便。

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

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

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

  • 先看如今的效果

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

  • 如今能够把里面的item元素也弄成一个BarItem.vue组件

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

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

先上代码BarItem.vue

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

  • main.vue如今的代码

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

  • 如今的效果仍是像以前同样,可是已经实现组件化

step5 HomeTab 路由切换

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

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

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

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

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

  • 想要达到这种效果

  • homeTab这部分也是能够提取出组件做为各个tab的头部

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

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

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

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里

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

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

  • 千万要注意一个概念叫作片断实例

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

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

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

  • 如今能够去书写你下拉刷新是要执行的函数了,在methods方法中定义

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

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

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

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

  • (3)中的状况是不管在动态tab下拉刷新仍是在前端tab下拉刷新同时会增长dom元素

  • 主要是由于这一句话,这里的this是指向整个template的根目录,这也正好能够解释片断实例为何获取不到元素了

    • main.vue

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

    • PullToRefresh.js

  • 如今能够main.vue 中的函数中获取这个参数,利用它获取当前元素的.card-container

step8再也不操做dom只关心数据

  • vue是为数据而生的,不应过多的操做dom,而以前的全部刷新都是append了一次元素

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

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

  • 而后在pullToRefresh.js中将它设置到自定义属性中,固然要记得在params中传入特性,(这里我总感受我这方法有点麻烦,若是在指令中传参我尚未明确,但愿有知道的大神指点一二)

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

  • html中用v-for输出数组

  • 基本的home.vue已经所有完成了

  • 当我很开心的觉得结束了今天的任务的时候,我切换一下底部菜单,而后再回到home tab时,整我的都很差了,刚刚下拉的数据所有没了,回到刚开始的页面

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

    • 先来了解一下动态组件

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

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

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

  • 如今不管怎么点tab键都能保留下拉刷新的那几个内容了

step9 我的中心布局

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

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

  • 整体预览

  • 以前作过一个HeaderTab的组件,如今能够拿来用了

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

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

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

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

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

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

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

step10 利用vue-resource 获取数据

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

  • 首先定义一个json文件,注意格式,不然解析不了会为null,建议能够把本身的json文件在线检测一下

    • news.json

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

import VueResource from 'vue-resource'

  • 参数解释:

  • 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

  • task1数据改变就会引发视图的变化,如今效果是这样的,那么改变一下之前下拉push进去的假数据

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

点击进入详情页面

  • 但愿每一个列表都能点击去看文章详情,那么要监听一下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

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

相关文章
相关标签/搜索