vite + vue3 多頁面實(shí)戰(zhàn)優(yōu)化續(xù)集:eslint+lint-staged+husky+stylelint

目的:項(xiàng)目投入使用發(fā)現(xiàn)很多使用起來不舒服的地方,進(jìn)行優(yōu)化

注意!!!:最新的一次創(chuàng)建項(xiàng)目運(yùn)行時(shí)候以及配置eslint部分出現(xiàn)了許多問題,可以結(jié)合最新的一篇vite+vue+ssg做官網(wǎng) 再記錄一下項(xiàng)目創(chuàng)建結(jié)合起來查看是否有你遇到的問題

前提:在上一篇vite + vue3多頁面配置記錄references,loadEnv等中我詳細(xì)記錄了通過各種配置了解多頁面項(xiàng)目。
結(jié)果:最終代碼放到gitee這里的release分支vite-vue3-multip-release

之前配置不合理的地方:

  1. 為了讓項(xiàng)目啟動(dòng)的時(shí)候自動(dòng)打開html,修改了項(xiàng)目根目錄到views下面root:'./src/views/',以至于outDir,windicss也要改。
  2. 所有生成的html全部扎堆生成在views下面,結(jié)構(gòu)混亂
  3. 未配置eslint,保存自動(dòng)格式化
  4. 未配置代碼提交自動(dòng)跑eslint檢測(cè)。使用lint-staged + husky
  5. 未配置stylelint,規(guī)范css書寫
  6. 未配置快速生成頁面腳本:用plop搞個(gè)模版生成index.vue和main.ts,可以直接看代碼庫代碼
上一版本文件結(jié)構(gòu).png

項(xiàng)目到實(shí)踐中肯定是不合理的,用起來不舒服。逐步解決上面的問題

第一步:不修改根目錄路徑;在根目錄新建一個(gè)html文件夾,并將生成的html放到根目錄統(tǒng)一管理,且要支持多級(jí)頁面

目錄結(jié)構(gòu).png

通過上圖目錄結(jié)構(gòu)可以看到,src/views下面有多級(jí)頁面,最后生成到src同級(jí)目錄下的html文件夾中,且結(jié)構(gòu)與之對(duì)應(yīng)。這樣看起來就很舒服,html文件夾中的東西根本不用動(dòng),通過腳本,每次dev的時(shí)候自動(dòng)生成

我們看代碼實(shí)現(xiàn),主要還是去修改build/getPage.ts, 利用mkdirp生成多級(jí)文件夾

import glob from 'glob'
import fs from 'fs'
import mkdirp from 'mkdirp'
import { resolve } from 'path'

const input = {}
const mainEntry = []
const iconDirs = []
function getPages () {
  // 遍歷文件夾中含有main.ts的文件夾路徑
  const allEntry = glob.sync('./src/views/**/main.ts')
  // console.log(allEntry)

  // console.log('allEntry', allEntry)
  // 創(chuàng)建多頁面模板
  allEntry.forEach((entry: string) => {
    const pathArr = entry.split('/')
    const tsName = pathArr[pathArr.length - 2]
    pathArr.pop()
    const targetFilePath = pathArr.splice(3).join('/')
    // 創(chuàng)建多級(jí)文件夾,去掉最后一個(gè)
    const mkdirPath = targetFilePath.replace(`/${tsName}`, '')
    const srcArr = entry.replace('./', '')
    // console.log(targetFilePath, mkdirPath)
    // 判斷文件是否存在
    try {
      fs.accessSync(`./html/${targetFilePath}.html`)
    } catch (err) {
      if (targetFilePath.includes('/')) {
        mkdirp(`./html/${mkdirPath}`).then(() => {
          generete(targetFilePath, srcArr)
        })
      } else {
        generete(targetFilePath, srcArr)
      }
    }

    const svgPath = srcArr.split('/')
    svgPath.splice(svgPath.length - 1, 1)
    // input中的配置
    input[targetFilePath] = resolve(`html/${targetFilePath}.html`)
   // vconsole用
    mainEntry.push(resolve(`${srcArr}`))
    iconDirs.push(resolve(process.cwd(), `${svgPath.join('/')}/svg`))
  })
};

