当前位置:   article > 正文

使用vue3 + TS + Pinia + Vant4 + vite搭建商城H5项目框架_vue h5框架

vue h5框架

本文主要将如何利用搭建一个初始化的商城H5项目框架。初始化阶段使用的技术栈有:vue3.2、vue-router、 TS 、 Pinia 、 Vant4、Less、vite

1. 环境检测:

                        node -v 检测是否有安装node.js,未安装请先去官网安装node.js

2. 创建初始化项目: 

        终端输入: npm init vite

        自定义项目名称project name:demodemo

        依次选择Vue + TypeScript创建项目

       启动项目:   cd demodemo

                                npm install

                                npm run dev

3. 初始化项目文件

删除style.css文件,清空assets文件和components文件

新增request文件(配置网络请求和集中存储请求API),文件下新建index.ts和api.ts两个空文件 

新增store文件(数据共享仓库),文件下新建index.ts和order.ts和address.ts三个空文件 

新增views文件(集中存放各页面),文件下新建Home.vue和Login.vue和404.vue等空文件 

新增types文件(声明TS类型),文件下新建api.d.ts一个空文件

 处理App.vue文件

  1. //App.vue
  2. <script setup lang="ts">
  3. </script>
  4. <template>
  5. //路由占位符
  6. <router-view />
  7. </template>
  8. <style scoped lang="less">
  9. </style>

 处理main.ts文件

  1. import { createApp } from 'vue'
  2. import App from './App.vue'
  3. const app = createApp(App)
  4. app.mount('#app')

4. 样式初始化,引入vant组件库,浏览器适配

1)样式初始化

        npm i reset-css

  1. //在main.ts中引入
  2. import "reset-css" //样式初始化

2)引入vant组件库

        npm i vant

  1. //在main.ts中引入vant
  2. import vant from "vant"
  3. import "../node_modules/vant/lib/index.css"

3)浏览器适配

        npm i postcss-px-to-viewport --save -dev

新建文件postcss.config.cjs,配置设计稿的视口宽度:viewportWidth: 375,其余可选配置

  1. //postcss.config.cjs文件
  2. module.exports = {
  3. plugins: {
  4. 'postcss-px-to-viewport': {
  5. viewportWidth: 375, // 设计稿的视口宽度
  6. unitPrecision: 5, // 单位转换后保留的精度
  7. propList: ["*"], // 能转化为vw的属性列表
  8. viewportUnit: "vw", // 希望使用的视口单位
  9. fontViewportUnit: "vw", // 字体使用的视口单位
  10. selectorBlackList: [], // 需要忽略的CSS选择器,不会转为视口单位,使用原有的px等单位。
  11. minPixelValue: 1, // 设置最小的转换数值,如果为1的话,只有大于1的值会被转换
  12. mediaQuery: false, // 媒体查询里的单位是否需要转换单位
  13. replace: true, // 是否直接更换属性值,而不添加备用属性
  14. exclude: undefined, // 忽略某些文件夹下的文件或特定文件,例如 'node_modules' 下的文件
  15. include: undefined, // 如果设置了include,那将只有匹配到的文件才会被转换
  16. landscape: false, // 是否添加根据 landscapeWidth 生成的媒体查询条件 @media (orientation: landscape)
  17. landscapeUnit: "vw", // 横屏时使用的单位
  18. landscapeWidth: 568, // 横屏时使用的视口宽度
  19. },
  20. },
  21. };

5. 自动导入常用的使用的第三方库的 API

npm i -D unplugin-auto-import

  1. //在vite.config.ts中配置
  2. import AutoImport from "unplugin-auto-import/vite"
  3. export default defineConfig({
  4. plugins: [
  5. vue(),
  6. AutoImport({
  7. imports: [ 'vue', 'vue-router'] //自动导入相关函数
  8. })
  9. ],
  10. })

6. 配置网络请求,全局配置请求的参数和返回值TS类型

        npm i axios

  1. //在request文件下的index.ts配置
  2. import axios from "axios"
  3. const instance = axios.create({
  4. baseURL: "https://XXXXXX.com",
  5. timeout: 20000
  6. })
  7. //请求拦截器
  8. instance.interceptors.request.use(
  9. config => {
  10. if(localStorage.getItem('token')){
  11. config.headers.Authorization = localStorage.getItem('token')
  12. }
  13. return config
  14. },
  15. err => {
  16. return Promise.reject(err)
  17. }
  18. )
  19. // 响应拦截器
  20. instance.interceptors.response.use(
  21. res => {
  22. return res.data
  23. },
  24. err => {
  25. return Promise.reject(err)
  26. }
  27. )
  28. export default instance
  1. //在request文件下的api.ts配置
  2. import request from "./index"
  3. export const helpCenterRes = () => request.get('/helpcenter')
  4. export const bannerAPIRes = ( params: BannerAPIReq ):Promise<BannerAPIRes> => request.get('/banner', {params})

