vue-cli搭建项目

☼ 注:笔者的文章是根据自己当前项目做的笔记,具体应用请参照自己实际项目情况

1、创建应用
npm install -g @vue/cli-service-global
vue create my-vue-project
cd my-vue-project
npm run serve
2、配置axios及proxy跨域代理

⑴ 安装http-proxy-middleware

npm install http-proxy-middleware --save

⑵ 在根目录新建一个vue.config.js文件

module.exports = {
    devServer: {
        proxy: {
            '/c': {
                target: 'https://10.8.20.25',
                changeOrigin: true,
                secure: false
            },
            '/v1': {
                target: 'https://10.8.20.25',
                changeOrigin: true,
                secure: false
            }
        }
    }
}

⑶ 安装axios

npm install axios --save

⑷ 在main.js中引用

import axios from 'axios'

Vue.prototype.axios = axios.create({
  baseURL: window.location.origin
})

⑸ 在业务中使用

// 开发环境或预发布环境
const apiUrl = window.location.origin.includes('cms') ? 'v1/web/estatewebadmin' : 'clife-estate-api-web-admin'
this.url = `/${apiUrl}/estate/merchant/detail/${this.id}.do`
axios.post(this.url, { carNo: this.carNo }).then(res => console.log(res)).catch(err => console.log(err))
3、配置基本路径,解决打包首页空白问题

在vue.config.js文件中,添加以下配置

module.exports = {
    publicPath: './', //基本路径,解决打包空白问题
}
4、配置rem方案

⑴ 在src目录下新建一个utils目录(用来放置工具函数),在utils目录下新建一个rem.js文件

/*设置REM方案*/
remFont()
window.addEventListener('resize', function () {
    remFont();
    // console.log(window.innerWidth)
});
function remFont() { 
    var html = document.getElementsByTagName('html')[0];
    var width = window.innerWidth;
    var font_Size = 0;
    if(width >= 320 && width <= 1024){
      font_Size = 100/375 * width;
    }else if(width<320){
      font_Size = 100/375 * 320;
    }else{
      font_Size = 100/375 * 1024;
    }
    html.style.fontSize = font_Size + 'px';
}

⑵ 在main.js文件中,引入rem.js

import './utils/rem'

⑶ 安装postcss-px2rem

npm i postcss-px2rem --save

⑷ 在vue.config.js中配置

const px2rem = require('postcss-px2rem')
const postcss = px2rem({
    remUnit: 100   //基准大小 baseSize,需要和rem.js中相同
})
module.exports = {
    css: {
        loaderOptions: {
            postcss: {
                plugins: [
                    postcss
                ]
            }
        }
    }
}
5、引入element-ui,配置按需加载

⑴ 安装element-ui

npm i element-ui --save

⑵ 在babel.config.js中配置

module.exports = {
  presets: [
    '@vue/app'
  ],
  plugins: [["component",
    {
      "libraryName": "element-ui",
      "styleLibraryName": "theme-chalk"
    }
  ]]
}

⑶ 在main.js中引入

import { Button, Message } from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'

Vue.use(Button)
Vue.component(Message.name, Message)
Vue.prototype.$message = Message
6、引入阿里iocn

⑴ 在src文件夹下新建一个assets文件夹,再新建一个iconfont文件夹,把阿里图库生成的文件放到文件夹下
⑵ 在main.js中引入

import '@/assets/iconfont/iconfont.css'
7、引入vue-echarts图表库

⑴ 安装vue-echarts

npm install echarts vue-echarts --save

⑵ 在vue.config.js中配置

module.exports = {
    transpileDependencies: [
        'vue-echarts',
        'resize-detector'
    ]
}

⑶ 在main.js中引入

import ECharts from 'vue-echarts'
import chinaJson from 'echarts/map/json/china.json'
import 'echarts/lib/chart/map'
import 'echarts/lib/component/geo'

ECharts.registerMap('china', chinaJson)
Vue.prototype.$echarts = ECharts
Vue.component('v-chart', ECharts)

⑷ 使用

<div class="map">
    <v-chart :options="mapOptions" :autoresize="true"></v-chart>
</div>
8、配置路由

⑴ 安装vue-router

