vite+vue3 2

原本要搞一下算法和Asp.NetCore,但是想到vite+vue3還有幾個插件沒有記錄,總是感覺怪怪的,所以今天下午把eslint+prettier、postcss和svgIcon這四個插件試驗性的做了個Demo,以下記錄這四個的使用過程。

 

一、開始工作

1.eslint+prettier

1.1.eslint

1.1.1.簡要說明:eslint的作用就是約束代碼規範的,不論是個人項目還是多人合作,規範的代碼風格都是一種保證,對可讀性,避免語法錯誤等有很大的幫助。

1.1.2.步驟過程

    a)安裝eslint:npm i eslint -D

            b)配置eslint:執行npx eslint --init命令,然後按照提示完成一系列操作來創建配置文件

                問:How would you like to use ESLint? (你想如何使用 ESLint?)

                答:選擇To check syntax, find problems, and enforce code style(檢查語法、發現問題並強制執行代碼風格)

                問:What type of modules does your project use?(你的項目使用哪種類型的模塊?)

                答:選擇 JavaScript modules (import/export)

                問:Which framework does your project use? (你的項目使用哪種框架?)

                答:選擇 Vue.js

                問:Does your project use TypeScript?(你的項目是否使用 TypeScript?)

                答:選擇No

                問:Where does your code run?(你的代碼在哪裏運行?)

                答:選擇 Browser 和 Node

                問:How would you like to define a style for your project?(你想怎樣爲你的項目定義風格?)

                答:選擇 Use a popular style guide(使用一種流行的風格指南)

                問:Which style guide do you want to follow?(你想遵循哪一種風格指南?)

                答:選擇 Airbnb

                問:What format do you want your config file to be in?(你想你的配置文件使用什麼格式?)

                答:選擇JavaScript

                問:Would you like to install them now with npm?(你想現在就用 NPM 安裝它們嗎?)

                答:選擇Yes

            c)在VSCode使用ESlint,需要安裝插件:ESLint

