vue3创建移动端H5项目准备
一、创建项目
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>
<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能够识别的路由格式,实现动态挂载路由;在实际项目中可能每个用户的角色不一样,能够看到的路由权限也不一样,这都是需要后端配合,待用户登录后拿到该用户的路由信息