欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

vue?demi支持sfc方式的vue2vue3通用庫開發(fā)詳解

 更新時間:2022年08月23日 16:08:05   作者:sakibcc  
這篇文章主要為大家介紹了vue?demi支持sfc方式的vue2vue3通用庫開發(fā)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

背景

隨著vue3的逐漸成熟,公司項目逐漸會存在vue2和vue3項目共存的情況,兼容vue2和vue3的公共組件開發(fā)能讓老項目較好地過渡到vue3。研究了vue-demi的源碼和demo,發(fā)現(xiàn)vue-demi只是簡單地根據(jù)vue版本生成對應(yīng)的類似中間件的東西,而且render函數(shù)也只是做了簡單的中轉(zhuǎn)處理;

國外大佬寫了一個h-demi解決了vue2/vue3的render函數(shù)attrs屬性的問題,這里我就直接貼issue鏈接,不做過多說明了: github.com/vueuse/vue-…

雖然vue-demi沒有提供sfc的兼容方案,但是其實仔細(xì)想一下,sfc的解析處理也不應(yīng)該是由vue-demi來解決,應(yīng)該是交給打包工具將template轉(zhuǎn)成render,而vue-demi只需要關(guān)注composition-api就行;于是往著這個思路,花了幾天時間研究一下vue2.6、vue2.7和vue3的sfc-compiler,得到以下開發(fā)方案。

技術(shù)要點(diǎn)

vue-demi

查看源碼可以發(fā)現(xiàn),vue-demi的工作是通過postinstall和 npx vue-demi-fix指令,判斷當(dāng)前項目安裝的vue版本,然后將對應(yīng)版本的插件復(fù)制到lib的根目錄,其插件的功能就是抹平vue2和vue3版本使用composition-api時的差異;

<=2.6: exports from vue + @vue/composition-api with plugin auto installing.

2.7: exports from vue (Composition API is built-in in Vue 2.7).

>=3.0: exports from vue, with polyfill of Vue 2's set and del API.

sfc compiler

在日常開發(fā)中寫的vue template,實際上最后是通過sfc-compiler轉(zhuǎn)成render函數(shù)輸出的,而vue2和vue3的sfc-compiler是互不兼容的。尤大大已經(jīng)提供了vue2.6.x,vue2.7和vue3的compiler,其實我們只需要在打包工具寫判斷不同的vue版本使用不同的compiler邏輯即可,本文是基于vite開發(fā),以下對應(yīng)的打包插件:

  • vue2.6: vite-plugin-vue2@2.6.14 + vue-template-compiler@2.6.14
  • vue2.7: vite-plugin-vue2@2.7.9 + vue-template-compiler@2.7.9; 或者@vitejs/plugin-vue2 + @vue/compiler-sfc
  • vue3: @vitejs/plugin-vue + @vue/compiler-sfc

實現(xiàn)方式

以下實現(xiàn)方式均是基于vite開發(fā),換成webpack和rollup原理上也是替換對應(yīng)的插件即可。

vue2.6 + vue3 + vite + vue-demi

以vue2.6為主包,開發(fā)vue2/vue3組件,該方式能做到通過一個package.json的scripts同時調(diào)試和打包vue2、vue3環(huán)境,以下講一下重點(diǎn);

package.json

package.json中的vue包是固定了2.6.14版本,這里要注意vue-template-compiler要和vue的版本對齊;

scripts中的switch:2 指令沒有按照文檔說的使用npx vue-demi-switch,是因為在實際調(diào)試過程中,由于vite是會緩存依賴的,dev調(diào)試時vue-demi-switch會出現(xiàn)一些莫名其妙的問題,具體原因我還沒搞明白,所以就改成用npx vue-demi-fix。

