Vue Shangpinhui Mall Project-day02 [15. Dynamic display of three-level menu linkage]

insert image description here

15. Dynamic display of three-level menu linkage

Question 1:

insert image description here

code:

<script>
export default {
  name: "TypeNav",
  computed:{
    ...mapState({categoryList:state=>state.home.categoryList})
  }
}
</script>

Reason analysis: because of the lack of mapState import, so the error mapState does not know, just add the import

import { mapState } from "vuex";

15.1 Dynamic call display three-level menu steps

A brief summary of the steps to use:

  1. Define functions in submodule actions and mutations
  2. Trigger the call in App.vue
  3. Write calculated properties so that the latest data is already saved in the state
  4. Traversing the dynamic splicing of the latest data at the template code for display

Detailed usage steps:

  • Step 1: /store/home/index.js introduces methods to define functions in actions and mutations.
import {getCategoryList} from '@/api'
//Home模块的小仓库
//actions代表一系列动作,可以书写自己的业务逻辑,也可以处理异步
const actions = {
    //getCategoryList返回的是一个Promise对象
    //需要用await接受成功返回的结果,await必须要结合async一起使用(CP)
    async getCategoryList(context) {
        let response = await getCategoryList();
        if (response.code == 200) {
            context.commit("GETCATEGORYLIST", response.data)
        }
    }
}
//mutations代表维护,操作维护的是state中的数据,且state中数据只能在mutations中处理
const mutations = {
    GETCATEGORYLIST(state, categoryList) {
        state.categoryList = categoryList
    }
}
//state代表仓库中的数据
const state = {
    //home仓库中存储三级菜单的数据
    categoryList:[]
}
//getters理解为计算属性,用于简化仓库数据,让组件获取仓库的数据更加方便
const getters = {}

//创建并暴露store
export default {
    actions,
    mutations,
    state,
    getters
}
  • Step 2: Define the mounted hook function in App.vue, dispatch an action, and obtain the data of the three-level list of commodity classification.
mounted() {
    //派发一个action||获取商品分类的三级列表的数据
    this.$store.dispatch("getCategoryList")
  }
  • Step 3: /components/TypeNav/index.vue, use mapState to generate calculation functions where state data is to be used, and then loop through the used data in the template.
<div class="item bo" v-for="(c1,index) in categoryList" :key="c1.categoryId">
    <h3>
        <a href="">{
   
   {c1.categoryName}}</a>
    </h3>
</div>

computed:{
    ...mapState({categoryList:state=>state.home.categoryList})
  }

Question 2: Why does the getCategoryList function defined by actions use async+await?

Answer: Because the getCategoryList function finally returns a promise object, as shown in the figure, and what we directly want is a successful callback function, so we can directly obtain it with async+await.

insert image description here

15.2 Complete the first-level menu mouse into the display background color

The effect is as follows:

insert image description here

There are 2 ways:

  • Method 1: css directly uses :hover to add background color, knowledge point: the special style added by hover when the mouse moves over the link
//方式1::hover
.item:hover {
 	background: skyblue;
}
  • Method 2: Add the background color by defining the mouse-in event. The idea shows that each item has its own index. We initialize the currentIndex to -1, and modify the value of currentIndex when the mouse is in. When judging that currentIndex == index, it will be dynamic Bind the style and match @mouseenter and @mouseleave at the same time to achieve the background effect of sliding in and out.
<div @mouseleave="leaveIndex" >
<h3 @mouseenter="changeIndex(index)" :class="{cur: currentIndex == index }">

methods: {
    //用于修改组件实例身上的currentIndex的属性值
    //当用户鼠标移入到h3身上的时候就会立即出发一次
    changeIndex(index) {
      this.currentIndex = index;
    },
    //当鼠标离开的时候,让商品分类列表进行隐藏
    leaveIndex() {
      this.currentIndex = -1;
    }
}
  
.cur {
    background: skyblue;
}

注意点1:mouseenter: When the mouse moves into the element itself (not including the child elements of the element), the event will be triggered, and the event will not be superimposed. The corresponding remove event is mouseleave.

注意点2: Now I want to achieve a new effect. The background color will not be removed when the mouse is moved from the first item to "All Product Category". The logic of control is to wrap the "all product categories" and the first-level menu with a div, and draw the control on the outside.

15.3 Controlling the display and hiding of the second and third-level product categories

