Cómo Vue Demi permite que su biblioteca admita tanto Vue2 como Vue3

¿Qué es Vue Demi?

Si desea desarrollar una biblioteca que admita ambos Vue2, puede pensar en las siguientes dos formas:Vue3

1. Crea dos ramas para apoyar Vue2yVue3

2. Solo use Vue2y Vue3admita ambosAPI

Estos dos métodos tienen desventajas. El primero es muy problemático y el segundo no puede usar la Vue3composición recién agregada API. De hecho, la Vue2.7+versión actual tiene soporte integrado para la composición API, Vue2.6y la versión anterior también puede usar @vue/composition. -api complemento para admitirlo, por lo que es completamente posible escribir solo un conjunto de código para admitir Vue2y al mismo tiempo 3. Aun así, en el desarrollo real, la misma APIfuente puede importarse en diferentes versiones. Por ejemplo ref, los métodos pueden importarse Vue2.7+directamente vuede , pero Vue2.6-solo pueden importarse @vue/composition-apide , lo que inevitablemente implicará el juicio de versión. Vue Demi se utiliza para resolver esto. problema Es muy simple de usar, solo Vue Demiexporte lo que necesita de él:

import {
    
     ref, reactive, defineComponent } from 'vue-demi'

Vue-demiJuzgará qué versión usar de acuerdo a tu proyecto Vue, en concreto su estrategia es la siguiente:

  • <=2.6: derivado de Vuey@vue/composition-api
  • 2.7: derivado Vuede (composición APIincorporada Vue 2.7)
  • >=3.0: Vuederivado y devuelto la suma de las polyfilldos versionesVue 2setdel API

A continuación, echemos un vistazo a cómo se implementa desde la perspectiva del código fuente.

Fundamental

Cuando lo instalemos npm i vue-demien nuestro proyecto, automáticamente ejecutará un script:

{
    
    
    "scripts": {
    
    
        "postinstall": "node ./scripts/postinstall.js"
    }
}
// postinstall.js
const {
    
     switchVersion, loadModule } = require('./utils')

const Vue = loadModule('vue')

if (!Vue || typeof Vue.version !== 'string') {
    
    
  console.warn('[vue-demi] Vue is not found. Please run "npm install vue" to install.')
}
else if (Vue.version.startsWith('2.7.')) {
    
    
  switchVersion(2.7)
}
else if (Vue.version.startsWith('2.')) {
    
    
  switchVersion(2)
}
else if (Vue.version.startsWith('3.')) {
    
    
  switchVersion(3)
}
else {
    
    
  console.warn(`[vue-demi] Vue version v${
      
      Vue.version} is not suppported.`)
}

Importe los instalados en nuestro proyecto vuey luego llame a los métodos según las diferentes versiones switchVersion.

Primero echemos un vistazo al loadModulemétodo:

function loadModule(name) {
    
    
  try {
    
    
    return require(name)
  } catch (e) {
    
    
    return undefined
  }
}

Es muy simple, solo está empaquetado requirepara evitar que los errores bloqueen el código.

Luego mira switchVersionel método:

function switchVersion(version, vue) {
    
    
  copy('index.cjs', version, vue)
  copy('index.mjs', version, vue)
  copy('index.d.ts', version, vue)

  if (version === 2)
    updateVue2API()
}

Después de ejecutar copyel método, podemos saber aproximadamente que es un archivo de copia del nombre de la función, y los tipos de los tres archivos también son muy claros, que son el commonjsarchivo de versión, ESMel archivo de versión y TSel archivo de definición de tipo.

Además, el método Vue2.6se ejecuta para la siguiente versión updateVue2API.

updateVue2APIVeamos el método más tarde, echemos un vistazo al copymétodo primero:

const dir = path.resolve(__dirname, '..', 'lib')

function copy(name, version, vue) {
    
    
  vue = vue || 'vue'
  const src = path.join(dir, `v${
      
      version}`, name)
  const dest = path.join(dir, name)
  let content = fs.readFileSync(src, 'utf-8')
  content = content.replace(/'vue'/g, `'${
      
      vue}'`)
  try {
    
    
    fs.unlinkSync(dest)
  } catch (error) {
    
     }
  fs.writeFileSync(dest, content, 'utf-8')
}

De hecho, es para copiar los tres archivos anteriores del directorio de diferentes versiones al directorio externo, que también admite el vuenombre de reemplazo, que vuedebe usarse cuando configura un alias para él.

En este punto, Vue Demise completa la ejecución automática después de la instalación. De hecho, según Vuela versión instalada en el proyecto de usuario, los archivos se copian de los tres directorios correspondientes como Vue Demiel archivo de entrada del paquete, y Vue Demise admiten tres sintaxis de módulo:

{
    
    
    "main": "lib/index.cjs",
    "jsdelivr": "lib/index.iife.js",
    "unpkg": "lib/index.iife.js",
    "module": "lib/index.mjs",
    "types": "lib/index.d.ts"
}

