IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> 【Vue3/Vant3】 搭建移动应用 -> 正文阅读

[JavaScript知识库]【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}
}

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-10-31 11:46:33  更:2022-10-31 11:49:22 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/17 17:02:17-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码