从零搭建vue3+ts+vite+element-plus

666 阅读7分钟

前置环境

  • node版本 需要16+
  • yarn版本 需要1.22.17+

创建步骤

  1. 创建项目:yarn create vite <项目名>

yarn create vite vue3-ts-admin

image.png

image.png

  1. 安装依赖

yarn

清理部分初始化后,我们暂时不需要的文件。

// src/App.tsx
import { defineComponent } from 'vue';
export default defineComponent({
    setup(){
        return ()=>{
            return <>
                <div>this is App page</div>
            </>
        }
    }
})
  1. 使用tsx语法,安装插件

yarn run dev

运行一下试试,发现报错了,打开控制台一看 ::React is not defined...... 因为我们要用到tsx,所以需要在vite配置中将vitejs/plugin-vue-jsx加入插件列表。

yarn add @vitejs/plugin-vue-jsx

// vite.config.ts
...
import vueJsx from '@vitejs/plugin-vue-jsx'
...
export default defineConfig({
  ...
  plugins: [
    vue(),
    vueJsx()
  ]
  ...
})

为了让我们更好的引入插件我们在main.ts做些改造,可能后续有些插件是异步的

// src/main.ts
import { createApp } from 'vue'
import './style.css'
import App from './App'

async function run(){
    const app = createApp(App)
    app.mount('#app')
}

run()
  1. 安装element-plus

yarn add element-plus

一般我们到这步就需要去main文件安装element组件(全部引入)。

...
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
...
async function run(){
    const app = createApp(App)
    app.use(ElementPlus)
    ...
    app.mount('#app')
}
run()

这是全部引入的方法,官网推荐使用按需引入,我们来看一下吧。

  1. 按需引入

element-plus.gitee.io/zh-CN/guide…

yarn add unplugin-auto-import --dev

yarn add unplugin-vue-components --dev

配置完每次运行都会根目录新增两个xxx.d.ts的文件。 但因为我们按需加载的文件是部分特定的格式所以我们需要配置一些参数。

// vite.config.ts
...
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
...
export default defineConfig({
  ...
  plugins: [
    vue(),
    vueJsx(),
    AutoImport({
      imports: ["vue", "vue-router"],
      include: [/\.vue$/, /\.vue\?vue/, /\.tsx$/,/\.t\?js$/],
      resolvers: [ElementPlusResolver()],
      dts:'./auto-imports.d.ts',
    }),
    Components({
      include: [/\.vue$/, /\.vue\?vue/, /\.tsx$/,/\.t\?js$/],
      resolvers: [ElementPlusResolver()],
      dts: true,
    }),
  ]
  ...
})

  1. 文件引用路径配置

因为需要用到path方法这个是node提供的一个方法,需要安装@types/node

yarn add @types/node

// vite.config.ts
import path from 'path'
export default defineConfig({
  ...
  resolve: {
    alias: {
        '@': path.resolve(__dirname,"./src")
    }
  },
  plugins: []
  ...
})
  1. 使用vue-router

router.vuejs.org/zh/installa…

yarn add vue-router@next

安装了最新的vue-router依赖

// src/router/index.ts
/* 需要单独定义一个ts类型 */
import type { AppRouteRecordRaw } from './types';
const baseRouter:AppRouteRecordRaw[] = [
    {
        path: '/',
        name: 'start',
        component: Start,
    }
]
/* 加入到路由集合中,代码量较大,查看demo代码 */
async function createRouterList(){
    return []
}
...
export async function setRouters(app:App<Element>){
    const loadRouter = await createRouterList()
    const routerList:RouteRecordRaw[] = [...baseRouter,...loadRouter]
    const history = createWebHistory()
    const router = createRouter({
        // 创建一个 hash 历史记录。
        history: history,
        // 应该添加到路由的初始路由列表。
        routes: routerList as RouteRecordRaw[],
        // 是否应该禁止尾部斜杠。默认为假
        strict: false
    });
    app.use(router);
}
...

我们刚才改的main.ts,就可以用来挂载路由了。

import { setRouters } from './router'
async function run(){
    const app = createApp(App)
    ...
    /* 配置路由 */
    await setRouters(app)
    ...
    app.mount('#app')
}
run()

扩展:自动批量导入,我们需要用到一个方法:import.meta.glob

/* 批量导入 */
const files: Record<string,any> = import.meta.glob('@/pages/**/*.tsx')

详细的代码,我们来看看。

  1. 使用axios

www.axios-http.cn/docs/api_in…

yarn add axios

新建api文件夹,新增一个index.ts来拦截接口的入参和回参。

// src/api/index.ts
import axios, { AxiosRequestConfig } from 'axios'
import { ResponseType } from './types'
/* 引入状态码文件 */
import { statusMessage } from './status'

const instance = axios.create({
    baseURL: '/api'
})

/* http response 拦截器 */
instance.interceptors.response.use(
	response => {
        /* 请求已发出,但是在2xx的范围 */
        return response;
	},
	error => {
        /* 非2xx的范围 传入响应码,匹配响应码对应信息 */
		statusMessage(error.response.status);           
		return Promise.reject(error.response.data);
	}
);

/* 封装 GET POST 请求并导出 */
const request = <T = any>(config: AxiosRequestConfig): Promise<ResponseType<T>> =>{
	return new Promise(async (resolve,reject)=>{
        const { data } = await instance.request<ResponseType<T>>(config)
        if(data.code === 0){
            resolve(data)
        }else{
            reject(data)
        }
    })
}

export default request
  1. 使用mock

mockjs.com/examples.ht…

yarn add mockjs

需要启动mock服务器createProdMockServer

yarn add vite-plugin-mock

// mock/index.ts
import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer'
/* 批量导入 */
const files = import.meta.glob('./modules/*.ts')

async function createMockModules(){
    const mockModules: any[] = []
    for(let key in files){
        const mod:any = await files[key]()
        mockModules.push(...mod.default);
    }
    return mockModules
}

/**
 * 在main.ts引入,需要手动启动mock服务
 */
export async function setupProdMockServer() {
    const mockModules = await createMockModules()
    createProdMockServer(mockModules);
}

在main.ts需要挂载后启动服务

// src/main.ts
import { setupProdMockServer } from '../mock'
...
async function run(){
    const app = createApp(App)
    ...
    /* 配置mock */
    await setupProdMockServer()
    ...
    app.mount('#app')
}

run()
  1. 使用pinia状态管理

yarn add pinia

// src/store/index.ts
import type { App } from 'vue'
import { createPinia } from 'pinia'
const store = createPinia()
export function setupStore(app: App<Element>) {
    app.use(store);
}

export { store };

在main.ts需要引入使用pinia

...
import { setupStore } from './store'
...
async function run(){
    const app = createApp(App)
    ...
    /* 配置 store , 类似Vuex 5 */
    setupStore(app)
    ...
    app.mount('#app')
}

run()

来看一个简单的例子

import { defineStore } from 'pinia'
import { ref } from 'vue';
export const useCountStore = defineStore('main', () => {
    let count = ref(0)
    function add(){
        count.value++;
    }
    return { count, add }
})

在页面上使用的话

import { useCountStore } from '@/store/modules/useCountStore';
export const Login = defineComponent({
    setup(){
        ...
        const store = useCountStore();
        ...
        store.add() // 调用更新方法
        ...
        store.count // 获取值
    }
})

今天的分享就到这里啦。


源码下载地址在

github.com/Yeyiqiang/v…


vite.config.ts配置

cn.vitejs.dev/config/

import { defineConfig } from 'vite'
import vueJsx from '@vitejs/plugin-vue-jsx'
import vue from '@vitejs/plugin-vue'
import path from 'path'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import { viteMockServe } from 'vite-plugin-mock'

// https://cn.vitejs.dev/config/
export default defineConfig({
  server: { 
    port: 2006,
    /* 反向代理 */
    proxy:{
      /* 本地开发环境通过代理实现跨域,生产环境使用 nginx 转发 */
      // '/api': 'http://localhost:4567',
    },
    open: false, /* 浏览器自动打开 */
    https: false, /* 是否开启 https */
  },
  /* 路径别名 */
  resolve: {
    alias: {
        '@': path.resolve(__dirname,"./src")
    }
  },
  /* 插件 */
  plugins: [
    vue(),
    vueJsx(),
    AutoImport({
      imports: ["vue", "vue-router"],
      /* 自动引入页面的后缀集合 */
      include: [/\.vue$/, /\.vue\?vue/, /\.tsx$/,/\.t\?js$/],
      resolvers: [ElementPlusResolver()],
      dts:'./auto-imports.d.ts',
    }),
    Components({
      /* 自动引入组件文件后缀的集合 */
      include: [/\.vue$/, /\.vue\?vue/, /\.tsx$/,/\.t\?js$/],
      resolvers: [ElementPlusResolver({
        importStyle: "sass",
      })],
      dts: true,
    }),
    viteMockServe({
      supportTs:false,
      logger: false,
      mockPath: "./mock/"
    }),
  ],
  publicDir:'public',
  /* css预处理 路径最后要加上;不然会报错 */
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: `
          @use "@/assets/styles/main.scss" as *;
        `,
      },
    },
  },
  esbuild: {
    jsxFactory: 'h',
    jsxFragment: 'Fragment'
  }
})

