preface

Hello, I believe that after a summer break, and back to the beginning of the dream – school, always feel a lot, think about yourself and a year older, also want to start learning, he also want to become a big man, the summer did not learn all make up back, so just good, The following is my recent learning small procedures in the process of their own preparation of a copy of jingdong shopping small procedures. Here are the processes I developed and some of the issues I encountered. In line with the principle of learning and sharing, I hope to bring some help to your journey of learning small programs. I look forward to learning progress fighting with you.

Pre-development preparation

  • VSCode code editor
  • Wechat developer tools
  • Jingdong shopping mini program
  • Fiddler snatches pictures
  • Have a good vant component library
  • Exquisite ICONS (Alibaba Vector Icon Library)
  • Wechat applets developer documentation
  • Interface proportional color selection Mark Man

Projects show

Dynamic graph display

The following giFs are all videos recorded using 80 years of Nokia, the picture quality is not very good, I hope you don’t mind.

Static picture display

The overall architecture

This project is based on the small program cloud development and design, in the creation of the use of cloud development module, based on MVVM mode design of a small program, data-driven interface, componentized, modular design, and then combined with the small program with its own NoSql database data storage for systematic development. As I didn’t have a clear understanding of modularity at the beginning, part of the data in front was directly defined on the interface, and there would be some places that were not rectified in place when I made overall modifications later. Please forgive me. I will use what I think is more important as a directory structure for your convenience.

Project analysis

1. Analysis of the overall structure

First of all, I set up the pages interface directory at the beginning: but I mainly write home(main interface), shop_CART (shopping cart interface), detail(product details page).

| | - JINDONG_APP project name - cloudfunctions cloud function module | - getdatabase custom a cloud functions of the database data module | | - miniprogram project - components custom components | - Function - display display | how - the list of goods list. | | - data custom data - data js related don't transform data placed in | | - images - config tabBar pictures | - miniprogram_npm building has a great component library | - | | - home pages page jingdong shopping front page - the detail product details page. | | - shopcart shopping cart interface - app js global js | - app. Json global json configuration | - app. WXSS global WXSSCopy the code

I want to achieve the interface of JINGdong small program for the following tabBar writing, because the effect of the small program with tabBar can meet my needs, I did not customize tabBar components, If you need to customize tabBar, you can refer to the link below to understand the custom tabBar

"tabBar": { "color": "#424242", "selectedColor": "#57be6a", "borderStyle": "white", "backgroundColor": "# f2f0f0", "a list" : [{" text ", "home page", "pagePath" : "pages/home/home", "iconPath" : PNG ", "selectedIconPath": "images/config/home_act. PNG "}, {"text": "classification ", "pagePath": "pages/classify/classify", "iconPath": "images/config/classify.png", "selectedIconPath": "Images /config/classify_act.png"}, {"text":" shopping cart", "pagePath": "pages/shop_cart/shop_cart", "iconPath": PNG ", "selectedIconPath": "images/config/shop_cart_act. PNG "}, {"text": "my ", "pagePath": "pages/my/my", "iconPath": "images/config/my.png", "selectedIconPath": "images/config/my_act.png" } ] },Copy the code

2. Database design

The database I use is a small program with its own cloud database, is also a NoSql database, and the difference between the traditional MySql database I summed up a little:

  • MySQL is a relational database based on a table design, while NoSQL is essentially a non-relational document-based design.

  • 2. MySQL database, covering the huge IT market; MySQL databases with a fixed market contain a large community. NoSQL databases are the latest arrivals, and the community is growing slowly compared to MySQL.

  • MySQL’s strict schema restrictions are not easily extended, whereas NoSQL can be easily extended with the dynamic schema feature.

  • 4, MySQL requires a detailed database model before creating a database, but in the case of NoSQL database types, no detailed modeling is required.

  • MySQL provides a number of reporting tools that can help your application be effective, while NoSQL databases lack reporting tools for analysis and performance testing.

  • 6, MySQL is a relational database, its design constraint flexibility is low; NoSQL, by its nature, is non-relational and offers a more flexible design than MySQL.

  • 7. The standard language used in MySQL is SQL. NoSQL lacks a standard query language.

Here is my database table design in this small program:

