Compare commits

...

5 Commits

Author SHA1 Message Date
neverland
8d9ccfd8c7
fix(@vant/use): keep forward compatible (#9592) 2021-09-29 18:24:31 +08:00
neverland
95ffe9bc19
perf(@vant/lazyload): reuse some utils (#9591) 2021-09-29 18:11:04 +08:00
neverland
ec5067ce7d
perf: remove passive event polyfill (#9590) 2021-09-29 17:36:50 +08:00
neverland
7aba794d80
perf(@vant/lazyload): remove CustomEvent polyfill (#9589) 2021-09-29 17:31:05 +08:00
neverland
e8186795b7
perf: no longer need transform prefix (#9588)
* perf: no longer need transform prefix

* chore: upd

* chore: upd
2021-09-29 17:04:03 +08:00
15 changed files with 91 additions and 235 deletions

View File

@ -15,9 +15,6 @@ function findRootDir(dir: string): string {
return findRootDir(parentDir); return findRootDir(parentDir);
} }
// Colors
export const GREEN = '#07c160';
// Root paths // Root paths
export const CWD = process.cwd(); export const CWD = process.cwd();
export const ROOT = findRootDir(CWD); export const ROOT = findRootDir(CWD);
@ -29,7 +26,6 @@ export const SITE_DIST_DIR = join(ROOT, 'site-dist');
export const VANT_CONFIG_FILE = join(ROOT, 'vant.config.js'); export const VANT_CONFIG_FILE = join(ROOT, 'vant.config.js');
export const PACKAGE_JSON_FILE = join(ROOT, 'package.json'); export const PACKAGE_JSON_FILE = join(ROOT, 'package.json');
export const ROOT_POSTCSS_CONFIG_FILE = join(ROOT, 'postcss.config.js'); export const ROOT_POSTCSS_CONFIG_FILE = join(ROOT, 'postcss.config.js');
export const CACHE_DIR = join(ROOT, 'node_modules/.cache');
// Relative paths // Relative paths
export const DIST_DIR = join(__dirname, '../../dist'); export const DIST_DIR = join(__dirname, '../../dist');
@ -47,7 +43,6 @@ export const SITE_DESKTOP_SHARED_FILE = join(
export const STYLE_DEPS_JSON_FILE = join(DIST_DIR, 'style-deps.json'); export const STYLE_DEPS_JSON_FILE = join(DIST_DIR, 'style-deps.json');
// Config files // Config files
export const BABEL_CONFIG_FILE = join(CONFIG_DIR, 'babel.config.js');
export const POSTCSS_CONFIG_FILE = join(CONFIG_DIR, 'postcss.config.js'); export const POSTCSS_CONFIG_FILE = join(CONFIG_DIR, 'postcss.config.js');
export const JEST_SETUP_FILE = join(CONFIG_DIR, 'jest.setup.js'); export const JEST_SETUP_FILE = join(CONFIG_DIR, 'jest.setup.js');
export const JEST_CONFIG_FILE = join(CONFIG_DIR, 'jest.config.js'); export const JEST_CONFIG_FILE = join(CONFIG_DIR, 'jest.config.js');

View File

@ -25,6 +25,9 @@
"@babel/core": "^7.12.9", "@babel/core": "^7.12.9",
"release-it": "^14.2.2" "release-it": "^14.2.2"
}, },
"dependencies": {
"@vant/use": "^1.3.1"
},
"release-it": { "release-it": {
"git": { "git": {
"tag": false, "tag": false,

View File

@ -1,5 +1,5 @@
import { h } from 'vue'; import { h } from 'vue';
import { inBrowser } from './util'; import { inBrowser } from '@vant/use';
export default (lazy) => ({ export default (lazy) => ({
props: { props: {

View File

@ -1,5 +1,6 @@
/* eslint-disable max-classes-per-file */ /* eslint-disable max-classes-per-file */
import { find, remove } from './util'; /* eslint-disable prefer-object-spread */
import { remove } from './util';
const defaultOptions = { const defaultOptions = {
selector: 'img', selector: 'img',
@ -74,13 +75,13 @@ export default class LazyContainerManager {
} }
update(el, binding, vnode) { update(el, binding, vnode) {
const container = find(this._queue, (item) => item.el === el); const container = this._queue.find((item) => item.el === el);
if (!container) return; if (!container) return;
container.update({ el, binding, vnode }); container.update({ el, binding, vnode });
} }
unbind(el) { unbind(el) {
const container = find(this._queue, (item) => item.el === el); const container = this._queue.find((item) => item.el === el);
if (!container) return; if (!container) return;
container.clear(); container.clear();
remove(this._queue, container); remove(this._queue, container);

View File

@ -1,4 +1,5 @@
import { inBrowser, loadImageAsync, noop } from './util'; import { inBrowser } from '@vant/use';
import { loadImageAsync, noop } from './util';
export default (lazyManager) => ({ export default (lazyManager) => ({
props: { props: {

View File

@ -1,14 +1,12 @@
import { nextTick } from 'vue'; import { nextTick } from 'vue';
import { inBrowser, getScrollParent } from '@vant/use';
import { import {
inBrowser,
CustomEvent,
remove, remove,
find, on,
_, off,
throttle, throttle,
supportWebp, supportWebp,
getDPR, getDPR,
scrollParent,
getBestSelectionFromSrcset, getBestSelectionFromSrcset,
isObject, isObject,
hasIntersectionObserver, hasIntersectionObserver,
@ -148,7 +146,7 @@ export default function () {
} }
if (!$parent) { if (!$parent) {
$parent = scrollParent(el); $parent = getScrollParent(el);
} }
const newListener = new ReactiveListener({ const newListener = new ReactiveListener({
@ -187,7 +185,7 @@ export default function () {
let { src } = value; let { src } = value;
src = getBestSelectionFromSrcset(el, this.options.scale) || src; src = getBestSelectionFromSrcset(el, this.options.scale) || src;
const exist = find(this.ListenerQueue, (item) => item.el === el); const exist = this.ListenerQueue.find((item) => item.el === el);
if (!exist) { if (!exist) {
this.add(el, binding, vnode); this.add(el, binding, vnode);
} else { } else {
@ -213,7 +211,7 @@ export default function () {
remove(el) { remove(el) {
if (!el) return; if (!el) return;
this._observer && this._observer.unobserve(el); this._observer && this._observer.unobserve(el);
const existItem = find(this.ListenerQueue, (item) => item.el === el); const existItem = this.ListenerQueue.find((item) => item.el === el);
if (existItem) { if (existItem) {
this._removeListenerTarget(existItem.$parent); this._removeListenerTarget(existItem.$parent);
this._removeListenerTarget(window); this._removeListenerTarget(window);
@ -274,7 +272,7 @@ export default function () {
*/ */
_addListenerTarget(el) { _addListenerTarget(el) {
if (!el) return; if (!el) return;
let target = find(this.TargetQueue, (target) => target.el === el); let target = this.TargetQueue.find((target) => target.el === el);
if (!target) { if (!target) {
target = { target = {
el, el,
@ -316,7 +314,7 @@ export default function () {
*/ */
_initListen(el, start) { _initListen(el, start) {
this.options.ListenEvents.forEach((evt) => this.options.ListenEvents.forEach((evt) =>
_[start ? 'on' : 'off'](el, evt, this.lazyLoadHandler) (start ? on : off)(el, evt, this.lazyLoadHandler)
); );
} }

View File

@ -32,7 +32,6 @@ export default class ReactiveListener {
this.elRenderer = elRenderer; this.elRenderer = elRenderer;
this._imageCache = imageCache; this._imageCache = imageCache;
this.performanceData = { this.performanceData = {
init: Date.now(),
loadStart: 0, loadStart: 0,
loadEnd: 0, loadEnd: 0,
}; };

View File

@ -1,4 +1,4 @@
const inBrowser = typeof window !== 'undefined'; import { inBrowser } from '@vant/use';
export const hasIntersectionObserver = export const hasIntersectionObserver =
inBrowser && inBrowser &&
@ -11,32 +11,13 @@ export const modeType = {
observer: 'observer', observer: 'observer',
}; };
// CustomEvent polyfill export function remove(arr, item) {
const CustomEvent = (function () {
if (!inBrowser) return;
if (typeof window.CustomEvent === 'function') return window.CustomEvent;
function CustomEvent(event, params) {
params = params || { bubbles: false, cancelable: false, detail: undefined };
const evt = document.createEvent('CustomEvent');
evt.initCustomEvent(
event,
params.bubbles,
params.cancelable,
params.detail
);
return evt;
}
CustomEvent.prototype = window.Event.prototype;
return CustomEvent;
})();
function remove(arr, item) {
if (!arr.length) return; if (!arr.length) return;
const index = arr.indexOf(item); const index = arr.indexOf(item);
if (index > -1) return arr.splice(index, 1); if (index > -1) return arr.splice(index, 1);
} }
function getBestSelectionFromSrcset(el, scale) { export function getBestSelectionFromSrcset(el, scale) {
if (el.tagName !== 'IMG' || !el.getAttribute('data-srcset')) return; if (el.tagName !== 'IMG' || !el.getAttribute('data-srcset')) return;
let options = el.getAttribute('data-srcset'); let options = el.getAttribute('data-srcset');
@ -101,21 +82,10 @@ function getBestSelectionFromSrcset(el, scale) {
return bestSelectedSrc; return bestSelectedSrc;
} }
function find(arr, fn) { export const getDPR = (scale = 1) =>
let item;
for (let i = 0, len = arr.length; i < len; i++) {
if (fn(arr[i])) {
item = arr[i];
break;
}
}
return item;
}
const getDPR = (scale = 1) =>
inBrowser ? window.devicePixelRatio || scale : scale; inBrowser ? window.devicePixelRatio || scale : scale;
function supportWebp() { export function supportWebp() {
if (!inBrowser) return false; if (!inBrowser) return false;
let support = true; let support = true;
@ -133,7 +103,7 @@ function supportWebp() {
return support; return support;
} }
function throttle(action, delay) { export function throttle(action, delay) {
let timeout = null; let timeout = null;
let lastRun = 0; let lastRun = 0;
return function (...args) { return function (...args) {
@ -154,42 +124,18 @@ function throttle(action, delay) {
}; };
} }
function testSupportsPassive() { export function on(el, type, func) {
if (!inBrowser) return; el.addEventListener(type, func, {
let support = false; capture: false,
try { passive: true,
const opts = Object.defineProperty({}, 'passive', { });
// eslint-disable-next-line getter-return
get() {
support = true;
},
});
window.addEventListener('test', null, opts);
} catch (e) {
//
}
return support;
} }
const supportsPassive = testSupportsPassive(); export function off(el, type, func) {
el.removeEventListener(type, func, false);
}
const _ = { export const loadImageAsync = (item, resolve, reject) => {
on(el, type, func, capture = false) {
if (supportsPassive) {
el.addEventListener(type, func, {
capture,
passive: true,
});
} else {
el.addEventListener(type, func, capture);
}
},
off(el, type, func, capture = false) {
el.removeEventListener(type, func, capture);
},
};
const loadImageAsync = (item, resolve, reject) => {
const image = new Image(); const image = new Image();
if (!item || !item.src) { if (!item || !item.src) {
@ -202,61 +148,23 @@ const loadImageAsync = (item, resolve, reject) => {
image.crossOrigin = item.cors; image.crossOrigin = item.cors;
} }
image.onload = function () { image.onload = () =>
resolve({ resolve({
naturalHeight: image.naturalHeight, naturalHeight: image.naturalHeight,
naturalWidth: image.naturalWidth, naturalWidth: image.naturalWidth,
src: image.src, src: image.src,
}); });
};
image.onerror = function (e) { image.onerror = (e) => reject(e);
reject(e);
};
}; };
const style = (el, prop) => export function isObject(obj) {
typeof getComputedStyle !== 'undefined'
? getComputedStyle(el, null).getPropertyValue(prop)
: el.style[prop];
const overflow = (el) =>
style(el, 'overflow') + style(el, 'overflow-y') + style(el, 'overflow-x');
const scrollParent = (el) => {
if (!inBrowser) return;
if (!(el instanceof HTMLElement)) {
return window;
}
let parent = el;
while (parent) {
if (parent === document.body || parent === document.documentElement) {
break;
}
if (!parent.parentNode) {
break;
}
if (/(scroll|auto)/.test(overflow(parent))) {
return parent;
}
parent = parent.parentNode;
}
return window;
};
function isObject(obj) {
return obj !== null && typeof obj === 'object'; return obj !== null && typeof obj === 'object';
} }
function noop() {} export function noop() {}
class ImageCache { export class ImageCache {
constructor({ max }) { constructor({ max }) {
this.options = { this.options = {
max: max || 100, max: max || 100,
@ -280,20 +188,3 @@ class ImageCache {
this._caches.shift(); this._caches.shift();
} }
} }
export {
ImageCache,
inBrowser,
CustomEvent,
remove,
find,
noop,
_,
isObject,
throttle,
supportWebp,
getDPR,
scrollParent,
loadImageAsync,
getBestSelectionFromSrcset,
};

View File

@ -397,6 +397,11 @@
dependencies: dependencies:
"@types/node" "*" "@types/node" "*"
"@vant/use@^1.3.1":
version "1.3.1"
resolved "https://registry.nlark.com/@vant/use/download/@vant/use-1.3.1.tgz?cache=0&sync_timestamp=1632302722581&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40vant%2Fuse%2Fdownload%2F%40vant%2Fuse-1.3.1.tgz#8609fc7f86713df0c4ace7109eee6df2f3bd9a0f"
integrity sha1-hgn8f4ZxPfDErOcQnu5t8vO9mg8=
ansi-align@^3.0.0: ansi-align@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.npm.taobao.org/ansi-align/download/ansi-align-3.0.0.tgz#b536b371cf687caaef236c18d3e21fe3797467cb" resolved "https://registry.npm.taobao.org/ansi-align/download/ansi-align-3.0.0.tgz#b536b371cf687caaef236c18d3e21fe3797467cb"

View File

@ -2,22 +2,6 @@ import { Ref, watch, isRef, unref, onUnmounted, onDeactivated } from 'vue';
import { onMountedOrActivated } from '../onMountedOrActivated'; import { onMountedOrActivated } from '../onMountedOrActivated';
import { inBrowser } from '../utils'; import { inBrowser } from '../utils';
// eslint-disable-next-line
export let supportsPassive = false;
if (inBrowser) {
try {
const opts = {};
Object.defineProperty(opts, 'passive', {
get() {
supportsPassive = true;
},
});
window.addEventListener('test-passive', null as any, opts);
// eslint-disable-next-line no-empty
} catch (e) {}
}
type TargetRef = EventTarget | Ref<EventTarget | undefined>; type TargetRef = EventTarget | Ref<EventTarget | undefined>;
export type UseEventListenerOptions = { export type UseEventListenerOptions = {
@ -43,11 +27,7 @@ export function useEventListener(
const element = unref(target); const element = unref(target);
if (element && !attached) { if (element && !attached) {
element.addEventListener( element.addEventListener(type, listener, { capture, passive });
type,
listener,
supportsPassive ? { capture, passive } : capture
);
attached = true; attached = true;
} }
}; };

View File

@ -1,5 +1,9 @@
export const inBrowser = typeof window !== 'undefined'; export const inBrowser = typeof window !== 'undefined';
// Keep forward compatible
// should be removed in next major version
export const supportsPassive = true;
export function raf(fn: FrameRequestCallback): number { export function raf(fn: FrameRequestCallback): number {
return inBrowser ? requestAnimationFrame(fn) : -1; return inBrowser ? requestAnimationFrame(fn) : -1;
} }

View File

@ -415,8 +415,7 @@ export default defineComponent({
); );
if (disabledDay) { if (disabledDay) {
const lastAbledEndDay = getPrevDay(disabledDay); select([startDay, getPrevDay(disabledDay)]);
select([startDay, lastAbledEndDay]);
} else { } else {
select([startDay, date], true); select([startDay, date], true);
} }
@ -528,9 +527,9 @@ export default defineComponent({
showTitle={props.showTitle} showTitle={props.showTitle}
showSubtitle={props.showSubtitle} showSubtitle={props.showSubtitle}
firstDayOfWeek={dayOffset.value} firstDayOfWeek={dayOffset.value}
onClick-subtitle={(event: MouseEvent) => { onClick-subtitle={(event: MouseEvent) =>
emit('click-subtitle', event); emit('click-subtitle', event)
}} }
/> />
<div ref={bodyRef} class={bem('body')} onScroll={onScroll}> <div ref={bodyRef} class={bem('body')} onScroll={onScroll}>
{months.value.map(renderMonth)} {months.value.map(renderMonth)}
@ -542,9 +541,7 @@ export default defineComponent({
watch(() => props.show, init); watch(() => props.show, init);
watch( watch(
() => [props.type, props.minDate, props.maxDate], () => [props.type, props.minDate, props.maxDate],
() => { () => reset(getInitialDate(currentDate.value))
reset(getInitialDate(currentDate.value));
}
); );
watch( watch(
() => props.defaultDate, () => props.defaultDate,

View File

@ -24,7 +24,7 @@ export type CascaderOption = {
type CascaderTab = { type CascaderTab = {
options: CascaderOption[]; options: CascaderOption[];
selectedOption: CascaderOption | null; selected: CascaderOption | null;
}; };
export type CascaderFieldNames = { export type CascaderFieldNames = {
@ -111,7 +111,7 @@ export default defineComponent({
state.tabs = selectedOptions.map((option) => { state.tabs = selectedOptions.map((option) => {
const tab = { const tab = {
options: optionsCursor, options: optionsCursor,
selectedOption: option, selected: option,
}; };
const next = optionsCursor.find( const next = optionsCursor.find(
@ -127,7 +127,7 @@ export default defineComponent({
if (optionsCursor) { if (optionsCursor) {
state.tabs.push({ state.tabs.push({
options: optionsCursor, options: optionsCursor,
selectedOption: null, selected: null,
}); });
} }
@ -142,7 +142,7 @@ export default defineComponent({
state.tabs = [ state.tabs = [
{ {
options: props.options, options: props.options,
selectedOption: null, selected: null,
}, },
]; ];
}; };
@ -152,7 +152,7 @@ export default defineComponent({
return; return;
} }
state.tabs[tabIndex].selectedOption = option; state.tabs[tabIndex].selected = option;
if (state.tabs.length > tabIndex + 1) { if (state.tabs.length > tabIndex + 1) {
state.tabs = state.tabs.slice(0, tabIndex + 1); state.tabs = state.tabs.slice(0, tabIndex + 1);
@ -161,7 +161,7 @@ export default defineComponent({
if (option[childrenKey]) { if (option[childrenKey]) {
const nextTab = { const nextTab = {
options: option[childrenKey], options: option[childrenKey],
selectedOption: null, selected: null,
}; };
if (state.tabs[tabIndex + 1]) { if (state.tabs[tabIndex + 1]) {
@ -176,7 +176,7 @@ export default defineComponent({
} }
const selectedOptions = state.tabs const selectedOptions = state.tabs
.map((tab) => tab.selectedOption) .map((tab) => tab.selected)
.filter(Boolean); .filter(Boolean);
const eventParams = { const eventParams = {
@ -260,19 +260,19 @@ export default defineComponent({
); );
const renderTab = (tab: CascaderTab, tabIndex: number) => { const renderTab = (tab: CascaderTab, tabIndex: number) => {
const { options, selectedOption } = tab; const { options, selected } = tab;
const title = selectedOption const title = selected
? selectedOption[textKey] ? selected[textKey]
: props.placeholder || t('select'); : props.placeholder || t('select');
return ( return (
<Tab <Tab
title={title} title={title}
titleClass={bem('tab', { titleClass={bem('tab', {
unselected: !selectedOption, unselected: !selected,
})} })}
> >
{renderOptions(options, selectedOption, tabIndex)} {renderOptions(options, selected, tabIndex)}
</Tab> </Tab>
); );
}; };
@ -298,9 +298,7 @@ export default defineComponent({
() => props.modelValue, () => props.modelValue,
(value) => { (value) => {
if (value || value === 0) { if (value || value === 0) {
const values = state.tabs.map( const values = state.tabs.map((tab) => tab.selected?.[valueKey]);
(tab) => tab.selectedOption?.[valueKey]
);
if (values.includes(value)) { if (values.includes(value)) {
return; return;
} }

View File

@ -1,9 +1,5 @@
import { Ref, watch, onBeforeUnmount, onDeactivated } from 'vue'; import { Ref, watch, onBeforeUnmount, onDeactivated } from 'vue';
import { import { getScrollParent, onMountedOrActivated } from '@vant/use';
getScrollParent,
supportsPassive,
onMountedOrActivated,
} from '@vant/use';
import { useTouch } from './use-touch'; import { useTouch } from './use-touch';
import { preventDefault } from '../utils'; import { preventDefault } from '../utils';
@ -45,11 +41,7 @@ export function useLockScroll(
const lock = () => { const lock = () => {
document.addEventListener('touchstart', touch.start); document.addEventListener('touchstart', touch.start);
document.addEventListener( document.addEventListener('touchmove', onTouchMove, { passive: false });
'touchmove',
onTouchMove,
supportsPassive ? { passive: false } : false
);
if (!totalLockCount) { if (!totalLockCount) {
document.body.classList.add(BODY_LOCK_CLASS); document.body.classList.add(BODY_LOCK_CLASS);

View File

@ -29,10 +29,8 @@ const MOMENTUM_LIMIT_DISTANCE = 15;
const [name, bem] = createNamespace('picker-column'); const [name, bem] = createNamespace('picker-column');
function getElementTranslateY(element: Element) { function getElementTranslateY(element: Element) {
const style = window.getComputedStyle(element); const { transform } = window.getComputedStyle(element);
const transform = style.transform || style.webkitTransform;
const translateY = transform.slice(7, transform.length - 1).split(', ')[5]; const translateY = transform.slice(7, transform.length - 1).split(', ')[5];
return Number(translateY); return Number(translateY);
} }
@ -316,36 +314,30 @@ export default defineComponent({
watch( watch(
() => props.defaultIndex, () => props.defaultIndex,
(value) => { (value) => setIndex(value)
setIndex(value);
}
); );
return () => { return () => (
const wrapperStyle = { <div
transform: `translate3d(0, ${state.offset + baseOffset()}px, 0)`, class={[bem(), props.className]}
transitionDuration: `${state.duration}ms`, onTouchstart={onTouchStart}
transitionProperty: state.duration ? 'all' : 'none', onTouchmove={onTouchMove}
}; onTouchend={onTouchEnd}
onTouchcancel={onTouchEnd}
return ( >
<div <ul
class={[bem(), props.className]} ref={wrapper}
onTouchstart={onTouchStart} style={{
onTouchmove={onTouchMove} transform: `translate3d(0, ${state.offset + baseOffset()}px, 0)`,
onTouchend={onTouchEnd} transitionDuration: `${state.duration}ms`,
onTouchcancel={onTouchEnd} transitionProperty: state.duration ? 'all' : 'none',
}}
class={bem('wrapper')}
onTransitionend={stopMomentum}
> >
<ul {renderOptions()}
ref={wrapper} </ul>
style={wrapperStyle} </div>
class={bem('wrapper')} );
onTransitionend={stopMomentum}
>
{renderOptions()}
</ul>
</div>
);
};
}, },
}); });