tsconfig.json配置

// tsconfig.json
"compilerOptions": {
  "incremental": true, // TS编译器在第一次编译之后会生成一个存储编译信息的文件,第二次编译会在第一次的基础上进行增量编译,可以提高编译的速度
  "tsBuildInfoFile": "./buildFile", // 增量编译文件的存储位置
  "diagnostics": true, // 打印诊断信息 
  "target": "ES5", // 目标语言的版本
  "module": "CommonJS", // 生成代码的模板标准
  "outFile": "./app.js", // 将多个相互依赖的文件生成一个文件,可以用在AMD模块中,即开启时应设置"module": "AMD",
  "lib": ["DOM", "ES2015", "ScriptHost", "ES2019.Array"], // TS需要引用的库,即声明文件,es5 默认引用dom、es5、scripthost,如需要使用es的高级版本特性,通常都需要配置,如es8的数组新特性需要引入"ES2019.Array",
  "allowJS": true, // 允许编译器编译JS,JSX文件
  "checkJs": true, // 允许在JS文件中报错,通常与allowJS一起使用
  "outDir": "./dist", // 指定输出目录
  "rootDir": "./", // 指定输出文件目录(用于输出),用于控制输出目录结构
  "declaration": true, // 生成声明文件,开启后会自动生成声明文件
  "declarationDir": "./file", // 指定生成声明文件存放目录
  "emitDeclarationOnly": true, // 只生成声明文件,而不会生成js文件
  "sourceMap": true, // 生成目标文件的sourceMap文件
  "inlineSourceMap": true, // 生成目标文件的inline SourceMap,inline SourceMap会包含在生成的js文件中
  "declarationMap": true, // 为声明文件生成sourceMap
  "typeRoots": [], // 声明文件目录,默认时node_modules/@types
  "types": [], // 加载的声明文件包
  "removeComments":true, // 删除注释 
  "noEmit": true, // 不输出文件,即编译后不会生成任何js文件
  "noEmitOnError": true, // 发送错误时不输出任何文件
  "noEmitHelpers": true, // 不生成helper函数,减小体积,需要额外安装,常配合importHelpers一起使用
  "importHelpers": true, // 通过tslib引入helper函数,文件必须是模块
  "downlevelIteration": true, // 降级遍历器实现,如果目标源是es3/5,那么遍历器会有降级的实现
  "strict": true, // 开启所有严格的类型检查
  "alwaysStrict": true, // 在代码中注入'use strict'
  "noImplicitAny": true, // 不允许隐式的any类型
  "strictNullChecks": true, // 不允许把null、undefined赋值给其他类型的变量
  "strictFunctionTypes": true, // 不允许函数参数双向协变
  "strictPropertyInitialization": true, // 类的实例属性必须初始化
  "strictBindCallApply": true, // 严格的bind/call/apply检查
  "noImplicitThis": true, // 不允许this有隐式的any类型
  "noUnusedLocals": true, // 检查只声明、未使用的局部变量(只提示不报错)
  "noUnusedParameters": true, // 检查未使用的函数参数(只提示不报错)
  "noFallthroughCasesInSwitch": true, // 防止switch语句贯穿(即如果没有break语句后面不会执行)
  "noImplicitReturns": true, //每个分支都会有返回值
  "esModuleInterop": true, // 允许export=导出,由import from 导入
  "allowUmdGlobalAccess": true, // 允许在模块中全局变量的方式访问umd模块
  "moduleResolution": "node", // 模块解析策略,ts默认用node的解析策略,即相对的方式导入
  "baseUrl": "./", // 解析非相对模块的基地址,默认是当前目录
  "paths": { // 路径映射,相对于baseUrl
    // 如使用jq时不想使用默认版本,而需要手动指定版本,可进行如下配置
    "jquery": ["node_modules/jquery/dist/jquery.min.js"]
  },
  "rootDirs": ["src","out"], // 将多个目录放在一个虚拟目录下,用于运行时,即编译后引入文件的位置可能发生变化,这也设置可以虚拟src和out在同一个目录下,不用再去改变路径也不会报错
  "listEmittedFiles": true, // 打印输出文件
  "listFiles": true// 打印编译的文件(包括引用的声明文件)
}

更多内容可以关注微信公众号

微信图片_20221101171958.jpg