wechat-mini-program-experience
经历了一次小程序开发,遇到了一些开发时遇到的坑,首先说明一下技术框架是使用的京东的 taro 进行的开发,并且使用的 hooks 的开发方式
Taro 的坑
首先来看看使用 taro 框架会有哪些坑是需要避免的呢
- 自定义组件的 props 不能传入复杂的渲染函数
// 比如一个带有头像的电话列表 const tels = [ { FIRST_CHAR: 'A' list: [{ avatar: 'imgsrc', tel: '12345678900' },{ avatar: 'imgsrc', tel: '12345678900' },{ avatar: 'imgsrc', tel: '12345678900' },{ avatar: 'imgsrc', tel: '12345678900' }] } ]; // 这样的一个数据结构,因为想做一个通用性更大的组件,所以在设计的时候想要把第二层渲染list的渲染函数写成一个从外部传入的函数 const render = () => { return (<Indexes renderItem={ item => (<View><Image src={item.avatar} /><Text>{item.tel}</Text></View>) } />); } // 但是并不能这样子写,taro不会进行渲染,所以只能够将列表的渲染方法写在组件内部 list.map((listItem) => ( <View key={`index-${listItem.key}`} className="index-list-item" id={`index-${listItem.key}`} > <View className="item-title">{listItem.title}</View> // {renderItem()} 如果是使用上面那种方式 直接在这里写成这样就可以了 {listItem.items.map((i) => { return ( <View key={i.name} className="item-container" hoverClass="item-container-hover" onClick={() => onClick(i)} > {needIcon && ( // 因为不确定 icon 的确切的值,所以就交给 CustomImage 组件内部处理,外面只需要管需不需要icon的属性 <CustomImage src={i.icon} mode="aspectFit" custom-class="icon" defaultImage={needIcon && defaultIcon} /> )} <Text>{i.name}</Text> </View> ); })} </View> // 所以复杂的渲染还是需要写在组件内部 // 另外如果需要传入 tsx 作为 props 的话,需要使用一个 View包裹起来 <Component tsx={<View>something</View>} />
在开发中需要保持的一些思维
- 在使用 useEffect 的时候需要有函数的思维
使用一个 useEffect 时会在后面加上其依赖,当依赖变化,就会触发一次 useEffect 的回调,所以在使用 setState 改变依赖的时候,需要有一个函数式的思维,改变变量进而产生不一样的结果
// 比如翻页请求下一页时,需要的是改变页码就能够触发函数,而不是不断的调用函数 const [page, setPage] = useState(1); useEffect(() => { // ... 获取相应页码的数据 }, [page]); const changePage = () => setPage(prePage => prePage + 1)
- 多次不间断触发查询条件,应该以最后一次结果为准展示
这个场景是在一个能够使用筛选的列表中遇到的,当我不断切换筛选条件并且在网络环境较差的时候,页面中的渲染会有多次渲染,渲染次数和切换的次数一样,原因是因为没有取最后一次结果作为渲染的依据。所以在有类似情况出现时,应该使用唯一标识去拿到最后一次请求的结果再进行页面的渲染,具体实施方法有很多,我使用的是使用一个数字代表请求次数,每次请求次数加一,请求完成次数减一,直到次数归零,才进行页面渲染。
useEffect(() => { setLoading(true); queryQueen++; searchStationByKeyword().then((res: StationCollection) => { queryQueen--; if (res.chcList && queryQueen === 0) { setLoading(false); } }); }, []);
- 对于 Image 组件需要有错误处理
很多同学在使用图片的时候都没有对于图片进行错误处理,导致只要遇到图片链接不正确或者没有链接的时候,页面中的展示就会出现问题,所以不论在什么情况下开发,对于图片也要有错误处理。
interface IProps extends StandardProps, ImageProps { defaultImage?: string; lazyLoad?: boolean; } const CustomImage: Taro.FC<IProps> = (props) => { const { src, mode, defaultImage, lazyLoad = true } = props; const [rightSrc, setRightSrc] = useState(defaultImage || imagePlaceholder); useEffect(() => { if (src) { setRightSrc(src); } }, [src]); return ( <Image lazyLoad={lazyLoad} src={rightSrc} mode={mode} className="custom-class" onError={() => setRightSrc(imagePlaceholder)} /> ); };
小程序中的一些注意事项
- canvas 的使用注意事项
a. 首先第一点就是 canvas 的 type="2d" 的模式不会进行绘制,原因尚未知晓,解决方法就是不设定 type,而是使用
canvas.getContext('2d')
。b. canvas 在绘制时如果要设置一些样式,需要配合
context.save() context.restore()
,因为 canvas 的样式是依据最近的一次,如果想要各个设置不互相干扰,就需要在设置前后使用上面两个 API 进行保存,实在是因为样式混杂不好处理,那么就在每次 draw 的时候进行整块画布清空context.clearRect()
。c. canvas 的所有计算完毕之后,一定要使用
context.draw()
进行绘制,否则画布上不会出现任何图像。d. 小程序的 canvas 的宽高就是其 style 的宽高,与 HTML 的不一样。
e. 在调试 canvas 的时候需要在真机上面进行调试,因为小程序 canvas 使用的是原生组件,模拟器上的样子不一定是真机上的样子,并且如果有 canvas 尽量不要放在浮层中,可能会因为 canvas 是原生组件而层级并不会在浮层上展示,同样因为是原生组件,所以也不能在 scroll-view 中使用 canvas,canvas 不会随着 scroll-view 滑动而滑动。
f. 如果需要在小程序中进行 canvas 的动画,需要调用
canvas.requestAnimationFrame()
,如果不生效,可以使用setInterval()
,但是小程序的动画在真机上可能会很卡,原因尚未知晓,尽量不要设计过于复杂的动效在小程序上。
埋点小技巧
Last updated