合肥千峰前端培训---vue-cli移动端实战项目所遇问题

vue-cli更改默认下载方式(用什么包管理器)

找到C:\Users\Administrator/.vuerc文件 Yarn/npm

node.sass下载失败

npm install -g mirror-config-china
先执行 这个命令
然后再安装 npm i node-sass -D

字体图标使用

  1. 开发环境
  • iconfont官网选图标 加购物车,添加到项目,生成在线css
  • public/index.html 导入在线css
  • 随用随添加,生成新的在线css链接,更改导入的在线css链接
    <link rel="stylesheet" href="//at.alicdn.com/t/font_1866688_130ihed3lqw.css">
    
  • 组件中使用(可以通过iconfont里更多操作,编辑项目,来修改字体图标前缀,但会覆盖vant里的一些图标,那vant里的一些组件就用不了了,通常用下面的方法)
    <i class="iconfont icon-shouye"></i>
    <van-icon class="iconfont" class-prefix="icon" name="shouye" />
    <!-- 自定义使用 -->
    <van-tabbar-item>
      <van-icon class="iconfont" slot="icon" class-prefix="icon" name="shouye" />
      <span>首页</span>
    </van-tabbar-item>
    
  1. 生产环境
  • iconfont 下载到本地,解压,将字体图标文件复制到静态资源目录assets/fonts下(iconfont.css/.eot/.svg)
  • 在main.js导入import ‘@/assets/fonts/iconfont.css’

移动端适配布局

  • h5用的框架一般有自己的布局方式

  • 自己布局 flex做布局,rem做单位,字体不能用rem做单位

    • 原理:根据根元素字体大小为单位(rem),设置其他元素尺寸
    • 根元素字体大小,随着屏幕尺寸地改变而改变 =>根据屏幕宽度动态的设置html的字体大小
      方法一
      function setRem(){
        document.documentElement.style.fontSize = document.documentElement.clientWidth/10 + 'px'
      }
      
      setRem()
      
      window.onresize = function(){
        setRem()
      }
      
      window.onorientationchange = function(){
        setRem()
      }
      
    • 写在assets/js/rem.js
    • main.js引入
    import '@/assets/js/rem.js'
    
    <!-- $rem:75rem;多少取决于设计图的宽度 -->
    <style lang="sass" scoped>
      $rem:75rem;
      #box{
        width:500/$rem;
        height:100/$rem;
      }
    </style>
    
    <!-- 手机尺寸375x667  -->
    <!-- ui设计图750x1334 设计图测量500px--> 
    <!-- html 1rem = 75px -->
    <!-- 使用500/75 rem * 75 = 500px -->
    
    

    方法二
    vue cli3.0 rem 使用
    首先安装amfe-flexible插件,在main.js里引入

    1、

    npm i amfe-flexible -D
    

    2、

    import 'amfe-flexible' 
    

    然后再,安装postcss-px2rem插件

      npm i postcss-px2rem -D
    

    在package.json中配置

    "postcss": {
      "plugins": {
        "autoprefixer": {},
        "postcss-px2rem": {
          <!-- 没设计图直接改37.5 -->
          "remUnit": 75
        }
      }
    }
    
    

轮播图数据获取

  1. xx
    • npm i axios -S
    • main.js引入 import axios from 'axios'
    • 将方法继承到Vue上,可以在实例上使用 Vue.prototype.$http = axios;
    • 组件内使用methods(){this.$http.get()then()}
    • **跨域问题–反向代理 **
      https://cli.vuejs.org/zh/config/#devserver-proxy