In this small program I mainly created two tables, are written in Excel, and then use.csv file upload, here should pay attention to save. CSV file when using UTF-8 encoding CSV file format, otherwise the import will appear garbled phenomenon.

Shop goods table | -type types are used for different interface of data driven effect | - S_id commodity id, A sign | - S_label introduction | | - S_introduce goods - S - price commodities price | - S - img image shopclassify product category table | - S_id commodity id, associated with the shop table | - C_id category id, Used for traversing | - C_img category image quantity | | - C_number category - C_price category price | - C_classify category description shopcart shopping cart goods list (is based on two tables in front, in add some logo, It is added to the database after the goods are added to the shopping cart, in order to traverse the shopping cart interface. | - C_Number commodities selected number | | - C_classify merchandise category - C_id category id | - C_img image | - C_introduce introduction | - C_price price | | - S_id commodity id - selected It's whether or not you're in the selected stateCopy the code

There are two different ways to use a database in applets:

1. Directly use the following command to read (but pay attention to change the user permission)

const db = wx.cloud.database(); // shop is the name of the database set const shopCollection = db.collection("shop")Copy the code

You can also refer to the link here for detailed instructions

2. Use cloud function to read (compare the difficult to explain here I will not explain the partners who need to go to see the link below)

Cloud function

The above two ways I also briefly say their advantages and disadvantages

  • The direct reading method is relatively simple, but only 20 pieces of data can be read at a time. It is suitable for reading small amounts of data. If you want to read more data, you can only read multiple times. All in all, remember to make sure access is accessible to all users, so don’t panic when you don’t get data. Look at the permission Settings.
  • When using the cloud function to access, the steps are tedious, can read 100 data at a time. Suitable for multi-data reading. Add it up and the default is administrator access so you don’t have to set permissions.

3. Interface style analysis

The following is a simple introduction to a few interface styles.

Main interface Style

NavigationStyle :”custom” :” navigationStyle”:”custom” in the.json file.

2. You can refer to the official website for the use of vant components. Here I’m using the search component search

<view class="search"> <van-search background="" class="search__input" show-action shape="round" placeholder=" </van-search> </view>Copy the code

The following is the configuration of the Vant component (I configured it globally). Of course, you can also configure it in the.json file of the current interface, but for reuse, it is recommended to configure it globally:

// Global configuration in app.json, "usingComponents": {"van-search": "./miniprogram_npm/@vant/weapp/search/index", "van-goods-action":"./miniprogram_npm/@vant/weapp/goods-action", "van-goods-action-icon": "./miniprogram_npm/@vant/weapp/goods-action-icon/index", "van-goods-action-button": "./miniprogram_npm/@vant/weapp/goods-action-button/index", "van-stepper": "./miniprogram_npm/@vant/weapp/stepper/index", "van-checkbox-group":"./miniprogram_npm/@vant/weapp/checkbox-group/index", "van-checkbox":"./miniprogram_npm/@vant/weapp/checkbox/index", "van-popup":"./miniprogram_npm/@vant/weapp/popup/index", "van-cell":"./miniprogram_npm/@vant/weapp/cell/index", "van-swipe-cell":"./miniprogram_npm/@vant/weapp/swipe-cell/index" },Copy the code

If you want to configure the vant component globally, you must remember to set the “style” of app.json: The “v2” configuration has been removed, so when you see a component that looks a little different than described, don’t worry, check your configuration file. Vant configuration address

3. Use scroll View

<view class="navigation" > <scroll-view class="navigation__src" scroll-x style="white-space:nowrap; width:100%" scroll-left="{{navScrollLeft}}" scroll-with-animation="{{true}}"> <view class=" {{item.id == navigationindex ? 'navigation__title':'navigation__big'}}" wx:for="{{navigations}}" wx:key="id" scroll-x bindtap="changeType1" data-id="{{item.id}}"> {{item.title}} </view> </scroll-view> </view>Copy the code

4. Open box here I’m using the rotation of the image to represent the click on the arrow pointing in the direction before and after, and then using characteristics of priority development display box, does not use pop-up components vant, but in the back of the use, because the beginning of the component is not very understanding, a combined feeling rely too much on the component will lose their own thinking, so I can write your own, The renderings are above, and I’ll show them here. Here is my code:

<view class="{{logoIndex == 1? 'container__shadow':''}}"> <view class="shadowNav__all">{{navigation2s[0]}}</view> <view wx:for="{{navigation1s}}" wx:key="id" data-id="{{item.id}}" bindtap="changeType2"> <view class="{{item.id == navigationindex ? 'shadowNav__list' : 'shadowNav__border' }}" >{{item.title}}</view> </view> </view>Copy the code

Detail interface Style

1. How to select the first element in CSS (using first-letter) :

//wxml
<view class="shop__detail__price">¥{{item.S_price}}</view>
// wxss
.shop__detail__price{
    display: inline;
    color: red;
    font-size: 40rpx;
    position: absolute;
    top: 120rpx;
}
.shop__detail__price::first-letter{
    font-size: 20rpx;
    color: red;
}
Copy the code

2. Here I use the Vant component library Popup layer, how to introduce vant components, I explained in the beginning of the above, students who do not understand can go to the above look. Here is my code:

<van-popup show="{{ show }}" closeable position="bottom" custom-style="height: 80%" bind:close="onClose" > <view class="shop__cartimage"> <view class="shop__detail" wx:for="{{shopinformation}}" wx:key="S_id"> <image class="shop__detail__image" src="{{item.S_img}}"></image> <view class="shop__detail__vert"> <view Class ="shop__detail__price">¥{{item.S_price}}</view> <view class="shop__detail__level"> <span Class ="shop__detail__choice"> Selected </span> <span class="shop__detail__list">{{choose}},</span> <span Class = "shop__detail__num" > {{choiceNumber}} < / span > < / view > < view > < view > < the view class = "shop__classify__color" > < / view > color  <view class="shop__classify__desc"> <view class="{{item.C_id == classifyid ? 'shop__classify__onclick':'shop__classify__click'}}" wx:for="{{shopclassify}}" wx:key="id" data-id="{{item.C_id}}" bindtap="choiceclassify"> {{item.C_classify}} </view> </view> <view class="shop__number__list"> <view Class ="shop__number__name"> quantity </view> <view class="shop__number__mod"> < van-Stepper value="{{item.c_number}}" bind:change="onChange" /> </view> </view> <view class="bottom"> <view class="button-add" bindtap="addToCart" > </view> <view class="button-buy" bindtap="buyImmediately" > </view> </view>Copy the code

Pop-up layer effect I show here :(here the style I will not add a explanation, I believe that we are big guy, this directly pass

3. Finally, the bottom vant component GoodsAction merchandise navigation

// Total is the number of items in the cart, <van-goods-action> <van-goods-action-icon icon="shop-o" text=" shop-o" /> <van-goods-action-icon icon="chat-o" Text ="cart "info="{{total}}" bindTap ="cart" /> <van-goods-action-icon icon="cart-o" info="{{total}}" bindtap=" cart" /> <van-goods-action text=" Warning "/> <van-goods-action text=" buy now" /> </van-goods-action>Copy the code

Shopping cart interface

I won’t introduce the interface design, I believe you all understand, is simple use of elastic layoutHere is an introduction to the shopping cart implementation, single, multiple, all, and delete

Describe the implementation of the interface effect, i.e. icon:

// All use the effects of the icon, <icon wx:if="{{item.selected}}" class='cart-icon' type="success" color="red" /> <icon wx:else type="circle" class='cart-icon' /> // all select icon <icon wx:if="{{selectAllStatus}}" type="success" color="red" /> <icon wx:else type="circle" color="gray" />Copy the code

Component design

This is also a simple way to use an elastic layout, and then iterate, because this is used in many different interfaces, I packaged it as a component in the form of a design-list, so that it can be reused later. The important thing is to use lazy-load when you iterate through the image. It will make your interface experience better. If you don’t understand it, I will explain it later. Part of the code of the following component is actually written in the same way as ordinary interface, except that it is put in different places.

/ / how many - list component inside WXML < the view class = "how many" > < the view class = "recommend__title" > < image class="recommend__title__logo" src="https://636c-cloud1-0gtw4ft6b96fe5ff-1306520646.tcb.qcloud.la/love.png? Sign = e1279d83a1acb227e8b4273b22dbcc3d & t = 1628339354 "> < / image > < the view class =" recommend__title__name "> for you to select < / view > < / view > <view class="recommend__alllist"> <view class="recommend__list" wx:for="{{shopinformation}}" wx:key="S_id" data-id="{{item.S_id}}" > <image lazy-load class="recommend__list__img" src="{{item.S_img}}"></image> <view Class =" system-fliel ">{{item.s_rule}}</view> <view class=" system-fliel "> </view> </view> </view> background: #f4f4f4 } .recommend__title{ width: 100%; display: flex; text-align: center; justify-content: center; height: 50rpx; padding-bottom: 10rpx; } .recommend__title__logo{ width: 50rpx; height: 50rpx; } .recommend__title__name{ font-size: 30rpx; line-height: 50rpx; } .recommend__alllist{ display: flex; flex-wrap: wrap; } .recommend__list{ margin-left: 25rpx; width: 340rpx; height: 480rpx; margin-bottom: 20rpx; background-color: white; } .recommend__list__img{ width: 340rpx; height: 340rpx; border-radius: 20rpx; } .recommend__list__desc{ display: -webkit-box; overflow: hidden; -webkit-line-clamp: 2; text-overflow: ellipsis; -webkit-box-orient: vertical; font-size: 30rpx; background-color: white; } .recommend__list__price{ color: red; } // Recommend the js Component({/** * list of Component properties */ properties: {shopinformation:Array}, /** * initial data of Component */ data: {}, /** * component method list */ methods: {}}) // home interface use, and pass, that is, when calling the data to be passed in the form of shopinformation="{{shopinformation}}". <view class="modified2" bindtap="gotodetial"> <recommend-list shopinformation="{{shopinformation}}"></recommend-list> </view>Copy the code

In the above interface, the component for traversing the goods (reference-list) is introduced, and then we still use a component (function-display) : this part will not be introduced.

For those of you who feel that I’m not being very clear, please refer to the following link:

Custom Components

4. Interface service logic

The following is a brief introduction to the level of business logic involved in the interface

The data-driven interface of the main interface

When you click different buttons in the navigation bar, the interface data will change, using the effect of the data-driven interface (I did not change the middle part) :

Here’s how to achieve this effect. Key points

First of all, for the data design, this is the navigation data design, so that each data is matched with an ID. Then the design of the data to be displayed on the interface (just take one data for analysis)

// This is the navigation data design, Const navigations = [{id:"0", title:" home "}, {id:"1", title:" home "},},]; Type const information = [{id:"1", type:"1", name:" display ", icon:"",}, {id:"11", Type :"2", name:" 2", icon:""},],Copy the code

Write js for click events: the main idea is (believe to everyone’s level, easy to understand)

When you click on the buttons in the Navigations navigation bar, you get the id of the button data, which is then matched to the Type value of the Information data. We just need to render the data of type== ID to the relevant position of the interface. Here, we only need to match the data traversal to obtain the relevant corresponding data. It should be noted that the data on the home page is different from other interfaces, so it is necessary to judge. When ID =0, it is the interface of the home page, and other id==type will display different data in the same interface format.

The following code will be a bit messy, especially in this.setdata.

// WXML data-id="{{item.id}}", which passes the navigations data id to changeType1. <view wx:for="{{navigations}}" wx:key="id" scroll-x bindtap="changeType1" data-id="{{item.id}}"> {{item.title}} </view> //js async changeType1(e){ var singleNavWidth = this.data.windowWidth / 7; / / get the value of id let tid = e.c. with our fabrication: urrentTarget. Dataset. Id; let {data} = await shopCollection .where({Type:_.eq(Number(tid))}) .orderBy("S_price","desc") .get() this.setData({ shopinformation:data, navigationindex:tid, Banners:0, categories:0, shoop:0, logo, navigations, navigations1s:"", Navigations2s :"", information, simple1, simple2:0}) if(tid ==0){this.setData({navScrollLeft: (tid-2) * singleNavWidth, navigationindex:tid, logoIndex:0, a:0, Banners, categories, shoop, navigation1s:"", Navigation2s :"", information, simple1, simple2})} else{let lists = []; this.data.message.forEach(list=>{ if(list.type == tid ){ lists.push(list) } }) this.data.message.filter(list=>{ list.type == tid }) this.setData({ navScrollLeft: (tid-2) * singleNavWidth, logoIndex:0, navigation1s:"", navigation2s:"", navigationindex:tid, information:lists, a:1, simple2:0, shoop }) } },Copy the code

When the product jumps to the details page on the main interface, the corresponding one by one is displayed

When you click on a picture in the main screen, it will carry a matching ID when you jump to the details page

// wxml <view class="recommend__list" wx:for="{{shopinformation}}" wx:key="S_id" data-id="{{item.S_id}}" bindtap="gotodetial"></view> //js gotodetial(event){ let {id} = event.currentTarget.dataset; // This is where the id goes. wx.navigateTo({ url:".. /detail/detail? id="+id }) },Copy the code

Then when the details interface is loaded, you can get this ID, and then match with the database data to get the corresponding data. Note the type conversion here, the general use of the id is a string, you can use the cast, Number, parseInt to convert, if not ignored.

Async onLoad(options) {// The id is stored in the options. Let {id} = options; console.log(id,"-----------"); // Match with the database, data is the corresponding commodity data. let {data} = await shopCollection .where({S_id:_.eq(Number(id))}) .orderBy("S_price","desc") .get() }Copy the code

Type selection drives interface changes

When it comes to selecting a style, and then driving the data binding above, and the data binding outside, it is all done by: remember that only through this.setData will the data be rendered into the interface for display. So when you’re working with data that you want to change, be sure to call this.setData at the end.

Bind :click="showPopup" // WXML <van-cell title=" {{choose}}, {{choiceNumber}}" <view class="{{item.C_id == classifyid ? 'shop__classify__onclick':'shop__classify__click'}}" wx:for="{{shopclassify}}" wx:key="id" data-id="{{item.C_id}}" Bindtap ="choiceclassify"> {{item.c_classify}} </view> // js starts with the type this.data.choiceId = 1, which defaults to C_id = 1 , enter this interface by default first. onLoad(){ this.data.shopclassify.forEach(res=>{ if(res.C_id == this.data.choiceid){ this.setData({ choose:res.C_classify }) // classify. Push (res)}})} // Then you click that button and the interface changes with the id matching. choiceclassify(e){ let { id } = e.currentTarget.dataset; console.log(id); this.data.shopclassify.forEach(res=>{ console.log(res.C_id); if(res.C_id == id){ this.setData({ classifyid:id, choose:res.C_classify }) } }) }Copy the code

Add shopping cart function to achieve

I say once inside the relevant business logic: first to choose the number of goods category information and stored in the database, (here I choose to use a database, and no small program used to own Storage store data), because I feel the database data comparison can save more, and by the way, could you familiarize yourself with the database related operations. To note here is that the choice of commodity information saved in the database, a database can not save the same data, so to judge when to add database to have the data, if any, is to update the quantity of goods, if not directly add related information.

Async addToCart(){let {data} = await shopclassifyCollection .where(_.and([{C_id:_.eq(this.data.classifyid)},{S_id:_.eq(Number(this.data.Sid))}])) .get() let classifydata = [...data] let {data:data1} = await shopcartCollection Where (_. And ([{C_id: _. Eq (this) data) classifyid)}, {S_id: _. Eq (Number (this. Data. Sid))}])). The get () / / judgment have database data if(data1.length==0){ await classifydata.forEach(res=>{ // console.log(res); shopcartCollection.add({ data:{... res,C_Number:this.data.choiceNumber,selected:false,order:order,C_introduce:this.data.shopinformation[0].S_introduce} }) order = order +1; }) }else{ let {data:data2} = await shopcartCollection .where({C_id:_.eq(this.data.classifyid)}) .get() await Data2.foreach (res=>{this.setData({databaseNumber: res.c_number, show:false})}) Where ({C_id:_. Eq (this.data.classifyID)}).update({data:{ C_Number:this.data.choiceNumber+this.data.databaseNumber }, success:res=>{ console.log(res); Let {data:data2} = await shopCartCollection.get (); let {total:total1} = await shopcartCollection.count() this.setData({ total:total1, Wx. showToast({icon:"success", title: 'add to cart successfully ', duration: 4000})},Copy the code

Operations on shopping cart items

The following is the JS part, which is the business logic part of the preparation. Respectively are the transition of choice state and the calculation of price:

// The first step is to initialize the interface effect and take the data out of the database and display it on the interface. So here I'm taking the database and turning it into an object, Async onLoad(options) {let {data} = await shopcartCollection.get() this.setData({shopcart:data, totalPrice:0 }) shopcart = data console.log(shopcart); } / / radio, multiple-choice selectshop (e) {let index = e.c. with our fabrication: urrentTarget. Dataset. The index const selected = shopcart [index]. Selected; // Each click changes the state shopcart[index]. Selected =! This.setdata ({shopCart: shopCart}) this.getTotalPrice(); {}, / / selection selectAll () let selectAllStatus = this. Data. SelectAllStatus; selectAllStatus = ! selectAllStatus; for(let i=0; i<shopcart.length; i++){ shopcart[i].selected = selectAllStatus; } this.setData({ selectAllStatus:selectAllStatus, shopcart:shopcart }); this.getTotalPrice(); GetTotalPrice (){let total = 0; for(let i =0; i<shopcart.length; i++){ if(shopcart[i].selected){ total +=shopcart[i].C_Number *shopcart[i].C_price; }} this.setData({shopcart:shopcart, totalPrice: total.tofixed (2)})}, // Delete merchandise deleteShop(e){// console.log(e); let index = e.currentTarget.dataset.id; console.log(index); shopcart.splice(index,1); console.log(shopcart); this.setData({ shopcart:shopcart }) },Copy the code

Article details

  • Lazy-load, which translates as “Lazy loading.” It is optimized for image loading timing: Larger sites in the pictures (such as e-commerce sites home page, or group-buying websites, games, home page, etc.), if we try when the user opens a page, put all of the images to load resources, that is likely to cause the phenomenon such as white, caton, because there are too many pictures really, relief handle so many tasks, the browser can’t do! But then we thought, do users really need so many images? No, the moment the user clicks on the page, only part of the screen is shown to him (we call it the first screen). As long as we can load the image resources on the first screen when the page opens, the user will think the page is fine. As for the picture below, we can wait for the user to pull down the moment and then immediately request, immediately present to him. As a result, the performance pressure is reduced, but the user experience is not worse — a lazy-load process known as lazy-load.

  • For the design of data, a unique identifier is always set, which is called ID

  • For the use of the database, a very key point, is to see it as a normal, after getting the data, in fact, it is the same as your usual data processing, reduce the direct operation in the database level, will affect the effect of the interface display time. Since operations on data are asynchronous, there is a point of async and await.

  1. Async — Declare an asynchronous function (Async function someName(){… })
  • Automatically converts a regular function to a Promise, and the return value is also a Promise object
  • The callback function specified by the THEN method is executed only after the asynchronous operation inside the async function is complete
  • Async functions can use await internally
  1. Await – suspend asynchronous function execution (var result = Await someAsyncCall();)
  • Placed before a Promise call, await forces the rest of the code to wait until the Promise completes and returns the result
  • Can only be used with promises, not with callbacks
  • Can only be used inside async functions
  1. Usage tip: Async functions can be thought of as multiple asynchronous operations wrapped as a Promise object, and await commands are syntactic candy for internal THEN commands.

advice

  • Start by writing and conceiving the overall structure of the project you want to write
  • If you are going to use a database, make sure you have a primary key in mind when creating a table
  • In the development process, first look at the wechat small program developer documents to find some of their own to use the relevant features
  • In the process of writing business logic, use console.log() to print relevant data, so that you can more directly see where the error occurred
  • Talk to your teammates when you have a problem. Don’t spend too much time on one problem
  • Don’t plan for perfection. Everything has its flaws
  • You’ll be more motivated and your interface will be closer to perfection if you implement it first and improve it later

The source code

Project gitee source code address

conclusion

This is the first time for me to write a small project by myself, and I am very grateful for the help from my classmates and teachers in this process. The article explains what is not good, and what logic is not written well, and can be improved, etc. Please forgive me for my shortcomings. Of course, I also hope you can point out my shortcomings. At the same time, I hope my article can bring you some help. If you feel my article is ok, or if it is of some help to you, please kindly move your finger and give me a thumbs up. Thank you. Finally, I look forward to learning and progress with you.

Thank you for your support