一步步打造最新前端框架组合 —— vite+vue3+pinia+vue-router@4+setup+ts

本文将会结合自己搭建经验一步一步展开,记录最新vue前端框架如何搭建。

1、安装vite

全局安装

npm init vite@latest

2、使用vite创建vue3工程

npm init @vitejs/app 

第一步:工程文件名

第二步:选择框架

第三步:选择vue-ts组合

 第四步:进入项目文件夹,安装依赖即可

 3、配置eslint、prettier、编辑器

首先安装相关依赖,如下:

npm i prettier eslint eslint-config-prettier eslint-plugin-prettier eslint-plugin-vue vue-eslint-parser @typescript-eslint/eslint-plugin @typescript-eslint/parser -D

项目根目录新建.eslintrc.json,内容如下:

{
    "root": true,
    "env": {
      "es2021": true,
      "node": true,
      "browser": true
    },
    "globals": {
      "node": true
    },
    "extends": [
      //    "plugin:vue/essential",
      /** @see https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin#recommended-configs */
      //    "plugin:@typescript-eslint/recommended",
      //    "eslint:recommended",
      "plugin:vue/vue3-recommended",
      /**@see https://github.com/prettier/eslint-plugin-prettier#recommended-configuration */
      "plugin:prettier/recommended"
    ],
    "parser": "vue-eslint-parser",
    "parserOptions": {
      "parser": "@typescript-eslint/parser",
      "ecmaVersion": 12,
      "sourceType": "module"
    },
    "plugins": ["@typescript-eslint"],
    "ignorePatterns": ["types/env.d.ts", "node_modules/**", "**/dist/**"],
    "rules": {
      "@typescript-eslint/no-unused-vars": "error",
      "@typescript-eslint/no-var-requires": "off",
      "@typescript-eslint/consistent-type-imports": "error",
      "@typescript-eslint/explicit-module-boundary-types": "off",
      "vue/singleline-html-element-content-newline": "off",
      "vue/multiline-html-element-content-newline": "off",
      "vue/no-v-html": "off",
  
      //    "space-before-blocks": "warn",
      //    "space-before-function-paren": "error",
      //    "space-in-parens": "warn",
      //    "no-whitespace-before-property": "off",
      /**
       * Having a semicolon helps the optimizer interpret your code correctly.
       * This avoids rare errors in optimized code.
       * @see https://twitter.com/alex_kozack/status/1364210394328408066
       */
      "semi": ["error", "always"],
      /**
       * This will make the history of changes in the hit a little cleaner
       */
      //    "comma-dangle": ["warn", "always-multiline"],
      /**
       * Just for beauty
       */
      "quotes": ["warn", "single"] 
    }
  }
  

项目根目录新建.prettierrc.json,内容如下:

{
    "printWidth": 100,
    "tabWidth": 4,
    "useTabs": false,
    "semi": true,
    "vueIndentScriptAndStyle": true,
    "singleQuote": true,
    "quoteProps": "as-needed",
    "bracketSpacing": true,
    "trailingComma": "es5",
    "jsxBracketSameLine": true,
    "jsxSingleQuote": false,
    "arrowParens": "always",
    "insertPragma": false,
    "requirePragma": false,
    "proseWrap": "never",
    "htmlWhitespaceSensitivity": "ignore",
    "endOfLine": "auto",
    "rangeStart": 0
  }
  
  

项目根目录新建.editorconfig,内容如下:

root = true

[*]
charset = utf-8
# end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
ij_html_quote_style = double
max_line_length = 120
tab_width = 2
trim_trailing_whitespace = true

完成。

 4、安装使用vuex终结者 —— pinia

安装pinia:

npm i -S pinia

项目src目录下新建一个store文件夹,新建一个countStore.ts,内容如下:

// store.js
import { defineStore } from 'pinia';

// defineStore 调用后返回一个函数,调用该函数获得 Store 实体
export const useStore = defineStore({
    // id: 必须的,在所有 Store 中唯一
    id: 'myGlobalState',
    // state: 返回对象的函数
    state: () => ({
        count: 1,
    }),
    actions: {
        increaseCount() {
            this.count++;
        },
    },
});

然后再main.ts中挂载pinia;代码如下:

import { createPinia } from 'pinia';

createApp(App).use(createPinia()).mount('#app');

在组件中使用pinia:

