<!--
* @Descripttion: Vue 3 + Typescript + Vite2.0 项目简介
-->
# Vue 3 + Typescript + Vite + vant3 搭建移动端通用架子
pc 管理系统请参考移步:VUE VBEN ADMIN2.0 (vite2.0+vue3+ts+antd-design-vue)
github 地址:git clone https://github.com/anncwb/vue-vben-admin.git vue-vben-admin-2.0
项目文档:https://vvbin.cn/doc-next/
element-plus(支持 vue3) 文档:https://element-plus.gitee.io/#/zh-CN/component/installation
vite2.0 中文文档:https://cn.vitejs.dev/
vue3 官方文档:https://v3.cn.vuejs.org/api/
Typescript 官方文档:https://www.tslang.cn/docs/home.html
vant3 官方文档:https://vant-contrib.gitee.io/vant/v3/#/zh-CN/home
vue-i18n@next 国际化配置文档:https://vue-i18n.intlify.dev/introduction.html
### 初始化项目 [yarn 和 npm 的区别不赘述]
npm init @vitejs/app or yarn create @vitejs/app ---> 输入项目名称 ---> 选择 vue-ts [vue3+TS]
### 安装依赖
npm i or cnpm i
### 启动项目
cnpm run dev
### 安装相关依赖 yarn 或者 cnpm npm 命令都行
yarn add xxx
npm i vue-i18n@next -S or cnpm i vue-i18n@next -S
npm i vue-router@4 -S or cnpm i vue-router@4 -S
npm i vuex@next -S or cnpm i vuex@next -S
### 注意问题
node api 报错 比如配置文件使用 require
node.js 不是内置对象的一部分,如果想用 typescript 写 Node.js,则需要引入第三方声明文件
解决办法:npm i @types/node -D or cnpm i @types/node -D
tsconfig.json types 中配置 node 字段
### 代码规范
vscode 提示无法在只读编辑器中编辑 json 文件
File → Preferences → Settings --> Eslint
Eslint 官方文档:https://eslint.org/docs/rules/
prettier 官方文档:https://prettier.io/docs/en/configuration.html
eslint-plugin-vue 官方文档:https://eslint.vuejs.org/user-guide/#usage 自动修复 eslint 报错
npm i eslint-plugin-vue -D or cnpm i eslint-plugin-vue -D
vscode Vetur 插件 ---> vue 高亮插件
vscode ESlint 插件 ---> Eslint 插件用于根据工程目录的.eslintrc.js 配置文件在编辑器中显示一些错误提示,后面的自定格式化根据这里的错误提示进行格式化操作
如果是 vue-cli 创建的项目 创建的时候把 Linter/Formatter 选上(默认已选上) 下一步选择 Eslint+Prettier --> Lint on save
// eslint 配置项,保存时自动修复
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
// 默认使用 prettier 格式化支持的文件
"editor.defaultFormatter": "esbenp.prettier-vscode",
// 自动设定 eslint 工作区
"eslint.workingDirectories": [
{ "mode": "auto" }
],
"explorer.confirmDelete": false,
"diffEditor.ignoreTrimWhitespace": true,
"eslint.codeAction.showDocumentation": {
"enable": true
},
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"eslint.format.enable": true,
"eslint.validate": ["javascript", "vue", "html","jsx", "javascriptreact"],
"editor.suggest.snippetsPreventQuickSuggestions": false,
"files.associations": {
"*.vue": "html"
},
"editor.formatOnSave": true
//由于 prettier 不能格式化 vue 文件 template 所以使用 js-beautify-html 格式化
"vetur.format.defaultFormatter.html": "js-beautify-html",
"vetur.format.defaultFormatterOptions": {
"js-beautify-html": {
"wrap*attributes": "force-aligned" //属性强制折行对齐
}
},
"prettier.singleQuote": true, //使用单引号而不是双引号
"prettier.jsxBracketSameLine": true, //将>多行 JSX 元素放在最后一行的末尾,而不是单独放在下一行
"prettier.printWidth": 120, // 超过最大值换行
"prettier.tabWidth": 2, // 缩进字节数
"prettier.useTabs": true, // 缩进使用 tab
"prettier.semi": false, // 句尾添加分号
"prettier.singleQuote": true, // 使用单引号代替双引号
"prettier.proseWrap": "preserve", // 默认值。因为使用了一些折行敏感型的渲染器(如 GitHub comment)而按照 markdown 文本样式进行折行
"prettier.arrowParens": "avoid", // (x) => {} 箭头函数参数只有一个时是否要有小括号。avoid:省略括号
"prettier.bracketSpacing": true, // 在对象,数组括号与文字之间加空格 "{ foo: bar }"
"prettier.endOfLine": "auto", // 结尾是
auto
"prettier.htmlWhitespaceSensitivity": "ignore",
"prettier.ignorePath": ".prettierignore", // 不使用 prettier 格式化的文件填写在项目的.prettierignore 文件中
"prettier.requireConfig": false, // Require a "prettierconfig" to format prettier
"prettier.trailingComma": "none", // 在对象或数组最后一个元素后面是否加逗号
/* 每种语言默认的格式化规则 \_/
"[html]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[css]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[scss]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[vue]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
安装插件让 ESLint 支持 TypeScript
npm i typescript @typescript-eslint/parser @typescript-eslint/eslint-plugin -D
当我们执行 npm run format 时还会报各种奇怪的错误 如:error: Delete ⏎ (prettier/prettier) at src/pages/xxx 等;这是因为 prettier 配置和 vscode 编辑器 prettier 配置冲突导致的 在 rules 中配置下覆盖掉就可以了
出现了下面的警告:} expected css(css lcurlyexpected), 解决方案:settings.json 中配置如下
"files.associations": {
"_.vue": "vue",
"_.tpl": "html"
},
### git 提交代码钩子校验
npm i husky lint-staged -D
or
cnpm i husky lint-staged -D
## package.json 中配置 GitHub 文档:https://github.com/typicode/husky
"husky": {
"hooks": {
"pre-commit": "lint-staged",
"pre-push": "lint-staged"
}
},
"lint-staged": {
"*.{js,jsx,md,html,css}": [
"prettier --write",
"git add"
]
}
### vm vw 布局适配方案 以及 css 预处理器 {这边先选择 less} UI 选型 {自己封装常用的 or 选择一种功能比较齐全的移动端 UI 库}
cnpm i postcss-viewport-units postcss-px-to-viewport postcss-write-svg autoprefixer -D
cnpm i less -D
cnpm i vant@next -S
cnpm i vite-plugin-imp -D // 按需加载 GitHub 文档:https://github.com/onebay/vite-plugin-imp
vant 官方网站:https://vant-contrib.gitee.io/vant/v3/#/zh-CN
其实在 Vite 中无须考虑按需引入的问题。Vite 在构建代码时,会自动通过 Tree Shaking 移除未使用的 ESM 模块。而 Vant 3.0 内部所有模块都是基于 ESM 编写的,天然具备按需引入的能力。现阶段遗留的问题是,未使用的组件样式无法被 Tree Shaking 识别并移除,后续 vant 团队会考虑通过 Vite 插件的方式进行支持
### 环境变量和打包命令配置
根目录下新建文件
.env.development (开发)
.env.test (测试)
.env.production (生产)
package.json 中配置打包命令
"build:dev": "vue-tsc --noEmit && vite build --mode development",
"build:test": "vue-tsc --noEmit && vite build --mode test",
"build:prod": "vue-tsc --noEmit && vite build --mode production"
打包命令
cnpm run build:dev // 本地包
cnpm run build:test // 测试包
cnpm run build:prod // 生产包
### 使用 http-server 开启一个本地服务器 预览效果
npm install http-server -g
cd dist
http-server -c-1(只输入 http-server 的话,更新了代码后,页面不会同步更新)ctrl + c 即可关闭
### 调试工具
cnpm i vconsole -S
在 main.ts 引入
import Vconsole from 'vconsole';
new Vconsole();
### PWA
参考文档:https://github.com/antfu/vite-plugin-pwa
cnpm i vite-plugin-pwa -D
配置 vite.config.ts 文件
import { VitePWA } from 'vite-plugin-pwa'
plugins:[
VitePWA({
manifest: {},
workbox: {
skipWaiting: true,
clientsClaim: true
}
})
]
### 组件样式按需加载配置
参考文档:https://github.com/anncwb/vite-plugin-style-import
配置 vite.config.ts 文件
cnpm i vite-plugin-style-import -D
import styleImport from 'vite-plugin-style-import'
css:{
preprocessorOptions:{
less:{
modifyVars:{},
javascriptEnabled: true
}
}
},
plugins:[
styleImport({
libs:[
{
libraryName: 'ant-design-vue',
esModule: true,
resolveStyle: name => `ant-design-vue/es/${name}/style/index`
}
]
})
]
### 生产环境生成 .gz 文件.
[content-encoding:gzip]
压缩代码,在传输的时候用 gzip 压缩,提高资源访问速度。后端以 nginx 为例的话,在 nginx.conf 需要开启 gizp 服务:
gzip on; //开启 gzip 压缩功能
这样你就可以在 network 查看到 content-encoding:gzip 这个选项
参考文档:https://github.com/anncwb/vite-plugin-compression
cnpm i vite-plugin-compression -D
配置 vite.config.ts 文件
import viteCompression from 'vite-plugin-compression'
plugins:[
viteCompression({
verbose: true,
disable: false,
threshold: 10240,
algorithm: 'gzip',
ext: '.gz'
})
]
### 国际化配置
安装 js-cookie
cnpm i js-cookie -S
cnpm i @types/js-cookie -D
参考文档:https://www.npmjs.com/package/js-cookie
cnpm i vue-i18n@next -S
### http 请求库
cnpm i axios -S
### normalize.css 更好的 reset.css 方案
cnpm i normalize.css -S
参考文档:https://github.com/necolas/normalize.css
项目环境以及打包命令配置
项目根目录下创建 .env.test 【测试环境】 .env.development 【开发环境】 .env.production 【生产环境】
分别对应内容
NODE_ENV=test
VITE_APP_BASE_URL='https://www.test.com/'
NODE_ENV=development
VITE_APP_BASE_URL='api'
NODE_ENV=production
VITE_APP_BASE_URL='https://www.prod.com/'
文件里获取 console.log(import.meta.env.VITE_APP_BASE_URL, '环境变量')
package.json文件下面配置
"build:dev": "vue-tsc --noEmit && vite build --mode development",
"build:test": "vue-tsc --noEmit && vite build --mode test",
"build:prod": "vue-tsc --noEmit && vite build --mode production"
关于代码规范:都可以自定义规则以下是自己简单的配置规则(提供参考具体查看对应官方文档配置属性)
.eslintignore文件
*.sh
node_modules
*.md
*.woff
*.ttf
.vscode
.idea
dist
/public
/docs
.husky
.local
/bin
Dockerfile
.eslintrc.js文件
/*
* @Descripttion: eslint代码规范
*/
module.exports = {
parser: 'vue-eslint-parser',
parserOptions: {
parser: '@typescript-eslint/parser',
ecmaVersion: 2020,
sourceType: 'module',
ecmaFeatures: {
jsx: true
}
},
extends: [
'plugin:vue/vue3-recommended',
'plugin:@typescript-eslint/recommended',
'prettier/@typescript-eslint',
'plugin:prettier/recommended'
],
rules: {
'@typescript-eslint/ban-ts-ignore': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-var-requires': 'off',
'@typescript-eslint/no-empty-function': 'off',
'vue/custom-event-name-casing': 'off',
'no-use-before-define': 'off',
// 'no-use-before-define': [
// 'error',
// {
// functions: false,
// classes: true,
// },
// ],
'@typescript-eslint/no-use-before-define': 'off',
// '@typescript-eslint/no-use-before-define': [
// 'error',
// {
// functions: false,
// classes: true,
// },
// ],
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/ban-types': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-unused-vars': [
'error',
{
argsIgnorePattern: '^h$',
varsIgnorePattern: '^h$'
}
],
'no-unused-vars': [
'error',
{
argsIgnorePattern: '^h$',
varsIgnorePattern: '^h$'
}
],
'space-before-function-paren': 'off',
quotes: ['error', 'single'],
'comma-dangle': ['error', 'never'],
"no-console": process.env.NODE_ENV === "production" ? "error" : "off",
"no-debugger": process.env.NODE_ENV === "production" ? "error" : "off"
}
}
.prettierignore文件
/dist/*
.local
.output.js
/node_modules/**
**/*.svg
**/*.sh
/public/*
.prettierrc.js文件
/*
* @Descripttion: prettier.config.js or .prettierrc.js package.json 中配置prettier属性
*/
// 如果vscode配置中有的这边可以不用再配置
module.exports = {
useTabs: false,
vueIndentScriptAndStyle: true,
quoteProps: 'as-needed',
jsxSingleQuote: false,
arrowParens: 'always',
htmlWhitespaceSensitivity: 'strict',
endOfLine: 'lf',
printWidth: 200, // 换行字符串阈值
tabWidth: 2, // 设置工具每一个水平缩进的空格数
singleQuote: true, // 用单引号
semi: false, // 句末是否加分号
trailingComma: 'none', // 最后一个对象元素加逗号
bracketSpacing: true, // 对象,数组加空格
jsxBracketSameLine: false, // jsx > 是否另起一行
disableLanguages: ["vue"] // 不格式化vue文件,vue文件的格式化单独设置
}
postcss.config.js文件
/*
* @Descripttion: postcss.config.js
*/
module.exports = {
plugins: {
autoprefixer: {
/* PostCSS plugin to parse CSS and add vendor prefixes to CSS rules */
/* 配置文档链接:https://github.com/postcss/autoprefixer#options */
overrideBrowserslist: [
'last 2 versions' // 最后两个版本
]
},
'postcss-viewport-units': {
/* vw兼容方案 */
/* 配置文档链接:https://github.com/springuper/postcss-viewport-units#options */
},
'postcss-px-to-viewport': {
/* 将px单位转换为视口单位的 (vw, vh, vmin, vmax) */
/* 配置文档链接:https://github.com/evrone/postcss-px-to-viewport/blob/master/README_CN.md#%E9%85%8D%E7%BD%AE%E5%8F%82%E6%95%B0 */
viewportWidth: 375,
viewportUnit: 'vw',
unitPrecision: 3,
minPixelValue: 1, // 小于或等于`1px`不转换为视窗单位,你也可以设置为你想要的值
mediaQuery: false,
// selectorBlackList: ['.vant'], // 以xxx开头
include: [], // 包括
exclude: [] // 排除
},
'postcss-write-svg': {
/* 在retina屏绘制1px细线 */
/* 配置文档链接:https://github.com/jonathantneal/postcss-write-svg#options */
},
}
}
tsconfig.json文件
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"moduleResolution": "node",
"strict": true,
"jsx": "preserve",
"sourceMap": true,
"resolveJsonModule": true,
"esModuleInterop": true,
"lib": ["esnext", "dom", "ES2015.promise"],
"types": ["vite/client", "node"],
"typeRoots": ["./node_modules/@types/", "./src/types"],
"paths": {
"@": ["./src"]
}
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
"exclude": ["node_modules"]
}
vite.config.ts配置
/*
* @Descripttion: vite.config.ts vite2.0
*/
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import viteCompression from 'vite-plugin-compression'
const path = require('path')
const resolve = (dir: string) => path.join(__dirname, dir)
// https://vitejs.dev/config/
export default defineConfig({
base: process.env.NODE_ENV === 'production' ? './' : '/',
plugins: [
vue(),
// 默认情况下,小于1501字节的文件不会被压缩
viteCompression({
// 是否在控制台输出压缩结果
verbose: true,
// 是否禁用
disable: false,
// 体积大于 threshold 才会被压缩,单位 b
threshold: 10240,
// 压缩算法
algorithm: 'gzip',
// 生成的压缩包后缀
ext: '.gz',
// 压缩后是否删除源文件
deleteOriginFile: false
})
],
// 别名
resolve: {
alias: {
'@': resolve('src'),
// 解决vue-i18n警告You are running the esm-bundler build of vue-i18n.
'vue-i18n': 'vue-i18n/dist/vue-i18n.cjs.js'
}
},
// 代理解决dev模式跨域
server: {
host: '0.0.0.0',
// https: false,
port: 9999, //启动端口
open: true, // 是否自动打开浏览器
cors: true,
proxy: {
// 如果是 /api 打头,则访问地址如下
'/api': {
target: 'http://jsonplaceholder.typicode.com',
changeOrigin: true,
// ws: true,
rewrite: (path) => path.replace(/^/api/, '')
}
}
},
css: {
preprocessorOptions: {
less: {
modifyVars: {
// Used for global import to avoid the need to import each style file separately
// reference: Avoid repeated references
hack: `true; @import (reference) "${resolve('src/assets/style/index.less')}";`
},
javascriptEnabled: true
}
}
},
build: {
target: 'es2015',
outDir: 'dist',
// 生产环境移除 console
terserOptions: {
compress: {
drop_console: process.env.NODE_ENV === 'production' ? true : false,
drop_debugger: process.env.NODE_ENV === 'production' ? true : false
}
}
}
})
vscode setting json配置:
{
"workbench.colorTheme": "Atom One Dark",
"workbench.iconTheme": "vscode-icons",
"git.ignoreWindowsGit27Warning": true,
"[html]": {
"editor.defaultFormatter": "HookyQR.beautify"
},
"[javascript]": {
"editor.defaultFormatter": "HookyQR.beautify"
},
"[json]": {
"editor.defaultFormatter": "vscode.json-language-features"
},
"fileheader.customMade": {
"Descripttion": "",
"version": "",
"Author": "lhl",
"Date": "Do not edit",
"LastEditors": "lhl",
"LastEditTime": "Do not Edit"
},
"fileheader.cursorMode": {
"name": "",
"test": "test font",
"msg": "",
"param": "",
"return": ""
},
"explorer.confirmDelete": false,
"diffEditor.ignoreTrimWhitespace": true,
"eslint.codeAction.showDocumentation": {
"enable": true
},
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"eslint.workingDirectories": [{ "mode": "auto" }],
"editor.defaultFormatter": "esbenp.prettier-vscode",
"eslint.format.enable": true,
"eslint.validate": ["javascript", "vue", "html", "typescript"],
"editor.suggest.snippetsPreventQuickSuggestions": false,
"files.associations": {
"*.vue": "vue",
"*.tpl": "html"
},
"vetur.format.defaultFormatter.html": "js-beautify-html",
"vetur.format.defaultFormatterOptions": {
"js-beautify-html": {
"wrap_attributes": "force-aligned"
}
},
"prettier.singleQuote": true,
"prettier.printWidth": 200,
"prettier.tabWidth": 2,
"prettier.useTabs": true,
"prettier.semi": false,
"prettier.proseWrap": "preserve",
"prettier.arrowParens": "avoid",
"prettier.bracketSpacing": true,
"prettier.endOfLine": "auto",
"prettier.htmlWhitespaceSensitivity": "ignore",
"prettier.ignorePath": ".prettierignore",
"prettier.requireConfig": false,
"prettier.trailingComma": "none",
"editor.formatOnSave": false,
"[dart]": {
"editor.formatOnSave": true,
"editor.formatOnType": true,
"editor.rulers": [
80
],
"editor.selectionHighlight": false,
"editor.suggest.snippetsPreventQuickSuggestions": false,
"editor.suggestSelection": "first",
"editor.tabCompletion": "onlySnippets",
"editor.wordBasedSuggestions": false
}
}
package.json文件
{
"name": "vite-project",
"version": "0.0.0",
"scripts": {
"dev": "vite",
"build": "vue-tsc --noEmit && vite build",
"serve": "vite preview",
"build:dev": "vue-tsc --noEmit && vite build --mode development",
"build:test": "vue-tsc --noEmit && vite build --mode test",
"build:prod": "vue-tsc --noEmit && vite build --mode production"
},
"dependencies": {
"@types/js-cookie": "^2.2.6",
"@vant/area-data": "^1.0.0",
"axios": "^0.21.1",
"js-cookie": "^2.2.1",
"normalize.css": "^8.0.1",
"vant": "^3.0.12",
"vconsole": "^3.4.0",
"vue": "^3.0.5",
"vue-i18n": "^9.0.0",
"vue-router": "^4.0.5",
"vuex": "^4.0.0"
},
"devDependencies": {
"@types/js-cookie": "^2.2.6",
"@types/node": "^14.14.37",
"@typescript-eslint/eslint-plugin": "^4.20.0",
"@typescript-eslint/parser": "^4.20.0",
"@vitejs/plugin-vue": "^1.2.1",
"@vue/compiler-sfc": "^3.0.5",
"autoprefixer": "^10.2.5",
"eslint": "^7.23.0",
"eslint-config-prettier": "^8.1.0",
"eslint-plugin-prettier": "^3.3.1",
"eslint-plugin-vue": "^7.8.0",
"husky": "^6.0.0",
"less": "^4.1.1",
"lint-staged": "^10.5.4",
"postcss-px-to-viewport": "^1.1.1",
"postcss-viewport-units": "^0.1.6",
"postcss-write-svg": "^3.0.1",
"prettier": "^2.2.1",
"typescript": "^4.1.3",
"vite": "^2.1.5",
"vite-plugin-compression": "^0.2.4",
"vite-plugin-imp": "^2.0.5",
"vue-tsc": "^0.0.15"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged",
"pre-push": "lint-staged"
}
},
"lint-staged": {
"*.{js,jsx,md,html,css}": [
"prettier --write",
"git add"
]
}
}
以上代码纯属自己整理和测试,未经允许请勿随意转载,若有不正请及时告知!!
EditorConfig for Visual Studio Code (有喜爱的可以配置上这个下载vscode插件即可配置)
根目录下创建 .editorconfig文件
配置参考:https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig