vue-cli3单页构建大型项目方案

一、vue-cli3单页面构建方案

1、在目标文件夹内执行

vue ui ; 一个ui版界面,用于创建vue项目;

2、打开router文件夹内的index,看情况配置router的模式,是默认的hash还是history?

ps:个人推介history模式,因为内嵌如app的H5页面的话,有可能某些app是不允许页面上带有'#'的,而hash会在url上利用#来做路由转发。
ps:history模式在发布到服务器上需要nginx配置一下。详情请自行百度。
const router = new VueRouter({
  base: '/',
  mode: 'history', //还可设置为'hash'模式
  routes
})

3、在根目录新建vue.config.js,覆盖webpack配置,将如下内容copy到文件中,作为初始配置


// const webpack = require('webpack')

module.exports = {
    lintOnSave: false, // 禁止eslint
    devServer: {
        open: true, // 构建完成自动打开浏览器
    },

    configureWebpack: {
        plugins: [
            // 全局配置node_modules中的模块,使用时无需引入
            new webpack.ProvidePlugin({
                $: "jquery",
                jQuery: "jquery",
                "windows.jQuery": "jquery"
            })
        ]

    },

    // webpack 链接 API,用于生成和修改 webapck 配置
    chainWebpack: (config) => {
        // 取消 chunks,每个页面只对应一个单独的 JS / CSS
        config.optimization.splitChunks({
            cacheGroups: {}
        });

        // config
        //     .plugin('webpack-bundle-analyzer')
        //     .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin)
    },

    pluginOptions: {

    }

}

4、配置完这些后,npm run serve启动项目,会加载如下两个js

npm run serve
1.png

app.js:是所有单页面首次渲染都必须加载的js,内部合并了框架及js(如vue、vue-x、vue-router及非异步组件但引用了的node_modules中的模块),及所有页面公用的模块。

about.js:是每个页面独立的js,这个跟router中引用模块的方式有关。

具体详解如下:

1、
import Home from '../views/Home.vue'
这种引用方式引用页面模版组件,就不会出现about.js文件,因为属于同步模块,当前件建的js会被打包进app.js。但是此种随着页面的增多,公用的app.js会越来越大。看情况在app.js大小接受的前提下权衡使用;
2、
const routes = [
  {
    path: '/',
    name: 'Home',
    // component: Home
    component: () => import(/* webpackChunkName: "home" */ '../views/Home.vue') // webpack的魔法注释,将拆分出的js命名为home
  },
  {
    path: '/about',
    name: 'About',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  }
]
这种引用方式就是异步引用模版组件,不会将当前组建的js打包进app.js,就不会出现1种的问题。因为只要没有加载到对应页面,就不会加载对应页面的js。对应页面的js会最为独立的js单独的动态引入,如同上图的about.js,在进入about页面时才会引入。
3、

如果在main.js中引入的node_modules包,则会直接打包进app.js,这个逃不掉。

ps:最终结论,建议每个页面都异步引用页面模版。

5、每个页面如果引了node_mudules就会存在相对应的vendors-home.js,如果没引入node_mudules的话,每个页面按需组件就不会存在vendors-home.js这个文件,如下图:

2.png
3.png

6、区分本地、测试、线上环境

ps:官网提供一种方案,但是需要建立多个环境配置的配置文件,嫌麻烦,就不使用官方的方式,使用如下插件拆分环境:
cross-env:https://github.com/kentcdodds/cross-env
cnpm i cress-env --save-dev // 更改node环境变量插件

之后在package.json中加入如下三行配置,即可区分本地、测试、线上环境

"scripts": {
    "serve": "cross-env NODE_ENV=development vue-cli-service serve",
    "test": "cross-env NODE_ENV=test vue-cli-service build",
    "build": "cross-env NODE_ENV=production vue-cli-service build"
  },
在vue.config.js中执行如下代码即可打印出当前环境。
console.log(process.env.NODE_ENV)
在src目录下新建config目录,进入目录,新建gateway.config.js文件用于配置不同环境接口host,代码如下:
gateway.config.js文件内容如下:
// 开发环境地址(npm run serve)
const devHost = {
  // 接口地址域名相关
  baseApi: 'https://abc.com',

}

// 测试环境地址(npm run test)
const testHost = {
  // 接口地址域名相关
  baseApi: 'https://abc.com',

}

// 线上环境地址(npm run build)
const proHost = {
  // 接口地址域名相关
  baseApi: 'https://abc1.com',

}

// 区分环境选择静态资源地址
const env = process.env.NODE_ENV

let exportConfig = ''
if (env === 'production') {
  exportConfig = proHost
} else if (env === 'test') {
  exportConfig = testHost
} else {
  exportConfig = devHost
}

export default exportConfig
结束:之后只需要在接口api的js文件中引入此文件即可。发布时区分环境打包。

7、浅谈项目引入第三方插件方案

ps:原则:移动端单个js大小不超过200k;pc端单个js不超过400k;
1.vue模块化引入node_modules包插件:
前提,各个页面都是异步加载,这样的好处是单个页面的js不会被打包进公共app.js中。之后在单个js中引入第三方库。但是据测试:这种模块化引入第三方插件,比cdn模式引入的js体积至少要大2呗,因为webpack内部对每个第三方库进行了二次处理,会增大js体积。权衡js大小使用。
2.cdn模式引入第三方插件:
提供这种方式,是因为有些库是不支持vue的,只支持cdn模式引入,而且比较轻量级,可以选择这种方案,异步cdn模式引入第三方插件,这些方式最下方脚手架示例中都有demo;

8、项目中常用的打包插件及第三方库

1.vue https://cn.vuejs.org
2.vue-router https://router.vuejs.org
3.vue-x https://vuex.vuejs.org
4.sass https://www.sass.hk
5.axios http://www.axios-js.com/
6.normalize.css http://necolas.github.io/normalize.css/
7.n-zepto https://npm.taobao.org/package/n-zepto
8.webpack-bundle-analyzer https://github.com/webpack-contrib/webpack-bundle-analyzer

下方是vue-cli3内置插件(直接在vue.config.js中配置):
如下插件可参考vue-cli3官网配置方法:

https://cli.vuejs.org/zh/config/#css-sourcemap

8.autoprefixer:自动添加浏览器前缀。(如:-webkit-等)
7.url-loader:改变静态资源引用路径
7.ProvidePlugin:全局配置node_modules中的模块
具体配置方法如下(比较全的vue.config.js配置):

const webpack = require('webpack')

const processEnv = process.env.VUE_APP_ENV; // 区分环境(值:production、development、test)
const isPro = processEnv === 'production'; // 判断production环境

const outputDir = 'dist'; // 输出文件目录(默认dist)
const assetsDir = ''; // 配置放置生成的静态资源 (js、css、img、fonts) 的 (相对于 outputDir 的) 目录

// 区分环境选择cdn地址
let publicPath = '' // 静态资源引用路径
let fontPublicPath = '' // 字体图标引用的cdn路径
let imgPublicPath = '' // css引用图片的cdn路径(c2c/static/img)
if (processEnv === 'production') {
    publicPath = 'https://abc.com/c2c/shop' // 正式环境静态资源css、js等cdn路径
    fontPublicPath = `https://abc.com/c2c/shop/${assetsDir ? assetsDir + '/' : '/'}fonts` // 正式环境字体图标引用的cdn路径
    imgPublicPath = `https://abc.com/c2c/shop/${assetsDir ? assetsDir + '/' : '/'}/img` // 正式环境css引用图片的cdn路径
} else if (processEnv === 'test') {
    // publicPath = './' // 正式环境静态资源css、js等cdn路径
    publicPath = 'https://bcd.com/c2c/shop/dist' // 测试环境静态资源css、js等cdn路径
    fontPublicPath = ''
    imgPublicPath = ''
} else {
    publicPath = '/'
    fontPublicPath = ''
    imgPublicPath = ''
}

const devServerHost = 'localhost';
const devServerPort = '8080'; // 端口号
const devServerOpen = true; // 热启动后自动打开浏览器

module.exports = {

    // 配置生成dist里面static的cdn资源路径(测试环境为./,正式环境走cdn路径)
    publicPath: publicPath,

    // 输出文件目录(默认dist)
    outputDir,

    // 配置放置生成的静态资源 (js、css、img、fonts) 的 (相对于 outputDir 的) 目录
    assetsDir,

    devServer: {
        host: devServerHost,
        port: devServerPort,
        open: devServerOpen, // 构建完成自动打开浏览器


        // eslint检测影响代码编译,注释调不会影响代码编译
        // overlay: {
        //     warnings: true,
        //     errors: true
        // }
    },
    lintOnSave: processEnv === 'development' ? true : false, // 开发环境开启eslint,测试和线上编辑代码禁止eslint

    //  webpack 配置,键值对象时会合并配置,为方法时会改写配置
    configureWebpack: config => {
        // 扩展资源,不将部分资源js等打入包内引用cdn资源
        let externals = {
            // 'swiper': 'Swiper',
        };
        config.externals = externals;

        //警告 webpack 的性能提示
        config.performance = {
            hints: isPro ? 'warning' : false, // 本地开发不显示警告
            // 入口起点的最大体积
            maxEntrypointSize: 512000, // 500kib
            // 生成文件的最大体积
            maxAssetSize: 307200, // 300kib
            // 只给出 js 文件的性能提示
            assetFilter(assetFilename) {
                return assetFilename.endsWith('.js');
            }
        };
    },

    // webpack 链接 API,用于生成和修改 webapck 配置
    chainWebpack: (config) => {
        // 取消 chunks,每个页面只对应一个单独的 JS / CSS
        config.optimization.splitChunks({
            cacheGroups: {}
        });

        // 全局配置node_modules中的模块,使用时无需引入
        config.plugin('provide').use(webpack.ProvidePlugin, [{
            $: "n-zepto",
            Zepto: "n-zepto",
            "window.Zepto": "n-zepto"
        }]);

        config.module
            .rule('images')
            .use('url-loader')
            .loader('url-loader')
            .tap(options => Object.assign(options, {
                limit: 10240, // 小于10k,压缩图片 => base64
                // limit: 3000,
                publicPath: imgPublicPath,
                name: `[name].[hash:8].[ext]`
            }))

        // 设置fonts字体文件引用的路径
        config.module
            .rule('fonts')
            .test(/\.(woff2?|eot|ttf|otf)(\?.*)?$/i)
            .use('url-loader')
            .loader('file-loader')
            .tap(options => Object.assign(options, {
                limit: 5000,
                publicPath: fontPublicPath,
                name: '[name].[hash:8].[ext]'
            }))


        // npm run report;打印app.js的模块报告,查看各个模块;
        if (processEnv === 'report') {
            config
                .plugin('webpack-bundle-analyzer')
                .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin)
        }

    },

    // css配置处理
    css: {
        // 是否使用css分离插件 ExtractTextPlugin;true:页面css独立分割,false:页面css同一打包;
        extract: true,
        // 开启 CSS source maps(默认false)线上关闭,测试和本地开启
        sourceMap: isPro ? false : true,
        // css预设器配置项
        loaderOptions: {
            sass: {
                // sass的公共方法和变量,需要预编译;
                prependData: `
                    @import "@/assets/css/global.scss";
                    @import "@/assets/css/func.scss";
                `
            },
            postcss: {
                plugins: [
                    // 浏览器自动加前缀
                    require('autoprefixer')({
                        overrideBrowserslist: [
                            "Android 4.0",
                            "iOS 7",
                            "Chrome > 31",
                            "ff > 31",
                            "ie >= 8"
                        ]
                    }),
                ]
            }

        },
        /** 启用 CSS modules for all css / pre-processor files.
         引入第三方ui库和如果在js中引用了css的话,必须设置为true,因为如果设置为false的话,会把这些通过js引入的css启动css modules,会将第三方ui的css类名后面加上哈希值,会出问题。
         参考文档(vue-cli官网解释):https://cli.vuejs.org/zh/config/#css-requiremoduleextension 和
         https://cli.vuejs.org/zh/guide/css.html#css-modules
         **/
        requireModuleExtension: true
    },

    // 构建时开启多进程处理 babel 编译
    parallel: require('os').cpus().length > 1,

    pwa: {
        iconPaths: {
            favicon32: 'favicon.ico',
            favicon16: 'favicon.ico',
            appleTouchIcon: 'favicon.ico',
            maskIcon: 'favicon.ico',
            msTileImage: 'favicon.ico'
        },

    },

    // 第三方插件配置
    pluginOptions: {
        // ...
    }

}

9、一个基础配置较为完善的基于vue-cli3的单页面项目方案脚手架:

项目脚手架集合project-init
其中的cli-start-spa文件夹,内部readme有项目细节。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,258评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,335评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,225评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,126评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,140评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,098评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,018评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,857评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,298评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,518评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,400评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,993评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,638评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,661评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容

  • vue-cli3项目搭建配置以及性能优化 在之前的开发中主要用的是vue-cli2,最近空闲时间比较多,接下来有新...
    bayi_lzp阅读 19,483评论 16 68
  • 环境 系统:window10node\npm版本: 已安装vue-cli3 vue版本: 工具:vscode 新建...
    偶余杭阅读 6,536评论 0 2
  • 一、 组件component 1. 什么是组件? 组件(Component)是 Vue.js 最强大的功能之一。组...
    饥人谷_Leonardo阅读 1,964评论 0 18
  • 创建项目 vue cli是一个基于vue.js进行快速开发的完整系统,通常包含三个组件,分别是: cli:@vue...
    JunChow520阅读 2,187评论 0 0
  • 本文是学习了这篇博客的一点个人心得,给大家分享一下。 今天实现了一个简单的自定义转场动画 源码地址 逻辑实现 在A...
    chatwyn阅读 1,837评论 2 9