vue-day13----City页面数据渲染-难点:cityList数据结构分析、数据优化-存储到localStorage中、点击右侧边栏字母去到对应的title、better-scroll的使用、scrollTo()方法、better-scroll二次封装(父组件访问子组件内部的方法:this.$refs.ref属性值.方法)、better-scroll组件(BScroll)横向滚动、sass

### City页面数据渲染-难点:cityList数据结构分析

    ①api/index.js中添加city接口:
        cityList:{
            city:"/v3/city"
        }
    ②api下新建request.js:
        import http from "@utils/request.js";
        import api from "./index.js";
        export const cityApi=()=>{
            return http({
                method:"get",
                url:api.cityList.city
            })
        }
    ③将数据存在vuex中,所以在store/index.js中
    引入cityApi:
        import { cityApi } from "@api/request.js";
    state中定义:
        state: {
            hotCity: [],
            cityList: []
        }
    通过actions请求数据:
        actions: {
            async getCityList({ commit }) {
                let data = await cityApi();
                commit("mutationsCityList", data.data.cities);
            }
        }
    actions中触发mutations中mutationsCityList()方法:
        mutations: {
            mutationsCityList(state, cities) {
                let hotCity = [], cityList = [];
                // 热门城市
                for (let i = 0; i < cities.length; i++) {
                    if (cities[i].isHot == 1) {
                        hotCity.push({ id: cities[i].id, nm: cities[i].nm });
                    }
                }
                // 城市列表
                /*
                    分析数据结构:
                        [
                            {title:A,list:[{id:1,nm:"鞍山"},{id:2,nm:"安庆"}]},
                            {title:B,list:[{id:1,nm:"北京"},{id:2,nm:"亳州"}]},
                        ]
                */
                for (let i = 0; i < cities.length; i++) {
                    // initials-首字母
                    let initials = cities[i].py.slice(0, 1).toLocaleUpperCase();
                    if (isFirst(initials).flag) {// 如果首字母在cityList中存在,则往这个字母对应的title下的list中push一个对象:{id,nm}
                        cityList[isFirst(initials).index].list.push({ id: cities[i].id, nm: cities[i].nm });
                    } else {// 如果首字母在cityList中不存在,则将这个首字母作为title的值,push一个对象{title:A,list:[{id,nm}]}
                        cityList.push({ title: initials, list: [{ id: cities[i].id, nm: cities[i].nm }] });
                    }
                }
                function isFirst(initials) {
                    let index = -1, flag = false;
                    for (let i = 0; i < cityList.length; i++) {
                        if (cityList[i].title == initials) {
                            flag = true;
                            index = i;
                            break;
                        }
                    }
                    return { index, flag };
                }
                cityList.sort(function (a, b) {
                    return a.title.charCodeAt() - b.title.charCodeAt();
                });
                // 更改state中的hotCity、cityList
                state.hotCity = hotCity;
                state.cityList = cityList;
                // 存储到localStorage中
                localStorage.setItem("hotCity",JSON.stringify(state.hotCity));
                localStorage.setItem("cityList",JSON.stringify(state.cityList));
            }
        }
    ③City页面created中执行请求函数,通过辅助函数拿到数据:
        import {mapActions,mapState} from "vuex";
        export default {
            name:"City",
            created() {
                this.getCityList();
            },
            computed: {
                ...mapState({
                    hotCity:state=>state.hotCity,
                    cityList:state=>state.cityList
                })
            },
            methods: {
                ...mapActions({
                    getCityList:"getCityList"
                })
            },
        }



### 数据优化-存储到localStorage中

    ①设置好state.hotCity、state.cityList将其存储到localStorage中(store/index.js):
        localStorage.setItem("hotCity",JSON.stringify(state.hotCity));
        localStorage.setItem("cityList",JSON.stringify(state.cityList));
    ②在state中设置cityList和hotCity的时候,先判断localStorage中有没有(store/index.js):
        state: {
            cityList:localStorage.getItem("cityList")?JSON.parse(localStorage.getItem("cityList")):[],
            hotCity:localStorage.getItem("cityList")?JSON.parse(localStorage.getItem("hotCity")):[]
        }
    ③在City/index.vue中进行数据请求时先判断localStorage中有没有cityList或hotCity:
        created() {
            if(!localStorage.getItem("cityList")||!localStorage.getItem("hotCity")){
                this.getCityList();
            }
        }

### 点击右侧边栏字母去到对应的title

    ①为每个li绑定tap事件(scrollToTitle(index)),将下标值传过去:
        <v-touch tag="li" v-for="(item,index) in cityList" :key="index" @tap="scrollToTitle(index)">{{item.title}}</v-touch>
    ②最外层盒子设置ref属性:
        <div class="city_body" ref="city_body">
    ③在所有的字母标题(.city_title_letter)中找到当前下标对应的字母标题,将offsetTop设置给city_body的scrollTop:
        methods: {
            scrollToTitle(index){
                let title=this.$refs.city_list.querySelectorAll(".city_title_letter")[index];
                let t=title.offsetTop;
                this.$refs.city_body.scrollTop=t;
            }
        }