//package.json部分片段
 "main": "./lib/vue-demi-sfc-component.umd.cjs",
 "exports": {
    ".": {
      "import": "./lib/vue-demi-sfc-component.js",
      "require": "./lib/vue-demi-sfc-component.umd.cjs"
    }
 },
 "scripts": {
    "postinstall": "node ./scripts/postinstall.mjs",
    "dev": "vite",
    "dev:3": "npm run switch:3 && vite --force",
    "dev:2": "npm run switch:2 && vite",
    "switch:2": "npx vue-demi-fix",
    "switch:3": "npx vue-demi-switch 3 vue3",
    "build:3": "npm run switch:3 && vue-tsc --noEmit && vite build",
    "build:2": "npm run switch:2 && vue-tsc --noEmit && vite build",
    "build": "rimraf lib && npm run build:2 && npm run build:3",
    "preview": "vite preview",
    "lint:fix": "eslint . --ext .js,.ts,.vue --fix",
    "prepare": "husky install",
    "pub": "npm publish --access=public"
  },
  "dependencies": {
    "@vue/composition-api": "^1.7.0",
    "vue-demi": "^0.13.8"
  },
  "peerDependencies": {
    "@vue/composition-api": "^1.7.0",
    "vue": "^2.0.0 || >=3.0.0"
  },
  "peerDependenciesMeta": {
    "@vue/composition-api": {
      "optional": true
    }
  },
  "peerDependencies": {
    "@vue/composition-api": "^1.7.0",
    "vue": "^2.0.0 || >=3.0.0"
  },
  "peerDependenciesMeta": {
    "@vue/composition-api": {
      "optional": true
    }
  },
  "devDependencies": {
    // ...其他依賴,這里就不復(fù)制了
    "@vitejs/plugin-vue": "^3.0.3",
    "vite": "^3.0.7",
    "vite-plugin-vue2": "^2.0.2",
    "vue": "2.6.14",
    "vue-eslint-parser": "^9.0.3",
    "vue-template-compiler": "2.6.14",
    "vue-tsc": "^0.39.5",
    "vue2": "npm:vue@2.6.14",
    "vue3": "npm:vue@^3.2.36"
  }

vite.config.ts

import { defineConfig } from 'vite'
import { createVuePlugin } from 'vite-plugin-vue2'
import * as compiler from '@vue/compiler-sfc'
import vue3 from '@vitejs/plugin-vue'
import path from 'path'
import { getLibDir } from './scripts/utils.mjs'
import { isVue2, version } from 'vue-demi'
console.log({ version })
const resolve = (str: string) => {
  return path.resolve(__dirname, str)
}
// https://vitejs.dev/config/
export default defineConfig({
  resolve: {
    alias: {
      '@': resolve('src'),
      vue: isVue2 ? resolve('/node_modules/vue2') : resolve('/node_modules/vue3')
    }
  },
  build: {
    lib: {
      entry: resolve('./src/components/index.ts'),
      name: 'vueDemiSfcComponent',
      fileName: 'vue-demi-sfc-component'
    },
    cssTarget: 'chrome61',
    rollupOptions: {
      external: ['vue-demi', 'vue'],
      output: {
        dir: getLibDir(version),
        globals: {
          vue: 'Vue',
          'vue-demi': 'VueDemi'
        }
      }
    }
  },
  optimizeDeps: {
    exclude: ['vue-demi']
  },
  plugins: [
    isVue2
      ? createVuePlugin()
      : vue3({
          compiler: compiler
        })
  ]
})

這個文件有幾個關(guān)鍵邏輯:

1、使用vue-demi的isVue2來判斷當(dāng)前打包環(huán)境

import { isVue2, version } from 'vue-demi'

2、alias要根據(jù)環(huán)境切換地址

 alias: {
  '@': resolve('src'),
  vue: isVue2 ? resolve('/node_modules/vue2') : resolve('/node_modules/vue3')
}

3、在以vue2.6為主包的時候,如果直接使用@vitejs/plugin-vue, 打包時會報錯

error when starting dev server:

Error: Failed to resolve vue/compiler-sfc.

@vitejs/plugin-vue requires vue (>=3.2.25) to be present in the dependency tree.

這是因為@vitejs/plugin-vue源碼中是直接找vue/compiler-sfc目錄的,如果以vue2為主包,這個時候nod_modules/vue是vue2的目錄結(jié)構(gòu),并沒有vue/compiler-sfc;

function resolveCompiler(root) {
  const compiler = tryRequire("vue/compiler-sfc", root) || tryRequire("vue/compiler-sfc");
  if (!compiler) {
    throw new Error(
      `Failed to resolve vue/compiler-sfc.
@vitejs/plugin-vue requires vue (>=3.2.25) to be present in the dependency tree.`
    );
  }
  return compiler;
}

所以就去尋找一下@vitejs/plugin-vue的options

