基于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 那么我们必须在写样式时,也将值改为设计图的一半。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值