preface

Preface don’t know what to say, but feel everybody have preface, I also can’t lose, that casually Lao dot good. I am a junior student, is a front side dish chicken, this is my first nuggets article. Previously, I teamed up with two teammates to write an original small program — Ape RESUME (online, but there is still a lot to improve). Recently, I learned VUE for the first time, and also involved in MPVUE, so I found a small project to practice, not only to find the feeling of small program, but also to practice the writing method of VUE. It was originally intended to be a one-week practice session to complete the main features, so it was really a small, small project with no technical difficulties, just a few tips to share. I hope you can see the happy.

Screenshots of some projects

Project preparation

When I wrote MPVue for the first time, I was not sure how to build the project, so I wrote the steps of building the project together, hoping to help you.

Initialize the project

Before initializing the project, ensure that the Node environment is properly installed on the machine

  • Download vue scaffolding

    npm install vue-cli -g
    Copy the code
  • Initialize the project

    vue init mpvue/mpvue-quickstart mpvue-zhirent
    Copy the code

    1. Whether to use VUEX (State management mode + library), no. Reason: Vuex is not always needed, this is just a small copycat project, so vuex is not used.

    2. Whether to use ESLint(pluggable linting utility for JavaScript and JSX), no. Because using ESLint would be very cumbersome.

  • Go to the project root directory

    cd mpvue-zhirent
    Copy the code
  • Install project dependencies based on package.json

    npm install
    Copy the code
  • Start the initialization project

    npm start || npm run dev
    Copy the code

Define the encapsulation of data and data requests

Define the data

The first thing that comes to mind when you see a professional applet is based onMVVM(Model-view-view Model) system architecture idea. Defining data then becomes the most important thing in project development. First of all, I searched the relevant websites of the employees, but I didn’t find any websites that could crawl the data (or MAYBE I didn’t see it), so I had to define fake data. And when it comes to defining fake data, the first idea is to useEasyMock(a persistent service that visualizes and quickly generates mock data) defines fake data. So hand masturbate false data masturbate to doubt life, at this time finally understand the programmer that the most beautiful love words – “I write interface for you?” .

I define the data by adding an “errno”:0 to determine the status of the interface request when the data is requested. Active and Sponsor are salon data and sponsor data respectively.

Data request and data request encapsulation

For this development I used Axios to request data.

  1. First, install AxiOS

    $ npm install axios
    Copy the code
  2. Find alias in the build > webpack.base.config.js file to add an alias for AXIos

     alias: {
      'vue': 'mpvue'.'axios':'axios/dist/axios'.The '@': resolve('src')}Copy the code
  3. Wrap axiOS by adding an axios.js file under SRC > utils

    import axios from 'axios'
    import qs from 'qs'
    
    axios.defaults.timeout = 30000;
    axios.defaults.baseURL =' ';
    axios.defaults.headers.post[ 'Content-Type' ] = 'application/x-www-form-urlencoded; charset=UTF-8';
    axios.defaults.adapter = function (config) {
      return new Promise((resolve, reject) => {
        let data = config.method === 'get' ? config.params : qs.stringify(config.data)
        wx.request({
          url:config.url,
          method:config.method,
          data:data,
          success:(res)=>{ 
            return resolve(res)
          },
          fail:(err)=>{
            returnReject (err)}})})} / / request interceptor axios. Interceptors. Request. Use (function(request) {// console.log(request) // The request succeededreturn request
    }, function( error ) { // console.log(error); // The request failedreturnPromise.reject(error); }); / / add the response interceptor axios. Interceptors. Response. Use (function(response) {// console.log(response.data.data) // The response succeededreturn response;
    }, function( error ) { // console.log(error); // The response failedreturn Promise.reject(error);
    });
    
    
    export function get (url,params) {
      return axios({
        method:'get',
        url:url,
        params:params
      })
    }
    Copy the code
  4. Encapsulate data requests in main.js files

    import Vue from 'vue'
    import App from './App'
    import axios from 'axios'// Import {get} from'./utils/axios'// Mount the get request to vue.prototype.$get=get;
    
    Vue.prototype.getList = function (){
      wx.showLoading({
        title:'Loading'
      }),
        this.$get("https://www.easy-mock.com/mock/5d17149edc925c312db9c9ea/zhirent/zhirent")
        .then((res) => {
          if (res.data.errno === 0) {
            this.active = res.data.data.active;
            this.sponsors = res.data.data.sponsors
          }
        })
        wx.hideLoading()
    }
    
    Vue.config.productionTip = false
    App.mpType = 'app'
    
    const app = new Vue(App)
    app.$mount(a)Copy the code

