How I Solved Real‑World Vuex Challenges in a Mobile Maintenance SPA
This article walks through the development of a Vue‑based single‑page application for the JD Car Maintenance project, covering why Vuex was chosen, its core concepts, store implementation, state persistence, cross‑origin handling, Axios/JSONP requests, code refactoring, lazy loading, and component extraction to improve maintainability and performance.
Project Overview
When the "JD Maintenance" project was assigned, we learned it was a mobile‑focused solution deployed on both the WeChat public account and the JD app. After discussing with the backend team, we decided to use a front‑end/back‑end separation architecture, choosing a webpack + Vue single‑page application (SPA) for the front end.
Previous internal SPA projects provided useful lessons, but each new project brings fresh problems. The following sections describe the issues we encountered and the solutions we applied.
Why Use Vuex
During the early technical selection, we realized that multiple form views needed to share a large amount of data. Simple route parameters were insufficient because the data volume made the URL overly complex. Vuex was introduced to enable centralized state sharing.
Example: the vehicle‑binding module requires a multi‑step process – entering the license plate, selecting brand/series/year, and then completing the binding information.
Vuex Usage
Core concepts of Vuex:
state – defines stored data.
getter – filters data.
mutation – the only way to change the store’s state.
action – similar to mutation but can contain asynchronous operations.
modules – split a large store into smaller, manageable pieces.
Store instance implementation:
import Vuex from 'vuex';
Vue.use(Vuex); // import and use Vuex
export default new Vuex.Store({
state: {
formParams: {},
address: {}
},
actions: {
GET_SERIES_LIST({ commit }, params) {
axios.get(url, { params, withCredentials: true })
.then(response => {
commit('setBrandsList', { data: response.data });
});
}
},
mutations: {
setAddress(state, data) {
state.address = data;
}
},
getters: {},
modules: {}
});Usage examples:
Reading state in a child component:
computed: {
address() {
return this.$store.state.address;
}
}Committing a mutation from a component:
this.$store.commit('setAddress', params);Dispatching an action from a component:
this.$store.dispatch('GET_SHOPS_LIST', params);Vuex State Persistence
Although Vuex simplifies data sharing, its state is lost on page refresh. To persist data, we first considered manually writing to sessionStorage or localStorage inside each mutation. A cleaner solution is to use the vuex‑persist plugin, which automatically syncs the store to storage.
import VuexPersistence from 'vuex-persist';
const vuexLocal = new VuexPersistence({
storage: window.sessionStorage
});
export default new Vuex.Store({
state: { formParams: {}, address: {} },
actions: { /* … */ },
mutations: { /* … */ },
plugins: [vuexLocal.plugin]
});With this configuration, data remains after a page refresh without needing manual storage calls in every mutation.
Local Development Cross‑Origin Issues
During local development the API requests were all cross‑origin. The backend only allowed a specific domain (e.g., local.jd.com), so simply enabling Chrome’s CORS was insufficient. Adding an entry to the hosts file ( 127.0.0.1 local.jd.com) allowed the domain to be used, but webpack‑dev‑server still blocked the request. Setting disableHostCheck: true in the dev server configuration resolved the issue.
Data Request Strategy and JSONP
Vue‑resource is deprecated; the team switched to axios for HTTP requests. However, some APIs required JSONP, which axios does not support. A small JSONP helper module was added:
jsonp('//d.jd.com/xxxx?fid=1&callback=getAreaListCallBack');
window.getAreaListCallBack = function(r) {
console.log(r);
};Reducing Repetitive Code in Actions
As the project grew, the actions section became cluttered with similar request logic. A generic request utility was created to centralise the pattern, allowing configuration of the HTTP method, URL, parameters, and credentials.
function xhr(apikey, params, changeState) {
const apis = this.apis;
const url = debug ? host + apis[apikey] : apis[apikey];
const data = params.params || {};
const type = apis[apikey] || 'get';
if (type === 'post') {
return axios.post(url, data, { withCredentials: true })
.then(response => changeState(response.data));
}
return axios.get(url, { params: data, withCredentials: true })
.then(response => changeState(response.data));
}This utility dramatically reduces duplicated code and makes future changes easier.
Lazy Loading Third‑Party Libraries
Some views (e.g., the map view) require heavy third‑party scripts. Instead of loading them globally, we load them on demand:
function loadMap() {
const self = this;
return new Promise((resolve, reject) => {
window.initTheMap = function() { resolve(self.initMap()); };
const script = document.createElement('script');
script.type = 'text/javascript';
script.async = true;
script.src = '//map.qq.com/api/js?v=2.exp&callback=initTheMap&key=' + self.k;
script.onerror = reject;
document.head.appendChild(script);
});
}Component Extraction
All views are treated as components. Frequently reused UI pieces—such as the top bar, modal dialogs, toast notifications, and infinite scroll—are extracted into their own component files, improving reusability and maintainability.
Example of importing and registering a header component:
import Header from '../component/header.vue';
export default {
components: { jHeader: Header }
};Usage in the template:
<j-header :title="'title'"/>Areas for Improvement
The project does not currently use Vue’s lazy‑loading feature. The total JavaScript bundle is about 200 KB, which is acceptable for now, but larger projects would benefit from code‑splitting to reduce initial load time.
Conclusion
Through this project I discovered that Vue’s data‑driven approach greatly simplifies state management compared to the DOM‑centric jQuery style. Extracting reusable components, centralising request logic, persisting Vuex state, and handling cross‑origin issues made development smoother and the final product more maintainable. I hope the shared experience helps others tackling similar Vue + Webpack projects.
If you find any inaccuracies, feel free to point them out!
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
JD.com Experience Design Center
Professional, creative, passionate about design. The JD.com User Experience Design Department is committed to creating better e-commerce shopping experiences.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