interface Options {
    include?: string | RegExp | (string | RegExp)[];
    exclude?: string | RegExp | (string | RegExp)[];
    isProduction?: boolean;
    script?: Partial<Pick<SFCScriptCompileOptions, 'babelParserPlugins'>>;
    template?: Partial<Pick<SFCTemplateCompileOptions, 'compiler' | 'compilerOptions' | 'preprocessOptions' | 'preprocessCustomRequire' | 'transformAssetUrls'>>;
    style?: Partial<Pick<SFCStyleCompileOptions, 'trim'>>;
    /**
     * Transform Vue SFCs into custom elements.
     * - `true`: all `*.vue` imports are converted into custom elements
     * - `string | RegExp`: matched files are converted into custom elements
     *
     * @default /\.ce\.vue$/
     */
    customElement?: boolean | string | RegExp | (string | RegExp)[];
    /**
     * Enable Vue reactivity transform (experimental).
     * https://github.com/vuejs/core/tree/master/packages/reactivity-transform
     * - `true`: transform will be enabled for all vue,js(x),ts(x) files except
     *           those inside node_modules
     * - `string | RegExp`: apply to vue + only matched files (will include
     *                      node_modules, so specify directories in necessary)
     * - `false`: disable in all cases
     *
     * @default false
     */
    reactivityTransform?: boolean | string | RegExp | (string | RegExp)[];
    /**
     * Use custom compiler-sfc instance. Can be used to force a specific version.
     */
    compiler?: typeof _compiler;
}

發(fā)現(xiàn)option中是有自定義compiler-sfc的參數(shù),于是就得到以下方案:

// vite.config.ts
import * as compiler from '@vue/compiler-sfc'
export default defineConfig({
// ... 
plugins: [
    isVue2
      ? createVuePlugin()
      : vue3({
          compiler: compiler
        })
  ]
})

main.ts

main.ts需要判斷isVue2后,區(qū)分vue2和vue3的依賴

import { isVue2 } from 'vue-demi'
import { createApp } from 'vue3'
import Vue2 from 'vue2'
import './style.css'
import App from './App.vue'
if (isVue2) {
  const app = new Vue2({
    render: (h) => h(App)
  })
  app.$mount('#app')
} else {
  const app = createApp(App)
  app.mount('#app')
}

postinstall

這里是模仿vue-demi的原理,在安裝時利用postinstall鉤子執(zhí)行node腳本,復(fù)制lib中的v2/v3目錄,具體可直接看文章最后的項目鏈接;這里有一個地方要注意,由于我是使用vite + ts 構(gòu)建的項目,package.json中的"type": "module"需要我把所有js改成mjs文件,這個時候,其他項目安裝這個項目時,會找不到 __dirname,因此utils.mjs加了以下邏輯。

import { fileURLToPath } from 'url'
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)

vue2.7 + vue3 + vite + vue-demi + yarn workspaces

以vue2.7為主包開發(fā)時,沒辦法像vue2.6可以在一個package.json項目下調(diào)試和打包,主要是因為vue2.7的代碼方式已經(jīng)是monorepo項目,因此在安裝vue2.7的時候,會重新下載@vue/compuler-sfc的2.7.x版本。

所以沒辦法直接使用@vue/compiler-sfc 包作為vue3的compiler;

那么我們就要換一個思路,做node_modules隔離,而node_modules隔離的方案現(xiàn)在主流的就是yarn workspaces、lerna和pnpm,這里我就以yarn workspaces來簡單講一下思路;

(ps: 該方式我并沒有上傳到github)

開啟yarn workspaces之后,新建packages文件夾

然后再packages下分別新建v2和v3目錄,這兩個目錄存放對應(yīng)vue2和vue3的package.json和vite.config.ts

