Правильная поза для многостраничной разработки и упаковки vue

внешний интерфейс JavaScript Vue.js Webpack
Правильная поза для многостраничной разработки и упаковки vue

В этой статье около 2000 слов, на прочтение этой статьи уйдет около 20 минут, а на ее опробование — час.

Некоторое время назад я сделал проект, стек технологий vue + webpack, в основном домашняя страница официального сайта плюс система управления фоном. По ситуации на тот момент были проанализированы три варианта

  1. Два спа-приложения (официальный сайт и фоновая система) встроены в код проекта
  2. Разделите два набора исходного кода проекта
  3. Набор исходных кодов проекта содержит spa-приложение

считать:

  1. Напрямую отрицает спа-приложение в наборе исходного кода проекта (стили пользовательского интерфейса будут перезаписывать друг друга, если нет спецификации кода, его будет сложно поддерживать позже)
  2. При наличии двух наборов исходного кода в фоне может быть открыто два порта, и тогда использование nginx в качестве обратного прокси может оказаться проблематичным, а фронтенд-разработка еще более проблематичной, ведь два git-репозитория и необходимо поддерживать два набора онлайн-процессов git, что может занять много времени.
  3. Я (слепо) уверен в собственной технологии и хочу ее попробовать, проанализировать потребности не очень сложно. Выбран первый вариант, то есть несколько отдельных страниц применяются в наборе исходного кода.

Предыдущая схема многостраничной структуры

скачать шаблон vue spa

npm install vue-cli -g
vue init webpack multiple-vue-amazing

Модернизация многостраничных приложений

npm install glob --save-dev

Измените структуру каталогов в папке src

Добавьте в util.js


/* 这里是添加的部分 ---------------------------- 开始 */

// glob是webpack安装时依赖的一个第三方模块,还模块允许你使用 *等符号, 例如lib/*.js就是获取lib文件夹下的所有js后缀名的文件
var glob = require('glob')
// 页面模板
var HtmlWebpackPlugin = require('html-webpack-plugin')
// 取得相应的页面路径,因为之前的配置,所以是src文件夹下的pages文件夹
var PAGE_PATH = path.resolve(__dirname, '../src/pages')
// 用于做相应的merge处理
var merge = require('webpack-merge')


//多入口配置
// 通过glob模块读取pages文件夹下的所有对应文件夹下的js后缀文件,如果该文件存在
// 那么就作为入口处理
exports.entries = function () {
    var entryFiles = glob.sync(PAGE_PATH + '/*/*.js')
    var map = {}
    entryFiles.forEach((filePath) => {
        var filename = filePath.substring(filePath.lastIndexOf('\/') + 1, filePath.lastIndexOf('.'))
        map[filename] = filePath
    })
    return map
}

//多页面输出配置
// 与上面的多页面入口配置相同,读取pages文件夹下的对应的html后缀文件,然后放入数组中
exports.htmlPlugin = function () {
    let entryHtml = glob.sync(PAGE_PATH + '/*/*.html')
    let arr = []
    entryHtml.forEach((filePath) => {
        let filename = filePath.substring(filePath.lastIndexOf('\/') + 1, filePath.lastIndexOf('.'))
        let conf = {
            // 模板来源
            template: filePath,
            // 文件名称
            filename: filename + '.html',
            // 页面模板需要加对应的js脚本,如果不加这行则每个页面都会引入所有的js脚本
            chunks: ['manifest', 'vendor', filename],
            inject: true
        }
        if (process.env.NODE_ENV === 'production') {
            conf = merge(conf, {
                minify: {
                    removeComments: true,
                    collapseWhitespace: true,
                    removeAttributeQuotes: true
                },
                chunksSortMode: 'dependency'
            })
        }
        arr.push(new HtmlWebpackPlugin(conf))
    })
    return arr
}
/* 这里是添加的部分 ---------------------------- 结束 */

файл webpack.base.conf.js

  /* 修改部分 ---------------- 开始 */
  entry: utils.entries(),
  /* 修改部分 ---------------- 结束 */

