首页 > 技术文章 > vue3创建移动端H5项目准备

pdxbb 2021-07-07 15:31 原文

一、创建项目

vue create vue3_h5

1.提示选择一个预置项,或者自定义,我们选择自定义

2.将我们需要的功能勾选上,利用上下箭头选中项,然后点击空格,(Router, Vuex, CSS Pre-processors, Linter),我这里除了typescript,PWA,unit test,E2E没选,其他基本功能都选了

3.接下来选择vue的版本:3.x

4.路由使用history模式:y

5.选择一种css预处理:sass/scss (dart-scss)

6.选择一种javascript格式化规范类型:ESLint+standart

7.保存时检测还是提交时检测并修复:lint on save

8.把以上的配置放在单独的文件中还是放在package.json文件中:in package.json

9.保存为预设置,之后系统自动安装项目就上面的设置来:no

 生成的项目目录结构如下:

二、项目配置

A.适配

首先启动项目:npm run serve , vue2项目中使用 npm run dev 启动项目

因为我创建的是h5项目,需要在移动端运行,考虑到适配问题,我们需要安装rem的插件,具体请看这篇博客:https://blog.csdn.net/u012878818/article/details/88190907

1.安装lib-flexible 

npm install lib-flexible --save
// 然后再main.js中引入
 import 'lib-flexible'

2.安装postcss-px2rem-exclude

npm install postcss-px2rem-exclude --save

然后在项目的根目录下找到文件.postcssrc.js文件(没有就创建一下),添加如下代码:

module.exports = {
  "plugins": {
    "postcss-import": {},
    "autoprefixer": {},
    "postcss-px2rem-exclude": {  // 添加的代码
      remUnit: 75,
      exclude: /node_modules|folder_name/i // 忽略node_modules目录下的文件
    }
  }
}

B.vue.config.js配置

vue3项目结构中省去了vue2中的config配置文件,取而代之的是根目录下的vue.config.js文件

在项目根目录下手动添加vue.config.js文件

module.exports = {
    publicPath:'/',  // 应用程序域名后的根目录,运行后为:http://localhost:8081/ ,如果设置子路径为/myh5/ :http://10.29.0.20:8082/myh5/
    outputDir:'dist',  // 生成生产环境文件的目录,(打包后的文件存放目录)
    assetsDir:'staticDir', // 防止生成的静态资源(js\css\img\fonts)(相对于outputDir的目录)
    indexPath:'index.html', // 指定生成的index.html的输出路径,在打包后也就是在dist文件中index.html生成的位置;如果写成a/b/c.html,那生成的dist里面index.html就是dist/a/b/c.html
    filenameHashing:false, // 默认为true,在打包之后,生成的dist目录的静态资源的文件名会追加上hash值,比如,common.f151bhg.js,设置为false,就不要hash
    // pages:{   // 多页模式下配置的,每个页面都有对应的条目文件,每个条目中都有entry,template,filename,title和chunks和其他自定义添加的属性
    //     index:{}
    // },
    lintOnSave: false,  // 设置是否在开发环境下每次保存代码时都进行eslint验证
                        // false:关闭每次保存都进行检测
                        // true:开启每次保存都进行检测,效果与warning一样
                        // ‘error’:开启每次保存都进行检测,lint 错误将显示到浏览器页面上,且编译失败。
                        // ‘default’:同’error’
                        // ‘warning’:开启每次保存都进行检测,lint 错误将显示到控制台命令行,而且编译并不会失败。
    // 所有 webpack-dev-server 的选项都支持
    devServer: {  // 配置dev的服务器,包括host,port,热更新,代理服务器配置等
      overlay: {
        warnings: false,
        errors: true
        },
        host:'http://localhost:8081/',
        port:8080,
        open:true, // 配置自动启动浏览器
        hotOnly:true, // 开启热更新
        disableHostCheck: true,  // 是否绕过主机检查,为true时表示不检查,不检查主机的应用容易受到DNS重新绑定攻击的攻击。
        proxy:{  // 配置跨域的代理服务器
          "/mall": {
            target: "http://dev.baijin.com",
            changeOrigin: true,
            secure: false,
            pathRewrite: {
              "^/mall": "/mall",
            },
          },
        }
      },
    productionSourceMap: false, // 如果你不需要生产环境的 source map,可以将其设置为 false 以加速生产环境构建。
    //     configureWebpack 和 chainWebpack 的作用相同(都是修改webpack的默认配置),唯一的区别就是它们修改 webpack 配置的方式不同:
    // ①chainWebpack 通过链式编程的形式,来修改默认的 webpack 配置
    // ②configureWebpack 通过操作对象的形式,来修改默认的 webpack 配置
    configureWebpack:{

    },
    css:{  // css
      // modules:false,  //启用css的模块化?后面被requireModuleExtension属性替代
      // 为所有的 CSS 及其预处理文件开启 CSS Modules。
      // 这个选项不会影响 `*.vue` 文件。
      requireModuleExtension:true,
      extract:false, // 使用css分离插件 ExtractTextPlugin?生产环境下是true,开发环境下是false
      sourceMap: false, // 开启 CSS source maps?
      loaderOptions: {  // 将选项传递给预加载程序处理器
        css: {}, // 这里的选项会传递给 css-loader
        postcss: {} // 这里的选项会传递给 postcss-loader
    }, // css预设器配置项
    },
    //是否为 Babel 或 TypeScript 使用 thread-loader。
    // 该选项在系统的 CPU 有多于一个内核时自动启用,仅作用于生产构建
     parallel:require('os').cpus().length > 1,
     // 向 PWA 插件传递选项
     pwa:{},

    //  可以用来传递任何第三方插件选项
    pluginOptions:{},
    // babel是一个编译器,主要作用是将ECMAScript 2015+版本的代码转换为向后兼容的js语法,
    // 因为Vue项目中普遍使用ES6语法,若要求兼容低版本浏览器,就需要引入babel,将ES6转换为E5
    babel:{}, 
}

 C.网络请求

