乐趣区

接触的第一个vue项目

序言

 刚来杭州的时候,是两个人一起来的。后来一个回家当老师,一个留在了杭州。想起那时,在慕课买的第一课程,囫囵吞枣的看完了。勉强才找到了一份工作,今天看起来,其实还是有点难过的。唉,其实人生不就是这样吗?还是要感谢思否还有类似掘金这种前端社区,帮助我解决不少问题,直到今天,也算是入门了。现在回过头来看看,这个临时抱佛脚的启蒙项目,真的还有不少的收获。漏了不少知识点,现在捡起来,总结一下。
  • 滚动视图的位置缓存

      /*
      *@to,from 路由元对象。@savedPosition 触发 H5 下的 popstate,go 导航 (通过浏览器的 前进 / 后退 按钮触发) 时才可用。*/
       scrollBehavior(to, from, savedPosition) {
          return {
            x: 0,
            y: 0
          }
        }
    1. 这个功能只在 HTML5 history 模式下可以使用。
    2. {selector: string, offset? : { x: number, y: number}} (offset 只在 2.6.0+ 支持)
    3. 路由导航,可以模拟 hash 滚动到锚点

       if (to.hash) {
          return {selector: to.hash}
        }
    4. kekeepAlive 结合,返回上次停留的位置(常用), 缓存页面数据,当且两个 keepAlive 以上的页面才可以触发。

          if (savedPosition) {return savedPosition} else {if (from.meta.keepAlive) {from.meta.savedPosition = document.body.scrollTop;}
              return {x: 0, y: to.meta.savedPosition ||0}
          }
  • 动态路由匹配参数

        routes:[
            {
                path: '/detail/:id',
                name: 'Detail',
                component: Detail
            }
        ]    
      this.$router.push({name:'Detail',params:{id:id}});
    1. 路由模式为 history 模式,服务器需要做到对前端路由的全匹配。可以参考官网
    2. 类似项目中的购物车,商品详情页等,可以做到直观符合逻辑,params 可以作为路径参数传递,前提是做好路由匹配。
  • mock 数据与跨域代理

    1. 建立 mock 数据,最好建立在 public 文件之内
    2. 配置代理,直接指向本地 mock 文件夹. 项目中可以配置后台接口或者映射地址,

      devServer: {
          proxy: {
            '/api': {
              // 到时候修改这个地方
              target: 'http://localhost:8080',
              changeOrigin: true,
              // 后台接口地址重定向,真实对接的时候下面的替换到路径,测试环境和本地。pathRewrite: {'^/api': '/mock'}
            }
          }
        }
  • 封装组件

awosome-swiper 为例,github 上的一个开源 swiper 组件,公共画廊组件的拆分,其实所用到数据是从上述本地 mock 中得来的。

通用画廊组件

// CommonGallary.vue
// 已在 main.js 中引用 awosome-swiper(use)
   <template>
      <div class="container" @click="handleGallaryClick">
        <dir class="wrapper">
          <swiper :options="swiperOptions">
            <swiper-slide v-for="(item, index) of imgs" :key="index">
              <img :src="item" class="gallary-img" />
            </swiper-slide>
            <div class="swiper-pagination" slot="pagination"></div>
          </swiper>
        </dir>
      </div>
    </template>
    
    <script>
        export default {
          name: "CommonGallary",
          data() {
            return {    // 分页参数配置
              swiperOptions: {
                pagination: ".swiper-pagination",
                paginationType: "fraction",
                // 监视到 dom 元素发生了变化,就会自我刷新一下,防止 swiper 滑动失败
                observer: true,
                observeParents: true
              }
            };
          },
          props: {
            imgs: {    // 子组件接收图片数组
              type: Array,
              default() {return [];
              }
            }
          },
          methods: {handleGallaryClick() {this.$emit("close");    // 子组件向上触发事件,父组件进行监听
            }
          }
        };
     </script>

