摘要:項目架構項目目錄項目目錄是采用自動生成,其它按需自己新建就好了。
項目架構 項目目錄
├── build ├── config ├── dist │?? └── static │?? ├── css │?? ├── fonts │?? ├── images │?? ├── js │?? └── lib ├── src │?? ├── api │?? ├── assets │?? │?? ├── global │?? │?? └── images │?? │?? └── footer │?? ├── components │?? │?? ├── common │?? │?? ├── news │?? │?? └── profile │?? │?? └── charge │?? ├── config │?? ├── mixin │?? ├── router │?? ├── service │?? ├── store │?? └── util └── static ├── images └── lib
項目目錄是采用 vue-cli 自動生成,其它按需自己新建就好了。
開發實踐 動態修改 document title在不同的路由頁面,我們需要動態的修改文檔標題,可以將每個頁面的標題配置在路由元信息 meta 里面帶上,然后在 router.afterEach 鉤子函數中修改:
import Vue from "vue"; import Router from "vue-router"; Vue.use(Router); const router = new Router({ mode: "history", routes: [ { path: "/", component: Index, meta: { title: "推薦產品得豐厚獎金" } }, { path: "/news", component: News, meta: { title: "公告列表" }, children: [ { path: "", redirect: "list" }, { path: "list", component: NewsList }, { path: "detail/:newsId", component: NewsDetail, meta: { title: "公告詳情" } } ] }, { path: "/guide", component: GuideProtocol, meta: { title: "新手指南" } } ] }); // 使用 afterEach 鉤子函數,保證路由已經跳轉成功之后修改 title router.afterEach((route) => { let documentTitle = "xxx商城會員平臺"; route.matched.forEach((path) => { if (path.meta.title) { documentTitle += ` - ${path.meta.title}`; } }); document.title = documentTitle; });根據 URL 的變化,動態更新數據
通常在一個列表集合頁,我們需要做分頁操作,同時分頁數據需要體現在 URL 中,那么如何動態的根據 URL 的變動來動態的獲取數據呢,我們可以使用 watch API,在 watch 里面監聽 $route,同時使用 this.$router.replace API 來改變 URL 的值。下面是示例代碼 common.js:
import qs from "qs"; export default { data() { return { queryParams: { currentPage: 1, pageSize: 10 } }; }, methods: { handlePageNoChange(e) { this.queryParams.currentPage = e; this.replaceRouter(); }, replaceRouter() { const query = qs.stringify(this.queryParams); this.$router.replace(`${location.pathname}?${query}`); }, routeChange() { this.assignParams(); this.fetchData(); }, assignParams() { this.queryParams = Object.assign({}, this.queryParams, this.$route.query); } }, mounted() { this.assignParams(); this.fetchData(); }, watch: { $route: "routeChange" } };
我們將這部分代碼抽取到一個公共的 mixin 中,在需要的組件那里引入它,同時實現自定義的同名 fetchData() 方法
mixin API 文檔:https://cn.vuejs.org/v2/guide...
export default DemoComponent { mixins: [common], data() { return { // 組件內部自定義同名查詢參數,將會和 mixin 中的默認參數合并 queryParams: { categoryId: "", pageSize: 12 }, } }, methods: { fetchData() { // 發送請求 } } }Event Bus 使用場景
我們在項目中引入了 vuex ,通常情況下是不需要使用 event bus 的,但是有一種情況下我們需要使用它,那就是在路由鉤子函數內部的時,在項目中,我們需要在 beforeEnter 路由鉤子里面對外拋出事件,在這個鉤子函數中我們無法去到 this 對象。
beforeEnter: (to, from, next) => { const userInfo = localStorage.getItem(userFlag); if (isPrivateMode()) { EventBus.$emit("get-localdata-error"); next(false); return; } })
在 App.vue 的 mouted 方法中監聽這個事件
EventBus.$on("get-localdata-error", () => { this.$alert("請勿使用無痕模式瀏覽"); });自定義指令實現埋點數據統計
在項目中通常需要做數據埋點,這個時候,使用自定義指令將會變非常簡單
在項目入口文件 main.js 中配置我們的自定義指令
// 坑位埋點指令 Vue.directive("stat", { bind(el, binding) { el.addEventListener("click", () => { const data = binding.value; let prefix = "store"; if (OS.isAndroid || OS.isPhone) { prefix = "mall"; } analytics.request({ ty: `${prefix}_${data.type}`, dc: data.desc || "" }, "n"); }, false); } });使用路由攔截統計頁面級別的 PV
由于第一次在單頁應用中嘗試數據埋點,在項目上線一個星期之后,數據統計后臺發現,首頁的 PV 遠遠高于其它頁面,數據很不正常。后來跟數據后臺的人溝通詢問他們的埋點統計原理之后,才發現其中的問題所在。
傳統應用,一般都在頁面加載的時候,會有一個異步的 js 加載,就像百度的統計代碼類似,所以我們每個頁面的加載的時候,都會統計到數據;然而在單頁應用,頁面加載初始化只有一次,所以其它頁面的統計數據需要我們自己手動上報
解決方案
使用 vue-router 的 beforeEach 或者 afterEach 鉤子上報數據,具體使用哪個最好是根據業務邏輯來選擇。
const analyticsRequest = (to, from) => { // 只統計頁面跳轉數據,不統計當前頁 query 不同的數據 // 所以這里只使用了 path, 如果需要統計 query 的,可以使用 to.fullPath if (to.path !== from.path) { analytics.request({ url: `${location.protocol}//${location.host}${to.path}` }); } }; router.beforeEach((to, from, next) => { if (to.matched.some(record => record.meta.requiresAuth)) { // 這里做登錄等前置邏輯判斷 // 判斷通過之后,再上報數據 ... analyticsRequest(to, from); } else { // 不需要判斷的,直接上報數據 analyticsRequest(to, from); next(); } });
在組件中使用我們的自定義指令
使用過濾器實現展示信息格式化如下圖中獎金數據信息,我們需要將后臺返回的獎金格式化為帶兩位小數點的格式,同時,如果返回的金額是區間類型,需要額外加上 起 字和 ¥ 金額符號
在入口文件 main.js 中配置我們自定義的過濾器
Vue.filter("money", (value, config = { unit: "¥", fixed: 2 }) => { const moneyStr = `${value}`; if (moneyStr.indexOf("-") > -1) { const scope = moneyStr.split("-"); return `${config.unit}${parseFloat(scope[0]).toFixed(config.fixed).toString()} 起`; } else if (value === 0) { return value; } return `${config.unit}${parseFloat(moneyStr).toFixed(config.fixed).toString()}`; });
在組件中使用:
axios 使用配置{{detail.priceScope | money}}
比率:{{detail.commissionRateScope}}%
獎金:{{detail.expectedIncome | money}}
在項目中,我們使用了 axios 做接口請求
在項目中全局配置 /api/common.js
import axios from "axios"; import qs from "qs"; import store from "../store"; // 全局默認配置 // 設置 POST 請求頭 axios.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded"; // 配置 CORS 跨域 axios.defaults.withCredentials = true; axios.defaults.crossDomain = true; // 請求發起前攔截器 axios.interceptors.request.use((config) => { // 全局 loading 狀態,觸發 loading 效果 store.dispatch("updateLoadingStatus", { isLoading: true }); // POST 請求參數處理成 axios post 方法所需的格式 if (config.method === "post") { config.data = qs.stringify(config.data); } // 這句不能省,不然后面的請求就無法成功發起,因為讀不到配置參數 return config; }, () => { // 異常處理 store.dispatch("updateLoadingStatus", { isLoading: false }); }); // 響應攔截 axios.interceptors.response.use((response) => { // 關閉 loading 效果 store.dispatch("updateLoadingStatus", { isLoading: false }); // 全局登錄過濾,如果沒有登錄,直接跳轉到登錄 URL if (response.data.code === 300) { // 未登錄 window.location.href = getLoginUrl(); return false; } // 這里返回的 response.data 是被 axios 包裝過的一成,所以在這里抽取出來 return response.data; }, (error) => { store.dispatch("updateLoadingStatus", { isLoading: false }); return Promise.reject(error); }); // 導出 export default axios;
然后我們在接口中使用就方便很多了 /api/xxx.js
import axios from "./common"; const baseURL = "/api/profile"; const USER_BASE_INFO = `${baseURL}/getUserBaseInfo.json`; const UPDATE_USER_INFO = `${baseURL}/saveUserInfo.json`; // 更新用戶實名認證信息 const updateUserInfo = userinfo => axios.post(UPDATE_USER_INFO, userinfo); // 獲取用戶基礎信息 const getUserBaseInfo = () => axios.get(USER_BASE_INFO);vuex 狀態在響應式頁面中的妙用
由于項目是響應式頁面,PC 端和移動端在表現成有很多不一致的地方,有時候單單通過 CSS 無法實現交互,這個時候,我們的 vuex 狀態就派上用場了,
我們一開始在 App.vue 里面監聽了頁面的 resize 事件,動態的更新 vuex 里面 isMobile 的狀態值
window.onresize = throttle(() => { this.updatePlatformStatus({ isMobile: isMobile() }); }, 500);
然后,我們在組件層,就能響應式的渲染不同的 dom 結構了。其中最常見的是 PC 端和移動端加載的圖片需要不同的規格的,這個時候我們可以這個做
methods: { loadImgAssets(name, suffix = ".jpg") { return require(`../assets/images/${name}${this.isMobile ? "-mobile" : ""}${suffix}`); }, } // 動態渲染不同規格的 dislog
下圖分別是 PC 端和移動短的表現形式,然后配合 CSS 媒體查詢實現各種布局
開發相關配置 反向代理在項目目錄的 config 文件下面的 index.js 配置我們的本地反向代理和端口信息
dev: { env: require("./dev.env"), port: 80, autoOpenBrowser: true, assetsSubDirectory: "static", assetsPublicPath: "/", proxyTable: { "/api/profile": { target: "[真實接口地址]:[端口號]", // 例如: http://api.xxx.com changeOrigin: true, pathRewrite: { "^/api/profile": "/profile" } } ... },
然后我們調用接口的形式就會變成如下映射,當我們調用 /api/profile/xxxx 的時候,其實是調用了 [真實接口地址]/profile/xxxx
/api/profile/xxxx => [真實接口地址]/profile/xxxx
nginx 配置
upstream api.xxx.com { #ip_hash; server [接口服務器 ip 地址]:[端口]; } server { ... location ^~ /api/profile { index index.php index.html index.html; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass http://api.xxx.com; rewrite ^/api/profile/(.*)$ /profile/$1 break; } ... }線上部署
如果路由使用的是 history 模式的話,需要在 nginx 里面配置將所有的請求到轉發到 index.html 去
在 nginx.conf 或者對應的站點 vhost 文件下面配置
location / { try_files $uri $uri/ /index.html; }優化
開啟靜態資源長緩存
location ~ .*.(gif|jpg|jpeg|png|bmp|swf|woff|ttf|eot|svg)$ { expires 1y; } location ~ .*.(js|css)$ { expires 1y; }
開啟靜態資源 gzip 壓縮
// 找到 nginx.conf 配置文件 vim /data/nginx/conf/nginx.conf gzip on; gzip_min_length 1k; gzip_buffers 4 8k; gzip_http_version 1.1; gzip_types text/plain application/javascript application/x-javascript text/javascript text/xml text/css;
開啟了 gzip 壓縮之后,頁面資源請求大小將大大減小,如下圖所示,表示已經開啟了 gzip 壓縮
Q&A文章到這就結束了,如果有遺漏或者錯誤的地方,歡迎私信指出。
希望這篇文章能帶給大家一絲絲收獲。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/51217.html
摘要:項目架構項目目錄項目目錄是采用自動生成,其它按需自己新建就好了。 項目架構 項目目錄 ├── build ├── config ├── dist │?? └── static │?? ├── css │?? ├── fonts │?? ├── images │?? ├── js │?? └── lib ├── src │?? ├── api │?...
摘要:項目架構項目目錄項目目錄是采用自動生成,其它按需自己新建就好了。 項目架構 項目目錄 ├── build ├── config ├── dist │?? └── static │?? ├── css │?? ├── fonts │?? ├── images │?? ├── js │?? └── lib ├── src │?? ├── api │?...
摘要:年底,公司項目番茄表單的前端部分,開始了從傳統的到的徹底重構。上傳流程圖不重要看文字事件觸發后,先去如果是圖片,可以同時通過以及將圖片預覽在頁面上后臺請求七牛的上傳,將拿到的和以及通過傳遞過來的一起到中。 關于上傳,總是有很多可以說道的。16年底,公司項目番茄表單的前端部分,開始了從傳統的jquery到vue 2.0的徹底重構。但是上傳部分,無論是之前的傳統版本,還是Vue新版本,都是...
摘要:原文來自集前端最近很火的框架資源定時更新,歡迎一下。推送自己整理近期三波關于的資訊這里就拋磚引玉了,望有更屌的資源送助攻。 原文來自:集web前端最近很火的vue2框架資源;定時更新,歡迎Star一下。 推送自己整理近期三波關于Vue.js的資訊; 這里就拋磚引玉了,望有更屌的資源送助攻。 showImg(https://segmentfault.com/img/bVVeiZ); 第...
閱讀 1265·2021-11-24 09:39
閱讀 1514·2021-09-07 09:59
閱讀 3479·2019-08-30 15:54
閱讀 2474·2019-08-30 11:00
閱讀 2669·2019-08-29 15:06
閱讀 2159·2019-08-26 13:52
閱讀 427·2019-08-26 13:24
閱讀 2488·2019-08-26 12:20