Hello, everyone, I am South·Su, the chief pit filling officer of @IT· Teamhead Alliance. Today, I want to share with you a small program project recently made by our company. Some good summaries and pits encountered in the process, I hope to bring some convenience to other siege lions

Today is the first day of the Mid-Autumn Festival, the morning of the holiday inexplicable woke up very early, DO not know why, maybe because, last night the company online project on the way back, found a small bug, the heart a little uneasy ~ ~

The above pure for bullshit, now began to seriously loaded force, please fasten your seat belt, the middle process may drive, please pay attention to safety !!!!!

Recently, I communicated with my team mates about this project and finally chose Wepy among many frameworks. Instead of directly using native, small program native… We all know that using wePY framework has brought convenience to ourselves and also brought many pits. But even so, I still cherish the mentality of “even if you abuse me thousands of times, I still treat you like my first love” to do the project well seriously.

Toast components

  • As we all know, the official API Wx. showToast cannot meet our requirements, because it only supports two states: “success” and “loading”. At the same time, “title text can display 7 Chinese characters at most”. This is the official words. The style is ugly

Wx. showToast({title: 'success', icon: 'success', duration: 2000}) wx.showModal({title: 'prompt ', content: 'This is a modal popover ', success: Function (res) {if (res.confirm) {console.log(' user clicks OK ')} else if (res.cancel) {console.log(' user clicks cancel ')}}}) function(res) {if (res.confirm) {console.log(' user clicks ok ')}})Copy the code

The text of wx. ShowModal content is not in the center (now I am not sure whether there is an extension, can be set), vaguely remember once because of the problem almost quarrel with the product manager, let the text in the center, I said at least two hours, then the product exploded, what ghost?? It takes two hours to center the text. Two hours?? Two hours?? Then I decided to package my own toast component. The following is part of the core code

