vue-cli3.x搭建移动端项目(vant+rem+axios简单封装+vue-navigation+过场动画)

安装

  • node:vue cli 需要node版本>=8.9, (官方推荐:8.11.0+ ),没有的自行下载:node下载地址

  • 安装vue脚手架:vue脚手架目前已经升级到4.x,所以直接 npm install -g @vue/cli 默认下载的是cli4.x ,想要安装cli3.x只需要输入命令:

    //我这里装的是3.11.0版本
    npm install -g @vue/cli@3.11.0
    

    输入以下命令查看是否安装成功,成功的会显示版本号

    vue -V
    

创建项目

  • 方式一:使用命令创建项目:

    //<Project Name>是项目名 不支持驼峰(含大写字母)
    vue create <Project Name>  
    
  • 方式二(推荐):也可以使用可视化页面来创建项目,运行 :

    vue ui
    

    然后会出现一个地址

    在浏览器中打开,然后新建项目,根据自己需求去创建,以下是我自己的配置可供参考:



安装vant

安装

  • 方式一:npm安装:
    npm i vant -S
    
  • 方式二:通过脚手架安装,运行 vue ui,然后找到安装依赖,搜索安装即可

引入

  • 方式一:全局引入
    // 在src/main.js进行全局引入
    
    // import Vue from 'vue';  //有了就不用引
    import Vant from 'vant';
    import 'vant/lib/index.css';
    Vue.use(Vant);
    
  • 方式二:自动按需引入组件
    // 安装插件:babel-plugin-import 是一款 babel 插件,它会在编译过程中将 import 的写法自动转换为按需引入的方式。
    npm i babel-plugin-import -D
    
    // 在.babelrc 中添加配置
    // 注意:webpack 1 无需设置 libraryDirectory
    {
      "plugins": [
        ["import", {
          "libraryName": "vant",
          "libraryDirectory": "es",
          "style": true
        }]
      ]
    }
    
    // 对于使用 babel7 的用户,可以在 babel.config.js 中配置
    module.exports = {
      plugins: [
        ['import', {
          libraryName: 'vant',
          libraryDirectory: 'es',
          style: true
        }, 'vant']
      ]
    };
    
    

使用

在项目中打开home组件,把vant的代码复制进template里看一下行不行

  <van-button type="primary">主要按钮</van-button>
  <van-button type="info">信息按钮</van-button>
  <van-button type="default">默认按钮</van-button>
  <van-button type="warning">警告按钮</van-button>
  <van-button type="danger">危险按钮</van-button>

成功是这样的:

如果你是自动按需引入组件的话,则需要再main.js中引入

import {Button} from 'vant'
Vue.use(Button)

// 引入多个:
import { Button, Row, Col } from 'vant'
Vue.use(Button).use(Row).use(Col)

如果想在页面中单独使用组件,则需要在改组件中这样引入:

//有的组件有专门的引入方式,自行去看vant官网

import { Button } from 'vant';
export default {
   name: 'Home',
   components: {
       [Button.name]: Button   
   },
}

加入rem

  1. 安装命令:
     // postcss-pxtorem 需要指定版本,不然运行会报错:PostCSS plugin postcss-pxtorem requires PostCSS 8.
     npm install postcss-pxtorem@5.1.1 --save-dev     
     
     npm i -S amfe-flexible
    
  2. main.js中引入
    import 'amfe-flexible/index.js'
    
  3. 在项目根目录下新建postcss.config.js,加入以下代码
    module.exports = {
      plugins: {
        'autoprefixer': {
          overrideBrowserslist: [
            'Android 4.1',
            'iOS 7.1',
            'Chrome > 31',
            'ff > 31',
            'ie >= 8'
          ]
        },
        'postcss-pxtorem': {
          rootValue: 37.5,   //基数,375px 即100%宽度,写px会自动rem处理,如果不想被rem处理,可以使用PX来写。
          propList: ['*']
        }
      }
    }
    
  4. 重跑项目,随便写个div,在浏览器审查元素,查看是否已转换为rem

配置vue.config.js

根目录下新建vue.config.js,下面是通用配置,直接复制代码使用

module.exports = {
    publicPath:'/',//部署应⽤包时的基本路径
    outputDir:'dist',//构建输出目录
    assetsDir:'assets',//静态资源目录(js,css,img,fonts)
    lintOnSave: false,//是否开启eslint保存监测,有效值:true  ||  false  ||  'error'
    devServer:{
        open:false, //项目运行起来自动打开浏览器
        host:'0.0.0.0',// 配置主机地址
        port:8080,//端口号
        https:false,// 开启https
        hotOnly:true, //热更新
        proxy:{
            //配置跨域
            '/api':{ // /api 的意义在于,声明axios中url已/api开头的请求都适用于该规则,
                target:'http://192.168.57.220:8868',
                ws:true,
                changOrigin:true,
                pathRewrite:{  //地址重写,将'/api'替换成''  
                    '^/api':''
                }
            }
        }
    },
}

