The original address
The company began to involve in the business of small programs. Since our department focuses on React technology stack, we decided to adopt the Taro framework of JINGdong after investigation. But the actual development still encountered a lot of holes, so I summarized some, also built some wheels, decided to share
Customize toast (or Modal) APIS
Although applets provide interaction apis such as Toast and Modal, Taro does the same. But products want to customize styles, ICONS, positions, and text length, so you can’t do it yourself
First, the effect demonstration
-
Call toast’s show and hide via the global API
-
Each interface can be called without manually importing components
2. Monitor interface routing
-
First, each interface is callable, and maintaining a global Store is easiest. Then read the current interface dynamically, and set the state of the corresponding interface
-
The global Store is simple and reads the current interface. Fortunately, the applet provides the getCurrentPages API
const pages = getCurrentPages()
const curPage = pages[pages.length - 1) | | {}Copy the code
- Routing changes the dynamic load, namely listening, pay attention to this is to monitor the global routing, Taro componentDidShow, componentDidHide is cannot handle. Looking for a long time inWechat open community for a discussionSearch the
wx.onAppRoute
wx.onAppRoute(res= > {
// Update currentPage for global store
})
Copy the code
Introduced laziness: Mixed development is the real deal
- Since you don’t want to manually introduce components into every interface, it’s good that applets dotemplateMechanism rematch
import
Can. So make a script and append string to the end after build
WXML <block wx:if="{{$taroCompReady}}"> <view class="demo" Wx :if="{{$taroCompReady}}"> <view class="demo"> </view> </block> <import src=".. /.. /components/toast/index.wxml" /> <template is="toast" data="{{__toast__}}" />Copy the code
- For data interaction, manually inject templates into build files. Forget the Taro Component level
getCurrentPages
There are nativesetData api. Create a variable in the template:<template is="toast" data="{{__toast__}}" />
Toast
With all the technical details sorted out, I started typing code happily
- @/utils/page is a simple encapsulation
let _currentPage: Taro.Page = {}
export const $page = {
get() {
return _currentPage
},
update() {
// Update current page
const pages = getCurrentPages()
_currentPage = pages[pages.length - 1] || {}
},
setData(key: string, source: any, force = false) {
_currentPage.setData({ [key]: source }) // Native setData
force && _currentPage.$component.forceUpdate() // Taro level forceUpdate, used on demand
},
getData(key: string) {
return _currentPage.data[key]
},
}
Copy the code
- The Toast class uses static + singleton because it is a global reference
const iconFactory = {
success: successSvg,
error: errorSvg,
}
export default class Toast {
static instance: Toast
page: Taro.Page
visible = false
static create() {
if (!this.instance) this.instance = new Toast()
return this.instance
}
// Define the convenience API
staticsuccess(title: string, during? : number, config? : Omit<ToastConfig,'title' | 'during'>) {
returnToast.show({ title, during, ... Object.assign({}, config, {icon: 'success'})})}// Define the convenience API
staticerror(title: string, during? : number, config? : Omit<ToastConfig,'title' | 'during'>) {
returnToast.show({ title, during, ... Object.assign({}, config, {icon: 'error'})})}// Define the convenience API
staticinfo(title: string, during? : number, config? : Omit<ToastConfig,'title' | 'during'>) {
returnToast.show({ title, during, ... Object.assign({}, config, {icon: 'none'})})}// Call API can be customized
static async show(config: ToastConfig) {
if (this.instance.visible) return
this.instance.visible = true
const { title, icon = 'none' } = config
// Start manipulating data to the template
$page.setData('__toast__', {
visible: true,
title,
icon: iconFactory[icon] || icon,
})
}
static async hide() {
if (!this.instance.visible) return
/ / hide the toast
$page.setData('__toast__', {
visible: false,})this.instance.visible = false}}Copy the code
- It initializes when the App hangs, listens for routes, and then uses it directly
import toast from '@/utils/toast'
import $page form '@/utils/page'
class App extends Component {
componentWillMount() {
toast.create()
wx.onAppRoute(res= > {
toast.hide()
$page.update() // Update the page after the previous interface toast is hidden
})
}
render() {
return <Index />
}
}
class Index extends Component {
render() {
return (
<View className='index'>
<Button onClick={()= >Toast. success(' submitted request successfully ')}>success</Button>
<Button onClick={()= > toast.hide()}>hide</Button>
</View>)}}Copy the code
- Toast WXML, that’s pretty simple, native
<template name="toast"> <view class="toast{{__toast__.visible ? '' : ' hidden'}}"> <image class="toast-icon{{__toast__.icon ! == 'none' ? "' : ' hidden'}}" src="{{__toast__.icon}}"></image> <view class="toast-text">{{__toast__.title}}</view> </view> </template>Copy the code
- Templates inject script. Taro Taro will generate app.json, which records all registered pages, and then fetch the corresponding index.wxml
const fs = require('fs')
const path = require('path')
const outputDir = 'dist/'
const appJson = 'app.json'
const str = `
`
let initPages = []
start()
async function start() {
// Get all page index. WXML
initPages = await getInjectPages()
// Write the template in
await injectAll(initPages, str)
}
function getInjectPages(jsonName = appJson) {
const appJsonPath = getAbsPath(outputDir, jsonName)
const suffix = '.wxml'
return new Promise((resolve, reject) = > {
// check app.json
if (fs.existsSync(appJsonPath)) {
const pageJson = require(appJsonPath)
const pages = (pageJson.pages || []).map(p= > outputDir + p + suffix)
// check all pages
if(! pages.some(p= >! fs.existsSync(p))) resolve(pages)else reject('did not find all pages')}}}async function injectAll(pages, template) {
const injectPromises = pages.map(p= > {
return new Promise((resolve, reject) = > {
fs.appendFileSync(p, template, 'utf8')
resolve()
})
})
await Promise.all(injectPromises)
}
Copy the code
- bootstrap
"scripts": {
"build:weapp": "rm -rf dist && taro build --type weapp"."inject": "node scripts/import-toast.js"
}
yarn build:weapp
yarn inject
Copy the code
Load json animations using canvas
First, the effect demonstration
2. Native support
-
Small program without SVG tag, some complex animation is not easy to implement, fortunately there is official website support Lottie – miniProgram
-
usage
// wxml
<canvas id="canvas" type="2d"></canvas>
// js
import lottie from 'lottie-miniprogram'
wx.createSelectorQuery()
.selectAll('#loading') // Canvas tag ID
.node(([res]) = > {
const canvas = res.node
const context = canvas.getContext('2d')
lottie.setup(canvas)
lottie.loadAnimation({
animationData: jsonData, // Add the json file
rendererSettings: { context },
})
}).exec()
Copy the code
3. API calling Loading
-
Note that the createSelectorQuery method will fail to find the Canvas ID if the above component is selected as a component and referenced in the page, but it can be placed in the page. It is preliminarily concluded that canvas component will appear in shadow DOM when the component is referenced, and this API must be used to write canvas. Please correct any errors
-
If the component invocation doesn’t work, use the API. Same steps as above: listen for routes, global store passes variables through setData, use in native templates, script injection
More free render props
There are always problems and limitations when using Taro Render props to pass components along with some logic
First, the effect demonstration
Second, Taro packaging research
-
Taro wanted to package the render props as slot + template
-
Render Normal={() =>
}
Render prop
// pages/index/index.wxml <block wx:if="{{$taroCompReady}}"> <view class="index"> <custom-render <view slot="normal"> <view> <template is="renderClosureNormalgzzzz" data="{{$compid__3}}"> AnonymousState__temp}}"></template> </view> </view> </custom-render> </view> </block name="renderClosureNormalgzzzz"> <block> <view>render prop</view> </block> </template> // WXML <block wx:if="{{$taroCompReady}}"> <view> </view> </block>Copy the code
- Slot-oriented programming! Taro will not compile lower-case components and will copy them directly, so we can do “mixed development”.
class Index extends Component {
render() {
return (
<View className='index'>
<CustomRender renderNormal={()= > <View>normal render prop</View>}> // Custom render props<view slot='gender'>
<View>this is my slot</View>
</view>
</CustomRender>
</View>)}}class CustomRender extends Component {
render() {
return (
<View>// Declare socket<slot name='gender' />
{this.props.renderNormal()}
</View>)}}Copy the code
Dynamic slot
- Not free enough? It is dynamic! Come on!
Scenario: In the form form, if custom: true in data, render slot, otherwise render regular item
class Index extends Component {
render() {
return (
<View className='index'>
<CustomRender
data={[
{ name: 'name', label:'name',value: 'lawler'}, {name: 'password', label:'password',value: 'pwd...'}, {name: 'gender', custom: true}, // Custom rendergender item
]}
renderNormal={()= > <View>normal render prop</View>} > // Declare the slot content of custom in lower case<view slot='gender'>
<View>Gender: Male Radio? Female radio</View>
</view>
</CustomRender>
</View>)}}class CustomRender extends Component {
render() {
const { data } = this.props
return (
<View>{data.map(item => { const { name, label, value custom } = item if (! custom) return<View>{`normal item, ${label}: ${value}`}</View>// Dynamic slot, happy is done!! return<slot name={name} />
})}
{this.props.renderNormal()}
</View>)}}Copy the code
The last
-
Source code: Taro mini Demo
-
As for the script injection template in Taro Watch mode, we changed the index.tsx in Pages which will re-generate index.wxml, so we must re-inject yarn, but this does not affect the final build of the project
-
For ease of development, use fs.watch to listen for index.wxml in your build and automatically append the template if it changes. Of course, this script serves the company internal, will not share out, interested can email me ~
-
Like small partners, remember to leave your small ❤️ oh ~
The resources
-
Wechat miniprogram: Lottie -miniprogram
-
NervJS/taro-sample-weapp