npm install vue-router --save

⑵ 在src目录下新建一个router.js文件

import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

const baseUrl = './pages/'
const communityUrl = './pages/community/components/'

const routes = [
    { path: '/', redirect: '/dataCenter' },
    { path: '/dataCenter', component: () => import(`${baseUrl}dataCenter`) },
    { path: '/community', component: () => import(`${baseUrl}community`), children: [
        {
            path: '/',
            component: () => import(`${communityUrl}guardView`)
        }, {
            path: 'parkGuard',
            component: () => import(`${communityUrl}parkGuard`)
        }
    ] }
]

export const router = new VueRouter({
    routes
})
// 登录状态重定向
router.beforeEach((to, from, next) => {
    if (!loginStatus && to.path !== '/login') {
        next('/login')
    } else {
        next()
    }
})

⑶ 在main.js中挂载

import { router } from './routes'

new Vue({
  router,
  render: h => h(App),
}).$mount('#app')
9、配置vuex

⑴ 安装vuex

npm i vuex --save

⑵ 在src目录下新建一个store文件夹,再新建一个index.js文件

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)
export default new Vuex.Store({
    state: {
        isCollapse: false
    },
    mutations: {
        toCollapse (state, bool) {
            state.isCollapse = bool
        }
    }
})

⑶ 在业务代码中使用

<template>
   <el-menu-item>
        <i v-if="!isCollapse" class="el-icon-s-fold" @click="toCollapse(true)"></i>
        <i v-else class="el-icon-s-unfold" @click="toCollapse(false)"></i>
   </el-menu-item>