### better-scroll的使用

    ①安装:npm i better-scroll
    ②引入:import BS from "better-scroll";
    ③mounted中实例化:this.BS=new BS(this.$refs.city_body);

 注意:

        1、city_body这个盒子里面只能有一个盒子,并且city_body的高度小于它的子级盒子高度。
        2、如果使用了better-scroll,后代元素中的fixed定位都会失效。
        3、better-scroll会破坏点击事件,所以在实例的时候,配置项中加上 click:true,tap:true

### better-scroll的scrollTo()方法

    在使用better-scroll初始化city_body后,点击字母去到对应的字母title时,滚动有bug,通过scrollTo()解决。
 
    在scrollToTitle()函数中用 this.BS.scrollTo(0,-t,500); 替代原来的 this.$refs.city_body.scrollTop=t; :
        scrollToTitle(index){
            let title=this.$refs.city_list.querySelectorAll(".city_title_letter")[index];
            let t=title.offsetTop;
            // this.$refs.city_body.scrollTop=t;
            // 往下滚动是正值,往上滚动是负值,和数学里的y轴坐标相反
            this.BS.scrollTo(0,-t,500);
        }

### better-scroll二次封装(父组件访问子组件内部的方法:this.$refs.ref属性值.方法

    ①plugins下新建BetterScroll/index.vue:
        <template>
            <div class="wrapper" ref="wrapper">
                <slot></slot>
            </div>
        </template>
        <script>
        import BScroll from "better-scroll";
        export default {
            name:"BScroll",
            mounted() {
                this.BScroll=new BScroll(this.$refs.wrapper,{click:true,tap:true});
            },
            methods: {
                scrollTo(l=0,t=0){
                    this.BScroll.scrollTo(l,t,500);
                }
            },
        }
        </script>
        <style>
        .wrapper{
            height: 100%;
        }
        </style>
    ②src下新建common/components.js(用于注册所有的组件):
        import Vue from "vue";
        import BScroll from "@plugins/BetterScroll/index.vue";
        Vue.component(BScroll.name,BScroll);
    ③main.js中全局注册:
        import "./common/components.js";
    ④City页面实现边缘回弹效果:
        只需要用<BScroll><BScroll>标签将需要页面包裹,不需要引入better-scroll:
            <template>
                <div class="city_body" ref="city_body">
                    <BScroll ref="BScroll">
                        <div>
                            <!-- 热门城市 -->
                            <div class="hot_city">
                                <div class="hot_title">热门城市</div>
                                <div class="hot_city_list">
                                    <div class="hot_city_name" v-for="item in hotCity" :key="item.id">{{item.nm}}</div>
                                </div>
                            </div>
                            <!-- 城市列表 -->
                            <div class="city_list" ref="city_list">
                                <div class="city_list_item" v-for="item in cityList" :key="item.index">
                                    <div class="city_title_letter">{{item.title}}</div>
                                    <div class="city_list_name">
                                        <div class="city_list_name_item" v-for="cityName in item.list" :key="cityName.id">{{cityName.nm}}</div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </BScroll>
                    <!-- 城市列表下标 -->
                    <aside class="city_list_title">
                        <ul>
                            <v-touch tag="li" v-for="(item,index) in cityList" :key="index" @tap="scrollToTitle(index)">{{item.title}}</v-touch>
                        </ul>
                    </aside>
                </div>
            </template>
    ⑤City页面实现点右击侧边栏滚动到对应title:
        (1)在<BScroll></BScroll>标签上添加ref属性:
            <BScroll ref="BScroll">
        (2)scrollToTitle()方法中使用 this.$refs.BScroll.scrollTo(0,-t); :
            scrollToTitle(index){
                let title=this.$refs.city_list.querySelectorAll(".city_title_letter")[index];
                let t=title.offsetTop;
                // this.$refs.city_body.scrollTop=t;
                // 往下滚动是正值,往上滚动是负值,和数学里的y轴坐标相反
                // this.BS.scrollTo(0,-t,500);
                this.$refs.BScroll.scrollTo(0,-t);
            }

### better-scroll组件(BScroll)横向滚动

    ①better-scroll配置项中设置scrollX:true,支持横向滚动:
        this.BScroll=new BScroll(this.$refs.wrapper,{
            click:true,
            tap:truescrollX: true
        });
    ②components/RecommendList/index.vue中用<BScroll></BScroll>标签将原来的内容包裹:
        <template>
            <BScroll>
                <div class="recommend_list">
                    <div
                        class="recommend_list-item"
                        v-for="item in recommend"
                        :key="item.target_id"
                        :data-id="item.target_id"
                    >
                        <div>
                            <img v-lazy="item.image" />
                        </div>
                        <div class="goodsName">{{item.title}}</div>
                        <div class="price">
                            <span>¥{{item.vip_price}}/{{item.volume}}</span>
                            <span>+</span>
                        </div>
                    </div>
                </div>
            </BScroll>
        </template>

### sass

    ①安装:npm i sass-loader node-sass -D
    ②style标签添加lang属性:
        <style lang="scss">
        $color:red;
        .classify{
            background: $color;
        }
        </style>
    注意:
        1、sass定义变量用$,less定义变量用@
        2、sass基于ruby,less基于JavaScript
        3、less比sass简单,less中可以直接写css语句
        4、sass通过服务端处理,less是通过客户端处理,sass的解析速度更快








原文地址:https://www.cnblogs.com/wuqilang/p/12424181.html