Ask for help on how to quickly understand an vue e-commerce project

recently got an outsourced vue family bucket e-commerce project and it was responsive. I was asked to change it, but I didn"t hand it over. I still wrote dom using pug template. I don"t know why the vue plug-in of chrome doesn"t work. Vue.config.devtools = true added it. I didn"t have a clue after looking at the code for two days. All kinds of nesting are too complex. I copied the code in header, and the plug-in lodash has not been used. I really feel like I"m going to run.

related codes

< template lang= "pug" >

.header-box.mb15(:view-mode="headerProps.viewMode")
    router-link.row.top-aadd.flex.jc-center(
        :to="`/goodsDetails?productId=${_got("ADObject.top_area.0.productId")}`"
        ref="topaadd"
    )
        //- 
                .topbar.pr.z10(ref="topbar" :class="{naviFloating}")
        .mc.flex.between.bbox.bar-height.w-box
            ._left.flex
                .main-left-width.mr15(v-show="headerProps.viewMode==0")
                .flex.city-container.pr
                    span(:title="currentCity.name") {{currentCity.name|trim(6)}}
                    i.icon.i-down1.pr
                    CitySelector(@input="cityChangeHandler" :cityId="currentCity.id")
        
                template(v-if="!login")
                    a.top-link.loginText(@click="tryLogin")      
                    |   |
                    router-link.top-link.loginText(to="/register")      
                template(v-if="login")
                    router-link.top-link.userNameOnTop(to="/user" @click.prevent.native="to(`/user`)")
                        span.pr.uname
                            |   {{userInfo.info.username | strcut(11)}}
                            i.icon.i-down1.pr.offset-y-1.pl025
                        .in-box
                            .row.flex.lh18
                                img(:src="userInfo.info.file")
                                .__right.pl05.cl-grey800.lh18
                                    .cl-occ-orange-hover.wnw(@click="to("/user")")
                                        |    :
                                        b   {{userInfo.info.username}}
                                    .fz09.cl-occ-orange-hover(@click="_loginOut") 
                    router-link.top-link.signBtn(to="/user/opower")      <i class="float-effect"></i>
            .right-box
                .nav-list.flex
                    template(v-if="isDebug")
                        router-link.top-link(to="/home") 
                        router-link.top-link(to="/goods") 
                        router-link.top-link(to="/building") 
                        router-link.top-link(to="/chat/index") 
                        a.top-link(href="http://web.occ-cn.com/join.html" target="_blank") 
                    template(v-if="!isDebug")
                        router-link.top-link(to="/home") 
                        router-link.top-link(to="/user/orderList") 
                        router-link.top-link(to="/chat/index") 
                        router-link.top-link(to="/user/info") 
                        router-link.top-link(to="/user/service") 
                        a.top-link(href="http://web.occ-cn.com/join.html" target="_blank") 
        
    .navi-in-view-placeholder.pr.offset-y-1(:id="`naviInViewPlaceholder_${_uid}`")
    .mc.pr.z100
        //.bar-height(v-show="headerProps.viewMode==1")
        LMenu.z100(
            ref="menu"
            :visible="menuVisible"
            @input="menuBeClick"
            @dataChange="menuDataChanged"
            :subCataId="$route.query.cataId||"""
            @reset="to("/")"
        )
    .bar-height(v-show="naviFloating")

    .search-bar-row
        .search-bar.row.flex.mc.between.mt15.ai-end.pr
            router-link.backToHomeLogo.pa(to="/home")
            ._left.flex.center
                .main-left-width.mr30
                input.pa.h1px.op00(ref="focuser")
                el-autocomplete.flat-mode.long.mr10(
                    placeholder=""
                    ref="autoComp"
                    icon="search"
                    v-model="input"
                    maxlength="180"
                    :fetchSuggestions="querySearchAsync"
                    :on-icon-click="handleIconClick"
                    @keydown.native.13="handleIconClick();$refs.autoComp.close()"
                    @select="handleSelect"
                )
                router-link.shopCartButton.pr.flex(
                    to="/cart" ,
                    @mouseover.native="cartShowState(true)" ,
                    @mouseout.native="cartShowState(false)"
                    ref="positionMsg"
                )
                    i.icon.i-gouwucheman.cl-occ-orange.fz12
                    span.cl-occ-orange.pl05 
                    span.cart-num(:class="{longNumber:totalNum>99}")
                        i.num.cl-red(ref="num") ({{totalNum<100?totalNum:"99+"}})
                    // 
                    .nav-card-wrapper.pa.active(v-show="showCart && totalNum" ,@click.prevent="")
                        .nav-card-list.of.h(v-show="totalNum")
                            .ul.nav-cart-items
                                router-link.cart-item.p15.bbox.flex(
                                    v-for="(item,i) in cartListPicked"
                                    :key="i"
                                    :to=""/goodsDetails?productId="+item.productId"
                                )
                                    .item-thumb
                                        img(:src="item.productImg")
                                    .item-desc.fx1.ml15
                                        .cd.cl-grey700.fz15(v-text="item.productName")
                                        .mt15.flex.between
                                            ._l
                                                span.fz12.cl-grey500   
                                                em.fz12.price-num.cl-grey800  {{item.salePrice}}
                                                span.pl05.pr05.cl-grey500   x
                                                em.fz12.cl-red.item-num    {{item.productNum}}

                                            ._r.flex.pr10
                                                .cs(v-show="item.color") {{item.color}}/
                                                .cs(v-show="item.size")  {{item.size}}
                                    .del-btn.del.of.h.hide-i(@click.stop="delGoods(item)") 
                            // 
                            .nav-cart-total
                                p    <strong>{{totalNum}}</strong>
                                h5  :<span class="price-icon">{{totalPrice}}</span>
                                h6
                                    y-button.h30.w100p.m00.cl-grey50.lh30.main-btn(
                                        class-style="main-btn"
                                        style="height: 40px;width: 100%;margin: 0;color: -sharpfff;font-size: 14px;line-height: 38px;"
                                        text=""
                                        @btnClick="tryLogin().then(()=>to(`/cart`))"
                                    )
                            .cart-con(v-show="!totalNum" style="height: 313px;text-align: center;")
                                p !
            ._right
                i.iico.sqcode.size96
                .sqbtn.flex
                    i.iico.sqcode.size96
                    i.icon.i-erweima
                    |   