1.1.3.配置信息

  1 module.exports = {
  2   env: {
  3     browser: true,
  4     es2021: true,
  5     'vue/setup-compiler-macros': true
  6   },
  7   extends: [
  8     'plugin:vue/essential',
  9     'airbnb-base',
 10     'plugin:prettier/recommended' // 添加 prettier 插件
 11   ],
 12   parserOptions: {
 13     ecmaVersion: 'latest',
 14     sourceType: 'module'
 15   },
 16   plugins: ['vue'],
 17   rules: {
 18     /*
 19      * "off" 或 0 - 關閉規則
 20      * "warn" 或 1 - 開啓規則,使用警告級別的錯誤:warn (不會導致程序退出)
 21      * "error" 或 2 - 開啓規則,使用錯誤級別的錯誤:error (當被觸發的時候,程序會退出)
 22      */
 23     'vue/no-multiple-template-root': 'off', // 關閉多根節點的校驗
 24     'vue/multi-word-component-names': 'off', // 關閉組件命名規則
 25     'vue/singleline-html-element-content-newline': 'off',
 26     'vue/multiline-html-element-content-newline': 'off',
 27     'vue/name-property-casing': ['error', 'PascalCase'],
 28     'vue/no-v-html': 'off',
 29 
 30     'import/no-unresolved': 'off',
 31     'import/extensions': 'off',
 32     'import/no-absolute-path': 'off',
 33     'import/no-extraneous-dependencies': 'off',
 34 
 35     'accessor-pairs': 2, // 定義對象的set存取器屬性時,強制定義get
 36     'arrow-spacing': [2, { before: true, after: true }], // =>的前/後括號
 37     'block-spacing': [2, 'always'], // 塊是否需要空格
 38     'brace-style': [2, '1tbs', { allowSingleLine: true }], // if while function 後面的{必須與if在同一行,java風格
 39     camelcase: [0, { properties: 'always' }], // 強制駝峯法命名
 40     'comma-dangle': [2, 'never'], // 數組和對象鍵值對最後一個逗號,never:不能帶末尾的逗號, always:必須帶末尾的逗號,always-multiline:多行模式必須帶逗號,單行模式不能帶逗號
 41     'comma-spacing': [2, { before: false, after: true }], // 控制逗號前後的空格
 42     'comma-style': [2, 'last'], // 控制逗號在行尾出現還是在行首出現
 43     'constructor-super': 2, // 強制在子類構造函數中用super()調用父類構造函數,TypeScript的編譯器也會提示
 44     curly: [2, 'multi-line'], // 強制所有控制語句使用一致的括號風格
 45     'dot-location': [2, 'property'], // 強制object.key中.的位置,參數:property:'.'號應與屬性在同一行, object:'.'號應與對象名在同一行
 46     'eol-last': 2, // 文件末尾強制換行
 47     eqeqeq: ['error', 'always', { null: 'ignore' }], // 使用 === 替代 ==
 48     'generator-star-spacing': [2, { before: true, after: true }], // 生成器函數*的前後空格
 49     'handle-callback-err': [2, '^(err|error)$'], // nodejs 處理錯誤
 50     indent: [2, 2, { SwitchCase: 1 }], // 縮進風格
 51     'jsx-quotes': [2, 'prefer-single'], // JSX 屬性中一致使用雙引號或單引號
 52     'key-spacing': [2, { beforeColon: false, afterColon: true }], // 對象字面量中冒號的前後空格
 53     'keyword-spacing': [2, { before: true, after: true }], // 對象字面量中冒號的前後空格
 54     'new-cap': [2, { newIsCap: true, capIsNew: false }], // 函數名首行大寫必須使用new方式調用,首行小寫必須用不帶new方式調用
 55     'new-parens': 2, // new時必須加小括號
 56     'no-array-constructor': 2, // 禁止使用數組構造器
 57     'no-caller': 2, // 禁止使用arguments.caller或arguments.callee
 58     'no-console': 'off', // 禁用 console
 59     'no-class-assign': 2, // 禁止給類賦值
 60     'no-cond-assign': 2, // 禁止在條件表達式中使用賦值語句
 61     'no-const-assign': 2, // 禁止修改const聲明的變量
 62     'no-control-regex': 0, // 禁止在正則表達式中使用控制字符
 63     'no-delete-var': 2, // 不能對var聲明的變量使用delete操作符
 64     'no-dupe-args': 2, // 函數參數不能重複
 65     'no-dupe-class-members': 2, // 不允許類中出現重複的聲明
 66     'no-dupe-keys': 2, // 在創建對象字面量時不允許鍵重複 {a:1,a:1}
 67     'no-duplicate-case': 2, // switch中的case標籤不能重複
 68     'no-empty-character-class': 2, // 正則表達式中的[]內容不能爲空
 69     'no-empty-pattern': 2, // 禁止使用空解構模式
 70     'no-eval': 2, // 禁止使用eval
 71     'no-ex-assign': 2, // 禁止給catch語句中的異常參數賦值
 72     'no-extend-native': 2, // 禁止擴展native對象
 73     'no-extra-bind': 2, // 禁止不必要的函數綁定
 74     'no-extra-boolean-cast': 2, // 禁止不必要的bool轉換
 75     'no-extra-parens': [2, 'functions'], // 禁止非必要的括號
 76     'no-fallthrough': 2, // 禁止switch穿透
 77     'no-floating-decimal': 2, // 禁止省略浮點數中的0 .5 3.
 78     'no-func-assign': 2, // 禁止重複的函數聲明
 79     'no-implied-eval': 2, // 禁止使用隱式eval
 80     'no-inner-declarations': [2, 'functions'], // 禁止在塊語句中使用聲明(變量或函數)
 81     'no-invalid-regexp': 2, // 禁止無效的正則表達式
 82     'no-irregular-whitespace': 2, // 不能有不規則的空格
 83     'no-iterator': 2, // 禁止使用__iterator__ 屬性
 84     'no-label-var': 2, // label名不能與var聲明的變量名相同
 85     'no-labels': [2, { allowLoop: false, allowSwitch: false }], // 禁止標籤聲明
 86     'no-lone-blocks': 2, // 禁止不必要的嵌套塊
 87     'no-mixed-spaces-and-tabs': 2, // 禁止混用tab和空格
 88     'no-multi-spaces': 2, // 不能用多餘的空格
 89     'no-multi-str': 2, // 字符串不能用\換行
 90     'no-multiple-empty-lines': [2, { max: 1 }], // 空行最多不能超過1行
 91     'no-native-reassign': 2, // 不能重寫native對象
 92     'no-negated-in-lhs': 2, // in 操作符的左邊不能有!
 93     'no-new-object': 2, // 禁止使用new Object()
 94     'no-new-require': 2, // 禁止使用new require
 95     'no-new-symbol': 2, // 禁止使用new symbol
 96     'no-new-wrappers': 2, // 禁止使用new創建包裝實例,new String new Boolean new Number
 97     'no-obj-calls': 2, // 不能調用內置的全局對象,比如Math() JSON()
 98     'no-octal': 2, // 禁止使用八進制數字
 99     'no-octal-escape': 2, // 禁止使用八進制轉義序列
100     'no-path-concat': 2, // node中不能使用__dirname或__filename做路徑拼接
101     'no-proto': 2, // 禁止使用__proto__屬性
102     'no-redeclare': 2, // 禁止重複聲明變量
103     'no-regex-spaces': 2, // 禁止在正則表達式字面量中使用多個空格 /foo bar/
104     'no-return-assign': [2, 'except-parens'], // return 語句中不能有賦值表達式
105     'no-self-assign': 2, // 自我分配
106     'no-self-compare': 2, // 不能比較自身
107     'no-sequences': 2, // 禁止使用逗號運算符
108     'no-shadow-restricted-names': 2, // 嚴格模式中規定的限制標識符不能作爲聲明時的變量名使用
109     'no-spaced-func': 2, // 函數調用時 函數名與()之間不能有空格
110     'no-sparse-arrays': 2, // 禁止稀疏數組, [1,,2]
111     'no-this-before-super': 2, // 在調用super()之前不能使用this或super
112     'no-throw-literal': 2, // 禁止拋出字面量錯誤 throw "error"
113     'no-trailing-spaces': 2, // 一行結束後面不要有空格
114     'no-undef': 2, // 不能有未定義的變量
115     'no-undef-init': 2, // 變量初始化時不能直接給它賦值爲undefined
116     'no-unexpected-multiline': 2, // 避免多行表達式
117     'no-unmodified-loop-condition': 2, // 檢查引用是否在循環中被修改
118     'no-unneeded-ternary': [2, { defaultAssignment: false }], // 禁止可以在有更簡單的可替代的表達式時使用三元操作符
119     'no-unreachable': 2, // 不能有無法執行的代碼
120     'no-unsafe-finally': 2, // 禁止對關係運算符的左操作數使用否定操作符
121     'no-unused-vars': [2, { vars: 'all', args: 'none' }], // 不能有聲明後未被使用的變量或參數
122     'no-useless-call': 2, // 禁止不必要的call和apply
123     'no-useless-computed-key': 2, // 沒有必要使用帶文字的計算屬性
124     'no-useless-constructor': 2, // 可以在不改變類的工作方式的情況下安全地移除的類構造函數
125     'no-useless-escape': 0, // 禁用不必要的轉義字符
126     'no-whitespace-before-property': 2, // 禁止屬性前有空白
127     'no-with': 2, // 禁用with
128     'one-var': [2, { initialized: 'never' }], // 連續聲明
129     'operator-linebreak': [2, 'after', { overrides: { '?': 'before', ':': 'before' } }], // 換行時運算符在行尾還是行首
130     'padded-blocks': [2, 'never'], // 塊語句內行首行尾是否要空行
131     'space-before-blocks': [2, 'always'], // 不以新行開始的塊{前面要不要有空格
132     'space-before-function-paren': [2, 'never'], // 函數定義時括號前面要不要有空格
133     'space-in-parens': [2, 'never'], // 小括號裏面要不要有空格
134     'space-infix-ops': 2, // 中綴操作符周圍要不要有空格
135     'space-unary-ops': [2, { words: true, nonwords: false }], // 一元運算符的前/後要不要加空格
136     // 註釋風格不要有空格什麼的
137     'spaced-comment': [
138       2,
139       'always',
140       { markers: ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ','] }
141     ],
142     'template-curly-spacing': [2, 'never'], // 要求或禁止模板字符串中的嵌入表達式周圍空格的使用
143     'use-isnan': 2, // 禁止比較時使用NaN,只能用isNaN()
144     'valid-typeof': 2, // 必須使用合法的typeof的值
145     'wrap-iife': [2, 'any'], // 立即執行函數表達式的小括號風格
146     'yield-star-spacing': [2, 'both'], // 強制在 yield* 表達式中 * 周圍使用空格
147     yoda: [2, 'never'], // 禁止尤達條件
148 
149     'prefer-const': 2, // 首選const
150     'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0, // 禁用 debugger
151     'object-curly-spacing': [0, 'always', { objectsInObjects: false }], // 大括號內是否允許不必要的空格
152     'array-bracket-spacing': [2, 'never'], // 是否允許非空數組裏面有多餘的空格
153 
154     'no-restricted-syntax': 0, // 禁止使用特定的語法
155     'no-loop-func': 0, // 禁止循環中存在函數
156     'consistent-return': 0, // 要求 return 語句要麼總是指定返回的值,要麼不指定
157     'import/prefer-default-export': 0, // 希望導出default,即export default xxx
158 
159     quotes: [2, 'single', { avoidEscape: true, allowTemplateLiterals: true }], // 引號類型 `` "" ''
160     semi: [2, 'never'], // 語句強制分號結尾
161     'semi-spacing': [2, { before: false, after: true }] // 分號前後空格
162   }
163 }
View Code

