Compare commits
19 Commits
72a4bec26a
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 026f4a5958 | |||
| abf8112359 | |||
| 6aa564ef7c | |||
|
|
61edc322f2 | ||
| 4225ae0b8f | |||
| a3a7b8c7e4 | |||
|
|
164bae388f | ||
| b9a9c52333 | |||
| 4a71105e28 | |||
| ba03278947 | |||
|
|
1f50ab1c84 | ||
|
|
62e40e7292 | ||
| c07431db08 | |||
| 4c60486511 | |||
| c0853b353d | |||
| 2ed86a03b5 | |||
| 6b7b452a56 | |||
| 9aad0f6c74 | |||
| 7fc2292d4b |
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 { 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>
|
||||
/**
|
||||
@@ -11,20 +13,35 @@ type KeyToDepMap = Map<any, Dep>
|
||||
*/
|
||||
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)
|
||||
_effect.run()
|
||||
if (options) {
|
||||
extend(_effect, options)
|
||||
}
|
||||
|
||||
if (!options || !options.lazy) {
|
||||
_effect.run()
|
||||
}
|
||||
}
|
||||
|
||||
export let activeEffect: ReactiveEffect | undefined
|
||||
|
||||
export class ReactiveEffect<T = any> {
|
||||
constructor(public fn: () => T) {}
|
||||
computed?: ComputedRefImpl<T>
|
||||
constructor(
|
||||
public fn: () => T,
|
||||
public scheduler: EffectScheduler | null = null
|
||||
) {}
|
||||
run() {
|
||||
//当前被激活的effect实例
|
||||
activeEffect = this
|
||||
return this.fn() //完成第一次getter行为的触发
|
||||
}
|
||||
stop() {}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,14 +93,26 @@ export function trigger(target: object, key: unknown, newValue: unknown) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 依次触发dep中保存的依赖
|
||||
* 触发dep中保存的依赖
|
||||
* @param dep
|
||||
*/
|
||||
export function triggerEffects(dep: Dep) {
|
||||
//把dep构建为一个数组
|
||||
const effects = isArray(dep) ? dep : [...dep]
|
||||
//依次触发依赖
|
||||
//不再依次触发依赖
|
||||
// for (const effect of effects) {
|
||||
// triggerEffect(effect)
|
||||
// }
|
||||
//而是先触发所有的计算属性依赖, 再触发所有的非计算属性依赖
|
||||
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
|
||||
*/
|
||||
export function triggerEffect(effect: ReactiveEffect) {
|
||||
effect.run()
|
||||
if (effect.scheduler) {
|
||||
effect.scheduler()
|
||||
} else {
|
||||
effect.run()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
export { reactive } from './reactive'
|
||||
export { effect } from './effect'
|
||||
export { reactive, isReactive } from './reactive'
|
||||
export { effect, ReactiveEffect } from './effect'
|
||||
export { ref } from './ref'
|
||||
export { computed } from './computed'
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { mutableHandlers } from './baseHandlers'
|
||||
import { isObject } from '@vue/shared'
|
||||
|
||||
/**
|
||||
* 响应性Map缓存对象
|
||||
@@ -7,6 +8,10 @@ import { mutableHandlers } from './baseHandlers'
|
||||
*/
|
||||
export const reactiveMap = new WeakMap<object, any>()
|
||||
|
||||
export const enum ReactiveFlags {
|
||||
IS_REACTIVE = '__v_isReactive'
|
||||
}
|
||||
|
||||
/**
|
||||
* 为复杂数据类型, 创建响应式对象
|
||||
* @param target 被代理的对象
|
||||
@@ -34,8 +39,16 @@ function createReactiveObject(
|
||||
}
|
||||
//未被代理则生成proxy实例
|
||||
const proxy = new Proxy(target, baseHandlers)
|
||||
|
||||
//添加reactive标识
|
||||
proxy[ReactiveFlags.IS_REACTIVE] = true
|
||||
//缓存代理对象
|
||||
proxyMap.set(target, proxy)
|
||||
return proxy
|
||||
}
|
||||
|
||||
export const toReactive = <T extends unknown>(value: T): T =>
|
||||
isObject(value) ? reactive(value as object) : value
|
||||
|
||||
export function isReactive(value): boolean {
|
||||
return !!(value && value[ReactiveFlags.IS_REACTIVE])
|
||||
}
|
||||
|
||||
69
packages/reactivity/src/ref.ts
Normal file
69
packages/reactivity/src/ref.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { createDep, Dep } from './dep'
|
||||
import { toReactive } from './reactive'
|
||||
import { activeEffect, triggerEffects, trackEffects } from './effect'
|
||||
import { hasChanged } from '@vue/shared'
|
||||
|
||||
export interface Ref<T = any> {
|
||||
value: T
|
||||
}
|
||||
|
||||
export function ref(value?: unknown) {
|
||||
return createRef(value, false)
|
||||
}
|
||||
function createRef(rawValue: unknown, shallow: boolean) {
|
||||
if (isRef(rawValue)) {
|
||||
return rawValue
|
||||
}
|
||||
return new RefImpl(rawValue, shallow)
|
||||
}
|
||||
class RefImpl<T> {
|
||||
private _value: T
|
||||
private _rawValue: T
|
||||
public dep?: Dep = undefined
|
||||
public readonly __v_isRef = true
|
||||
constructor(
|
||||
value: T,
|
||||
public readonly __v_isShallow: boolean
|
||||
) {
|
||||
//原始数据
|
||||
this._rawValue = value
|
||||
this._value = __v_isShallow ? value : toReactive(value)
|
||||
}
|
||||
get value() {
|
||||
trackRefValue(this)
|
||||
return this._value
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
if (activeEffect) {
|
||||
trackEffects(ref.dep || (ref.dep = createDep()))
|
||||
}
|
||||
}
|
||||
//是否为ref
|
||||
export function isRef(r: any): r is Ref {
|
||||
return !!(r && r.__v_isRef === true)
|
||||
}
|
||||
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,2 +1,25 @@
|
||||
export { ShapeFlags } from './shapeFlags'
|
||||
export { normalizeClass } from './normalizeProp'
|
||||
//判断是否为一个数组
|
||||
export const isArray = Array.isArray
|
||||
|
||||
export const isObject = (val: unknown) =>
|
||||
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>
|
||||
@@ -1,19 +0,0 @@
|
||||
<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">
|
||||
<p id="p1"></p>
|
||||
<p id="p2"></p>
|
||||
</div>
|
||||
</body>
|
||||
<script>
|
||||
const { reactive } = Vue
|
||||
const obj=reactive('张三')//不能接收简单数据类型
|
||||
console.log(obj)
|
||||
</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>
|
||||
</body>
|
||||
<script>
|
||||
const { reactive,effect } = Vue
|
||||
const obj=reactive({
|
||||
const { ref,effect } = Vue
|
||||
const obj=ref({
|
||||
name:'张三'
|
||||
})
|
||||
|
||||
effect(()=>{
|
||||
document.querySelector('#app').innerText=name
|
||||
document.querySelector('#app').innerText=obj.value.name
|
||||
})
|
||||
setTimeout(()=>{
|
||||
obj.name='李四'
|
||||
obj.value.name='李四'
|
||||
},2000)
|
||||
</script>
|
||||
</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 } 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