2021-06-07 Silicon Valley takeout

1, Project development preparation

1. Project description

It is a takeout project. It is a front-end and back-end spa application (single page rich application), including multiple sub modules such as merchants, commodities, shopping carts and users. It is developed in a component, modular and engineering mode using front-end technologies such as Vue family bucket + ES6+Webpack.

2. Technology selection

Foreground data processing / interaction / componentization: vue2.5, Vue router, vuex, mint UI, Vue lazload, Vue scroller   , better-scorll,swiper,moment,data-fns   The Vue library includes vue2.5, Vue router, vuex, mint UI, Vue lazload and Vue scroller, and the sliding library includes Vue scroller   , Better scoll and swiper. Date processing includes moment and data FNS

Front and back interaction: mock data: mockjs, interface test: postman, ajax request: axios

Modularization: ES6, babel

Project construction / Engineering: webpack, Vue cli, eslint

css precompiler: stylus

3. API interface

The interface of front and back interaction. An interface contains four aspects of information: url, request mode, request parameter format and response data format. Two aspects of the test interface: whether it is connected or not, and whether it is consistent with the document.

4. What did you learn from the project

Development methods and patterns, the use of some plug-ins and libraries, and things in style layout

2, What did you do on the first day

1. Create a project using Vue cli scaffolding

2. Installs all dependencies and specified dependencies

3. Distinguish operation mode: development environment operation (packaging in memory) and production environment packaging and publishing

4. The understanding and use of stylus, like less, is a css precompiler, structured (with nested hierarchy consistent with html hierarchy), variables, functions and minxin (mixed)

5. Understanding and use of Vue Router: $Router: router object, which contains some function functions for operating routing to realize programmed navigation (jump routing)                                                                                                                    $ route: current routing object, container of some current routing information data, path / meta / parameters / query, etc                         Component labels: router view, router link, keep alive

  Project route splitting: app = > msite + search + order + profile      

Bottom navigation component: FootGuide  , The header component: HeaderTop is displayed / hidden through the routing meta, and the inter component communication tag structure is realized through slot   , Merchant list component: ShopList

6. Background project

Start: npm start  

Test background interface: postman

Modify interface document

7. Front and rear interaction

ajax request Library: axios

ajax request function encapsulation: axios+promise   Get the response.data data directly through promise

Object.keys   Returns an array whose elements are strings. The elements come from the attributes that can be enumerated directly from the given object. The order of these properties is the same as when you manually traverse the properties of the object

Note: in ES5, if the parameter of this method is not an object (but an original value), it will throw   TypeError. In ES2015, non object parameters will be cast to an object

// simple array
var arr = ['a', 'b', 'c'];
console.log(Object.keys(arr)); // console: ['0', '1', '2']

// array like object
var obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.keys(obj)); // console: ['0', '1', '2']

// array like object with random key ordering
var anObj = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.keys(anObj)); // console: ['2', '7', '100']

// getFoo is a property which isn't enumerable
var myObj = Object.create({}, {
  getFoo: {
    value: function () { return this.foo; }
  }
});
myObj.foo = 1;
console.log(Object.keys(myObj)); // console: ['foo']

Interface request function encapsulation: each background interface

Synchronize vue projects to remote warehouses using git( How to synchronize vue project to the blog of _godfreyzhu in the remote GitHub warehouse - CSDN blog)

8. Asynchronous display data

Configure the agent to realize cross domain (configured in index.js under the config folder)

proxyTable: {
      '/api': {   //Match all request paths starting with '/ api'
        target: 'http://localhost:4000 ', / / the basic path of the proxy target
        changeOrigin: true,  //Support cross domain
        pathRewrite: { //Rewrite path: Remove '/ api' from the beginning of the path
          '^/api': ''
        }
      }
    },

(agent: it is a program running on the foreground server. The agent intercepts the request sent by the foreground application to the front server and forwards it to the background server. The background application returns the response data, the background server returns the result to the agent, and the agent gives it to the foreground application)

Use vuex to manage status:

1. Create index configuration and state, action, mutation and getter object modules in the store folder

2. Each object module exposes its own attributes and methods and introduces them in the index

3. Register the store in main.js and use vuex

4. Update status in app.vue in two ways:   