There are 2 ways:

  • Method 1: Control the block|none display and hide of display through :hover of css
.item-list {
            display: none;
            ...
}
&:hover {
    .item-list {
        display: block;
    }
}
  • Method 2: Control the display and hide of the display through js
 <div class="item-list clearfix" :style="{display: currentIndex == index ? 'block' : 'none'}">

Question: What does &:hover in css mean?

Answer:

Paste a CSS code to explain

.box{
		&:before{
			border-color: red;
		}
		&:after{
			border-color: green;
		}
	}

& means to go back one layer in the nested hierarchy, that is, &:before is equivalent to .box:before

15.4 Demonstration of stuck phenomenon, introducing anti-shake and throttling

Normal: The event is triggered very frequently, and every time it is triggered, the callback function must be executed (if the time is short and there is calculation inside the callback function, then the browser may freeze), the normal effect is shown in Figure 1, and the error Effects such as, 2.

insert image description here

Figure 1

insert image description here

Figure 2

Throttling: The callback will not be triggered repeatedly within the specified interval time range, and the callback will only be triggered if the time interval is greater than this time interval, turning frequent triggers into a small number of triggers [give the browser enough time to parse the code].

Anti-shake: All the previous triggers are cancelled, and the last execution will only be triggered after the specified time, that is to say, if the triggers are triggered continuously and quickly, they will only be executed once.

Caveat 0:

安装命令:cnpm install --save lodash

How to use:

//引入lodash:是把lodash全部封装好的函数全都引入进来了
//按需引入:只是引入节流函数,其他的函数没有引入(模块),这样做的好处是,当你打包项目的时候体积会小一些
import throttle from "lodash/throttle";

changeIndex: throttle(function(index) {
      //修改当前currentIndex索引值
      //函数节流:在20MS时间之内只能执行一次
      this.currentIndex = index;
    }, 20),

注意点1: You can use the lodash plug-in: the anti-shake and throttling business method of the encapsulated function [closure + delayer]

注意点2: What is the difference between anti-shake and throttling?

Answer: "Throttling" is executed only once within the specified time, and "anti-shake" is the last execution no matter how long it takes.

注意点3: lodash official website: https://www.lodashjs.com/

Anti-shake:debounce(func, [wait=0], [options=])

Throttling:throttle(func, [wait=0], [options=])

15.5 Reuters jump and transfer parameters of three-level linkage components

Three-level linkage users can click: first-level classification, second-level classification, and third-level classification. When you click, the Home module will jump to the Search module. The first level will put the product (product name, product ID) selected by the user in the routing When jumping, pass it on.

Routing jump:

  • Declarative navigation: router-link

  • Programmatic navigation: push|replace

Question 1: Should you choose "declarative navigation" or "programmatic navigation"?

Answer: Programmatic navigation should be used. If you use the declarative navigation router-link, you can realize the routing jump and pass parameters, but you need to pay attention, there will be a freeze phenomenon, because the three-level linkage menu is generated by cyclic traversal, if the cyclic traversal is 1000 times, it will have to be newly generated/ Tabs take up a lot of memory and will definitely freeze.

Question 2: Since "programmatic navigation" is selected, is the / label bound to the click event? Or use other methods?

Answer: You can't use the a tag binding event method, you should use the "parent element delegation event" method. Because it only needs to define the function once, but with the a tag, I have to loop through and define the function 1000 times.

Question 3: We want the effect to be to click on the a label to jump to the route. How to confirm the a label?

Answer: Bind custom attributes to the a tag, and use event.target.dataset to get the custom attribute name. The one that can get the custom attribute name must be the a tag we want.

Question 4: After adopting the "parent element delegation event" method, there will be an error effect as shown in the figure: when clicking on the first-level menu -> first jump to the search page -> then automatically jump to the home page

Answer: The href attribute of the / tag must be deleted

Question 5: What is the name of the custom attribute called data-xxx? The output and printed dataset names are all lowercase, but why is the data- in front gone? For example, the name of the custom attribute is: data-categoryName. Why is the last name obtained by the dataset is categoryname, the obvious prefix data- is gone, and it is inexplicably lowercase?

insert image description here

Answer: If you want to use dataset to obtain custom attributes in JS, then the custom attribute name must be "data-xxx" in the HTML template.

The following is an excerpt from someone else's blog:

HTML5 has added the data-* method to customize attributes, in fact, just add the prefix data- before the custom attribute name, and use this structure to store data. Using data-* can solve the current situation of messy and unmanaged custom attributes.