简单封装axios

先安装

npm i axios -S

再src下新建api、http文件夹,http下新建http.js
我们在http.js中进行封装axios

import axios from 'axios';

var http = axios.create({
    baseURL: '/unifyPlatform/mobile',
    timeout: 5000,
    responseType: 'json',
    headers: {
        'Content-Type': 'application/json;charset=UTF-8'
    },
})


export function api(method, url, params) {
    if (method.toUpperCase() == 'GET') {
        return http.get(url)
            .then(res => res)
            .catch(err => { 
                throw Error(err)
             })
    } else if (method.toUpperCase() == 'POST') {
        return http.post(url, params)
            .then(res => res)
            .catch(err => { 
                throw Error(err)
             })
    }
}

再api文件夹下新建home.js

import {api} from '../http/http.js'

//get接口测试
export function testGet() {
    return api('GET','/index')
}

//post接口测试
export function testPost(params) {
    return api('POST','/business/list', params)
}

修改vue.config.js

proxy:{
    '/':{
        target:'http://192.168.1.1:8080',
        ws:true,
        changOrigin:true,
        pathRewrite:{  //地址重写,将'/api'替换成''  
            '^/api':''
        }
    }
}

home.vue中引入

import { testGet, testPost } from "@/api/home.js"
export default {
  name: "Home",
  data() {
    return {
      params: {
        keyword: "",
        userInfo: {
          name: "Name",
          idcard: "2110231998xxxxxxxx",
        },
      },
    };
  },
  created() {
    testGet().then((data) => {
      console.log("testGet", data);
    });

    testPost(this.params).then((data) => {
      console.log("testPost", data);
    });
  }
}

效果


到这里 项目就可以满足基本使用了,如果有兴趣,可以继续往下看,根据自己的需求看是否需要配置

去除eslint报错

个人比较烦一些报错,我这里新增了两个不希望eslint报错的配置:

//package.json:

"rules": {
  "no-unused-vars": "off",   //禁止声明变量不使用:关闭
  "vue/no-unused-components": "off"  //禁止vue引入组件不使用:关闭
}


//注意:
//json文件不能写注释
//改变了eslintrc文件中的配置,一定要重启项目才能生效

样式初始化

在src/assets下新建css、js、img、fonts、json(放json数据,如城市级联)文件夹,用来存放一些资源文件
在css文件夹下新建base.less,用于重置浏览器的默认样式,以下是我使用的,可根据自己需求自行增减

点击查看代码
html {
  -webkit-text-size-adjust: 100%;
  -ms-text-size-adjust: 100%;
  -webkit-overflow-scrolling : touch;
  }
  html, body, #app{width:100%;height:100%;}

  input[type="submit"], input[type="reset"], input[type="button"], input {
    font-family: Arial, Helvetica, sans-serif;
    resize: none;
    border: none;
  }

  body, div, ul, li, ol, h1, h2, h3, h4, h5, h6, input, textarea, select, p, dl, dt, dd, a, img, button, form, table, th, tr, td, tbody, article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section {
    -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
    font-size:14px;
    box-sizing:border-box;
  }

  article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section {
    display: block;
  }

  img {
    width: 100%;
    height: auto;
    width: auto\9; /* ie8 */
    display: block;
    -ms-interpolation-mode: bicubic;/*为了照顾ie图片缩放失真*/
  }

  body, div, ul, li, ol, h1, h2, h3, h4, h5, h6, input, textarea, select, p, dl, dt, dd, a, img, button, form, table, th, tr, td, tbody, article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section {
    margin: 0;
    padding: 0;
  }
  body {
    font: 12px/1.5 'Microsoft YaHei','宋体', Tahoma, Arial, sans-serif;
    color: #555;
    background-color: #fff;
  }
  em, i {
    font-style: normal;
  }
  ul,li{
    list-style-type: none;
  }
  strong {
    font-weight: normal;
  }
  .clearfix:after {
    content: "";
    display: block;
    visibility: hidden;
    height: 0;
    clear: both;
  }
  .clearfix {
    zoom: 1;
  }
  a {
    text-decoration: none;
    color: #969696;
    font-family: 'Microsoft YaHei', Tahoma, Arial, sans-serif;
  }
  a:hover {
    text-decoration: none;
  }
  ul, ol {
    list-style: none;
  }
  h1, h2, h3, h4, h5, h6 {
    font-size: 100%;
    font-family: 'Microsoft YaHei';
  }
  img {
    border: none;
  }
  input{
    font-family: 'Microsoft YaHei';
  }

  .one-txt-cut{
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
  }

  .txt-cut{
    overflow : hidden;
    text-overflow: ellipsis;
    display: -webkit-box;
    -webkit-box-orient: vertical;
  }

  a:link,a:active,a:visited,a:hover {
    background: none;
    -webkit-tap-highlight-color: rgba(0,0,0,0);
    -webkit-tap-highlight-color: transparent;
  }
  input[type=button], input[type=submit], input[type=file], button { cursor: pointer; -webkit-appearance: none; }