import {mapActions} from 'vuex'

export default {
   mounted () {
    //  this.$store.dispatch('getAddress')
    this.getAddress()
  },
  methods: {
    ...mapActions(['getAddress'])
  },

5. Read the address.name data in MSite and display the local address asynchronously

<HeaderTop :title="address.name">


import {mapState} from 'vuex'

export default {
computed: {
    ...mapState(['address'])
  },
}

6. Asynchronously display the food classification rotation list

Generate a two-dimensional array from a one-dimensional array; execute before asynchronously updating the interface (solve it with watch and $nextTick)

 watch: {
      categorys (value) { // There is data in the categorys array, which is executed before asynchronously updating the interface
        // Using setTimeout can achieve the effect, but it is not very good
        /*setTimeout(() => {
          // Create a Swiper instance object to implement the rotation
          new Swiper('.swiper-container', {
            loop: true, // Can rotate
            // If pager is required
            pagination: {
              el: '.swiper-pagination',
            },
          })
        }, 100)*/

        // Swiper objects are created as soon as the interface is updated
        this.$nextTick(() => {// Once the interface update is completed, call immediately (this statement should be written after the data update)
          // Create a Swiper instance object to implement the rotation
          new Swiper('.swiper-container', {
            loop: true, // Can rotate
            // If pager is required
            pagination: {
              el: '.swiper-pagination',
            },
          })

          // new BScroll('.miste-content-wrapper', {
          //   click: true
          // })
        })
       }
      },

7. Display merchant data asynchronously

8. Use svg to display the loading interface

9.Star components

9. Register login function

1. Interface related effects

Switch login mode: add click on the corresponding a tag and click listen to change the loginWay status of class

Mobile phone number validity check: bind the mobile phone number input box in both directions, and use the calculation attribute to monitor the value of the input box. If it meets the regular expression, change the style of the 'get verification code' button to highlight, and add the clicked class style to the button

Countdown effect: when the button style is set to the correct mobile phone number, it can be clicked. Set click monitoring (the button is in the form form, and the default form submission behavior of clicking the button should be cancelled) to bind the countdown data. Add a cycle timer to the click to obtain the countdown function to achieve the countdown effect (remember to clear the timer when the time is less than 0) Finally, change the display effect before and after sending in the template

Switch between displaying or hiding passwords: add an input box of type text to the password input box, add binding showPwd data, use v-if to switch different password input box styles, use v-module to add two-way binding to the password input box, add click monitoring to the click button to switch the input box style, bind the class with mobile animation style div, and change the state according to showPwd data

(when there is only one style, bind the class to use the object method: {style name: status}. When there are two styles, use the judgment method: status? Style 1: Style 2)

Foreground verification prompt: bind the input boxes of verification code, account number, password and graphic verification code in two directions, create AlertTip component, receive alertText data and distribute custom closeTip function, bind the monitoring function login to the login button, and output the corresponding pop-up box if the conditions are not met (you can create a function to encapsulate duplicate code, remember to bind the distributed custom function)

2. Issues related to front and back desk interaction

How to check whether an application has sent an ajax request (the browser's network)

Send an ajax request 404 (the path of the request, whether the agent is effective or configured, whether it is restarted, and whether the server application is running)

Data is returned in the background, but the page is not displayed (whether there is data in vuex and whether it is correctly read in the component)

3. Perfect login and registration function (front and back interaction)

Dynamically obtain the one-time graphic verification code: add the interface path svg diagram to the src attribute value at the corresponding position, bind the listening function, and click refresh the one-time graphic verification code

(use this.$refs to get the component, and this.$refs.captcha.src to set the SRC attribute value of the graphic verification code. Note the change of SRC when clicking. You can use '? time='+Date.now() after the path to add a timestamp to make it change.)

(ref   Is used to register reference information for elements or child components. Reference information will be registered in the parent component  $ refs   Object. If used on an ordinary DOM element, the reference points to the DOM element; if used on a child component, the reference points to the component instance, and the reference information will be registered in the parent component  $ refs   Object, $refs is a collection of all registered refs)

Dynamic one-time SMS verification code

Use cloud communication to register an account, improve the interface request encapsulation function, and introduce it into the login routing component. In the login function, use await and reqSendCode functions to send ajax requests asynchronously. A pop-up box will be displayed when sending SMS fails

(intervalId needs to be set to this.intervalId, otherwise it cannot be used as a local variable inside the function)

(the background interfaces of this project have been written. They only need to be used and appear when sending text messages

statusCode: '111139', statusMsg: '[Account] master account suspended'

Error, change the account SID, auth token and appid (default) in the interface code, and then recompile and start)

4. An error is found when logging in with the password and mobile verification code. There is a problem with the server. There is a 500 error when logging in with the password and a 504 error when logging in with the mobile verification code. The server problem here cannot be solved. There is a problem with the login function.

5. Store user information in state, create a set of methods to obtain and change user information, change the corresponding display information of login interface and personal page interface, set different display effects before and after login, add click to jump routing function, and update the corresponding part of the home page.

6. Realize automatic login, because the user login information exists in the state, and the login information will be updated when the page is refreshed. In the background program, the user login is realized by using cookie s and sessions. Cookies save the account id and determine the saving time on the client side, and sessions on the server side

(difference between cookie and session) Detailed explanation and difference between cookie and session - Test Development meow - blog Garden )Use the reqUserInfo function to obtain the user login information, so as to realize automatic login

7. Realize asynchronous logout. Use the mint UI component library, the label component Button and the non label components MessageBox and Toast. When using the mint UI component library, you need some preparation. First download the npm run dependency and the development dependency packaged on demand, and then configure babel

 "plugins": ["transform-vue-jsx", "transform-runtime",["component", 
    {
    "libraryName": "mint-ui",
    "style": true
    }
    ]],

(note that the formats configured in different versions are different, and the latest version is available.)

Then, the corresponding components are introduced according to the official documents, and the logout function is used to log out the user

10. Build the overall business interface

1. Split the routing component and determine the basic style (there is a problem with the resource style, which has not been done yet)

2. Design json data

3. Use mockjs to simulate data and design mockServer interface. This interface does not need to expose any data, as long as it is running

4.ajax requests mockjs to simulate the interface, sets the request interface function, and uses $store.dispath to distribute the mounted in the component

5. Set merchant header display

(when obtaining data asynchronously, vue template is an empty object at the initial display and cannot display data. The obtained value can be displayed only after obtaining data from the background. At this time, an error will be reported: TypeError: Cannot read property '0' of undefined or null.                     It can be v-if judged that it will not be displayed when there is no data, but only when there is data.                                                             Summary rule: when the vue template parsing expression has only two levels A.B, it will not report an error, but it will report an error from the third level a.b.c. it is already undefined when parsing to the second level A.B.)

(review of transition animation settings, name="fade", css style settings)

6. Develop ShopGoods components

In the component mounted, use dispatch to obtain the asynchronous action data to vuex, then use... mapState in the calculated to obtain the data in vuex to the component, and finally call the display on the template.

Use the better scroll plug-in to realize the rebound sliding of the mobile end in the category list and commodity list. As in the case of swiper, it should be executed after the list data is updated, using the $nextTick statement. This time, use the callback function technique.

mounted() {
      this.$store.dispatch('getGoods',() => {  //Update after data execution
        this.$nextTick(() => {    //Execute after the list data is updated and displayed
          new BScroll('menu-wrapper')   //Create after list display
          new BScroll('foods-wrapper')
        })
      })
    },


 // Get product information asynchronously
    async getGoods({commit},callback) {
        const result = await reqGoods()
        if (result.code === 0) {
            const goods = result.data
            commit(RECEIVE_GOODS,{goods})
            //The data has been updated. Please inform the component
            callback && callback()
        }
    },

Initialize the array tops composed of the Y-axis coordinate scrollY of the right sliding and the top of all the right classification li

methods: {
      // Initialization method preceded by_
      // Initialize scrolling
      _initScroll () {
         //Create after list display
          new BScroll('.menu-wrapper',{

          }) 
          const foodsScroll = new BScroll('.foods-wrapper',{
            probeType: 2   //Inertial sliding is not triggered
          })
          // Bind scroll listening to the list on the right
          foodsScroll.on('scroll',({x,y}) => {
            this.scrollY = Math.abs(y)
          })
      },
      // Initialize tops
      _initTops () {
        // Initialize tops
        const tops = []
        let top = 0
        // collect
        tops.push(top)
        // Find all categories
        const lis = this.$refs.foodsWarpperUl.getElementByClassName('food-list-hook')
        // Change pseudo array to true array
        Array.prototype.slice.call(lis).forEach(li => {
          top += li.clientHeight
          tops.push(top)
        });
        // Update data
        this.tops = tops 
      }
      
    },

Slide the right list to update the left option value

 currentIndex () {  // Initial and relevant data have changed
      // Get data conditions
        const {scrollY,tops} = this
      // Calculate a result according to the conditions
        const index = tops.findIndex((top,index) => {
          return scrollY >= top && scrollY < tops[index + 1]
        })
        // Return results
        return index
      },

(solve the bug that inertial sliding does not update the current classification)

The first way is to change the probeType configuration value to 3. The second way is to bind scrollEnd to the list on the right (listen)

Click classification to slide the list on the right

// Event callback method
      clickMenuItem (index) {
        // Slide the right list to the corresponding position
        // Get the scrollY value of the target location
        const scrollY = this.tops[index]
        // Update the scrollY value immediately (click update now to display the classification list on the left)
        this.scrollY = scrollY
        // Smooth scrolling right list
        this.foodsScroll.scrollTo(0, -scrollY,300)
      }

7. Add CartControl component, accept food data, and add / reduce two functions. You can use Boolean value to pass value to realize two uses of one function (judge whether to add)

How to add data binding to the new attributes of bound Vue data (the first case is in vuex: use the set method of Vue syntax, and Vue.set(obj,'xxx',value) can pass three values: object, attribute name and attribute value. The second case is in the component: use this.$set(obj,'xxx',value))

(why does the food list on the right slide and the classification list on the left slide together to the part not displayed at the bottom:

Need to simulate click acquisition, mouse sliding in the browser is invalid)

8. Add Food component and introduce child components into ShopGoods parent component

 // Show clicked food
      showFood (food) {
        // Set food
        this.food = food
        // Display food component (method of calling sub component object in parent component)
        this.$refs.food.toggleShow()
      }

9. Add the ShopCart component, use state to manage the food list in the shopping cart, getters to manage and calculate the total quantity and price of food in the shopping cart, calculate the attribute in the component, and monitor the quantity of food in the shopping cart to determine whether to highlight and display the distribution information

(a bug is found. It may be the style of the shop component. After clicking shoplist, you enter the redirected routing component shopGoods, but the three buttons of the router link are squeezed out. If there is no redirection, you will enter the shop routing component. After clicking the router link button, you enter the sub routing component of shopGoods, and the button is squeezed out again. Because the technical problem has not been solved)

The shopping cart that pops up after clicking is a bscoll instance. Multiple clicks will not produce multiple bscoll instance objects, resulting in the problem of adding the number of shopping carts after clicking Add

Because the list in ShopCart is not an initialized list, it will be refreshed dynamically. After adding the better scroll plug-in, you should refresh the scroll bar

Bind and click to empty the shopping cart. Use the dispatch distribution clearpart method to update the data of food.count in the cartFood array in state to 0 and empty the array to achieve the effect of emptying the shopping cart

10. Add the ShopRatings routing component to display the merchant's user evaluation. Filter the array ratings so that the two conditions are all, positive and negative, and whether to only see the evaluation with content. Click to switch the display

filterRatings () {
      const {ratings,onlyShowText,selectType} = this
      // Generate a new filtered array
         return ratings.filter(rating => {
          const {rateType,text} = rating
                    /*
            Condition 1:
                selectType: 0/1/2
                rateType: 0/1
                selectType===2 || selectType===rateType
            Condition 2
                onlyShowText: true/false
                text: With / without value
                !onlyShowText || text.length>0
           */
          return (selectType === 2 || selectType === rateType) && (!onlyShowText || text.length > 0)
        })
    }

11. Add the ShopInfo routing component to display the shop details of the merchant, and set the vertical sliding and horizontal sliding. The value of ul during horizontal sliding should include the values of all li. After refreshing the routing page, the initial value is an empty array. If you create a bscoll instance in mounted, [Vue warn]: Error in mounted hook: "TypeError: Cannot read property 'length' of undefined" will appear   bug, reason: the current routing object is created immediately after refreshing. Asynchronous request to obtain data (when mounted, it should be used when the data already exists). At this time, two situations should be considered: 1. When other routing components are switched over, 2. When the current routing component is refreshed

Solution 1:

mounted() {
     // If the data is not available, end it directly
    if (!this.info.pics) {
      return
    }
    // With the data, you can create a bscoll object to form a slide
    this._initScroll()
  },
  methods: {
    _initScroll () {
      new BScroll('.shop-info')

      //Dynamically calculate the width of ul
      const ul = this.$refs.picsUl
      const liWidth = 120
      const space = 6
      const count = this.info.pics.length
      ul.style.width = (liWidth + space) * count - space + 'px'

      new BScroll('.pic-wrapper',{
        scrollX: true  //Horizontal sliding
    })
    }
  },

Solution 2:

watch: {
    info () {  // Refresh process -- > update data
      this.$nextTick(() => {  //Asynchronous creation
        this._initScroll()
      })
    }
  }

12. Add Search routing component

The main function is to input content and click search to display the list of businesses with relevant content. At this time, it should be noted that the data is displayed only when there is a value in searchShops. No search results are displayed when it is not found after search. Whether it is displayed in v-if depends on whether the monitoring method getSearchShops has a corresponding value

computed: {
      ...mapState(['searchShops'])
    },
    methods: {
      search () {
        // Get search keywords
        const keyword = this.keyword.trim()
        // Search
        if (keyword) {
          this.$store.dispatch('getSearchShops',keyword)
        }
      }
    },
    watch: {
      getSearchShops (value) {
        if (!value.length) {   // no data
          this.noSearchShops = true
        } else {     // Have data
          this.noSearchShops = false
        }
      }
    },

13. Cache routing component object

Benefits: reuse the routing component object, reuse the background data obtained by the routing component, and store the component object in the browser side memory

<keep-alive>
      <router-view></router-view>
    </keep-alive>

14. Lazy loading of routing components

When necessary, go to the background to request the code of the routing component

// import MSite from '../pages/MSite/MSite'
// import Order from '../pages/Order/Order'
// import Profile from '../pages/Profile/Profile'
// import Search from '../pages/Search/Search'
// Lazy loading of routing components
const MSite = () => import('../pages/MSite/MSite')
const Order = () => import('../pages/Order/Order')
const Profile = () => import('../pages/Profile/Profile')
const Search = () => import('../pages/Search/Search')

15. Picture loading

Using the Vue lazload plug-in

import VueLazyload from 'vue-lazyload'

import loading from './common/imgs/loading.gif'

Vue.use(VueLazyload, { // Internally customize an instruction lazy
  loading
})


  <img v-lazy="food.image">

16. Implement date filter with moment

Configure the index file in the filters folder (multiple filters can be configured), and then use it in the component

import Vue from 'vue'
// import moment from 'moment'
import format from 'date-fns/format'
// Custom filter
Vue.filter('date-format', function (value,formatStr='YYYY-MM-DD HH:mm:ss') {
    // return moment(value).format(formatStr)
    return format(value, formatStr)
})

17. Packaged file analysis and optimization

vue scaffolding provides a package webpack bundle analyzer and configuration for visual analysis of packaged files

Enable packaging visualization: npm run build --report     The packaged file forms a visual graphical interface, including all parts of the file

You can see that the moment is too large. Use date FNS instead

import Vue from 'vue'
// import moment from 'moment'
import format from 'date-fns/format'
// Custom filter
// The moment uses the format YYYY-MM-DD HH:mm:ss
Vue.filter('date-format', function (value,formatStr='yyyy-MM-dd HH:mm:ss') {
    // return moment(value).format(formatStr)
    return format(value, formatStr)
})

It was completed on June 23. Although some interfaces in the middle failed, resulting in some pictures not coming out, the two login methods could not log in due to server problems, and there were some style bug s (css style was not well learned and could not be improved). I fished in the middle for some time and was a little confused, but now it is determined to go further and further on the front-end road in the future, I hope I don't forget my original heart and have the courage to explore.

Tags: ECMAScript Vue.js

Posted on Fri, 24 Sep 2021 22:02:32 -0400 by SCRUBBIE1