< / template >
< script >

import {mapMutations, mapState} from "vuex"
import {getCartList, cartDel, getQuickSearch} from "/src/api/goods"
import {loginOut} from "/api/index"
import {setStore, getStore, removeStore} from "/utils/storage"
import "element-ui/lib/theme-default/index.css"
import {menuList} from "/src/config"
import LMenu from "/src/components/LMenu.vue"
import daovoice from "/src/utils/daovoice"
import {cataCacher, merchantCacher, productCacher, searchTipCacher} from "/src/utils/dataCacher"
import {routeList} from "/src/utils/router.js";
import inView from "in-view";
import floatLayer from "/src/components/FloatLayer"
import {brodcastMixin} from "/src/utils/globalMixin"
import CitySelector from "/src/components/CitySelector";
import {curretCityInfo} from "/src/utils/amap.util"
import JMath from "/src/utils/JMath"

let userNav = _.find(routeList, {name: "user"});
userNav = userNav.children;
userNav = userNav.filter(el => !el.hideinmenu);
userNav = userNav.slice(0, 6);
export default {
    components: {LMenu, floatLayer, CitySelector},
    mixins: [brodcastMixin],
    name: "header",
    data() {
        return {
            matchdCataId: "",
            user: {},
            // 

            //
            st: false,
            // 
            cartShow: false,
            positionL: 0,
            positionT: 0,
            timerCartShow: null, // 
            input: "",
            timeout: null,
            token: "",
            userNav,
            menuList,

            menuVisible: true,

            //
            naviFloating: false,
        }
    },
    computed: {
        headerProps() {
            return this.$store.state.headerProps
        },
        currentCata() {
            return this.$store.state.currentCata;
        },
        ...mapState([
            "cartList", "login", "receiveInCart", "showCart", "userInfo", "idDebug", "ADObject", "currentCity"
        ]),
        cartListPicked() {
            return _.compact(this.cartList || [])
        },
        // 
        totalPrice() {
            var totalPrice = 0
            this.cartListPicked.forEach(item => {
                totalPrice += (item.productNum * item.salePrice)
            })
            return JMath.strip(totalPrice);
        },
        // 
        totalNum() {
            var totalNum = 0
            this.cartListPicked.forEach(item => {
                totalNum += (item.productNum)
            })
            return JMath.strip(totalNum);
        }
    },
    methods: {
        ...mapMutations(["ADD_CART", "INIT_BUYCART", "ADD_ANIMATION", "SHOW_CART", "REDUCE_CART", "RECORD_USERINFO", "EDIT_CART"]),
        menuDataChanged() {
            var m = this;
            m.$nextTick(() => {
                if (!m.$refs || !m.$refs.menu || !m.$refs.menu.$el) return setTimeout(m.menuDataChanged, 333);

                //
                m.headerProps.menuHeight = m._got("$refs.menu.$el.offsetHeight");
            })
        },

        cityChangeHandler(city) {
            this.$store.commit("changeCity", city);
        },

        //
        menuBeClick(cata) {
            var m = this;
            m.to("/goods", {cataId: cata.id})
        },

        //
        handleSelect(item) {
            // this.input = item.value;
            this.input = "";
            this.to("/goodsDetails", {productId: item.productId})
        },

        //
        handleIconClick(ev) {
            this.to("/search", {key: this.input});
        },


        //
        querySearchAsync(queryString, callback) {
            var m = this;
            if (!m._querySearchAsync) {
                m._querySearchAsync = _.debounce(resp => {
                    m.input = m.input || ""
                    m.input = m.input.trim();
                    searchTipCacher.byId(m.input)
                        .then(ins => {
                            if (!ins) return [];
                            return ins.data;
                        })
                        .catch(() => [])
                        .then(callback)
                    ;
                }, 210)
            }
            if (!m.input) return callback([]);
            m._querySearchAsync();
        },

        // 
        cartShowState(state) {
            this.SHOW_CART({showCart: state})
        },


        //
        _getCartList() {
            var m = this;
            getCartList(getStore("userId"))
                .then(res => {
                    if (!res.success) throw res;
                    return Promise.all(
                        _.map(res.result, el => {
                            return productCacher.byId(el.productId)
                                .then(pd => {
                                    //merchantCacher.save(pd.merchant)
                                    if (pd._status === 0) {
                                        throw "";
                                    }
                                    el.merchantId = pd.merchantId;
                                    return el;
                                })
                                .catch(error => {
                                    util.consoleError(error);
                                    return null;
                                })
                                ;
                        })
                    ).then(resp => {
                        let ret = [];
                        _.compact(resp).forEach(pd=>{
                            let csLs = pd.colorSize;
                            delete pd.colorSize;
                            if(csLs && csLs.length){
                                csLs.forEach(cs=>{
                                    let _pd = _.merge({},pd);
                                    _pd.productNum = cs.num;
                                    _pd.size=cs.size;
                                    _pd.color=cs.color;
                                    _pd.checked=cs.checked;
                                    ret.push(_pd)
                                })
                            }
                        })
                        setStore("buyCart", ret)
                        this.INIT_BUYCART();
                    })
                    // 
                })
                .catch(util.toastError)
            ;
        },
        // 
        delGoods({productId,color="",size=""}) {
            if (this.login) { // 
                cartDel({userId: getStore("userId"), productId, color, size}).then(res => {
                    this.EDIT_CART({productId, color, size})
                })
            } else {
                this.EDIT_CART({productId, color, size})
            }
        },

        // 
        _loginOut() {
            let params = {token: util.token()};
            io("/member/loginOut", "get", {params}).then(res => {
                removeStore("buyCart");
                store.commit("logout")
                // this.to("/home")
                location.href = "/"
            })
        },
    },
    mounted() {
        var m = this;
        this.token = getStore("token");
        m.rootOn("clearSearchInput", function () {
            m.input = "";
        })
        m.$watch("$route.query.key", key => {
            if (m.$route.path == "/search") {
                m.input = key;
            }
        }, {})


        m.$watch("login", login => {
            if (login) {
                this._getCartList();
                let user = m.userInfo.info;
                daovoice.popup(user.id, user.username, user.email, "");
            } else {
                this.INIT_BUYCART()
            }
        }, {immediate: true})

        if (typeof (this.$route.query.key) !== undefined) {
            this.input = this.$route.query.key
        }

        let matchdCataIdFunction = _.debounce((cataData, cataId) => {
            cataCacher.byId(cataId).then(cata => {
                m.$store.commit("currentCata", cata || "")
            })
        }, 150)

        m.unwatcherRecord = m.$watch("$store.state.cataData", cataData => {
            if (!cataData) {
                return;
            }

            m.unwatcherRecord = m.$watch("$route.query.cataId", cataId => {
                matchdCataIdFunction(cataData, cataId);
            }, {immediate: true})
        }, {immediate: true})
        //m.$watch("$store.state.currentCata",()=>matchdCataIdFunction(),{immediate:true});

        m.unwatcherRecord = m.$watch("$route.path", path => {
            if (
                0
                || path == "/home"
                || path == "/goods"
            ) {
                m.menuVisible = true;
            } else {
                m.menuVisible = false;
            }
        }, {immediate: true});

        // m.$watch("")
        // m.headerProps.height = m.$el.offsetHeight - m._got("$refs.topaadd.offsetHeight",0);

        const calcHeight = _.debounce(() => {
            let vm = store.state.headerProps.viewMode;
            let offset = -m._got("$refs.topaadd.$el.offsetHeight", 0);

            if (vm != 0) {
                offset -= m._got("$refs.topbar.offsetHeight", 0);
            }

            m.headerProps.height = m.$el.offsetHeight + offset;
            m.menuDataChanged();
        }, 150)

        m.$watch("$store.state.ADObject.top_area", calcHeight, {immediate: true});
        m.$watch("$store.state.headerProps.viewMode", calcHeight, {immediate: true})
        m.$watch("$store.state.cataData", calcHeight, {immediate: true})
        m.$watch("$route", calcHeight, {immediate: true, deep: true})

        //

        let _idInView = `-sharpnaviInViewPlaceholder_${m._uid}`;
        inView(_idInView)
            .on("enter", el => {
                m.naviFloating = false;
            })
            .on("exit", el => {
                m.naviFloating = true;
            })
        ;

        m.naviFloating = !inView.is(document.querySelector(_idInView));

        curretCityInfo().then(console.log)


    },

    beforeDestroy() {
        var m = this;
    }
}