</template>
<script>
import { mapMutations, mapState } from 'vuex' 
export default {
  computed: {
    ...mapState(['isCollapse'])
  },
  methods: {
    ...mapMutations(['toCollapse']),
}
</script>
10、结合axios拦截器和vuex模块化封装管理请求
⑴ 在src下新建一个api目录,用来统一管理接口地址

在api目录下新建一个config.js,配置baseURL

const HOST = window.location.origin

const apiUrl = HOST.includes('cms') ? '/v1/web/estatewebadmin' : '/clife-estate-api-web-admin'

export default HOST + apiUrl

再新建一个axios.js配置拦截器

import axios from 'axios'
import qs from 'qs'
import baseURL from './config'
import vm from '@/main'

const Axios = axios.create({
    baseURL,
    withCredentials: true, // 是否允许跨域请求携带cookie
    responseType: 'json', // 服务器响应的数据类型, 默认json
    headers: {
        'Content-Type': 'application/json;charset=UTF-8' // 请求头content-type类型, 默认json
    }
})

let loadingInstance = null

const loadingOn = loading => {
    if (!loadingInstance && loading !== false) {
        loadingInstance = vm.$loading({ target: '.el-main>div' })
    }
}

const loadingOff = () => {
    loadingInstance && setTimeout(() => {
        loadingInstance.close()
        loadingInstance = null
    }, 500)
}

// 请求拦截器
Axios.interceptors.request.use(
    config => {
        // 检测网络
        if (!navigator.onLine){
            vm.$message.error('当前网络已断开,请联网重试!')
            return
        }
        // 针对post请求和get请求处理
        if (config.method === 'post') {
            // 可以把自定义的信息和参数一起传过来,再做统一处理
            const { headers, responseType, param, loading } = config.data || {}
            // 开启loading
            loadingOn(loading)
            // 如果重写了headers, 则替换
            if (headers) {
                config.headers = headers
            }
            // 如果重写了responseType, 则替换
            if (responseType) {
                config.responseType = responseType
            }
            // 如果请求头Content-Type为表单形式,则通过qs插件把参数做相关处理
            config.data = config.headers['Content-Type'] === 'application/x-www-form-urlencoded' ? qs.stringify(param) : param
        } else {
            // 开启loading
            loadingOn(config.loading)
            // 参数
            config.params = config.param
        }
        // 取消请求
        config.cancelToken = vm.$store.state.Root.source.token

        return config
    },
    err => {
        loadingOff()
        return Promise.reject(err)
    }
)

Axios.interceptors.response.use(
    response => {
        loadingOff()
        const { data, config } = response || {}
        // code为0,返回响应数据
        if (+data.code === 0) {
            return data
        }
        // 文件流格式
        if (config.responseType === 'blob') {
            // message提示后台返回json格式的错误信息
            if (data.type === 'application/json') {
                let reader = new FileReader()
                reader.onload = e => JSON.parse(e.target.result).msg
                reader.readAsText(data)
                return
            }
            // 需要针对IE做兼容,所以直接返回blob对象,再针对不同浏览器做url转化处理
            return  new Blob([data], { type: data.type })
        }

        // 后台返回用户未登录或用户不存在
        if (+data.code === 100010110 || +data.code === 100021401) {

            return
        }

        // code非0
        vm.$message(data.msg)
        return Promise.reject(data)


    },
    err => {
        loadingOff()
        if (!err.response) {
            // 如果是取消请求导致的错误,则不做msg提示
            return Promise.reject(err)
        }
        vm.$message.warning('服务调用失败,请稍后重试!')
        return Promise.reject(err)
    }
)

export default Axios

然后按模块划分接口,比如 /api/setting/basic/project.js

import axios from '@/api/axios'

export default {
    queryProject: data => axios.post('/estate/project/queryProject.do', data)
}
⑵ 在src目录下新建一个store目录, 把接口请求方法和数据处理统一放在vuex里完成

store目录下新建一个root.js,用来处理全局方法和数据,比如取消axios请求或收缩菜单等

export default {
    namespaced: true,
    state: {
        isCollapse: false,
        source: { token: null, cancel: null }
    },
    mutations: {
        // 改变侧边菜单伸缩状态
        toCollapse: (state, bool) => {
            state.isCollapse = bool
        },
        // 取消axios请求
        soureToken: (state, source) => {
            state.source = source
        }
    }
}

再按模块划分不同的modules,和api下的模块保持一致,比如store/setting/basic/project.js

import Project from '@/api/setting/basic/project'

export default {
    namespaced: true,
    state: {
        projectRecord: [],
        totalRows: 0
    },
    actions: {
        queryProject: async ({ state }, params) => {
            const data = await Project.queryProject(params)
            try {
                state.projectRecord = data.data.record
                state.totalRows = data.data.totalRows
            } catch (err) {
                return Promise.reject(err)
            }
        }
    },
    getters: {
        projectList: state => {
            return state.projectRecord.map(item => ({
                id: item.projectId,
                name: item.projectName
            }))
        }
    }
}

然后在setting下新建一个index.js引入各个小模块的modules

import Project from './basic/project'

export default {
    namespaced: true,
    modules: {
        Project
    }
}

最后在store下新建一个index.js引入各个大模块的modules

import Vue from 'vue'
import Vuex from 'vuex'

import Root from './root'
import Setting from './setting'

Vue.use(Vuex)

export default new Vuex.Store({
    modules: {
       Root, Setting
   }
})
⑶ 在页面调用请求,获取处理后的数据结果
import { mapState, mapGetters, mapMutations } from 'vuex'
export default {
    computed: {
        ...mapState('Root', ['source']),
        ...mapState('Setting/Project', ['projectRecord']),
        ...mapGetters('Setting/Project', ['projectList']),
    },
    methods: {
        ...mapMutations('Root', ['soureToken']),
        handleClick() {
            this.source.cancel && this.source.cancel()
            this.soureToken(this.$axios.CancelToken.source())
            this.queryProject()
        },
        queryProject() {
            this.$store.dispatch('Setting/Project/queryProject', {
                param: {
                    data: {
                        project: {
                            projStatus: 1,
                            ...this.formValue
                        },
                        pager: {
                            pageRows: 10,
                            pageIndex: 1,
                            paged: true
                        },
                        orderBy: {
                            orderColumn: 'createTime',
                            orderType: 'desc'
                        }
                    }
                },
                // loading: false
            }).then(() => {
                console.log(this.projectRecord)
            })
        }
    },
    created() {
        this.queryProject()
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,036评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,046评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,411评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,622评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,661评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,521评论 1 304
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,288评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,200评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,644评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,837评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,953评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,673评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,281评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,889评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,011评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,119评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,901评论 2 355

推荐阅读更多精彩内容