devserver配置_前端开发,一份关于vue-cli3项目常用项配置

配置全局cdn,包含js、css开启Gzip压缩,包含文件js、css去掉注释、去掉console.log压缩图片本地代理设置别名,vscode也能识别配置环境变量开发模式、测试模式、生产模式请求路由动态添加axios配置添加mock数据配置全局less只打包改变的文件开启分析打包日志

vue.config.js

完整的架构配置

const path = require('path');const UglifyJsPlugin = require('uglifyjs-webpack-plugin') // 去掉注释const CompressionWebpackPlugin = require('compression-webpack-plugin'); // 开启压缩const { HashedModuleIdsPlugin } = require('webpack');function resolve(dir) {    return path.join(__dirname, dir)}const isProduction = process.env.NODE_ENV === 'production';// cdn预加载使用const externals = {    'vue': 'Vue',    'vue-router': 'VueRouter',    'vuex': 'Vuex',    'axios': 'axios',    "element-ui": "ELEMENT"}const cdn = {    // 开发环境    dev: {        css: [            'https://unpkg.com/element-ui/lib/theme-chalk/index.css'        ],        js: []    },    // 生产环境    build: {        css: [            'https://unpkg.com/element-ui/lib/theme-chalk/index.css'        ],        js: [            'https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.min.js',            'https://cdn.jsdelivr.net/npm/vue-router@3.0.1/dist/vue-router.min.js',            'https://cdn.jsdelivr.net/npm/vuex@3.0.1/dist/vuex.min.js',            'https://cdn.jsdelivr.net/npm/axios@0.18.0/dist/axios.min.js',            'https://unpkg.com/element-ui/lib/index.js'        ]    }}module.exports = {    lintOnSave: false, // 关闭eslint    productionSourceMap: false,    publicPath: './',     outputDir: process.env.outputDir, // 生成文件的目录名称    chainWebpack: config => {        config.resolve.alias            .set('@', resolve('src'))        // 压缩图片        config.module            .rule('images')            .test(/.(png|jpe?g|gif|svg)(?.*)?$/)            .use('image-webpack-loader')            .loader('image-webpack-loader')            .options({ bypassOnDebug: true })        // webpack 会默认给commonChunk打进chunk-vendors,所以需要对webpack的配置进行delete        config.optimization.delete('splitChunks')        config.plugin('html').tap(args => {            if (process.env.NODE_ENV === 'production') {                args[0].cdn = cdn.build            }            if (process.env.NODE_ENV === 'development') {                args[0].cdn = cdn.dev            }            return args        })                config            .plugin('webpack-bundle-analyzer')            .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin)    },    configureWebpack: config => {        const plugins = [];        if (isProduction) {            plugins.push(                new UglifyJsPlugin({                    uglifyOptions: {                        output: {                            comments: false, // 去掉注释                        },                        warnings: false,                        compress: {                            drop_console: true,                            drop_debugger: false,                            pure_funcs: ['console.log']//移除console                        }                    }                })            )            // 服务器也要相应开启gzip            plugins.push(                new CompressionWebpackPlugin({                    algorithm: 'gzip',                    test: /.(js|css)$/,// 匹配文件名                    threshold: 10000, // 对超过10k的数据压缩                    deleteOriginalAssets: false, // 不删除源文件                    minRatio: 0.8 // 压缩比                })            )            // 用于根据模块的相对路径生成 hash 作为模块 id, 一般用于生产环境            plugins.push(                new HashedModuleIdsPlugin()            )            // 开启分离js            config.optimization = {                runtimeChunk: 'single',                splitChunks: {                    chunks: 'all',                    maxInitialRequests: Infinity,                    minSize: 1000 * 60,                    cacheGroups: {                        vendor: {                            test: /[/]node_modules[/]/,                            name(module) {                                // 排除node_modules 然后吧 @ 替换为空 ,考虑到服务器的兼容                                const packageName = module.context.match(/[/]node_modules[/](.*?)([/]|$)/)[1]                                return `npm.${packageName.replace('@', '')}`                            }                        }                    }                }            };            // 取消webpack警告的性能提示            config.performance = {                hints: 'warning',                //入口起点的最大体积                maxEntrypointSize: 1000 * 500,                //生成文件的最大体积                maxAssetSize: 1000 * 1000,                //只给出 js 文件的性能提示                assetFilter: function (assetFilename) {                    return assetFilename.endsWith('.js');                }            }            // 打包时npm包转CDN            config.externals = externals;        }        return { plugins }    },    pluginOptions: {        // 配置全局less        'style-resources-loader': {            preProcessor: 'less',            patterns: [resolve('./src/style/theme.less')]        }    },    devServer: {        open: false, // 自动启动浏览器        host: '0.0.0.0', // localhost        port: 6060, // 端口号        https: false,        hotOnly: false, // 热更新        proxy: {            '^/sso': {                target: process.env.VUE_APP_SSO, // 重写路径                ws: true,   //开启WebSocket                secure: false,      // 如果是https接口,需要配置这个参数                changeOrigin: true            }        }    }}

