vue.jsすげーな。
中国でめっちゃ発達しているし、コードもきれい。
レベル上がってきたな。。。
ということで今回は無限スクロールさせちゃいましょう。
そこら辺に書いてあるコードだといろいろエラーが出たり、
動かなかったりする。
さらに簡単に書いてみた。
タブ版ね。
<template>
<div>
<el-tabs type="card" @tab-click="handleClick" :stretch="true" v-model="activeTab">
<el-tab-pane label="new" name="first" :disabled="loading">
<span slot="label"><i class="fas fa-magic"></i> 新着占い</span>
</el-tab-pane>
<el-tab-pane label="ninki" name="second" :disabled="loading">
<span slot="label"><i class="fas fa-crown"></i> 人気占い</span>
</el-tab-pane>
</el-tabs>
<div v-for="(v,key) in list">
{{v.id}} : {{v.message}}
</div>
<infinite-loading force-use-infinite-wrapper="true" @infinite="onInfinite" ref="infiniteLoading" :distance="500" no-more="これ以上ない"></infinite-loading>
</div>
</template>
<script>
import axios from 'axios'
import InfiniteLoading from 'vue-infinite-loading'
Vue.use(InfiniteLoading, {
slots: {
noMore: 'すべて読み込みました', // you can pass a string value
noResults: '読み込み完了しています', // you can pass a string value
},
});
export default {
data() {
return {
activeTab:"first",
storageName:'contactShow',
targetUrl:'/contact/show/?kanteizumi=1&order=id',//パラメーターが無い時も必ず ? をつけて終わらせる
loading:false,//タブを disabled にする
query:'',
nextUrl:'',
list:[],//ここに取得したデータが入る
InfiniteLoadingOption: {
total:0,//データが合計いくつあるか
page:1,
nextPage:1,
lastPage:10,
}
};
},
created () {
},
mounted() {
if(this.$route.query.page){
if (sessionStorage.getItem(this.storageName + '_list')) {
this.readPageStorage();
}
} else {
//他の画面を経由して来た場合は検索結果をリセット
this.clearStorage();
}
},
methods: {
//検索条件を組み立てる
makeSearch() {
//例えば、ここで targetUrl を元に 並び順などを 組み立てますか。
this.loading = true;
//選択されているタブに応じて取得先を切り替える
if(this.activeTab == 'first'){
this.targetUrl = '/contact/show/?kanteizumi=1&order=id';
}
if(this.activeTab == 'second'){
this.targetUrl = '/contact/show/?order=id';
}
for( var key in this.$route.query) {
if(key != 'page' && key != 'y'){
this.targetUrl += "&"+key+"="+this.$route.query[key];
this.query += "&"+key+"="+this.$route.query[key];
}
}
//検索条件を保存
this.nextUrl = this.targetUrl + "&page=" + this.InfiniteLoadingOption.nextPage;
},
//現在のページ情報を保存する
addPageStorage() {
sessionStorage.setItem(this.storageName + '_list',JSON.stringify(this.list));
sessionStorage.setItem(this.storageName + '_total',this.InfiniteLoadingOption.total);
sessionStorage.setItem(this.storageName + '_page',this.InfiniteLoadingOption.page);
sessionStorage.setItem(this.storageName + '_nextPage',this.InfiniteLoadingOption.nextPage);
sessionStorage.setItem(this.storageName + '_activeTab',this.activeTab);
},
//戻ってきた場合、ページ情報を復元する
readPageStorage() {
this.list = JSON.parse(sessionStorage.getItem(this.storageName + '_list'));
this.InfiniteLoadingOption.total = sessionStorage.getItem(this.storageName + '_total');
this.InfiniteLoadingOption.page = sessionStorage.getItem(this.storageName + '_page');
this.InfiniteLoadingOption.nextPage = sessionStorage.getItem(this.storageName + '_nextPage');
this.activeTab = sessionStorage.getItem(this.storageName + '_activeTab');
},
//session storage を 削除。 clear sessionStorage.clear() は使わない。
clearStorage() {
sessionStorage.removeItem(this.storageName + '_list');
sessionStorage.removeItem(this.storageName + '_total');
sessionStorage.removeItem(this.storageName + '_page');
sessionStorage.removeItem(this.storageName + '_nextPage');
sessionStorage.removeItem(this.storageName + '_activeTab');
},
onInfinite() {
this.makeSearch();
//
if (this.InfiniteLoadingOption.page < this.InfiniteLoadingOption.lastPage) {
axios.get(this.nextUrl).then(e => {
if (e.data.data.length) {
this.InfiniteLoadingOption.total = e.data.total;
//クエリーがある場合はクエリーを含み次のページのURLとする。
var idou = location.pathname + "?page=" + e.data.current_page + "&y=" + window.scrollY + this.query.replace(/\//g,"");
//現在のURLを置き換える
this.$router.replace({
path: idou
});
this.list = this.list.concat(e.data.data);
this.$refs.infiniteLoading.stateChanger.loaded();
this.InfiniteLoadingOption.nextPage++;
this.addPageStorage();
if (e.data.current_page >= e.data.last_page) {
console.log("最終ページまで行ったので処理しません");
this.$refs.infiniteLoading.stateChanger.complete();
}
} else {
console.log("データがないので終了します");
this.$refs.infiniteLoading.stateChanger.complete();
}
this.loading = false;
}).catch((error) => {
console.log("エラー");
});
} else {
this.$refs.infiniteLoading.stateChanger.complete();
console.log("全ページ読み込み完了");
}
},
handleClick(tab, event) {
this.InfiniteLoadingOption.nextPage = 1;
this.InfiniteLoadingOption.total = 0;
this.InfiniteLoadingOption.page = 1;
this.list = [];
// //現在のURLを置き換える
this.$router.push({
path: location.pathname
});
//
this.$refs.infiniteLoading.stateChanger.reset();
this.clearStorage();
}
}
}
</script>
追記: sessionStorage.clear() を使わずに
clearStorage(){
sessionStorage.removeItem('top_list');
sessionStorage.removeItem('top_total');
sessionStorage.removeItem('top_page');
sessionStorage.removeItem('top_nextPage');
sessionStorage.removeItem('top_activeTab');
},
上記のような感じで削除していこう。
でないとバグる。
<template>
<div>
<div v-for="(v,key) in list">
<a v-touch="$root.linkTo('/f/'+v.id+'/')">{{v.name}}</a>
</div>
<infinite-loading force-use-infinite-wrapper="true" @infinite="onInfinite" ref="infiniteLoading" :distance="500" no-more="これ以上ない" ></infinite-loading>
</div>
</template>
<script>
import axios from 'axios'
import InfiniteLoading from 'vue-infinite-loading';
Vue.use(InfiniteLoading, {
slots: {
noMore: 'すべて読み込みました', // you can pass a string value
noResults: '読み込み完了しています', // you can pass a string value
},
});
export default {
components: {
InfiniteLoading,
},
data () {
return {
list:[],//ここに取得したデータが入る
// targetUrl:'/fortune/show/?order=graph_type',
targetUrl:'/fortune/show/?',//条件がない場合は ? で終わらせておく
nextUrl:'',
InfiniteLoadingOption: {
total:0,//データが合計いくつあるか
page:1,
nextPage:1,
lastPage:10,
}
};
},
created () {
},
mounted() {
if(this.$route.query.page){
console.log("ページ遷移先から戻ってきました。");
if (sessionStorage.list) {
this.readPageStorage();
}
} else {
//他の画面を経由して来た場合は検索結果をリセット
sessionStorage.clear();
}
},
methods: {
//検索条件を組み立てる
makeSearch() {
//例えば、ここで targetUrl を元に 並び順などを 組み立てますか。
//検索条件を保存
this.nextUrl = this.targetUrl + "&page=" + this.InfiniteLoadingOption.nextPage;
},
//現在のページ情報を保存する
addPageStorage(){
sessionStorage.list = JSON.stringify(this.list);
sessionStorage.total = this.InfiniteLoadingOption.total;
sessionStorage.page = this.InfiniteLoadingOption.page;
sessionStorage.nextPage = this.InfiniteLoadingOption.nextPage;
},
//戻ってきた場合、ページ情報を復元する
readPageStorage(){
this.list = JSON.parse(sessionStorage.getItem('list'));
this.InfiniteLoadingOption.nextPage = sessionStorage.getItem('nextPage');
this.InfiniteLoadingOption.total = sessionStorage.getItem('total');
this.InfiniteLoadingOption.page = sessionStorage.getItem('page');
},
onInfinite() {
this.makeSearch();
if (this.InfiniteLoadingOption.page < this.InfiniteLoadingOption.lastPage) {
axios.get(this.nextUrl).then(e => {
if (e.data.data.length) {
this.InfiniteLoadingOption.total = e.data.total;
//現在のURLを置き換える
this.$router.replace({
path: location.pathname + "?page=" + e.data.current_page + "&y=" + window.scrollY
});
this.list = this.list.concat(e.data.data);
this.$refs.infiniteLoading.stateChanger.loaded();
this.InfiniteLoadingOption.nextPage++;
this.addPageStorage();
if(e.data.current_page >= e.data.last_page){
console.log("最終ページまで行ったので処理しません");
this.$refs.infiniteLoading.stateChanger.complete();
}
} else {
console.log("データがないので終了します");
this.$refs.infiniteLoading.stateChanger.complete();
}
}).catch((error) => {
console.log("エラー");
});
} else {
this.$refs.infiniteLoading.stateChanger.complete();
console.log("全ページ読み込み完了");
}
}
}
}
</script>
検索条件を変更するには
this.$refs.infiniteLoading.stateChanger.reset();
追記。
pulltoとかのライブラリと併用しようとしたがうまくいかない。
オプション変更したい場合
hoge.vue
// app.js でも import して、さらに このMugen.vue でもimportするのが味噌
import InfiniteLoading from 'vue-infinite-loading';
Vue.use(InfiniteLoading, {
slots: {
noMore: 'すべて読み込みました', // you can pass a string value
noResults: '読み込み完了しています', // you can pass a string value
},
});
参考
https://blog.csdn.net/zfangls/article/details/72727198
インストール
npm install vue-infinite-loading --save
app.js
import InfiniteLoading from 'vue-infinite-loading';
コード
elementui を使っているとバグるみたい。
読み込みまくって止まらない。ということで、
force-use-infinite-wrapper="true"
を忘れずに。
<template>
<div class="list-con">
<div class="list" v-for="(item,key) in list">
<span v-text="key+1"></span>
<p>
<a :href="item.url">{{item.title}}</a>
</p>
</div>
<infinite-loading force-use-infinite-wrapper="true" @infinite="onInfinite" ref="infiniteLoading" :distance="1000"></infinite-loading>
</div>
</template>
<script>
// app.js でも import して、さらに このMugen.vue でもimportするのが味噌
import InfiniteLoading from 'vue-infinite-loading';
//あえてページは指定しない
const api = 'https://hn.algolia.com/api/v1/search_by_date?tags=story&page=';
export default {
components: {
InfiniteLoading,
},
data() {
return {
list:[]
}
},
mounted: function() {
},
created(){
},
methods: {
onInfinite() {
let page = this.list.length / 20 + 1;
console.log(page);
axios.get(api+page)
.then(function (res) {
if (res.data.hits.length) {
//今まで読みこんだ配列に、新しく読み込んだ配列を結合
this.list = this.list.concat(res.data.hits);
// this.$router.push("/mugen?page=" + page);
console.log("現在のスクロール位置" + window.scrollY);
this.$router.push({
path: "/mugen?page=" + page + "&y=" + window.scrollY,
});
this.$refs.infiniteLoading.stateChanger.loaded();
//10ページ目まで読み込んだらそれ以上は読み込ませない
if (this.list.length / 20 === 10) {
console.log("大量にデータがあるのでここで終了しておきます。");
this.$refs.infiniteLoading.stateChanger.complete();
}
} else {
//データがなけりゃそれ以上読み込ませない
console.log("全て読み込ました");
this.$refs.infiniteLoading.stateChanger.complete();
}
// bind しておくことで、 axios、then 内で this や page を 使えるようにする
}.bind(this).bind(page))
.catch(function (error) {
console.log(error);
});
}
}
}
</script>
<style scoped>
.list{
overflow:hidden;
margin:20px 0;
}
span{
float: left;
margin-right: 5px;
}
p{
float: left;
}
</style>
おまけ
さらに早く読み込みたい!
ってときは distance を指定する。
<infinite-loading @infinite="onInfinite" ref="infiniteLoading" :distance="1000">
<span slot="no-more">すべて読み込みました</span>
</infinite-loading>
laravel との併用
laravel のコントローラーで
// ユーザー一覧を表示
public function show()
{
$res = User::query()->paginate(25);
return response()->json($res);
}
これでユーザー情報を 25人分表示せよと。
<template>
<div>
<div class="list-con">
<div class="list" v-for="(item,key) in list">
<p>
{{item.id}} : {{item.name}}
</p>
</div>
<infinite-loading @infinite="onInfinite" ref="infiniteLoading" :distance="500">
<span slot="no-more">すべて読み込みました</span>
</infinite-loading>
</div>
</div>
</template>
<script>
// app.js でも import して、さらに このMugen.vue でもimportするのが味噌
import InfiniteLoading from 'vue-infinite-loading';
export default {
components: {
InfiniteLoading,
},
data() {
return {
list:[],
nextUrl:'/user/show/'
}
},
mounted: function() {
},
created(){
},
methods: {
onInfinite() {
axios.get(this.nextUrl).then(e => {
// console.log(e.data);
// //メインデータ
// console.log(e.data.data);
//
//
// //次のページのURL
// console.log(e.data.next_page_url);
if (e.data.data.length) {
//今まで読みこんだ配列に、新しく読み込んだ配列を結合
this.list = this.list.concat(e.data.data);
this.$refs.infiniteLoading.stateChanger.loaded();
this.$router.push("/show?page=" + e.data.current_page);
this.nextUrl = e.data.next_page_url;
//現在のページ と 最後のページが同一なら終了
if (e.data.current_page == e.data.last_page) {
alert("読み込み完了しました");
this.$refs.infiniteLoading.stateChanger.complete();
}
} else {
//データがなけりゃそれ以上読み込ませない
this.$refs.infiniteLoading.stateChanger.complete();
}
// console.log(e.data);
}).catch((error) => {
console.log(error);
console.log("エラー");
});
}
}
}
</script>
<style scoped>
.list{
overflow:hidden;
margin:20px 0;
}
span{
float: left;
margin-right: 5px;
}
p{
float: left;
}
</style>
何が素晴らしいって、laravel の paginate は最後のページ数とかも計算してくれるので簡単にvue.jsと連携できるのがすごい。
セッションストレージでキャッシュをとっておく
<template>
<div>
<div style="margin-top: 45px;">
現在のページ数 {{page}}
</div>
<div class="list" v-for="(item,key) in list">
<span v-text="key+1"></span>
<p>
<a v-touch="$root.linkTo('/user/10049/')">{{item.title}}</a>
</p>
</div>
<infinite-loading force-use-infinite-wrapper="true" @infinite="onInfinite" ref="infiniteLoading" :distance="1000"></infinite-loading>
</div>
</template>
<script>
// app.js でも import して、さらに このMugen.vue でもimportするのが味噌
import InfiniteLoading from 'vue-infinite-loading';
//あえてページは指定しない
const api = 'https://hn.algolia.com/api/v1/search_by_date?tags=story&page=';
export default {
components: {
InfiniteLoading
},
data() {
return {
list:[],
page:1,
}
},
mounted() {
//ブラウザ戻るできた場合は必ず page があるので。
//これでバックしてきたと判断する
if(this.$route.query.page){
// console.log("ページあるで");
if (sessionStorage.list) {
this.list = JSON.parse(sessionStorage.getItem('list'));
console.log(this.list);
}
if (sessionStorage.page) {
this.page = sessionStorage.getItem('page');
console.log(this.page);
}
} else {
//他の画面を経由して来た場合は検索結果をリセット
sessionStorage.clear();
}
},
created(){
},
methods: {
onInfinite() {
if(this.page < 10){
this.page = this.list.length / 20 + 1;
sessionStorage.page = this.page;
axios.get(api+this.page).then(res => {
if (res.data.hits.length) {
//今まで読みこんだ配列に、新しく読み込んだ配列を結合
this.list = this.list.concat(res.data.hits);
sessionStorage.list = JSON.stringify(this.list);
this.$router.push({
path: "/mugen?page=" + this.page + "&y=" + window.scrollY,
});
this.$refs.infiniteLoading.stateChanger.loaded();
//10ページ目まで読み込んだらそれ以上は読み込ませない
if (this.page >= 10) {
console.log("大量にデータがあるのでここで終了しておきます。");
this.$refs.infiniteLoading.stateChanger.complete();
}
} else {
//データがなけりゃそれ以上読み込ませない
console.log("全て読み込ました");
this.$refs.infiniteLoading.stateChanger.complete();
}
console.log("成功");
}).catch((error) => {
console.log("エラー");
});
} else {
console.log("セッションストレージですでにマックス。");
this.$refs.infiniteLoading.stateChanger.complete();
}
},
refresh(loaded) {
alert("再読込完了");
loaded('done');
}
}
}
</script>
<style scoped>
.list{
overflow:hidden;
margin:20px 0;
}
span{
float: left;
margin-right: 5px;
}
p{
float: left;
}
</style>