【Vue3/Vant3】 搭建移动应用

技术栈

vue3.2(setup) + ts + vue-router@4 + pinia + vite + axios + vant3 

脚手架搭建

npm init vue@latest
cd Admin   
npm i

 清理文件

组件安装

1.设置移动端适配

安装插件

npm i postcss-px-to-viewport -D

npm i amfe-flexible -D

配置vite.config.ts

import postCssPxToRem from 'postcss-pxtorem'
export default defineConfig({
	plugins: [
		...	],
	resolve: {
		...
	},
	css: {
		// 此代码为适配移动端px2rem
		postcss: {
			plugins: [
				postCssPxToRem({
					rootValue: 37.5, // 1rem的大小
					propList: ['*'], // 需要转换的属性,这里选择全部都进行转换
				}),
			],
		},
	},
})

main.ts 

import 'amfe-flexible'

2.安装axios

npm i axios -S

axios二次封装 

utils/request.js

import axios from 'axios'
export let baseURL = 'http://10.7.162.150:8089'
/**
 * process.env.NODE_ENV
 *    production 生产环境
 *        npm run build
 *
 *    development  开发环境
 *        npm run dev
 *
 */
switch (process.env.NODE_ENV) {
	case 'production':
		baseURL = 'https://api.yuguoxy.com'
		break
	case 'development':
		baseURL = 'http://10.7.162.150:8089'
		break
}
const axiosServer = axios.create({
	baseURL,
	timeout: 5000,
})
//请求拦截器
axiosServer.interceptors.request.use(
	config => {
		// console.log('请求拦截器 config ', config)
		// 设置token到authorization头部
		let token = localStorage.getItem('TOKEN')
		if (token) {
			// console.log('config.headers ',config.headers);
			config.headers['Authorization'] = token
		}

		return config
	},
	error => {
		// 对请求错误做些什么
		return Promise.reject(error)
	}
)
//响应拦截器
axiosServer.interceptors.response.use(
	function (response) {
		return response.data
	},
	function (error) {
		// 对响应错误做点什么
		return Promise.reject(error)
	}
)
export default axiosServer

3.解决引入vue组件ts报错问题

en.d.ts文件

declare module "*.vue" {
  import { DefineComponent } from "vue"
  const component: DefineComponent<{}, {}, any>
  export default component
}

4.vant组件安装

npm i vant

按需引入

npm i unplugin-vue-components -D

配置插件

//vite.config.js
const { VantResolver } = require('unplugin-vue-components/resolvers');
const ComponentsPlugin = require('unplugin-vue-components/webpack');
module.exports = {
  configureWebpack: {
    plugins: [
      ComponentsPlugin({
        resolvers: [VantResolver()],
      }),
    ],
  },
};

使用组件

<template>
  <van-button type="primary" />
</template>

引入函数组件的样式

可以在项目的入口文件或公共模块中引入以上组件的样式,这样在业务代码中使用组件时,便不再需要重复引入样式了。

//main.ts
import 'vant/es/toast/style';
import 'vant/es/dialog/style';
import 'vant/es/notify/style';
import 'vant/es/image-preview/style';
//xx.vue 在setup里
import { Toast } from 'vant';
import { Dialog } from 'vant';
import { Notify } from 'vant';
import { ImagePreview } from 'vant';
        import { Notify } from 'vant';
        const username = ref('');
        const password = ref('');
        const onSubmit = () => {
            Notify({ type: 'success', message: '登录成功' }); //登录成功弹出
        };

5.项目样式重置

npm install normalize.css -S
// main.ts
import 'normalize.css'

6.安装css预处理器依赖  

npm i sass -S 

7.路由vue-router

useRoute 和 useRouter 的区别

  • useRoute 路由信息对象,获得路由参数
  • useRouter 路由跳转

home.ts 路由跳转 暴露

import { useRouter } from "vue-router"; //useRouter(路由跳转)
//商品详情跳转
export const userDetailGoon = ()=>{
    const router = useRouter()
    const onDetail = (id:number) => {
         router.push({ path: '/detail/'+id }) //动态传参 动态跳转路由界面 详情页需要路由接收
    }
    return {onDetail}
}

detail.ts 路由接收 暴露

import { ref,onMounted } from "vue";
import { useRoute } from "vue-router"; //导入 useRouter钩子函数useRoute 路由信息对象(获取路由参数)
export const useDetailMessage = ()=>{
    const route = useRoute() //拿到路由信息对象
        const onClickLeft = () =>{
            history.back()  //返回上一页
        }
    onMounted (()=>{
         getProductDetail(Number(route.params.id)) //路由接收router.push({ path: '/detail/'+id })的id
       })
    const getProductDetail = async (id:number)=>{
        let data =await RequestProductDetail(id)
        let {resultCode,resultInfo} = data
        if(resultCode == 1){
            product.value =resultInfo
        }
    }
    return {onMounted, onClickLeft}
}

