文件目錄展示
├── public public中的靜態資源會被複制到輸出目錄(dist)中,不會經過webpack
├── scr
│ ├── api 與後端交互使用相關方法和配置
│ │ ├── index.js: api的入口文件
│ │ ├── administrator.js: 對應模塊得相關方法
│ ├── assets 放置一些靜態資源文件
│ ├── components
│ │ ├── index.js:components的入口文件
│ │ ├── global: 全局組件文件夾
│ │ │ ├── alert: alert組件
│ │ │ │ ├── index.vue: 組件.vue文件
│ │ │ │ ├── index.js: 導出組件文件
│ ├── filters
│ │ ├── index.js:filters入口文件
│ ├── router
│ │ ├── index.js:router 入口文件
│ ├── store
│ │ ├── index.js:store入口文件
│ │ ├── modules:模塊文件
│ │ │ ├── app.js: 公共的
│ ├── utils
│ │ ├── utils.js: 工具方法
│ ├── views
│ │ ├── 404: 404頁面
│ ├── mock mock數據
│ │ ├── index.js: mock數據入口文件
│ ├── lang 存放i18n文件
│ │ ├── index.js: lang入口文件
│ ├── App.vue 頂層路由組件
│ ├── main.js vue入口文件
├── .browserslistrc 目標瀏覽器配置表
├── .editorconfig 編輯器配置
├── .env.development 本地啓動項目運行環境配置(配置環境變量)
├── .env.production 生產環境配置(配置環境變量)
├── .eslintrc eslint配置文件
├── .gitignore 忽略提交git倉庫得文件
├── .babel.config.js 配置babel文件
├── package.json 模塊描述文件
├── package-lock.json 鎖版本包
├── postcss.config.js css預處理器
├── README.md 項目描述文件
├── vue.config.js 項目自定義配置文件
api
// book.js
import _axios from "./index";
export const book = params => _axios("get", "/book/ebook", params);
// index.js
import axios from "axios";
// 創建axios的一個實例
var instance = axios.create({
baseURL: process.env.VUE_APP_BASE_URL, // 環境變量
timeout: 6000
});
// 一、請求攔截器
instance.interceptors.request.use(
function(config) {
return config;
},
function(error) {
// 對請求錯誤做些什麼
return Promise.reject(error);
}
);
// 二、響應攔截器
instance.interceptors.response.use(
function(response) {
return response.data;
},
function(error) {
// 對響應錯誤做點什麼
return Promise.reject(error);
}
);
/**
* 使用es6的export default導出了一個函數,導出的函數代替axios去幫我們請求數據,
* 函數的參數及返回值如下:
* @param {String} method 請求的方法:get、post、delete、put
* @param {String} url 請求的url:
* @param {Object} data 請求的參數
* @returns {Promise} 返回一個promise對象,其實就相當於axios請求數據的返回值
*/
export default function(method, url, data = null) {
method = method.toLowerCase();
if (method == "post") {
return instance.post(url, data);
} else if (method == "get") {
return instance.get(url, { params: data });
} else if (method == "delete") {
return instance.delete(url, { params: data });
} else if (method == "put") {
return instance.put(url, data);
} else {
console.error("未知的method" + method);
return false;
}
}
<!-- 使用 -->
<script>
import { book } from '@/api/book'
export default {
name: 'app',
mounted() {
this.books()
},
methods: {
async books() {
//book()執行完,
//book_info
let book_info = await book()
console.log(book_info);
//2.2假設登錄成功,返回的數據應該是
//book_info={code:200,msg:'success',data:[]}
// 等右邊有返回值時,纔會繼續執行代碼
}
}
}
</script>
components
- global/fa-button
// index.js
import faButton from "./index.vue";
export default faButton;
<!-- index.vue -->
<template>
<div>test component</div>
</template>
<script>
export default {
name:'faButton' // 全局註冊的需要定義name值,作爲組件的名字
}
</script>
<style lang="less" scoped></style>
- index.js : 自動加載
global
目錄下的.js
結尾的文件, 來實現自動註冊組件目的
// index.js
import Vue from 'vue'
// 自動加載 global 目錄下的 .js 結尾的文件
const componentsContext = require.context('./global', true, /\.js$/)
componentsContext.keys().forEach(component => {
const componentConfig = componentsContext(component)
/**
* 兼容 import export 和 require module.export 兩種規範
*/
const ctrl = componentConfig.default || componentConfig
Vue.component(ctrl.name, ctrl)
// main.js
import '@/components' // 全局組件自動註冊
<!-- 使用組件 -->
<template>
<div>
<fa-button></fa-button>
</div>
</template>
router
// Home/index.js
export default [
{
path:'/',
redirect:'/home'
},
{
path: '/home',
name: 'home',
component: () => import(/* webpackChunkName: "index" */ '@/views/Home')
}
]
// index.js
import Vue from "vue";
import Router from "vue-router";
Vue.use(Router);
let routes = [];
// 自動解析 ./ 目錄下的 index.js 文件 自動引入該文件中
const routerContext = require.context("./", true, /index\.js$/);
routerContext.keys().forEach(route => {
// 如果是根目錄的 index.js 不處理
if(route.startsWith('./index')){
return
}
const routerModule = routerContext(route)
/**
* 兼容 import export 和 require module.export 兩種規範
*/
routes = [...routes,...(routerModule.default || routerModule)]
})
export default new Router({
routes
});
store
// modules/book.js
const book = {
state: {
test: 1
},
actions: {
setTest: ({ commit, state }, newTest) => {
return commit("SET_TEST", newTest);
}
},
mutations: {
'SET_TEST': (state, newTest) => {
state.test = newTest;
}
},
getters: {
book: state => state.test
}
};
export default book
// index.js
import Vue from "vue";
import Vuex from "vuex";
import book from "./modules/book"
Vue.use(Vuex);
export default new Vuex.Store({
modules:{
book
}
})
<!-- 使用 -->
<script>
// 這樣會寫很多重複代碼而且不方便維護,後面使用mixin的方法統一管理
import { mapGetters } from 'vuex'
import { mapActions } from 'vuex'
// import { ebookMixin } from '@/utils/mixin'
export default {
name: 'app',
// mixins: [ebookMixin], // mixin以當前代碼進行混合,減少重複代碼
computed: {
...mapGetters(['book'])
},
created() {
// 提交參數10之後,返回的是promise對象, 來獲取 book 的值
this.setTest(10).then(() => {
console.log(this.book); // 獲取vuex中book的值
})
},
methods: {
...mapActions(['setTest']) // 這樣寫就可以簡化提交操作,這種提交vuex數據需要在methods中才能生效
}
}
</script>
utils
//book.js 存放某個模塊下的靜態變量
export const FONT_SIZE_LIST = [
{ fontSize: 12 },
{ fontSize: 14 },
{ fontSize: 16 },
{ fontSize: 18 },
{ fontSize: 20 },
{ fontSize: 22 },
{ fontSize: 24 }
];
npm install web-storage-cache -S
// localStorage.js 引入第三方庫(進行超時處理,序列化)
import Storage from "web-storage-cache";
const localStorage = new Storage();
export function setLocalStorage(key, value) {
return localStorage.set(key, value);
}
export function getLocalStorage(key) {
return localStorage.get(key);
}
export function removeLocalStorage(key) {
return localStorage.delete(key);
}
export function clearLocalStorage() {
return localStorage.clear();
}
/**
* mixin.js 複用的方法放在 mixin 裏,儘量讓單個組件的代碼精簡
* */
import { mapGetters, mapActions } from "vuex";
export const ebookMixin = {
computed: {
...mapGetters(["book"])
},
methods: {
...mapActions(['setTest'])
},
};
<!-- 使用 -->
<script>
import { FONT_SIZE_LIST } from '@/utils/book'
import { ebookMixin } from '@/utils/mixin'
import { setLocalStorage } from '@/utils/localStorage' // localStorage引入第三方庫
export default {
name: 'app',
mixins: [ebookMixin], // mixin以當前代碼進行混合,減少重複代碼
data() {
return {
aFontSize: FONT_SIZE_LIST
}
},
created() {
setLocalStorage('test', { name: 1 })
}
}
</script>
mock
npm install mockjs -D # 項目上線時要禁用mock.js,不然會出現請求頭無法攜帶cookie的問題
// book.js 存放數據文件
module.exports = {
list:[
{"name":"肖申克的救贖"},
{"name":"狼性"},
{"name":"弟子規"}
]
}
// index.js
import Mock from 'mockjs'
import book from './book'
Mock.mock(/\/book\/ebook/,'get',book)
// main.js
import './mock'
new Vue({
router,
i18n,
store,
render: h => h(App)
}).$mount("#app");
lang
npm install vue-i18n -S
// cn.js 存放數據文件
const messages = {
home:{
title:'書城'
},
book:{
title:'書名'
}
}
export default messages
// index.js
import Vue from 'vue'
import VueI18N from 'vue-i18n'
import cn from './cn'
Vue.use(VueI18N)
const messages = {
cn
}
const locale = 'cn'
const i18n = new VueI18N({
locale,
messages
})
export default i18n
// main.js
import i18n from './lang' // 國際化
Vue.config.productionTip = false;
new Vue({
router,
i18n,
store,
render: h => h(App)
}).$mount("#app");
<!-- 使用 -->
<router-link to="/">{{$t('home.title')}}</router-link>|
移動端適配( “lib-flexible” + “postcss-px2rem-exclude”)
- 在main.js中引入如下代碼
// lib-flexible.js
;(function(win, lib) {
var doc = win.document;
var docEl = doc.documentElement;
var metaEl = doc.querySelector('meta[name="viewport"]');
var flexibleEl = doc.querySelector('meta[name="flexible"]');
var dpr = 0;
var scale = 0;
var tid;
var flexible = lib.flexible || (lib.flexible = {});
if (metaEl) {
console.warn('將根據已有的meta標籤來設置縮放比例');
var match = metaEl.getAttribute('content').match(/initial\-scale=([\d\.]+)/);
if (match) {
scale = parseFloat(match[1]);
dpr = parseInt(1 / scale);
}
} else if (flexibleEl) {
var content = flexibleEl.getAttribute('content');
if (content) {
var initialDpr = content.match(/initial\-dpr=([\d\.]+)/);
var maximumDpr = content.match(/maximum\-dpr=([\d\.]+)/);
if (initialDpr) {
dpr = parseFloat(initialDpr[1]);
scale = parseFloat((1 / dpr).toFixed(2));
}
if (maximumDpr) {
dpr = parseFloat(maximumDpr[1]);
scale = parseFloat((1 / dpr).toFixed(2));
}
}
}
if (!dpr && !scale) {
var isAndroid = win.navigator.appVersion.match(/android/gi);
var isIPhone = win.navigator.appVersion.match(/iphone/gi);
var devicePixelRatio = win.devicePixelRatio;
if (isIPhone) {
// iOS下,對於2和3的屏,用2倍的方案,其餘的用1倍方案
if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) {
dpr = 3;
} else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)){
dpr = 2;
} else {
dpr = 1;
}
} else {
// 其他設備下,仍舊使用1倍的方案
dpr = 1;
}
scale = 1 / dpr;
}
docEl.setAttribute('data-dpr', dpr);
if (!metaEl) {
metaEl = doc.createElement('meta');
metaEl.setAttribute('name', 'viewport');
metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
if (docEl.firstElementChild) {
docEl.firstElementChild.appendChild(metaEl);
} else {
var wrap = doc.createElement('div');
wrap.appendChild(metaEl);
doc.write(wrap.innerHTML);
}
}
function refreshRem(){
var width = docEl.getBoundingClientRect().width;
if (width / dpr > 750) { // 這裏的值需求去改
width = 750 * dpr;
}
var rem = width / 10;
docEl.style.fontSize = rem + 'px';
flexible.rem = win.rem = rem;
}
win.addEventListener('resize', function() {
clearTimeout(tid);
tid = setTimeout(refreshRem, 300);
}, false);
win.addEventListener('pageshow', function(e) {
if (e.persisted) {
clearTimeout(tid);
tid = setTimeout(refreshRem, 300);
}
}, false);
if (doc.readyState === 'complete') {
doc.body.style.fontSize = 12 * dpr + 'px';
} else {
doc.addEventListener('DOMContentLoaded', function(e) {
doc.body.style.fontSize = 12 * dpr + 'px';
}, false);
}
refreshRem();
flexible.dpr = win.dpr = dpr;
flexible.refreshRem = refreshRem;
flexible.rem2px = function(d) {
var val = parseFloat(d) * this.rem;
if (typeof d === 'string' && d.match(/rem$/)) {
val += 'px';
}
return val;
}
flexible.px2rem = function(d) {
var val = parseFloat(d) / this.rem;
if (typeof d === 'string' && d.match(/px$/)) {
val += 'rem';
}
return val;
}
})(window, window['lib'] || (window['lib'] = {}));
- 安裝postcss-px2rem-exclude
npm install postcss-px2rem-exclude --save
- 正確的配置位置是項目根目錄下的postcss.config.js文件,如果你的項目沒有生成這個獨立文件,就需要在你的package.js裏設置。
// postcss.config.js
module.exports = {
plugins: {
autoprefixer: {},
"postcss-px2rem-exclude": {
remUnit: 75,
exclude: /node_modules|folder_name/i
}
}
};
// package.json
"postcss": {
"plugins": {
"autoprefixer": {},
"postcss-px2rem-exclude":{
"remUnit": 37.5,
"exclude":"/node_modules|floder_name/i"
}
}
},
溫馨提示: remUnit這個配置項的數值是多少呢??? 通常我們是根據設計圖來定這個值,原因很簡單,便於開發。假如設計圖給的寬度是750,我們通常就會把remUnit設置爲75,這樣我們寫樣式時,可以直接按照設計圖標註的寬高來1:1還原開發。之所以設爲37.5,是爲了引用像mint-ui這樣的第三方UI框架,因爲第三方框架沒有兼容px2rem ,將remUnit的值設置爲設計圖寬度(這裏爲750px)75的一半,即可以1:1還原mint-ui的組件,否則會樣式會有變化,例如按鈕會變小。既然設置成了37.5 那麼我們必須在寫樣式時,也將值改爲設計圖的一半。