файл webpack.dev.conf.js

 /* 注释这个区域的文件 ------------- 开始 */
    // new HtmlWebpackPlugin({
    //   filename: 'index.html',
    //   template: 'index.html',
    //   inject: true
    // }),
    /* 注释这个区域的文件 ------------- 结束 */
    new FriendlyErrorsPlugin()

    /* 添加 .concat(utils.htmlPlugin()) ------------------ */
  ].concat(utils.htmlPlugin())

файл webpack.prod.conf.js

 /* 注释这个区域的内容 ---------------------- 开始 */
    // new HtmlWebpackPlugin({
    //   filename: config.build.index,
    //   template: 'index.html',
    //   inject: true,
    //   minify: {
    //     removeComments: true,
    //     collapseWhitespace: true,
    //     removeAttributeQuotes: true
    //     // more options:
    //     // https://github.com/kangax/html-minifier#options-quick-reference
    //   },
    //   // necessary to consistently work with multiple chunks via CommonsChunkPlugin
    //   chunksSortMode: 'dependency'
    // }),
    /* 注释这个区域的内容 ---------------------- 结束 */
    // copy custom static assets
    new CopyWebpackPlugin([
      {
        from: path.resolve(__dirname, '../static'),
        to: config.build.assetsSubDirectory,
        ignore: ['.*']
      }
    ])
    /* 该位置添加 .concat(utils.htmlPlugin()) ------------------- */
  ].concat(utils.htmlPlugin())

Внедрить стороннюю библиотеку пользовательского интерфейса

npm install element-ui bootstrap-vue --save

Внедрите разные интерфейсы на разных страницах index.js

import BootstrapVue from 'bootstrap-vue'
Vue.use(BootstrapVue)

admin.js

import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI)

Конфигурация вышеупомянутых нескольких страниц основана на Интернете, и идеи в Интернете в основном похожи.Ядро состоит в том, чтобы изменить несколько записей.После завершения конфигурации во время разработки не может быть обнаружено никаких проблем, и тогда это будет разрабатывался около месяца.После разработки При анализе производительности официального веб-сайта было обнаружено, что время загрузки в сети vendor.js, упакованного webpack, было очень долгим, что привело к очень долгому времени белого экрана на первом экране. Наконец, вывод был получен с помощью анализа -webpack-bundle-analyzer.

npm run build --report

Вы обнаружите, что vendor.js содержит общие части index.html и admin.html, поэтому этот пакет вендора обречен быть очень большим и избыточным.

Решения

Поскольку поставщик слишком велик, чтобы замедлить скорость загрузки, рекомендуется отделить поставщика. Я думал так: извлеките сторонний код, используемый на каждой странице, в vendor.js, а затем упакуйте сторонний код, используемый на каждой странице, в свой собственный vendor-x.js, например существующий индекс страницы .html, admin.html, vendor.js, vendor-index.js, vendor-admin.js в конечном итоге будут упакованы.

файл webpack.prod.conf.js

    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor-admin',
      chunks: ['vendor'],
      minChunks: function (module, count) {
        return (
          module.resource &&
          /\.js$/.test(module.resource) &&
          module.resource.indexOf(path.join(__dirname, '../node_modules')) === 0 &&
          module.resource.indexOf('element-ui') != -1
        )
      }
    }),
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor-index',
      chunks: ['vendor'],
      minChunks: function (module, count) {
        return (
          module.resource &&
          /\.js$/.test(module.resource) &&
          module.resource.indexOf(path.join(__dirname, '../node_modules')) === 0 &&
          module.resource.indexOf('bootstrap-vue') != -1
        )
      }
    }),

Еще раз анализ, все ок, vendor.js разделен на vendor.js,vendor-index,vendor-admin.js

Я думал, что проблема с отделением vendor.js от CommonsChunkPlugin решена, а потом распаковал и обнаружил, что импортированы и index.html, и admin.html (соответствующий vendor-xx.js)

решение

