vue for webapp 中的总结&&填坑

来源:互联网 发布:java的三层架构是什么 编辑:程序博客网 时间:2024/06/11 12:50

GitHub:https://github.com/liu9183/vue-for-sellApp

历时三个星期,在教程和文档的抽丝剥茧中敲完,中间体会以此为记

本项目使用的vue-cli脚手架,安装过程简单交代, 如想详细了解,请参考官方文档

1.新建工程文件vue-for-sellapp

2.cd vue-for-sellapp

3.npm install vue-cli

4.vue init webpack  //项目基于webpack模板

5.npm install  //安装依赖

6.npm run dev //启动服务器,默认端口8080


现在挑几个重点难点说一说

一、vue-router

vuejs将多个组件组合为一个应用,却没有解决不同页面的切换问题,也就是说同一个页面区域内,可能需要根据用户的手动切换进行不同的渲染,展示不同的内容。

vue-router提供了很好的路由指向功能,看如下代码

<div class="tab">       <div class="tab-item">       <router-link to="/goods">商品</router-link>       </div>       <div class="tab-item">            <router-link to="/ratings">评论</router-link>       </div>       <div class="tab-item">       <router-link to="/seller">商家</router-link>       </div>    </div>        <router-view :seller="seller"></router-view> <!--路由匹配到的组件将自此处渲染 -->    

代码中的<router-link to=""></router-link>标签提供了路由指向功能,当我们点击“商品”时便会把定义好的goods组件渲染到 

<router-view :seller="seller"></router-view>

之中,同时将seller对象传入到匹配到的组件中(这里称其为子组件 ),在此子组件中通过props接收到父组件传入的seller对象,一个很巧妙的数据流动方式。

<router-link to="/goods">商品</router-link>默认的渲染结果是<a href="/goods">商品<a>

当我们点击了“商品”时, <router-link> 对应的路由匹配成功,将自动设置 class 属性值 .router-link-active,我们便可通过此类名为点击事件添加样式。

到此为止,我们都在讲vue-router的原理和使用,那么它是如何引入到项目中的呢,我们看项目的入口文件main.js

import Vue from 'vue';import VueRouter from 'vue-router'; //首先引入依赖,命名为VueRouterimport VueResource from 'vue-resource';import App from './App.vue';import goods from './components/goods/goods.vue';import ratings from './components/ratings/ratings.vue';import seller from './components/seller/seller.vue';Vue.use(VueRouter);//插件注册Vue.use(VueResource);const router = new VueRouter({routes:[  {  path:'/goods',//为每个组件定义路由路径,使用此路径便可以调用对应组件    component:goods  },  {  path:'/ratings',    component:ratings  },  {  path:'/seller',    component:seller  }]});let sellapp=new Vue({el:'#app',template: '<App/>',components:{ App },router    //为实例注入路由,从而让整个应用都有路由功能})
在这里有的开发者对Vue.use()函数抱有很多幻想,这里推荐一篇文章Vue.use源码分析

至于vue-resource先不做讨论

 二、生命周期钩子


生命周期分八个状态对应八个函数,自始至终分别是

1.beforeCreate()

2.created()-------实例已经被创建完成并调用,数据观测(data observer),属性和方法的运算,watch/event事件回调已准备就绪,但是实例尚未挂载,没有进行DOM树构建 和渲染

3.beforeMount()

4.mounted()-------el 被新创建的vm.$el 替换,组件挂载成功

5.beforeUpdate()-------数据更新时调用,此时还没发生DOM重新渲染以及打补丁,可以在这个钩子中进一步更改状态,这不会触发附加的重渲染过程

6.updated()-------数据更改导致DOM重新渲染和打补丁,在这之后调用该钩子,当这个钩子被调用时,组件DOM已经更新,所以可以执行依赖更新后的DOM节点的操作

7.beforeDestroy()

8.destroyed()

三、深入响应式原理

基于本项目所涉及,这里只提一下vm.$nextTick()

看如下代码

_initScroll() {this.$nextTick(() => {if(!this.scroll) {this.scroll = new BScroll(this.$refs.seller, { click: true});} else {this.scroll.refresh();}})}
将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,然后等待 DOM 更新。它跟全局方法 Vue.nextTick 一样,不同的是回调的this 自动绑定到调用它的实例上。

