Skip to content

1. uni-app 中对于 v-slot 的支持问题

在父子组件

2. 如何解决 uni-app 在 H5、微信小程序、支付宝小程序等各端组件与组件间的样式问题

2.1. 在支付宝小程序中 其样式不像微信小程序那样组件内部完全成为一个内部沙盒,其特性上跟 html 较像,完全支持在组件中修改子组件的样式(导致样式隔离问题:less、scss 等 scoped 完全可以解决 )。 但是其也存在一个问题就是我们有些时候需要修改子组件样式,如

html
<view class="search-input" @click="handleClickGotoSearch">
	<uiIcon class="icon-search" type="ui-icon" name="ui-icon-search"></uiIcon>
	<input class="search-header__input" placeholder="搜索课程、关键词" disabled ref="input" />
</view>

但是我们发现我们修改子组件的样式完全失效(在微信小程序上有效)

css
.search-input .icon-search {
	position: absolute;
	top: 15upx;
	left: 20upx;
}

为什么?

我们看一下支付宝小程序的渲染方式

发现其完全没有 icon-search 这个 class。 那么如何解决这个问题

html
<template>
	<text class="ui-icon" :class="[type,name , className]"></text>
</template>

<script>
	export default {
		name: 'ui-icon',
		props: {
			size: {
				type: [Number, String],
				default: 28,
			},
			// 其组件上的 class 作为 className props传入子组件  所以需要使用 :class="[className]" 去继承
			className: String,
		},
		methods: {
			_onClick(event) {
				this.$emit('click', event);
			},
		},
	};
</script>

其组件上的 class 作为 className props 传入子组件 所以需要使用 :class="[className]" 去继承

2.2. 微信小程序组件间样式问题

对于上面支付宝小程序问题我们先看一下微信小程序的渲染结果

从中我们可以发现其每一个组件虽然是一个个独立的沙盒,但是其子组件占位元素还是存在的,我们在组件上添加的 class 完全可以作用于占位元素 class 上,所以我们上面修改子组件的位置是可以的。

但是我们上面说支付宝小程序在样式处理上比较接近于 html,但是微信小程序就是两个世界了,其在父组件上去修改子组件的样式一般的 css 方式是不可以的

就如我们上面的假如需要修改 icon 的颜色

css
/* 在header-search中去修改icon的样式 */
/*
  在 支付宝小程序 、H5 : ICON颜色为 red
  在 微信小程序        : ICON为原来的颜色
 */
.search-input .ui-icon {
	color: red;
}

所以对于微信小程序我们修改子组件的位置、大小等可以直接在占位组件元素上添加 class,然后通过这个 class 去修改组件的位置、形状;

  • 但是如果我们需要修改微信小程序子组件内部元素的样式,上述方式就不能用了,只能通过外部样式属性:externalClasses 或者使用 下面 3:uni 对于 options 的处理中设置 addGlobalClass: true
注意:

对于上述的问题所以我们在微信小程序中会遇到很多问题

  1. 如我们存在两个组件 ui-btn 其 相邻选择器失效
<view>
  <ui-cell>按钮1</ui-cell>
  <ui-cell>按钮1</ui-cell>
</view>

<!-- css -->

.ui-cell + .ui-cell{
  margin-top:10rpx;  // 无效
}
原因:

其实原因很很简单,如上面其子组件的组件元素是一个隔离的,其上面还有一个占位组件元素,所以实际上 ui-cell 不是兄弟元素,所以那么我们设置了 addGlobalClass: true 其也是无效的。

  1. 微信小程序组件根元素 百分比失效

我们一般有一个组件 宫格布局组件 grid 和 grid-item,其设计思路是在 grid 设置 columns 的个数,然后我们在 grid-item 上使用 100% / columns 去分配空间,但是实际上我们发现其

3. uni 对于 options 的处理

对于微信小程序我们会设置