然后在mian.js中引入即可

import "./assets/css/base.less"

同时也可以在同级文件夹中新建一个global.less,用于写一些全局样式,比如修改vant里的一些样式来定制ui风格,不要忘了在main.js中引入

安装vue-navigation

在移动端经常遇到这样的情形:有三个页面,首页---> 列表--> 详情,我们每次从详情返回列表的时候,列表都要重新加载一次,这个对于用户体验比较差,同时也增加了服务器的压力。所以这时候我们需要把列表页缓存起来,那我们就可以使用vue提供的keep-alive进行缓存。
如果我们仅仅把该组件的keep-alive改为true的话,遇到这样场景就会有bug:当首页进入列表时,假如此时列表的第一条数据为 "姓名:张三" ,那我们点击这条数据进入 张三 的详情页,若详情中有修改功能的话,我们将张三的名字改为 李四 再返回列表。此时列表因为被keep-alive缓存了,所以列表的第一条数据还是 张三,这时我们就需要动态的改变keep-alive的值,但是如果我们页面层级比较多的时候,判断起来真的是超级麻烦而且容易乱,那我们就可以借助vue-navigation来帮我们解决这个问题。

vue-navigation就完美的实现了 前进刷新,后退缓存 的效果,这正是我们需要的。

安装

npm i -S vue-navigation

引入

import Navigation from 'vue-navigation'
Vue.use(Navigation, {router})

使用

打开app.vue,用navigation标签包裹起来就大功告成了

<template>
  <div id="app">
    <navigation>
      <router-view/>
    </navigation>
  </div>
</template>

此时刷新页面,查看url会有VNK的字样就说明安装完成了

方法

vue-navigation还提供了一下监听方法,可以在不同的事件响应方法中执行不同的功能操作:

  • on()
  • once()
  • off()

每个方法都有 to、from 这个两个参数(分别代表来源路由、目标路由),to、from 包含了

  • name:路由的名称(包含 key,类型为 string)
  • route:vue-route 的路由信息对象

vue-navigation支持监听的事件类型

  • forward:前进
  • back:后退
  • replace:替换
  • refresh:刷新
  • reset:重置

例:

// app.vue:

<template>
  <div id="app">
    <navigation>
      <router-view/>
    </navigation>
  </div>
</template>
 
<script>
export default {
  name: 'App',
  created() {
      // 当路由前进时触发
      this.$navigation.on('forward', (to, from) => {
        console.log('forward to', to, 'from ', from)
      })
      // 当路由后退时触发
      this.$navigation.on('back', (to, from) => {
        console.log('back to', to, 'from ', from)
      })
      // 当路由替换时触发
      this.$navigation.on('replace', (to, from) => {
        console.log('replace to', to, 'from ', from)
      })
      // 当路由刷新时触发
      this.$navigation.on('refresh', (to, from) => {
        console.log('refresh to', to, 'from ', from)
      })
      // 当路由重置时触发
      this.$navigation.on('reset', () => {
        console.log('reset')
      })     
    }
}
</script>

此外,vue-navigation 提供了如下两个方法可以对路由记录进行操作:

  • getRoutes():获取路由记录
  • cleanRoutes():清空路由记录

例:

//在组件中使用
this.$navigation.getRoutes();

//在全局环境中使用
this.$navigation.cleanRoutes();

如果发现跳转路由时候有报错:

这是因为router的版本太高的问题,和vue/lic有冲突,此时我们只需要安装低版本的的路由就可以了,直接重装覆盖:

npm i vue-router@3.0.7 -S

我们可以利用这些监听方法,加入移动端的过场动画:

移动端的过场动画


直接贴完整代码

//app.vue:

<template>
  <div id="app">
    <transition :name="transitionName">
      <navigation>
        <router-view />
      </navigation>
    </transition>
  </div>