1.1.4.使用問題

         a)在VSCode下ESlint插件不生效,如何排查問題

               1.確保eslint配置文件和node_modules在根目錄下

               2.確定安裝了vscode eslint插件

               3.確認package.json中安裝了 eslint和eslint-plugin等

               4.vscode 按下f1

               5.輸入eslint

               6.選擇ESLint: Show Output Channel查看eslint報錯信息

               7.按照錯誤信息,去解決

        b)defineProps' is not defined

              1.定位問題:defineProps 屬於 Vue3 的規則校驗,需要在 eslint-plugin-vue官方指南中尋找對應配置。

              2.解決問題:打開.eslintrc.js文件,在env配置項中添加“'vue/setup-compiler-macros': true”

1.2.prettier 

1.2.1.簡要說明:它是一款強大的代碼格式化工具,支持JavaScript、Typescript、Css、Scss、Less、JSX、Angular、Vue、GraphQL、JSON、Markdown等,基本上前端能用到的文件格式都可以搞定。

1.2.2.步驟過程

     a)安裝prettier:npm i prettier -D

          b)創建 Prettier 配置文件,在./src下創建.prettierrc文件,文件內容如下:

 1 {
 2   "useTabs": false,
 3   "tabWidth": 2,
 4   "printWidth": 100,
 5   "singleQuote": true,
 6   "trailingComma": "none",
 7   "bracketSpacing": true,
 8   "semi": false,
 9   "endOfLine": "auto"
10 }
View Code

          c)使用其命令格式化代碼文件

                # 格式化所有文件 (. 表示所有文件)

                npx prettier --write .

         d)使用VSCode編輯器,安裝Prettier插件:Prettier - Code formatter

 

