赞
踩
npm i vite -g
一步创建
- # npm 6.x
- npm create vite@latest my-vue-app --template vue-ts
-
- # npm 7+, extra double-dash is needed:
- npm create vite@latest my-vue-app -- --template vue-ts
-
- # yarn
- yarn create vite my-vue-app --template vue-ts
-
- # pnpm
- pnpm create vite my-vue-app --template vue-ts
配置创建
npm init vue@latest
如果安装依赖后运行 npm run dev 报以下错误
解决方法: 更新node版本
https://nodejs.org/zh-cn/
在 public目录 下,添加一个 favicon.icon 图片
在 index.html 文件的 title标签 中配置
能让 代码提示 变得更加友好
{ "compilerOptions":{ // 允许从没有设置默认导出的模块中默认导入。这并不影响代码的输出,仅为了类型检查。 "allowSyntheticDefaultImports":true, // 解析非相对模块名的基准目录 "baseUrl":".", // 模块加载兼容模式,可以是呀import from语法导入commonJS模块 "esModuleInterop":true, // 从 tslib 导入辅助工具函数(比如 __extends, __rest等) "importHelpers":true, // 指定生成哪个模块系统代码 "module":"esnext", // 决定如何处理模块。 "moduleResolution":"node", // 启用所有严格类型检查选项。 // 启用 --strict相当于启用 --noImplicitAny, --noImplicitThis, --alwaysStrict, // --strictNullChecks和 --strictFunctionTypes和--strictPropertyInitialization。 "strict":true, "noImplicitAny":false,//关闭implicitly has an 'any' type // 支持jsx语法 "jsx":"preserve", // 生成相应的 .map文件。 "sourceMap":true, // 忽略所有的声明文件( *.d.ts)的类型检查。 "skipLibCheck":true, // 指定ECMAScript目标版本 "target":"esnext", // 要包含的类型声明文件名列表 "types":[ "node" ], "typeRoots":[ "../node_modules/@types" ], // isolatedModules 设置为 true 时,如果某个 ts 文件中没有一个import or export 时,ts 则认为这个模块不是一个 ES Module 模块,它被认为是一个全局的脚本, "isolatedModules":true, // 模块名到基于 baseUrl的路径映射的列表。 "paths":{ "@/*":[ "src/*" ] }, "vueCompilerOptions":{ "experimentalDisableTemplateSupport":true//去掉volar下el标签红色波浪线问题 }, // 编译过程中需要引入的库文件的列表。 "lib":[ "ESNext", "DOM", "DOM.Iterable", "ScriptHost" ]}, // 解析的文件 "include":[ "env.d.ts", "src/**/*", "src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue", "src/*.js", "src/**/*.jsx"], "exclude":[ "node_modules"], "references":[ { "path":"./tsconfig.node.json" }]}
eslint 配置格式化选项说明
// 1.一行代码的最大字符数,默认是80(printWidth: <int>) printWidth: 80, //2.tab宽度为2空格(tabWidth: <int>) tabWidth: 2, //3.是否使用tab来缩进,我们使用空格(useTabs: <bool>) useTabs: false, //4.结尾是否添加分号,false的情况下只会在一些导致ASI错误的其工况下在开头加分号,我选择无分号结尾的风格(semi: <bool>) semi: false, //5.使用单引号(singleQuote: <bool>) singleQuote: true, //6.object对象中key值是否加引号(quoteProps: "<as-needed|consistent|preserve>")as-needed只有在需求要的情况下加引号,consistent是有一个需要引号就统一加,preserve是保留用户输入的引号 quoteProps: 'as-needed', //7.在jsx文件中的引号需要单独设置(jsxSingleQuote: <bool>) jsxSingleQuote: false, //8.尾部逗号设置,es5是尾部逗号兼容es5,none就是没有尾部逗号,all是指所有可能的情况,需要node8和es2017以上的环境。(trailingComma: "<es5|none|all>") trailingComma: 'es5', //9.object对象里面的key和value值和括号间的空格(bracketSpacing: <bool>) bracketSpacing: true, //10.jsx标签多行属性写法时,尖括号是否另起一行(jsxBracketSameLine: <bool>) jsxBracketSameLine: false, //11.箭头函数单个参数的情况是否省略括号,默认always是总是带括号(arrowParens: "<always|avoid>") arrowParens: 'always', //12.range是format执行的范围,可以选执行一个文件的一部分,默认的设置是整个文件(rangeStart: <int> rangeEnd: <int>) rangeStart: 0, rangeEnd: Infinity, //18. vue script和style标签中是否缩进,开启可能会破坏编辑器的代码折叠 vueIndentScriptAndStyle: false, //19. endOfLine: "<lf|crlf|cr|auto>" 行尾换行符,默认是lf, endOfLine: 'lf', //20.embeddedLanguageFormatting: "off",默认是auto,控制被引号包裹的代码是否进行格式化 embeddedLanguageFormatting: 'off',
- {
- "singleQuote":true,
- "tabWidth":4,
- "semi":false,
- }
安装 gzip 和 mock 依赖
npm i vite-plugin-compression vite-plugin-mock -D
- import { defineConfig } from'vite'
- import vue from'@vitejs/plugin-vue'
- import vueJsx from'@vitejs/plugin-vue-jsx'
-
- import path from'path'// gzip插件
- import viteCompression from'vite-plugin-compression'// mock插件
- import { viteMockServe } from'vite-plugin-mock'
-
- constresolve = (dir) => path.resolve(__dirname, dir)
-
- export defaultdefineConfig({
- base: './', //打包路径
- publicDir: resolve('public'), //静态资源服务的文件夹
- plugins: [
- vue(),
- vueJsx(),
- // gzip压缩 生产环境生成 .gz 文件
- viteCompression({
- verbose: true,
- disable: false,
- threshold: 10240,
- algorithm: 'gzip',
- ext: '.gz',
- }),
- //mock
- viteMockServe({
- mockPath: './mocks', // 解析,路径可根据实际变动
- localEnabled: true, // 此处可以手动设置为true,也可以根据官方文档格式
- }),
- ],
- // 配置别名
- resolve: {
- alias: {
- '@': resolve('src'),
- },
- // 导入时想要省略的扩展名列表
- extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'],
- },
- css: {
- // css预处理器
- preprocessorOptions: {
- scss: {
- additionalData:
- '@import "@/assets/styles/common.scss";@import "@/assets/styles/reset.scss";',
- },
- },
- },
- //启动服务配置
- server: {
- host: '0.0.0.0',
- port: 8000,
- open: true, // 自动在浏览器打开
- proxy: {},
- },
- // 打包配置
- build: {
- //浏览器兼容性 "esnext"|"modules"
- target: 'modules',
- //指定输出路径
- outDir: 'build',
- //生成静态资源的存放路径
- assetsDir: 'assets',
- //启用/禁用 CSS 代码拆分
- cssCodeSplit: true,
- sourcemap: false,
- assetsInlineLimit: 10240,
- // 打包环境移除console.log, debugger
- minify: 'terser',
- terserOptions: {
- compress: {
- drop_console: true,
- drop_debugger: true,
- },
- },
- rollupOptions: {
- input: {
- main: resolve('index.html'),
- },
- output: {
- entryFileNames: `js/[name]-[hash].js`,
- chunkFileNames: `js/[name]-[hash].js`,
- assetFileNames: `[ext]/[name]-[hash].[ext]`,
- },
- },
- },
- })
-

assets 存放 => 静态资源
css => 样式重置
img => 图片文件
font => 字体文件
components 存放 => 公共组件
hooks 存放 => 公共常用的hook
mock 存放 => 模拟接口数据
router 存放 => 路由管理
service 存放 => 接口请求
stores 存放 => 状态管理
utils 存放 => 插件、第三方插件
views 存放 => 视图、页面
自定义的css公共文件放置在assets中的css文件中即可
01 - 安装
npm i normalize.css
02 - 引入
// 在 main.js 中引入import'normalize.css';
01 - 代码
- html,
- body,
- div,
- span,
- applet,
- object,
- iframe,
- h1,
- h2,
- h3,
- h4,
- h5,
- h6,
- p,
- blockquote,
- pre,
- a,
- abbr,
- acronym,
- address,
- big,
- cite,
- code,
- del,
- dfn,
- em,
- font,
- img,
- ins,
- kbd,
- q,
- s,
- samp,
- small,
- strike,
- strong,
- sub,
- sup,
- tt,
- var,
- b,
- u,
- i,
- center,
- dl,
- dt,
- dd,
- ol,
- ul,
- li,
- fieldset,
- form,
- label,
- legend,
- caption {
- margin: 0;
- padding: 0;
- border: 0;
- outline: 0;
- font-size: 100%;
- vertical-align: baseline;
- background: transparent;
- }
-
- table,
- tbody,
- tfoot,
- thead,
- tr,
- th,
- td {
- margin: 0;
- padding: 0;
- outline: 0;
- font-size: 100%;
- vertical-align: baseline;
- background: transparent;
- }
-
- button,
- input,
- textarea {
- margin: 0;
- padding: 0;
- }
-
- /* form elements 表单元素 */body,
- button,
- input,
- select,
- textarea {
- font: normal 12px/1.5'\5FAE\8F6F\96C5\9ED1', tahoma, arial;
- }
-
- /*设置的字体,行高*/h1,
- h2,
- h3,
- h4,
- h5,
- h6,
- th {
- font-size: 100%;
- font-weight: normal;
- }
-
- /*重置标题*/address,
- cite,
- dfn,
- var {
- font-style: normal;
- }
-
- /* 将斜体扶正 */code,
- kbd,
- pre,
- samp {
- font-family: 'courier new', courier, monospace;
- }
-
- /* 统一等宽字体 */
- small {
- font-size: 12px;
- }
-
- /* 小于 12px 的中文很难阅读,让 small 正常化 */ul,
- ol {
- list-style: none;
- }
-
- /* 重置列表元素 */button,
- input[type="submit"],
- input[type="button"] {
- cursor: pointer;
- }
-
- input[type="radio"],
- input[type="checkbox"],
- input[type="submit"],
- input[type="reset"] {
- vertical-align: middle;
- cursor: pointer;
- border: none;
- }
-
- /** 重置文本格式元素 **/a {
- text-decoration: none;
- }
- a:hover {
- text-decoration: underline;
- }
- a:focus {
- outline: 0;
- }
- sup {
- vertical-align: text-top;
- }
-
- /* 重置,减少对行高的影响 */
- sub {
- vertical-align: text-bottom;
- }
-
- /** 重置表单元素 **/legend {
- color: #000;
- }
-
- /* for ie6 */fieldset,
- img {
- border: 0;
- }
-
- /* img 搭车:让链接里的 img 无边框 */button,
- input,
- select,
- textarea {
- background: transparent;
- font-size: 100%;
- outline: 0;
- }
-
-
- /* 使得表单元素在 ie 下能继承字体大小 *//* 注:optgroup 无法扶正 */table {
- border-collapse: collapse;
- border-spacing: 0;
- }
-
- td,
- th {
- vertical-align: middle;
- }
-
-
- /** 重置表格元素 **//* 重置 HTML5 元素 */article,
- aside,
- details,
- figcaption,
- figure,
- footer,
- header,
- hgroup,
- menu,
- nav,
- section,
- summary,
- time,
- mark,
- audio,
- video {
- display: block;
- margin: 0;
- padding: 0;
- }
-
- /*回复标签重置*/blockquote,
- q {
- quotes: none;
- }
-
- blockquote:before,
- blockquote:after,
- q:before,
- q:after {
- content: '';
- display: none;
- }

02 - 引入
// 在 main.js 中引入import'./assets/css/reset.css';
01 - 代码
- // 清除浮动.clearfix {
- *zoom: 1;
- }
-
- ......
02 - 引入
// 在 main.js 中引入import'./assets/css/common.css';
一步创建需要安装依赖、配置路由, 引入mian.ts, 配置创建则已自动生成
npm i vue-router
- // 1. 导入
- import { createRouter, createWebHashHistory } from'vue-router';
-
- // 2. 创建路由对象constrouter = createRouter({
- history: createWebHashHistory(),
- routes: [
- {
- path: '/',
- redirect: '/home'
- },
- {
- path: '/home',
- component: () => import('xxx/home.vue')
- }
- ]
- });
-
- // 3. 导出
- export default router;

- // main.jsimport { createApp } from'vue';
- importAppfrom'./App.vue';
- // 1. 导入import router from'./router';
-
- import'normalize.css';
- import'./assets/css/reset.css';
- import'./assets/css/common.css';
-
- // 2. 使用createApp(App).use(router).mount('#app');
在该用的地方加上
一步创建需要安装依赖、配置路由, 引入mian.ts, 配置创建则已自动生成
npm i pinia
- // main.jsimport { createApp } from'vue';
- import { createPinia } from"pinia";
-
- importAppfrom'./App.vue';
- // 1. 导入import router from'./router';
-
- import'normalize.css';
- import'./assets/css/reset.css';
- import'./assets/css/common.css';
-
- // 2. 使用createApp(App).use(createPinia()).use(router).mount('#app');
- // 1. 导入import { defineStore } from'pinia';
-
- // 2. 使用const useDemoStore = defineStore('demoStore', {
- state: () => ({
- arrList: []
- }),
- actions: {},
- getters: {}
- });
-
- // 3. 导出exportdefault useDemoStore;
npm i axios
在 utils 目录下创建 request.ts 文件,配置好适合自己业务的请求拦截和响应拦截
- import axios, { AxiosRequestConfig, Method } from 'axios';
-
- // 创建请求实例
- const instance = axios.create({
- baseURL: '/api',
- // 指定请求超时的毫秒数
- timeout: 10000,
- // 表示跨域请求时是否需要使用凭证
- withCredentials: false,
- });
-
- // 设置请求头
- instance.defaults.headers.post['Content-Type'] = 'application/json;charset=UTF-8';
- instance.defaults.headers.put['Content-Type'] = 'application/x-www-form-urlencoded';
- // instance.defaults.headers.put['Content-Type'] = 'application/json';
-
-
- // 取消重复请求
- const pending = [];
-
- // 定义接口
- interface PendingType {
- url?: string;
- method?: Method;
- params: any;
- data: any;
- cancel: any;
- }
-
- // 移除重复请求
- const removePending = (config: AxiosRequestConfig) => {
- for (const key in pending) {
- const item: number = +key;
- const list: PendingType = pending[key];
- // 当前请求在数组中存在时执行函数体
- if (list.url === config.url && list.method === config.method && JSON.stringify(list.params) === JSON.stringify(config.params) && JSON.stringify(list.data) === JSON.stringify(config.data)) {
- // 执行取消操作
- list.cancel('操作太频繁,请稍后再试');
- // 从数组中移除记录
- pending.splice(item, 1);
- }
- }
- };
-
-
- // 请求拦截器(发起请求之前的拦截)
- instance.interceptors.request.use(
- (config): AxiosRequestConfig<any> => {
- removePending(config);
- config.cancelToken = new axios.CancelToken(c => {
- pending.push({ url: config.url, method: config.method, params: config.params, data: config.data, cancel: c });
- });
- /**
- * 在这里一般会携带前台的参数发送给后台,比如下面这段代码:
- * const token = getToken()
- * if (token) {
- * config.headers.token = token
- * }
- */
- return config;
- },
- (error) => {
- return Promise.reject(error);
- },
- );
-
- // 响应拦截器(获取到响应时的拦截)
- instance.interceptors.response.use(
- (response) => {
- removePending(response.config);
- /**
- * 根据你的项目实际情况来对 response 和 error 做处理
- * 这里对 response 和 error 不做任何处理,直接返回
- */
- return response;
- },
- (error) => {
- return Promise.reject(error);
- },
- );
-
- interface ResType<T> {
- code: number;
- data?: T;
- msg?: string;
- message?: string;
- err?: string;
- }
-
- interface Http {
- post<T>(url: string, data?: unknown, params?: unknown,): Promise<ResType<T>>;
- get<T>(url: string, params?: unknown): Promise<ResType<T>>;
- put<T>(url: string, data?: unknown, params?: any): Promise<ResType<T>>;
- _delete<T>(url: string, params?: unknown): Promise<ResType<T>>;
- }
-
- // 导出常用函数
- const http: Http = {
- post(url, data, params) {
- return new Promise((resolve, reject) => {
- instance
- .post(url, JSON.stringify(data), params)
- .then((res) => {
- resolve(res.data);
- })
- .catch((err) => {
- reject(err.data);
- });
- });
- },
- get(url, params) {
- return new Promise((resolve, reject) => {
- axios
- .get(url, { params })
- .then((res) => {
- resolve(res.data);
- })
- .catch((err) => {
- reject(err.data);
- });
- });
- },
- put(url, data, params) {
- return new Promise((resolve, reject) => {
- instance
- .put(url, data, params)
- .then((res) => {
- resolve(res.data);
- })
- .catch((err) => {
- reject(err.data);
- });
- });
- },
- _delete(url, params) {
- return new Promise((resolve, reject) => {
- instance
- .delete(url, params)
- .then((res) => {
- resolve(res.data);
- })
- .catch((err) => {
- reject(err.data);
- });
- });
- },
-
- }
-
- export default http;