在types文件下api.d.ts,配置各个请求接口的参数类型和返回值类型,并在api.ts中应用

  1. interface BannerAPIReq {
  2. title: string;
  3. }
  4. interface BannerListType {
  5. detail_url: string;
  6. picture: string;
  7. url: string;
  8. }
  9. interface BannerAPIRes {
  10. code: number;
  11. message: string;
  12. banner: BannerListType[];
  13. }
  1. //在Index.vue中使用封装的接口
  2. <script setup lang="ts">
  3. import { ref,onMounted } from "vue"
  4. import { bannerAPIRes } from '../request/api'
  5. onMounted( () => {
  6. getBannerRes()
  7. })
  8. interface BannerListType {
  9. detail_url: string;
  10. picture: string;
  11. url: string;
  12. }
  13. const bannerList = ref<BannerListType[]>([])
  14. const getBannerRes = async() => {
  15. const res:any = await bannerAPIRes({
  16. title: 'newapp'
  17. })
  18. console.log(res)
  19. if(res.code == 200){
  20. bannerList.value = res.banner
  21. }
  22. }
  23. </script>
  24. <template>
  25. <van-swipe :autoplay="3000" :height="420" lazy-render>
  26. <van-swipe-item v-for="item in bannerList">
  27. <img style="width: 100%;height: 419.4px;" :src="item.picture" />
  28. </van-swipe-item>
  29. </van-swipe>
  30. </template>
  31. <style scoped lang="less">
  32. </style>

7.  配置路由

npm i vue-router          //下载vue-router插件第四版本

  1. //main.ts中引入和挂载路由
  2. import router from './router'
  3. app.use(router)
  1. //router/index.ts
  2. import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router";
  3. import Home from "../views/Home.vue"
  4. import Index from "../views/Index.vue"
  5. const routes: Array<RouteRecordRaw> = [
  6. {
  7. path: '/',
  8. component: Home,
  9. redirect: '/index', //路由重定向
  10. children: [ //路由嵌套
  11. {
  12. path: 'index',
  13. name: 'Index',
  14. component: Index
  15. },
  16. {
  17. name: 'Wash',
  18. path: 'wash',
  19. component: () => import('../views/Wash.vue')
  20. },
  21. {
  22. name: 'Order',
  23. path: 'order',
  24. component: () => import('../views/Order.vue')
  25. },
  26. {
  27. name: 'My',
  28. path: 'my',
  29. component: () => import('../views/My.vue')
  30. }
  31. ]
  32. },
  33. {
  34. name: 'Login',
  35. path: '/login',
  36. component: () => import('../views/Login.vue')
  37. },
  38. {
  39. name: "404",
  40. path: "/:pathMatch(.*)*",
  41. component: () => import('../views/404.vue')
  42. }
  43. ]
  44. const router = createRouter({
  45. history: createWebHistory(),
  46. routes
  47. })
  48. router.beforeEach( ( news,old,next ) => {
  49. next()
  50. })
  51. export default router

8. 配置pinia,并做持久化存储

npm i pinia

npm i pinia-plugin-persist

  1. //在store/index.ts配置pinia和共享数据持久化处理
  2. import { createPinia } from "pinia";
  3. import piniaPluginPersist from "pinia-plugin-persist"
  4. const store = createPinia()
  5. store.use(piniaPluginPersist)
  6. export default store
  1. //在mian.ts中引入并挂载pinia
  2. import store from "../src/store/index"
  3. app.use(store)

为了避免共享的数据混乱放在一个文件下,因此需模块化处理不同页面的共享数据

order.ts表示订单页面涉及到的共享数据及数据的处理方法

address.ts表示地址页面涉及到的共享数据及数据的处理方法