ajax请求处理

  1. 上面xx方法使用场景:

    • 小型项目,且公司里只有一个前端(你一个人写这个项目)
    • 外包的,不需要做版本更新迭代
  2. axios请求接口函数单独提取,方便复用、维护,axios还需要一些配置

    • 在utils文件夹下创建request.js文件
    import axios from 'axios'
    const request = axios.create({
      baseURL: '/hundan/',
      // baseURL用于开发环境、测试环境以及线上环境的不同的源
      timeout: 8000
    })
    
  • axios拦截
    1. 全局loading实现
    2. 接口token处理(有没有,有没有过期)
    // Add a request interceptor
    request.interceptors.request.use(function (config) {
      // Do something before request is sent
      return config;
    }, function (error) {
      // Do something with request error
      return Promise.reject(error);
    });
    
    // Add a response interceptor
    request.interceptors.response.use(function (response) {
      // Do something with response data
      return response;
    }, function (error) {
      // Do something with response error
      return Promise.reject(error);
    });
    

scss嵌套

  • 在嵌套中还有一个标识符是 & 我们可以使用

  • 先来看一个例子

    div {
        width: 100px;
        height: 100px;
    
        :hover {
            width: 200px;
        }
    }
    
    // 我想的是 div 被鼠标悬停的时候 width 变成 200
    // 但是编译结果却是
    div {
        width: 100px;
        height: 100px;
    }
    div :hover {
      	width: 200px;
    }
    
  • 和预想的结果不一样了

  • 这个时候就要用到 & 来连接了

    div {
        width: 100px;
        height: 100px;
    
        &:hover {
            width: 200px;
        }
    }
    
    // 编译结果
    div {
        width: 100px;
        height: 100px;
    }
    div:hover {
      	width: 200px;
    }
    
  • 这个时候就和我需要的一样了

post传参注意(axios)

  • 问题产生原因:在使用axios时,POST请求默认参数格式是application/json,而后台接受的数据格式默认是application/x-www-form-urlenxoded
  • 解决方法:
    1. 安装依赖
      npm i qs -S
      qs.stringify()
    

富文本图片宽度超出屏幕问题

```javascript
computed: {
  content () {
    if (this.item.content) {
      <!-- 正则匹配字符串替换手动设置width:100% -->
      return this.item.content.replace(/<img/g, "<img style='width:100%'")
    } else {
      return ''
    }
  }
}
```

接口校验(携带token)

// Add a request interceptor
request.interceptors.request.use(function (config) {
  // Do something before request is sent
  // token请求头携带用于接口校验
  config.headers.accessToken = localStorage.getItem('accessToken')
  return config
}, function (error) {
  // Do something with request error
  return Promise.reject(error)
})

// Add a response interceptor
request.interceptors.response.use(function (response) {
  // Do something with response data
  if (response.data.code === 0) {
    return response
  } else if (response.data.code === 401) {
    router.push('/login')
  }
}, function (error) {
  // Do something with response error
  return Promise.reject(error)
})

prams传参和query传参的区别

prams 隐性传参,必须和name使用,刷新会造成数据丢失
query直接在地址栏上面拼接,显性的,一般和path一起使用,刷新不会造成数据丢失

禁止用户双击缩放

<meta name="viewport" content="width=device-width,initial-scale=1.0">