< / script >
< style lang= "less" rel= "stylesheet/less" scoped >

@import "tool";
@import "vars.less";
@import "headr.less";

< / style >

Jul.08,2021

to put it pragmatically, it is not difficult to find areas where new requirements need to be changed, is it? Really not, debugger can also locate the location of the code. If the component partition of the previous development is still fine-grained, then as long as it is not a major change at the architectural level, the adjustment of individual components will not be very complex, and it should not be difficult to implement the requirements.

if the project uses Vue Router and Vuex, to give priority to understanding routing design and global state design, at least figure out which parts will affect your requirements implementation. Of course, it doesn't matter if the interface is simply adjusted.

Ideally, to understand the whole project, start with understanding the component tree. Vue's one-way data flow concept ensures that you (in most cases) can trace all the way from the root component to the target component and sort out the status update process. For most projects, sort out the logical implementation of several key lines of business, and the whole project will be basically mastered.

of course, if the previous developer didn't divide the components very much, or used too much "dark magic", good luck.

For the problem with

Vue DevTools, it is best to restart the browser after installation. When opening the project page, first pay attention to whether the menu icon in the upper right corner is lit, and whether it prompts DevTools to be available when it is lit. In addition, if Vue App is in a < iframe > , DevTools will not work properly, try to visit the Vue App page itself directly.

A few other (useless) tips:

  1. if you are in charge of the project in the future, you can take over and format the whole project code according to your preference first (or file by file if you like). Remember to submit the code before any formatting, and then submit it again after formatting.
  2. if possible, it is recommended to use IDE with engineering parsing (such as WebStorm) to read the source code, which can help you quickly jump the code at the syntax tree level and save a lot of trouble to rename variables if necessary; use more code folding and bookmarking features to reduce visual pressure and memory pressure.
  3. Don't just focus on the developer tools that come with Vue DevTools,Chrome, especially on style inheritance analysis and script black boxes, which are helpful for you to debug your code.
Menu