在上面已经配置了代理服务器,现在我们就可以写页面,发送网络请求了,现在需要封装一下网络请求,如下:这里就简单封装下

组件我用的是有赞团队的vant,参照官网安装即可

main.js

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import 'lib-flexible'

import Vant from 'vant';
import 'vant/lib/index.css';

createApp(App).use(store).use(router).use(Vant).mount('#app')

npm i axios -s

在src/utils/request.js

import axios from 'axios'
import { Toast } from "vant";
const service = axios.create({
    baseURL: "", // api的base_url
    timeout: 20000, // request timeout
  });
// 添加请求拦截器
service.interceptors.request.use(function (config) {
    // 在发送请求之前做些什么
    return config;
  }, function (error) {
    // 对请求错误做些什么
    console.log(error,'error');
    return Promise.reject(error);
  });


  // 添加响应拦截器
  service.interceptors.response.use(function (res) {
    // 对响应数据做点什么
    if (res.data.code === "ACK") {
        return res.data;
      } else if (res.data.code == "401") {
        Toast("请重新登录");
      } else if (res.data.code === "NACK") {
        Toast(res.data.message);
        return Promise.reject(res.data.message);
      }
    // return response;
    
  }, function (error) {
    // 对响应错误做点什么
    Toast("服务器异常,请耐心等待");
    return Promise.reject(error);
  });

  export default service;

 现在就可以在页面里面进行网络请求了,src/views/home.vue 

import api from '../utils/request'
api.post('/user/login/uLogin', {
        // loginId: parm.Username,
        // password: parm.Password,
        appId: "68775308525830144",
        key: key,
      })
      .then(function (response) {
        console.log(response);
      })
      .catch(function (error) {
        console.log(error);
      });

另一种请求网络的方式,将url,method,data放在一起

src/api/login.js

import request from '../utils/request'
export function ulogin(data){
    return request({  // 返回的axios实例是一个promise对象,然后去then,catch
        url:'/user/login/uLogin',
        method:'post',
        data
    })
}

然后在页面中就可以这样使用:

 import {ulogin} from '../api/login'
ulogin({
        loginId: parm.Username,
        password: parm.Password,
        appId: "68775308525830144",
        key: key,
      }).then(function (response) {
        console.log(response);
        
      })
      .catch(function (error) {
        console.log(error);
      });

 D.配置页面路由

h5项目下又三个tab切换的选项,我们把他卸载App.vue 组件中,作为公共组件,然后利用路由的mate元信息来控制tab选项是否当前页面是否显示

<template>
<div class="app">
  <transition name="fade">
  <router-view/>
  </transition>
  <app-footer v-show="$route.meta.showTabbar"></app-footer>
</div>
</template>
<script>
import AppFooter from './components/Footer'
  export default {
    components:{
      AppFooter
    },
  data(){
    return{

    }
  }
  }