function generete (targetFilePath, srcArr) {
  // 獲取模板
  const temp = fs.readFileSync('./index.html')
// 多級(jí)相對(duì)路徑
  let relativeStr = ''
  targetFilePath.split('/').forEach(() => {
    relativeStr += '../'
  })
  console.log(`創(chuàng)建${targetFilePath}.html文件`)
  const index = temp.toString().indexOf('</body>')
  const content =
    temp.toString().slice(0, index) +
    `<script type="module" src="${relativeStr}${srcArr}"></script>` +
    temp.toString().slice(index)
  fs.writeFile(`./html/${targetFilePath}.html`, content, err => {
    if (err) console.log(err)
  })
}
getPages()
// console.log(input, mainEntry, iconDirs)
export { input, mainEntry, iconDirs }


有了以上代碼。每次運(yùn)行都會(huì)根據(jù)入口main.ts,生成對(duì)應(yīng)的html。但是啟動(dòng)項(xiàng)目怎么直接打開我想要的index.html呢?

server里面有配置,配置open即可。想到當(dāng)時(shí)僅僅為了啟動(dòng)項(xiàng)目打開入口頁面就更改root是多么的不理智了

// vite.config.ts
 server: {
      open: '/html/index.html',
      proxy: {
        '/api': {
          target: 'https://app-api-0.com',
          changeOrigin: true
        }
      }
    },

第二步:配置eslint

這個(gè)就比較簡(jiǎn)單了,網(wǎng)上也有很多教程,當(dāng)走到教程前面幾步就可以了。我這里也記錄一下

  1. 安裝eslint npm i eslint -D
  2. 初始化配置:npx eslint --init
    然后根據(jù)提示一步步操作
    2.1 選擇模式: (To check syntax and find problems)
    我選第三個(gè):To check syntax, find problems, and enforce code style
    2.2 (選JavaScript modules): 選第一個(gè):JavaScript modules (import/export)
    2.3 選擇語言框架 (選Vue.js)
    2.4 是否使用ts : 我這里選yes
    2.5 代碼在哪里運(yùn)行 (用空格選中 Browser+Node)
    2.6 選擇一個(gè)風(fēng)格:我選的Standard, 完全就夠用了
    后面就是yes安裝,會(huì)發(fā)現(xiàn)安裝了這些插件
"eslint-config-standard": "^17.0.0",
    "eslint-plugin-import": "^2.26.0",
    "eslint-plugin-n": "^15.2.4",
    "eslint-plugin-promise": "^6.0.0",
    "eslint-plugin-vue": "^9.2.0",
"@typescript-eslint/eslint-plugin": "^5.30.7",
    "@typescript-eslint/parser": "^5.30.7",

一些教程里面還單獨(dú)安裝@typescript-eslint/parser,我實(shí)踐中走完上面就已經(jīng)全部有了,生成的.eslintrc.js也不需要修改。

只是部分規(guī)則不適用vue3,我們可以在rules直接關(guān)掉即可:比如 vue/multi-word-component-names, no-irregular-whitespace

3.選擇性安裝vite-plugin-eslint, 用于配置vite運(yùn)行的時(shí)候自動(dòng)檢測(cè)eslint規(guī)范,使用npm run dev時(shí)會(huì)主動(dòng)檢查代碼并報(bào)錯(cuò)

npm i -D vite-plugin-eslint

使用
import eslint from 'vite-plugin-eslint'

export default defineConfig({
  plugins: [
    vue(),
   eslint()
  ]
})
  1. setting.json 需要的內(nèi)容
// setting.json
{
  // 重新設(shè)定tabsize
  "editor.tabSize": 2,
  // 每次保存的時(shí)候自動(dòng)格式化 
  "editor.formatOnSave": false,
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true
  }
 }

這里提一嘴i18n-ally多語言翻譯這個(gè)vscode 插件,好用。

// setting.json中加入
{
  "i18n-ally.localesPaths": ["src/views/login/lang"],
  "i18n-ally.keystyle": "nested",
  "i18n-ally.sortKeys": true,
// pathMatcher 必需開啟namespace ,{locale}指的就是翻譯文件名"i18n-ally.localesPaths",{ext}就是enabledParsers中的文件后綴
  "i18n-ally.namespace": true,
  "i18n-ally.pathMatcher": "{locale}/{namespaces}.{ext}",
  "i18n-ally.enabledParsers": ["js", "json", "ts"],
  "i18n-ally.sourceLanguage": "en",
  "i18n-ally.displayLanguage": "zh", // 展示中文
  "i18n-ally.enabledFrameworks": ["vue", "react"],
}

