Jesse A. Green / notebook

Live demo: the DocCompiler guide library as a static, read-only snapshot (October 2026). No backend, no accounts, nothing to submit.

vue2-elm Admin & Architecture Guide

Generated June 2026 from bailicangdu/vue2-elm at b85ff02

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

This guide is the deep reference for developers, architects, and anyone maintaining, extending, or deploying vue2-elm. Where the User Guide covers using the app, this guide covers how it is built: its architecture, its configuration and build pipeline, its data and state layers, its security posture, and the technical debt a maintainer should be aware of. It is written to be complete enough that an engineer — or an AI — could reconstruct the project from this document alone.

vue2-elm is a client-side single-page application. It has no server of its own; it is a Vue 2 front end that consumes a separate REST backend (node-elm). Keep that boundary in mind throughout: nearly every "operational" concern here is about building, hosting, and configuring a static bundle, not about running a service.


Table of Contents

  1. Executive Summary
  2. Architecture
  3. Requirements & Toolchain
  4. Configuration Reference
  5. The Data Layer (Service & Fetch)
  6. State Management (Vuex Store)
  7. Routing
  8. Shared Components & Utilities
  9. Build & Deployment
  10. Security Analysis
  11. Code Quality & Patterns
  12. Operations & Troubleshooting
  13. Potential Improvements
  14. Quick Reference

1. Executive Summary