Эта проблема на самом деле является проблемой HtmlWebpackPlugin. Измените исходный chunksSortMode: 'dependency' на конфигурацию пользовательской функции следующим образом.

файл util.js

chunksSortMode: function (chunk1, chunk2) {
          var order1 = chunks.indexOf(chunk1.names[0])
          var order2 = chunks.indexOf(chunk2.names[0])
          return order1 - order2
        },

Извлеките модуль common-api, общий для нескольких страниц.(2018/4/23)Обновлено

На самом деле, в сценарии извлечения общих файлов между несколькими страницами средние и крупные проекты будут использовать больше.Сначала я увидел комментарий ниже, что когда нужно извлечь common-api, больше common-api.css будет упаковано, и некоторые студенты Личное сообщение Я столкнулся с некоторыми проблемами с commonChunk, я сделаю обновление и предложу идеи

Требования: Некоторые общие js и даже css в проекте могут быть повторно использованы на каждой странице, например, admin.js относится к common-api.js, а index.js также относится к common-api.js. Теперь извлеките модуль common-api, общий для нескольких страниц.

добавить общий каталог

Создайте новый файл common/index.js и напрямую ссылайтесь на локальный файл js (здесь я использую jquery вместо общедоступного js)

Ссылаясь на общедоступные документы

Упоминается в admin.js index.js

import $ from '../../common'
console.log($('body'))

Пакетирование, очевидно, может обнаружить, что jquery упакован дважды, и ресурсы тратятся впустую.

решение

  • Поскольку он многостраничный, необходимо добавить общедоступную запись common-api.
  • commonChunkPlugin Извлечь common-api (есть ямки, о которых речь пойдет ниже)
  • Изменить порядок фрагментов htmlWebpackPlugin

Яма в том, что чанки должны быть указаны, иначе будет сообщено об ошибке webpack ERROR в CommonsChunkPlugin: при работе в обычном режиме не разрешено использовать чанк без входа

В начале одноклассник спросил меня, как решить эту ошибку, я сначала не знал, но проверив некоторую информацию, обнаружил, что эту ошибку можно решить, указав чанки.Разместите раздел проблем с github, если вам интересно, вы можете узнать о нем больше.

Повторно укажите порядок htmlPlugin в util.js

 let chunks = filename === 'admin' ?
      ['manifest', 'vendor', 'vendor-admin', 'common-api', filename] :
      ['manifest', 'vendor', 'vendor-index', 'common-api', filename]

Наконец, глядя на результаты, common-api извлекается, jq загружается только один раз, а common-api.css больше не запаковывается, порядок скриптов в html тоже правильный

наконец понял

  • Каждая страница загружает свой фрагмент
  • Каждая страница имеет разные параметры
  • Каждая страница может иметь общий фрагмент
  • Кэширование браузера для повышения производительности
  • Если он все еще слишком медленный, включите gzip.
  • Каждая страница извлекает общедоступную функцию common-api (Обновлено 23 апреля 2018 г.)

впечатление

Вы закончили, хотя конфигурация выглядит очень простой, но я долго думал об этом, когда занимался разработкой, поэтому, если вы не знакомы с CommonsChunkPlugin и HtmlWebpackPlugin или используете только другие сторонние таблицы конфигурации, предполагается, что вы наступите на большую яму, например, CommonsChunkPlugin Если чанки не указаны, что по умолчанию? Большинство людей в minChunks могут только записывать значение, но способ написания пользовательской функции на самом деле самый мощный.Согласно моему личному опыту, фрагменты в сочетании со способом написания пользовательской функции в minChunks могут решить почти все сверхъестественные события Плагин CommonsChunk.

webpack4

Хоть эта статья и основана на webpack3, но идея многостраничной конфигурации и оптимизированной упаковки webpack4 на самом деле такая же.Используйте ее с уверенностью и решите, если есть яма.

исходный код

Исходный код этой статьинравится нравится

Ссылаться на

Многостраничный справочник по конфигурации

Проблема с заказом HtmlWebpackPlugin