自定义插件(loading)

  1. 创建模板文件 src/plugins/loading/Loading.vue 状态isShow控制显隐

    <template>
      <div class="bg" v-if="isShow">
        <div class="dotBox">
          <p>
            <span></span>
            <span></span>
            <span></span>
            <span></span>
          </p>
          <p>
            <span></span>
            <span></span>
            <span></span>
            <span></span>
          </p>
        </div>
      </div>
    </template>
    
    <script>
    export default {
      data () {
        return {
          isShow: true
        }
      }
    }
    </script>
    
    <style lang="scss" scoped>
    .bg{
      position: fixed;
      top: 0;
      bottom: 0;
      left: 0;
      right: 0;
      z-index: 999;
      background:rgba($color: #000000, $alpha: 0.8);
      .dotBox{
        width: 120px;
        height: 120px;
        top: 50%;
        left: 50%;
        margin-top: -60px;
        margin-left: -60px;
        position: absolute;
        p{
          width: 120px;
          height: 120px;
          position: absolute;
          span{
            display: block;
            width: 24px;
            height: 24px;
            border-radius: 12px;
            background: white;
            position: absolute;
            animation: twinkle 1.5s linear infinite;
            &:nth-of-type(2){
              top: 0;
              right: 0;
            }
            &:nth-of-type(3){
              bottom: 0;
              right: 0;
            }
            &:nth-of-type(4){
              bottom: 0;
              left: 0;
            }
          }
          &:nth-of-type(1){
             span{
                &:nth-of-type(1){
                  animation-delay: -0.4s;
                }
                &:nth-of-type(4){
                  animation-delay: -0.8s;
                }
                &:nth-of-type(3){
                  animation-delay: -1.2s;
                }
                &:nth-of-type(2){
                  animation-delay: -1.6s;
                }
             }
          }
          &:nth-of-type(2){
            transform: rotate(45deg);
            span{
              &:nth-of-type(1){
                animation-delay: -0.2s;
              }
              &:nth-of-type(4){
                animation-delay: -0.6s;
              }
              &:nth-of-type(3){
                animation-delay: -1s;
              }
              &:nth-of-type(2){
                animation-delay: -1.4s;
              }
            }
          }
        }
      }
    }
    
    @keyframes twinkle{
      from{transform: scale(0);}
      50%{transform: scale(1);}
      to{transform: scale(0);}
    }
    </style>
    
    
  2. 创建插件导出文件 src/plugins/loading/index.js

    // 导入模板文件
    import LoadingTpl from './Loading'
    const loading = {
      install (Vue, options) {
        // 生成vue组件
        let Load = Vue.extend(LoadingTpl)
        // 生成vue组件实例
        let load = new Load()
    
        // 手动渲染到页面
        document.body.appendChild(load.$mount().el)
    
        // 创建实例方法
        // 控制显示loading效果
        Vue.prototype.$showLoading = () => {
          load.isShow = true
        }
    
        // 控制隐藏loading效果
        Vue.prototype.$hideLoading = () => {
          load.isShow = false
        }
      }
    }
    
    // 导出插件
    export default loading
    
    
  3. main.js入口文件中使用插件

    import Vue from 'vue'
    import loading from '@/plugins/loading'
    
    Vue.use(loading)
    
  4. 在axios实例文件中,接口拦截加loading效果

    import axios from 'axios'
    import Vue from 'vue'
    import router from '@/router'
    var bus = new Vue()
    const request = axios.create({
      baseURL: '/hundan/',
      //  baseURL用于开发环境、测试环境以及线上环境的不同的源
      timeout: 10000
    })
    
    // Add a request interceptor
    request.interceptors.request.use(function (config) {
      // 发起请求时,出现loading
      bus.$showLoading()
      // Do something before request is sent
      // token请求头携带用于接口校验
      config.headers.accessToken = localStorage.getItem('accessToken')
      return config
    }, function (error) {
      // Do something with request error
      return Promise.reject(error)
    })
    
    // Add a response interceptor
    request.interceptors.response.use(function (response) {
      // Do something with response data
      // 接受响应时,隐藏loading
      bus.$hideLoading()
      if (response.data.code === 0) {
        return response
      } else if (response.data.code === 401) {
        router.push('/login')
      }
    }, function (error) {
      // Do something with response error
      return Promise.reject(error)
    })
    
    export default request
    
    

搜索页面

  1. 移动端的页面的搜索栏都是假的,是点击跳转到一个搜索页面
  2. 历史记录可以存在localStorage里面,条数限制,数组apiunshift pop
  3. 历史记录去重 数组去重find findIndex [... new Set(oldArr)]x

移动端滚动效果优化(better-scroll iScroll)

  1. 普通使用
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Document</title>
      <style>
        * {
          margin: 0;
          padding: 0;
        }
    
        li {
          background: #13ac62;
          list-style-type: none;
          line-height: 30px;
          margin: 10px;
          color: salmon;
        }
    
        li:nth-of-type(2n) {
          background: #e222a2;
          color: sandybrown;
        }
    
        html,
        body {
          height: 100%;
          /* 不显示浏览器自带的滚动条 */
          overflow: hidden;
        }
    
        .wrapper {
          height: 100%;
        }
      </style>
    </head>
    
    <body>
      <div class="wrapper">
        <ul>
          <li onclick="window.alert(1)">惦记我</li>
          <li><a href="javascript:alert(2);">xxxx</a></li>
          <li>3</li>
          <li>4</li>
          <li>5</li>
          <li>6</li>
          <li>7</li>
          <li>8</li>
          <li>9</li>
          <li>10</li>
          <li>11</li>
          <li>12</li>
          <li>13</li>
          <li>14</li>
          <li>15</li>
          <li>16</li>
          <li>17</li>
          <li>18</li>
          <li>19</li>
          <li>20</li>
          <li>1</li>
          <li>2</li>
          <li>3</li>
          <li>4</li>
          <li>5</li>
          <li>6</li>
          <li>7</li>
          <li>8</li>
          <li>9</li>
          <li>10</li>
        </ul>
        <div>
          1222222222222222222
        </div>
      </div>
      
    	<!-- 去npmjs上下载,官网上的有点问题(下拉刷新没试出来)-->
      <script src="../../../../utils/bscroll/bscroll.js"></script>
      <script>
        let wrapper = document.querySelector('.wrapper')
        // DOM对象或选择器
        let scroll = new BScroll(wrapper, {
          // 默认会阻止浏览器的原生 click 事件,当设置为 true,better-scroll 会派发一个 click 事件
          click: true,
          // 配置下拉刷新功能,默认false,可配true,或对象
          pullDownRefresh: {
            // 可以配置顶部下拉的距离来决定刷新的时机
            threshold: 50,
            // 回弹停留的距离 (谷歌自带的模拟器回弹停留的距离有点问题,但真机上没问题)
            stop: 20
          },
          probeType:2,
          // 配置下拉刷新功能,默认false,可配true,或对象
          pullUpLoad: {
            // 可以配置底部上拉的距离来决定加载的时机
            threshold: 50
          }
        })
        // 监听下拉事件,在下拉刷新动作后,一般用来去后端请求数据(官网起步下的文件没用,npm下个)
        scroll.on('pullingDown',function(){
          console.log('发送ajax请求了...')
          scroll.finishPullDown()
        })
        // scroll.on('scroll',function(){
        //   console.log('我滚动了...')
        // })
    
      </script>
    </body>
    
    </html>
    
  2. 项目中使用
    npm i better-scroll -S
    
    写个展示组件bScroll.vue
    <template>
      <div class="wrapper" :style="{height:typeof(height)===Number?height+'px':height, overflow: 'hidden'}">
        <slot></slot>
      </div>
    </template>
    
    <script>
    import BScroll from 'better-scroll'
    import { setTimeout } from 'timers'
    export default {
      data () {
        return {
          scroll: ''
        }
      },
      mounted () {
        // ***等到生成真实的dom元素时才能用选择器
        setTimeout(() => {
          this.scroll = new BScroll('.wrapper', this.options)
          if (this.options.pullDownRefresh) {
            this.scroll.on('pullingDown', () => {
              // props接受的回调函数,用来请求数据
              this.$emit('fn')
              this.scroll.finishPullDown()
            })
          }
        }, 20)
      },
      props: {
        height: {
          type: [Number, String],
          require: true
        },
        lists: {
          type: Array,
          require: true
        },
        options: {
          type: Object,
          default () {
            return {
              click: true,
              pullDownRefresh: false,
              probeType: 2,
              pullUpLoad: false
            }
          }
        }
      },
      watch: {
        lists (n) {
          console.log('重置...')
          this.scroll.refresh()
        }
      }
    }
    </script>
    
    <style lang="scss" scoped>
    
    </style>
    
    
    组件中使用
    <template>
      <div>
        <page-header title='分类' :backIcon="false"></page-header>
        <div class="main">
          <div class="left">
            <van-tag @click="fn(index,cate.id)" :type="num===index?'primary':'default'" size="large"  v-for="(cate, index) in cates" :key="cate.id">{{cate.name}}</van-tag>
          </div>
          <div class="right">
            <b-scroll height="calc(100vh - 100px)" :options="{click: true,pullDownRefresh: {threshold: 50,stop: 20}}" :lists="items" @fn="pullingDownFn">
              <!-- 注意!只对第一个子容器生效 -->
              <!-- 原理一定要搞清 wrapper高度定死、overflow:hidden去除浏览器默认滚动条,以及隐藏子容器超出自己的部分 -->
              <!-- 第一个子容器高度超过wrapper就可以滚动了 -->
              <div class="list">
                <van-card
                  v-for="item in items"
                  :key="item.id"
                  :price="item.minPrice"
                  :desc="item.characteristic"
                  :title="item.name"
                  :thumb="item.pic"
                  @click="enterDetail(item.id)"
                />
              </div>
              <div v-if="!items.length" class="null_pic">
                <img src="@/assets/images/data_null.jpg" alt="暂无数据">
              </div>
            </b-scroll>
          </div>
        </div>
        <tabbar/>
      </div>
    </template>
    
    <script>
    import tabbar from '@/components/tabbar'
    import pageHeader from '@/components/pageHeader'
    import { getAllCates, getItemList } from '@/api'
    import bScroll from '@/components/bScroll.vue'
    export default {
      data () {
        return {
          cates: [],
          items: [],
          num: 0
        }
      },
      components: {
        tabbar,
        pageHeader,
        bScroll
      },
      created () {
        this.fetchAllCates()
      },
      methods: {
        // 下拉刷新事件
        pullingDownFn () {
          // **:fn="pullingDownFn"
          // **不要写成这样:fn="fetchItems(cates[num].id)"
          // 这他妈的不是一个函数,而是函数调用,死循环,算了,是我眼瞎
          // 错错错,哪有传函数的,用事件总线呀,子组件触发绑定自身上的父组件事件
          console.log('下拉刷新了')
          this.fetchItems(this.cates[this.num].id)
        },
        // 进入商品详情
        enterDetail (goodsId) {
          this.$router.push('/detail/' + goodsId)
        },
        fetchAllCates () {
          getAllCates().then(res => {
            if (res.data.code === 0) {
              this.cates = res.data.data
              this.fetchItems(this.cates[0].id)
            }
          })
        },
        fetchItems (id) {
          getItemList({ categoryId: id }).then(res => {
            if (res) {
              this.items = res.data.data
            } else {
              this.items = []
            }
          })
        },
        fn (i, id) {
          this.num = i
          this.fetchItems(id)
        }
      }
    }
    </script>
    
    <style lang="scss" scoped>
    .main{
      display: flex;
      height: calc(100vh - 100px);
      .left{
        display: flex;
        flex: 1;
        flex-wrap: wrap;
        overflow: auto;
        background: #bbb;
      }
      .right{
        display: flex;
        flex: 4;
        background: #f2f2f2;
        .list{
          display: flex;
          flex-wrap: wrap;
        }
        .null_pic{
          img{
            width: 100%;
          }
        }
      }
      .van-tag{
        font-size: 14px;
        width: 100%;
        margin: 5px 0;
      }
      .van-card{
        width: 100%;
      }
    }
    </style>
    
    

其他注意

  1. 移动端边框圆角给确定值,最好不要用百分比
  2. vant全部引入记得引入css样式,不然会出错,比如search使用设置圆角设置不上
  3. index作条件判断时,注意可能为0
  4. 全选反选 个体判大,小用change,全用change出错,直接都用click就行
  5. *对象作为参数传递时是地址传递 let newObj = JSON.parse(JSON.stringify(oldObj))
  6. let解构的数据为只读的,用一个变量来接受它,更改这个变量不会更改原来的数组或对象 'carts' is assigned a value but never used no-unused-vars,(如果想修改state.carts直接写,不用结构)
    var obj = {
      a:1,
      b:2
    };
    
    let { a } = obj;
    a = 3;
    
    console.log(obj) //{a: 1,b: 2}
    
  7. htmlcss中calc使用运算符加空格 height: calc(100vh - 100px)
  8. vant组件有些要穿透才能设置上css样式 /deep/.iconBox {xxxx}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值