<script setup lang="ts">
    // 引入store
    import { useStore } from '../store/countStore';
    // 使用store
    const count2 = useStore();
</script>
<template>
    <!-- 使用store中的store -->
    <p>{
   
   { count2.count }}</p>
</template>

具体关于pinia,可以查看官网 Piniahttps://pinia.vuejs.org/

状态管理这部分完成。

 5、安装使用vue-router@4

相较之前的版本,有一些变化。

但是主要是要学会再setup语法糖中如何使用。

安装:

npm i -S vue-router@4

在src目录下新建一个router文件夹,新建index.ts文件,内容如下:

import { createWebHashHistory, createRouter } from 'vue-router';
const routes = [
    {
        path: '/home', // 路由的路径
        name: 'home', // 路由的名称
        component: () => import('../components/Home.vue'), // 路由的组件
    },
    {
        path: '/about',
        name: 'about',
        component: () => import('../components/About.vue'),
    },
];

// 创建路由实例并传递 `routes` 配置
const router = createRouter({
    history: createWebHashHistory(), // 内部提供了 history 模式的实现,这里使用 hash 模式
    routes, // `routes: routes` 的缩写
});
export default router;

注意,创建路由的方法不再是new,而是一个函数。

如何在组件内部使用router或者是route?

这里介绍两个hooks函数。useRoute, useRouter。拿useRouter为例,代码如下:

<script lang="ts" setup>

   // 引入hooks
 import { useRouter } from 'vue-router';
 const router = useRouter();
    // 路由跳转
 const changeRoute = () => {
    router.push('/about')
 }

</script>

路由基本使用完成。

6、安装element-plus并实现自动导入

安装:

npm i -S element-plus

还需安装自动导入插件:

npm i -D unplugin-auto-import unplugin-vue-components

根目录新建vite.config.ts,代码如下:

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import AutoImport from 'unplugin-auto-import/vite';
import Components from 'unplugin-vue-components/vite';
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';

// https://vitejs.dev/config/
export default defineConfig({
    plugins: [
        vue(),
        AutoImport({
            resolvers: [ElementPlusResolver()],
        }),
        Components({
            resolvers: [ElementPlusResolver()],
        }),
    ],
    css: {
        preprocessorOptions: {
            scss: {
                additionalData:
                    '@import "./src/common/scss/var.scss";@import "./src/common/scss/mixin.scss";',
            },
        },
    },
    server: {
        host: '0.0.0.0',
        port: 12000,
        proxy: {
            '/local': {
                target: 'https://172.17.11.59:1111/',
                // 允许跨域
                changeOrigin: true,
                ws: true,
                rewrite: (path) => path.replace(/^\/local/, ''),
            },
        },
    },
});

如何使用?

在一个组件内无需引入,无需注册,直接使用即可。如下:

// 无需引入,直接使用即可
<template>

    <el-button></el-button>

</template>

7、封装axios并封装接口

安装:

npm i -S axios

src下新建request目录,新建index.ts文件:

import axios from 'axios';
import { ElMessage } from 'element-plus';

axios.defaults.timeout = 1000 * 60; //设置接口超时时间
axios.defaults.headers.post['Content-Type'] = 'application/json';
axios.interceptors.request.use(
    (config) => {
        // // 暂时写死token
        // // eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsImlzcyI6Imx4a2oiLCJpYXQiOjE1NjYzNTU5NTQsImp0aSI6IjY2NjY2NiJ9.5MaKCpzwnwojjUJPowXjaMrz4apl3AOAW4oK0LD7vqo
        // const TOKEN =
        //     'eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsImlzcyI6Imx4a2oiLCJpYXQiOjE1NjYzNTU5NTQsImp0aSI6IjY2NjY2NiJ9.5MaKCpzwnwojjUJPowXjaMrz4apl3AOAW4oK0LD7vqo';
        // if (TOKEN) {
        //     config.headers.common['token'] = TOKEN;
        // }
        return config;
    },
    (error) => {
        return Promise.reject(error);
    }
);

