| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817 |
- import Vue from 'vue';
- function parseData (data, vueComponentOptions) {
- if (!data) {
- return
- }
- vueComponentOptions.mpOptions.data = data;
- }
- function parseComponents (vueComponentOptions) {
- vueComponentOptions.components = global.__wxVueOptions.components;
- }
- const _toString = Object.prototype.toString;
- const hasOwnProperty = Object.prototype.hasOwnProperty;
- function isFn (fn) {
- return typeof fn === 'function'
- }
- function isPlainObject (obj) {
- return _toString.call(obj) === '[object Object]'
- }
- function hasOwn (obj, key) {
- return hasOwnProperty.call(obj, key)
- }
- function noop () {}
- /**
- * Create a cached version of a pure function.
- */
- function cached (fn) {
- const cache = Object.create(null);
- return function cachedFn (str) {
- const hit = cache[str];
- return hit || (cache[str] = fn(str))
- }
- }
- /**
- * Camelize a hyphen-delimited string.
- */
- const camelizeRE = /-(\w)/g;
- const camelize = cached((str) => {
- return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : '')
- });
- const SOURCE_KEY = '__data__';
- const COMPONENT_LIFECYCLE = {
- created: 'onServiceCreated',
- attached: 'onServiceAttached',
- ready: 'mounted',
- moved: 'moved',
- detached: 'destroyed'
- };
- const COMPONENT_LIFECYCLE_KEYS = Object.keys(COMPONENT_LIFECYCLE);
- const PAGE_LIFETIMES = {
- show: 'onPageShow',
- hide: 'onPageHide',
- resize: 'onPageResize'
- };
- const PAGE_LIFETIMES_KEYS = Object.keys(PAGE_LIFETIMES);
- const PAGE_LIFECYCLE = [
- 'onLoad',
- 'onShow',
- 'onReady',
- 'onHide',
- 'onUnload',
- 'onPullDownRefresh',
- 'onReachBottom',
- 'onShareAppMessage',
- 'onPageScroll',
- 'onResize',
- 'onTabItemTap'
- ];
- function parsePageMethods (mpComponentOptions, vueComponentOptions) {
- const methods = Object.create(null);
- Object.keys(mpComponentOptions).forEach(key => {
- const value = mpComponentOptions[key];
- if (isFn(value) && PAGE_LIFECYCLE.indexOf(key) === -1) {
- methods[key] = value;
- }
- });
- vueComponentOptions.methods = methods;
- }
- function parsePageLifecycle (mpComponentOptions, vueComponentOptions) {
- Object.keys(mpComponentOptions).forEach(key => {
- if (PAGE_LIFECYCLE.indexOf(key) !== -1) {
- vueComponentOptions[key] = mpComponentOptions[key];
- }
- });
- }
- function parsePage (mpComponentOptions) {
- const vueComponentOptions = {
- mixins: [],
- mpOptions: {}
- };
- parseComponents(vueComponentOptions);
- parseData(mpComponentOptions.data, vueComponentOptions);
- parsePageMethods(mpComponentOptions, vueComponentOptions);
- parsePageLifecycle(mpComponentOptions, vueComponentOptions);
- return vueComponentOptions
- }
- function parseProperties (properties, vueComponentOptions) {
- if (!properties) {
- return
- }
- vueComponentOptions.mpOptions.properties = properties;
- }
- function parseOptions (options, vueComponentOptions) {
- if (!options) {
- return
- }
- vueComponentOptions.mpOptions.options = options;
- }
- function parseMethods (methods, vueComponentOptions) {
- if (!methods) {
- return
- }
- if (methods.$emit) {
- console.warn('Method "$emit" conflicts with an existing Vue instance method');
- delete methods.$emit;
- }
- vueComponentOptions.methods = methods;
- }
- function parseLifecycle (mpComponentOptions, vueComponentOptions) {
- COMPONENT_LIFECYCLE_KEYS.forEach(name => {
- if (hasOwn(mpComponentOptions, name)) {
- (vueComponentOptions[COMPONENT_LIFECYCLE[name]] || (vueComponentOptions[COMPONENT_LIFECYCLE[name]] = []))
- .push(mpComponentOptions[name]);
- }
- });
- }
- const mpBehaviors = {
- 'wx://form-field': {},
- 'wx://component-export': {}
- };
- function callDefinitionFilter (mpComponentOptions) {
- const {
- behaviors,
- definitionFilter
- } = mpComponentOptions;
- const behaviorDefinitionFilters = [];
- if (Array.isArray(behaviors)) {
- behaviors.forEach(behavior => {
- behavior = typeof behavior === 'string' ? mpBehaviors[behavior] : behavior;
- if (behavior.definitionFilter) {
- behaviorDefinitionFilters.push(behavior.definitionFilter);
- behavior.definitionFilter.call(null, mpComponentOptions, []);
- }
- });
- }
- if (isFn(definitionFilter)) {
- return function (defFields) {
- definitionFilter(defFields, behaviorDefinitionFilters);
- }
- }
- }
- function parseDefinitionFilter (mpComponentOptions, vueComponentOptions) {
- callDefinitionFilter(mpComponentOptions);
- }
- function parseBehavior (behavior) {
- const {
- data,
- methods,
- behaviors,
- properties
- } = behavior;
- const vueComponentOptions = {
- watch: {},
- mpOptions: {
- mpObservers: []
- }
- };
- parseData(data, vueComponentOptions);
- parseMethods(methods, vueComponentOptions);
- parseBehaviors(behaviors, vueComponentOptions);
- parseProperties(properties, vueComponentOptions);
- parseLifecycle(behavior, vueComponentOptions);
- parseDefinitionFilter(behavior);
- return vueComponentOptions
- }
- const BEHAVIORS = {
- 'wx://form-field': {
- beforeCreate () {
- const mpOptions = this.$options.mpOptions;
- if (!mpOptions.properties) {
- mpOptions.properties = Object.create(null);
- }
- const props = mpOptions.properties;
- // TODO form submit,reset
- if (!hasOwn(props, 'name')) {
- props.name = {
- type: String
- };
- }
- if (!hasOwn(props, 'value')) {
- props.value = {
- type: String // 默认类型调整为 String,否则默认值为 null,导致一些自定义 input 显示不正确
- };
- }
- }
- }
- };
- function parseBehaviors (behaviors, vueComponentOptions) {
- if (!behaviors) {
- return
- }
- behaviors.forEach(behavior => {
- if (typeof behavior === 'string') {
- BEHAVIORS[behavior] && vueComponentOptions.mixins.push(BEHAVIORS[behavior]);
- } else {
- vueComponentOptions.mixins.push(parseBehavior(behavior));
- }
- });
- }
- function parseSinglePath (path) {
- return path.split('.')
- }
- function parseMultiPaths (paths) {
- return paths.split(',').map(path => parseSinglePath(path))
- }
- function parseObservers (observers, vueComponentOptions) {
- if (!observers) {
- return
- }
- const {
- mpObservers
- } = vueComponentOptions.mpOptions;
- Object.keys(observers).forEach(path => {
- mpObservers.push({
- paths: parseMultiPaths(path),
- observer: observers[path]
- });
- });
- }
- function relative (from, to) {
- if (to.indexOf('/') === 0) {
- from = '';
- }
- const fromArr = from.split('/');
- const toArr = to.split('/');
- fromArr.pop();
- while (toArr.length) {
- const part = toArr.shift();
- if (part !== '' && part !== '.') {
- if (part !== '..') {
- fromArr.push(part);
- } else {
- fromArr.pop();
- }
- }
- }
- return fromArr.join('/')
- }
- function parseRelations (relations, vueComponentOptions) {
- if (!relations) {
- return
- }
- Object.keys(relations).forEach(name => {
- const relation = relations[name];
- relation.name = name;
- relation.target = relation.target ? String(relation.target) : relative(global.__wxRoute, name);
- });
- vueComponentOptions.mpOptions.relations = relations;
- }
- function parseExternalClasses (externalClasses, vueComponentOptions) {
- if (!externalClasses) {
- return
- }
- if (!Array.isArray(externalClasses)) {
- externalClasses = [externalClasses];
- }
- vueComponentOptions.mpOptions.externalClasses = externalClasses;
- if (!vueComponentOptions.mpOptions.properties) {
- vueComponentOptions.mpOptions.properties = Object.create(null);
- }
- externalClasses.forEach(externalClass => {
- vueComponentOptions.mpOptions.properties[camelize(externalClass)] = {
- type: String,
- value: ''
- };
- });
- }
- function parseLifetimes (lifetimes, vueComponentOptions) {
- if (!lifetimes) {
- return
- }
- parseLifecycle(lifetimes, vueComponentOptions);
- }
- function parsePageLifetimes (pageLifetimes, vueComponentOptions) {
- if (!pageLifetimes) {
- return
- }
- PAGE_LIFETIMES_KEYS.forEach(key => {
- const lifetimeFn = pageLifetimes[key];
- isFn(lifetimeFn) && (vueComponentOptions[PAGE_LIFETIMES[key]] = lifetimeFn);
- });
- }
- function parseComponent (mpComponentOptions) {
- const {
- data,
- options,
- methods,
- behaviors,
- lifetimes,
- observers,
- relations,
- properties,
- pageLifetimes,
- externalClasses
- } = mpComponentOptions;
- const vueComponentOptions = {
- mixins: [],
- props: {},
- watch: {},
- mpOptions: {
- mpObservers: []
- }
- };
- parseComponents(vueComponentOptions);
- parseData(data, vueComponentOptions);
- parseOptions(options, vueComponentOptions);
- parseMethods(methods, vueComponentOptions);
- parseBehaviors(behaviors, vueComponentOptions);
- parseLifetimes(lifetimes, vueComponentOptions);
- parseObservers(observers, vueComponentOptions);
- parseRelations(relations, vueComponentOptions);
- parseProperties(properties, vueComponentOptions);
- parsePageLifetimes(pageLifetimes, vueComponentOptions);
- parseExternalClasses(externalClasses, vueComponentOptions);
- parseLifecycle(mpComponentOptions, vueComponentOptions);
- parseDefinitionFilter(mpComponentOptions);
- return vueComponentOptions
- }
- function initRelationHandlers (type, handler, target, ctx) {
- if (!handler) {
- return
- }
- const name = `_$${type}Handlers`;
- (ctx[name] || (ctx[name] = [])).push(function () {
- handler.call(ctx, target);
- });
- }
- function initLinkedHandlers (relation, target, ctx) {
- const type = 'linked';
- const name = relation.name;
- const relationNodes = ctx._$relationNodes || (ctx._$relationNodes = Object.create(null));
- (relationNodes[name] || (relationNodes[name] = [])).push(target);
- initRelationHandlers(type, relation[type], target, ctx);
- }
- function initUnlinkedHandlers (relation, target, ctx) {
- const type = 'unlinked';
- initRelationHandlers(type, relation[type], target, ctx);
- }
- function findParentRelation (parentVm, target, type) {
- const relations = parentVm &&
- parentVm.$options.mpOptions &&
- parentVm.$options.mpOptions.relations;
- if (!relations) {
- return []
- }
- const name = Object.keys(relations).find(name => {
- const relation = relations[name];
- return relation.target === target && relation.type === type
- });
- if (!name) {
- return []
- }
- return [relations[name], parentVm]
- }
- function initParentRelation (vm, childRelation, match) {
- const [parentRelation, parentVm] = match(vm, vm.$options.mpOptions.path);
- if (!parentRelation) {
- return
- }
- initLinkedHandlers(parentRelation, vm, parentVm);
- initLinkedHandlers(childRelation, parentVm, vm);
- initUnlinkedHandlers(parentRelation, vm, parentVm);
- initUnlinkedHandlers(childRelation, parentVm, vm);
- }
- function initRelation (relation, vm) {
- const type = relation.type;
- if (type === 'parent') {
- initParentRelation(vm, relation, function matchParent (vm, target) {
- return findParentRelation(vm.$parent, target, 'child')
- });
- } else if (type === 'ancestor') {
- initParentRelation(vm, relation, function matchAncestor (vm, target) {
- let $parent = vm.$parent;
- while ($parent) {
- const ret = findParentRelation($parent, target, 'descendant');
- if (ret.length) {
- return ret
- }
- $parent = $parent.$parent;
- }
- return []
- });
- }
- }
- function initRelations (vm) {
- const {
- relations
- } = vm.$options.mpOptions || {};
- if (!relations) {
- return
- }
- Object.keys(relations).forEach(name => {
- initRelation(relations[name], vm);
- });
- }
- function handleRelations (vm, type) {
- // TODO 需要移除 relationNodes
- const handlers = vm[`_$${type}Handlers`];
- if (!handlers) {
- return
- }
- handlers.forEach(handler => handler());
- }
- const sharedPropertyDefinition = {
- enumerable: true,
- configurable: true,
- get: noop,
- set: noop
- };
- function proxy (target, sourceKey, key) {
- sharedPropertyDefinition.get = function proxyGetter () {
- return this[sourceKey][key]
- };
- sharedPropertyDefinition.set = function proxySetter (val) {
- this[sourceKey][key] = val;
- };
- Object.defineProperty(target, key, sharedPropertyDefinition);
- }
- function setDataByExprPath (exprPath, value, data) {
- const keys = exprPath.replace(/\[(\d+?)\]/g, '.$1').split('.');
- keys.reduce((obj, key, idx) => {
- if (idx === keys.length - 1) {
- obj[key] = value;
- } else {
- if (typeof obj[key] === 'undefined') {
- obj[key] = {};
- }
- return obj[key]
- }
- }, data);
- return keys.length === 1
- }
- function setData (data, callback) {
- if (!isPlainObject(data)) {
- return
- }
- Object.keys(data).forEach(key => {
- if (setDataByExprPath(key, data[key], this.data)) {
- !hasOwn(this, key) && proxy(this, SOURCE_KEY, key);
- }
- });
- this.$forceUpdate();
- isFn(callback) && this.$nextTick(callback);
- }
- /**
- * https://github.com/swan-team/swan-js/blob/61e2a63f7aa576b5daafbe77fdfa7c65b977060c/src/utils/index.js
- */
- const _toString$1 = Object.prototype.toString;
- /**
- * 深度assign的函数
- * @param {Object} targetObject 要被拷贝的目标对象
- * @param {Object} originObject 拷贝的源对象
- * @return {Object} merge后的对象
- */
- const deepAssign = (targetObject = {}, originObject) => {
- const originType = _toString$1.call(originObject);
- if (originType === '[object Array]') {
- targetObject = originObject.slice(0);
- return targetObject
- } else if (originType === '[object Object]') {
- for (const key in originObject) {
- targetObject[key] = deepAssign(targetObject[key], originObject[key]);
- }
- return targetObject
- } else if (originType === '[object Date]') {
- return new Date(originObject.getTime())
- } else if (originType === '[object RegExp]') {
- const target = String(originObject);
- const lastIndex = target.lastIndexOf('/');
- return new RegExp(target.slice(1, lastIndex), target.slice(lastIndex + 1))
- }
- return originObject
- };
- /**
- * 深度拷贝逻辑,不同于lodash等库,但是与微信一致
- * @param {*} [originObj] 原对象
- * @return {Object|Array} 拷贝结果
- */
- const deepClone = originObj => {
- return deepAssign(_toString$1.call(originObj) === '[object Array]' ? [] : {}, originObj)
- };
- const PROP_DEFAULT_VALUES = {
- [String]: '',
- [Number]: 0,
- [Boolean]: false,
- [Object]: null,
- [Array]: [],
- [null]: null
- };
- function getDefaultVal (propType) {
- return PROP_DEFAULT_VALUES[propType]
- }
- function getPropertyVal (options) {
- if (isPlainObject(options)) {
- if (hasOwn(options, 'value')) {
- return options.value
- }
- return getDefaultVal(options.type)
- }
- return getDefaultVal(options)
- }
- function getType (propOptions) {
- return isPlainObject(propOptions) ? propOptions.type : propOptions
- }
- function validateProp (key, propsOptions, propsData, vm) {
- let value = propsData[key];
- if (value !== undefined) {
- const propOptions = propsOptions[key];
- const type = getType(propOptions);
- value = formatVal(value, type);
- const observer = propOptions && propOptions.observer;
- if (observer) {
- // 初始化时,异步触发 observer,否则 observer 中无法访问 methods 或其他
- setTimeout(function () {
- observe(observer, vm, value);
- }, 4);
- }
- return value
- }
- return getPropertyVal(propsOptions[key])
- }
- function formatVal (val, type) {
- if (type === Boolean) {
- return !!val
- } else if (type === String) {
- return String(val)
- }
- return val
- }
- function observe (observer, vm, newVal, oldVal) {
- try {
- if (typeof observer === 'function') {
- observer.call(vm, newVal, oldVal);
- } else if (typeof observer === 'string' &&
- typeof vm[observer] === 'function'
- ) {
- vm[observer](newVal, oldVal);
- }
- } catch (err) {
- console.error(`execute observer ${observer} callback fail! err: ${err}`);
- }
- }
- function initProperties (vm, instanceData) {
- const properties = vm.$options.mpOptions.properties;
- if (!properties) {
- return
- }
- const propsData = deepClone(vm.$options.propsData) || {};
- for (const key in properties) {
- const observer = isPlainObject(properties[key]) ? properties[key].observer : false;
- let value = validateProp(key, properties, propsData, vm);
- Object.defineProperty(instanceData, key, {
- enumerable: true,
- configurable: true,
- get () {
- return value
- },
- set (newVal) {
- const oldVal = value;
- /* eslint-disable no-self-compare */
- if (newVal === value || (newVal !== newVal && value !== value)) {
- return
- }
- // TODO 临时方案,clone array
- value = Array.isArray(newVal) ? newVal.slice(0) : newVal;
- if (observer) {
- observe(observer, vm, newVal, oldVal);
- }
- // 触发渲染
- vm.$forceUpdate();
- }
- });
- }
- }
- function updateProperties (vm) {
- const properties = vm.$options.mpOptions && vm.$options.mpOptions.properties;
- const propsData = vm.$options.propsData;
- if (propsData && properties) {
- Object.keys(properties).forEach(key => {
- if (hasOwn(propsData, key)) {
- vm[key] = formatVal(propsData[key], getType(properties[key]));
- }
- });
- }
- }
- function initState (vm) {
- const instanceData = JSON.parse(JSON.stringify(vm.$options.mpOptions.data || {}));
- vm[SOURCE_KEY] = instanceData;
- const propertyDefinition = {
- get () {
- return vm[SOURCE_KEY]
- },
- set (value) {
- vm[SOURCE_KEY] = value;
- }
- };
- Object.defineProperties(vm, {
- data: propertyDefinition,
- properties: propertyDefinition
- });
- vm.setData = setData;
- initProperties(vm, instanceData);
- Object.keys(instanceData).forEach(key => {
- proxy(vm, SOURCE_KEY, key);
- });
- }
- function initMethods (vm) {
- const oldEmit = vm.$emit;
- vm.triggerEvent = (eventName, detail, options) => {
- const target = {
- dataset: vm.$el.dataset
- };
- const event = {
- target,
- currentTarget: target,
- detail,
- preventDefault: noop,
- stopPropagation: noop
- };
- oldEmit.call(vm, eventName, event);
- };
- // 主要是Vant 自己封装了 $emit,放到 methods 中会触发 Vue 的警告,索性,框架直接重写该方法
- vm.$emit = (...args) => {
- vm.triggerEvent(...args);
- };
- vm.getRelationNodes = (relationKey) => {
- // 需要过滤已被销毁的vm
- /* eslint-disable no-mixed-operators */
- return (vm._$relationNodes && vm._$relationNodes[relationKey] || []).filter(vm => !vm._isDestroyed)
- };
- vm._$updateProperties = updateProperties;
- }
- function handleObservers (vm) {
- const watch = vm.$options.watch;
- if (!watch) {
- return
- }
- Object.keys(watch).forEach(name => {
- const observer = watch[name];
- if (observer.mounted) {
- const val = vm[name];
- let handler = observer.handler;
- if (typeof handler === 'string') {
- handler = vm[handler];
- }
- handler && handler.call(vm, val, val);
- }
- });
- }
- var polyfill = {
- beforeCreate () {
- // 取消 development 时的 Proxy,避免小程序组件模板中使用尚未定义的属性告警
- this._renderProxy = this;
- this._$self = this;
- this._$noop = noop;
- },
- created () { // properties 中可能会访问 methods,故需要在 created 中初始化
- initState(this);
- initMethods(this);
- initRelations(this);
- },
- mounted () {
- handleObservers(this);
- },
- destroyed () {
- handleRelations(this, 'unlinked');
- }
- };
- global.__wxRoute = '';
- global.__wxComponents = Object.create(null);
- global.__wxVueOptions = Object.create(null);
- function Page (options) {
- const pageOptions = parsePage(options);
- pageOptions.mixins.unshift(polyfill);
- pageOptions.mpOptions.path = global.__wxRoute;
- global.__wxComponents[global.__wxRoute] = pageOptions;
- }
- function initRelationsHandler (vueComponentOptions) {
- // linked 需要在当前组件 attached 之后再执行
- if (!vueComponentOptions.onServiceAttached) {
- vueComponentOptions.onServiceAttached = [];
- }
- vueComponentOptions.onServiceAttached.push(function onServiceAttached () {
- handleRelations(this, 'linked');
- });
- }
- function Component (options) {
- const componentOptions = parseComponent(options);
- componentOptions.mixins.unshift(polyfill);
- componentOptions.mpOptions.path = global.__wxRoute;
- initRelationsHandler(componentOptions);
- global.__wxComponents[global.__wxRoute] = componentOptions;
- }
- function Behavior (options) {
- return options
- }
- const nextTick = Vue.nextTick;
- var index = uni.__$wx__;
- export default index;
- export { Behavior, Component, Page, nextTick };
|