[微信小程序]小提示:cover-view与filter:blur合用不起作用
这是一个小提示:为 cover-view 设置 filter: blur(6px)
,在模拟器上看起来好好的,但在真机上不会有模糊效果,只会显示透明。这应该是小程序的 bug。只需要替换 cover-view
为 view
即可。当然,还需要注意 z-index
的问题,如果涉及到了的话。
这是一个小提示:为 cover-view 设置 filter: blur(6px)
,在模拟器上看起来好好的,但在真机上不会有模糊效果,只会显示透明。这应该是小程序的 bug。只需要替换 cover-view
为 view
即可。当然,还需要注意 z-index
的问题,如果涉及到了的话。
在 uni-app 中使用自定义 tabBar,需要注意的一共有 3 点:app.json
的配置、custom-tab-bar
目录的建立、tab 页中getTabBar
方法的调用。
首先根据官方文档,配置app.json
。在 uni-app 项目中,app.json
对应的是pages.json
,每个页面的.json
文件,需要写在pages.json
的pages-style
段中:
{
"tabBar": {
"custom": true,
"color": "#000000",
"selectedColor": "#000000",
"backgroundColor": "#000000",
"list": [
{
"pagePath": "pages/index/index",
"text": "首页"
},
{
"pagePath": "pages/me/index",
"text": "我的"
}
]
},
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "首页",
"usingComponents": {}
}
},
{
"path": "pages/me/index",
"style": {
"navigationBarTitleText": "我的",
"usingComponents": {}
}
}
],
......
}
然后需要在根目录下新建custom-tab-bar
目录,在这个目录中手动新建小程序四件套,即index.wxml
、index.js
、index.wxss
、index.json
。这个文件夹会被原封不动地复制到编译好的项目中。接着就是编写 tabBar 的样式和逻辑,这和原生开发是一样的,这里不再赘述。
最后需要在 tab 页面中调用getTabBar
方法。uni-app 的页面是.vue
文件。在onShow
事件中,添加如下代码:
onShow() {
this.$mp.page.getTabBar().setData({
selectdIndex: 1
});
}
注意,getTabBar
方法并不是直接在this
上,而是在this.$mp.page
上。
还有一点需要注意的是,tabBar 会占用一部分页面高度,tab 页最下方的内容会被遮住,需要处理一下这个问题,比如给 tab 页增加一个与 tabBar 高度一样的padding-bottom
。
Vuex 方便了管理状态,但也使开发流程更加复杂。为了提高开发速度,“让你少按几次键”,Vuex 提供了诸如mapState
方法,但其实帮助不大。如果你在 methods 中看到调用了一个陌生的方法,那十有八九是 Vuex 中的 Action 或者 Mutation。
要是你想使用 v-model 绑定 state 中的字段,享受双向绑定的便捷,那在享受之前,你要好好忍受一下折磨,因为要实现这个功能是很麻烦的:在定义了 state 后,还要定义对应的 mutation;导入到组件的 computed 时,不能使用 mapState,还要给导入的 state 设置 setter,在 setter 中 commit mutation。这是至关重要的一步,否则 v-model 不会正常工作,而是会警告你缺少 setter:
Computed property "xxxx" was assigned to but it has no setter
这实在是很麻烦,必须找到更快捷的方法,更愉快地使用 v-model。这里提供一个简单的方法,应该会提高一点点开发速度。
如果给每一个 state 都定义相应的 mutation,那代码就要写到天荒地老。不妨定义一个通用的 mutation,只需传入需要修改的 state 的字段名和新值:
updateState(state, payload){
state[payload.key] = payload.val;
}
这样就方便多了。至于一些需要特殊处理的 state,再单独给它们定义 mutation。
能用程序生成的,怎么能复制粘贴修改呢?computed 是一个对象,咱们就生成一个对象,再用...
操作符合并到组件的 computed 中。
首先在你喜欢的地方新建一个 .js
文件。因为提交 mutation 需要用到 store 实例,store 实例又在 Vue 实例上,咱们就从 main.js
或任何你能想到的地方导出 Vue 实例,在刚才新建的 .js
文件中导入:
import vm from '@/main'
接着,定义一个数组,元素是 state 的字段名:
let stateKeys = ['name', 'age', 'sex'];
接着,循环这个数组,生成计算属性:
let _state = {};
stateKeys.map(key =>{
_state[key] = {
get(){
//如果你的state在模块中,记得在`state`后加上.模块名,下同
return vm.$store.state[key];
},
set(val){
vm.$store.commit('updateState', {
key,
val
});
}
}
})
export const state = _state;
之后,在组件中引入刚刚的 state:
import state from '刚刚的js路径和名称'
最后,在组件的 computed 属性中导入 state:
computed: {
...state
}
现在,就能在模板中正常使用 state 了:
<input v-model="name">
一个人不喜欢刷碗不一定是个懒人,他可能只是不喜欢干类似的活。程序员一般都不喜欢干复制粘贴这样的重复性工作 :)
End
我是菜鸟,今天才发现这个问题。
Vue计算属性看似是一个函数,但实际上跟 data 更像。如果计算属性返回的是一个对象,并且在模板中读取了这个对象的属性,那就要保证开始时这个对象的属性一定要存在,下面是有问题的代码:
<header>{{ pageInfo.title }}</header>
...
computed: {
pageInfo(){
const mapPageInfo = {
'index': {
title: 'index page',
subTitle: 'index subtitle'
},
'list': {
title: 'list page',
subTitle: 'list subtitle'
}
};
return mapPageInfo[this.$route.path]
}
}
....
上面的代码看起来没有问题,运行起来也是正常的,但实际上在控制台报错了:
cannot read property title of undefined...
原因就是 pageInfo 在刚开始的时候没有组件中读取的 title 属性。下面是正确的做法:
...
pageInfo(){
//**保证属性一定是存在的**
let pageInfo = {
title: '',
subTitle: ''
};
const mapPageInfo = {
...
}
....
return Object.assign(pageInfo, mapPageInfo[this.$route.path]);
}
...
End
最近试图使用wx.onAppRoute
来设置一些全局的东西,比如给每个页面都设置onShareAppMessage
方法,让每个页面都可以分享:
//app.js
onLaunch(){
wx.onAppRoute(()=>{
const pages = getCurrentPages();
const currPage = pages[pages.length - 1];
if (currPage.onShareAppMessage !== undefined) return;
currPage.onShareAppMessage = ()=>{
return { title: 'Hi' }
}
})
}
这样的做法已经失效了,点击右上角的三个点,分享按钮还是灰色的。
难道是我使用的方法不对吗?如果有错误请告诉我~
幸运的是,现在Page
也支持behaviors
了,虽然还是要每个页面都引用定义的behaviors
,但是总比每个页面都写上一段相同的代码好一点。
End
Object()
方法来判断一个变量是否为对象:如果Object
方法的参数是一个对象,它总是返回该对象,即不用转换。这一点可以用来判断变量是都是对象。
var obj = {};
var isObject = obj === Object(obj);
Object.prototype.toString()
方法判断变量类型toString
方法返回变量的类型字符串,可以用来判断变量类型。由于数组、字符串、函数、Date等对象(构造函数)都自定义了自己的toString
方法,所以要使用Object.prototype.toString()
:
Object.prototype.toString.call(2); // "[object Number]"
Object.prototype.toString.call(''); // "[object String]"
Object.prototype.toString.call(true); // "[object Boolean]"
Object.prototype.toString.call(undefined); // "[object Undefined]"
Object.prototype.toString.call(null); // "[object Null]"
Object.prototype.toString.call(Math); // "[object Math]"
Object.prototype.toString.call({}); // "[object Object]"
Object.prototype.toString.call([]); // "[object Array]"
这个方法比typeof
更准确,例如:
typeof []; //'object'
Array.isArray()
判断变量是否为数组同样的,这个方法也可以弥补typeof
的不足:
typeof []; // 'object'
Array.isArray([]); // true
concat()
“如果数组成员包括对象,concat方法返回当前数组的一个浅拷贝。所谓“浅拷贝”,指的是新数组拷贝的是对象的引用。”
https://wangdoc.com/javascript/stdlib/array.html
确切的意思是,如果数组成员包括对象,concat
方法返回的新数组中,对象成员是原数组对象成员的引用:
var arr = [1, 2, {a: 3}];
var arr1 = arr.concat(4); // [1, 2, {a: 3}, 4]
console.log(arr1[2]); // {a: 3}
arr1[2].a = 5;
console.log(arr[2]); // {a: 5}
pop
push
shift
unshift
reverse
splice
, sort
[1, 2, 3].slice(); // 返回原数组的拷贝 [1, 2, 3]
同样的,对于包含对象成员的数组,返回的新数组中的对象仍然是原数组的浅拷贝,也就是引用。
var arrLikeObj = { 0: 1, 1: 2, length: 2};
Array.prototype.slice.call(arrLikeObj); // [1, 2]
类数组对象,就是具有数字类型的键值,又具有length
属性的对象。
splice
方法插入新元素splice
方法的第二个参数是0
时,表示只插入新元素,不删除任何元素:
var arr = [1, 2, 3];
arr.splice(0, 0, 0);
console.log(arr); // [0, 1, 2, 3]
众所周知,在 html 中,下面的代码:
<div style="height: 100px; padding: 20px">
<div class="inner" style="height: 100%"></div>
</div>
.inner
的高度是100px
。如果给外面的 div 设置 box-sizing: border-box
,那么.inner
的高度将会变成100px - 20px - 20px
,也就是60px
。
但是在微信小程序的 scroll-view 组件中却不同:
<scroll-view style="height: 100px; padding: 20px">
<view class="inner" style="height: 100%"></view>
</scroll-view>
.inner
的高度将会是100px + 20px + 20px = 140px
。如果给外面的 div 设置 box-sizing: border-box
,那么.inner
的高度将会变成100px - 20px - 20px + 20px + 20px
,也就是100px
。
也就是说,scroll-view 的高度设置成 100% 后,它会忽略父容器的 padding,始终等于父元素的总高度,设置box-sizing: border-box
也不能够让它表现正常。
所以,如果你想要设置 scroll-view 直接子元素的高度为 100%,那么尽可能使用绝对长度,不要使用 100%。
END
使用 text 组件的时候,不要把代码格式化成下面这样:
<text>
文字
</text>
这会使得 text 显示多余的空白。
如果你的编辑器一定要像上面那样格式化 text,不能自由配置,那么这里有一个方法,能够避免这个问题,就是:
使用 view 替代 text。
END
现在前端工作已经离不开各种框架了。在各种MVVM框架的基础上,还有各种UI框架。想要什么功能,首先想到的是找找相应的插件,即使那个功能实际上很简单。
这些框架极大的方便了开发,也使更多的人可以从事前端开发工作,而不必有太多的基础知识。
方便的开发牺牲了其他的一些东西。双向数据绑定和虚拟DOM消耗了更多的内存,在移动设备上还体现在消耗更多的电量。快速搭建界面的UI框架生成了非常多的冗余代码,DOM树臃肿不堪。因为不必想方设法地优化DOM结构,不必过于关注JavaScript语言本身,工作过程中创造性的成就感已经非常少,取而代之的是搬砖似的机械重复的劳累感。
硬件的提升给了实现这些东西的基础,硬件提升的性能也往往被软件的升级消耗掉,所以硬件需要不断升级。
其实大多数的情况我们要的就是实现目的,上面这些文字不带有感情色彩。
async 函数适合执行一些流程,不要试图使用 async 函数返回一些东西。有一个错误的例子就是在Vue的模板中绑定一个计算属性,这个计算属性里面使用 async 函数获取数据。示例代码如下:
HTML模板部分:
<div>{{computedValue}}</div>
JavaScript部分:
//......
computed: {
async computedValue(){
let val = await aAsyncMethod();
return val;
}
}
如果你这么做了,你永远只会得到如下字符串:
'[object Promise]'
为什么?
原因很简单,就像 MDN 上这篇文档 MDN - async函数 介绍的,async 遇到await
的时候会立即返回一个Promise
,把控制权交出,直到 await 后面的异步操作有结果(resolve
或者reject
)才会继续执行后面的代码。可以说,async 函数的返回值永远是一个Promise
。
END