axios.interceptors.response.use(
    (response) => {
        /* let {
            data,
            config
        } = response;
        if (response.status === 200 && response.data.code == 2) {
            window.location.href = `${server_path}/#/login`;
            window.sessionStorage.clear();
            ElMessage.error('登录超时了,请重新登录');
        } */
        return response;
    },
    (error) => {
        //断网处理或者请求超时
        console.log(error);
        if (!error.response) {
            //请求超时
            if (error.ElMessage.includes('timeout')) {
                ElMessage.error('请求超时了,请稍后重试');
            } else {
                //断网,可以展示断网组件
                ElMessage.error('请检查网络连接是否正常');
            }
            return;
        }
        const status = error.response.status;
        switch (status) {
            case 500:
                ElMessage.error('服务器内部错误');
                break;
            case 404:
                ElMessage.error('未找到远程服务器');
                break;
            case 400:
                ElMessage.error('数据异常');
                break;
            default:
                ElMessage.error(error.response.data.ElMessage);
        }
        return Promise.reject(error);
    }
);

export default axios;

// 封装动态url
let _hostname;
let _protocol;
let _port;
let _hostname_sess = window.localStorage.getItem('_host');
let _protocol_sess = window.localStorage.getItem('_protocol');
let _port_sess = window.localStorage.getItem('_port');
let url1: string = '';
let modelUrl: string = '';
if (process.env.NODE_ENV == 'development') {
    // 开发环境
    /* const HOST_NAME = [
        '172.17.11.59',
    ];
    const PORT = [
        8861,
        1111
    ];
    _hostname = '172.17.11.59';
    _protocol = 'https:';
    _port = 8861; */
    url1 = '/local/';
    modelUrl = 'https://172.17.11.59:1111/';
} else if (process.env.NODE_ENV == 'production') {
    const { hostname, protocol, port } = window.location;
    // 生产环境
    _hostname = _hostname_sess ? _hostname_sess : hostname;
    _protocol = _protocol_sess ? _protocol_sess : protocol;
    _port = _port_sess ? _port_sess : port;
    url1 = `${_protocol}//${_hostname}:${_port}/`;
    modelUrl = `${_protocol}//${_hostname}:${_port}/`;
}

console.log('设置之后的ip地址是', url1);

export { url1, modelUrl };

这里需要注意的就是 —— import { ElMessage } from 'element-plus';

虽然我们配置了自动导入,但是对于element-plus中message,alert等诸如此类需要使用其方法的组件,还是要import

在src目录下新建一个apis文件夹,新建两个文件,一个是api.ts,主要是用来写所有的请求路径的;一个是index.ts,主要是用来写axios请求的。

api.ts代码如下:

import { url1 } from '../request/index';
const GET_INFOS: string = `${url1}/getInfos`;
const GET_INFOS1: string = `${url1}/getInfos`;
const GET_LIST: string = `${url1}/getList`;

export { GET_INFOS, GET_INFOS1, GET_LIST };

index.ts代码如下:

import { GET_INFOS, GET_INFOS1, GET_LIST } from './api';
import axios from '../request/index';

export const getInfos = axios.get(GET_INFOS);
export const getInfos1 = axios.get(GET_INFOS1);
export const getList = axios.get(GET_LIST);

axios基本配置完成。

8、setup语法糖中如何使用ref、props、emit、expose

他们分别对应了ref,defineProps,defineEmits, defineExpose。

其中defineProps,defineEmits, defineExpose无需引入直接并且只能在语法糖中直接使用。

举例一:使用props。

使用props,并且需要写入props的默认值,推荐使用 withDefaults.(同样无需引入)。

使用方法如下代码:

// 子组件中 Child.vue

<script setup lang="scss">
// 使用typescript定义props
interface Prop {
    count: number
}

// 直接使用defineProps
const ChildProps = withDefaults(defineProps<Prop>(), {
      count: 0,
});

</script>

<template>
    {
   
   { ChildProps.count  }}
</template>


--------------------------- 分割线 --------------------------------

// 父组件
<script setup lang="scss">
import { ref } from 'vue';

// 引入子组件 无需注册无需return直接使用
import ChildCmp from './ChildCmp.vue'

// 定义一个数字传递给子组件,同样无需return,直接使用
const count = ref<number>(0);
</script>

<template>
   <ChildCmp :count="count"/>
</template>

举例二:使用emit。

// 子组件中 Child.vue

<script setup lang="scss">
// 使用defineEmits定义事件名称
const chileEmit = defineEmits(['sendMsg']);

// 定义事件
const handleEmit = () => {
    chileEmit('sendMsg', 'hello world')
}
</script>