引入

<template>
    <div>
        <van-nav-bar title="详情页" left-text="返回" left-arrow @click-left="onClickLeft" />
    </div>
</template>
<script setup lang="ts">
import { useDetailMessage  } from "@/hooks/detail";  //引入
const {onClickLeft} = useDetailMessage () //解构
</script>

8.pinia使用

在stores下脚手架帮忙生成了count.ts的文件,改为cart.ts

import { defineStore } from "pinia"; //1
/**interface ICartProduct{
  id:number,
  name:string,
  url:string,
  price:number,
  state:boolean,
  num:number
}*/
/*interface IState{
	list:Array<ICartProduct>
}*/                                      //cart名字随便取
export const useCounterStore = defineStore("cart",{ //2
  state:():IState=>{  //数据
    return{
      list:[] //购物车商品列表
    }
  },
  actions:{//不用定义mutations
    addProduct(product:ICartProduct){//购物车添加方法
        this.list.push(product)  //这里可以用this
    }
  },
  getters:{
    cartList:state => state.list //cartList不能和上面List名字一样
  }
})

detail.ts 

import { useCounterStore } from "@/stores/cart";//引入pinia中暴露的函数
//pinia不能用平常的解构,解构了会失去响应性,在addCart里解构
import { Toast } from "vant";
export const useDetailMessage = ()=>{
    //购物车
    const addCart = ()=>{
        const store = useCounterStore()  //解构
        let cartList = store.cartList  //pinia getters
        //判断购物车cartList相同商品是否存在,存在则商品数量+1
       let oldProduct = cartList.find((item:IProduct)=>{
        //购物车cartList的id是否和新加入购物车商品id相同
       return item.id == product.value.id})    
        if(oldProduct){//存在则商品数量+1
            oldProduct.num++
        }else{//不存在则添加新商品
            store.addProduct({ //pinia actions方法
                id:product.value.id,
                name:product.value.product,
                url:product.value.picture,
                price:product.value.price,
                num:1,
                state:false
            })
        }
        Toast.success('加入成功!')
    }
    return {addCart}
}

pinia持久化存储

安装

npm i pinia-plugin-persist

集成插件

        stores/index.ts

import { createPinia } from 'pinia'
// 引入持久化插件
import piniaPluginPersist from 'pinia-plugin-persist'
const store = createPinia() //创建pinia存储根store
store.use(piniaPluginPersist) //集成持久化存储插件
export default store

        main.ts

//引入持久化插件
import store from "@/stores/index"; //默认暴露
app.use(store)

使用

stores/cart.ts