</script>
<style lang="scss">
.app{
  width: 100%;
  height: 100vh;
}
.fade-enter-active, .fade-leave-active {
  transition: opacity .5s;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
  opacity: 0;
}
</style>
AppFooter公共组件的代码如下
<template>
  <div class="footer">
    <router-link to="/home">
    <div class="tabber-item" @click="activeItem('home')" :style="{color:(active == 'home' ? activeColor : '#000')}">      
        <van-icon name="wap-home-o"/>
        <span>首页</span>      
    </div>
    </router-link>
    <router-link to="/order">
    <div class="tabber-item" @click="activeItem('order')" :style="{color:(active == 'order' ? activeColor : '#000')}">      
      <van-icon name="orders-o" />
      <span>订单</span>     
    </div>
    </router-link>
    <router-link to="/main">
    <div class="tabber-item" @click="activeItem('user')" :style="{color:(active == 'user' ? activeColor : '#000')}">     
      <van-icon name="user-o"/>
      <span>我的</span>     
    </div>
    </router-link>
  </div>
</template>

<script >
export default {
  name: 'appfooter',
  data(){
    return{
      active:'home',
      activeColor:'#1989fa'
    }
  },
  methods:{
    activeItem(item){
      this.active = item
    }
  }
}
</script>

<style scoped lang="scss">
  .footer{
    width: 100%;
    height: 50px;
    background: #fff;
    border-top: 1px solid #ddd;
    display: flex;
    justify-content: space-around;
    align-items: center;
    position: fixed;
    bottom: 0;
    z-index: 999;
  }
  .tabber-item{
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    .van-icon{
      font-size: 20px;
    }
    span{
      padding-top: 5px;
    }
  }
</style>

想要达到这样的一个效果:点击左边的分类切换,右边出现相应的页面,同时左右2边控制滚动;这里就需要用到嵌套路由,home组件下嵌套这子路由,然后再home组件中定义路由<router-view />

 home代码如下:

<template>
  <div class="home">
    <!-- <van-nav-bar title="首页" /> -->
    <div class="left" :style="{height:deviceHeight}">
      <van-sidebar v-model="activeKey">
        <router-link :to="{path:'/home/one'}"><van-sidebar-item title="手机" /></router-link>
        <router-link :to="{path:'/home/two'}"><van-sidebar-item title="iphone" /></router-link>
        <router-link :to="{path:'/home/one'}"><van-sidebar-item title="电脑" /></router-link>
        <router-link :to="{path:'/home/two'}"><van-sidebar-item title="显示屏" /></router-link>
        <router-link :to="{path:'/home/one'}"><van-sidebar-item title="电风扇" /></router-link>
        <router-link :to="{path:'/home/two'}"><van-sidebar-item title="空调" /></router-link>
        <router-link :to="{path:'/home/one'}"><van-sidebar-item title="充电线" /></router-link>
        <router-link :to="{path:'/home/one'}"><van-sidebar-item title="手机" /></router-link>
        <router-link :to="{path:'/home/two'}"><van-sidebar-item title="iphone" /></router-link>
        <router-link :to="{path:'/home/one'}"><van-sidebar-item title="电脑" /></router-link>
        <router-link :to="{path:'/home/two'}"><van-sidebar-item title="显示屏" /></router-link>
        <router-link :to="{path:'/home/one'}"><van-sidebar-item title="电风扇" /></router-link>
        <router-link :to="{path:'/home/two'}"><van-sidebar-item title="空调" /></router-link>
        <router-link :to="{path:'/home/one'}"><van-sidebar-item title="充电线" /></router-link>
    </van-sidebar>
    </div>
    
    <div class="right" :style="{height:deviceHeight}">
      <router-view class="view one"></router-view>
      <router-view class="view two" name="a"></router-view>
    </div>
    
  </div>
</template>
<script>
  export default {
    name:'home',
    data(){
      return{
        activeKey:0,
        deviceHeight:document.documentElement.clientHeight-50+'px', // 减去底部pp-footer公共tab组件的高度
      }
    },
    beforeRouteEnter(to,from,next){
      next()
    },
    mounted(){
    },
  }
</script>
<style lang="scss" scoped>
.home{
  // padding-top: 50px;
  padding-bottom: 50px;
  display: flex;
  background: #f2f2f2;
  .van-nav-bar{
    width: 100%;
    position: fixed;
    top: 0;
  }
  .right{
    flex:1;
    background: #fff;
    margin-left: 10px;
  }
  .left ,.right{
    overflow-y: auto;  //控制左右div的滚动
    
  }
}
</style>

 这里我是直接通过router-link来跳转到不同的页面,来改变右边的内容,实际项目中是要通过后端返回该用户的路由信息,然后前端对路由信息进行处理,转化为vue-router能够识别的路由格式,实现动态挂载路由;在实际项目中可能每个用户的角色不一样,能够看到的路由权限也不一样,这都是需要后端配合,待用户登录后拿到该用户的路由信息

推荐阅读