创建工程
- vue create vue3_h5
- 选择 Manually select features
- css预处理建议选择less,因为ui插件vant是用的less,后面定制主题不需要重复装
- 进入文件夹yarn serve 运行项目
安装移动端尺寸适配插件
- 安装yarn add postcss-px-to-viewport --save-dev
- 配置
根目录创建postcss.config.js并在里面添加
module.exports ={
plugins:{
autoprefixer:{},
"postcss-px-to-viewport":{
viewportWidth:375, //视窗的宽度,对应的是我们设计稿的宽度
viewportHeight:667,//视窗的高度,对应的是我们设计稿的高度(也可以不配置)
unitPrecision:5, //指定'px'转换为视窗单位值的小数位数(很多时候无法整除)
viewportUnit:'vw',//指定需要转换成的视窗单位,建议使用vw
minPixelValue:1,//小于或者等于'1px'不转换为视窗单位
mediaQuery:false,//允许在媒体查询中转换'px'
selectorBlackList:['tabBar','tabBaritem'],//指定不需要转换的类,或者在不需要转换的,写的时候后面多跟一个类名
exclude:[/tabbar/]
}
}
}
安装vue3版本的vant库
- 安装 yarn add vant@next -S
module.exports = {
plugins: [
['import', {
libraryName: 'vant',
libraryDirectory: 'es',
style: true
}, 'vant']
]
};
- 单个页面引用
import { NavBar } from 'vant'
components: { [NavBar.name]: NavBar },
- main.js全局引入
import { Icon } from 'vant'
createApp(App).use(Icon)
定制vant主题
- 按需引入样式
在 babel.config.js 中配置按需引入样式源文件,注意 babel6 不支持按需引入样式,请手动引入样式。
module.exports = {
plugins: [
[
'import',
{
libraryName: 'vant',
libraryDirectory: 'es',
style: (name) => `${name}/style/less`,
},
'vant',
],
],
};
- 修改样式变量,如果less-loader报错,把版本降到@5.0.0
module.exports = {
css: {
loaderOptions: {
less: {
lessOptions: {
modifyVars: {
'text-color': '#111',
'border-color': '#eee',
hack: `true; @import "your-less-file-path.less";`,
},
},
},
},
},
};
路由处理
- 在router文件夹下创建module文件夹,页面的路由都放里面
export default [
{
path: '/home',
name: 'home',
component: () => import('../../views/Home.vue'),
meta: {
title: '首页',
showNav: true,
showTabbar: false,
backPath: 'app'
}
}
]
- 配置layout页面
// layout.vue
<template>
<div class="layout">
<van-nav-bar
v-if="$route.meta.showNav"
:title="$route.meta.title"
left-text="返回"
left-arrow
class="layout__nav-bar"
>
<template #left>
<van-icon name="arrow-left" size="18" @click="onClickLeft" />
</template>
<template #right>
<van-icon name="search" size="18" @click="onClickRight" />
</template>
</van-nav-bar>
<div class="app-content">
<router-view v-if="$route.meta.keepAlive"></router-view>
</div>
<van-tabbar
v-model="active"
v-if="$route.meta.showTabbar"
:placeholder="true"
>
<van-tabbar-item icon="wap-home-o">工作台</van-tabbar-item>
<van-tabbar-item icon="orders-o">工单</van-tabbar-item>
<van-tabbar-item icon="user-o">我的</van-tabbar-item>
</van-tabbar>
</div>
</template>
<script>
import { ref } from 'vue'
import { NavBar, Tabbar, TabbarItem } from 'vant'
export default {
components: {
[NavBar.name]: NavBar,
[Tabbar.name]: Tabbar,
[TabbarItem.name]: TabbarItem
},
setup () {
const active = ref(0)
const onClickLeft = () => { console.log('返回') }
const onClickRight = () => console.log('右边按钮')
return {
active,
onClickLeft,
onClickRight,
}
},
};
</script>
<style lang="less" scoped>
.layout {
height: 100vh;
overflow: auto;
background: #f0f0f0;
.layout-bar {
}
.app-content {
}
}
</style>
- 配置router文件夹下的index.js
import { createRouter, createWebHistory } from "vue-router"
import Index from '../views/index.vue'
const routerContext = require.context('./modules', true, /\.js$/)
const IndexRoute = {
path: '/',
component: Index,
meta: {},
children: []
}
routerContext.keys().forEach(route => {
if (route.startsWith('./index')) {
return
}
const routerModule = routerContext(route)
IndexRoute.children = [...IndexRoute.children, ...(routerModule.default || routerModule)]
})
let routes = [
IndexRoute
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes: routes
})
export default router
icon图标处理
- 安装依赖:
yarn add svg-sprite-loader
- 引入svg文件,在src下的assets文件夹下的icons文件夹下创建svg文件夹,里面放svg文件
- 创建svg-icon组件
import { defineComponent, computed } from "vue"
import "./style.less"
export default defineComponent({
name: "SvgIcon",
props: {
iconClass: {
type: String,
required: true
},
className: {
type: String,
default: ''
}
},
setup (props) {
const className = computed(() => {
if (props.className) {
return `svg-icon ${props.className}`
} else {
return "svg-icon"
}
})
const icon = computed(() => {
return `#icon-${props.iconClass}`
})
return () => (
<svg class={className.value} aria-hidden="true">
<use xlinkHref={icon.value} />
</svg>
)
}
})
//style.less样式文件
.svg-icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
- 在main.js中处理引入并全局注册
import { createApp } from "vue"
import App from "./App.vue"
import "./registerServiceWorker"
import router from "./router"
import store from "./store"
import { Icon } from 'vant'
import SvgIcon from '@/components/SvgIcon'
const requireAll = requireContext => requireContext.keys().map(requireContext)
const req = require.context('./assets/icons/svg', false, /\.svg$/)
requireAll(req)
createApp(App)
.use(Icon)
.use(store)
.use(router)
.component('svg-icon', SvgIcon)
.mount("#app")
ajax请求处理
- 安装axios, yarn add axios --save
- 在src下创建utils文件夹,里面创建request.js
import axios from 'axios'
let baseURL = ''
switch (process.env.NODE_ENV) {
case 'prod':
baseURL = 'https://gateway.shenzhenpoly.com/customer-service-api'
break
case 'test':
baseURL = 'https://api-gateway.shenzhenpoly.com/customer-service-api'
break
default:
baseURL = 'https://api-gateway.shenzhenpoly.com/customer-service-api'
break
}
const service = axios.create({
timeout: 60 * 1000,
baseURL: baseURL
})
service.defaults.headers.post['Content-Type'] = 'application/json;charset=UTF-8'
service.interceptors.request.use(config => {
return config
}, err => {
return Promise.reject(err)
})
service.interceptors.response.use(res => {
return Promise.resolve(res)
}, err => {
return Promise.reject(err)
})
export default service
- 在src下创建api文件夹,创建home.js
import request from '../utils/request'
export function getUserInfo (data) {
return request({
url: '/user/loginUserInfo',
method: 'POST',
data: data,
})
}
- 在组件中使用
<script>
import { onMounted } from 'vue'
import { Swipe, SwipeItem } from 'vant'
import user from './components/user'
import { getUserInfo } from './../../api/home'
export default {
name: "Home",
components: { user, [Swipe.name]: Swipe, [SwipeItem.name]: SwipeItem },
setup () {
async function getList () {
let { data } = await getUserInfo({})
console.log(data, 'data')
}
onMounted(() => {
getList()
})
}
};
</script>
附上demo地址
gitee.com/lanbin1017/…