Appearance
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
注意:
对于上述的问题所以我们在微信小程序中会遇到很多问题
- 如我们存在两个组件
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 其也是无效的。
- 微信小程序组件根元素 百分比失效
我们一般有一个组件 宫格布局组件 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 对象