js
options: {
  styleIsolation: 'isolated',              // app.wxss 或页面的 wxss 中使用了标签名选择器(或一些其他特殊选择器)来直接指定样式,这些选择器会影响到页面和全部组件
  multipleSlots: true,                     // 在组件定义时的选项中启用多slot支持
  addGlobalClass: true                     //  这个选项等价于设置 styleIsolation: apply-shared ,但设置了 styleIsolation 选项后这个选项会失效。
}

这三个属性, 但是我们发现在 uni-app 中我们设置options无效,为什么?

js
export default {
	options: {
		styleIsolation: 'shared',
	},
};

下面我们看一下 uniapp 的处理

js
function parseBaseComponent(vueComponentOptions) {
	var options = {
		multipleSlots: true,
		addGlobalClass: true,
	};
	{
		// 微信 multipleSlots 部分情况有 bug,导致内容顺序错乱 如 u-list,提供覆盖选项
		if (vueOptions['mp-weixin'] && vueOptions['mp-weixin']['options']) {
			Object.assign(options, vueOptions['mp-weixin']['options']);
		}
	}
	var componentOptions = {
		options: options,
		data: initData(vueOptions, _vue.default.prototype),
		behaviors: initBehaviors(vueOptions, initBehavior),
		properties: initProperties(vueOptions.props, false, vueOptions.__file),
		lifetimes: {
			attached: function attached() {
				var properties = this.properties;
				var options = {
					mpType: isPage.call(this) ? 'page' : 'component',
					mpInstance: this,
					propsData: properties,
				};
				initVueIds(properties.vueId, this); // 处理父子关系

				initRelation.call(this, {
					vuePid: this._$vuePid,
					vueOptions: options,
				}); // 初始化 vue 实例

				this.$vm = new VueComponent(options); // 处理$slots,$scopedSlots(暂不支持动态变化$slots)

				initSlots(this.$vm, properties.vueSlots); // 触发首次 setData

				this.$vm.$mount();
			},
			ready: function ready() {
				// 当组件 props 默认值为 true,初始化时传入 false 会导致 created,ready 触发, 但 attached 不触发
				// https://developers.weixin.qq.com/community/develop/doc/00066ae2844cc0f8eb883e2a557800
				if (this.$vm) {
					this.$vm._isMounted = true;

					this.$vm.__call_hook('mounted');

					this.$vm.__call_hook('onReady');
				}
			},
			detached: function detached() {
				this.$vm && this.$vm.$destroy();
			},
		},
		pageLifetimes: {
			show: function show(args) {
				this.$vm && this.$vm.__call_hook('onPageShow', args);
			},
			hide: function hide() {
				this.$vm && this.$vm.__call_hook('onPageHide');
			},
			resize: function resize(size) {
				this.$vm && this.$vm.__call_hook('onPageResize', size);
			},
		},
		methods: {
			__l: handleLink,
			__e: handleEvent,
		},
	};
	if (isPage) {
		return componentOptions;
	}
	return [componentOptions, VueComponent];
}

从上面我们可以看出对于 小程序中 options 的设置如果我们使用 options 是无效的,需要使用

js
export default {
	'mp-weixin': {
		options: {
			styleIsolation: 'shared',
		},
	},
};

4.

报错: Property or method "toJSON" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property.

原因:

js
function cloneWithData(vm) {
	// 确保当前 vm 所有数据被同步
	var ret = Object.create(null);
	var dataKeys = [].concat(Object.keys(vm._data || {}), Object.keys(vm._computedWatchers || {}));

	dataKeys.reduce(function(ret, key) {
		ret[key] = vm[key];
		return ret;
	}, ret);
	//TODO 需要把无用数据处理掉,比如 list=>l0 则 list 需要移除,否则多传输一份数据
	Object.assign(ret, vm.$mp.data || {});
	if (Array.isArray(vm.$options.behaviors) && vm.$options.behaviors.indexOf('uni://form-field') !== -1) {
		//form-field
		ret['name'] = vm.name;
		ret['value'] = vm.value;
	}

	return JSON.parse(JSON.stringify(ret));
}

然后我们看 ret 对象