Vue多页面优化踩坑记录

最近优化之前的项目,从单页面拆分成了多页面,这里记录下整个踩坑过程。

拆分成多个页面

1.将原项目的单页面替换为多页面


这里我添加了index.htmluser.html两个页面。

2.配置vue.config.js

// vue.config.jsconst titles = require('./title.js')const glob = require('glob')const pages = {}glob.sync('./src/pages/**/main.js').forEach(path => {    const chunk = path.split('./src/pages/')[1].split('/main.js')[0]    pages[chunk] = {        entry: path,        template: 'public/index.html',        title: titles[chunk],        chunks: ['chunk-vendors', 'chunk-common', chunk]    }})module.exports = {    pages,    chainWebpack: config => config.plugins.delete('named-chunks'),    devServer: {        proxy: {            '/api': {                target: 'http://127.0.0.1:8080',                changeOrigin: true,                pathRewrite: { '^/api': '' }            }        }    }}

可以执行vue inspect查看完整配置信息:

路由懒加载

vue-router官方给出的示例如下,这里webpackChunkName如果不写打包时会自动生成序号代替。

//router/index.js{    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')  },

为了方便追踪打包情况最好写上,就可以看到about.[hash].js的大小了。

如果想要多个路由打包进一个js里,写同一个webpackChunkName即可

{    path: '/another',    name: 'another',    // 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/Another.vue')  }

打包后about.js文件变大了0.33Kb,多了一个页面。

引入代码分析BundleAnalyzerPlugin

npm i webpack-bundle-analyzer后修改vue.config.js配置

//vue.config.jsconst BundleAnalyzerPlugin = require("webpack-bundle-analyzer")  .BundleAnalyzerPlugin;const report = process.env.npm_config_report; module.exports = {  ...  configureWebpack: config => {    if (report) {      config.plugins.push(new BundleAnalyzerPlugin());    }  },  ... }

再在package.json中添加

"scripts": {    ...    "analyze": "npm_config_report=true npm run build"  },

这里通过控制npm_config_report来显示分析页面,执行npm run analyze可以看到打包情况

提取第三方插件

上图可以看到vuevue-routervuex占据了大部分空间,它们和我们的实际开发无关,且不会经常变化,我们可以把它们单独提取出来,这样不用每次都重新打包,浏览器访问时因为并行加载和缓存会极大地提高访问效率。

常见的优化方法有两种:一是通过cdn搭配externals来实现,二是通过单独打包搭配DllReferencePlugin

简单说下两种优劣:
cdn+externals:配置简单,全环境下都可访问到cdn,如果不慎调用了打包文件内部的方法,可能会导致重复打包;
DllReferencePlugin:配置较复杂,需要手动生成dll,所有访问指向同一份dll,不会造成重复打包。

利用`externals`优化

1.简单的配置方法

最简单的做法就是将cdn手动添加到index.html

<body>    <div id="app">    <script src="https: cdn.bootcss.com="" vue="" 2.6.10="" vue.min.js"="">    <script src="https: cdn.bootcss.com="" vue-router="" 3.1.3="" vue-router.min.js"="">    <script src="https: cdn.bootcss.com="" vuex="" 3.1.1="" vuex.min.js"=""/></script src="https:></script src="https:></div id="app"></body>

然后在vue.config.js中说明externals

configureWebpack: (config) => {            config.externals = {                vue: 'Vue',                vuex: 'Vuex',                'vue-router': 'VueRouter',                // 'alias-name': 'ObjName'                // 写法: 中划线: 上驼峰            }},

这时再打包就没有这些依赖项了。

2.区分开发环境和正式环境

以上方法配置后,因为引入的是vue.min.js,会导致Vue DevTool无法使用,怎么办呢?

const isProduction = process.env.NODE_ENV === "production";...configureWebpack: (config) => {        if(isProduction){               config.externals = {                vue: 'Vue',                vuex: 'Vuex',                'vue-router': 'VueRouter',                // 'alias-name': 'ObjName'                // 写法: 中划线: 上驼峰            }        }},

正式环境才使用externals可以吗?可以,但是报错:

Uncaught TypeError: Cannot redefine property: $router

因为在index.html中已经引入过一次vue-router了,如果不把它放入externals,就会重复定义。

因此我们需要仅在正式环境中引入cdn,调整之前的代码:

// vue.config.jsconst cdn = {    css: [],    js: [        'https://cdn.bootcss.com/vue/2.6.10/vue.min.js',        'https://cdn.bootcss.com/vue-router/3.1.3/vue-router.min.js',        'https://cdn.bootcss.com/vuex/3.1.1/vuex.min.js',    ],}module.exports={    ...    chainWebpack: (config) => {        if (isProduction) {          // 生产环境注入cdn + 多页面          glob.sync("./src/pages/**/main.js").forEach(path => {            const chunk = path.split("./src/pages/")[1].split("/main.js")[0];            config.plugin("html-" + chunk).tap(args => {              args[0].cdn = cdn;              return args;            });          });        }    },}

这一步在多页面模式下有个坑,官方有提到但是一句带过了——就是会存在多个 html-webpack-plugin实例,而config.plugin()必须接收准确的plugin名称,如果硬着头皮按照网上教程走,肯定会卡在这里。其实很简单,只要vue inspect --plugins就可以看到了

接下来,index.html处理下cdn参数就完成啦😝

<!DOCTYPE html><html lang="en">  <head>    <meta charset="utf-8">    <meta http-equiv="X-UA-Compatible" content="IE=edge">    <meta name="viewport" content="width=device-width,initial-scale=1.0">    <link rel="icon" href="<%= BASE_URL %>favicon.ico">    <title><%= htmlWebpackPlugin.options.title %/></title>    <!-- 使用CDN的CSS文件 -->    <% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.css) { %>    <link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="preload" as="style" />    <link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="stylesheet" />    <% } %>    <!-- 使用CDN的JS文件 -->    <% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %>    <link href="<%= htmlWebpackPlugin.options.cdn.js[i] %>" rel="preload" as="script" />    <% } %>  </% } %></link href="<%= htmlWebpackPlugin.options.cdn.js[i] %></% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %></% } %></link href="<%= htmlWebpackPlugin.options.cdn.css[i] %></link href="<%= htmlWebpackPlugin.options.cdn.css[i] %></% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.css) { %></link rel="icon" href="<%= BASE_URL %></meta name="viewport" content="width=device-width,initial-scale=1.0"></meta http-equiv="X-UA-Compatible" content="IE=edge"></meta charset="utf-8"></head>  <body>    <noscript>      <strong>We're sorry but vue-multiple-pages-demo doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>    </noscript>    <div id="app">    <% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %>    <script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>">    <% } %>    <!-- built files will be auto injected -->  </% } %></script src="<%= htmlWebpackPlugin.options.cdn.js[i] %></% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %></div id="app"></body>

</html lang="en">

利用`dllPlugin`优化

前面提到也可以用dllPlugin来优化,不过如果你使用chromeVue DevToolvue就不能放进dllPlugin了。

1.创建webpack.dll.conf.js

const path = require('path')const webpack = require('webpack')const {CleanWebpackPlugin} = require('clean-webpack-plugin')// dll文件存放的目录const dllPath = 'public/vendor'module.exports = {    entry: {        core: ['vue-router','vuex'],        // other: [],    },    output: {        path: path.join(__dirname, dllPath),        filename: '[name].dll.js',        // vendor.dll.js中暴露出的全局变量名        // 保持与 webpack.DllPlugin 中名称一致        library: '[name]_[hash]',    },    plugins: [        // 清除之前的dll文件        // "clean-webpack-plugin": "^1.0.0"  注意版本不同的写法不同        // new CleanWebpackPlugin(['*.*'], {        //     root: path.join(__dirname, dllPath),        // }),        // "clean-webpack-plugin": "^3.0.0"        new CleanWebpackPlugin(),        // 设置环境变量        new webpack.DefinePlugin({            'process.env': {                NODE_ENV: 'production',            },        }),        // manifest.json 描述动态链接库包含了哪些内容        new webpack.DllPlugin({            path: path.join(__dirname, dllPath, '[name]-manifest.json'),            // 保持与 output.library 中名称一致            name: '[name]_[hash]',            context: process.cwd(),        }),    ],}

2.预编译dll
package.json中添加

script:{    ...    "dll": "webpack -p --progress --config ./webpack.dll.conf.js"}

运行npm run dll就可以在public/vendor下生成dll了。

3.在webpack中声明预编译部分
声明后webpack打包时就会跳过这些dll。

// vue.config.js  configureWebpack: config => {    if (isProduction) {      config.externals = {        vue: "Vue"        // vuex: "Vuex", 这些都改成dllPlugin编译        // "vue-router": "VueRouter"        // 'alias-name': 'ObjName'        // 写法: 中划线: 上驼峰      };    }    config.plugins.push(        // 名称要和之前的一致,可以继续扩展多个      ...["core"].map(name => {        return new webpack.DllReferencePlugin({          context: process.cwd(),          manifest: require(`./public/vendor/${name}-manifest.json`)        });      })    );  },

4.引用dll
最后就是引入到index.html中,你可以简单地直接写入:

<script src=. vendor="" core.dll.js="">

</script src=.>

如果想更智能些,就用用到add-asset-html-webpack-plugin,它能将生成在public/vendor下的dll自动注入到index.html中。

const AddAssetHtmlPlugin = require("add-asset-html-webpack-plugin");    config.plugins.push(    ...      // 将 dll 注入到 生成的 html 模板中      new AddAssetHtmlPlugin({        // dll文件位置        filepath: path.resolve(__dirname, "./public/vendor/*.js"),        // dll 引用路径        publicPath: "./vendor",        // dll最终输出的目录        outputPath: "./vendor"      })    );  },

大功告成!

最后

DEMO

地址:github.com/vita2333/vu…

每一个commit对应一个配置步骤,vue利用external引入,区分了开发和配置环境,其他的vue-router等都是用dllPlugin引入的,有需要的小伙伴可以去看看 :)

参考

github.com/Plortinus/v…
juejin.im/post/5cb041…
blog.csdn.net/neoveee/art…


原文链接:juejin.im

上一篇:@ndhoule/arity
下一篇:emoji

相关推荐

  • 🙋Hanjst汉吉斯特优化+JsonDataFromScript等

    近日继续对 🙋Hanjst汉吉斯特优化改进。这次的改进思考是从服务器端返回的 HanjstJsonData的容器设计问题。目前的做法是服务器端的HanjstJsonData放入终端页面的一个Div元...

    2 个月前
  • 🔥基于vue3.0.1 beta搭建仿京东淘宝的电商商城项目!

    前言 就在前段时间,vue官方发布了3.0.0beta.1 版本,趁着五一假期有时间,就把之前的一个电商商城的项目,用最新的Composition API拿来改造一下! 👉GitHub地址请访问🔗...

    3 个月前
  • 🍊仿Element自定义Vue组件库

    (/public/upload/643b972fb2ebd2e6272ff8b16712b205) 前言 🍊 市面上目前已有各种各样的UI组件库,他们的强大毋庸置疑。

    1 个月前
  • (详解)从浏览器输入 URL 到页面展示过程

    引言 对于面试常问的从浏览器输入 URL 到页面渲染过程发生了什么?,我想大家都或多或少能说出一二。但是,其实这个问题很有深度,而你是否回答的有深度,在很大程度上会影响到面试官对你的印象。

    4 个月前
  • (源码分析)为什么 Vue 中 template 有且只能一个 root ?

    引言 今年,疫情并没有影响到各种面经的正常出现,可谓是络绎不绝(学不动...)。然后,在前段时间也看到一个这样的关于 Vue 的问题,为什么每个组件 template 中有且只能一个 root? 可能...

    4 个月前
  • (小白篇)vue-cli3.0创建项目+引入element-ui

    vuecli在2018年8月份发布了3.0版本,经过重构之后,可以说是一个船心版本! 在项目都落地之后,就想升级一下cli版本,尝一尝3.0带来的舒适,也是为后面项目的开展做一个准备。

    1 年前
  • (小小黑科技)vue+echarts实现半圆图表

    如何用echarts实现半圆图表?在echarts官方实例倒腾一波,发现官方并没有提供半圆图表的写法,那怎么办呢?官方没提供,但需求还是要实现的。 半圆图表其实就是饼图的一半,那么简单的思路如下:设...

    1 年前
  • (原创)vue-router的Import() 异步加载模块问题的解决方案

    关注不迷路,如果解决了问题,留下个赞。 1、问题现象 (/public/upload/e221e3db24c3f24a41062b6e4e389df8) 2、出现问题的代码点 (/publ...

    3 个月前
  • (vue框架)为element组件赋初始值以后无法更改值得问题

    情况描述:组件未加载时已有初始值,mounted里面加载数据,赋值,渲染以后,组件无法更改内容 data里面已经有这个表单对象的初始值但还是无法修改,之前有过一次,没有给表单绑定对象,所以赋值以后无法...

    1 年前
  • (vuejs学习)2、使用ElementUI(*)

    1.element安装 开发环境是win10,一到node官网下载node的.msi包(https://npm.taobao.org/mirrors/node/v10.16.0/nodev10.16....

    1 年前

官方社区

扫码加入 JavaScript 社区