// v2/package.json
"scripts": {
    "dev": "vite",
    "build": "rimraf lib/v2 && vue-tsc --noEmit && vite build",
    "preview": "vite preview",
    "lint:fix": "eslint . --ext .js,.ts,.vue --fix",
    "prepare": "husky install",
    "pub": "npm publish --access=public"
},
"devDependencies": {
    "@vitejs/plugin-vue2": "^2.7.9",
    "vite": "^3.0.7",
    "vite-plugin-vue2": "^2.0.2",
    "vue": "2.7.9",
    "vue-eslint-parser": "^9.0.3",
    "vue-template-compiler": "2.7.9",
    "vue-tsc": "^0.39.5",
    "vue2": "npm:vue@2.7.9",
    "vue3": "npm:vue@^3.2.36"
  }
 // v3/package.json
 "scripts": {
    "dev": "vite",
    "build": "rimraf lib/v3 && vue-tsc --noEmit && vite build",
    "preview": "vite preview",
    "lint:fix": "eslint . --ext .js,.ts,.vue --fix",
    "prepare": "husky install",
    "pub": "npm publish --access=public"
},
"devDependencies": {
    "@vitejs/plugin-vue": "^3.0.3",
    "vite": "^3.0.7",
    "vite-plugin-vue2": "^2.0.2",
    "vue": "3.2.26",
    "vue-eslint-parser": "^9.0.3",
    "vue-template-compiler": "2.6.14",
    "vue-tsc": "^0.39.5",
    "vue2": "npm:vue@2.6.14",
    "vue3": "npm:vue@^3.2.26"
  }

vite.config.ts的區(qū)別主要是 rollupOptions.output.dir,和對應(yīng)的plugin,然后alias不需要再指定vue路徑,main.ts也不需要區(qū)分vue2和vue3的依賴;

// v2/vite.config.ts
import { defineConfig } from 'vite'
import { createVuePlugin } from 'vite-plugin-vue2'
// or import vue2 from '@vitejs/plugin-vue2'
import path from 'path'
const resolve = (str: string) => {
  return path.resolve(__dirname, str)
}
// https://vitejs.dev/config/
export default defineConfig({
  // ...
  resolve: {
    alias: {
      '@': resolve('src'),
    }
  },
  build: {
    // ...
    rollupOptions: {
      external: ['vue-demi', 'vue'],
      output: {
        dir: resolve('../../lib/v2'), // 區(qū)別在這
        globals: {
          vue: 'Vue',
          'vue-demi': 'VueDemi'
        }
      }
    }
  },
  optimizeDeps: {
    exclude: ['vue-demi']
  },
  plugins: [createVuePlugin()] // or vue2()
})
// v3/vite.config.ts
import { defineConfig } from 'vite'
import vue3 from '@vitejs/plugin-vue'
import path from 'path'
const resolve = (str: string) => {
  return path.resolve(__dirname, str)
}
// https://vitejs.dev/config/
export default defineConfig({
  // ...
  resolve: {
    alias: {
      '@': resolve('src'),
    }
  },
  build: {
    rollupOptions: {
      external: ['vue-demi', 'vue'],
      output: {
        dir: resolve('../../lib/v3'), // 區(qū)別在這
        globals: {
          vue: 'Vue',
          'vue-demi': 'VueDemi'
        }
      }
    }
  },
  optimizeDeps: {
    exclude: ['vue-demi']
  },
  plugins: [vue3()]
})

main.ts

// main.ts
import { createApp } from 'vue-demi'
import './style.css'
const app = createApp(App)
app.mount('#app')

整體目錄結(jié)構(gòu)如下,最后通過node腳本去同時構(gòu)建v2和v3即可。

目前沒找到vue3為主包的開發(fā)方式

文章看到這里,大概能知道整個方案其實是基于vue-demi處理composition-api和使用vue3的自定義compiler處理分別打包vue2、vue3;而vite-plugin-vue2是沒有對應(yīng)自定義compiler的options,并且在vue3為主包的情況下,會報vue-template-compiler與vue版本不一致的錯誤;而@vitejs/plugin-vue2存在跟vue3沖突的情況;

目前如果要基于vue3為主包的方式開發(fā),我想到如下2個思路,待后續(xù)有時間再去驗證:

  • vite-plugin-vue2增加自定義compiler選項
  • 開發(fā)rollup插件,支持修改vue-template-compiler在讀取require(vue)時,重定向到"vue2": "npm:vue@2.6.14"對應(yīng)的路徑

注意點(diǎn)

1、@vue/composition-api重復(fù)引用問題

由于vue-demi在v2.6的場景下,會自動install @vue/composition-api,,如果項目自身也在需要在入口時注冊@vue/composition-api,會出現(xiàn)多次注冊@vue/composition-api實例的情況,導(dǎo)致出setup相關(guān)的報錯,這時需要在項目的alias加上以下代碼:

alias: {
   '@vue/compostion-api': resolve('./node_modules/@vue/composition-api')
},

2、由于要兼容vue2,vue3的 setup sfc語法糖不兼容

這一點(diǎn)無法解決,寫組件template的時候,還是只能用vue2的template寫法,包括template還是需要有唯一的跟節(jié)點(diǎn);

最后

寫到最后,其實我發(fā)現(xiàn)去寫兼容vue2和vue3的template代碼,并不能完全解決vue2到vue3過渡的問題。希望vue3社區(qū)以后越來越完善~

貼上項目地址(vue2.6 + vue3 + vite + vue-demi):vue-demi-sfc-component

以上就是vue demi支持sfc方式的vue2vue3通用庫開發(fā)詳解的詳細(xì)內(nèi)容,更多關(guān)于vue demi支持sfc通用庫的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • vue基于Teleport實現(xiàn)Modal組件

    vue基于Teleport實現(xiàn)Modal組件

    Teleport 提供了一種干凈的方法,允許我們控制在 DOM 中哪個父節(jié)點(diǎn)下渲染了 HTML,而不必求助于全局狀態(tài)或?qū)⑵洳鸱譃閮蓚€組件。
    2021-05-05
  • vuex vue簡單使用知識點(diǎn)總結(jié)

    vuex vue簡單使用知識點(diǎn)總結(jié)

    在本篇文章里小編給大家整理了關(guān)于vuex vue簡單使用知識點(diǎn)總結(jié),有需要的朋友們可以參考下。
    2019-08-08
  • vue2.0實現(xiàn)導(dǎo)航菜單切換效果

    vue2.0實現(xiàn)導(dǎo)航菜單切換效果

    這篇文章主要為大家詳細(xì)介紹了vue2.0實現(xiàn)導(dǎo)航菜單切換效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-05-05
  • 解決Vue3報錯:Property?“xxx“?was?accessed?during?render?but?is?not?defined?on?instance.

    解決Vue3報錯:Property?“xxx“?was?accessed?during?render?but

    這篇文章主要給大家介紹了關(guān)于解決Vue3報錯:Property?“xxx“?was?accessed?during?render?but?is?not?defined?on?instance.的相關(guān)資料,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2023-01-01
  • vue?+elementui?項目登錄通過不同賬號切換側(cè)邊欄菜單的顏色

    vue?+elementui?項目登錄通過不同賬號切換側(cè)邊欄菜單的顏色

    這篇文章主要介紹了vue?+elementui?項目登錄通過不同賬號切換側(cè)邊欄菜單的顏色,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2024-01-01
  • vueJS簡單的點(diǎn)擊顯示與隱藏的效果【實現(xiàn)代碼】

    vueJS簡單的點(diǎn)擊顯示與隱藏的效果【實現(xiàn)代碼】

    下面小編就為大家?guī)硪黄獀ueJS簡單的點(diǎn)擊顯示與隱藏的效果【實現(xiàn)代碼】。小編覺得挺不錯的,現(xiàn)在分享給大家,一起跟隨小編過來看看吧
    2016-05-05
  • vue實現(xiàn)自定義多選與單選的答題功能

    vue實現(xiàn)自定義多選與單選的答題功能

    這篇文章主要介紹了使用vue實現(xiàn)自定義多選與單選的答題功能,vue組件中在表單方面提供了一個v-model指令,非常好用,具體實現(xiàn)過程大家跟隨腳本之家小編一起看看吧
    2018-07-07
  • vue路由緩存的幾種實現(xiàn)方式小結(jié)

    vue路由緩存的幾種實現(xiàn)方式小結(jié)

    這篇文章主要介紹了vue路由緩存的幾種實現(xiàn)方式,結(jié)合實例形式詳細(xì)分析了vue.js路由緩存常見實現(xiàn)方式、使用技巧與操作注意事項,需要的朋友可以參考下
    2020-02-02
  • vue移動端自適應(yīng)適配問題詳解

    vue移動端自適應(yīng)適配問題詳解

    這篇文章主要介紹了vue移動端自適應(yīng)適配問題,本文通過實例代碼詳解給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2021-04-04
  • Vue router-view和router-link的實現(xiàn)原理

    Vue router-view和router-link的實現(xiàn)原理

    這篇文章主要介紹了Vue router-view和router-link的實現(xiàn)原理,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03

最新評論