html模板配置cdn

            We're sorry but  doesn't work properly without JavaScript enabled.            Please enable it to continue.                    

开启Gzip压缩,包含文件js、css

new CompressionWebpackPlugin({      algorithm: 'gzip',      test: /.(js|css)$/, // 匹配文件名      threshold: 10000, // 对超过10k的数据压缩      deleteOriginalAssets: false, // 不删除源文件      minRatio: 0.8 // 压缩比})

去掉注释、去掉console.log

安装cnpm i uglifyjs-webpack-plugin -D

const UglifyJsPlugin = require('uglifyjs-webpack-plugin')new UglifyJsPlugin({    uglifyOptions: {        output: {            comments: false, // 去掉注释        },        warnings: false,        compress: {            drop_console: true,            drop_debugger: false,            pure_funcs: ['console.log'] //移除console        }    }})

压缩图片

chainWebpack: config => {    // 压缩图片    config.module        .rule('images')        .test(/.(png|jpe?g|gif|svg)(?.*)?$/)        .use('image-webpack-loader')        .loader('image-webpack-loader')        .options({ bypassOnDebug: true })}

本地代理

devServer: {    open: false, // 自动启动浏览器    host: '0.0.0.0', // localhost    port: 6060, // 端口号    https: false,    hotOnly: false, // 热更新    proxy: {        '^/sso': {            target: process.env.VUE_APP_SSO, // 重写路径            ws: true, //开启WebSocket            secure: false, // 如果是https接口,需要配置这个参数            changeOrigin: true        }    }}

设置vscode 识别别名

在vscode中插件安装栏搜索 Path Intellisense 插件,打开settings.json文件添加 以下代码 "@": "${workspaceRoot}/src",按以下添加

{    "workbench.iconTheme": "material-icon-theme",    "editor.fontSize": 16,    "editor.detectIndentation": false,    "guides.enabled": false,    "workbench.colorTheme": "Monokai",    "path-intellisense.mappings": {        "@": "${workspaceRoot}/src"    }}

在项目package.json所在同级目录下创建文件jsconfig.json

{    "compilerOptions": {        "target": "ES6",        "module": "commonjs",        "allowSyntheticDefaultImports": true,        "baseUrl": "./",        "paths": {          "@/*": ["src/*"]        }    },    "exclude": [        "node_modules"    ]}

如果还没请客官移步在vscode中使用别名@按住ctrl也能跳转对应路径

配置环境变量开发模式、测试模式、生产模式

在根目录新建

.env.development

# 开发环境NODE_ENV='development'VUE_APP_SSO='http://http://localhost:9080'

.env.test

NODE_ENV = 'production' # 如果我们在.env.test文件中把NODE_ENV设置为test的话,那么打包出来的目录结构是有差异的VUE_APP_MODE = 'test'VUE_APP_SSO='http://http://localhost:9080'outputDir = test

.env.production

NODE_ENV = 'production'VUE_APP_SSO='http://http://localhost:9080'

package.json

"scripts": {    "build": "vue-cli-service build", //生产打包    "lint": "vue-cli-service lint",    "dev": "vue-cli-service serve", // 开发模式    "test": "vue-cli-service build --mode test", // 测试打包    "publish": "vue-cli-service build && vue-cli-service build --mode test" // 测试和生产一起打包 }
41938fb7b89741b29df4e006083452e6

请求路由动态添加

router/index.js文件

import Vue from 'vue';import VueRouter from 'vue-router'Vue.use(VueRouter)import defaultRouter from './defaultRouter'import dynamicRouter from './dynamicRouter';import store from '@/store';const router = new VueRouter({    routes: defaultRouter,    mode: 'hash',    scrollBehavior(to, from, savedPosition) {        // keep-alive 返回缓存页面后记录浏览位置        if (savedPosition && to.meta.keepAlive) {            return savedPosition;        }        // 异步滚动操作        return new Promise((resolve, reject) => {            setTimeout(() => {                resolve({ x: 0, y: 0 })            }, 200)        })    }})// 消除路由重复警告const selfaddRoutes = function (params) {    router.matcher = new VueRouter().matcher;    router.addRoutes(params);}// 全局路由拦截router.beforeEach((to, from, next) => {    const { hasRoute } = store.state; // 防止路由重复添加    if (hasRoute) {        next()    } else {        dynamicRouter(to, from, next, selfaddRoutes)    }})export default router;

dynamicRouter.js

import http from '@/http/request';import defaultRouter from './defaultRouter'import store from '@/store'// 重新构建路由对象const menusMap = function (menu) {    return menu.map(v => {        const { path, name, component } = v        const item = {            path,            name,            component: () => import(`@/${component}`)        }        return item;    })}// 获取路由const addPostRouter = function (to, from, next, selfaddRoutes) {    http.windPost('/mock/menu') // 发起请求获取路由        .then(menu => {            defaultRouter[0].children.push(...menusMap(menu));            selfaddRoutes(defaultRouter);            store.commit('hasRoute', true);            next({ ...to, replace: true })        })}export default addPostRouter;

defaultRouter.js 默认路由

const main = r => require.ensure([], () => r(require('@/layout/main.vue')), 'main')const index = r => require.ensure([], () => r(require('@/view/index/index.vue')), 'index')const about = r => require.ensure([], () => r(require('@/view/about/about.vue')), 'about')const detail = r => require.ensure([], () => r(require('@/view/detail/detail.vue')), 'detail')const error = r => require.ensure([], () => r(require('@/view/404/404.vue')), 'error');const defaultRouter = [    {        path: "/",         component: main, // 布局页        redirect: {            name: "index"        },        children:[            {                path: '/index',                component: index,                name: 'index',                meta: {                    title: 'index'                }            },            {                path: '/about',                component: about,                name: 'about',                meta: {                    title: 'about'                }            },            {                path: '/detail',                component: detail,                name: 'detail',                meta: {                    title: 'detail'                }            }        ]    },    {        path: '/404',        component: error,        name: '404',        meta: {            title: '404'        }    }]export default defaultRouter;

axios配置

import axios from "axios";import merge from 'lodash/merge'import qs from 'qs'/** * 实例化 * config是库的默认值,然后是实例的 defaults 属性,最后是请求设置的 config 参数。后者将优先于前者 */const http = axios.create({    timeout: 1000 * 30,    withCredentials: true, // 表示跨域请求时是否需要使用凭证});/** * 请求拦截 */http.interceptors.request.use(function (config) {    return config;}, function (error) {    return Promise.reject(error);});/** * 响应拦截 */http.interceptors.response.use(response => {    // 过期之类的操作    if (response.data && (response.data.code === 401)) {        // window.location.href = ''; 重定向    }    return response}, error => {    return Promise.reject(error)})/** * 请求地址处理 */http.adornUrl = (url) => {    return url;}/** * get请求参数处理 * params 参数对象 * openDefultParams 是否开启默认参数 */http.adornParams = (params = {}, openDefultParams = true) => {    var defaults = {        t: new Date().getTime()    }    return openDefultParams ? merge(defaults, params) : params}/** * post请求数据处理 * @param {*} data 数据对象 * @param {*} openDefultdata 是否开启默认数据? * @param {*} contentType 数据格式 *  json: 'application/json; charset=utf-8' *  form: 'application/x-www-form-urlencoded; charset=utf-8' */http.adornData = (data = {}, openDefultdata = true, contentType = 'json') => {    var defaults = {        t: new Date().getTime()    }    data = openDefultdata ? merge(defaults, data) : data    return contentType === 'json' ? JSON.stringify(data) : qs.stringify(data)}/** * windPost请求 * @param {String} url [请求地址] * @param {Object} params [请求携带参数] */http.windPost = function (url, params) {    return new Promise((resolve, reject) => {        http.post(http.adornUrl(url), qs.stringify(params))            .then(res => {                resolve(res.data)            })            .catch(error => {                reject(error)            })    })}/** * windJsonPost请求 * @param {String} url [请求地址] * @param {Object} params [请求携带参数] */http.windJsonPost = function (url, params) {    return new Promise((resolve, reject) => {        http.post(http.adornUrl(url), http.adornParams(params))            .then(res => {                resolve(res.data)            })            .catch(error => {                reject(error)            })    })}/** * windGet请求 * @param {String} url [请求地址] * @param {Object} params [请求携带参数] */http.windGet = function (url, params) {    return new Promise((resolve, reject) => {        http.get(http.adornUrl(url), { params: params })            .then(res => {                resolve(res.data)            })            .catch(error => {                reject(error)            })    })}/** * 上传图片 */http.upLoadPhoto = function (url, params, callback) {    let config = {}    if (callback !== null) {        config = {            onUploadProgress: function (progressEvent) {                //属性lengthComputable主要表明总共需要完成的工作量和已经完成的工作是否可以被测量                //如果lengthComputable为false,就获取不到progressEvent.total和progressEvent.loaded                callback(progressEvent)            }        }    }    return new Promise((resolve, reject) => {        http.post(http.adornUrl(url), http.adornParams(params), config)            .then(res => {                resolve(res.data)            })            .catch(error => {                reject(error)            })    })}export default http;

添加mock数据

const Mock = require('mockjs')// 获取 mock.Random 对象const Random = Mock.Random// mock新闻数据,包括新闻标题title、内容content、创建时间createdTimeconst produceNewsData = function () {    let newsList = []    for (let i = 0; i < 3; i++) {        let newNewsObject = {}        if(i === 0){            newNewsObject.path = '/add/article';            newNewsObject.name  = 'add-article';            newNewsObject.component = 'modules/add/article/article';        }        if(i === 1){            newNewsObject.path = '/detail/article';            newNewsObject.name  = 'detail-article';            newNewsObject.component = 'modules/detail/article/article'        }        if(i === 2){            newNewsObject.path = '/edit/article';            newNewsObject.name  = 'edit-article';            newNewsObject.component = 'modules/edit/article/article'        }        newsList.push(newNewsObject)    }    return newsList;}Mock.mock('/mock/menu', produceNewsData)

配置全局less

pluginOptions: {    // 配置全局less    'style-resources-loader': {        preProcessor: 'less',        patterns: [resolve('./src/style/theme.less')]    }}

只打包改变的文件

安装cnpm i webpack -D

const { HashedModuleIdsPlugin } = require('webpack');configureWebpack: config => {        const plugins = [];    plugins.push(        new HashedModuleIdsPlugin()    )}

开启分析打包日志

安装cnpm i webpack-bundle-analyzer -D

chainWebpack: config => {    config        .plugin('webpack-bundle-analyzer')        .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin)}

原作者姓名:羊先生
原出处:segmentfault
原文链接:一份关于vue-cli3项目常用项配置 - vipbic - SegmentFault 思否

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值