export const useCounterStore = defineStore("cart",{
     state:()=>{  //数据
       ...
      },
  actions:{//不用定义mutations
    ....
  },
  getters:{
    ...
  },
    // 使用该插件,开启数据缓存
	persist: {
		//这里存储默认使用的是session
		enabled: true,
		strategies: [
			{
				//key的名称
				key: 'cartKey',
				//更改默认存储,我更改为localStorage
				storage: localStorage,
				// 可以选择哪些进入local存储,这样就不用全部都进去存储了
				// 默认是全部进去存储
				paths: ['list'],
			},
		],
	},
}

9.购物车响应式商品列表

import {useCounterStore} from "@/stores/cart"  //引入cart.ts pinia
import {ref} from "vue"  //ref包裹为响应式 调用需要.value
// 购物车业务
export const useCart = ()=>{
    const store = useCounterStore()  //解构
    const cartList = ref(store.cartList)  //改为响应式购物车商品列表
    return{}
}

10.自定义组件

components/HeaderSearch.vue

<template>
	<div>
		<van-nav-bar>
			<template #left>
				<router-link to="/city">城市</router-link>
			</template>
			<template #title>
				<router-link to="/search">搜索</router-link>
			</template>
			<template #right>
				<van-image
					v-if="user.headerimg"
					:src="user.headerimg"
					type="contain"
					round
					width="40px"
					height="40px"
					@click="$router.push('/login')"
				/>
				<router-link to="/login" v-else>登录</router-link>
			</template>
		</van-nav-bar>
	</div>
</template>
<script setup lang="ts">
import { useUserStore } from '@/stores/user'
interface IUser {
	nick: string
	headerimg: string
}
const store = useUserStore()
const user:IUser = store.userInfo
</script>

 views/Home/index.vue

<template>
	<div>
		<HeaderSearch></HeaderSearch>
    </div>
</template>
<script setup lang="ts">
    import HeaderSearch from '@/components/HeaderSearch.vue'  //引入
</script>

11.样式穿透

用 :deep()包裹将组件起来

:deep(.van-nav-bar__title) {
		background-color: white;
		height: 30px;
		line-height: 30px;
		border-radius: 30px;
		box-sizing: border-box;
		max-width: 70%;
		flex: 1;
		text-align: left;
		padding-left: 30px;
		a {
			color: #232326;
			font-size: 16px;
		}
	}

12.mock

下载 

npm i mockjs vite-plugin-mock

根目录下新建mock文件夹 新建文件user.ts 自建接口文档

export const hotType = {
	url: '/api/type',
	method: 'get',
	response: () => {
		return {
			resultCode: 1,
			resultInfo: {
				list: [
					{
						id: 1001,
						text: '京东超市',
						url: 'https://m15.360buyimg.com/mobilecms/jfs/t1/175540/24/19329/6842/60ec0b0aEf35f7384/ec560dbf9b82b90b.png!q70.jpg',
					},
				],
			},
		}
	},
}
export const testRegister = {
	url: '/api/h52208/register',
	method: 'get',
	response: () => {
		return {
			code: 0,
			message: 'ok',
			data: {
				id: '@increment()',
				username: '',
				password: '',
			},
		}
	},
}

mock文件下新建index.ts  

import {hotType,testRegister} from './user' 引入
export default [
    hotType,
    testRegister
]

修改vite.config.ts

import { viteMockServe } from 'vite-plugin-mock'
export default defineConfig({
	plugins: [
		vue(),
		...,
		viteMockServe({
			// 更多配置见最下方
			supportTs: true,
			logger: false,
			mockPath: './mock/', // 文件位置
		  }),	 
	],
})

使用接口

api/index.ts

//import axiosServer from '../utils/request'
import axios from 'axios'
/**
 * 热门商品
 */
export const RequesetHotProduct =()=>{
    return axios({  //不是axiosServer 这里axiossever加了根地址
        method:'get',
        url:'/api/type'
    })
}

动态渲染

import{RequesetHotProduct} from '@/api/index'
/**
 * 热门商品
 */
export const useHotProduct = ()=>{
	let hotList = ref([])
	const getHotProduct = ()=>{
		RequesetHotProduct().then(res=>{  //这里不像axiosserver把data过滤了,这里必须要res.data
			let {resultCode,resultInfo} = res.data
			if(resultCode == 1){
				hotList.value = resultInfo.list
			}
		})
	}
	onMounted(()=>{
		getHotProduct()
	})
	return {hotList}
}

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于Vue 3和Vant移动端TabBar的使用,你可以按照以下步骤进行操作: 1. 首先,确保你已经安装了Vue 3和Vant。你可以使用以下命令来安装它们: ```bash npm install vue@next vant ``` 2. 在你的Vue项目中,创建一个新的组件文件,例如`TabBar.vue`。 3. 在`TabBar.vue`文件中,引入VueVant的相关组件和样式: ```javascript <template> <div> <van-tabbar v-model="active"> <van-tabbar-item icon="home-o" to="/"> 首页 </van-tabbar-item> <van-tabbar-item icon="search" to="/search"> 搜索 </van-tabbar-item> <van-tabbar-item icon="star-o" to="/favorites"> 收藏 </van-tabbar-item> <van-tabbar-item icon="contact" to="/profile"> 个人中心 </van-tabbar-item> </van-tabbar> </div> </template> <script> import { Tabbar, TabbarItem } from 'vant'; export default { components: { [Tabbar.name]: Tabbar, [TabbarItem.name]: TabbarItem, }, data() { return { active: '/', }; }, }; </script> <style> /* 这里可以添加自定义样式 */ </style> ``` 4. 在你的主应用组件中,例如`App.vue`,使用`TabBar`组件: ```html <template> <div id="app"> <!-- 其他内容 --> <TabBar /> </div> </template> <script> import TabBar from './components/TabBar.vue'; export default { components: { TabBar, }, }; </script> <style> /* 这里可以添加全局样式 */ </style> ``` 5. 最后,你可以根据自己的需要在`TabBar.vue`中设置每个Tab项的图标、文字和链接。你还可以通过修改`active`的值来控制当前选中的Tab。 这样,你就可以在Vue 3项目中使用Vant移动端的TabBar了。记得根据自己的需求进行样式和功能的调整。希望对你有帮助!如果有任何问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值