</template>
 
<script>
export default {
  name: "App",
  data() {
    return {
      transitionName: "",
    };
  },
  created() {
    // 前进
    this.$navigation.on("forward", (to, from) => {
      this.transitionName = "slide-left";
    });
    // 后退
    this.$navigation.on("back", (to, from) => {
      this.transitionName = "slide-right";
    });
  },
};
</script>
<style lang="less">

.slide-right-enter-active,
.slide-right-leave-active,
.slide-left-enter-active,
.slide-left-leave-active {
  position: fixed; 
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  will-change: transform;
  transition: all 0.5s;
}

// 路由前进-离开元素
.slide-left-enter {
  opacity: 0;
  transform: translate(100%, 0);
}
// 路由前进-进场元素
.slide-left-leave-active {
  opacity: 0;
  transform: translate(-100%, 0);
}

// 路由后退-进场元素
.slide-right-enter {
  opacity: 0;
  transform: translate(-100%, 0);
}
// 路由后退-离开元素
.slide-right-leave-active {
  opacity: 0;
  transform: translate(100%, 0);
}
</style>

保留滚动条高度

我们使用vue-navigation虽然可以缓存页面,但是如果有列表,它却无法记住滚动条高度,试想一下我如果下滑到列表的第100条数据,然后进详情再返回列表,此时的列表滚动条高度就会重置,我再次查看第100条数据的时候又要滑很久,这样用户体验就会比较差,所以需要我们保留滚动条的高度
我的实现原理是:配合vue-navigation,在路由前进时获取滚动条高度,然后记录在路由的meta中,等路由返回的时候,我们直接在路由的meta中获取滚动高度值,然后赋值给页面。

<template>
  <div id="app">
    <transition :name="transitionName">
      <navigation>
        <router-view />
      </navigation>
    </transition>
  </div>
</template>
 
<script>
export default {
  name: "App",
  data() {
    return {
      transitionName: "",
    };
  },
  created() {
    // 前进
    this.$navigation.on("forward", (to, from) => {
      this.transitionName = "slide-left";
      this.$nextTick(() => {
        if (document.querySelector('.slide-left-leave-active #scrollDom')) {
          from.route.meta.scrollTop = document.querySelector('.slide-left-leave-active #scrollDom').scrollTop;
        }
      });
    });
    // 后退
    this.$navigation.on("back", (to, from) => {
      this.transitionName = "slide-right";
      this.$nextTick(() => {
        if (document.querySelector('.slide-right-enter-active #scrollDom')) {
          document.querySelector('.slide-right-enter-active #scrollDom').scrollTop = to.route.meta.scrollTop;
        }
      });
    });
  },
};
</script>
<style lang="less">

.slide-right-enter-active,
.slide-right-leave-active,
.slide-left-enter-active,
.slide-left-leave-active {
  position: fixed; 
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  will-change: transform;
  transition: all 0.5s;
}

// 路由前进-离开元素
.slide-left-enter {
  opacity: 0;
  transform: translate(100%, 0);
}
// 路由前进-进场元素
.slide-left-leave-active {
  opacity: 0;
  transform: translate(-100%, 0);
}

// 路由后退-进场元素
.slide-right-enter {
  opacity: 0;
  transform: translate(-100%, 0);
}
// 路由后退-离开元素
.slide-right-leave-active {
  opacity: 0;
  transform: translate(100%, 0);
}
</style>

代码里的 #scrollDom 是关键,我们在写页面的时候如果有需要保留滚动条的话,我们只需要给滚动元素的父盒子,就是带有overflow:scroll属性的那个元素,加上id="scrollDom"就可以了,但是注意,一个页面只能有一个scrollDom,不然会就有冲突

//列表页
<template>
  <div  >
    <ul id="scrollDom">
        <li v-for="i in 150" @click='goInfo' :key="i">{{i}}</li>    
    </ul> 
  </div>
</template>

<script>
export default {
  data() {
    return {};
  },
  methods: {
      goInfo(){
        this.$router.push('/e')
      },
  },
};
</script>
<style lang="less">
ul{
    height: 90vh;
    overflow:scroll;
    background-color: pink;
}
</style>

安装FastClick

解决移动端点击事件300毫秒延迟

npm install fastclick --save

在main.js中引入就可以了

import FastClick from 'fastclick'
FastClick.attach(document.body)

最后,一定要打包试试看有没有报错!!!有时候项目运行的很正常,但是打包会报错,所以在写业务之前一定要打包试试。

posted @ 2021-12-23 17:42  Hidden28  阅读(307)  评论(1)    收藏  举报