之后在 api 文件夹中以业务模型对接口进行拆分,举个例子,将所有跟用户相关接口封装在 User 类中,此类称作用户模型。
在 User 类中比如有登录、注册、获取用户信息等方法,如果有业务逻辑变动,只需要修改相关方法即可。
- import { post } from '@/utils/request';
-
- export default class User {
- /**
- * 登录
- * @param {String} username 用户名
- * @param {String} password 密码
- * @returns
- */
- static async login(username: string, password: string) {
- return post('/login', {
- username,
- password,
- });
- }
- }

把每个业务模型独立成一个 js 文件,声明一个类通过其属性和方法来实现这个模型相关的数据获取,这样可以大大提升代码的可读性与可维护性。
在需要使用接口的地方,引入对应的业务模型文件,参考如下
- <script setup lang="ts">import User from '@/api/user';
- import { ref } from'vue'const username = ref('')
- const password = ref('')
-
- constlogin = async () => {
- const res = await User.login(username.value, password.value);
- console.log(res);
- },
- </script>
首先我们先安装sass和sass-loader:
npm i sass sass-loader -D
然后我们需要在vite.config.ts中配置css预处理器
- export defaultdefineConfig({
- css: {
- preprocessorOptions: {
- scss: {
- additionalData: '@import "@/assets/styles/global.scss";@import "@/assets/styles/reset.scss";',
- },
- }
- }
- })
我们这里默认加载global.scss中的样式,那么我们就需要创建一个这样的文件:
- // src/assets/style/global.scss
- $primary-color: #5878e2; // 主题色
最后在main.ts中引入即可:
import"./assets/style/global.scss";
然后在组件中使用时,就可以直接使用:
- <script setup lang="ts">
- import {GlobalStore} from '@/store'
- const global = GlobalStore();
- </script>
- <template>
- <div>{{global.token}}</div>
- </template>
- <style scoped lang="scss">
- div {
- color: $primary-color; // 主题色
- }
- </style>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。