文件目录展示
├── 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 那么我们必须在写样式时,也将值改为设计图的一半。