主要提這個(gè)i18n-ally.pathMatcher路徑匹配,多份翻譯文件,放在在同一個(gè)二級(jí)文件src/lang/en,src/lang/zh夾就能通過它namespaces匹配到且生效

效果圖.png

第三步:配置lint-staged + husky。在git commit 的時(shí)候?qū)μ峤晃募M(jìn)行校驗(yàn)

lint-staged工具對(duì)暫存的代碼進(jìn)行 lint,通常都是配配合husky使用.
husky幫助你在git操作環(huán)節(jié)執(zhí)行l(wèi)int-staged,也就是里面的 eslint --fix 對(duì)暫存的src下面的文件進(jìn)行eslint校驗(yàn)。
一般我們會(huì)看到如下package.json配置

//package.json
{
  "lint-staged": {
    "src/**/*.{js,jsx,ts,tsx,vue}": [ 
      "eslint --fix" // 僅僅校驗(yàn)修改暫存的文件
       // "npm run lint:eslint". 如果要校驗(yàn)所有文件的也可以,除非你想這么做
    ]
  }
}

//"scripts": {
//"lint:eslint": "eslint --cache --max-warnings 0  \"{src,mock}/**/*.{vue,ts,js,tsx}\" --fix",
// }

通過husky安裝參考文檔可以發(fā)現(xiàn),安裝husky有兩種方式,結(jié)果是一樣的

  1. 自動(dòng)方式,只需要執(zhí)行一句npx husky-init && npm install
npx husky-init && npm install       # npm
npx husky-init && yarn              # Yarn 1
yarn dlx husky-init --yarn2 && yarn # Yarn 2+
pnpm dlx husky-init && pnpm install # pnpm

執(zhí)行完后會(huì)在package.json/scripts 自動(dòng)加上"prepare": "husky install",
在根目錄生成了一個(gè).husky文件夾,里面有個(gè)_pre-commit腳本文件

#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npm test

修改npm test為自己的腳本命令,比如:npx lint-staged

這里的命令修改就可以自由發(fā)揮了,只要能觸發(fā)package.json中的 lint-staged就成
比如我可以在這里寫npm run lint:lint-staged,那package.json/scripts中有對(duì)應(yīng)的命令"lint:lint-staged": "lint-staged"即可
tip:不走lint-staged,直接在這里寫npm run lint:eslint也是可以的(lint:eslint是scripts中的eslint --fix命令)。說明僅僅只安裝husky也是可以的,那也就是校驗(yàn)所有文件了。配合lint-staged只校驗(yàn)暫存區(qū)文件才是最好的

  1. 手動(dòng)方式,按照順序多執(zhí)行幾個(gè)命令
  // 先安裝
npm i husky -D.
// 先執(zhí)行這句才能執(zhí)行有第四句
npx husky install 
 // 在package.json/scripts 加上`"prepare": "husky install"`。自己手動(dòng)去加也行
npm set-script prepare "husky install" 
// 這句就是在`_pre-commit`腳本文件加上自己的命令啦
npx husky add .husky/pre-commit "npx  lint-staged"

以上就能成功了。

第四步:配置stylelint

vscode 擴(kuò)展必不可少:
stylelint.png
  1. 通過stylelint官方文檔我們先安裝三個(gè)插件:我項(xiàng)目用的less,多來一個(gè)postcss-less。如果你用scss,來一個(gè)postcss-scss就好,下面配置一樣的
npm i stylelint stylelint-config-standard postcss-less -D
  1. 新建文件.stylelintignore
