在线不卡日本ⅴ一区v二区_精品一区二区中文字幕_天堂v在线视频_亚洲五月天婷婷中文网站

  • <menu id="lky3g"></menu>
  • <style id="lky3g"></style>
    <pre id="lky3g"><tt id="lky3g"></tt></pre>

    一篇學(xué)會(huì)如何使用Vite重構(gòu)Vue3項(xiàng)目

    一篇學(xué)會(huì)如何使用Vite重構(gòu)Vue3項(xiàng)目

    前言

    截止發(fā)文時(shí)間,vite正式版已經(jīng)發(fā)布快2年時(shí)間了,vue3也發(fā)布到3.2版本了,它的周邊設(shè)施基本上已經(jīng)齊活了。也是時(shí)候再次重構(gòu)下我那個(gè)vue3.0的開(kāi)源項(xiàng)目了。

    本篇文章就記錄下我的重構(gòu)過(guò)程,歡迎各位感興趣的開(kāi)發(fā)者閱讀本文。

    環(huán)境搭建

    1年多前,我用Vue Cli 4.5構(gòu)建的此項(xiàng)目,有關(guān)此項(xiàng)目的更多細(xì)節(jié)請(qǐng)移步我的另一篇文章使用Vue3重構(gòu)Vue2項(xiàng)目。同樣的,從CLI遷移到Vite仍然是在package.json中添加vite的依賴項(xiàng),在項(xiàng)目中添加它的配置文件。

    此次項(xiàng)目構(gòu)建還加入了volta的相關(guān)配置,對(duì)此感興趣的開(kāi)發(fā)者請(qǐng)移步:強(qiáng)大的JavaScript工具管理器Volta

    新增vite相關(guān)依賴項(xiàng)

    我們打開(kāi)package.json,找到devDependencies字段,移除CLI相關(guān)的依賴,添加vite相關(guān)的依賴,如下所示:

    • +綠色標(biāo)識(shí)代表新增
    • -紅色標(biāo)識(shí)代表移除

    { “dependencies”: {- “compression-webpack-plugin”: “^5.0.1”, }, “devDependencies”: {+ “@vitejs/plugin-vue”: “^3.0.0”,+ “vite”: “^3.0.0”,+ “vue-tsc”: “^0.38.4”,+ “@types/node”: “^18.6.3”,- “sass-loader”: “^8.0.2”,- “@vue/cli-plugin-babel”: “~4.5.0”,- “@vue/cli-plugin-eslint”: “~4.5.0”,- “@vue/cli-plugin-router”: “~4.5.0”,- “@vue/cli-plugin-typescript”: “~4.5.0”,- “@vue/cli-plugin-vuex”: “~4.5.0”,- “@vue/cli-service”: “~4.5.0”,- “@vue/compiler-sfc”: “^3.0.0-0”- }}

    隨后,我們找到scripts字段,修改項(xiàng)目的運(yùn)行與構(gòu)建命令。

    { “scripts”: { “serve”: “vite –open”, “build”: “vue-tsc –noEmit && vite build”, “preview”: “vite preview” }}

    vite3.x版本要求node版本必須大于14.18.0,因此我們需要在engines字段中做一下提示,如下所示:

    { “engines”: { “npm”: “please-use-yarn”, “yarn”: “>= 1.0.0”, “node”: “>= 14.18.0” }}

    除了上述配置外,我們還需要在項(xiàng)目的根目錄創(chuàng)建.npmrc文件,寫(xiě)入下述內(nèi)容:

    engine-strict = true

    配置完成后,我們執(zhí)行在終端執(zhí)行yarn install安裝依賴即可。

    在上述配置中,我們還強(qiáng)制設(shè)置了yarn作為項(xiàng)目的包管理工具,如果項(xiàng)目開(kāi)發(fā)成員使用了npm install則不會(huì)開(kāi)始安裝依賴并提示其使用yarn來(lái)安裝依賴。

    添加vite配置文件

    在vite中,index.html已經(jīng)從public文件夾遷移到項(xiàng)目的根目錄下了,官方文檔對(duì)此的解釋為:在開(kāi)發(fā)期間 Vite 是一個(gè)服務(wù)器,而 index.html 是該 Vite 項(xiàng)目的入口文件。

    有關(guān)此變更的詳細(xì)解釋請(qǐng)移步:index.html 與項(xiàng)目根目錄

    接下來(lái),我們?cè)陧?xiàng)目的根目錄創(chuàng)建index.html文件(將public目錄下的文件刪除)

    • 引入靜態(tài)文件時(shí)不需要使用%PUBLIC_URL%作為占位符,可以直接寫(xiě)/來(lái)訪問(wèn),vite會(huì)將其解析到public根目錄下
    • 通過(guò)

      注意:如果你的項(xiàng)目比較復(fù)雜,有多個(gè)入口,那么就將index.html文件放到對(duì)應(yīng)入口的根目錄下。

      最后,我們創(chuàng)建vite.config.ts文件,配置代碼如下所示:

      • 設(shè)置開(kāi)發(fā)環(huán)境的端口號(hào)
      • 設(shè)置路徑別名
      • 設(shè)置打包后base地址以及打包輸出目錄

      import { defineConfig } from “vite”;import { resolve } from “path”;import vue from “@vitejs/plugin-vue”;const IS_PRODUCTION = process.env.NODE_ENV === “production”;export default defineConfig({ plugins: [vue()], server: { host: true, port: 8020, proxy: {} }, resolve: { // 設(shè)置路徑別名 alias: { “@”: resolve(__dirname, “./src”), “*”: resolve(“”) } }, base: IS_PRODUCTION ? “/chat-system” : “./”, define: { “process.env”: {} }, build: { outDir: resolve(__dirname, “dist”) }});

      注意:我的項(xiàng)目配置比較簡(jiǎn)單,它只有一個(gè)入口,打包后只會(huì)部署到生產(chǎn)環(huán)境。如果你的項(xiàng)目較為復(fù)雜,也不必太過(guò)擔(dān)心,你的應(yīng)用場(chǎng)景vite也是支持的,按照文檔進(jìn)行相關(guān)的配置就好,如下所示:

      • 自定義構(gòu)建
      • 多頁(yè)面應(yīng)用模式
      • 環(huán)境變量和模式

      當(dāng)你的項(xiàng)目有多個(gè)入口時(shí),期望通過(guò)不同命令來(lái)啟動(dòng)不同項(xiàng)目時(shí),你可以使用yarn的–cwd指令來(lái)指定其運(yùn)行時(shí)的工作目錄。

      例如:你有兩個(gè)入口,那么就在src目錄下創(chuàng)建兩個(gè)文件夾:**A、B **。A和B中分別有自己的index.html、main.ts以及package.json文件(配置start、build命令,傳入不同的參數(shù)來(lái)啟動(dòng)/構(gòu)建不同入口的項(xiàng)目)

      根目錄的package.json中你就可以配置啟動(dòng)/構(gòu)建命令為:

      { “scripts”: { “dev:A”: “yarn –cwd ./src/A run start”, “dev:B”: “yarn –cwd ./src/B run start”, “build:A”: “yarn –cwd ./src/A run build”, “build:B”: “yarn –cwd ./src/B run build”, “build”: “vue-tsc –noEmit && vite build”, “preview”: “vite preview” },}

      最后,我們以A入口為例,列舉下package.json文件中的配置:

      { “name”: “A”, “version”: “1.0.0”, “main”: “index.js”, “license”: “MIT”, “scripts”: { “start”: “vite serve –config ../../vite.config-A.ts –mode development”, “build”: “vue-tsc –noEmit && vite build –config ../../vite.config-A.ts –mode production” }}

      升級(jí)Vue周邊依賴項(xiàng)

      vue3.2的單文件組件引入了setup規(guī)范,它可以讓代碼變得更簡(jiǎn)潔,可以使用純 TypeScript 聲明 props 和拋出事件,有著更好的運(yùn)行時(shí)性能。這些優(yōu)點(diǎn)讓我有了升級(jí)vue版本的動(dòng)力,之前的3.0版本寫(xiě)起來(lái)很臃腫,需要return一大堆東西,甚是麻煩。

      打開(kāi)package.json文件作出下述變動(dòng):

      • 更新了vue、router、vuex的版本號(hào)
      • 新增了vueuse包,這是一個(gè)基于 Composition API 的實(shí)用函數(shù)集合,封裝了一些常用的功能(實(shí)時(shí)獲取鼠標(biāo)位置、防抖、節(jié)流、獲取客戶端系統(tǒng)主題等),可以避免一些重復(fù)性的工作內(nèi)容,大大提升開(kāi)發(fā)效率。

      { “dependencies”: { – “vue”: “^3.0.0-0”,- “vue-class-component”: “^8.0.0-0”- “vue-router”: “^4.0.0-0”,- “vuex”: “^4.0.0-0”,+ “vue”: “^3.2.37”,+ “vue-router”: “^4.1.3”,+ “vuex”: “^4.0.2”,+ “@vueuse/components”: “^8.9.2”,+ “@vueuse/core”: “^8.9.2” }}

      最后執(zhí)行yarn install即可完成整個(gè)環(huán)境的搭建,本章節(jié)重構(gòu)完成后的完整文件請(qǐng)移步:

      • .npmrc
      • index.html
      • package.json
      • vite.config.ts

      經(jīng)驗(yàn)分享

      本章節(jié)就跟大家分享下,我切到新環(huán)境后做的一些優(yōu)化點(diǎn)以及遇到的問(wèn)題和解決方案。

      本章節(jié)修改到的文件,完整文件代碼如下:

      • package.json
      • tsconfig.json

      require不存在

      一切準(zhǔn)備就緒后,按下了項(xiàng)目啟動(dòng)按鈕,很快啊,651ms項(xiàng)目就啟動(dòng)了,不愧是vite速度就是快,嘴角瘋狂上揚(yáng)。

      瀏覽器加載完項(xiàng)目后,我傻眼了,我的登陸界面呢:new_moon_with_face:?順勢(shì)打開(kāi)控制臺(tái),發(fā)現(xiàn)報(bào)錯(cuò)require is not defined。

      解決方案

      打開(kāi)Login.vue文件后,發(fā)現(xiàn)我用require導(dǎo)入了一些圖片文件,在VueCLI環(huán)境下的require會(huì)交給webpack處理。在vite中是不存在的,那么我們就需要查看vite是怎么處理靜態(tài)文件了。

      翻了下文檔后,在靜態(tài)資源處理章節(jié)發(fā)現(xiàn)他有兩種處理方法

      • 通過(guò)import語(yǔ)句直接導(dǎo)入圖片
      • 通過(guò)new URL來(lái)導(dǎo)入圖片

      我打算將所有組件都重構(gòu)為setup形式,因此直接使用import方式來(lái)導(dǎo)入圖片可以保持組件的一致性,可以大大提升可讀性。

      我們寫(xiě)個(gè)簡(jiǎn)單的demo來(lái)嘗試下,如下所示:

      已經(jīng)可以正確解析出圖片的路徑了。

      注意:本文不會(huì)過(guò)多講解setup的語(yǔ)法,對(duì)此不了解的開(kāi)發(fā)者請(qǐng)移步:?jiǎn)挝募M件 – script setup

      new URL方式可以用來(lái)引入一個(gè)動(dòng)態(tài)資源,例如:你有一份json配置文件,里面描述了圖片的文件名,這些圖片是放在項(xiàng)目中的,他們的訪問(wèn)前綴都一樣,此時(shí)你就可以通過(guò)遍歷json文件通過(guò)此方式來(lái)引入這些圖片。

      vue相關(guān)模塊不存在

      我試圖從vue的包中導(dǎo)入shallowRef時(shí),編輯器報(bào)錯(cuò): TS2305: Module ‘xxx’ has no exported member ‘shallowRef’. 。

      解決方案

      經(jīng)過(guò)一番排查后,是因?yàn)轫?xiàng)目typescript版本是3.x,跟3.2版本的vue不兼容,需要將其升級(jí)至4.x版本。

      打開(kāi)package.json文件,作出如下所示的修改,重新執(zhí)行yarn install命令即可。

      { “devDependencies”: {- “typescript”: “~3.9.3”,+ “typescript”: “~4.7.4”, }}

      setup中的變量警告未被使用

      當(dāng)我在setup中聲明了一個(gè)函數(shù)或者導(dǎo)入了一個(gè)文件,在template中已經(jīng)使用了,但是他卻報(bào)錯(cuò)ESLint: ‘xx’ is assigned a value but never used.(@typescript-eslint/no-unused-vars)

      解決方案

      在 eslint-plugin-vue 插件的Issues中看到有人遇到了跟我同樣的問(wèn)題,在v9.0.0: regression in unused variables in script setup中我找到了解決方案。

      我們需要升級(jí)下@vue/eslint-config-typescript和eslint-plugin-vue的版本號(hào),如下所示:

      { “devDependencies”: { “@vue/eslint-config-typescript”: “^11.0.0”, “eslint-plugin-vue”: “^9.0.0” }}

      隨后在eslint的配置文件中,添加parser屬性,重新執(zhí)行yarn install命令即可。

      module.exports = {+ parser: ‘vue-eslint-parser’}

      模塊隔離

      Vite 使用 esbuild 來(lái)轉(zhuǎn)譯 TypeScript,并受限于單文件轉(zhuǎn)譯的限制,因此需要在ts的配置文件中將isolatedModules屬性設(shè)置為true。

      { “compilerOptions”: { “isolatedModules”: true }}

      process不存在

      在路由配置文件中,我們需要從process中獲取BASE_URL,此時(shí)編輯器報(bào)錯(cuò): TS2591: Cannot find name ‘process’. Do you need to install type definitions for node? Try npm i –save-dev @types/node and then add ‘node’ to the types field in your tsconfig.

      解決方案

      由于vite中已經(jīng)沒(méi)有process了,需要用import.meta來(lái)代替,那么上述的路由配置文件就應(yīng)該改為:

      const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), // 地址欄不帶# routes});

      無(wú)法導(dǎo)入json文件

      在表情面板模塊,我將每個(gè)表情都放入了json文件中。在vite中引入文件需要使用import,改了寫(xiě)法后,發(fā)現(xiàn)它報(bào)錯(cuò):Cannot find module ‘xx.json’. Consider using ‘–resolveJsonModule’ to import module with ‘.json’ extension.

      解決方案

      我們需要在ts的配置文件中添加resolveJsonModule屬性,如下所示:

      { “compilerOptions”: {+ “resolveJsonModule”: true }}

      使用vite提供的對(duì)象

      當(dāng)我想使用vite所提供的glob屬性時(shí),發(fā)現(xiàn)編輯器報(bào)錯(cuò): TS2339: Property ‘glob’ does not exist on type ‘ImportMeta’.

      解決方案也很簡(jiǎn)單,我們只需要在ts的配置文件中添加vite/client即可,如下所示:

      { “compilerOptions”: { “types”: [+ “vite/client” ] }}

      獲取全局屬性

      當(dāng)我們使用一些第三方庫(kù)的時(shí)候它會(huì)在globalProperties掛載一些方法,當(dāng)在ts+setup環(huán)境下使用時(shí),會(huì)出現(xiàn)類型無(wú)法推導(dǎo)問(wèn)題,如下所示:

      第三方庫(kù)提供了一個(gè)$connect方法

      我們通過(guò)proxy來(lái)訪問(wèn)

      他會(huì)出現(xiàn)報(bào)錯(cuò): TS2339: Property ‘xx’ does not exist on type ‘ComponentPublicInstance{}, {}, {}, {}, {}, {}, {}, {}, false, ComponentOptionsBase >’.

      解決方案

      我們可以在type目錄下新建一個(gè)global文件夾,在這里存放一些我們擴(kuò)展出來(lái)的全局方法。

      如下所示,我們:

      • 創(chuàng)建了一個(gè)useCurrentInstance方法
      • 將globalProperties屬性暴露出去

      import { ComponentInternalInstance, getCurrentInstance } from “vue”;export default function useCurrentInstance() { const { appContext } = getCurrentInstance() as ComponentInternalInstance; const proxy = appContext.config.globalProperties; return { proxy };}

      我們?cè)诮M件中使用暴露出來(lái)的proxy即可,如下所示:

      無(wú)法識(shí)別NodeJS類型

      我們?cè)诮osetinterval和setTimeout指定類型時(shí),會(huì)用到NodeJS模塊,會(huì)出現(xiàn)報(bào)錯(cuò):ESLint: ‘NodeJS’ is not defined.(no-undef)。

      這個(gè)問(wèn)題的解決方案是:打開(kāi)eslint的配置文件在globals對(duì)象中添加NodeJS選項(xiàng),如下所示:

      { globals: { NodeJS: true }}

      除了將類型聲明為NodeJS.Timeout外,我們還可以將其聲明為number類型,但是需要攜帶window前綴(window.setinterval/window.setTimeout)

      管理靜態(tài)資源

      當(dāng)我們?cè)诮M件中使用import導(dǎo)入很多靜態(tài)資源時(shí),組件看起來(lái)會(huì)很雜亂。此時(shí)我們可以將其按照功能類型進(jìn)行拆分。我的做法如下:

      • 在src下創(chuàng)建resource文件夾
      • 根據(jù)功能類型創(chuàng)建ts文件,將其導(dǎo)出

      import defaultAvatar from “@/assets/img/login/LoginWindow_BigDefaultHeadImage@2x.png”;import defaultLoginBtnIcon from “@/assets/img/login/icon-enter-undo@2x.png”;import //imgq8.q578.com/ef/0816/df977c06ae2a4ebf.jpg from “@/assets/img/login/icon-enter-undo@2x.png”;import loginBtnHover from “@/assets/img/login/icon-enter-hover@2x.png”;import loginBtnDown from “@/assets/img/login/icon-enter-down@2x.png”;export { defaultAvatar, defaultLoginBtnIcon, //imgq8.q578.com/ef/0816/df977c06ae2a4ebf.jpg, loginBtnHover, loginBtnDown};

      分離模版與邏輯代碼

      我的項(xiàng)目中有一個(gè)很復(fù)雜的組件,有上千行代碼,去年我用CompositionAPI優(yōu)化了一版,將組件中所有的方法都拆分成了一個(gè)個(gè)獨(dú)立的ts文件,做到了邏輯代碼與模版代碼分離,模版需要什么方法我就通過(guò)import導(dǎo)入進(jìn)來(lái),最后return給模版。

      在拆分出來(lái)的文件中,是沒(méi)有辦法訪問(wèn)vue提供的一些內(nèi)置屬性的,比如:defineProps、defineEmits、getCurrentInstance。因此我想了一個(gè)奇妙的方法:將這些無(wú)法訪問(wèn)的屬性都存起來(lái)。具體的做法請(qǐng)移步我另一篇文章:使用Vue3的CompositionAPI來(lái)優(yōu)化代碼量-創(chuàng)建InitData.ts文件

      適配方案

      vue3.2的setup語(yǔ)法糖支持import進(jìn)來(lái)的方法都能在模版中直接使用,那我們的組件又可以精簡(jiǎn)下了,我花了億點(diǎn)點(diǎn)時(shí)間對(duì)其進(jìn)行了適配。

      之前我們想獲取組件的emit需要從context中拿,props聲明并從setup函數(shù)的參數(shù)中獲取,如下所示:

      現(xiàn)在我們就不用這么麻煩了,直接通過(guò)defineProps、defineEmits獲取即可,如下所示:

      鄭重聲明:本文內(nèi)容及圖片均整理自互聯(lián)網(wǎng),不代表本站立場(chǎng),版權(quán)歸原作者所有,如有侵權(quán)請(qǐng)聯(lián)系管理員(admin#wlmqw.com)刪除。
    上一篇 2022年8月16日 12:20
    下一篇 2022年8月16日 12:20

    相關(guān)推薦

    聯(lián)系我們

    聯(lián)系郵箱:admin#wlmqw.com
    工作時(shí)間:周一至周五,10:30-18:30,節(jié)假日休息