<template>
    <el-button @click="handleEmit">点击子组件的数据传递给父组件<el-button>
</template>


--------------------------- 分割线 --------------------------------

// 父组件
<script setup lang="scss">
// 接收子组件传递的数据
const receive = (val) => {
    console.log('接收子组件的数据', val)
}
</script>

<template>
   <ChildCmp @sendMsg="receive"/>
</template>

举例三:使用defineExpose。

// 子组件中 Child.vue
// 为什么使用defineExpose?
/**
因为setup组件默认是关闭的,也就是说每个组件内部的数据或者是方法默认情况下是不能被别的组件访问
。

所以,需要使用defineExpose将其他组件可以访问的数据或者是方法暴漏出去,让对方通过ref的形式访问到。

*/

<script setup lang="scss">
// 定义一个数据
const a: number = 1000;
// 使用defineExpose暴漏出去
defineExpose({
    a,
})
</script>

<template>
    
</template>


--------------------------- 分割线 --------------------------------

// 父组件
<script setup lang="scss">
import { ref, onMounted } from 'vue';
// 首先通过ref获取子组件然后就可以访问到数据a
const childDom = ref<any>(null);
onMounted (() => {
    console.log('childDom', childDom.value.a);
})
</script>

<template>
   <ChildCmp ref="childDom" @sendMsg="receive"/>
</template>

9、注入全局scss

首先需要安装sass

npm i -D sass

src目录下新建一个scss文件夹,新建两个文件,分别为mixin.scss和var.scss。

mixin.scss主要是经常使用的scss混入,代码参考如下:

//文本n行溢出隐藏
@mixin ellipsisBasic($clamp:2) {
    overflow: hidden;
    text-overflow: ellipsis;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: $clamp;
}

// 清除浮动
@mixin clearfix {
    &:after {
        clear: both;
        content: '.';
        display: block;
        height: 0;
        line-height: 0;
        overflow: hidden;
    }

    *height: 1%;
}

// 弹性布局
@mixin flexBox($direction: row, $justify: null, $align: null, $flex-wrap: null) {
    display: flex;

    @if ($direction !=null) {
        flex-direction: $direction;
    }

    @if ($justify !=null) {
        justify-content: $justify;
    }

    @if ($align !=null) {
        align-items: $align;
    }

    @if ($flex-wrap !=null) {
        flex-wrap: $flex-wrap;
    }
}

// 绝对定位布局
@mixin posa($top:null, $right:null, $bottom:null, $left:null) {
    position: absolute;

    @if ($left !=""& & $left !=null) {
        left: $left;
    }

    @if ($right !=""& & $right !=null) {
        right: $right;
    }

    @if ($top !=""& & $top !=null) {
        top: $top;
    }

    @if ($bottom !=""& & $bottom !=null) {
        bottom: $bottom;
    }
}

// 背景图片
@mixin bg($src, $size: cover){
    background: url($src) no-repeat center center;
    background-size: $size
}


// 模块布局
@mixin bj($h){
    width: 100%;
    height: $h;
    line-height: $h;
    box-sizing: border-box;
    padding-left: 25px;
    color: #333;
    border-right: 1px solid #eaeaea;
    border-bottom: 1px solid #eaeaea;
}

var.scss可以放一些全局使用的变量,代码如下:

// 全局
$white: #fff;
$fs: 14px;
$side-bg: #999;

定义好了之后,暂时还不能使用。因为需要在vite.config.ts中提前导入。打开vite.config.ts,

需要增加css配置项,代码如下:

......


css: {
        preprocessorOptions: {
            scss: {
                additionalData:
                    '@import "./src/common/scss/var.scss";@import "./src/common/scss/mixin.scss";',
            },
        },
},



......

然后就可以在组件中使用sass全局变量了,如下:

<style lang="scss" scoped>
    .count {
        font-size: $fs;
        color: $side-bg;
    }
</style>

至此,一个基本完整的最前沿的vue3+vite+ts的框架已经形成了。

10、vite vue 这个组合这么新,需要避坑指南吗?不完全的有一份

vite vue3 ts 项目跳坑记_ws_code_csdn的博客-CSDN博客vite vue3 遇到的问题以及解决方案https://blog.csdn.net/jmszl1991/article/details/124173690不断更新中......

猜你喜欢

转载自blog.csdn.net/jmszl1991/article/details/122825151
今日推荐