La entrada predeterminada es un archivo commonjsde módulo , se pueden usar archivos cjsadmitidos y también se proporcionan archivos del tipo que se pueden usar directamente en el navegador .ESMmjsiife

VueA continuación, echemos un vistazo a lo que se ha hecho para las tres versiones .

versión v2

Vue2.6Las versiones solo tienen una exportación predeterminada:

Solo miramos mjsel documento, y cjsaquellos que estén interesados ​​​​pueden leerlo por sí mismos:

import Vue from 'vue'
import VueCompositionAPI from '@vue/composition-api/dist/vue-composition-api.mjs'

function install(_vue) {
    
    
  _vue = _vue || Vue
  if (_vue && !_vue['__composition_api_installed__'])
    _vue.use(VueCompositionAPI)
}

install(Vue)
// ...

Importaciones Vuey VueCompositionAPIcomplementos, y llama automáticamente Vue.useal complemento de instalación del método.

continuar:

// ...
var isVue2 = true
var isVue3 = false
var Vue2 = Vue
var version = Vue.version

export {
    
    
	  isVue2,
    isVue3,
    Vue,
    Vue2,
    version,
    install,
}

/**VCA-EXPORTS**/
export * from '@vue/composition-api/dist/vue-composition-api.mjs'
/**VCA-EXPORTS**/

isVue2Primero, dos variables y se exportan isVue3para facilitar que nuestro código de biblioteca juzgue el entorno.

Entonces en Vueel mismo momento de exportar también se Vue2exporta nuevamente a través del nombre ¿Por qué es esto? De hecho es porque todos los objetos Vue2están APImontados en Vueel objeto, por ejemplo si quiero hacer algunas configuraciones globales entonces Solo puedo hacer esto:

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

if (isVue2) {
    
    
  Vue.config.xxx
}

De esta manera, Vue2no hay problema en el entorno, pero cuando nuestra biblioteca está en Vue3el entorno, en realidad no hay necesidad de importar Vueobjetos, porque no es necesario, pero la herramienta de compilación no lo sabe, por lo que empaquetará Vue3todos los código en él, pero Vue3una gran cantidad de contenido que no usamos es innecesario, pero debido a que importamos todos APIlos Vueobjetos, no podemos eliminarlos, por lo que podemos Vue2exportar un objeto por separado para la versión Vue2, podemos hacer esto:

import {
    
     Vue2 } from 'vue-demi'

if (Vue2) {
    
    
  Vue2.config.xxx
}

Luego verás que está Vue3en la exportación de , por lo que este problema se puede solucionar.Vue2undefined

Luego exportó Vuela versión y installel método, lo que significa que puede instalar manualmente VueCompositionAPIel complemento.

Luego lo VueCompositionAPIproporciona el export plug-in API, es decir combinado , pero pueden ver que hay dos líneas de comentarios antes y después, recuerden que el método APImencionado anteriormente también ejecuta el método para la versión, ahora echemos un vistazo en lo que hace:switchVersionVue2updateVue2API

function updateVue2API() {
    
    
  const ignoreList = ['version', 'default']
  // 检查是否安装了composition-api
  const VCA = loadModule('@vue/composition-api')
  if (!VCA) {
    
    
    console.warn('[vue-demi] Composition API plugin is not found. Please run "npm install @vue/composition-api" to install.')
    return
  }
  // 获取除了version、default之外的其他所有导出
  const exports = Object.keys(VCA).filter(i => !ignoreList.includes(i))
  // 读取ESM语法的入口文件
  const esmPath = path.join(dir, 'index.mjs')
  let content = fs.readFileSync(esmPath, 'utf-8')
  // 将export * 替换成 export { xxx }的形式
  content = content.replace(
    /\/\*\*VCA-EXPORTS\*\*\/[\s\S]+\/\*\*VCA-EXPORTS\*\*\//m,
`/**VCA-EXPORTS**/
export { ${
      
      exports.join(', ')} } from '@vue/composition-api/dist/vue-composition-api.mjs'
/**VCA-EXPORTS**/`
    )
  // 重新写入文件
  fs.writeFileSync(esmPath, content, 'utf-8')
}

Lo principal que debe hacer es verificar si está instalado @vue/composition-apiy luego filtrar todo el contenido exportado @vue/composition-apiexcepto versiony default, finalmente:

export * from '@vue/composition-api/dist/vue-composition-api.mjs'

se reescribe como:

export {
    
     EffectScope, ... } from '@vue/composition-api/dist/vue-composition-api.mjs'

¿Por qué filtrar versiony default? versionDebido a que ya ha sido exportado Vue, versionentrará en conflicto. No es necesario en absoluto, defaultes decir, la exportación predeterminada. @vue/composition-apiLa exportación predeterminada es en realidad un objeto que contiene sus installmétodos. Como ha visto antes, puede importar por defecto @vue/composition-apiLuego Vue.useinstálelo, esto en realidad no necesita ser Vue Demiexportado, de lo contrario se verá muy extraño como el siguiente:

import VueCompositionAPI from 'vue-demi'

En este punto, todo el contenido se exporta y luego podemos vue-demiimportar , como:

import {
    
     isVue2, Vue, ref, reactive, defineComponent } from 'vue-demi'

versión v2.7

A continuación, echemos un vistazo a cómo lidiar con Vue2.7la exportación de la versión. En comparación con Vue2.6la versión anterior, Vue2.7está directamente integrado @vue/composition-api, por lo que además del Vueobjeto de exportación predeterminado, también se exporta la fórmula combinada API:

import Vue from 'vue'

var isVue2 = true
var isVue3 = false
var Vue2 = Vue
var warn = Vue.util.warn

function install() {
    
    }

export {
    
     Vue, Vue2, isVue2, isVue3, install, warn }
// ...

Comparado v2con , el contenido exportado es similar, porque no necesita ser instalado @vue/composition-api, por lo que installes una función vacía, la diferencia es que también se exporta un método warn, que no se menciona en este documento, pero el motivo se puede encontrar en ediciones pasadas , que se Vue3exporta aproximadamente como un warnmétodo, y Vue2el warnmétodo está en Vue.utilel objeto, así que para unificar la exportación manual, ¿por qué V2la versión no exporta uno manualmente? La razón es muy simple, porque este método @vue/composition-apiestá incluido en el exportar.

continuar:

// ...
export * from 'vue'
// ...

Exporte Vuetodas las exportaciones de la figura anterior, incluidas version, combinadas API, pero tenga en cuenta que esta forma de escribir no exportará el valor predeterminado Vue, por lo que si utiliza la importación predeterminada como la siguiente, no obtendrá Vueel objeto:

import Vue from 'vue-demi'

continuar:

// ...
// createApp polyfill
export function createApp(rootComponent, rootProps) {
    
    
  var vm
  var provide = {
    
    }
  var app = {
    
    
    config: Vue.config,
    use: Vue.use.bind(Vue),
    mixin: Vue.mixin.bind(Vue),
    component: Vue.component.bind(Vue),
    provide: function (key, value) {
    
    
      provide[key] = value
      return this
    },
    directive: function (name, dir) {
    
    
      if (dir) {
    
    
        Vue.directive(name, dir)
        return app
      } else {
    
    
        return Vue.directive(name)
      }
    },
    mount: function (el, hydrating) {
    
    
      if (!vm) {
    
    
        vm = new Vue(Object.assign({
    
     propsData: rootProps }, rootComponent, {
    
     provide: Object.assign(provide, rootComponent.provide) }))
        vm.$mount(el, hydrating)
        return vm
      } else {
    
    
        return vm
      }
    },
    unmount: function () {
    
    
      if (vm) {
    
    
        vm.$destroy()
        vm = undefined
      }
    },
  }
  return app
}

Es diferente a Vue2crear una instancia, es a través del método, new Vueeste método se enchufa , entonces se hace manualmente .VueVue3createApp@vue/composition-apipolyfillVue2.7Vue Demipolyfill

En este punto, Vue2.7se acabó lo que se ha hecho en contra.

versión v3

Vue3En comparación con la versión anterior, la mayor diferencia es que ya no hay una Vueexportación separada:

import * as Vue from 'vue'

var isVue2 = false
var isVue3 = true
var Vue2 = undefined

function install() {
    
    }

export {
    
    
  Vue,
  Vue2,
  isVue2,
  isVue3,
  install,
}
// ...

Debido a que el objeto no se exporta de forma predeterminada Vue, import * as Vuetodas las exportaciones se cargan en Vueel objeto a través de la importación general, y luego también puede ver que la Vue2exportación también es una función vacía.undefinedinstall

continuar:

// ...
export * from 'vue'
// ...

No hay nada que decir, Vuetodo el contenido exportado se exporta directamente.

continuar:

// ...
export function set(target, key, val) {
    
    
  if (Array.isArray(target)) {
    
    
    target.length = Math.max(target.length, key)
    target.splice(key, 1, val)
    return val
  }
  target[key] = val
  return val
}

export function del(target, key) {
    
    
  if (Array.isArray(target)) {
    
    
    target.splice(key, 1)
    return
  }
  delete target[key]
}

Los últimos polyfilldos métodos en realidad los @vue/composition-apiproporciona el complemento, porque @vue/composition-apila implementación receptiva provista APIno usa Proxyun proxy y aún se Vue2implementa en función del sistema receptivo, por lo que Vue2las limitaciones del sistema receptivo aún existen. similares Vue.sety Vue.deletemétodos para agregar o quitar atributos a los datos receptivos.

Supongo que te gusta

Origin blog.csdn.net/sinat_33488770/article/details/128294084
Recomendado
Clasificación