以order.ts为示例

  1. import { defineStore } from "pinia";
  2. export const orderStore = defineStore( "order", {
  3. //存放需要共享的数据
  4. state: () => {
  5. return {
  6. orderType: "takein",
  7. count: 1
  8. }
  9. },
  10. //Store 状态的计算值
  11. getters: {
  12. doubleCount: (state) => state.count * 2
  13. },
  14. //存放同步和异步方法
  15. actions: {
  16. countAdd(num: number){
  17. this.count = this.count + num
  18. }
  19. },
  20. //开启数据持久化存储
  21. persist: {
  22. enabled: true
  23. }
  24. })
  1. //在页面使用store中的数据
  2. <script setup lang="ts">
  3. import { storeToRefs } from 'pinia';
  4. import { orderStore } from '../store/order'; //引入order模块数据
  5. const orderstore = orderStore()
  6. const {count, doubleCount} = storeToRefs(orderstore) //解构成响应式数据
  7. const addNum = (num: number) => {
  8. orderstore.countAdd(num) //用actions中的方法
  9. }
  10. </script>
  11. <template>
  12. <div>state中的数据响应式展示: {{ count }} </div>
  13. <div>getters中的数据响应式展示: {{ doubleCount }} </div>
  14. <button @click="addNum(10)">点击操作state中的数据</button>
  15. </template>
  16. <style scoped lang="less">
  17. </style>

 开启数据持久化存储后,刷新页面,数据不会变成原始数据

9. 设置网站logo和网站名称

在index.html文件配置网站logo和网站名称

网站logo的是.ico格式,其他格式图片需要转成.ico文件

  1. <head>
  2. <meta charset="UTF-8" />
  3. <link rel="icon" href="/tidylogo.ico" /> //配置logo
  4. <title>XXXXX有限公司官方网站</title> //配置网站名称
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  6. </head>

10. 配置less

        npm i less -D

  1. <style scoped lang="less">
  2. @color: pink; //定义一个颜色变量
  3. //类名嵌套
  4. .title{
  5. div{
  6. color: @color; //使用变量
  7. }
  8. }
  9. </style>

11. 首页配置自定义tabbar底部栏

  1. //Home.vue中配置tabbar栏数据和跳转路由等,同时在router/index.ts中配置相应的路由
  2. <script setup lang="ts">
  3. import { ref,reactive,onMounted } from "vue"
  4. onMounted( () => {
  5. })
  6. const active = ref(0)
  7. const tabbarList = reactive([
  8. {
  9. title: '首页',
  10. path: '/index',
  11. inactive: 'https://tidyimages.oss-cn-hangzhou.aliyuncs.com/newh5/home.png',
  12. active: 'https://tidyimages.oss-cn-hangzhou.aliyuncs.com/newh5/home_active.png'
  13. },
  14. {
  15. title: '洗衣',
  16. path: '/wash',
  17. inactive: 'https://tidyimages.oss-cn-hangzhou.aliyuncs.com/newh5/wash.png',
  18. active: 'https://tidyimages.oss-cn-hangzhou.aliyuncs.com/newh5/wash_active.png'
  19. },
  20. {
  21. title: '订单',
  22. path: '/order',
  23. inactive: 'https://tidyimages.oss-cn-hangzhou.aliyuncs.com/newh5/order.png',
  24. active: 'https://tidyimages.oss-cn-hangzhou.aliyuncs.com/newh5/order_active.png'
  25. },
  26. {
  27. title: '我的',
  28. path: '/my',
  29. inactive: 'https://tidyimages.oss-cn-hangzhou.aliyuncs.com/newh5/my.png',
  30. active: 'https://tidyimages.oss-cn-hangzhou.aliyuncs.com/newh5/my_active.png'
  31. }
  32. ])
  33. const onChangeTab = (index: number | string) => {
  34. console.log(index)
  35. }
  36. </script>
  37. <template>
  38. <router-view></router-view>
  39. <van-tabbar v-model="active" active-color="#26D7CC" route @change="onChangeTab">
  40. <van-tabbar-item replace :to="item.path" v-for="(item,index) in tabbarList" :key="index">
  41. <span :class="active === index ? active : ''">{{ item.title }}</span>
  42. <template #icon="props">
  43. <img :src="props.active ? item.active : item.inactive" />
  44. </template>
  45. </van-tabbar-item>
  46. </van-tabbar>
  47. </template>
  48. <style scoped lang="less">
  49. </style>

效果图如下:


​​​​​​​

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小舞很执着/article/detail/905724
推荐阅读
相关标签
  

闽ICP备14008679号