Compare commits
17 Commits
9aad0f6c74
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 026f4a5958 | |||
| abf8112359 | |||
| 6aa564ef7c | |||
|
|
61edc322f2 | ||
| 4225ae0b8f | |||
| a3a7b8c7e4 | |||
|
|
164bae388f | ||
| b9a9c52333 | |||
| 4a71105e28 | |||
| ba03278947 | |||
|
|
1f50ab1c84 | ||
|
|
62e40e7292 | ||
| c07431db08 | |||
| 4c60486511 | |||
| c0853b353d | |||
| 2ed86a03b5 | |||
| 6b7b452a56 |
40
packages/reactivity/src/computed.ts
Normal file
40
packages/reactivity/src/computed.ts
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import { isFunction } from '@vue/shared'
|
||||||
|
import { Dep } from './dep'
|
||||||
|
import { ReactiveEffect } from './effect'
|
||||||
|
import { trackRefValue, triggerRefValue } from './ref'
|
||||||
|
|
||||||
|
export class ComputedRefImpl<T> {
|
||||||
|
public dep?: Dep = undefined
|
||||||
|
private _value!: T
|
||||||
|
public readonly effect: ReactiveEffect<T>
|
||||||
|
public readonly __v_isRef = true
|
||||||
|
|
||||||
|
public _dirty = true //为true时,需要重新执行run方法, 也就是说数据脏了的意思
|
||||||
|
constructor(getter) {
|
||||||
|
this.effect = new ReactiveEffect(getter, () => {
|
||||||
|
if (!this._dirty) {
|
||||||
|
this._dirty = true
|
||||||
|
triggerRefValue(this)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.effect.computed = this
|
||||||
|
}
|
||||||
|
get value() {
|
||||||
|
trackRefValue(this)
|
||||||
|
if (this._dirty) {
|
||||||
|
this._dirty = false
|
||||||
|
this._value = this.effect.run()
|
||||||
|
}
|
||||||
|
return this._value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function computed(getterOrOptions) {
|
||||||
|
let getter
|
||||||
|
const onlyGetter = isFunction(getterOrOptions)
|
||||||
|
if (onlyGetter) {
|
||||||
|
getter = getterOrOptions
|
||||||
|
}
|
||||||
|
const cRef = new ComputedRefImpl(getter)
|
||||||
|
return cRef
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
import { createDep, Dep } from './dep'
|
import { createDep, Dep } from './dep'
|
||||||
import { isArray } from '@vue/shared'
|
import { extend, isArray } from '@vue/shared'
|
||||||
|
import { ComputedRefImpl } from './computed'
|
||||||
|
export type EffectScheduler = (...args: any[]) => any
|
||||||
|
|
||||||
type KeyToDepMap = Map<any, Dep>
|
type KeyToDepMap = Map<any, Dep>
|
||||||
/**
|
/**
|
||||||
@@ -11,20 +13,35 @@ type KeyToDepMap = Map<any, Dep>
|
|||||||
*/
|
*/
|
||||||
const targetMap = new WeakMap<any, KeyToDepMap>()
|
const targetMap = new WeakMap<any, KeyToDepMap>()
|
||||||
|
|
||||||
export function effect<T = any>(fn: () => T) {
|
export interface ReactiveEffectOptions {
|
||||||
|
lazy?: boolean
|
||||||
|
scheduler?: EffectScheduler
|
||||||
|
}
|
||||||
|
export function effect<T = any>(fn: () => T, options?: ReactiveEffectOptions) {
|
||||||
const _effect = new ReactiveEffect(fn)
|
const _effect = new ReactiveEffect(fn)
|
||||||
_effect.run()
|
if (options) {
|
||||||
|
extend(_effect, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!options || !options.lazy) {
|
||||||
|
_effect.run()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export let activeEffect: ReactiveEffect | undefined
|
export let activeEffect: ReactiveEffect | undefined
|
||||||
|
|
||||||
export class ReactiveEffect<T = any> {
|
export class ReactiveEffect<T = any> {
|
||||||
constructor(public fn: () => T) {}
|
computed?: ComputedRefImpl<T>
|
||||||
|
constructor(
|
||||||
|
public fn: () => T,
|
||||||
|
public scheduler: EffectScheduler | null = null
|
||||||
|
) {}
|
||||||
run() {
|
run() {
|
||||||
//当前被激活的effect实例
|
//当前被激活的effect实例
|
||||||
activeEffect = this
|
activeEffect = this
|
||||||
return this.fn() //完成第一次getter行为的触发
|
return this.fn() //完成第一次getter行为的触发
|
||||||
}
|
}
|
||||||
|
stop() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -76,14 +93,26 @@ export function trigger(target: object, key: unknown, newValue: unknown) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 依次触发dep中保存的依赖
|
* 触发dep中保存的依赖
|
||||||
* @param dep
|
* @param dep
|
||||||
*/
|
*/
|
||||||
export function triggerEffects(dep: Dep) {
|
export function triggerEffects(dep: Dep) {
|
||||||
|
//把dep构建为一个数组
|
||||||
const effects = isArray(dep) ? dep : [...dep]
|
const effects = isArray(dep) ? dep : [...dep]
|
||||||
//依次触发依赖
|
//不再依次触发依赖
|
||||||
|
// for (const effect of effects) {
|
||||||
|
// triggerEffect(effect)
|
||||||
|
// }
|
||||||
|
//而是先触发所有的计算属性依赖, 再触发所有的非计算属性依赖
|
||||||
for (const effect of effects) {
|
for (const effect of effects) {
|
||||||
triggerEffect(effect)
|
if (effect.computed) {
|
||||||
|
triggerEffect(effect)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const effect of effects) {
|
||||||
|
if (!effect.computed) {
|
||||||
|
triggerEffect(effect)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,5 +121,9 @@ export function triggerEffects(dep: Dep) {
|
|||||||
* @param effect
|
* @param effect
|
||||||
*/
|
*/
|
||||||
export function triggerEffect(effect: ReactiveEffect) {
|
export function triggerEffect(effect: ReactiveEffect) {
|
||||||
effect.run()
|
if (effect.scheduler) {
|
||||||
|
effect.scheduler()
|
||||||
|
} else {
|
||||||
|
effect.run()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
export { reactive } from './reactive'
|
export { reactive, isReactive } from './reactive'
|
||||||
export { effect } from './effect'
|
export { effect, ReactiveEffect } from './effect'
|
||||||
export { ref } from './ref'
|
export { ref } from './ref'
|
||||||
|
export { computed } from './computed'
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ import { isObject } from '@vue/shared'
|
|||||||
*/
|
*/
|
||||||
export const reactiveMap = new WeakMap<object, any>()
|
export const reactiveMap = new WeakMap<object, any>()
|
||||||
|
|
||||||
|
export const enum ReactiveFlags {
|
||||||
|
IS_REACTIVE = '__v_isReactive'
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 为复杂数据类型, 创建响应式对象
|
* 为复杂数据类型, 创建响应式对象
|
||||||
* @param target 被代理的对象
|
* @param target 被代理的对象
|
||||||
@@ -35,7 +39,8 @@ function createReactiveObject(
|
|||||||
}
|
}
|
||||||
//未被代理则生成proxy实例
|
//未被代理则生成proxy实例
|
||||||
const proxy = new Proxy(target, baseHandlers)
|
const proxy = new Proxy(target, baseHandlers)
|
||||||
|
//添加reactive标识
|
||||||
|
proxy[ReactiveFlags.IS_REACTIVE] = true
|
||||||
//缓存代理对象
|
//缓存代理对象
|
||||||
proxyMap.set(target, proxy)
|
proxyMap.set(target, proxy)
|
||||||
return proxy
|
return proxy
|
||||||
@@ -43,3 +48,7 @@ function createReactiveObject(
|
|||||||
|
|
||||||
export const toReactive = <T extends unknown>(value: T): T =>
|
export const toReactive = <T extends unknown>(value: T): T =>
|
||||||
isObject(value) ? reactive(value as object) : value
|
isObject(value) ? reactive(value as object) : value
|
||||||
|
|
||||||
|
export function isReactive(value): boolean {
|
||||||
|
return !!(value && value[ReactiveFlags.IS_REACTIVE])
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { createDep, Dep } from './dep'
|
import { createDep, Dep } from './dep'
|
||||||
import { toReactive } from './reactive'
|
import { toReactive } from './reactive'
|
||||||
import { activeEffect, track, trackEffects } from './effect'
|
import { activeEffect, triggerEffects, trackEffects } from './effect'
|
||||||
|
import { hasChanged } from '@vue/shared'
|
||||||
|
|
||||||
export interface Ref<T = any> {
|
export interface Ref<T = any> {
|
||||||
value: T
|
value: T
|
||||||
@@ -17,20 +18,46 @@ function createRef(rawValue: unknown, shallow: boolean) {
|
|||||||
}
|
}
|
||||||
class RefImpl<T> {
|
class RefImpl<T> {
|
||||||
private _value: T
|
private _value: T
|
||||||
|
private _rawValue: T
|
||||||
public dep?: Dep = undefined
|
public dep?: Dep = undefined
|
||||||
public readonly __v_isRef = true
|
public readonly __v_isRef = true
|
||||||
constructor(
|
constructor(
|
||||||
value: T,
|
value: T,
|
||||||
public readonly __v_isShallow: boolean
|
public readonly __v_isShallow: boolean
|
||||||
) {
|
) {
|
||||||
|
//原始数据
|
||||||
|
this._rawValue = value
|
||||||
this._value = __v_isShallow ? value : toReactive(value)
|
this._value = __v_isShallow ? value : toReactive(value)
|
||||||
}
|
}
|
||||||
get value() {
|
get value() {
|
||||||
trackRefValue(this)
|
trackRefValue(this)
|
||||||
return this._value
|
return this._value
|
||||||
}
|
}
|
||||||
set value(newValue) {}
|
|
||||||
|
/**
|
||||||
|
* newValue 新数据
|
||||||
|
* this._rawValue为旧数据(原始数据)
|
||||||
|
* 对比两个数据是否发生改变
|
||||||
|
*/
|
||||||
|
set value(newValue) {
|
||||||
|
if (hasChanged(newValue, this._rawValue)) {
|
||||||
|
//更新原始数据
|
||||||
|
this._rawValue = newValue
|
||||||
|
//更新 .value的值
|
||||||
|
this._value = toReactive(newValue)
|
||||||
|
//触发依赖
|
||||||
|
triggerRefValue(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
//为ref的value进行触发依赖工作
|
||||||
|
export function triggerRefValue(ref) {
|
||||||
|
if (ref.dep) {
|
||||||
|
triggerEffects(ref.dep)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//收集依赖
|
||||||
export function trackRefValue(ref) {
|
export function trackRefValue(ref) {
|
||||||
if (activeEffect) {
|
if (activeEffect) {
|
||||||
trackEffects(ref.dep || (ref.dep = createDep()))
|
trackEffects(ref.dep || (ref.dep = createDep()))
|
||||||
|
|||||||
75
packages/runtime-core/src/apiWatch.ts
Normal file
75
packages/runtime-core/src/apiWatch.ts
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
import { EMPTY_OBJ, hasChanged, isObject } from '@vue/shared'
|
||||||
|
import { isReactive, ReactiveEffect } from '@vue/reactivity'
|
||||||
|
import { queuePreFlushCb } from '@vue/runtime-core'
|
||||||
|
|
||||||
|
export interface WatchOptions<immediate = boolean> {
|
||||||
|
immediate?: immediate
|
||||||
|
deep?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export function watch(source, cb: Function, options?: WatchOptions) {
|
||||||
|
return doWatch(source, cb, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
function doWatch(
|
||||||
|
source,
|
||||||
|
cb: Function,
|
||||||
|
{ immediate, deep }: WatchOptions = EMPTY_OBJ
|
||||||
|
) {
|
||||||
|
let getter: () => any
|
||||||
|
if (isReactive(source)) {
|
||||||
|
getter = () => source
|
||||||
|
deep = true
|
||||||
|
} else {
|
||||||
|
getter = () => {}
|
||||||
|
}
|
||||||
|
//处理deep的状态
|
||||||
|
if (cb && deep) {
|
||||||
|
//todo
|
||||||
|
//浅拷贝的形式--指向同样的内存空间
|
||||||
|
const baseGetter = getter
|
||||||
|
//等同于将source的属性传进去了
|
||||||
|
getter = () => traverse(baseGetter())
|
||||||
|
}
|
||||||
|
|
||||||
|
let oldValue = {}
|
||||||
|
//每次执行等同于watch被触发一次
|
||||||
|
const job = () => {
|
||||||
|
if (cb) {
|
||||||
|
const newValue = effect.run()
|
||||||
|
if (deep || hasChanged(newValue, oldValue)) {
|
||||||
|
cb(newValue, oldValue)
|
||||||
|
oldValue = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//调度器
|
||||||
|
let scheduler = () => queuePreFlushCb(job)
|
||||||
|
const effect = new ReactiveEffect(getter, scheduler)
|
||||||
|
if (cb) {
|
||||||
|
if (immediate) {
|
||||||
|
job()
|
||||||
|
} else {
|
||||||
|
oldValue = effect.run()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
effect.run()
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
effect.stop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 本质上是拿value, 出发一次getter行为
|
||||||
|
* @param value
|
||||||
|
*/
|
||||||
|
export function traverse(value: unknown) {
|
||||||
|
if (!isObject(value)) {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
for (const key in value as object) {
|
||||||
|
traverse((value as object)[key])
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
25
packages/runtime-core/src/h.ts
Normal file
25
packages/runtime-core/src/h.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { createVNode, isVNode, VNode } from './vnode'
|
||||||
|
import { isObject } from '@vue/shared'
|
||||||
|
|
||||||
|
export function h(type: any, propsOrChildren?: any, children?: any): VNode {
|
||||||
|
const l = arguments.length
|
||||||
|
if (l === 2) {
|
||||||
|
//是对象
|
||||||
|
if (isObject(propsOrChildren) && !Array.isArray(propsOrChildren)) {
|
||||||
|
if (isVNode(propsOrChildren)) {
|
||||||
|
return createVNode(type, null, [propsOrChildren])
|
||||||
|
}
|
||||||
|
return createVNode(type, propsOrChildren, [])
|
||||||
|
} else {
|
||||||
|
//是数组
|
||||||
|
return createVNode(type, null, propsOrChildren)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (l > 3) {
|
||||||
|
children = Array.prototype.slice.call(arguments, 2) //生成新的children
|
||||||
|
} else if (l === 3 && isVNode(children)) {
|
||||||
|
children = [children]
|
||||||
|
}
|
||||||
|
return createVNode(type, propsOrChildren, children)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export { queuePreFlushCb } from './scheduler'
|
||||||
|
export { watch } from './apiWatch'
|
||||||
|
export { h } from './h'
|
||||||
|
export { Fragment, Text, Comment } from './vnode'
|
||||||
|
|||||||
35
packages/runtime-core/src/scheduler.ts
Normal file
35
packages/runtime-core/src/scheduler.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
let isFlushPending = false
|
||||||
|
|
||||||
|
const resolvedPromise = Promise.resolve() as Promise<any>
|
||||||
|
|
||||||
|
let currentFlushPromise: Promise<void> | null = null
|
||||||
|
|
||||||
|
const pendingPreFlushCbs: Function[] = []
|
||||||
|
export function queuePreFlushCb(cb: Function) {
|
||||||
|
queueCb(cb, pendingPreFlushCbs)
|
||||||
|
}
|
||||||
|
function queueCb(cb: Function, pendingQueue: Function[]) {
|
||||||
|
pendingQueue.push(cb)
|
||||||
|
queueFlush()
|
||||||
|
}
|
||||||
|
|
||||||
|
function queueFlush() {
|
||||||
|
if (!isFlushPending) {
|
||||||
|
isFlushPending = true
|
||||||
|
currentFlushPromise = resolvedPromise.then(flushJobs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function flushJobs() {
|
||||||
|
isFlushPending = false
|
||||||
|
flushPreFlushCbs()
|
||||||
|
}
|
||||||
|
export function flushPreFlushCbs() {
|
||||||
|
if (pendingPreFlushCbs.length) {
|
||||||
|
//拷贝去重,类似深拷贝
|
||||||
|
let activePreFlushCbs = [...new Set(pendingPreFlushCbs)]
|
||||||
|
pendingPreFlushCbs.length = 0
|
||||||
|
for (let i = 0; i < activePreFlushCbs.length; i++) {
|
||||||
|
activePreFlushCbs[i]()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
66
packages/runtime-core/src/vnode.ts
Normal file
66
packages/runtime-core/src/vnode.ts
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import {
|
||||||
|
isArray,
|
||||||
|
isFunction,
|
||||||
|
isObject,
|
||||||
|
isString,
|
||||||
|
normalizeClass,
|
||||||
|
ShapeFlags
|
||||||
|
} from '@vue/shared'
|
||||||
|
|
||||||
|
export const Fragment = Symbol('Fragment')
|
||||||
|
export const Text = Symbol('Text')
|
||||||
|
export const Comment = Symbol('Comment')
|
||||||
|
|
||||||
|
export interface VNode {
|
||||||
|
__v_isVNode: true
|
||||||
|
type: any
|
||||||
|
props: any
|
||||||
|
children: any
|
||||||
|
shapeFlag: number
|
||||||
|
}
|
||||||
|
export function isVNode(value): value is VNode {
|
||||||
|
return value ? value.__v_isVNode === true : false
|
||||||
|
}
|
||||||
|
export function createVNode(type, props, children): VNode {
|
||||||
|
if (props) {
|
||||||
|
let { class: klass, style } = props
|
||||||
|
if (klass && !isString(klass)) {
|
||||||
|
props.class = normalizeClass(klass)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const shapeFlag = isString(type)
|
||||||
|
? ShapeFlags.ELEMENT
|
||||||
|
: isObject(type)
|
||||||
|
? ShapeFlags.STATEFUL_COMPONENT
|
||||||
|
: 0
|
||||||
|
return createBaseVNode(type, props, children, shapeFlag)
|
||||||
|
}
|
||||||
|
|
||||||
|
function createBaseVNode(type, props, children, shapeFlag) {
|
||||||
|
const vnode = {
|
||||||
|
__v_isVNode: true,
|
||||||
|
type,
|
||||||
|
props,
|
||||||
|
shapeFlag
|
||||||
|
} as VNode
|
||||||
|
//解析/标准化当前vnode下的children
|
||||||
|
normalizeChildren(vnode, children)
|
||||||
|
return vnode
|
||||||
|
}
|
||||||
|
export function normalizeChildren(vnode: VNode, children: unknown) {
|
||||||
|
let type = 0
|
||||||
|
const { shapeFlag } = vnode
|
||||||
|
if (children == null) {
|
||||||
|
children = null
|
||||||
|
} else if (isArray(children)) {
|
||||||
|
type = ShapeFlags.ARRAY_CHILDREN
|
||||||
|
} else if (typeof children === 'object') {
|
||||||
|
} else if (isFunction(children)) {
|
||||||
|
} else {
|
||||||
|
children = String(children)
|
||||||
|
type = ShapeFlags.TEXT_CHILDREN
|
||||||
|
}
|
||||||
|
vnode.children = children
|
||||||
|
vnode.shapeFlag |= type
|
||||||
|
}
|
||||||
@@ -1,5 +1,25 @@
|
|||||||
|
export { ShapeFlags } from './shapeFlags'
|
||||||
|
export { normalizeClass } from './normalizeProp'
|
||||||
//判断是否为一个数组
|
//判断是否为一个数组
|
||||||
export const isArray = Array.isArray
|
export const isArray = Array.isArray
|
||||||
|
|
||||||
export const isObject = (val: unknown) =>
|
export const isObject = (val: unknown) =>
|
||||||
val !== null && typeof val === 'object'
|
val !== null && typeof val === 'object'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对比两个数据是否发生改变, 如果发生改变则返回true
|
||||||
|
*/
|
||||||
|
export const hasChanged = (value: any, oldValue: any): boolean =>
|
||||||
|
!Object.is(value, oldValue)
|
||||||
|
|
||||||
|
export const isFunction = (val: unknown): val is Function => {
|
||||||
|
return typeof val === 'function'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isString = (val: unknown): val is string => {
|
||||||
|
return typeof val === 'string'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const extend = Object.assign
|
||||||
|
|
||||||
|
export const EMPTY_OBJ: { readonly [key: string]: any } = {}
|
||||||
|
|||||||
24
packages/shared/src/normalizeProp.ts
Normal file
24
packages/shared/src/normalizeProp.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { isArray, isObject, isString } from '@vue/shared'
|
||||||
|
|
||||||
|
export function normalizeClass(value: unknown): string {
|
||||||
|
let res = ''
|
||||||
|
|
||||||
|
if (isString(value)) {
|
||||||
|
res = value
|
||||||
|
} else if (isArray(value)) {
|
||||||
|
for (let i = 0; i < value.length; i++) {
|
||||||
|
const normalized = normalizeClass(value[i])
|
||||||
|
if (normalized) {
|
||||||
|
res += normalized + ' '
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (isObject(value)) {
|
||||||
|
for (const name in value as object) {
|
||||||
|
if ((value as object)[name]) {
|
||||||
|
res += name + ' '
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.trim()
|
||||||
|
}
|
||||||
30
packages/shared/src/shapeFlags.ts
Normal file
30
packages/shared/src/shapeFlags.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
export const enum ShapeFlags {
|
||||||
|
/**
|
||||||
|
* type=Element
|
||||||
|
*/
|
||||||
|
ELEMENT = 1,
|
||||||
|
/**
|
||||||
|
* 函数组件
|
||||||
|
*/
|
||||||
|
FUNCTIONAL_COMPONENT = 1 << 1,
|
||||||
|
/**
|
||||||
|
* 有状态(响应数据)组件
|
||||||
|
*/
|
||||||
|
STATEFUL_COMPONENT = 1 << 2,
|
||||||
|
/**
|
||||||
|
* children=Text
|
||||||
|
*/
|
||||||
|
TEXT_CHILDREN = 1 << 3,
|
||||||
|
/**
|
||||||
|
* children=Array
|
||||||
|
*/
|
||||||
|
ARRAY_CHILDREN = 1 << 4,
|
||||||
|
/**
|
||||||
|
* children=slot
|
||||||
|
*/
|
||||||
|
SLOTS__CHILDREN = 1 << 5,
|
||||||
|
/**
|
||||||
|
* 组件: 有状态(响应数据)组件 | 函数组件
|
||||||
|
*/
|
||||||
|
COMPONENT = ShapeFlags.STATEFUL_COMPONENT | ShapeFlags.FUNCTIONAL_COMPONENT
|
||||||
|
}
|
||||||
28
packages/vue/examples/reactivity/computed-cache.html
Normal file
28
packages/vue/examples/reactivity/computed-cache.html
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Document</title>
|
||||||
|
<script src="../../dist/vue.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
</body>
|
||||||
|
<script>
|
||||||
|
const { reactive,effect,computed } = Vue
|
||||||
|
const obj=reactive({
|
||||||
|
name:'张三'
|
||||||
|
})
|
||||||
|
const computedObj= computed(()=>{
|
||||||
|
console.log('计算属性执行')
|
||||||
|
return '姓名:'+obj.name
|
||||||
|
})
|
||||||
|
effect(()=>{
|
||||||
|
document.querySelector('#app').innerText=computedObj.value
|
||||||
|
document.querySelector('#app').innerText=computedObj.value
|
||||||
|
})
|
||||||
|
setTimeout(()=>{
|
||||||
|
obj.name='李四'
|
||||||
|
},2000)
|
||||||
|
</script>
|
||||||
|
</html>
|
||||||
26
packages/vue/examples/reactivity/computed.html
Normal file
26
packages/vue/examples/reactivity/computed.html
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Document</title>
|
||||||
|
<script src="../../dist/vue.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
</body>
|
||||||
|
<script>
|
||||||
|
const { reactive,effect,computed } = Vue
|
||||||
|
const obj=reactive({
|
||||||
|
name:'张三'
|
||||||
|
})
|
||||||
|
const computedObj= computed(()=>{
|
||||||
|
return '姓名:'+obj.name
|
||||||
|
})
|
||||||
|
effect(()=>{
|
||||||
|
document.querySelector('#app').innerText=computedObj.value
|
||||||
|
})
|
||||||
|
setTimeout(()=>{
|
||||||
|
obj.name='李四'
|
||||||
|
},2000)
|
||||||
|
</script>
|
||||||
|
</html>
|
||||||
26
packages/vue/examples/reactivity/lazy.html
Normal file
26
packages/vue/examples/reactivity/lazy.html
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Document</title>
|
||||||
|
<script src="../../dist/vue.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
</body>
|
||||||
|
<script>
|
||||||
|
const { reactive,effect } = Vue
|
||||||
|
const obj=reactive({
|
||||||
|
count:1
|
||||||
|
})
|
||||||
|
|
||||||
|
effect(()=>{
|
||||||
|
console.log('obj.count',obj.count)
|
||||||
|
},{
|
||||||
|
lazy:false
|
||||||
|
})
|
||||||
|
obj.count=2
|
||||||
|
|
||||||
|
console.log('代码运行结束');
|
||||||
|
</script>
|
||||||
|
</html>
|
||||||
21
packages/vue/examples/reactivity/ref-shallow.html
Normal file
21
packages/vue/examples/reactivity/ref-shallow.html
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Document</title>
|
||||||
|
<script src="../../dist/vue.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
</body>
|
||||||
|
<script>
|
||||||
|
const { ref,effect } = Vue
|
||||||
|
const obj=ref('张三')
|
||||||
|
effect(()=>{
|
||||||
|
document.querySelector('#app').innerText=obj.value
|
||||||
|
})
|
||||||
|
setTimeout(()=>{
|
||||||
|
obj.value='李四'
|
||||||
|
},2000)
|
||||||
|
</script>
|
||||||
|
</html>
|
||||||
@@ -9,16 +9,15 @@
|
|||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
</body>
|
</body>
|
||||||
<script>
|
<script>
|
||||||
const { reactive,effect } = Vue
|
const { ref,effect } = Vue
|
||||||
const obj=reactive({
|
const obj=ref({
|
||||||
name:'张三'
|
name:'张三'
|
||||||
})
|
})
|
||||||
|
|
||||||
effect(()=>{
|
effect(()=>{
|
||||||
document.querySelector('#app').innerText=name
|
document.querySelector('#app').innerText=obj.value.name
|
||||||
})
|
})
|
||||||
setTimeout(()=>{
|
setTimeout(()=>{
|
||||||
obj.name='李四'
|
obj.value.name='李四'
|
||||||
},2000)
|
},2000)
|
||||||
</script>
|
</script>
|
||||||
</html>
|
</html>
|
||||||
28
packages/vue/examples/reactivity/scheduler-2.html
Normal file
28
packages/vue/examples/reactivity/scheduler-2.html
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Document</title>
|
||||||
|
<script src="../../dist/vue.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
</body>
|
||||||
|
<script>
|
||||||
|
const { reactive,effect,queuePreFlushCb } = Vue
|
||||||
|
const obj=reactive({
|
||||||
|
count:1
|
||||||
|
})
|
||||||
|
|
||||||
|
effect(()=>{
|
||||||
|
console.log(obj.count)
|
||||||
|
},{
|
||||||
|
scheduler(){
|
||||||
|
queuePreFlushCb(()=>console.log(obj.count));
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
obj.count=2
|
||||||
|
obj.count=3
|
||||||
|
</script>
|
||||||
|
</html>
|
||||||
30
packages/vue/examples/reactivity/scheduler.html
Normal file
30
packages/vue/examples/reactivity/scheduler.html
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Document</title>
|
||||||
|
<script src="../../dist/vue.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
</body>
|
||||||
|
<script>
|
||||||
|
const { reactive,effect } = Vue
|
||||||
|
const obj=reactive({
|
||||||
|
count:1
|
||||||
|
})
|
||||||
|
|
||||||
|
effect(()=>{
|
||||||
|
console.log(obj.count)
|
||||||
|
},{
|
||||||
|
scheduler(){
|
||||||
|
setTimeout(()=>{
|
||||||
|
console.log(obj.count)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
obj.count=2
|
||||||
|
|
||||||
|
console.log('代码运行结束');
|
||||||
|
</script>
|
||||||
|
</html>
|
||||||
26
packages/vue/examples/reactivity/watch.html
Normal file
26
packages/vue/examples/reactivity/watch.html
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Document</title>
|
||||||
|
<script src="../../dist/vue.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
</body>
|
||||||
|
<script>
|
||||||
|
const { reactive,watch } = Vue
|
||||||
|
const obj=reactive({
|
||||||
|
name:'张三'
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(obj,(value,oldValue)=>{
|
||||||
|
console.log('watch 监听被触发---value',value);
|
||||||
|
},{
|
||||||
|
immediate:true
|
||||||
|
})
|
||||||
|
setTimeout(()=>{
|
||||||
|
obj.name='李四'
|
||||||
|
},2000)
|
||||||
|
</script>
|
||||||
|
</html>
|
||||||
38
packages/vue/examples/runtime/h-component-ym-test.html
Normal file
38
packages/vue/examples/runtime/h-component-ym-test.html
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Document</title>
|
||||||
|
<script src="../../dist/vue.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
</body>
|
||||||
|
<script>
|
||||||
|
const { h, render } = Vue
|
||||||
|
const component = {
|
||||||
|
render() {
|
||||||
|
// const vnode1=h('div','这是一个component')
|
||||||
|
// console.log(vnode1);
|
||||||
|
// return vnode1
|
||||||
|
|
||||||
|
//直接利用当前打印的vnode , 绕过h的渲染
|
||||||
|
return {
|
||||||
|
"__v_isVNode": true,
|
||||||
|
"type": 'div',
|
||||||
|
"children": '这是一个component',
|
||||||
|
"shapeFlag": 9
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// const vnode2=h(component)
|
||||||
|
// console.log(vnode2);
|
||||||
|
|
||||||
|
const vnode2 = {
|
||||||
|
"__v_isVNode": true,
|
||||||
|
"shapeFlag": 4,
|
||||||
|
"type": component
|
||||||
|
}
|
||||||
|
render(vnode2, document.querySelector('#app'))
|
||||||
|
</script>
|
||||||
|
</html>
|
||||||
24
packages/vue/examples/runtime/h-component.html
Normal file
24
packages/vue/examples/runtime/h-component.html
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Document</title>
|
||||||
|
<script src="../../dist/vue.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
</body>
|
||||||
|
<script>
|
||||||
|
const { h } = Vue
|
||||||
|
const component = {
|
||||||
|
render() {
|
||||||
|
const vnode1=h('div','这是一个component')
|
||||||
|
console.log(vnode1);
|
||||||
|
return vnode1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const vnode2=h(component)
|
||||||
|
console.log(vnode2);
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</html>
|
||||||
22
packages/vue/examples/runtime/h-element-array.html
Normal file
22
packages/vue/examples/runtime/h-element-array.html
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Document</title>
|
||||||
|
<script src="../../dist/vue.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
</body>
|
||||||
|
<script>
|
||||||
|
const {h} =Vue
|
||||||
|
const vnode=h('div',{
|
||||||
|
class:'test'
|
||||||
|
},[
|
||||||
|
h('p','p1'),
|
||||||
|
h('p','p2'),
|
||||||
|
h('p','p3')
|
||||||
|
])
|
||||||
|
console.log(vnode)
|
||||||
|
</script>
|
||||||
|
</html>
|
||||||
28
packages/vue/examples/runtime/h-element-class.html
Normal file
28
packages/vue/examples/runtime/h-element-class.html
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Document</title>
|
||||||
|
<script src="../../dist/vue.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
</body>
|
||||||
|
<script>
|
||||||
|
const {h} =Vue
|
||||||
|
const vnode=h('div',{
|
||||||
|
class:[
|
||||||
|
{
|
||||||
|
'red':true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'pink':true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'blue':false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},'增强class')
|
||||||
|
console.log(vnode)
|
||||||
|
</script>
|
||||||
|
</html>
|
||||||
18
packages/vue/examples/runtime/h-element-text.html
Normal file
18
packages/vue/examples/runtime/h-element-text.html
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Document</title>
|
||||||
|
<script src="../../dist/vue.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
</body>
|
||||||
|
<script>
|
||||||
|
const {h} =Vue
|
||||||
|
const vnode=h('div',{
|
||||||
|
class:'test'
|
||||||
|
},'hello render')
|
||||||
|
console.log(vnode)
|
||||||
|
</script>
|
||||||
|
</html>
|
||||||
27
packages/vue/examples/runtime/h-other-ym-test.html
Normal file
27
packages/vue/examples/runtime/h-other-ym-test.html
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Document</title>
|
||||||
|
<script src="../../dist/vue.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
</body>
|
||||||
|
<script>
|
||||||
|
//Fragment : 片段 vue3一个模板中, 可以有多个根节点,就是利用这片段完成的
|
||||||
|
const { h, render, Text,Comment,Fragment } = Vue
|
||||||
|
// const vnodeText = h(Text, '这是一个Text')
|
||||||
|
// console.log(vnodeText)
|
||||||
|
|
||||||
|
// const vnodeComment=h(Comment,'这是一个comment')
|
||||||
|
// console.log(vnodeComment);
|
||||||
|
|
||||||
|
// render(vnodeComment,document.querySelector('#app'))
|
||||||
|
|
||||||
|
const vnodeFragment=h(Fragment)
|
||||||
|
|
||||||
|
console.log(vnodeFragment);
|
||||||
|
render(vnodeFragment,document.querySelector('#app'))
|
||||||
|
</script>
|
||||||
|
</html>
|
||||||
24
packages/vue/examples/runtime/h-other.html
Normal file
24
packages/vue/examples/runtime/h-other.html
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Document</title>
|
||||||
|
<script src="../../dist/vue.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
</body>
|
||||||
|
<script>
|
||||||
|
//Fragment : 片段 vue3一个模板中, 可以有多个根节点,就是利用这片段完成的
|
||||||
|
const { h, render, Text,Comment,Fragment } = Vue
|
||||||
|
const vnodeText = h(Text, '这是一个Text')
|
||||||
|
console.log(vnodeText)
|
||||||
|
|
||||||
|
const vnodeComment=h(Comment,'这是一个comment')
|
||||||
|
console.log(vnodeComment);
|
||||||
|
|
||||||
|
|
||||||
|
const vnodeFragment=h(Fragment)
|
||||||
|
console.log(vnodeFragment);
|
||||||
|
</script>
|
||||||
|
</html>
|
||||||
@@ -1 +1,9 @@
|
|||||||
export { reactive, effect, ref } from '@vue/reactivity'
|
export { reactive, effect, ref, computed } from '@vue/reactivity'
|
||||||
|
export {
|
||||||
|
queuePreFlushCb,
|
||||||
|
watch,
|
||||||
|
h,
|
||||||
|
Fragment,
|
||||||
|
Text,
|
||||||
|
Comment
|
||||||
|
} from '@vue/runtime-core'
|
||||||
|
|||||||
Reference in New Issue
Block a user