Skip to content

view diffs #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 25 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
9c2d8e7
feat: 加入gogoend示例以便进行调试
gogoend Dec 26, 2021
c93ad08
feat: 了解composition-api是如何安装到Vue上的
gogoend Dec 26, 2021
e662e37
feat: 了解setup的返回值是如何被赋值到实例上的
gogoend Dec 26, 2021
b0f897e
Merge branch 'main' of github.com:js-dive/vue-composition-api into go…
gogoend Apr 13, 2022
6674960
feat: 调整importmap、`process.env.NODE_ENV`打包配置,使得demo可运行
gogoend Apr 13, 2022
d1d182a
docs: 尝试了解ref
gogoend Apr 14, 2022
67be7da
feat: 了解setup的返回值如何关联到vm(接e662e374)
gogoend Apr 15, 2022
514eeda
feat(全局mixin): 了解mounted、updated全局混入的目的 - 保持ref引用的对应DOM/组件为最新
gogoend Apr 15, 2022
c60fccd
feat(setCurrentInstance、getCurrentInstance): 激活当前实例、反激活当前实例函数定义的位置
gogoend Jul 7, 2022
e5b0312
docs: 尝试了解createObserver、markRaw的作用
gogoend Jul 7, 2022
9407596
docs: 了解`observe` - 返回一个经过响应式处理的对象;同时了解使用它的shallowReactive、reactive、s…
gogoend Jul 7, 2022
b144585
docs(setup中生命周期函数的合并): 了解setup中,`onMounted`等生命周期钩子是怎样被合并到Vue选项中的
gogoend Jul 11, 2022
9be3b73
refactor: 加入上次提交使用的demo
gogoend Jul 11, 2022
f9d42fa
docs(watch): 了解watch、watchEffect函数的入口
gogoend Jul 11, 2022
be9a37b
docs(watch): 了解createVueWatcher、createWatcher的实现
gogoend Jul 11, 2022
73727c4
docs(watch): 了解createWatcher的主要流程 - 先跳过调度相关的内容
gogoend Jul 17, 2022
c3eb8e1
feat(gogoend): 加入gogoend demo
gogoend Jul 17, 2022
ec5f64f
docs(watch): 尝试了解watch 回调执行流程
gogoend Jul 13, 2022
0cd734b
docs(watch): 了解flushQueue的逻辑以及执行时机;了解queueFlushJob
gogoend Jul 17, 2022
d8aba43
feat(gogoend): 加入与上次提交相关的gogoend demo
gogoend Jul 17, 2022
13cdbde
docs(computed): 了解computed流程
gogoend Jul 17, 2022
f8139b7
docs(effectScope): 初见effectScope - 了解入口:`bindCurrentScopeToVM` 用于在vue…
gogoend Jul 19, 2022
d456ecf
docs(effectScope): WIP: 尝试了解effectScope
gogoend Jul 19, 2022
03ecef3
docs(runtimeContext): 尝试了解一些运行时上下文相关函数
gogoend Jul 19, 2022
453131b
feat(provide): 测试provide
gogoend Sep 14, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions example/gogoend/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>gogoend CompositionAPI 测试</title>
</head>
<body>
<div id="app"></div>
<script type="importmap">
{
"imports": {
"vue": "/node_modules/vue/dist/vue.esm.browser.js",
"@vue/composition-api": "/dist/vue-composition-api.esm.js"
}
}
</script>
<script type="module" src="index.js"></script>
</body>
</html>
82 changes: 82 additions & 0 deletions example/gogoend/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import Vue from 'vue'
import VueCompositionAPI, {
ref,
createApp,
onMounted,
defineComponent,
watch,
effectScope,
watchEffect,
computed,
provide,
} from '@vue/composition-api'

Vue.use(VueCompositionAPI)

