接触的第一个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>

《接触的第一个vue项目》到这里的就基本上差不多了。

  • 事件监听与吸顶过渡

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>

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

    原文作者:dell_di
    原文地址: https://segmentfault.com/a/1190000020119518
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