Once a custom is prefixed with data-, this attribute can be obtained through elementNodeObject.dataset in JS, obviously dataset is a subset of attribute collection.
The value of the dataset attribute is an instance of DOMStringMap, which is a map of name-value pairs. In this mapping, each attribute of the form data-name will have a corresponding attribute, but without the data- prefix.

Complete code src/commponents/TypeNav/index.vue

<!--1级联动菜单-->
        <div class="sort">
          <div class="all-sort-list2" @click="goSearch">
            <div class="item bo" v-for="(c1,index) in categoryList" :key="c1.categoryId">
              <h3 @mouseenter="changeIndex(index)" :class="{cur: currentIndex == index }">
                <a :data-categoryName="c1.categoryName" :data-category1Id="c1.categoryId">{
   
   {c1.categoryName}}</a>
              </h3>
              <!--方式2:通过js的方式控制display的显示与隐藏-->
              <div class="item-list clearfix" :style="{display: currentIndex == index ? 'block' : 'none'}">
                <div class="subitem">
                  <!--2级联动菜单-->
                  <dl class="fore" v-for="(c2,index) in c1.categoryChild" :key="c2.categoryId">
                    <dt>
                      <a :data-categoryName="c2.categoryName" :data-category2Id="c2.categoryId">{
   
   {c2.categoryName}}</a>
                    </dt>
                    <dd>
                      <!--3级联动菜单-->
                      <em v-for="(c3,index) in c2.categoryChild" :key="c3.categoryId">
                        <a :data-categoryName="c3.categoryName" :data-category3Id="c3.categoryId">{
   
   {c3.categoryName}}</a>
                      </em>
                    </dd>
                  </dl>
                </div>
              </div>
            </div>
          </div>
        </div>
------------------------------------------------------------------------------------------------------
//进行路由跳转的回调函数
    goSearch(event) {
      //最好的解决方案:编程式导航+事件委派
      //存在一些问题:事件委派,是把全部的子节点【h3、dt、dl、em】的事件委派给父亲节点
      //问题1:点击a标签的时候,才会进行路由跳转【怎么能确认点击的一定是a标签?】
      //答案:给a标签绑定自定义data-categoryName属性,只要能获取到自定义属性就代表是a标签
      //event.target:获取到的是触发事件的元素(div、h3、a、em、dt、dl)
      let element = event.target;
      //节点有一个属性dataset,可以过去节点的自定义属性与属性值
      let {categoryname, category1id, category2id, category3id} = element.dataset;
      //如果标签身上带有categoryname一定是a标签,且当前这个if语句:一定是a标签才会进入
      if (categoryname) {
        //准备路由跳转的参数对象
        let location = {name: 'search'}
        let query = {categoryName: categoryname}
        //一级目录
        if(category1id) {
          query.category1Id = category1id
          //二级目录
        } else if (category2id) {
          query.category2Id = category2id
          //三级目录
        } else {
          query.category3Id = category3id
        }
        //动态给location配置对象添加query属性
        location.query = query;
        //路由跳转
        this.$router.push(location);
      }
    }

Question 6: When the Vue project starts, http://0.0.0.0:8080 is opened by default, and the browser shows that it cannot be accessed, but the page can be displayed by entering http://localhost:8080

insert image description here

Solution: Add a new line in the vue.config.js file: host: 'localhost'.

module.exports = {
  productionSourceMap:false,
  // 关闭ESLINT校验工具
  lintOnSave: false,
  devServer: {
    //代理服务器解决跨域
    proxy: {
      "/api": {
        target: "http://39.98.123.211:8510"
      },
    },
    //解决默认打开浏览器,会出现0.0.0.0:8080,浏览器显示无法访问
    host: 'localhost'
  }
};

Links to my other related articles

1. Vue Shangpinhui mall project-day02 [9. Home component split + 10. postman test interface]
2. Vue Shangpinhui mall project-day02 [vue plug-in-13. Use of nprogress progress bar]
3. Vue Shang Pinhui Mall Project-day02【11. Secondary packaging of axios + 12. Unified interface management】
4. Vue Shangpinhui Mall Project-day02【14.vuex status management library】
5. Vue Shangpinhui Mall Project-day02【 15. Dynamic display of three-level menu linkage]

Guess you like

Origin blog.csdn.net/a924382407/article/details/129893255