hello大家好我是雪人⛄,不知不觉断更好久了,经过了长时间的学习,终于踏入了前端工程化的大门,大家再日常开发中总是会用到一个开发工具:脚手架,大家在使用其他人的脚手架或者一些官方脚手架的时候,可能只懂得使用并不懂得如何实现,看到一些代码只是知其然不知其所以然,今天为大家带来一套教程,教大家入门“脚手架”,相信你一定会有所收获。
目前项目已开源且仍处于开发阶段,后续会更新更多内容,如有不正确的地方请大家指正,我会及时更新并纠正我的错误。
- GitHub:LonelySnowman/sv3-template
- 官方文档:SV3-Family | Vue3
项目使用的依赖:
参考学习项目:
下面是我学习到的一些项目目录结构,大家可以参考,学习完毕后可以根据自己的习惯进行更改。
sv3-template/
|- .husky/ # git钩子配置
|- build/ # 项目打包配置
|- mock/ # 数据请求模拟
|- plop-templates/ # 项目开发模板
|- public/ # 不经过打包的静态资源
|- type/ # ts类型定义
|- src/ # 项目资源
|- api/ # http请求管理
|- assets/ # 经过打包的静态资源
|- components/ # 通用组件
|- hooks/ # 通用组件状态逻辑函数
|- router/ # 项目路由管理
|- store/ # 组件状态管理
|- styles/ # 项目通用样式
|- utils/ # 工具函数
|- http/axios/ # axios封装
|- views/ # 页面组件
pnpm install axios pinia pinia-plugin-persistedstate vue vue-router nprogress
# 本项目使用 element plus 大家可以根据个人习惯选择自己常用的组件库
pnpm install element-plus @element-plus/icons-vue
pnpm install -D typescript sass
下面简单介绍一下这些依赖的作用,大家根据个人习惯选择安装即可。
首先应该搭建一个基础的Vue项目结构
sv3-template/
|- public/ # 不经过打包的静态资源
|- src/ # 项目资源
|- assets/ # 经过打包的静态资源
|- components/ # 通用组件
|- styles/ # 项目通用样式
|- utils/ # 工具函数
|- http/axios/ # axios封装
|- views/ # 页面组件
|- App.vue # 项目的主组件
|- main.ts # 入口ts文件
| - index.html # 入口html文件
index.html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>sv3-templatetitle>
head>
<body>
<div id="app">div>
<script type="module" src="/src/main.ts">script>
body>
html>
/src/App.vue
/src/styles/_reset.scss
_reset.scss
是进行一个对基础HTML默认样式的重置html,
body,
p,
ol,
ul,
li,
dl,
dt,
dd,
blockquote,
figure,
fieldset,
legend,
textarea,
pre,
iframe,
hr,
h1,
h2,
h3,
h4,
h5,
h6 {
margin: 0;
padding: 0;
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-size: 100%;
font-weight: normal;
}
ul {
list-style: none;
}
button,
input,
select {
margin: 0;
}
html {
box-sizing: border-box;
}
*, *::before, *::after {
box-sizing: inherit;
}
img,
video {
height: auto;
max-width: 100%;
}
iframe {
border: 0;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
td,
th {
padding: 0;
}
/src/main.ts
import { createApp } from 'vue';
import App from './App.vue';
import './styles/_reset.scss';
const app = createApp(App);
app.mount('#app');
/views/xxx.vue
创建页面结构
views/
|- home/ # 页面文件
|- components/ # 放置页面使用的组件
|- xxx.vue
|- index.vue # 经过打包的静态资源
我们这里可以随便写一个简单的组件
Hello Vue
/src/router/index.ts
@
是我们配置的别名,指向了src,在后面会讲解到如何配置import { createRouter, createWebHashHistory, RouteRecordRaw, RouteRecordRaw } from 'vue-router';
import NProgress from 'nprogress';
import 'nprogress/nprogress.css';
// 配置路由
const routes: Array<RouteRecordRaw> = [{
path: '/',
name: 'Home',
component: () => import('@/viwes/home/index.vue'),
meta: {},
children: [],
}];
const router = createRouter({
history: createWebHashHistory(),
routes,
});
router.beforeEach(async (_to, _from, next) => {
NProgress.start();
next()
});
router.afterEach((_to) => {
NProgress.done();
});
export default router;
/src/main.ts
import { createApp } from 'vue';
import App from './App.vue';
import router from '@/router'; // ++
import './styles/_reset.scss';
const app = createApp(App);
app.use(router); // ++
app.mount('#app');
/src/store/index.ts
import { createPinia } from 'pinia';
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate';
const pinia = createPinia();
// 使用pinia数据持久化插件
pinia.use(piniaPluginPersistedstate);
export default pinia;
/src/main.ts
import { createApp } from 'vue';
import App from './App.vue';
import router from '@/router';
import pinia from '@/store'; // ++
import './styles/_reset.scss';
const app = createApp(App);
app.use(router);
app.use(pinia); // ++
app.mount('#app');
/src/utils/http/index.ts
import axios from 'axios';
import type {
AxiosInstance,
AxiosRequestConfig,
AxiosResponse,
AxiosError,
InternalAxiosRequestConfig,
} from 'axios';
const service: AxiosInstance = axios.create({
baseURL: '/'
timeout: 15000,
});
// axios实例拦截请求
service.interceptors.request.use(
(config: InternalAxiosRequestConfig) => {
return config;
},
(error: AxiosError) => {
return Promise.reject(error);
}
);
// axios实例拦截响应
service.interceptors.response.use(
(response: AxiosResponse) => {
},
(error: any) => {
}
);
export default service
pnpm install -D vite @vitejs/plugin-vue @vitejs/plugin-vue-jsx
pnpm install -D @types/node @types/nprogress vue-tsc
Vite官方文档对环境变量的介绍:环境变量和模式 | Vite 官方中文文档 (vitejs.dev)
import.meta.env
对象上暴露环境变量。我们在项目根目录下新建三个文件:.env
,.env.production
,.env.development
npm run dev 会加载 .env 和 .env.development 内的配置
npm run build 会加载 .env 和 .env.production 内的配置
mode 可以通过命令行 --mode 选项来重写。
.env
# axios请求的 baseURL
VITE_APP_API_BASEURL = /api
.env.[mode]
之后会介绍到,这里我们就先配置这一项即可/src/utils/http/index.ts
const service: AxiosInstance = axios.create({
// 这样我们就可以在环境变量中改变 axios 的 baseURL
baseURL: import.meta.env.VITE_APP_API_BASEURL
timeout: 15000,
});
我们在开发过程中,环境变量可能会越来越多,我们可能想要获得智能的TypeScript语法提示来让我们知道有哪些环境变量。
在项目根目录下新建types
文件夹
/types/env.d.ts
///
interface ImportMetaEnv {
// 我们每次添加完新的环境变量就在此添加一次ts类型
// 这样我们就能在使用 import.meta.env 时获得ts语法提示
readonly VITE_APP_API_BASEURL: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}
vite.config.ts
vite.config.ts
文件import { defineConfig, loadEnv } from 'vite';
import type { UserConfig, ConfigEnv } from 'vite';
import { fileURLToPath } from 'url';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';
export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
// 获取当前工作目录
const root = process.cwd();
// 获取环境变量
const env = loadEnv(mode, root);
return {
// 项目根目录
root,
// 项目部署的基础路径
base: '/',
publicDir: fileURLToPath(new URL('./public', import.meta.url)), // 无需处理的静态资源位置
assetsInclude: fileURLToPath(new URL('./src/assets', import.meta.url)), // 需要处理的静态资源位置
plugins: [
// Vue模板文件编译插件
vue(),
// jsx文件编译插件
vueJsx(),
],
// 运行后本地预览的服务器
server: {
// 是否开启https
https: false,
// 指定服务器应该监听哪个 IP 地址。 如果将此设置为 0.0.0.0 或者 true 将监听所有地址,包括局域网和公网地址。
host: true,
// 开发环境预览服务器端口
port: 3000,
// 启动后是否自动打开浏览器
open: false,
// 是否开启CORS跨域
cors: true,
// 代理服务器
// 帮助我们开发时解决跨域问题
proxy: {
// 这里的意思是 以/api开头发送的请求都会被转发到 http://xxx:3000
'/api': {
target: 'http://xxx:3000',
// 改变 Host Header
changeOrigin: true,
// 发起请求时将 '/api' 替换为 ''
rewrite: (path) => path.replace(/^\/api/, ''),
},
},
},
// 打包配置
build: {
// 关闭 sorcemap 报错不会映射到源码
sourcemap: false,
// 打包大小超出 4000kb 提示警告
chunkSizeWarningLimit: 4000,
rollupOptions: {
// 打包入口文件 根目录下的 index.html
// 也就是项目从哪个文件开始打包
input: {
index: fileURLToPath(new URL('./index.html', import.meta.url)),
},
// 静态资源分类打包
output: {
format: 'esm',
chunkFileNames: 'static/js/[name]-[hash].js',
entryFileNames: 'static/js/[name]-[hash].js',
assetFileNames: 'static/[ext]/[name]-[hash].[ext]',
},
},
},
// 配置别名
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),
'#': fileURLToPath(new URL('./types', import.meta.url)),
},
},
};
});
tsconfig.json
/tsconfig.json
{
"compilerOptions": {
// 编译出JS的目标ES版本
"target": "esnext",
// 使用的ES版本
"module": "esnext",
// 用于选择模块解析策略,有'node'和'classic'两种类型
"moduleResolution": "node",
// 开启严格模式
"strict": true,
// 强制代码中使用的模块文件名必须和文件系统中的文件名保持大小写一致
"forceConsistentCasingInFileNames": true,
// 允许使用 xxx 代替 * as xxx 导入
"allowSyntheticDefaultImports": true,
// 指定 jsx 代码的生成: 'preserve', 'react-native', or 'react'
"jsx": "preserve",
// 用来指定编译时是否生成.map文件
"sourceMap": true,
// 通过为导入内容创建命名空间,实现CommonJS和ES模块之间的互操作性
"esModuleInterop": true,
// 是否可以导入 json module
"resolveJsonModule": true,
// 是否检测定义了但是没使用的变量
"noUnusedLocals": true,
// 是否检查是否有在函数体中没有使用的参数
"noUnusedParameters": true,
// 是否启用实验性的装饰器特性
"experimentalDecorators": true,
// ts中可以使用的库 这里配置为 dom 与 es模块
"lib": ["dom", "esnext"],
// 不允许变量或函数参数具有隐式any类型
"noImplicitAny": false,
// 启用阻止对下载库的类型检查
"skipLibCheck": true,
// types用来指定需要包含的模块
"types": ["vite/client", "element-plus/global"],
// 编译的时候删除注释
"removeComments": true,
// 使用绝对路径时使用baseUrl去解析导入路径
"baseUrl": ".",
// 为导入路径配置别名
"paths": {
"@/*": ["src/*"],
"#/*": ["types/*"]
},
// 配置虚拟目录
"rootDirs": []
},
// 指定需要编译文件
"include": [
"src/**/*.ts",
"src/**/*.d.ts",
"src/**/*.tsx",
"src/**/*.vue",
"types/**/*.d.ts",
"types/**/*.ts",
"build/**/*.ts",
"build/**/*.d.ts",
"mock/**/*.ts",
"vite.config.ts"
],
// 指定不需要编译的文件
"exclude": ["node_modules", "dist", "**/*.js"]
}
/package.json
"scripts": {
"dev": "vite --mode development",
// 先进行语法检查 再进行项目打包
"build": "vue-tsc --noEmit --skipLibCheck && vite build",
},
接下来我们就可以运行项目吧项目跑起来啦!
pnpm run dev
一个基础的 Vue3+TypeScrpit+Vite 的项目就此构造完毕!
后续文章:
如果对你有帮助的话,请给我点个赞吧
关注我,后续文章不迷路⛄