<template lang="wxml"> <view class="ui-toast {{ className }}" hidden="{{ ! visible }}"> <view class="ui-toast_bd"> <icon wx:if="{{ options.icon}}" type="{{ options.icon }}" size="40" color="{{ options.color }}" class="ui-toast_icon" /> <view class="ui-toast_text">{{ options.text }}</view> </view> </view> </template> <script> import wepy from 'wepy'; const __timer__ =1900; Class Toast extends wepy.component {/** * default data */ data={list:[{type: ={list:[{type: :}} `success`, icon: `success`, className: `ui-toast-success`, }, { type: `cancel`, icon: `cancel`, className: `ui-toast-cancel`, }, { type: `forbidden`, icon: `warn`, className: `ui-toast-forbidden`, }, { type: `text`, icon: ``, className: `ui-toast-text`, }, ], timer:null, scope: `$ui.toast`, animateCss:'animateCss', className:'', visible:! 1, options:{ type: ``, timer: __timer__, color: `#fff`, text: ` completed `,}} / * * * * / __setDefaults__ default parameters () {return {type: ` success `, timer: __timer__, color: # ` FFF `, text: 'Done', success() {}, __setVisible__(className = 'uI-animate -fade-in') {this.className = '${this.animatecss} ${className}`; this.visible = ! 0; this.$apply(); } /** * set the element to hide */ __setHidden__(className = 'uI-animal-fade', timer = 300) { this.className = `${this.animateCss} ${className}`; this.$apply(); setTimeout(() => { this.visible = ! 1; this.$apply(); }, timer)} /** * displays the column of @IT· The Chief Pit Filler, Su Nan, @param {Object} opts configuration item * @param {String} opts.type Prompt type * @param {Number} opts.timer Prompt delay * @param {String} Opts.color Icon color * @param {String} opts.text Prompt text * @param {Function} opts.success Closed callback Function */ __show__(opts = {}) {let options = Object.assign({}, this.__setDefaults__(), opts) const TOAST_TYPES = this.list; TOAST_TYPES.forEach((value, key) => { if (value.type === opts.type) { options.icon = value.icon; options.className = value.className } }) this.options = options; if(! this.options.text){ return ; }; clearTimeout(this.timer); this.__setVisible__(); this.$apply(); this.timer = setTimeout(() => { this.__setHidden__() options.success&&options.success(); }, options.timer); } __info__(args=[]){ let [ message, callback, duration ] = args; this.__show__({ type: 'text', timer: (duration||__timer__), color: '#fff', text: message, success: () => {callback&&callback()} }); } __success__(args=[]){ let [ message, callback, duration ] = args; this.__show__({ type: 'success', timer: (duration||__timer__), color: '#fff', text: message, success: () => {callback&&callback()} }); } __warning__(args){ let [ message, callback, duration ] = args; this.__show__({ type: 'forbidden', timer: (duration||__timer__), color: '#fff', text: message, success: () => {callback&&callback()} }); } __error__(args){ let [ message, callback, duration ] = args; this.__show__({ type: 'cancel', timer: (duration||__timer__), color: '#fff', text: message, success: () => {callback&&callback()} }); {} __showLoading__ (options) wx. ShowLoading ({title: (options && options. The title | | "load"),}); } __hideLoading__(){ wx.hideLoading(); } onLoad(){ this.$apply() } } export default Toast; </script>Copy the code

Call example:

<template> <view class="demo-page"> <Toast /> <Modals /> </view> </template> <script> import wepy from 'wepy' // @it · import Toast from '... /components/ui/Toast' import Modals from '.. /components/ui/Modals' import {fetchJson} from '.. /utils/fetch'; export default class Index extends wepy.page { config = { navigationBarBackgroundColor: "#0ECE8D", navigationBarTextStyle:"white", navigationBarTitleText: '' } components = { Toast: Toast, Modals: Modals} methods = {tapToast(){this.$invoke("Toast","__success__",[' you have committed successfully, thank you for your support ']); } } } </script>Copy the code

Storage

  • Storage(Storage) On the front end, the way we store,cookie,localStorage,sessionStorageSuch as these, features are not clear, small program we all know, data storage can only call wx.setStorage, wX. setStorageSync, equivalent to H5localStorageAnd thelocalStorageIt doesn’t expire, you know, and in a lot of interviews, the interviewer will ask this question, howlocalStoragelikecookieTwo hours, two days, even two minutes? This is the way we’ve been using it in our project, and it’s also useful in small programs:

class storage { constructor(props) { this.props = props || {} this.source = wx||this.props.source; } get(key) { const data = this.source, Timeout = (data. GetStorageSync (` ${key} __expires__ `) | | 0) / / expire if (Date. Now () > = timeout) {this. Remove (key) return; } const value = data.getStoragesync (key) return value} // Set cache // timeout: The expiration time (minutes) set (key, value, the timeout) {let data = this. Source let _timeout = the timeout | | 120; data.setStorageSync(key,(value)); data.setStorageSync(`${key}__expires__`,(Date.now() + 1000*60*_timeout)); return value; } remove(key) { let data = this.source data.removeStorageSync(key) data.removeStorageSync(`${key}__expires__`) return undefined; } } module.exports = new storage();Copy the code

Actually very simple, everyone looked after all “oh, that can also like” see, it’s just a thought, is a tip, every time when stored in an aging time stamp at the same time, and before to get the data, first compared with the current time, if less than the current time has expired, returns an empty data directly.

Interface API maintenance

  • ** API maintenance, ** before nodeJS, front-end seems to have been dealing with different environment to call the corresponding API, do more is to use the domain name to determine, of course, there are some advanced practice, the back end of the page rendering, Save a variable into the cookie or the page output a global API variable (based on without the separation of front and back side), the small program, it was the same as each time to manually change the environment, then a project may have different business, API to invoke a different domain name, and to distinguish the different environment, how can become better??

    Exports = {wabApi:{host:”dev-ali.southsu.com/XX/api/**”,}, QuestionApi: {host: “dev-ali.bin.com/question/ap…” ,}, mockApi: {host: “easy.com/mock/594635…” }, inWelApi: { host: “https://dev.**.com/Wab/api/escene/v2” } };

    import dev from ‘./env/dev’; Import uat from ‘./env/pre’; Import PRD from ‘./env/ PRD ‘; / / online

    var ENV = “prd”; //’dev | uat | prd’; let base = { dev, uat, prd }[ENV]; var config = { ENV, baseAPI:{… Base, env: env}, appID:”wx***b625e”, // isAuthorization:true, ‘logId’: ‘gvDSMhhaAS4QSSOtB-gZgzohsz ‘, ‘logKey’: ‘pxFOgJn3JyjOVr’, questionnaireNo:’zInsu’ // survey number}; export const DEBUG = (ENV! =”prd”); export default config;

    Example of a request to invoke API processing

    import wepy from ‘wepy’ import login from ‘./login’; import config,{DEBUG} from ‘./config’; import ‘wepy-async-function’; Export const fetchJson = (options)=>{/* * public data processing before request, Mei Bin’s column * @param {String} URL request address * @param {String} Type Request Type * @param {String} sessionId User userToken * @param {Boolean} openLoad enable load prompt, default, true- on, False – Disables * @param {function} StaticToast. For details, see Components/UI /Toast * @param {Object} header {Boolean} isMandatory specifies whether user authorization isMandatory. Obtain user information */

    StaticToast = getCurrentPages()[getCurrentPages().length - 1]; let { url,openLoad=true, type, data={},header={}, ... others } = options||{}; let sessionId = (Storage.get(__login__.server+'_userToken')||""); /*Start */ var regExp = /\/(.*?) \//, hostkey = url.match(regExp)[1]; let baseUrl = config.baseAPI[hostkey].host; url = url.replace(regExp, '/'); /*End */ __DEBUG__&&console.log('#--baseUrl:', baseUrl); __DEBUG__ && console. The log (' # - request address: '` ${baseUrl} ${url} `); __DEBUG__ && console. The log (' -- -- -- -- -- -- -- -- -- -- line -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- '); openLoad&&StaticToast.__showLoading__(); return new Promise((resolve, reject) => { return wepy.request({ url:`${baseUrl}${url}`, method:(type || 'POST'), data, header:{ "t9oken":sessionId, 'content-type': 'application/json', // 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', ... header }, success:(res)=>{ StaticToast.__hideLoading__(); return resolve(resHandler(res,options)); }, error:(err,status)=>{ StaticToast.__hideLoading__(); return reject(errorHandler(err,options,err.statusCode)); }}); })Copy the code

Business Invocation Example

fetchJson({ type:"post", // url:"/mockApi/service/XXX", Last request https://easy.com/mock/594635 * * c/miniPrograms/service/XXX (domain different environment is different, Data :{name:" console.log "}, success:res=>{console.log(' hello, I am @it · console.log ',res)}})Copy the code

It’s time to fill the hole

  • It’s time to fill the hole.wepyThe onload lifecycle callback for each component in the framework, as long as it is introduced into the component, regardless of whether your view is rendered or not, it will be executed, causing some business logic does not use it when it is also executed to generate an exception.Github.com/Tencent/wep… , Github.com/Tencent/wep…I don’t know if anyone has solved it.

Rich – text component

  • 民运分子rich-text,** a small program of a component, although there is a little bit of use, but have to say what it is used ah? I put up with the rest,aThe label,aTag ah, attributes have no, then need it ah, you don’t want me to jump, I still need you? B, I, span, em… Which one can’t I use? Don’t know whether this component design played the brain by a donkey, may god bless, I’m here to scold him, don’t be seen, haha ~), and the content of the business needs the background configuration with links, can’t, come on, make it, make it to the dead, all tergiversation is your excuse technology low (you see, you see, how others can jump, How do people do that? Give me a knife and I can chop the product to shreds), hence the backfill:

<template> <view class="test-page"> < button@tap ="cutting"> </button> <view wx:if="{{result.length>0}}" class="parse-list"> <view class="parse-view" wx:for="{{result}}" wx:key="unique" wx:for-index="index" wx:for-item="items"> <block wx:if="{{items.children&&items.children.length}}"> <block wx:for="{{items.children}}" wx:for-item="child" > <text wx:if="{{child.type == 'link'}}" class="parse-link" @tap="goToTap({{child.link}})">{{child.value}}</text> <text class="parse-text" wx:else>{{child.value}}</text> </block> </block> <text class="parse-text" wx:else>{{items.value}}</text> </view> </view> <Toast /> <Modals /> </view> </template> <script> // Column of Chief Pit Filler Su Nan, Import wepy from 'wepy' import {connect} from 'wepy-redux' import Toast from '.. /components/ui/Toast' import Modals from '.. /components/ui/Modals' import {fetchJson} from '.. /utils/fetch'; import Storage from ".. /utils/storage"; function wxHtmlParse(htmlStr=''){ if(! htmlStr){ return [] }; const httpExp =/(http:\/\/|https:\/\/)((\w|=|\? |\.|\/|\&|-)+)/g; Const aExp=/<a.[^>]*? >([(^<a|\s\S)]*?) <\/a>/ig; //a tag split regular let cuttingArr = htmlstr.split (/[\n]/); let result = []; / / have a tag HTML processing let itemParse = (itemHtml = ' ') = > {let itemCutting = itemHtml. Split (aExp) | | []; let itemResult = []; for(var i = 0; i<itemCutting.length; i++){ let _html = itemCutting[i]; if(_html! ==''){ let itemData = { value:_html, type:'text', class:"parse-text" }; let matchTag = itemHtml.match(aExp)||[]; If (matchtag.length){let itemIndex = matchtag.findIndex ((k,v)=>(K.indexof (_html)! = = 1)); if(itemIndex>=0){ let link = matchTag[itemIndex].match(httpExp)[0]; itemData.type = 'link'; itemData.link = link; itemData.class = "parse-link"; }; }; itemResult.push(itemData) } }; return itemResult; }; cuttingArr.map((k,v)=>{ let itemData = {type : "view",class:"parse-view"}; let isATag = k.match(aExp); if(isATag){ itemData.children = itemParse(k); }else{ itemData.value = k; }; result.push(itemData); return k; }); return result; }; export default class Index extends wepy.page { config = { navigationBarBackgroundColor: "#0ECE8D", navigationBarTitleText: "white", navigationBarTitleText: 'A tag in data'} Components = {Toast: Toast, Modals: Modals} data = {HTML :' Hello, my name is South Su, \ N Occupation: @IT· Brother Union - chief pit filling officer, \n Height: 176cm, \n Gender: Male, \n Sexual orientation: female, \ N Company: At present, HE is working in a business division of Shenzhen branch of an Internet finance company which is a joint venture of Tencent, Ali and Ping An. , gitHub:https://github.com/meibin08/, \ n interest: running, badminton, mountain climbing, music, read a book, share their meager knowledge to help others... , \n Others: Want to know more? Add < a href = "https://honeybadger8.github.io/blog/#/" > 386485473 < / a > communication group, Also can call me < a href = "https://github.com/meibin08/" > 134 xx852xx5 < / a >, Result :[]} methods = {cutting(e){this.result = wxHtmlParse(this.html); console.log(`result`,this.result); this.$apply(); }, } } </script>Copy the code

Remember Star, Star, Watch oh, thank you!

Today’s share here, I have been writing for a long time, I just started to try to write blog recently, I am a novice on the road, if there is something wrong in the article, please kindly correct it. If you found this article helpful, please remember to like it. Want to know more about me? Welcome to follow the public account below

Author: Su Nan – Chief Pit Filling Officer Communication group: 912594095, public account: honeyBadger8 Commercial reprint please contact @IT· Flathead brother alliance for authorization, non-commercial reprint please indicate the original link and source.

Links to this essay call: juejin.cn/post/684490…