Vue 3 + Typescript + Vite2.0 搭建移动端 代码规范以及注意的点

<!--
 * @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", // 结尾是 \n \r \n\r 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

posted @ 2021-04-30 17:12  蓝色帅-橙子哥  阅读(2595)  评论(0编辑  收藏  举报