Attribute Value
Type Mobile-first SPA (front end only)
Domain Food-delivery storefront (an Ele.me / 饿了么 clone)
Framework Vue 2 + Vuex 2 + Vue Router 2
Build webpack 1, Babel (ES6/7 → ES5), Sass
Routing Hash mode (#/path)
Backend External: node-elm (not in this repo)
Output Static assets in elm/
Scope ~45 screens, deeply nested routes (up to 4 levels)
License GPL
Purpose A real-world learning project demonstrating a complex, money-handling SPA

What makes it architecturally interesting: unlike most Vue learning demos (single-page players, to-do lists), vue2-elm implements a complete commerce flow with the hard parts — a persistent multi-restaurant cart, a multi-step checkout, address management, and an authentication flow — and it leans on Vuex precisely where local component state would break down. It is a textbook example of "when do you actually need a store."

What it intentionally omits: real payment (the flow stops at the pay step by design) and any of its own backend. Treat both as deliberate scope boundaries, not gaps.


2. Architecture

2.1 High-level topology

                        ┌──────────────────────────────────────────┐
   Browser (mobile)     │              vue2-elm SPA                 │
   ┌───────────────┐    │                                          │
   │  index.html   │───▶│  main.js  →  new Vue({ router, store })   │
   └───────────────┘    │       │                                  │
                        │       ├─ Vue Router (hash)  ── pages/*    │
                        │       ├─ Vuex Store ──────── cart, user   │
                        │       └─ App.vue (router-view + keepAlive)│
                        │                                          │
                        │  service/getData.js                      │
                        │       │  (one function per endpoint)     │
                        │       ▼                                  │
                        │  config/fetch.js  (fetch + XHR fallback) │
                        └───────────────┼──────────────────────────┘
                                        │  HTTP (cookies: credentials:include)
                                        ▼
                        ┌──────────────────────────────────────────┐
                        │   node-elm backend  (separate project)   │
                        │   cangdu.org (prod)  |  localhost (local) │
                        └──────────────────────────────────────────┘

2.2 Application bootstrap

The entry sequence is small and explicit:

  • index.html mounts a single #app element.
  • src/main.js is the program entry. It:
    • Registers Vue Router and constructs the router from src/router/router.js.
    • Reads routerMode from src/config/env.js (hash).
    • Imports src/config/rem.js for the mobile rem layout (side-effect import).
    • Defines a scrollBehavior that preserves scroll position for keep-alive routes.
    • Instantiates the root new Vue({ router, store }).$mount('#app').
  • src/App.vue is the shell. It renders two <router-view>s wrapped in transitions — one inside <keep-alive> (for routes whose meta.keepAlive is true, e.g. Msite) and one outside it — plus the global <svg-icon> sprite component.

Note

The dual router-view pattern is how this app gets selective view caching: only routes that opt in via meta: { keepAlive: true } are kept alive (preserving scroll and state when you navigate back), while everything else re-renders fresh.

2.3 Layered design

The src/ tree separates concerns cleanly into layers:

Layer Directory Responsibility
Entry src/ (main.js, App.vue) Bootstrap, root shell
Config src/config/ Environment, fetch wrapper, utilities, rem layout
Routing src/router/ Route table (lazy-loaded pages)
Pages src/page/ One folder per screen; nested children for sub-screens
Components src/components/ Reusable UI: common/, header/, footer/
Service src/service/ API surface — one exported function per endpoint
Store src/store/ Vuex state, mutations, actions, getters, mutation-types
Styles src/style/ Shared SCSS (common.scss, mixin.scss)

This is a conventional and well-factored Vue 2 layout: pages compose components, call the service layer for data, and read/write shared state through the store.

2.4 Data flow for a typical screen

Using Msite (src/page/msite/msite.vue) as the canonical example:

  1. beforeMount resolves a geohash — from the route query if present, otherwise via cityGuess() (IP-based) — and commits it to the store (SAVE_GEOHASH).
  2. It calls msiteAddress(geohash) to resolve the address, sets the header title, and commits RECORD_ADDRESS (latitude/longitude) to the store.
  3. mounted calls msiteFoodTypes(geohash), chunks the categories into pages of 8, and initializes a Swiper carousel.
  4. The embedded <shop-list> component reads latitude/longitude from the store (mapState) and calls shopList(...) itself, paginating via infinite scroll.

This illustrates the app's consistent pattern: lifecycle hooks fetch via the service layer, shared coordinates flow through Vuex, and child components subscribe to that state.


3. Requirements & Toolchain

3.1 Runtime/build requirements

Tool Declared / recommended Source
Node.js engines.node >= 4.0.0; README recommends >= 6.0.0 package.json, README
npm engines.npm >= 3.0.0 package.json
Yarn Optional alternative (lockfile committed) README, yarn.lock

The author's documented development environment was macOS 10.12.3, Chrome 56, Node 6.10.0.

3.2 Runtime dependencies

Package Version Role
vue ^2.1.0 Core framework
vue-router ^2.1.1 SPA routing
vuex ^2.0.0 State management
better-scroll 1.15.2 Smooth scrolling regions
fastclick ^1.0.6 Removes mobile tap delay
iscroll ^5.2.0 Scrolling fallback
showdown ^1.6.4 Markdown → HTML (used for static text content)

3.3 Notable build dependencies

The devDependencies define a classic webpack-1 + Babel-6 pipeline: webpack@^1.13.2, vue-loader@^10, vue-template-compiler, babel-preset-es2015 + babel-preset-stage-2, node-sass + sass-loader, less + less-loader, autoprefixer, extract-text-webpack-plugin, html-webpack-plugin, http-proxy-middleware (dev API proxy), webpack-dev-middleware / webpack-hot-middleware (HMR), and cross-env (cross-platform NODE_ENV).

Warning

This is an aged toolchain. node-sass builds native bindings against the active Node version and is the most common install failure on modern Node. webpack 1 and Babel 6 are both several major versions behind current. See Potential Improvements.


4. Configuration Reference

vue2-elm's configuration is concentrated in two small files plus the npm scripts. There are no environment files (.env) and no runtime configuration service — everything is decided at build time.

4.1 src/config/env.js — environment switching

This file exports the host configuration, selected by process.env.NODE_ENV:

Export Development Production Notes
baseUrl '' (empty — requests are dev-proxied) //elm.cangdu.org Prefixed onto every API path in fetch.js.
imgBaseUrl /img/ //elm.cangdu.org/img/ Base for image URLs.
routerMode hash hash Constant; consumed by main.js.
let baseUrl = '';
let routerMode = 'hash';
let imgBaseUrl = '';

if (process.env.NODE_ENV == 'development') {
    imgBaseUrl = '/img/';
} else if (process.env.NODE_ENV == 'production') {
    baseUrl = '//elm.cangdu.org';
    imgBaseUrl = '//elm.cangdu.org/img/';
}

export { baseUrl, routerMode, imgBaseUrl }

Caution

Negative knowledge — localapi and proapi do not exist. Several modules (src/store/mutations.js, src/components/common/mixin.js, src/page/login/login.vue) write import { localapi, proapi } from 'src/config/env'. env.js exports only baseUrl, routerMode, and imgBaseUrl, so localapi and proapi resolve to undefined. They are dead leftovers from an earlier configuration scheme and are never used meaningfully. Do not document them as configuration knobs; if cleaning up, remove the imports.

4.2 config/index.js — dev server & build paths

This is the webpack-template config, split into build and dev sections.

build block (production):

Key Value Meaning
env.NODE_ENV "production" Build-time environment.
index ../elm/index.html Output HTML path.
assetsRoot ../elm Output root.
assetsSubDirectory static Asset subfolder.
assetsPublicPath /elm/ Public base path for assets.
productionSourceMap true Emit source maps.
productionGzip false Gzip is off (host handles it).

dev block (development):

Key Value Meaning
env.NODE_ENV "development" Dev environment.
port 8000 Dev server port.
assetsPublicPath / Public base in dev.
proxypath http://cangdu.org:8001 Upstream API target for the dev proxy.
context see below Path prefixes proxied to proxypath.
cssSourceMap false CSS source maps off.

The proxied context prefixes are exactly the API namespaces used by the service layer:

/shopping  /ugc  /v1  /v2  /v3  /v4  /bos  /member  /promotion  /eus  /payapi  /img

Note

This proxy is why baseUrl is empty in development: the dev server forwards those prefixed requests to proxypath, avoiding cross-origin issues. In a production build, baseUrl (//elm.cangdu.org) is prepended instead and the proxy is not involved.

4.3 npm scripts and NODE_ENV

"scripts": {
  "dev":   "cross-env NODE_ENV=online node build/dev-server.js",
  "local": "cross-env NODE_ENV=local node build/dev-server.js",
  "build": "node build/build.js"
}

dev and local both launch the same dev server; the NODE_ENV value (online vs local) is what selects which backend the dev tooling targets. build runs the production build into elm/.

Note

The build/ directory (dev-server.js, build.js, and webpack configs) is the standard vuejs-templates/webpack scaffolding referenced by these scripts and by config/index.js. It orchestrates the dev server, the API proxy, and the production build.


5. The Data Layer (Service & Fetch)

5.1 src/config/fetch.js — the HTTP client

All network traffic funnels through a single default-exported async function. Its contract:

fetch(url = '', data = {}, type = 'GET', method = 'fetch')

Behavior:

  • Prepends baseUrl (from env.js) to url.
  • For GET, serializes data into a key=value&… query string.
  • If window.fetch is available and method == 'fetch', it issues a fetch() with:
    • credentials: 'include' (sends cookies — the session mechanism),
    • headers: Accept/Content-Type: application/json,
    • mode: "cors", cache: "force-cache",
    • JSON-stringified body for POST.
  • Otherwise it falls back to a manual XMLHttpRequest (with ActiveXObject for very old IE), using application/x-www-form-urlencoded.
  • Returns the parsed JSON; throws new Error(error) on failure.

Important

credentials: 'include' means the app relies on cookies for authentication state on the backend side, while also persisting user_id in localStorage on the client. The two work together; see Security Analysis.

Warning

cache: "force-cache" tells the browser to prefer a cached response when one exists. For a data API this can serve stale results; it is a questionable default worth revisiting.

5.2 src/service/getData.js — the API surface

This file is the single, centralized catalog of every backend call — one exported function per endpoint, grouped by domain. This is an exemplary pattern: pages never construct URLs themselves; they import named functions. The complete inventory:

Function Method Endpoint
cityGuess GET /v1/cities?type=guess
hotcity GET /v1/cities?type=hot
groupcity GET /v1/cities?type=group
currentcity GET /v1/cities/:id
searchplace GET /v1/pois?type=search
msiteAddress GET /v2/pois/:geohash
msiteFoodTypes GET /v2/index_entry
shopList GET /shopping/restaurants
searchRestaurant GET /v4/restaurants
foodCategory GET /shopping/v2/restaurant/category
foodDelivery GET /shopping/v1/restaurants/delivery_modes
foodActivity GET /shopping/v1/restaurants/activity_attributes
shopDetails GET /shopping/restaurant/:id
foodMenu GET /shopping/v2/menu
getRatingList GET /ugc/v2/restaurants/:id/ratings
ratingScores GET /ugc/v2/restaurants/:id/ratings/scores
ratingTags GET /ugc/v2/restaurants/:id/ratings/tags
mobileCode POST /v4/mobile/verify_code/send
getcaptchas POST /v1/captchas
checkExsis GET /v1/users/exists
sendMobile POST /v1/mobile/verify_code/send
checkout POST /v1/carts/checkout
getRemark GET /v1/carts/:id/remarks
getAddress GET /v1/carts/:id/addresses
searchNearby GET /v1/pois?type=nearby
postAddAddress POST /v1/users/:id/addresses
placeOrders POST /v1/users/:id/carts/:cartId/orders
rePostVerify POST /v1/carts/:id/verify_code
validateOrders POST /v1/users/:id/carts/:cartId/orders
payRequest GET /payapi/payment/queryOrder
getService GET /v3/profile/explain
vipCart POST /member/v1/users/:id/delivery_card/physical_card/bind
getHongbaoNum GET /promotion/v2/users/:id/hongbaos
getExpired GET /promotion/v2/users/:id/expired_hongbaos
exChangeHongbao POST /v1/users/:id/hongbao/exchange
getUser GET /v1/user
sendLogin POST /v1/login/app_mobile
getOrderList GET /bos/v2/users/:id/orders
getOrderDetail GET /bos/v1/users/:id/orders/:orderId/snapshot
getAddressList GET /v1/users/:id/addresses
getSearchAddress GET v1/pois?type=nearby
deleteAddress DELETE /v1/users/:id/addresses/:addressId
accountLogin POST /v2/login
signout GET /v2/signout
changePassword POST /v2/changepassword

Note

The API namespaces (/v1–/v4, /shopping, /ugc, /bos, /member, /promotion, /payapi) mirror Ele.me's real micro-service decomposition and are exactly the context prefixes proxied in config/index.js. Adding a new endpoint means adding a function here and (if it introduces a new prefix) registering that prefix in the dev proxy context list.


6. State Management (Vuex Store)

The store (src/store/) is split into the canonical Vuex files: index.js (state + assembly), mutations.js, action.js, getters.js, and mutation-types.js (string constants).

6.1 State shape (src/store/index.js)

State key Initial Purpose
latitude, longitude '' Current location coordinates.
cartList {} The cart, keyed shopid → category_id → item_id → food_id.
shopDetail null Current restaurant detail.
userInfo null Logged-in user object.
shopid null Active restaurant id.
remarkText, inputText null / '' Order remark (selected + free text).
invoice false Whether an invoice was requested.
newAddress, searchAddress, choosedAddress, addressIndex [] / null Address selection in checkout.
geohash '31.22299,121.36025' Location geohash (defaults to Shanghai coords).
needValidation null Whether the order needs extra verification.
cartId, sig null Cart identifier + signature returned at checkout.
orderParam, orderMessage, orderDetail null Order placement parameters and results.
login true Login flag.
imgPath null Avatar image path.
removeAddress, addAddress [] / '' Profile address-book editing.
question null Selected service-center question.
cartPrice null VIP card price.

6.2 Mutations (src/store/mutations.js)

Mutations are declared with computed-property names bound to the constants in mutation-types.js. The cart mutations are the most substantial:

  • ADD_CART builds a nested object path (cartList[shopid][category_id][item_id][food_id]), incrementing num if the dish exists or creating it (with name, price, specs, packing_fee, sku_id, stock). It reassigns cartList with a spread to trigger reactivity and persists to localStorage via setStore('buyCart', …).
  • REDUCE_CART decrements, nulling the entry at zero, and re-persists.
  • INIT_BUYCART rehydrates cartList from localStorage on load.
  • CLEAR_CART empties one restaurant's cart.

Other mutation groups handle user info (RECORD_USERINFO persists user_id; GET_USERINFO, RETSET_NAME, OUT_LOGIN, SAVE_AVANDER), checkout (SAVE_GEOHASH, CONFIRM_REMARK, CONFIRM_INVOICE, CHOOSE_ADDRESS, CONFIRM_ADDRESS, NEED_VALIDATION, SAVE_CART_ID_SIG, SAVE_ORDER_PARAM, CHANGE_ORDER_PARAM, ORDER_SUCCESS, SAVE_ORDER, SAVE_SHOPID), addresses (SAVE_ADDRESS, SAVE_ADDDETAIL, ADD_ADDRESS), and misc (SAVE_QUESTION, BUY_CART).

Tip

The cart's reliance on {...spread} reassignment is a deliberate Vue 2 reactivity workaround: Vue 2 cannot detect new properties added to an object, so the code replaces the object reference to force re-render. In Vue 3 (Proxy-based reactivity) this would be unnecessary.

6.3 Actions (src/store/action.js)

Only two async actions exist — both bridge the service layer into the store:

  • getUserInfo calls getUser() and commits GET_USERINFO.
  • saveAddress loads the user's address list (getAddressList) and commits SAVE_ADDRESS, short-circuiting if removeAddress is already populated.

Most data fetching happens directly in components, not in actions — a pragmatic choice for a project of this size.

6.4 Getters (src/store/getters.js)

The getters file is empty (export default {}). All derived values are computed locally in components (e.g. buyCart.vue's foodNum). This is worth noting for anyone expecting a getters layer — there isn't one.


7. Routing

src/router/router.js defines the entire route tree. Two conventions dominate:

7.1 Lazy loading via require.ensure

Every page is wrapped in a webpack-1 code-split chunk:

const msite = r => require.ensure([], () => r(require('../page/msite/msite')), 'msite')

Each page becomes a named chunk ('msite', 'shop', …), so the browser only downloads a screen's code when it is first visited. This keeps the initial bundle small despite ~45 screens.

7.2 Deeply nested routes

Routes mirror the UI's drill-down structure, nesting up to four levels. For example, the checkout address flow:

/confirmOrder
  └─ chooseAddress
       └─ addAddress
            └─ searchAddress

and the profile address editor:

/profile → info → address → add → addDetail

All pages are children of a single top-level route whose component is App. The Msite route sets meta: { keepAlive: true } to participate in the <keep-alive> view caching described in §2.2.

Note

Because routing is hash mode, no server-side rewrite configuration is needed for deep links — the path after # is handled entirely client-side. This simplifies hosting (any static server works) at the cost of # in URLs.


8. Shared Components & Utilities

8.1 Common components (src/components/common/)

Component Responsibility
shoplist.vue The restaurant list used by Msite and Shop. Owns infinite scroll (the v-load-more directive), back-to-top, loading skeletons, and per-item rendering. Reads latitude/longitude from the store.
buyCart.vue The add/remove cart control on each dish. Computes per-dish quantity from cartList, dispatches ADD_CART/REDUCE_CART, and emits the "drop into cart" animation event. Handles multi-spec dishes.
ratingStar.vue Renders a 5-star rating using overlaid SVG stars and a width clip.
loading.vue The sprite-sheet loading animation.
alertTip.vue A modal alert with a single confirm button; emits closeTip.
computeTime.vue A payment countdown timer. Its "go to pay" handler shows 暂不开放支付接口 — the explicit hook where payment is stubbed out.
svg.vue A hidden inline SVG sprite (<symbol> defs) referenced everywhere via <use xlink:href="#id">. Defines icons: eleme, user, shop, location, star, backtop, cart icons, payment-brand icons (weixin, zhifubao), and more.
mixin.js Two reusable mixins (below).

8.2 mixin.js — shared behavior

  • loadMore — registers a load-more directive implementing touch-driven infinite scroll (listens to touchstart/touchmove/touchend, computes scroll position with requestAnimationFrame, and invokes the bound callback at the bottom).
  • getImgPath — converts a backend image hash into a CDN URL on https://fuss10.elemecdn.com (splitting the hash into path segments and appending .jpeg or .png); falls back to //elm.cangdu.org/img/default.jpg.

8.3 src/config/mUtils.js — utilities

Export Purpose
setStore / getStore / removeStore localStorage helpers (JSON-encode objects).
getStyle Cross-browser computed-style reader.
loadMore Standalone infinite-scroll helper (parallel to the directive).
showBack Scroll listener that toggles the back-to-top button past 500 px.
animate A hand-rolled animation engine supporting ease-out, linear, and ease-in modes, rem-aware unit handling, opacity/scrollTop special-casing, and chained callbacks.

8.4 src/config/rem.js — the mobile layout primitive

A self-invoking function that sets the document's root font size proportionally to viewport width, recalculating on resize/orientationchange:

docEl.style.fontSize = 20 * (clientWidth / 320) + 'px';

This makes 1rem = 20px at a 320 px-wide screen and scales linearly with device width — the mechanism behind every rem dimension in the SCSS. It is also why a desktop viewport renders the UI oversized.

  • src/components/header/head.vue — the shared top bar (title, back button, search slot, context-specific slots used by Home/Msite/Login).
  • src/components/footer/footGuide.vue — the bottom tab navigation.

9. Build & Deployment

9.1 Building

npm run build

Per config/index.js, this writes:

  • elm/index.html (HTML entry),
  • elm/static/… (JS/CSS/img assets, with source maps),
  • using a public asset path of /elm/.

The result is a self-contained static site.

9.2 Deploying

Because vue2-elm is a static SPA with hash routing, deployment is simply serving the elm/ directory from any static host (nginx, Apache, a CDN, Netlify, Surge, GitHub Pages, etc.). No server-side routing rules are required.

Important

The production build hard-codes baseUrl = //elm.cangdu.org (via env.js). If you host the front end yourself and run your own backend, you must edit env.js so the production baseUrl/imgBaseUrl point at your API host before building. Otherwise the deployed app will call elm.cangdu.org.

9.3 Deployment checklist

  • [ ] Backend (node-elm or equivalent) deployed and reachable over HTTPS.
  • [ ] src/config/env.js production baseUrl/imgBaseUrl point to your API + image hosts.
  • [ ] npm run build completed without errors.
  • [ ] elm/ served by a static host; assetsPublicPath matches the serving path.
  • [ ] App verified in a mobile viewport over HTTPS.

Note

The project's own demo notes emphasize that the preview and its interfaces must be accessed over HTTPS. Mixed-content (HTTPS page calling HTTP API) will be blocked by browsers.


10. Security Analysis

vue2-elm is a learning project, and its security posture reflects that. A maintainer adapting it toward anything real should weigh the following.

10.1 Authentication & session

  • Auth state is carried by cookies (fetch uses credentials: 'include') plus a client-side user_id stored in localStorage. The real session authority is the backend cookie; the localStorage value is convenience state.
  • The login flow (src/page/login/login.vue) supports SMS and account+captcha modes. Against the demo backend, unknown accounts auto-register on login — acceptable for a sandbox, unacceptable for production.

10.2 Transport

  • HTTPS is required (the demo explicitly calls this out). The hard-coded //-relative baseUrl inherits the page scheme, which is the correct behavior on an HTTPS page.
  • cache: "force-cache" on every request risks serving stale authenticated data from cache.

10.3 Client-side validation only

  • Input validation (phone format via /^1\d{10}$/, SMS code via /^\d{6}$/, required fields) is purely client-side and user-experience oriented. All real authorization must be enforced by the backend — the front end cannot be trusted.

10.4 Sensitive data exposure

  • productionSourceMap: true ships source maps to production, exposing readable source. Fine for an open-source learning project; disable for anything proprietary.
  • No secrets live in the front end (correctly) — there are no API keys in the client.

Caution

Do not treat vue2-elm as a security template. Its value is in UI/state architecture, not in auth hardening. Any production fork needs server-enforced authz, CSRF protection appropriate to its cookie auth, a sane cache policy, and removal of auto-registration.


11. Code Quality & Patterns

11.1 Strengths

  • Clean layering. Config / service / store / pages / components are well separated.
  • Centralized API surface. getData.js as the single endpoint catalog is exemplary and makes the backend contract auditable in one place.
  • Named-chunk lazy loading keeps the initial payload small across ~45 screens.
  • Pragmatic Vuex usage. State is reserved for genuinely shared concerns (cart, user, checkout hand-off between deeply nested routes) — exactly where prop-drilling would hurt.
  • localStorage persistence of cart and session gives a resilient UX.

11.2 Rough edges

  • Dead imports of localapi/proapi (see §4.1).
  • Empty getters layer; derived state is scattered in components.
  • Two parallel infinite-scroll implementations (the mixin.js directive and mUtils.loadMore).
  • Bilingual inline comments (Chinese-first) — informative but a barrier for non-Chinese maintainers (mitigated by the bundled README-en.md).
  • cache: "force-cache" as a global default is risky for a data API.
  • Hand-rolled animate duplicates what CSS transitions / a small lib would handle more robustly.

11.3 Conventions to follow when extending

  • Add backend calls only in getData.js; never build URLs in a page.
  • Register any new API prefix in the config/index.js dev-proxy context.
  • Use mutation-types.js constants for new mutations.
  • Keep dimensions in rem so the layout scales with rem.js.
  • Opt a route into caching with meta: { keepAlive: true } only when scroll/state preservation is genuinely wanted.

12. Operations & Troubleshooting

Because there is no server to operate, "operations" here means build/host health and the external-backend dependency.

Symptom Likely cause Resolution
All data calls return ERROR_DATA / 404 Backend down or wrong host Run local node-elm; use npm run local; verify baseUrl
npm install fails on node-sass Node version mismatch Use Node 6–8 via nvm; reinstall
browserslist@1.7.7 warning Stale caniuse data Harmless; ignore
UI oversized on desktop rem layout for 320 px View in mobile emulation
Mixed-content blocked HTTPS page → HTTP API Serve API over HTTPS
Login captcha image broken Backend unreachable Fix backend; tap "换一张" to refetch
Deployed app calls cangdu.org unexpectedly Production baseUrl not changed Edit env.js, rebuild

Tip

When diagnosing "nothing loads," open DevTools → Network and inspect a failing request. A blocked/ERR_CONNECTION or non-200 to a /v1//shopping/… path confirms a backend connectivity problem rather than a front-end bug.


13. Potential Improvements

A prioritized list for anyone modernizing the project:

  1. Modernize the toolchain. webpack 1 → 5 and Babel 6 → current remove the node-sass install pain and unlock current tooling. This is the highest-leverage change.
  2. Migrate Vue 2 → Vue 3. Composition API and Proxy reactivity would eliminate the {...spread} cart workaround and the empty-getters awkwardness.
  3. Remove dead code. Delete the localapi/proapi imports and one of the two duplicate infinite-scroll implementations.
  4. Externalize configuration. Replace the hard-coded hosts in env.js with build-time environment variables so self-hosting doesn't require editing source.
  5. Fix the cache policy. Replace the global cache: "force-cache" with per-request caching (or none) to avoid stale data.
  6. Consolidate data fetching. Move component-level fetches into Vuex actions for testability, or adopt a data-fetching library.
  7. Replace node-sass with dart-sass (sass) to remove native-build fragility.
  8. Add tests. There is no test suite; component and store unit tests would make refactors (especially the cart) safe.
  9. Disable production source maps for any non-open deployment.

14. Quick Reference

14.1 Project map

vue2-elm/
├── build/                  webpack config, dev-server, build scripts
├── config/index.js         build paths + dev proxy (port 8000, proxypath cangdu.org:8001)
├── elm/                    PRE-BUILT output (not source)
├── src/
│   ├── main.js             entry: router + store + rem, mounts #app
│   ├── App.vue             shell: dual router-view + keep-alive + svg sprite
│   ├── config/
│   │   ├── env.js          baseUrl / imgBaseUrl / routerMode (hash)
│   │   ├── fetch.js         HTTP client (fetch + XHR fallback)
│   │   ├── mUtils.js        localStorage, getStyle, loadMore, showBack, animate
│   │   └── rem.js           root font-size = 20 * (width/320) px
│   ├── service/getData.js   ALL API endpoints (one fn each)
│   ├── store/               Vuex: state, mutations, action, getters(empty), mutation-types
│   ├── router/router.js     route tree (lazy require.ensure chunks)
│   ├── components/
│   │   ├── common/          shoplist, buyCart, loading, alertTip, ratingStar, computeTime, svg, mixin
│   │   ├── header/head.vue
│   │   └── footer/footGuide.vue
│   ├── page/                ~45 screens, nested children
│   └── style/               common.scss, mixin.scss
├── package.json            scripts: dev / local / build
└── README.md / README-en.md

14.2 Commands

Command Effect
npm install / yarn Install dependencies
npm run dev Dev server → public backend (NODE_ENV=online)
npm run local Dev server → local node-elm (NODE_ENV=local)
npm run build Production build → elm/

14.3 Key facts

Fact Value
Dev server port 8000
Dev proxy target http://cangdu.org:8001
Production API host //elm.cangdu.org
Image CDN https://fuss10.elemecdn.com
Router mode hash
Default geohash 31.22299,121.36025 (Shanghai)
localStorage keys buyCart, user_id
Payment Intentionally not implemented
Backend repo node-elm
Admin console vue2-manage
License GPL