此处有坑,看下面代码(seller.vue组件中)

   watch: {'seller' () {this._initScroll();//console.log("watch执行了"); //console.log(this.$refs.info);//console.log(this.seller.infos);//this._initPics();}},mounted() {this._initScroll();  //console.log("mounted执行了");//console.log(this.$refs.info);//console.log(this.seller.infos);//this._initPics();}
_initScroll()

    _initScroll() {this.$nextTick(() => { if(!this.scroll) {this.scroll = new BScroll(this.$refs.seller, { click: true});} else {this.scroll.refresh();}})}
很明显,我要在seller组件中实现页面滚动,为什么需要mounted钩子以及watch监视seller对象共用呢,宝宝心里苦啊,原本以为vue生命周期中mounted钩子调用阶段页面DOM早已构建渲染好,直接滑动便可,确实,我在主页面http://localhost:8080/#/到点击进入http://localhost:8080/#/seller中确实实现了seller页的滚动,可当我再次刷新seller页面(http://localhost:8080/#/seller)时,页面又不能滚动了……

查看控制台输出,发现this.seller.infos获取不到,也就是seller对象并没有传进来,但是this.$refs.info却获取到了,不明白为什么seller对象得不到页面数据渲染却成功了。

然后经过一番纠结困恼,我添加了seller对象监视,在监视到seller对象变化时,再次调用_initScroll(),如此这般,问题竟然奇迹般解决了,不管是seller页面刷新还是从主页面进入seller页,滚动都没问题,我尝试将this.$nextTick方法弃用,发现又不行了……

总结一下:刷新seller页面时,seller对象异步获取,在还没有获取成功便执行了_initScroll(),此时页面依赖seller对象提供的数据撑开,子元素没有超出父元素的界限,自然不会触发滚动,而使用watch监控seller变化,在seller变化完成后又执行_initSrcoll(),便可以触发滚动

四、父子组件通信

我们知道子组件想获取父组件的对象,可以用props进行传递,那么父组件要获取子组件传递的信息该如何实现呢??

本项目为例,在ratings组件中嵌入了ratingselect子组件,而子组件获取用户的点击信息要传递到父组件进行条件展示,比如客户点击了“满意”按钮,那么父组件要响应展示评论中好评的部分。

看下面代码

<div class="rating-type"><span @click="select(2,$event)" class="block positive" :class="{'active':selectType===2}">{{desc.all}}<span          class="count">{{ratings.length}}</span></span><span @click="select(0,$event)" class="block positive" :class="{'active':selectType===0}">{{desc.positive}}<span          class="count">{{positives.length}}</span></span><span @click="select(1,$event)" class="block negative" :class="{'active':selectType===1}">{{desc.negative}}<span          class="count">{{negatives.length}}</span></span></div>

select方法

select(type, event) {if(!event._constructed) {return;}this.$emit('select', type);}
vue为我们提供了$emit方法,将type绑定到指定的方法(第一个参数指定方法名)上,在这里是select

好了,我们来看父组件如何拿到select方法。

<ratingselect :select-type="selectType" :only-content="onlyContent" :ratings="ratings" @select="select" @toggle="toggle"></ratingselect>

很清楚,在ratingselect组件挂载到父组件ratings时就已经通过v-on传了进来,@select便是将子组件的select方法传到父组件,后面的字符串便是在父组件中对应的函数名,这里还是用select。

最后,看一下在父组件中的函数使用

select(type){this.selectType = type;this.$nextTick(() => {this.scroll.refresh();});}

五、web存储

参见我的另一篇博客web storage

六、解析url

项目在部署使用过程中,难免遇到与个人信息绑定的东西,比如id为1234的用户点击收藏后,再次刷新http://localhost:8080/?id=1234#/seller,则依然显示已收藏,这就需要计算机得到用户id,并且缓存相关信息,再次刷新后读取缓存

那么如何从URL中得到用户信息呢

见我一篇博文解析url,生成对应JSON对象

七、初涉Express框架

在本项目中express来获取data.json中的数据,并向客户端传送JSON响应


我已在另一篇博客比较细致地介绍了Express的基本原理和使用方法,请参见——Express框架学习笔记

未完待续……………………………………………………




1 0
原创粉丝点击