// ----- effectScope相关开始 -----
const scope = effectScope()
const counter = ref(1)
scope.run(() => {
const doubled = computed(() => counter.value * 2)

watch(doubled, () => console.log(doubled.value))

watchEffect(() => console.log('Count: ', doubled.value))
})
// 处理掉当前作用域内的所有 effect
scope.stop()
// ----- effectScope相关结束 -----

const App = defineComponent({
template: `
<div>
<div>{{ msg }} {{ msg1 }}</div>
<button @click="counter++">{{ counter }}</button>
</div>
`,
setup() {
const msg = ref('666')
console.log(msg)
provide(msg)

watch(
msg,
(...args) => {
debugger
console.log('w1', ...args)
},
{}
)
watch(
msg,
(...args) => {
debugger
console.log('w2', ...args)
},
{
immediate: true,
}
)
msg.value = '777'

onMounted(() => {
debugger
console.log(1)
})
onMounted(() => {
debugger
console.log(2)
})
return {
msg,
counter,
}
},
data() {
return {
msg1: '777',
}
},
})

createApp(App).mount('#app')
2 changes: 1 addition & 1 deletion rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ function genConfig({ outFile, format, mode }) {
'process.env.NODE_ENV':
format === 'es'
? // preserve to be handled by bundlers
'process.env.NODE_ENV'
'undefined'
: // hard coded dev/prod builds
JSON.stringify(isProd ? 'production' : 'development'),
__DEV__:
Expand Down
13 changes: 12 additions & 1 deletion src/apis/computed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,12 @@ export function computed<T>(
export function computed<T>(
getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>
): ComputedRef<T> | WritableComputedRef<T> {
// 获得当前激活的(在作用域内的vm)
const vm = getCurrentScopeVM()
let getter: ComputedGetter<T>
let setter: ComputedSetter<T> | undefined

// 归一化getterOrOptions,将它们分别赋值给getter、setter
if (isFunction(getterOrOptions)) {
getter = getterOrOptions
} else {
Expand All @@ -41,19 +43,25 @@ export function computed<T>(
let computedSetter
let computedGetter

// 如果能拿到vm,并且在非服务器渲染环境下
if (vm && !vm.$isServer) {
// 获得Vue内部的Watcher及Dep类
const { Watcher, Dep } = getVueInternalClasses()
let watcher: any
computedGetter = () => {
if (!watcher) {
// 设置计算属性Watcher —— 逻辑与传统的Vue 2主项目一致
watcher = new Watcher(vm, getter, noopFn, { lazy: true })
}
// 如果watcher dirty了,就重新进行一次计算来获取新的值
if (watcher.dirty) {
watcher.evaluate()
}
// TODO: 如果存在全剧唯一正在被计算的watcher,那么就进行以来收集
if (Dep.target) {
watcher.depend()
}
// 返回计算属性watcher的值
return watcher.value
}

Expand All @@ -67,7 +75,9 @@ export function computed<T>(
setter(v)
}
}
} else {
}
// 否则,创建一个新的vue实例,来托管computed
else {
// fallback
const computedHost = defineComponentInstance(getVueConstructor(), {
computed: {
Expand All @@ -91,6 +101,7 @@ export function computed<T>(
}
}

// 返回一个用于获得computed值的ref
return createRef<T>(
{
get: computedGetter,
Expand Down
16 changes: 16 additions & 0 deletions src/apis/effectScope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import {
import { defineComponentInstance } from '../utils'
import { warn } from './warn'

/**
* 正在活动(全局唯一)的effectScope
*/
let activeEffectScope: EffectScope | undefined
const effectScopeStack: EffectScope[] = []

Expand Down Expand Up @@ -75,12 +78,20 @@ export class EffectScope extends EffectScopeImpl {
}
}

/**
* 记录effectScope?
* @param effect
* @param scope
* @returns
*/
export function recordEffectScope(
effect: EffectScope,
scope?: EffectScope | null
) {
scope = scope || activeEffectScope
if (scope && scope.active) {
// 如果不是游离的Effect,那么就往当前scope effects里push一下传入的effect
// 看起来有点像一棵树
scope.effects.push(effect)
return
}
Expand Down Expand Up @@ -109,21 +120,26 @@ export function onScopeDispose(fn: () => void) {
}

/**
* 获得当前在scope内的vm?TODO: 为何有两种写法?
* @internal
**/
export function getCurrentScopeVM() {
return getCurrentScope()?.vm || getCurrentInstance()?.proxy
}

/**
* 绑定当前scope到vm上?
* @internal
**/
export function bindCurrentScopeToVM(
vm: ComponentInternalInstance
): EffectScope {
// 如果vm上没有scope,就设置一下
if (!vm.scope) {
const scope = new EffectScopeImpl(vm.proxy) as EffectScope
vm.scope = scope

// vm销毁的时候,scope给停掉
vm.proxy.$on('hook:destroyed', () => scope.stop())
}
return vm.scope
Expand Down
52 changes: 45 additions & 7 deletions src/apis/lifecycle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,39 +8,77 @@ import {
import { getCurrentInstanceForFn } from '../utils/helper'

const genName = (name: string) => `on${name[0].toUpperCase() + name.slice(1)}`

/**
* 生命周期工厂函数?
* @param lifeCyclehook 生命周期的名称
* @returns
*/
function createLifeCycle(lifeCyclehook: string) {
return (callback: Function, target?: ComponentInternalInstance | null) => {
const instance = getCurrentInstanceForFn(genName(lifeCyclehook), target)
return (
instance &&
injectHookOption(getVueConstructor(), instance, lifeCyclehook, callback)
)
}
return (
/**
* 在setup函数执行期间,在调用生命周期钩子(如onMounted等)将会执行的函数 - 该函数用于合并生命周期钩子
* @param callback 生命周期回调函数
* @param target Vue3 对象
* @returns
*/
(callback: Function, target?: ComponentInternalInstance | null) => {
const instance = getCurrentInstanceForFn(genName(lifeCyclehook), target)
return (
instance &&
injectHookOption(getVueConstructor(), instance, lifeCyclehook, callback)
)
}
)
}

/**
* 合并生命周期钩子
* @param Vue Vue构造函数
* @param instance 当前实例(Vue3)
* @param hook 生命周期钩子名称
* @param val 值(生命周期回调函数)
* @returns
*/
function injectHookOption(
Vue: VueConstructor,
instance: ComponentInternalInstance,
hook: string,
val: Function
) {
// 当前实例中的选项
const options = instance.proxy.$options as Record<string, unknown>
// 获得钩子的合并策略 - 见Vue2源代码 `mergeHook`
const mergeFn = Vue.config.optionMergeStrategies[hook]
// 获得一个经过包裹的回调函数
const wrappedHook = wrapHookCall(instance, val)
// 将经过合并的钩子重新赋值给到组件option
// options[hook] 是个数组,组件生命周期的执行实际上是挨个执行其中的函数
// 每调用一次对应的onHook(eg. onMounted)都会往对应数组里加一个函数
options[hook] = mergeFn(options[hook], wrappedHook)
return wrappedHook
}

/**
* 获得一个经过包裹的回调函数 - 经过包裹后,回调函数执行期间,将确保正在激活的组件实例是当前实例(Vue3)
* @param instance 当前实例(Vue3)
* @param fn 生命周期钩子中的回调函数
* @returns 经过包裹后的回调函数
*/
function wrapHookCall(
instance: ComponentInternalInstance,
fn: Function
): Function {
return (...args: any) => {
// 保存上一个正在激活状态的组件(Vue3)
let prev = getCurrentInstance()
// 设置激活状态组件为当前组件(Vue3)
setCurrentInstance(instance)
try {
// 执行生命周期钩子的回调函数
return fn(...args)
} finally {
// 恢复上一个正在激活状态的组件(Vue3)
setCurrentInstance(prev)
}
}
Expand Down
Loading