Stylus compilation

  1. Install the stylus and the stylus-Loader package
    $ npm i stylus stylus-loader -D
    Copy the code
  2. inbuild > webpack.base.config.jsFile to add find rules, add file handling rules
    {
        test: /\.css$/,
        loader: 'style-loader! css-loader! stylus-loader'
      }
    Copy the code

    The diagram below:

  3. Restart the service after the configuration is complete. Ensure that the service is restarted !!!!

Mpvue trample pit tour Start

Ok, the installation has also been installed, the configuration has also been configured, and finally we can start the pleasant and exciting Coding journey. The mini program includes three main pages of salon, organizer and me, as well as two pages of salon details and organizer details. My page contains information such as personal activity registration, organizer collection and activity collection. Next, I’ll share some of the potholes encountered in the project and the implementation of some of the major features.

Add pages and customize tabBar

Add a page

Mpvue does not add pages as easily as applets. You need to manually add files, manually configure page information, and manually add page paths. Every time you add a new page, be sure to add the path information to app.json!!

1. Create a page file

We need to manually create a page folder named index in SRC /pages and add two files named index.vue and main.js to index. Vue is our HTML page. Main.js cannot be renamed. If you change the name of main to any other name, an error will occur.

  • Index. vue needs to write the following basic structure before any page structure is written, otherwise it will appear

    Pages /mine/main.js script error or incorrectly called Page() error message

    <template>
      <div></div>
    </template>
    
    <script>
    export default {
    
    }
    </script>
    
    <style>
    
    </style>
    
    Copy the code
  • The basic structure of main.js is

    import Vue from 'vue'
    import App from './index'  // If index.vue is renamed home.vue, here index is changed to home
    
    const app = new Vue(App)
    app.$mount()
    Copy the code

2. Add a page

Every time you create a new page, add the page path manually to Pages in the SRC /app.json file, otherwise it will appear

NavigateTo: Fail page “pages/index/main” is not found

"pages": [
    "pages/index/main"
   ]
Copy the code

3. Configure the paging surface

NavigationBarTitle on the page can be set dynamically by calling the wx.setNavigationBarTitle({}) API in the onLoad(){} life cycle

onLoad(){
    wx.setNavigationBarTitle({
      title: 'authorization'})}Copy the code

You can also manually add a main.json file and statically set the navigationBarTitle of the paging page

{
    "backgroundTextStyle": "light"."navigationBarBackgroundColor": "#224fa4"."navigationBarTitleText": "Professional"."navigationBarTextStyle": "white"
}
Copy the code

Custom tabBar

SRC /app.json tabBar to make the changes, just like the tabBar custom process for the applet. Note that tabBar ICONS are placed in static/tabs and other page ICONS are placed in images for easy management. If the icon of the page can be selected in SVG format, select an SVG image, which will help save space for the applet. However, the applet tabbar-Icon does not support SVG images for the time being. I have set the image path alias in webpack.base.config.js, so the image path can be directly written static/tabs/car1.png, as shown below:

"tabBar": { "color": "#999", "backgroundColor": "#fff", "selectedColor": "#0c53ab", "borderStyle": "black", "list": [{"text": "tabs ", "pagePath": "pages/salon/main", "iconPath": "static/tabs/car1.png", "selectedIconPath": "Static /tabs/car.png"}, {"text":" sponsor ", "pagePath": "pages/ Sponsor /main", "iconPath": "Static /tabs/home1.png", "selectedIconPath": "static/tabs/home.png"}, {"text":" my ", "pagePath": "pages/mine/main", "iconPath": "static/tabs/mine1.png", "selectedIconPath": "static/tabs/mine.png" } ], "position": "bottom" }Copy the code

2. Authorization Page

After authorization, the page is redirected to salon. If the user clicks reject, the page will remain there. Next time the user enters the mini program, the page can still be redirected to the authorization page.

  • First, we need to determine whether the user is authorized by calling wx.getSetting() when entering the applet. If authorized, then we can get the user information by calling wx.getUserInfo(), and if not, jump to the written authorization page. Then we need to judge the onShow or onLaunch lifecycle in app.vue

    // Get the current Settings of the user. Only the permissions that the applet has already requested from the user appear in the return value.
    wx.getSetting({
      success(res){
        if (res.authSetting['scope.userInfo']) {
          // If authorized, you can call getUserInfo directly to get user information
          wx.getUserInfo({
            success: function(res) {
              console.log(res.userInfo)
            }
          })
        }else{
          // If not authorized, go to the authorization page
          wx.reLaunch({
            url: '/pages/authorize/main',})}}})Copy the code
  • Then, through the small program

    <button class="authorize" open-type="getUserInfo" @getuserinfo="getUserInfo"</button>Copy the code
    getUserInfo(){
      wx.getSetting({
        success:(res) = > {
          if(res.authSetting['scope.userInfo']){
            wx.showLoading({
              title:'Loading'
            })
            wx.reLaunch({
              url:'/pages/salon/main'
            })
            wx.hideLoading()
          }
        }
      })
    }
    Copy the code
  • After successful authorization, refresh the page to see the printed user information



At that time because of their own to do too much things really, product manager from 0 to 1, UI from 0 to 1, cut diagram son from 0 to 1, plus two big guys are out of the question, feel small chicken oneself is more impossible, also concentrate on doing their share of things. When they try to authorize the interface, it is very want to manual ai te my two teammates come in to be beaten. What’s the big deal, buddy?

....

So when you meet any problem, you should try it yourself, you can’t feel that others won’t, you must try to write it yourself!!

Salon/Sponsor page


These two pages are very simple, very intuitive two data rendering pages. Both pages are written almost the same, so I’ll pick one with less code.

The data load

First of all, let’s make a distinction between onLoad and onShow

  • OnLoad is triggered only once for a page. It is triggered when the page is loaded for the first time, usually for data that does not need to be updated in real time
  • OnShow is executed every time the page is opened, triggering the page load and generally placing real-time updated data

To sum up, we load these two pages of data and should request the data during the onLoad lifecycle. Since we already wrapped the data request in the main.js file, we can simply call the wrapped method.

onLoad(){
    this.getList();
  }
Copy the code

Data rendering

Obviously, both of these pages need to use data loops when you need to use V-for nesting.

// Sponsor page<div class="sponsor">
    <div v-for="(item,index) in sponsors"  :key="index"  @click="toSponsorInfo(index)" class="card-container">
      <div class="card-content">
        <div class="card-icon">
          <image class="icon" :src="item.avatar"></image>
        </div>
        <div class="card-info">
          <div class="card-title">{{item.name}}</div>
          <div class="card-desc">{{item.info}}</div>
        </div>
        <button @click.stop="collect(index)" :class="collectList[index] ? 'like' : 'unlike'">{{collectList[index] ? 'Concerned' : 'concerned '}}</button>
      </div>
      <div class="card-footer">
        <div class="card-salon-num">The salon {{item.salonNum}} will be held together</div>
        <div v-if="item.salonNum>0" class="card-recently">{{item.salons[0].title}}</div>
      </div>
    </div>
  </div>
Copy the code

When using V-for nesting, note that the same index cannot be used in mpvue for both the inner and outer loops. You need to change the index identifier of one of them, otherwise it will appear

V-for nested in the same component cannot continuously use the same index. The current value is index, and an error message is displayed indicating index

The use of the stylus

Because we have the Stylus configured in preparation for the project, we can use the Stylus directly in index.vue

<style lang="stylus" scoped>
</style>
Copy the code

Scoped means that the style is private to the page and does not pollute the world.

4. Jump to the details page

The page is displayed

