项目笔记第一篇:搭建一个vue+vite+TypeScript+vant项目

562 阅读7分钟

vue3+vite+TypeScript+vant项目搭建

序言

今天又接了个移动端项目,在搭建项目的过程中仍然磕磕绊绊。想想移动端项目这也算是第三个了,参考了这么多大佬的博客,仍然是这个水平,顿感羞愧。痛定思痛,决定把搭建过程详细记录下来,以便后续参考,交流学习。

链接

以下是我参考的文章链接,大佬们的文章像是黑夜里的明灯,指引着我从小小白走到小白,万分感谢。

  1. Vue3+TS+Vant3+Pinia(H5端)配置教程
  2. 带你快速构建一个vite+vue3.0+ts+naive-ui的项目
  3. Vite+Vue项目添加sass预处理器

说了这么多,让我们进入正题!

搭建一个vue+TypeScript项目

打开工作目录

BDBF8E7A-B4B7-4069-BFF2-F658C39C7739.png 1.打开终端,输入命令npm init vite@latest

2.回车执行命令,你会得到下边的界面,随你喜好给项目取个名字

Dingtalk_20221213175716.jpg 3.选择vue框架

Dingtalk_20221213175910.jpg 4. 选择TypeScript

Dingtalk_20221213180028.jpg 5.等它执行完毕,打开创建的项目文件夹,看到的目录结构是这样滴

Dingtalk_20221213180737.jpg

6.别心急哦,再次打开终端,先npm i安装依赖,然后npm run dev运行项目,看到这样的界面就算是成功搭建了一个vue3+ts+vite项目了。

DC9D1F04-E40D-464a-B9C1-7D168CE9985A.png

引入sass

vite有内置的sass配置信息,所以直接安装sass即可!(要像大佬一样精准冷酷的说话!!!),终端输入代码npm install --save-dev sass,全局搜索可以看到有以下依赖。

CC2C1E76-F5F3-4a47-8CBB-0FF301D2969B.png

接下来就可以愉快的用sass写样式了

Dingtalk_20221213181658.jpg

引入vant

按以下命令复制执行即可

# Vue 3 项目,安装最新版 Vant
npm i vant

# Vue 2 项目,安装 Vant 2
npm i vant@latest-v2

移动端适配

适配总是移动端项目绕不开的话题,这里使用的是rem适配方案。在这里介绍两个插件。

postcss-pxtorem插件

用来将px转换成rem适配(意思就是你只需要填写对应的px值,就可以在页面上自动适配,不需要自己手动转rem。但笔者项目实测,好像只有.vue文件写在style里的px会被转化成rem,行内式和外链式样式不会被转化,大家也可以试一下。)。

npm install postcss-pxtorem

不需要创建什么postcss.config.ts文件,感谢大佬的文章让我避坑。vite中是自带了这种写法,所以只需要直接在vite.config.ts中填写相关配置就可以了。

amfe-flexible插件

npm i -S amfe-flexible

下载完两个插件后一定要记得在main.ts文件中import amfe-flexible

import App from './App.vue'
//不要忘记import
import "amfe-flexible";

const app = createApp(App)
app.use(router).mount('#app')

以下是vite.config.ts文件中的配置代码

//vite.base.config.js
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import Components from 'unplugin-vue-components/vite';
//vant组件自动注册
import { VantResolver } from 'unplugin-vue-components/resolvers';
import postcssImport from "postcss-pxtorem"

export default defineConfig({
    base: './',                                     // 设置打包路径
    //vant组件自动注册
    plugins: [
        vue(),
        Components({
            resolvers: [VantResolver()],
        })
    ],
    resolve: {
        alias: [
            {
                find: '@',
                replacement: `/src`,
            },
            {
                find: '@api',
                replacement: `/src/api`,
            }
        ]
    },
    server: {
        host: '0.0.0.0',
        port: 4000,                                 // 设置服务启动端口号
        open: true,                                 // 设置服务启动时是否自动打开浏览器
        cors: true,                                 // 允许跨域
        // 设置后台请求代理,根据我们项目实际情况配置
        proxy: {
            '/api': {
                target: '',
                changeOrigin: true,
                secure: false,
                rewrite: (path) => path.replace('/api/', '/')
            }
        }
    },
    //适配
    css: {
        postcss: {
            plugins: [
                postcssImport({
                    // 这里的rootValue就是你的设计稿大小
                    rootValue: 37.5,
                    propList: ['*'],
                })
            ]
        }
    }
})

做完以上,你的移动端页面就可以适配绝大部分机型了。

vant组件自动注册

在使用组件的时候,如果用到一个引入一个,这是非常不方便的。vant提供了一种非常便捷的自动注册方法,在基于 vitewebpackvue-cli 的项目中使用 Vant 时,可以使用 unplugin-vue-components 插件,它可以自动引入组件,并按需引入组件的样式。官网有详细的介绍,这里我也简单记录一下。

# 通过 npm 安装
npm i unplugin-vue-components -D

# 通过 yarn 安装
yarn add unplugin-vue-components -D

# 通过 pnpm 安装
pnpm add unplugin-vue-components -D

插件安装结束后,在vite.config.ts编写相应的配置代码,具体见上方适配代码,有一起贴出来。

安装router4

这个时候我们可以愉快的编写页面了,路由配置是必不可少的。

npm install vue-router

图片.png

创建一个router文件夹,在router文件夹下新建一个index.ts文件。创建路由代码如下:

import {
    createRouter,
    createWebHashHistory,
    RouteRecordRaw,
} from 'vue-router';

