mirror of
https://github.com/ZiuChen/ZiuChen.github.io.git
synced 2025-08-17 06:49:38 +08:00
docs: new note
This commit is contained in:
parent
d9b9258772
commit
ef597c8f0e
295
docs/article/Vue Demi 做了什么?.md
Normal file
295
docs/article/Vue Demi 做了什么?.md
Normal file
@ -0,0 +1,295 @@
|
||||
# Vue Demi 做了什么?
|
||||
|
||||
在 Vue Demi 的主页有三行介绍 vue-demi 的策略:
|
||||
|
||||
> `<=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.
|
||||
|
||||
## 安装钩子
|
||||
|
||||
在执行 `npm install vue-demi` 后,会触发 postinstall 钩子:
|
||||
|
||||
```js
|
||||
// scripts/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 supported.`)
|
||||
}
|
||||
```
|
||||
|
||||
通过判断 Vue 的版本来为 `switchVersion` 传入不同的参数:
|
||||
|
||||
```js
|
||||
// scripts/utils.js
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
const dir = path.resolve(__dirname, '..', 'lib')
|
||||
|
||||
function loadModule(name) {
|
||||
try {
|
||||
return require(name)
|
||||
} catch (e) {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
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}'`)
|
||||
// unlink for pnpm, #92
|
||||
try {
|
||||
fs.unlinkSync(dest)
|
||||
} catch (error) { }
|
||||
fs.writeFileSync(dest, content, 'utf-8')
|
||||
}
|
||||
|
||||
function updateVue2API() {
|
||||
const ignoreList = ['version', 'default']
|
||||
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
|
||||
}
|
||||
|
||||
const exports = Object.keys(VCA).filter(i => !ignoreList.includes(i))
|
||||
|
||||
const esmPath = path.join(dir, 'index.mjs')
|
||||
let content = fs.readFileSync(esmPath, 'utf-8')
|
||||
|
||||
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')
|
||||
|
||||
}
|
||||
|
||||
function switchVersion(version, vue) {
|
||||
copy('index.cjs', version, vue)
|
||||
copy('index.mjs', version, vue)
|
||||
copy('index.d.ts', version, vue)
|
||||
|
||||
if (version === 2)
|
||||
updateVue2API()
|
||||
}
|
||||
```
|
||||
|
||||
而 `switchVersion` 本质上是在根据版本,将 `lib` 目录下针对不同版本的 patch 文件拷贝到 `node_modules/vue-demi/` 目录下供外部导入使用。
|
||||
|
||||
## 不同版本的 patch 文件在做什么?
|
||||
|
||||
```js
|
||||
import { ref } from 'vue-demi';
|
||||
```
|
||||
|
||||
针对 `<=2.6` `=2.7` `>=3` 这三种情况,当我们直接从 `vue-demi` 导入 Vue 的 API 时,Vue Demi 做了不同的操作:
|
||||
|
||||
- `<=2.6` 从 `vue` 和 `@vue/composition-api` 导出
|
||||
- `=2.7` 从 `vue` 导出
|
||||
- 因为 `@vue/composition-api` 已内置于 `vue` 中并统一导出了
|
||||
- `>=3` 从 `vue` 导出
|
||||
- 除此之外,`vue-demi` 还对两个 Vue2 版本的 API 进行了 polyfill
|
||||
|
||||
下面以不同版本的 `index.mjs` 为例解读细节:
|
||||
|
||||
```js
|
||||
// v3/index.mjs
|
||||
import * as Vue from 'vue'
|
||||
|
||||
var isVue2 = false
|
||||
var isVue3 = true
|
||||
var Vue2 = undefined
|
||||
|
||||
function install() {}
|
||||
|
||||
// 兼容 Vue2 通过 Object.defineProperty 对数组 api 的处理
|
||||
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]
|
||||
}
|
||||
|
||||
export * from 'vue'
|
||||
export {
|
||||
Vue,
|
||||
Vue2,
|
||||
isVue2,
|
||||
isVue3,
|
||||
install,
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
// v2.7/index.mjs
|
||||
import Vue from 'vue'
|
||||
import { getCurrentInstance } from 'vue'
|
||||
|
||||
var isVue2 = true
|
||||
var isVue3 = false
|
||||
var Vue2 = Vue
|
||||
var warn = Vue.util.warn
|
||||
|
||||
function install() {}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
export {
|
||||
Vue,
|
||||
Vue2,
|
||||
isVue2,
|
||||
isVue3,
|
||||
install,
|
||||
warn
|
||||
}
|
||||
|
||||
// Vue 3 components mock
|
||||
function createMockComponent(name) {
|
||||
return {
|
||||
setup() {
|
||||
throw new Error('[vue-demi] ' + name + ' is not supported in Vue 2. It\'s provided to avoid compiler errors.')
|
||||
}
|
||||
}
|
||||
}
|
||||
export var Fragment = /*#__PURE__*/ createMockComponent('Fragment')
|
||||
export var Transition = /*#__PURE__*/ createMockComponent('Transition')
|
||||
export var TransitionGroup = /*#__PURE__*/ createMockComponent('TransitionGroup')
|
||||
export var Teleport = /*#__PURE__*/ createMockComponent('Teleport')
|
||||
export var Suspense = /*#__PURE__*/ createMockComponent('Suspense')
|
||||
export var KeepAlive = /*#__PURE__*/ createMockComponent('KeepAlive')
|
||||
|
||||
export * from 'vue'
|
||||
|
||||
// Not implemented https://github.com/vuejs/core/pull/8111, falls back to getCurrentInstance()
|
||||
export function hasInjectionContext() {
|
||||
return !!getCurrentInstance()
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
// v2/index.mjs
|
||||
import Vue from 'vue'
|
||||
import VueCompositionAPI, { getCurrentInstance } 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)
|
||||
|
||||
var isVue2 = true
|
||||
var isVue3 = false
|
||||
var Vue2 = Vue
|
||||
var version = Vue.version
|
||||
|
||||
// 统一 composition-api 导出出口
|
||||
/**VCA-EXPORTS**/
|
||||
export * from '@vue/composition-api/dist/vue-composition-api.mjs'
|
||||
/**VCA-EXPORTS**/
|
||||
|
||||
export {
|
||||
Vue,
|
||||
Vue2,
|
||||
isVue2,
|
||||
isVue3,
|
||||
version,
|
||||
install,
|
||||
}
|
||||
|
||||
|
||||
// Vue 3 components mock
|
||||
function createMockComponent(name) {
|
||||
return {
|
||||
setup() {
|
||||
throw new Error('[vue-demi] ' + name + ' is not supported in Vue 2. It\'s provided to avoid compiler errors.')
|
||||
}
|
||||
}
|
||||
}
|
||||
export var Fragment = /*#__PURE__*/ createMockComponent('Fragment')
|
||||
export var Transition = /*#__PURE__*/ createMockComponent('Transition')
|
||||
export var TransitionGroup = /*#__PURE__*/ createMockComponent('TransitionGroup')
|
||||
export var Teleport = /*#__PURE__*/ createMockComponent('Teleport')
|
||||
export var Suspense = /*#__PURE__*/ createMockComponent('Suspense')
|
||||
export var KeepAlive = /*#__PURE__*/ createMockComponent('KeepAlive')
|
||||
|
||||
// Not implemented https://github.com/vuejs/core/pull/8111, falls back to getCurrentInstance()
|
||||
export function hasInjectionContext() {
|
||||
return !!getCurrentInstance()
|
||||
}
|
||||
```
|
Loading…
x
Reference in New Issue
Block a user