过度动画通用组件

 // FadeAnimation.vue
 // 已在 main.js 中引用 awosome-swiper(use)
       <template>
       <div>
         <transition>
           <!-- 里面的内容由父组件来传入 dom -->
           <slot></slot>
         </transition>
       </div>
     </template>
     
     <script>
       export default {name: 'FadeAnimation'}
     </script>
     
     <style lang="stylus" scoped>
     /* 开始进入和首次离开 透明度为 0 动画进入激活 - 动画失活 1s 按照透明度过度  */
       .v-enter, .v-leave-to
         opacity 0
       .v-enter-active , .v-leave-active
         transition opacity 1s
     </style>

画廊父组件 DetailBanner.uve

    // DetailBanner.vue
    // 
   <template>
       <div>
         <div class="banner" @click="handleBannerClick">
           <img class="banner-img" :src="bannerImg" alt="bannerImg">
           <div class="banner-info">
             <div class="banner-title">
              {{this.sightName}}
             </div>
             <div class="banner-number">
               <span class='iconfont banner-icon'>&#xe633;</span>
               {{this.bannerImgs.length}}
             </div>
           </div>
         </div>
         // 引入过度动画和公用画廊
         <fade-animation>
           <common-gallary :imgs="bannerImgs" v-show="showGallary" @close="handleGallaryClose"></common-gallary>
         </fade-animation>    
       </div>
 </template>
 <script>
     import CommonGallary from 'common/gallary/Gallary'
     import FadeAnimation from 'common/fade/FadeAnimation'
       export default {
         name: 'DetailBanner',
         props: {
           sightName: String,    // prop 传参
           bannerImg: String,
           bannerImgs: Array
         },
         components: {
           CommonGallary,
           FadeAnimation
         },
         data() {
           return {
             showGallary: false,
             imgs: ["http://img1.qunarzz.com/sight/p0/201404/23/04b92c99462687fa1ba45c1b5ba4ad77.jpg_800x800_70debc93.jpg", "http://img1.qunarzz.com/sight/p0/1709/76/7691528bc7d7ad3ca3.img.png_800x800_9ef05ee7.png"]
           }
         },
         methods: {
            // 触发显示和监听关闭
           handleBannerClick () {this.showGallary = true},
           handleGallaryClose () {this.showGallary = false}
         }
       }
     </script>

到这里的就基本上差不多了。

  • 事件监听与吸顶过渡

Header 头部记录

   <template>
    <div>
        <router-link 
          to="/" 
          tag="div" 
          class="header-abs"
          v-show="showAbs"
          >
          <div class='iconfont header-abs-back'>&#xe624;</div>
        </router-link> 
        <div 
          class="header-fixed" 
          v-show="!showAbs"
          :style="opacityStyle"
          >
          景点详情
          <router-link to="/">
            <div class='iconfont header-fixed-back'>&#xe624;</div>
          </router-link>
        </div>
    </div>
    </template>
    
    <script>
      export default {
        name: 'DetailHeader',
        data() {
          return {
            showAbs: true,
            opacityStyle: {opacity: 0}
          }
        },
        methods: {handleScroll () {console.log('scroll')
            // 获取当前滚动高度
            const top = document.documentElement.scrollTop
            if(top > 60) { 
              // 当滚动高度超过一个 header 的高度时,触发吸顶过度动画
              let opacity = top / 140    // 透明度随高度改变 最终为 1
              opacity = opacity > 1 ? 1 : opacity
              this.opacityStyle = {opacity}    //  键值相等
              this.showAbs = false    // 箭头返回关闭
            }
            else{this.showAbs = true}
            // console.log(document.documentElement.scrollTop)
          }
        },
        // 也可以用 destory 进行销毁
        activated () {
          // 这里是绑定在了全局 window 对象中,太过于消耗性能,需要全局事件的解绑
          window.addEventListener('scroll', this.handleScroll)
        },
        // 页面即将被隐藏,或者页面即将被替换掉时
        deactivated () {window.removeEventListener('scroll', this.handleScroll)
        },
      }
    </script>

之后再接着写吧,有点累了。不知道说什么好了,公司开的就剩我一个前端了,可能我也要走了, 我可不想这么轻易的滚回家。

退出移动版