基於vuecli3.0腳手架項目工程的搭建

文件目錄展示


├── 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 那麼我們必須在寫樣式時,也將值改爲設計圖的一半。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章