vue+webpack项目实战

概述

--

  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 配置路由

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

原文地址:https://www.cnblogs.com/axl234/p/5884282.html