vue-router

router这里踩的坑主要是组件的重用。构建单页面大型应用的话,肯定要开启组件的缓存的,因为一般会要求后退的时候不要重新加载页面,而且要记住原始的滚动位置。
首先,引入router-view的地方要加上keep-alive

<router-view keep-alive></router-view>

然后开启html5 history模式,并开启位置纪录

const router = new Router({
history: true, // use history=false when testing
saveScrollPosition: true
})

开启keep-alive以后,当要求一个组件的内容发生变化时,比如 漫画详情页面是一个路由带参数的组件,当参数变化时,router会重用这个组件,而不是重新请求数据,这显然是不符合要求的,所以正确的姿势是:
首先,用一个字段保存这个路由参数,
用router的钩子函数data获取路由变化参数,保存到字段里


route:{
data: function(transition){
this.bookId = transition.to.params.id;
}
}

写一个watcher来拉取数据并填充模版,因为在data钩子函数中,我们已经修改了相应字段,所以当路由参数更改时会直接触发这个watcher

watch: {
'bookId' : function(val){
//do something
}
}

如果是多个参数的,可以把这些参数放到一个对象里,watcher采用深监测

route:{
data: function(transition){
this.watcher.type = transition.to.params.type;
this.watcher.id = transition.to.query.id;
}
},
watch : {
'watcher' : {
handler: function(val){
//do something
window.scrollTo(0,0);// 不使用缓存时,不使用记录好的用户位置,滑倒顶部
},
deep: true
}
}

一开始没有用这种方法出了很多的bug,改了以后,路由和缓存方面的逻辑瞬间就变得清晰了,组件的切换也更加流畅了。
第二个坑就是关于缓存页面浏览位置的纪录,router是通过html5 history的pushState来纪录当前滚动位置的,切换路由的时候,把当前位置push进去,用户后退时,会触发onpopstate事件,这个时候再把位置取出来并滚动到指定位置,但是!某些浏览器本身也设置了一些奇怪的位置滚动,vue-router的滚动就失效了,所以需要延迟执行一下

   window.addEventListener("popstate",function(e){
setTimeout(()=>{
window.scrollTo(0,e.state.pos.y);//通过打log,发现了位置纪录在这个变量里了
},300)
},false);

然而,浏览器只能记录一个位置,所以会有这样的情况: 从m.cm233.com 到 m.cm233.com/book,再返回到m.cm233.com,这时浏览器跳到了当时记录的位置,但是再前进到/book时,浏览器还是会停在首页的那个位置上,这个bug暂时还没有解决,好在用户场景不是很多。所以告诉我们,子页面路由参数变化的时候,要把滚动条人工弄到最上面,要不然就会滚动到入口页面的浏览位置。也就是watcher里还要加一句如上的滚动。

页面标题也是要手动更改的,所以每个页面要放一个专门的title变量存一下,然后在data钩子函数(用于组件缓存时) 和 路由参数的watcher(用于组件更新时) 里 都改变title

route:{
data: function(transition){
this.title = 'hiahia';
document.title = this.title;
}
},
watch : {
'id' : function(val){
this.title = 'hiahia';
document.title = this.title;
}
}

通常页面的标题不是固定的,用变量存储title,主要是为了记住上一次组件被用的时候的title,以便于重用的时候更换。

然而,ios微信不会监测document.title的变化,所以要写一个专门针对它的hack,通过创建iframe

//全局函数
window.isWeiXin = function(){
var ua = window.navigator.userAgent.toLowerCase();
if(ua.match(/MicroMessenger/i) == 'micromessenger'){
return true;
}else{
return false;
}
} window.weiXinChange = function(title){
if(window.isWeiXin()){
document.title = title;
var iframe = document.createElement('iframe');
iframe.src = './favicon.ico';
iframe.style.display = 'none';
iframe.onload = function(){
setTimeout(function() {
document.body.removeChild(iframe);
}, 0);
}
document.body.appendChild(iframe);
}
} //组件中 route:{
data: function(transition){
this.title = '墨瞳漫画';
document.title = this.title;
window.weiXinChange(this.title);
}
},
watch : {
'id' : function(val){
this.title = '墨瞳漫画';
document.title = this.title;
window.weiXinChange(this.title);
}
}

vue-infinite-scroll (directive)

(为什么不自己写!)

组件地址 https://github.com/ElemeFE/vue-infinite-scroll 饿了么出品

使用方法
main.js

import Scroll from 'vue-infinite-scroll'
Vue.use(Scroll)

组件中

  <dl v-infinite-scroll="loadMore()" infinite-scroll-disabled="busy" infinite-scroll-distance="7">
<template v-for="item in list">
<dd class="page-item">
</dd>
</template>
</dl>

其中busy这个变量比较重要,他控制着这个指令是否继续执行,当没有下一页数据的时候,应该把busy置为true来关闭滚动加载。正在读取下一页数据时,要先把busy置为true,数据返回时在置为false

loadmore(){
this.busy = true;
someApi.someFunction().then((data) => {this.busy = false;})
}