/dist/*
/public/*
public/*
  1. 新建.stylelintrc.js。stylelint默認(rèn)只能識(shí)別css文件,我項(xiàng)目使用的less,上面安裝一個(gè)postcss-less,使用官網(wǎng)提到的自定義語法customSyntax參數(shù)讓其能夠識(shí)別less文件。
    如果沒有下面配置的overrides/customSyntax,less文件中會(huì)有看不懂的報(bào)錯(cuò):Unknown word (CssSyntaxError)Stylelint(CssSyntaxError)
// .stylelintrc.js
module.exports={
  extends:["stylelint-config-standard",],
  overrides:[
          {
            files:["**/*.less"],
              customSyntax:"postcss-less"
          }
    ]
}
  1. 有了上面的操作,已經(jīng)能看到less文件中的樣式各種紅色提示了,還需要保存自動(dòng)格式化,在.vscode/setting.json中增加下面的內(nèi)容就可以了。
 "editor.codeActionsOnSave": {
    "source.fixAll.stylelint": true
  }
  1. 項(xiàng)目是vue項(xiàng)目,vue文件中的樣式無法識(shí)別,而且能看到輸出的報(bào)錯(cuò)信息,提示我要postcss-html,安裝它
    輸出提示vue文件識(shí)別樣式.png

    在.stylelintrc.js中加入它
module.exports={
  extends:["stylelint-config-standard"],
  customSyntax: "postcss-html",
  overrides: [
    {
      files:["**/*.less"],
      customSyntax:"postcss-less"
    }
  ],
}

這時(shí)候你會(huì)發(fā)現(xiàn)vue文件中樣式并沒有提示問題。還差一步,在.vscode/settings.json中增加

"stylelint.validate": ["vue", "less", "css"]

有了以上的簡(jiǎn)單配置,就可以保存自動(dòng)按照stylelint格式化你的樣式了

  1. 升級(jí)內(nèi)容
    (1)結(jié)合lint-staged,提交檢測(cè)樣式,在package.json中增加
"lint-staged": {
    "**/*.{css,less,scss,vue}":[
      "stylelint --fix"
    ]
  }

(2)插件幫助我們css按照順序書寫

// 安裝插件
npm i -D stylelint-order stylelint-config-rational-order

stylelint-config-rational-order插件用來簡(jiǎn)化了插件stylelint-order的引用和配置。你只用stylelint-order也是可以的

//.stylelintrc.js 中使用插件
module.exports={
  extends:["stylelint-config-standard", 'stylelint-config-rational-order'],
}

下面是一些問題記錄

1. 當(dāng)項(xiàng)目中使用了自動(dòng)導(dǎo)入unplugin-auto-import/vite插件,eslint會(huì)提示ref等不存在

(1) 安裝 vue-global-api
(2) 在eslintrc.js中加入它

extends: [
    'plugin:vue/vue3-essential',
    'standard',
    'vue-global-api'
  ],
2. 記錄一下cross-env,cross-env 使用單個(gè)命令,而不用擔(dān)心環(huán)境變量的設(shè)置

安裝

npm i cross-env -D

使用

"report": "cross-env REPORT=true npm run build",

取參數(shù)

const isReport = process.env.REPORT === 'true'
isReport
      ? visualizer({
   //生成的stats放到別的地方去
        filename: './node_modules/.cache/visualizer/stats.html',
        open: true,
        gzipSize: true,
        brotliSize: true
      })
      : [],
3. 項(xiàng)目中加入一個(gè)插件后,在linux編譯時(shí)莫名其妙報(bào)錯(cuò):Error: The package "esbuild-linux-64" could not be found, and is needed by esbuild.

If you are installing esbuild with npm, make sure that you don't specify the
"--no-optional" flag. The "optionalDependencies" package.json feature is used
by esbuild to install the correct binary executable for your current platform.

按照道理說jenkins工作區(qū)域的node_modules早就有了,怎么突然沒這個(gè)依賴了呢?
直接刪除工作區(qū)域代碼,重新npm i 跑一遍解決了

4. 該項(xiàng)目開發(fā)的多頁面是給安卓app用的,用的lzyzsd/JsBridge。舊版app使用registerHandler提供native調(diào)用js的能力,在新版app失效,這個(gè)庫issue里面也提到了

直接將js方法掛到window下面,native去調(diào)用

// 該方法給安卓調(diào)用,安卓返回是否開啟消息提醒
window.getPunchInReminderStatus = (res) => {
systemRemindOpen = res === '1'
}
5. window.onresize不觸發(fā)

舊版本安卓在進(jìn)入頁面或者失去焦點(diǎn)的時(shí)候會(huì)觸發(fā)window.onresize,當(dāng)時(shí)使用它時(shí)為了控制fixed元素不被鍵盤頂起來。
在新版安卓中,鍵盤并沒有將底部fixed元素頂起來,且失去焦點(diǎn)的時(shí)候window.onresize不觸發(fā)了,用不上它了

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容