Vue多頁面優化踩坑記錄

最近優化以前的項目,從單頁面拆分紅了多頁面,這裏記錄下整個踩坑過程。javascript

拆分紅多個頁面

1.將原項目的單頁面替換爲多頁面
css


這裏我添加了 index.htmluser.html兩個頁面。

2.配置vue.config.jshtml

// vue.config.js
const 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,
    chainWebpackconfig => config.plugins.delete('named-chunks'),
    devServer: {
        proxy: {
            '/api': {
                target'http://127.0.0.1:8080',
                changeOrigintrue,
                pathRewrite: { '^/api''' }
            }
        }
    }
}
複製代碼

能夠執行vue inspect查看完整配置信息:
vue

路由懶加載

vue-router官方給出的示例以下,這裏webpackChunkName若是不寫打包時會自動生成序號代替。java

//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的大小了。webpack

若是想要多個路由打包進一個js裏,寫同一個webpackChunkName便可git

  {
    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,多了一個頁面。
github

引入代碼分析BundleAnalyzerPlugin

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

//vue.config.js
const 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中添加vue-router

  "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"></div>
    <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
    <script src="https://cdn.bootcss.com/vue-router/3.1.3/vue-router.min.js"></script>
    <script src="https://cdn.bootcss.com/vuex/3.1.1/vuex.min.js"></script>
</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.js
const 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" />
    <% } %>
  </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"></div>

    <% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %>
    <script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
    <% } %>

    <!-- built files will be auto injected -->
  </body>
</html>
複製代碼

利用`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(),
          manifestrequire(`./public/vendor/${name}-manifest.json`)
        });
      })
    );
  },
複製代碼

4.引用dll
最後就是引入到index.html中,你能夠簡單地直接寫入:

    <script src=./vendor/core.dll.js></script>
複製代碼

若是想更智能些,就用用到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…

相關文章
相關標籤/搜索