但是这个组件在路由切换的时候会出问题,routerView被移除时,组件会触发加载(大概是因为页面高度突然塌陷),而且会一直加载到我们自己设置的停止条件(busy=true)。所以离开页面的时候,需要在路由的deactivate钩子函数里把滚动关掉,再次进入页面的时候再开启(路由无变化在data钩子函数里开启,有变化的话在watcher里开启,如果不需要在路由改变时向子组件延时传递参数也可以都在data钩子函数里开启)

route:{
deactivate: function(transition){
this.busy = true;
transition.next();
},
data: function(transition){
if(){
this.busy = false;
}//这里输入组件路由参数没有变化的条件
}
}

lazyload

(为什么不自己写!)
网上找了几个lazyload的组件,都不太好使,就自己改了一个,是改了一个,原组件叫vue-lazyload, 毛病还挺多的,写这个组件的人估计没有真正在大项目中用过就匆匆发布在npm了,es6版本也写的不伦不类的 - -,不过还是很厉害,自己写的话毛病肯定会更多。我改后的放在https://github.com/Ganother/blog/blob/master/lazyload.js了,是个较为稳定的版本。其中过渡动画写在img-loaded这个class里

/*简单的透明度渐入,图片加载完成后会删掉这个class,以防router切换缓存页面时再次引起动画*/
.img-loaded {
animation: loaded .2s ease-in-out;
}
@keyframes loaded {
0%{
opacity: 0;
}
100%{
opacity: 1;
}
}
let loadingJpg = require('assets/loading.jpg');//这里引入一张loading图,会被转成base64
Vue.use(VueLazyload, {
preLoad: 1.3, //图片顶端距窗口顶端1.3个屏幕高度时开始加载
loading: loadingJpg,
error: loadingJpg
})

自适应的图片:如果服务端传过来的图片带了宽高信息,可以在img外层包一个class为img-bar的元素,图片过来的时候先设置一个min-height为响应高,组件在图片加载后会自动取掉这个min-height。这样可以防止loading图和图片大小不一样引起的页面跳动继而导致的加载图片时机错误。

vue-resource

跨域时,会先发送一个空的options请求来查看接口是不是支持跨域,再发送一次真实请求。还不是很了解这种方式的好处,当接口较多时,请求数量多了一倍也是有点尴尬的,所以要设置一下。而且如果接口每次都打印空参数的log的话。。。嗯。

Vue.http.options.emulateJSON = true;
Vue.http.options.headers={
'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'
};

结构目录

vue-cli直接构建的,src里的目录如下

api 放一些ajax请求接口的函数
assets 放一些静态资源,图片,公共sass
directives 放一些指令js,比如改动后的lazyload
pages 页面入口组件,用在router中
components 小组件们
vuex vuex
app.vue
main.js

另外,可以修改下生成的静态文件,vue-cli默认声称的静态文件时间戳是加在文件名上的,如app.fefdfd7s8f78sd7.js,这样版本迭代很快后会使服务器上积压过多无用文件,我们希望这样加时间戳 app.js?t=32j32ih4u32h 所以改一下webpack.prod.conf.js就好了,如下

  output: {
path: config.build.assetsRoot,
filename: utils.assetsPath('js/[name].js?t=[chunkhash]'),
chunkFilename: utils.assetsPath('js/[id].js')
} new ExtractTextPlugin(utils.assetsPath('css/[name].css?t=[contenthash]')),

最新文章

  1. sql报句柄无效。 (异常来自 HRESULT:0x80070006 (E_HANDLE))
  2. jquery 模糊查询下拉框
  3. 组合数(codevs 1631)
  4. XCode5 真机调试及发布应用
  5. (笔记)angular 的根据后台StateCode本地显示指定文案
  6. java的变量
  7. Line Search and Quasi-Newton Methods
  8. ios code style
  9. Android读取url图片保存及文件读取
  10. RSA算法原理(二)
  11. h.264 mvp求解过程
  12. Entity Framework 的事务
  13. 【转】十款让 Web 前端开发人员更轻松的实用工具
  14. 【知识整理】这可能是最好的RxJava 2.x 入门教程(五)
  15. vue class与style 绑定详解——小白速会
  16. Solidity构造函数和析构函数
  17. win7电脑蓝牙 耳机
  18. HBase电子书
  19. [C++]最小生成元 (Digit Generator, ACM/ICPC Seoul 2005, UVa1583)
  20. responseHandler

热门文章

  1. JDBC的开发步骤
  2. ALGEBRA-前言
  3. 2020-04-08:谈一下IOC底层原理
  4. 《JavaScript语言入门教程》记录整理:面向对象
  5. GaussDB基本操作
  6. 【HNOI2015】菜肴制作 - 拓扑排序+贪心
  7. Vue管理系统前端系列六动态路由-权限管理实现
  8. 结合Excel批量操作网页,模拟登陆
  9. 四则运算(C语言实现)
  10. 微众银行FATE联邦学习框架