const routes: Array<RouteRecordRaw> = [
    //工资详情界面
    {
        path: '/salary/detail', component: () => import('@/views/home/salary/SalaryDetail.vue'),
    },
];

const router = createRouter({

    history: createWebHashHistory(),

    routes,

});

//全局前置路由守卫
// router.beforeEach((to, from, next) => {
//     if (to.path == '/login') {
//         next()
//     } else {
//         if (window.sessionStorage.getItem('sid') || to.query.sid) {
//             next()
//         } else {
//             next('/login')
//         }
//     }
// })

export default router;

同样别忘了在main.ts文件中import router对象

import { createApp } from 'vue'
import router from './router';
import "amfe-flexible";
import App from './App.vue'

const app = createApp(App)
app.use(router).mount('#app')

安装axios

愉快的写完静态界面,这个时候我们要和后端的同学对接口了,这个时候要安装axios

npm install axios
pnpm install axios

在这里写下我的封装方式,仅供参考。

图片.png

我习惯于将axios二次封装后,声明成一个全局方法,所以先创建一个api.d.ts的声明文件。

interface Api {
    post(url: string, data?: any, config?: any): Promise<any>;
    get(url: string, config?: any): Promise<any>;
    put(url: string, data?: any, config?: any): Promise<any>;
    delete(url: string, config?: any): Promise<any>;
    patch(url: string, data?: any, config?: any): Promise<any>;
    postForm(url: string, data?: any, config?: any): Promise<any>;
    putForm(url: string, data?: any, config?: any): Promise<any>;
    patchForm(url: string, data?: any, config?: any): Promise<any>;
}

declare const App: {
    api: Api
}

这里声明一个全局的App对象,然后创建axios文件夹,文件夹结构如下图

图片.png

//RequestInterceptor.ts
import { AxiosRequestConfig, AxiosError } from 'axios';

const request = (config: AxiosRequestConfig<any>) => {
    // 在发送之前的配置
    return config
}

const requestError = (error: AxiosError) => {
    // 对请求错误的处理
    return error
}

export {
    request,
    requestError
}
//ResponseInterceptor.ts
import { AxiosResponse, AxiosError } from 'axios';

const response = (response: AxiosResponse<any, any>) => {
    // 对请求结果的处理
    if (response.status === 200) {
        return response.data
    }
}

const responseError = async (error: AxiosError) => {
    //对响应错误的处理
    const code = error.response?.status;
    switch (code) {
        case 401:
            // console.log('未授权');
            break;
        case 403:
            // console.log('您没有访问权限');
            break;
        case 404:
            break;
        case 500:
            break;
        case 502:
            break;
        case 503:
            break
    }
    return error
}

export {
    response,
    responseError
}
//index.ts
import axios from 'axios';
import { request, requestError } from './Interceptor/RequestInterceptor';
import { response, responseError } from './Interceptor/ResponseInterceptor';

axios.defaults.withCredentials = true
const _axios = axios.create({
    baseURL: '/api/',
    headers: {
        'Content-Type': 'application/json;charset=UTF-8;',
    },
    timeout: 100000
})

//请求拦截器
_axios.interceptors.request.use(request, requestError);

//响应拦截器
_axios.interceptors.response.use(response, responseError);

export default () => {
    if (!App.api) {
        Object.defineProperty(App, 'api', {
            value: {
                post(url: string, data?: any, config?: any) {
                    return _axios.post(url, data, config);
                },
                get(url: string, config?: any) {
                    return _axios.get(url, config);
                },
                put(url: string, data?: any, config?: any) {
                    return _axios.put(url, data, config);
                },
                delete(url: string, config?: any) {
                    return _axios.delete(url, config);
                },
                patch(url: string, data?: any, config?: any) {
                    return _axios.patch(url, data, config);
                },
                postForm(url: string, data?: any, config?: any) {
                    return _axios.postForm(url, data, config);
                },
                putForm(url: string, data?: any, config?: any) {
                    return _axios.putForm(url, data, config);
                },
                patchForm(url: string, data?: any, config?: any) {
                    return _axios.patchForm(url, data, config);
                },
            },
            writable: false,
            enumerable: true
        })
    }
}

同样别忘了import

import { createApp } from 'vue'
import router from './router';
import App from './App.vue';
import "amfe-flexible";
import axios from '@/axios'

const app = createApp(App)
app.use(router,axios).mount('#app')

创建一个test.ts文件,能够.出来全局Axios方法就算是封装成功了, 图片.png tip:补充一下,全局方法的封装逻辑,是如果找不到App对象中的api方法,则通Object.defineProperty方法定义App的api属性,将请求方法封装到App对象,发散一下,这可以实现很多不同类公共方法的封装。所以需要声明一个App对象,否则会报错。声明也很简单,在index.html文件里var一个App对象就好了,如下:

截屏2023-06-20 10.32.17.png

配置映射路径

目录层级深了,引用不方便。这个时候就需要配置路径映射了。配置路径映射也很简单,我们只需要在vite.config.ts文件中添加配置就好。代码如下:

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
  resolve: {
    //路径映射
    alias: [
      {
        find: '@',
        replacement: `/src`,
      },
      {
        find: '@api',
        replacement: `/src/api`,
      }
    ]
  },
})

推测如果是js,到这一步应该就可以了,但要通过Ts编译的话,还需要在tsconfig.json中添加配置,否则会报红。配置如下:

{
    /*其他配置省略*/
  "compilerOptions": {
    //路径映射配置
    "paths": {
      "@/*": [
        "./src/*"
      ]
    }
  }
}

这样写可以避免长串的../../../,使得路径导入变得简洁而优雅。使用方法如下: 图片.png

结束

再次感谢各位大佬对我的帮助!!!