哈喽,大家好!我是陈坚强。一个有代码洁癖的前端攻城狮( 哈哈,生活很邋遢(* ̄︶ ̄) )
前言
作为一名优秀的前端攻城狮(自吹一波),我想说Vue
是我们中国程序员的骄傲,是尤雨溪大神呕心沥血的杰作,Vue.js
正式发布于2014
年2月,对于目前的Vue.js
的在开发人数上,总共有 370
多构建者。在受关注程度上,目前行业的黑话:不会Vue
的前端不是合格的前端工程师!
由于对技术的追求、热爱,和好奇心的驱使,今天也对Vue3.0
尝了尝鲜。果断将本地的@vue/cli
更新一下
npm i @vue/cli@latest -g
之后,vue create vue3_demo
就会提示可以选择vue
的版本,肯定是选择vue3.0
了,赶紧把项目距跑起来,做了一个特别的操作,😬
import * as vue from 'vue';
console.log(vue)
那么vue
里面到底有什么呢?让我们来看看
{
BaseTransition: Object,
Comment: Symbol(Comment),
Fragment: Symbol(Fragment),
KeepAlive: Object,
Static: Symbol(Static),
Suspense: Object,
Teleport: Object,
Text: Symbol(Text)
Transition: (props, { slots }) => {…},
TransitionGroup: Object,
callWithAsyncErrorHandling: ƒ callWithAsyncErrorHandling(fn, instance, type, args),
callWithErrorHandling: ƒ callWithErrorHandling(fn, instance, type, args),
camelize: (str) => {…},
capitalize: (str) => {…},
cloneVNode: ƒ cloneVNode(vnode, extraProps, mergeRef = false),
compile: () => {…},
computed: ƒ computed(getterOrOptions),
createApp: (...args) => {…},
createBlock: ƒ createBlock(type, props, children, patchFlag, dynamicProps),
createCommentVNode: ƒ createCommentVNode(text = ''){},
createHydrationRenderer: ƒ createHydrationRenderer(options),
createRenderer: ƒ createRenderer(options),
createSSRApp: (...args) => {…},
createSlots: ƒ createSlots(slots, dynamicSlots),
createStaticVNode: ƒ createStaticVNode(content, numberOfNodes),
createTextVNode: ƒ createTextVNode(text = ' ', flag = 0),
createVNode: (...args) => {…},
customRef: ƒ customRef(factory),
defineAsyncComponent: ƒ defineAsyncComponent(source),
defineComponent: ƒ defineComponent(options),
devtools: Object,
getCurrentInstance: () => currentInstance || currentRenderingInstance,
getTransitionRawChildren: ƒ getTransitionRawChildren(children, keepComment = false),
h: ƒ h(type, propsOrChildren, children),
handleError: ƒ handleError(err, instance, type, throwInDev = true),
hydrate: (...args) => {…},
inject: ƒ inject(key, defaultValue, treatDefaultAsFactory = false),
isProxy: ƒ isProxy(value),
isReactive: ƒ isReactive(value),
isReadonly: ƒ isReadonly(value),
isRef: ƒ isRef(r),
isVNode: ƒ isVNode(value),
markRaw: ƒ markRaw(value),
mergeProps: ƒ mergeProps(...args),
nextTick: ƒ nextTick(fn),
onActivated: ƒ onActivated(hook, target),
onBeforeMount: (hook, target = currentInstance) => {…},
onBeforeUnmount: (hook, target = currentInstance) => {…},
onBeforeUpdate: (hook, target = currentInstance) => {…},
onDeactivated: ƒ onDeactivated(hook, target),
onErrorCaptured: (hook, target = currentInstance) => {…},
onMounted: (hook, target = currentInstance) => {…},
onRenderTracked: (hook, target = currentInstance) => {…},
onRenderTriggered: (hook, target = currentInstance) => {…},
onUnmounted: (hook, target = currentInstance) => {…},
onUpdated: (hook, target = currentInstance) => {…},
openBlock: ƒ openBlock(disableTracking = false),
popScopeId: ƒ popScopeId(),
provide: ƒ provide(key, value),
proxyRefs: ƒ proxyRefs(objectWithRefs),
pushScopeId: (...),
queuePostFlushCb: (...),
reactive: (...),
readonly: (...),
ref: (...),
registerRuntimeCompiler: (...),
render: (...),
renderList: (...),
renderSlot: (...),
resolveComponent: (...),
resolveDirective: (...),
resolveDynamicComponent: (...),
resolveTransitionHooks: (...),
setBlockTracking: (...),
setDevtoolsHook: (...),
setTransitionHooks: (...),
shallowReactive: (...),
shallowReadonly: (...),
shallowRef: (...),
ssrContextKey: (...),
ssrUtils: (...),
toDisplayString: (...),
toHandlers: (...),
toRaw: (...),
toRef: (...),
toRefs: (...),
transformVNodeArgs: (...),
triggerRef: (...),
unref: (...),
useCssModule: (...),
useCssVars: (...),
useSSRContext: (...),
useTransitionState: (...),
vModelCheckbox: (...),
vModelDynamic: (...),
vModelRadio: (...),
vModelSelect: (...),
vModelText: (...),
vShow: (...),
version: (...),
warn: (...),
watch: (...),
watchEffect: (...),
withCtx: (...),
withDirectives: (...),
withKeys: (...),
withModifiers: (...),
withScopeId: (...)
}
于是乎,赶紧去看了看node_modules
下的vue
到底有什么
各模块规范的vue.js
,具体核心模块还是在node_modules/@vue
下
看图比较直观一些,下面我们来看一下,这些模块里都有什么,
@vue/reactivity
reactive
接收一个普通对象然后返回该普通对象的响应式代理
源码相关函数
function reactive(target) {
// if trying to observe a readonly proxy, return the readonly version.
if (target && target["__v_isReadonly" /* IS_READONLY */]) {
return target;
}
return createReactiveObject(target, false, mutableHandlers, mutableCollectionHandlers);
}
function createReactiveObject(target, isReadonly, baseHandlers, collectionHandlers) {
if (!shared.isObject(target)) {
{
console.warn(`value cannot be made reactive: ${String(target)}`);
}
return target;
}
// target is already a Proxy, return it.
// exception: calling readonly() on a reactive object
if (target["__v_raw" /* RAW */] &&
!(isReadonly && target["__v_isReactive" /* IS_REACTIVE */])) {
return target;
}
// target already has corresponding Proxy
const proxyMap = isReadonly ? readonlyMap : reactiveMap;
const existingProxy = proxyMap.get(target);
if (existingProxy) {
return existingProxy;
}
// only a whitelist of value types can be observed.
const targetType = getTargetType(target);
if (targetType === 0 /* INVALID */) {
return target;
}
const proxy = new Proxy(target, targetType === 2 /* COLLECTION */ ? collectionHandlers : baseHandlers);
proxyMap.set(target, proxy);
return proxy;
}
简单使用
readonly
获取一个对象 (响应式或纯对象) 或 ref 并返回原始代理的只读代理。只读代理是深层的:访问的任何嵌套 property 也是只读的。
源码相关函数
function readonly(target) {
return createReactiveObject(target, true, readonlyHandlers, readonlyCollectionHandlers);
}
function createReactiveObject(target, isReadonly, baseHandlers, collectionHandlers) {
if (!shared.isObject(target)) {
{
console.warn(`value cannot be made reactive: ${String(target)}`);
}
return target;
}
// target is already a Proxy, return it.
// exception: calling readonly() on a reactive object
if (target["__v_raw" /* RAW */] &&
!(isReadonly && target["__v_isReactive" /* IS_REACTIVE */])) {
return target;
}
// target already has corresponding Proxy
const proxyMap = isReadonly ? readonlyMap : reactiveMap;
const existingProxy = proxyMap.get(target);
if (existingProxy) {
return existingProxy;
}
// only a whitelist of value types can be observed.
const targetType = getTargetType(target);
if (targetType === 0 /* INVALID */) {
return target;
}
const proxy = new Proxy(target, targetType === 2 /* COLLECTION */ ? collectionHandlers : baseHandlers);
proxyMap.set(target, proxy);
return proxy;
}
简单使用
ref
接受参数并返回它包装在具有
value
property
的对象中,然后可以使用该property
访问或更改响应式变量的值,同时也可以作用的组件或者元素上
源码相关函数
function ref(value) {
return createRef(value);
}
function createRef(rawValue, shallow = false) {
if (isRef(rawValue)) {
return rawValue;
}
return new RefImpl(rawValue, shallow);
}
class RefImpl {
constructor(_rawValue, _shallow = false) {
this._rawValue = _rawValue;
this._shallow = _shallow;
this.__v_isRef = true;
this._value = _shallow ? _rawValue : convert(_rawValue);
}
get value() {
track(toRaw(this), "get" /* GET */, 'value');
return this._value;
}
set value(newVal) {
if (shared.hasChanged(toRaw(newVal), this._rawValue)) {
this._rawValue = newVal;
this._value = this._shallow ? newVal : convert(newVal);
trigger(toRaw(this), "set" /* SET */, 'value', newVal);
}
}
}
简单使用
toRef
可以用来为源响应式对象上的
property
性创建一个ref
。然后可以将ref
传递出去,从而保持对其源property
的响应式连接。
源码相关函数
function toRef(object, key) {
return isRef(object[key])
? object[key]
: new ObjectRefImpl(object, key);
}
class ObjectRefImpl {
constructor(_object, _key) {
this._object = _object;
this._key = _key;
this.__v_isRef = true;
}
get value() {
return this._object[this._key];
}
set value(newVal) {
this._object[this._key] = newVal;
}
}
简单使用
toRefs
将响应式对象转换为普通对象,其中结果对象的每个
property
都是指向原始对象相应property
的ref
。
源码相关函数
function toRefs(object) {
if ( !isProxy(object)) {
console.warn(`toRefs() expects a reactive object but received a plain one.`);
}
const ret = shared.isArray(object) ? new Array(object.length) : {};
for (const key in object) {
ret[key] = toRef(object, key);
}
return ret;
}
简单使用
computed
与
ref
和watch
类似,也可以使用从Vue
导入的computed
函数在Vue
组件外部创建计算属性。
源码相关函数
function computed(getterOrOptions) {
let getter;
let setter;
if (shared.isFunction(getterOrOptions)) {
getter = getterOrOptions;
setter = () => {
console.warn('Write operation failed: computed value is readonly');
}
;
}
else {
getter = getterOrOptions.get;
setter = getterOrOptions.set;
}
return new ComputedRefImpl(getter, setter, shared.isFunction(getterOrOptions) || !getterOrOptions.set);
}
class ComputedRefImpl {
constructor(getter, _setter, isReadonly) {
this._setter = _setter;
this._dirty = true;
this.__v_isRef = true;
this.effect = effect(getter, {
lazy: true,
scheduler: () => {
if (!this._dirty) {
this._dirty = true;
trigger(toRaw(this), "set" /* SET */, 'value');
}
}
});
this["__v_isReadonly" /* IS_READONLY */] = isReadonly;
}
get value() {
if (this._dirty) {
this._value = this.effect();
this._dirty = false;
}
track(toRaw(this), "get" /* GET */, 'value');
return this._value;
}
set value(newValue) {
this._setter(newValue);
}
}
简单使用
customRef
创建一个自定义的
ref
,并对其依赖项跟踪和更新触发进行显式控制。它需要一个工厂函数,该函数接收track
和trigger
函数作为参数,并应返回一个带有get
和set
的对象。
源码相关函数
function customRef(factory) {
return new CustomRefImpl(factory);
}
class CustomRefImpl {
constructor(factory) {
this.__v_isRef = true;
const { get, set } = factory(() => track(this, "get" /* GET */, 'value'), () => trigger(this, "set" /* SET */, 'value'));
this._get = get;
this._set = set;
}
get value() {
return this._get();
}
set value(newVal) {
this._set(newVal);
}
}
简单使用
toRaw
返回
reactive
或readonly
代理的原始对象。这是一个转义口,可用于临时读取而不会引起代理访问/跟踪开销,也可用于写入而不会触发更改。不建议保留对原始对象的持久引用。请谨慎使用。 源码相关函数
function toRaw(observed) {
return ((observed && toRaw(observed["__v_raw" /* RAW */])) || observed);
}
简单使用
shallowReactive
/shallowReadonly
/shallowRef
shallowReactive
创建一个响应式代理,该代理跟踪其自身property
的响应性,但不执行嵌套对象的深度响应式转换 (暴露原始值)。
shallowReadonly
创建一个代理,使其自身的 property 为只读,但不执行嵌套对象的深度只读转换 (暴露原始值)。
shallowRef
创建一个 ref,它跟踪自己的 .value 更改,但不会使其值成为响应式的。
源码相关函数
function shallowReactive(target) {
return createReactiveObject(target, false, shallowReactiveHandlers, shallowCollectionHandlers);
}
function shallowReadonly(target) {
return createReactiveObject(target, true, shallowReadonlyHandlers, readonlyCollectionHandlers);
}
function shallowRef(value) {
return createRef(value, true);
}
//createReactiveObject 上文已提,创建一个响应式对象
//createRef 上文已提,创建一个ref对象
简单使用
@vue/runtime-core
watch
/watchEffect
watch
需要侦听特定的data
(响应式) 源,并在单独的回调函数中副作用。默认情况下,它也是惰性的——即,回调是仅在侦听源发生更改时调用。
watchEffect
在响应式地跟踪其依赖项时立即运行一个函数,并在更改依赖项时重新运行它。
源码相关函数
function watch(source, cb, options) {
if ( !shared.isFunction(cb)) {
warn(`\`watch(fn, options?)\` signature has been moved to a separate API. ` +
`Use \`watchEffect(fn, options?)\` instead. \`watch\` now only ` +
`supports \`watch(source, cb, options?) signature.`);
}
return doWatch(source, cb, options);
}
function watchEffect(effect, options) {
return doWatch(effect, null, options);
}
function doWatch(source, cb, { immediate, deep, flush, onTrack, onTrigger } = shared.EMPTY_OBJ, instance = currentInstance) {
if ( !cb) {
if (immediate !== undefined) {
warn(`watch() "immediate" option is only respected when using the ` +
`watch(source, callback, options?) signature.`);
}
if (deep !== undefined) {
warn(`watch() "deep" option is only respected when using the ` +
`watch(source, callback, options?) signature.`);
}
}
const warnInvalidSource = (s) => {
warn(`Invalid watch source: `, s, `A watch source can only be a getter/effect function, a ref, ` +
`a reactive object, or an array of these types.`);
};
let getter;
const isRefSource = reactivity.isRef(source);
if (isRefSource) {
getter = () => source.value;
}
else if (reactivity.isReactive(source)) {
getter = () => source;
deep = true;
}
else if (shared.isArray(source)) {
getter = () => source.map(s => {
if (reactivity.isRef(s)) {
return s.value;
}
else if (reactivity.isReactive(s)) {
return traverse(s);
}
else if (shared.isFunction(s)) {
return callWithErrorHandling(s, instance, 2 /* WATCH_GETTER */);
}
else {
warnInvalidSource(s);
}
});
}
else if (shared.isFunction(source)) {
if (cb) {
// getter with cb
getter = () => callWithErrorHandling(source, instance, 2 /* WATCH_GETTER */);
}
else {
// no cb -> simple effect
getter = () => {
if (instance && instance.isUnmounted) {
return;
}
if (cleanup) {
cleanup();
}
return callWithErrorHandling(source, instance, 3 /* WATCH_CALLBACK */, [onInvalidate]);
};
}
}
else {
getter = shared.NOOP;
warnInvalidSource(source);
}
if (cb && deep) {
const baseGetter = getter;
getter = () => traverse(baseGetter());
}
let cleanup;
const onInvalidate = (fn) => {
cleanup = runner.options.onStop = () => {
callWithErrorHandling(fn, instance, 4 /* WATCH_CLEANUP */);
};
};
// in SSR there is no need to setup an actual effect, and it should be noop
// unless it's eager
if ( isInSSRComponentSetup) {
if (!cb) {
getter();
}
else if (immediate) {
callWithAsyncErrorHandling(cb, instance, 3 /* WATCH_CALLBACK */, [
getter(),
undefined,
onInvalidate
]);
}
return shared.NOOP;
}
let oldValue = shared.isArray(source) ? [] : INITIAL_WATCHER_VALUE;
const job = () => {
if (!runner.active) {
return;
}
if (cb) {
// watch(source, cb)
const newValue = runner();
if (deep || isRefSource || shared.hasChanged(newValue, oldValue)) {
// cleanup before running cb again
if (cleanup) {
cleanup();
}
callWithAsyncErrorHandling(cb, instance, 3 /* WATCH_CALLBACK */, [
newValue,
// pass undefined as the old value when it's changed for the first time
oldValue === INITIAL_WATCHER_VALUE ? undefined : oldValue,
onInvalidate
]);
oldValue = newValue;
}
}
else {
// watchEffect
runner();
}
};
// important: mark the job as a watcher callback so that scheduler knows it
// it is allowed to self-trigger (#1727)
job.allowRecurse = !!cb;
let scheduler;
if (flush === 'sync') {
scheduler = job;
}
else if (flush === 'post') {
scheduler = () => queuePostRenderEffect(job, instance && instance.suspense);
}
else {
// default: 'pre'
scheduler = () => {
if (!instance || instance.isMounted) {
queuePreFlushCb(job);
}
else {
// with 'pre' option, the first call must happen before
// the component is mounted so it is called synchronously.
job();
}
};
}
const runner = reactivity.effect(getter, {
lazy: true,
onTrack,
onTrigger,
scheduler
});
recordInstanceBoundEffect(runner);
// initial run
if (cb) {
if (immediate) {
job();
}
else {
oldValue = runner();
}
}
else if (flush === 'post') {
queuePostRenderEffect(runner, instance && instance.suspense);
}
else {
runner();
}
return () => {
reactivity.stop(runner);
if (instance) {
shared.remove(instance.effects, runner);
}
};
}
简单使用
nextTick
将回调推迟到下一个 DOM 更新周期之后执行。在更改了一些数据以等待 DOM 更新后立即使用它。
源码相关函数
function nextTick(fn) {
const p = currentFlushPromise || resolvedPromise;
return fn ? p.then(fn) : p;
}
$nextTick: () => nextTick,
简单使用
provide
/inject
provide
和inject
启用依赖注入。只有在使用当前活动实例的 setup() 期间才能调用这两者。
源码相关函数
function provide(key, value) {
if (!currentInstance) {
{
warn(`provide() can only be used inside setup().`);
}
}
else {
let provides = currentInstance.provides;
// by default an instance inherits its parent's provides object
// but when it needs to provide values of its own, it creates its
// own provides object using parent provides object as prototype.
// this way in `inject` we can simply look up injections from direct
// parent and let the prototype chain do the work.
const parentProvides = currentInstance.parent && currentInstance.parent.provides;
if (parentProvides === provides) {
provides = currentInstance.provides = Object.create(parentProvides);
}
// TS doesn't allow symbol as index type
provides[key] = value;
}
}
function inject(key, defaultValue, treatDefaultAsFactory = false) {
// fallback to `currentRenderingInstance` so that this can be called in
// a functional component
const instance = currentInstance || currentRenderingInstance;
if (instance) {
const provides = instance.provides;
if (key in provides) {
// TS doesn't allow symbol as index type
return provides[key];
}
else if (arguments.length > 1) {
return treatDefaultAsFactory && shared.isFunction(defaultValue)
? defaultValue()
: defaultValue;
}
else {
warn(`injection "${String(key)}" not found.`);
}
}
else {
warn(`inject() can only be used inside setup() or functional components.`);
}
}
简单使用
getCurrentInstance
获取当前组件的实例,类似于2.0的this
源码相关函数
const getCurrentInstance = () => currentInstance || currentRenderingInstance;
简单使用
@vue/runtime-dom
createApp
/createSSRApp
返回一个提供应用上下文的应用实例。应用实例挂载的整个组件树共享同一个上下文。
源码相关函数
const createApp = ((...args) => {
const app = ensureRenderer().createApp(...args);
{
injectNativeTagCheck(app);
}
const { mount } = app;
app.mount = (containerOrSelector) => {
const container = normalizeContainer(containerOrSelector);
if (!container)
return;
const component = app._component;
if (!shared.isFunction(component) && !component.render && !component.template) {
component.template = container.innerHTML;
}
// clear content before mounting
container.innerHTML = '';
const proxy = mount(container);
container.removeAttribute('v-cloak');
container.setAttribute('data-v-app', '');
return proxy;
};
return app;
});
const createSSRApp = ((...args) => {
const app = ensureHydrationRenderer().createApp(...args);
{
injectNativeTagCheck(app);
}
const { mount } = app;
app.mount = (containerOrSelector) => {
const container = normalizeContainer(containerOrSelector);
if (container) {
return mount(container, true);
}
};
return app;
});
简单使用
结束语
以上是对vue3.0
的学习总结,vue2.0
与vue3.0
最明显的区别就是从options-api
到 composition-api
。更方便利于生产环境的tree shaking
,当然肯定不止这一点
Vue3.0
的文章很多,希望与大家一起学习。写的不好大家多多指正。如果对你有帮助,请不要吝啬赞👍
文章推荐: