¿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 Vue2
yVue3
2. Solo use Vue2
y Vue3
admita ambosAPI
Estos dos métodos tienen desventajas. El primero es muy problemático y el segundo no puede usar la Vue3
composición recién agregada API
. De hecho, la Vue2.7+
versión actual tiene soporte integrado para la composición API
, Vue2.6
y 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 Vue2
y al mismo tiempo 3
. Aun así, en el desarrollo real, la misma API
fuente puede importarse en diferentes versiones. Por ejemplo ref
, los métodos pueden importarse Vue2.7+
directamente vue
de , pero Vue2.6-
solo pueden importarse @vue/composition-api
de , lo que inevitablemente implicará el juicio de versión. Vue Demi se utiliza para resolver esto. problema Es muy simple de usar, solo Vue Demi
exporte lo que necesita de él:
import {
ref, reactive, defineComponent } from 'vue-demi'
Vue-demi
Juzgará qué versión usar de acuerdo a tu proyecto Vue
, en concreto su estrategia es la siguiente:
<=2.6
: derivado deVue
y@vue/composition-api
2.7
: derivadoVue
de (composiciónAPI
incorporadaVue 2.7
)>=3.0
:Vue
derivado y devuelto la suma de laspolyfill
dos versionesVue 2
set
del
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-demi
en 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 vue
y luego llame a los métodos según las diferentes versiones switchVersion
.
Primero echemos un vistazo al loadModule
método:
function loadModule(name) {
try {
return require(name)
} catch (e) {
return undefined
}
}
Es muy simple, solo está empaquetado require
para evitar que los errores bloqueen el código.
Luego mira switchVersion
el 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 copy
el 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 commonjs
archivo de versión, ESM
el archivo de versión y TS
el archivo de definición de tipo.
Además, el método Vue2.6
se ejecuta para la siguiente versión updateVue2API
.
updateVue2API
Veamos el método más tarde, echemos un vistazo al copy
mé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 vue
nombre de reemplazo, que vue
debe usarse cuando configura un alias para él.
En este punto, Vue Demi
se completa la ejecución automática después de la instalación. De hecho, según Vue
la versión instalada en el proyecto de usuario, los archivos se copian de los tres directorios correspondientes como Vue Demi
el archivo de entrada del paquete, y Vue Demi
se 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 commonjs
de módulo , se pueden usar archivos cjs
admitidos y también se proporcionan archivos del tipo que se pueden usar directamente en el navegador .ESM
mjs
iife
Vue
A continuación, echemos un vistazo a lo que se ha hecho para las tres versiones .
versión v2
Vue2.6
Las versiones solo tienen una exportación predeterminada:
Solo miramos mjs
el documento, y cjs
aquellos 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 Vue
y VueCompositionAPI
complementos, y llama automáticamente Vue.use
al 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**/
isVue2
Primero, dos variables y se exportan isVue3
para facilitar que nuestro código de biblioteca juzgue el entorno.
Entonces en Vue
el mismo momento de exportar también se Vue2
exporta nuevamente a través del nombre ¿Por qué es esto? De hecho es porque todos los objetos Vue2
están API
montados en Vue
el 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, Vue2
no hay problema en el entorno, pero cuando nuestra biblioteca está en Vue3
el entorno, en realidad no hay necesidad de importar Vue
objetos, porque no es necesario, pero la herramienta de compilación no lo sabe, por lo que empaquetará Vue3
todos los código en él, pero Vue3
una gran cantidad de contenido que no usamos es innecesario, pero debido a que importamos todos API
los Vue
objetos, no podemos eliminarlos, por lo que podemos Vue2
exportar 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á Vue3
en la exportación de , por lo que este problema se puede solucionar.Vue2
undefined
Luego exportó Vue
la versión y install
el método, lo que significa que puede instalar manualmente VueCompositionAPI
el complemento.
Luego lo VueCompositionAPI
proporciona 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 API
mencionado anteriormente también ejecuta el método para la versión, ahora echemos un vistazo en lo que hace:switchVersion
Vue2
updateVue2API
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-api
y luego filtrar todo el contenido exportado @vue/composition-api
excepto version
y 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 version
y default
? version
Debido a que ya ha sido exportado Vue
, version
entrará en conflicto. No es necesario en absoluto, default
es decir, la exportación predeterminada. @vue/composition-api
La exportación predeterminada es en realidad un objeto que contiene sus install
métodos. Como ha visto antes, puede importar por defecto @vue/composition-api
Luego Vue.use
instálelo, esto en realidad no necesita ser Vue Demi
exportado, 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-demi
importar , 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.7
la exportación de la versión. En comparación con Vue2.6
la versión anterior, Vue2.7
está directamente integrado @vue/composition-api
, por lo que además del Vue
objeto 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 v2
con , el contenido exportado es similar, porque no necesita ser instalado @vue/composition-api
, por lo que install
es 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 Vue3
exporta aproximadamente como un warn
método, y Vue2
el warn
método está en Vue.util
el objeto, así que para unificar la exportación manual, ¿por qué V2
la versión no exporta uno manualmente? La razón es muy simple, porque este método @vue/composition-api
está incluido en el exportar.
continuar:
// ...
export * from 'vue'
// ...
Exporte Vue
todas 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á Vue
el 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 Vue2
crear una instancia, es a través del método, new Vue
este método se enchufa , entonces se hace manualmente .Vue
Vue3
createApp
@vue/composition-api
polyfill
Vue2.7
Vue Demi
polyfill
En este punto, Vue2.7
se acabó lo que se ha hecho en contra.
versión v3
Vue3
En comparación con la versión anterior, la mayor diferencia es que ya no hay una Vue
exportació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 Vue
todas las exportaciones se cargan en Vue
el objeto a través de la importación general, y luego también puede ver que la Vue2
exportación también es una función vacía.undefined
install
continuar:
// ...
export * from 'vue'
// ...
No hay nada que decir, Vue
todo 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 polyfill
dos métodos en realidad los @vue/composition-api
proporciona el complemento, porque @vue/composition-api
la implementación receptiva provista API
no usa Proxy
un proxy y aún se Vue2
implementa en función del sistema receptivo, por lo que Vue2
las limitaciones del sistema receptivo aún existen. similares Vue.set
y Vue.delete
métodos para agregar o quitar atributos a los datos receptivos.