1.3.兼容問題

1.3.1.基於個人觀點,其實使用eslint基本上已經滿足需求了,使用這種組合方式反而會出現很多兼容性問題要解決。看到網友都推薦eslint+prettier組合,就記錄一下吧。

1.3.2.解決eslint和prettier衝突

          a)安裝插件:npm i eslint-plugin-prettier eslint-config-prettier -D

          b)修改.eslintrc.js配置文件,添加prettier插件

1 module.exports = {
2   ...
3   extends: [
4     'plugin:vue/essential',
5     'airbnb-base',
6     'plugin:prettier/recommended' // 添加 prettier 插件
7   ],
8   ...
9 }
View Code

         c)插件功能說明:

              eslint-plugin-prettier 將 Prettier 的規則設置到 ESLint 的規則中。

              eslint-config-prettier 關閉 ESLint 中與 Prettier 中會發生衝突的規則。

        d)特別說明:即便配置上解決規則衝突的設置,還是會出現很多兼容性問題。

 

2.postcss

2.1.簡要說明:postcss是一種對css編譯的工具,類似於babel處理js,主要功能:1.使用下一代css語法,2.自動補全瀏覽器前綴,3.自動把px替換爲rem,4.css代碼壓縮等。

2.2.步驟過程

  a).安裝postcss和postcss-preset-env插件:npm install postcss postcss-preset-env -D

  b).配置vite.config.js文件:

 1 import { defineConfig } from 'vite'
 2 import vue from '@vitejs/plugin-vue'
 3 import postcssPresetEnv from 'postcss-preset-env'
 4 
 5 // https://vitejs.dev/config/
 6 export default defineConfig({
 7   base: './',
 8   publicDir: 'public', // 靜態資源服務的文件夾
 9   logLevel: 'info', // 控制檯輸出的級別 info 、warn、error、silent
10   clearScreen: true, // 設爲false 可以避免 vite 清屏而錯過在終端中打印某些關鍵信息
11   css: {
12     postcss: {
13       plugins: [postcssPresetEnv]
14     }
15   }
16 })
View Code

       c).修改App.vue文件,編寫測試示例:

 1 <template>
 2   <img alt="Vue logo" src="@/assets/logo.png" />
 3 
 4   <div class="title">Hello Vite!</div>
 5 
 6   <router-view />
 7 </template>
 8 
 9 <script>