v-for="(item,index) in sponsors

  1. Find the loop firstdivTag to bind an event and pass the index of the current list
    <div v-for="(item,index) in sponsors"  :key="index"  @click="toSponsorInfo(index)" class="card-container">
    Copy the code
  2. in<script>Add a methods property to the methods property to create a write method.
    // toSponsorInfo(index){// concatenate the URL and upload the received index to the page const url = '/pages/sponsorInfo/main? index=${index}`
      wx.navigateTo({
        url
      });
    Copy the code
  3. The onLoad life cycle in the details page receives the parameters passed through options
    onLoad(options){
     let i = options.index;
     wx.showLoading({
      title:'Loading'
    }),
    this.$http
      .get("https://www.easy-mock.com/mock/5d17149edc925c312db9c9ea/zhirent/zhirent"). Sponsor => {// If (sponsor = res.data.data. wx.hideLoading() }); }Copy the code

5. Data query



avtive
sponsors
name
sponsors
salons
title

  • The list of sponsors in salon details is given heresponsorsNumField for easy judgment. Data traversal search is required only when the sponsor number is greater than 0.
  • A list of salons in the organizer details is also given heresalonNumField, data traversal search is performed only when the number of salons held is greater than 0.

Take the sponsor details as an example

  • First of all, at the time of the data request, get all the salon data,
    this.$http
      .get("https://www.easy-mock.com/mock/5d17149edc925c312db9c9ea/zhirent/zhirent")
      .then((res) = > {
        // Get the sponsor details for the index
        this.sponsor = res.data.data.sponsors[i];
        // Get all the salon data
        let salonsInfos = res.data.data.active
    
        // Walk through the array to get the list of salons held in the host details
        // If the number of salons hosted by the sponsor is greater than 0, the array is traversed
        if(this.sponsor.salonNum>0) {// Run through the list of salons held by the host
          this.salonList = this.sponsor.salons.map(item= >{
            let sal
            // Run through all the salon data
            salonsInfos.forEach(salon= > {
             // When the theme of the salon is the same as that of the salon listed by the sponsor, get the information of the salon and replace the information with the title field of the salon list
              if(salon.title === item.title) sal = salon
            })
            return sal
          })
        }
        
         wx.hideLoading()
      });
    },
    Copy the code

    So, we have the correspondencetitleThe detailed data of the salon list, and then render the list to the page.

Six, my page

As can be seen from the GIF picture, my page is composed of three parts: the header displays user information, customer service and Settings, the TAB bar selects options, and the corresponding display page of TAB options.

User profile picture and nickname

There are two ways to display a user’s avatar and nickname:

  • The user’s image and nickname are obtained by calling wx.getUserInfo(), but user authorization is required to obtain the user’s image and nickname successfully.

  • Use the official

    tag to display the user’s avatar and nickname directly (without authorization)

    <open-data type="userAvatarUrl"></open-data> // Get the user's avatar to display directly in the applet <open-datatype="userNickName"></open-data> // Get the user's nickname to display directly in the appletCopy the code


    < img/image > < width=”50px”; height=”50px”; Border-radius =”50%” style, this will not take effect. The picture will look like the one below, without any change.

    So how can I change the style of the

    tag? We can add a display: Flex property to make the image size change, and a Overflow: Hidden property to make the rounded corners appear.

    <open-data class="thumb" type="userAvatarUrl"></open-data>
    Copy the code
    .thumb
        width 100%
        height 100%
        border-radius 50%
        overflow hidden
        display flex
    Copy the code

    And you’re done with a nice avatar display.

Customer service

Because this is only a small demo project, plus the customer service function also need to configure the developer information and add customer service account, it is really troublesome, I will not bother to do. However, there is an icon here, I always feel uncomfortable that I cannot click, so I added a click event, and when you wonder what it is, a prompt box will pop up that is not supported for the time being hahahahaha. Don’t ask, ask is lazy, don’t say, say is unnecessary.

If you want to know how to realize the customer service function of friends can check some small procedures official documents:Customer service message usage guide,Customer service Account Management.

Set up the

Click the pinion to jump to a set page, the page is divided into two items, the first item is the staff small program % %¥? I don’t know how to describe it, but he said it was about professionals. The second is the host center. These two clicks are a jump page, the two pages have no operation, is a page display. About the job page I directly cut a map up, after all, WE also have no UI little sister, can only steal lazy, not lazy program ape is not a good program ape HHHH.

TAB page switch (highlight highlight!)

When any TAB is clicked, the style and display changes accordingly.

Hiss, how does this style display dynamically? Add a class name to a DOM node by clicking on an event? That’s a little too much trouble. Add a data point to judge? What a hassle.

How can I display the page based on what I clicked on? Clicking on a TAB gives you data to determine whether to display the page or not? It’s too much trouble.

How to implement this seemingly simple but somewhat difficult little TAB bar?

  • First, we define a navList in the data source, and let the TAB field be rendered by the data, so that we can get its index value, and then assign the value through index.

    data(){
        return{
            // Give a default changeNav value,
            changeNav:0.navList: [{name:'参加'}, {name:'Interested'}, {name:'attention'}}}]Copy the code
  • We can then customize the data through the DATA-attribute of HTML and bind the custom data to the index value of the click.

    <div class="tab-item"  v-for="(item,index) in navList" :key="index" :data-current="index" @click="swichNav">
    Copy the code
  • Finally, custom data is retrieved through defined click events

    methods:{
        swichNav(e){
          const current = e.currentTarget.dataset.current
          this.changeNav = current
        }
    }
    Copy the code
  • For style issues, we dynamically add an active class name by determining whether the changed changeNav value is equal to the index value

    :class="{active:changeNav == index}"
    Copy the code
  • Page conversion is determined by V-if to show which page

    <Nothing v-if="changeNav==0" :tips="salonTip"></Nothing>
    <Interest v-if="changeNav==1" :salon="interestList" @goDetail="goSalonDetail"></Interest>
    <Collect v-if="changeNav==2" :sponsors="collectList" @go="goSponDetail"></Collect>
    Copy the code

This small program to fill in information what what, and after all, it is an activity, feel a little bit not good to sign up at will, so there is no sign up small page, directly show a salon page that did not participate in. (Not because of laziness)

7. Local cache achieves the function of sponsor concern




  • Step 1: Add a click event to the button and prevent bubbling events.

    First of all, after clicking the button to follow or not to follow, it will jump to the details page, because there is also a click event to jump to the details page in the parent element of the button. If no processing is done, the event of the target element will bubble to the parent element. So when we write button click events, we need to prevent bubbling events.
     <button @click.stop="collect(index)" :class="collectList[index] ? 'like' : 'unlike'">{{collectList[index] ? 'Concerned' : 'attention'}}</button>
    Copy the code
  • Step 2: Define a default array at the data sourcecollectList:[]Read the state of the local cache before page loading to see if there is any concern, if there is no local cache, the default arraycollectListAdd to local cache. If there is a local cache, the locally cached data is assigned tocollectList
    onShow(){
        var cache = wx.getStorageSync('collectList')
        if(! cache){ wx.setStorage({key:"collectList".data:this.collectList
          })
        }else{
          this.collectList = cache
        }
      }
    Copy the code
  • Step 3: InmethodsTo write click eventscollect(){}
    methods:{
        collect(index){
          // Prevent this from pointing to change
          let self = this
          // Get the cache collectList array
          var cache = wx.getStorageSync('collectList')
          // Get whether the current sponsor is being followed
          var currentCache = cache[index]
          // If there is no attention
          if(! currentCache){// Set the attention status of the current list to attention
            currentCache = true
            wx.showLoading({
              title: 'Loading',})// Assign the concerned state of the current list to the local cache
            cache[index] = currentCache
            // Reset the cache
            wx.setStorage({
              key:'collectList'.data:cache
            })
            // Assign the cache to the data source
            self.collectList = cache
            wx.hideLoading()
          }else{
            // Call the action menu if there is a cache state
            wx.showActionSheet({
              itemList: ['Unfollow'],
              success (res) {
                wx.showLoading({
                  title: 'Loading',
                })
                currentCache = false
                cache[index] = currentCache
                wx.setStorage({
                  key:'collectList'.data:cache
                })
                self.collectList = cache
                wx.hideLoading()
              }
            })
          }
    }
    Copy the code

    In this way, we have realized the function of clicking which sponsor’s attention button, which sponsor is convenient to change the status, then we need to do similar processing in the details page.

  • Step 4: Define one on the data source of the detail pageisCollected:''The cache state is read at page load time and assigned to the data sourceisCollected
    onShow(){
        // Get the attention of the cache
        var cache = wx.getStorageSync('collectList')
        // When the page receives the index parameter, option.index is assigned to the index in the data source, so we call this.index directly
        this.isCollected = cache[this.index]
    },
    Copy the code
  • Step 5: Add click events to the buttons on the details page
    <button @click="collect" :class="isCollected ? 'like' : 'unlike'">{{isCollected ? 'Concerned' : 'concerned '}}</button>
    Copy the code
    Methods: {// The following operation is no different from the above operation... I won't write it...collect(){
          var cache = wx.getStorageSync('collectList')
          let self = this
          var currentCache = cache[self.index]
          if(! currentCache){ wx.showLoading({ title:'Loading'
            })
            currentCache = true
            cache[self.index] = currentCache
            wx.setStorage({
              key:'collectList',
              data:cache
            })
            self.isCollected = cache[self.index]
            wx.hideLoading()
          }else{
            wx.showActionSheet({
              itemList: ['Unfollow'],
              success(res){
                wx.showLoading({
                  title:'Loading'
                })
                currentCache = false
                cache[self.index] = currentCache
                wx.setStorage({
                  key:'collectList',
                  data:cache
                })
                self.isCollected = cache[self.index]
                wx.hideLoading()
              }
            })
          }
        },
    }
    Copy the code

If and else have the same operation when determining whether or not there is an attention state, but I did not write them together. Instead, I wrote them separately. This is because, when written together, asynchracy occurs, and the next assignment is performed before the else operation is complete, causing the operation to fail. Solve asynchrony can be usedPromise“, but promise also repeats the code twice. Why not use the most straightforward way to solve the problem? So lazy Ben just repeats the code twice in the if judgment.

Display of interest/concern list

How do we get the list details data of interest and concern since we implemented the interest/concern function using local cache earlier? Again, we can get detailed data through array traversal. Take the sponsor concern, for example, we can get the host collection list in the cache when the data is loaded, and get the attention of the sponsor’s subscript array through the array traversal, and then get the details of the corresponding subscript organizer attention through traversal. Just render the data.

onShow(){
this.$get("https://www.easy-mock.com/mock/5d17149edc925c312db9c9ea/zhirent/zhirent")
      .then((res) = > {
        // All the organizers
        wx.showLoading()
        this.sponsors = res.data.data.sponsors;

        // Get the list of sponsors' favorites in the cache
        var cache = wx.getStorageSync('collectList')
        // Get the subscript array of the favorite sponsor
        this.targetList = []
        cache.forEach((item,i) = > {
          if(item) this.targetList.push(i)
        });

        // Get a list of collected sponsor information
        this.collectList = this.targetList.map(index= > {
          let spon
          this.sponsors.forEach((item,i) = > {
            if(index === i) spon = item
          })
          return spon
        })
        // console.log(this.collectList)
}
Copy the code

In addition, click any list of concerns/interests on my page to jump to the corresponding sponsor/salon details page, we just need to change the index value of the jump to the details page when writing the page.

goSponDetail(index){ const url = `/pages/sponsorInfo/main? index=${this.targetList[index]}`
  wx.navigateTo({
    url
  })
}
Copy the code

Nine, the details of the salon page some small functions

Click interested to synchronize user information

As with sponsor lists, lists implement features of interest that I won’t describe in detail. Unlike the sponsor page, the page loads to determine if you are interested, and if so, your profile should appear on the profile picture list when the page loads. When clicking uninterested, the personal information will be deleted. Since we wrote the authorization page and authorized the applet, we can call wx.getUserInfo() directly to get the user information

onShow(){
    self = this
    // Get user information
    wx.getUserInfo({
      success(res){
        var userInfo = res.userInfo
        var nickName = userInfo.nickName
        var avatarUrl = userInfo.avatarUrl
        // Assign user information to user in the data source
        self.user = {
          name:nickName,
          avatar:avatarUrl
        }
        var cache = wx.getStorageSync('interestList')
        self.isInterested = cache[self.index]
        if(self.isInterested){
          self.interestInfo.unshift(self.user)
        }
      }
    })
  },
Copy the code

Click on the preview image

This call applet official apiWx. previewImage is good.

Custom Action menu components (parent-child component communication)

Since the action menu is not right next to the bottom, we need to define a share component and give the data source an isShare:false state to determine whether the share component is displayed or not. First, we click the Share button to change the state in the data source to True. Then we define a click event in the Share button and send the event to the parent via $emit.

  • writeshareComponent and give the correspondingitemAdd the corresponding click event. Share with friends or groups directly via wechat officialbuttonIn the componentopen-typeProperty and willopen-typeIs set toshare, can realize sharing to friends function. Save the salon poster will automatically generate the two-dimensional code of the corresponding activity, there is no online two-dimensional code. So I’m not going to write that feature. One thing to mention here is the small programbuttonIf the background color is white, it will have its own border, herebuttonAdd aplain="true"Properties are good.
    <div class="container">
        <div class="tips" >Share the salon</div>
        <button plain="true" open-type="share" class="type">Share with friends or groups</button>
        <button plain="true" class="type" @click="save">Save salon posters</button>
        <div class="cancel" @click="cancel">cancel</div>
      </div>
    Copy the code
  • Add one to the share component’s data sourceshare:falseThe data of
    data() {
        return {
          share:false}}Copy the code
  • Click the Cancel button and the child component passes data to the parent via a $emit trigger event
    cancel(){
      this.$emit('cancel',this.share)
    }
    Copy the code
  • In the parent componentshareComponent and defined in the data sourceisShare:falseThrough theisShareTo determine whether to display the operation menu, and passv-onBinds a Cancel event to listen for the triggering event of the child component,
    <Share v-if="isShare"  @cancel="cancelShare"></Share>
    Copy the code
    <script> import Share from '@/components/share/share' export default{ data(){ return:{ isShare:false, } }, Components :{Share}, methods:{ IsShare cancelShare(MSG){this.isshare = MSG}} </script>Copy the code

This completes a simple custom action menu component. Since we are talking about parent-child communication, let me talk about how the parent communicates with the child in VUE, and how the sibling communicates with the child.

Parent component passes value to child component

Because the salon detail page structure is too many modules, I packaged each presentation module as a component to improve the readability of the code. And there are two pages related to the click to expand details, so I also encapsulated into a component, improve code reuse, here to click to expand details of the component to say how the parent component to the child component of the value of the problem.

  • Customize a text scaling component (? & %…… I don’t know how to describe this component, just call it that.
    <template> <div> <div class="info-desc" :class="isEllipsis ? 'ellipsis':'unellipsis'">{{info}}</div> <div class="text-expander" @click="ellipsis" > <text class="text-expander__content">{{isEllipsis? }}</text> <image class="text-expander__icon" : SRC ="isEllipsis? '/static/images/down.svg':'/static/images/up.svg'"></image> </div> </div> </template> <script> export default { data() {  return { isEllipsis:true } }, methods: { ellipsis(){ this.isEllipsis =! this.isEllipsis } }, props:[ 'info' ] } </script> <style lang="stylus" scoped> .info-desc font-size 14px display -webkit-box -webkit-box-orient vertical text-overflow ellipsis overflow hidden line-height 26px text-align left .ellipsis -webkit-line-clamp 3 .unellipsis -webkit-line-clamp 0 .text-expander margin 0 auto width 80px height 20px .text-expander__content color #c4c8c7 font-size 16px .text-expander__icon width 15px height 15px </style>Copy the code

The text is presented differently on different pages, so we need to receive data from the parent component. The parent component can pass data to the child component using the props property. The child component wants info data like the parent, and requests it via props.

  • References the child component in the parent component and passesv-bind(can be shortened to @) to bind data.
    <Expander :info="salonInfo"></Expander>
    Copy the code

This way, the child component can get the salonInfo data defined in the parent component. You can also learn about $ON (the child component receives data) and how siblings of non-parent components communicate with each other (build a Vue event bus object and emit events via bus.$ON.

conclusion

Conclusion also don’t know what to say, but others also have, I also can’t lose! Then feel free to talk, QAQ.

Originally, I was going to find a small project with simple interface and more functions to practice, but in the end, 80% of my work was still in cheatu. I have to say that Cheatu is really happy, so my deskmate Wang often jokes me: “Pure front end”, of course, referring to pure cheatu. The next time you write a small project, try jSONP to request data, or use a versatile crawler to crawl data, or develop a whole stack of small projects. Writing projects is also fun, and it’s a great feeling of accomplishment when you implement a small feature or come up with a solution (although there are really no technical difficulties in this project t-T). In the lastThe source codeHope I can help you a little diu Diu.