10 
11 export default {
12   name: 'App'
13 }
14 </script>
15 
16 <style type="text/css">
17 .title {
18   font-size: 30px;
19   color: yellow;
20   user-select: none;
21 }
22 </style>
View Code

2.3.查看效果:啓動項目,命令:npm run dev。我們重點關注樣式title的user-select: none;打開瀏覽器,你會看到user-select:none;多出了好幾處有前綴的屬性項,如:-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;

2.4.注意事項

      2.4.1.上述過程中,使用了postcss-preset-env與postcss搭配,你也可以使用其他的插件,如autoprefixer等

      2.4.2.由於vite自身已經集成了postcss,所以不要在根目錄再創建postcss.config.js文件。如果創建了這個文件可能還會出錯,因爲vite是基於esmodule管理的,所有文件都會轉換爲module。

 

3.svgIcon

3.1.簡要說明:svg圖標是基於xml定義可縮放的矢量圖形,由於其不會圖形質量不會因分辨率導致失真,所以被大量應用於程序編碼中。

3.2.步驟過程

        a)創建目錄和文件結構:

             目錄:./src/icons

             目錄:./src/icons/svg

             文件:./src/icons/index.vue

             文件:./src/icons/svgBuilder.js

        b)在./src/icons/svg目錄裏添加測試圖標login-user.svg

        c)安裝svg插件:npm install svg-sprite-loader -D

        d)編寫./src/icons/index.vue文件

 1 <template>
 2   <div
 3     v-if="isExternal"
 4     :style="styleExternalIcon"
 5     class="svg-external-icon svg-icon"
 6     v-on="$attrs"
 7   />
 8   <svg v-else :class="svgClass" aria-hidden="true" v-on="$attrs">
 9     <use :xlink:href="iconName" />
10   </svg>
11 </template>
12 <script>
13 /**
14  * 名稱:SvgIcon
15  * @param iconClass String required
16  * @param className String
17  * 依賴:src/icons/svgBuilder.js 需要在vite中配置
18  * 使用方式:
19  * 在 template 中使用 <svg-icon icon-class="login-user"/>
20  */
21 export default {
22   name: 'SvgIcon',
23   props: {
24     iconClass: {
25       type: String,
26       required: true
27     },
28     className: {
29       type: String,
30       default: ''
31     }
32   },
33   computed: {
34     isExternal() {
35       return /^(https?:|mailto:|tel:)/.test(this.iconClass)
36     },
37     iconName() {
38       return `#icon-${this.iconClass}`
39     },
40     svgClass() {
41       if (this.className) {
42         return `svg-icon ${this.className}`
43       }
44       return 'svg-icon'
45     },
46     styleExternalIcon() {
47       return {
48         mask: `url(${this.iconClass}) no-repeat 50% 50%`,
49         '-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%`
50       }
51     }
52   }
53 }
54 </script>
55 
56 <style scoped>
57 .svg-icon {
58   width: 1em;
59   height: 1em;
60   vertical-align: -0.15em;
61   fill: currentColor;
62   overflow: hidden;
63 }
64 
65 .svg-external-icon {
66   background-color: currentColor;
67   mask-size: cover !important;
68   display: inline-block;
69 }
70 </style>
View Code

        e)引入./src/icons/index.vue文件,在main.js文件中

1 import * as Vue from 'vue'
2 
3 import SvgIcon from './icons/index.vue'
4 
5 import App from './App.vue'
6 
7 const app = Vue.createApp(App)
8 app.component('svg-icon', SvgIcon)
9 app.mount('#app')
View Code

        f)編寫./src/icons/svgBuilder.js文件,需要安裝fs模塊:npm install fs

 1 import { readFileSync, readdirSync } from 'fs'
 2 
 3 let idPerfix = ''
 4 const svgTitle = /<svg([^>+].*?)>/
 5 const clearHeightWidth = /(width|height)="([^>+].*?)"/g
 6 const hasViewBox = /(viewBox="[^>+].*?")/g
 7 const clearReturn = /(\r)|(\n)/g
 8 
 9 function svgFind(dir) {
10   const svgRes = []
11   const dirents = readdirSync(dir, {
12     withFileTypes: true
13   })
14   for (const dirent of dirents) {
15     if (dirent.isDirectory()) {
16       svgRes.push(...svgFind(`${dir + dirent.name}/`))
17     } else {
18       const svg = readFileSync(dir + dirent.name)
19         .toString()
20         .replace(clearReturn, '')
21         .replace(svgTitle, ($1, $2) => {
22           let width = 0
23           let height = 0
24           let content = $2.replace(clearHeightWidth, (s1, s2, s3) => {
25             if (s2 === 'width') {
26               width = s3
27             } else if (s2 === 'height') {
28               height = s3
29             }
30             return ''
31           })
32           if (!hasViewBox.test($2)) {
33             content += `viewBox="0 0 ${width} ${height}"`
34           }
35           return `<symbol id="${idPerfix}-${dirent.name.replace('.svg', '')}" ${content}>`
36         })
37         .replace('</svg>', '</symbol>')
38       svgRes.push(svg)
39     }
40   }
41   return svgRes
42 }
43 
44 export const svgBuilder = (path, perfix = 'icon') => {
45   if (path === '') return
46   idPerfix = perfix
47   const res = svgFind(path)
48 
49   return {
50     name: 'svg-transform',
51     transformIndexHtml(html) {
52       return html.replace(
53         '<body>',
54         `
55           <body>
56             <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position: absolute; width: 0; height: 0">
57               ${res.join('')}
58             </svg>
59         `
60       )
61     }
62   }
63 }
View Code

       g)配置vite.config.js文件,使svgBuilder.js文件功能啓用

 1 import { defineConfig } from 'vite'
 2 import vue from '@vitejs/plugin-vue'
 3 import { svgBuilder } from './src/icons/svgBuilder'
 4 
 5 // https://vitejs.dev/config/
 6 export default defineConfig({
 7   base: './',
 8   plugins: [
 9     vue(),
10     svgBuilder('./src/icons/svg/') // 這裏已經將src/icons/svg/下的svg全部導入
11   ]
12 })
View Code

       h)修改App.vue文件,編寫測試示例

 1 <template>
 2   <img alt="Vue logo" src="@/assets/logo.png" />
 3   <div>
 4     <svg-icon icon-class="login-user" />
 5   </div>
 6   <router-view />
 7 </template>
 8 
 9 <script>
10 export default {
11   name: 'App'
12 }
13 </script>
View Code

3.3.查看效果:啓動項目,命令:npm run dev。

 

二、總結

通過《vite+vue3》和《vite+vue3 2》兩篇文章的記錄,基本上把web開發中使用的基礎模塊和常用插件都記錄了。

 

三、參考信息

1.https://blog.csdn.net/z591102/article/details/106787171/

2.https://juejin.cn/post/6991233820410249252

3.https://juejin.cn/post/6973288527802925092

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章