предисловие
В эпоху http1 обычная оптимизация производительности заключается в объединении количества http-запросов.Обычно мы объединяем много кода js вместе, но если пакет js особенно велик, это немного излишне для повышения производительности. И если мы разумно разделим весь код, разделим код на первом экране и на нераскладывающемся экране, разделим бизнес-код и код базовой библиотеки и загрузим его, когда потребуется определенный фрагмент кода. это снова, вы можете прочитать его из кеша, который может лучше использовать кеш браузера, а также может повысить скорость загрузки первого экрана, что может значительно улучшить взаимодействие с пользователем.
Главная мысль
Разделение бизнес-кода и базовой библиотеки
На самом деле это очень легко понять. Бизнес-код обычно часто обновляется и итерируется, в то время как базовая библиотека обычно обновляется медленно. Если вы разделите ее здесь, вы сможете полностью использовать кеш браузера для загрузки кода базовой библиотеки.
Асинхронная загрузка по запросу
Это в основном решает проблему размера запроса первого экрана, при доступе к первому экрану нам нужно только загрузить логику, требуемую первым экраном, вместо того, чтобы загружать код всех маршрутов.
реальный бой
Недавно я использовал vuetify для преобразования внутренней системы.В начале я использовал наиболее часто используемую конфигурацию веб-пакета, и функция была разработана быстро, но когда я ее упаковал, я обнаружил, что эффект не очень очевиден, и многие большие пакеты были набраны.
Здесь мы смотрим на распределение упаковки. Мы используем WebPack-Bundle-Analyzer здесь. Можно четко видеть, что модули, такие как Vue и Vuetify, были неоднократно упакованы.
Здесь мы сначала вставим конфигурацию, а потом используем ее для анализа:
const path = require('path')
const webpack = require('webpack')
const CleanWebpackPlugin = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const generateHtml = new HtmlWebpackPlugin({
title: '逍遥系统',
template: './src/index.html',
minify: {
removeComments: true
}
})
module.exports = {
entry: {
vendor: ['vue', 'vue-router', 'vuetify'],
app: './src/main.js'
},
output: {
path: path.resolve(__dirname, './dist'),
filename: '[name].[hash].js',
chunkFilename:'[id].[name].[chunkhash].js'
},
resolve: {
extensions: ['.js', '.vue'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'public': path.resolve(__dirname, './public')
}
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
loaders: {
}
// other vue-loader options go here
}
},
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: 'file-loader',
options: {
objectAssign: 'Object.assign'
}
},
{
test: /\.css$/,
loader: ['style-loader', 'css-loader']
},
{
test: /\.styl$/,
loader: ['style-loader', 'css-loader', 'stylus-loader']
}
]
},
devServer: {
historyApiFallback: true,
noInfo: true
},
performance: {
hints: false
},
devtool: '#eval-source-map',
plugins: [
new BundleAnalyzerPlugin(),
new CleanWebpackPlugin(['dist']),
generateHtml,
new webpack.optimize.CommonsChunkPlugin({
name: 'ventor'
}),
]
}
if (process.env.NODE_ENV === 'production') {
module.exports.devtool = '#source-map'
// http://vue-loader.vuejs.org/en/workflow/production.html
module.exports.plugins = (module.exports.plugins || []).concat([
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
}),
new webpack.optimize.UglifyJsPlugin({
sourceMap: true,
compress: {
warnings: false
}
}),
new webpack.LoaderOptionsPlugin({
minimize: true
})
])
}
CommonChunkPlugin
Здесь мы обнаружили, что запись ventor не отфильтровала все модули в указанном node_module, такие как axios, поэтому она была упакована в app.js, здесь мы делаем разделение
entry: {
vendor: ['vue', 'vue-router', 'vuetify', 'axios'],
app: './src/main.js'
},
Тогда здесь есть еще одна проблема. Я не могу вручную ввести модуль вручную. В это время нам может понадобиться автоматически отделить вентор. Здесь нам нужно ввести minChunks. В конфигурации мы можем упаковать все модули, указанные в разделе mode_module . Измените конфигурацию следующим образом
entry: {
//vendor: ['vue', 'vue-router', 'vuetify', 'axios'], //删除
app: './src/main.js'
}
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: ({ resource }) => (
resource &&
resource.indexOf('node_modules') >= 0 &&
resource.match(/\.js$/)
)
}),
После оптимизации вышеперечисленных шагов давайте еще раз посмотрим на раздачу файлов, и мы обнаружим, что модули под node_module все собраны под вендором.
Здесь мы можем получить опыт, то есть в проекте модули под node_module могут быть специально упакованы и оптимизированы. Но если вы будете здесь внимательны, то можете обнаружить, что компонента codemirror отсутствует также и в node_module, но почему он не упакован, а повторяется на других отдельных страницах? На самом деле, это потому, что использование атрибута name в commonChunk фактически означает, что только Следуйте за записью, чтобы найти зависимые пакеты. Поскольку наши компоненты загружаются асинхронно, мы не будем их здесь упаковывать. Давайте проведем эксперимент, чтобы убедиться, что теперь мы удаляем ленивую загрузку маршрутизации dbmanage и системных страниц и меняем на прямой импорт
// const dbmanage = () => import(/* webpackChunkName: "dbmanage" */'../views/dbmanage.vue')
// const system = () => import(/* webpackChunkName: "system" */'../views/system.vue')
import dbmanage from '../views/dbmanage.vue'
import system from '../views/system.vue'
В настоящее время мы можем переупаковать и обнаружить, что codemirror упакован, поэтому вопрос в том, хорошо ли это?
async
Ответ на поставленный вопрос да, нет, очевидно ventor это наш код входа, то есть первый экран.Нам вообще не нужно загружать этот компонент codemirror.Сначала восстанавливаем модификацию роутинга именно сейчас,но потом Новая проблема заключается в том, что наше зеркало кода упаковано в две отдельные страницы одновременно, а некоторые компоненты, которые упакованы сами по себе, такие как MTable или MDataTable, также упакованы повторно. И кодзеркало очень большое, и одновременная загрузка двух отдельных страниц тоже вызовет большие проблемы с производительностью.Проще говоря, после того, как мы загрузим кодовое зеркало на первую единственную страницу, оно не должно загружаться на вторую страницу. Чтобы решить эту проблему, здесь мы можем использовать асинхронность CommonsChunkPlugin и метод count в minChunnks для определения числа, пока количество повторных использований превышает два асинхронно загружаемых модуля, включая два (то есть чанки, сгенерированные import()). мы все считаем, что они могут быть отмечены как общедоступные, здесь мы добавляем конфигурацию.
new webpack.optimize.CommonsChunkPlugin({
async: 'used-twice',
minChunks: (module, count) => (
count >= 2
),
})
После повторной упаковки мы обнаружили, что все используемые компоненты были перенабраны в 0.used-twice-app.js, поэтому размер каждой отдельной страницы также уменьшился, а в среднем стал меньше примерно на 10 тыс.
Однако здесь мы обнаруживаем, что vuetify.js и vuetify.css слишком велики, что приводит к тому, что наш запакованный код очень большой. Здесь мы рассматриваем его извлечение. Чтобы избежать повторной упаковки, нам нужно использовать внешние и комбинировать vue и vuetify Код читается cdn, сначала измените index.html
css引入
<link href='https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons' rel="stylesheet" type="text/css">
<link href="https://unpkg.com/vuetify/dist/vuetify.min.css" rel="stylesheet">
js引入
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vuetify/dist/vuetify.js"></script>
//去掉main.js中之前对vuetifycss的引入
//import 'vuetify/dist/vuetify.css'
Измените конфигурацию веб-пакета и добавьте внешние
externals: {
'vue':'Vue',
"vuetify":"Vuetify"
}
После перепаковки вы можете увидеть, что код, связанный с vue, исчез.В настоящее время относительно большим является только используемый-дважды-app.js, а app.js был уменьшен почти на 200 КБ.
Но снова приходит новая проблема.Кодмирор очень большой,и б/у дважды нужен для первого экрана.Этот пакет точно не очень хорош для первого экрана.Здесь надо поменять кодмирор компоненты системы и dbmanage страницы для асинхронной загрузки и упаковать их отдельно. Измените следующим образом:
// import MCode from "../component/MCode.vue"; //注释掉
components: {
MDialog,
MCode: () => import(/* webpackChunkName: "MCode" */'../component/MCode.vue')
},
После переупаковки вы можете видеть, что кодовое зеркало было удалено, код в верхней части страницы был дополнительно уменьшен, а код used-twice-app.js был уменьшен почти на 150 КБ.
После выполнения стольких оптимизаций, описанных выше, js бизнес-теста в основном был разделен примерно на 50 КБ (игнорируя файл карты), что считается успешной оптимизацией.
Суммировать
Некоторые друзья могут спросить, что разделение только vue и vuetify приведет к увеличению количества запросов.Тут я хотел бы добавить, что наш бизнес сейчас перешел на http2.Из-за мультиплексирования и кеширования браузера мы делим количество запросов подлежащий удалению фактически контролируется в разумных пределах.
Наконец, я опубликую оптимизированную конфигурацию веб-пакета здесь, давайте обмениваться и учиться вместе.
const path = require('path')
const webpack = require('webpack')
const CleanWebpackPlugin = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const generateHtml = new HtmlWebpackPlugin({
title: '逍遥系统',
template: './src/index.html',
minify: {
removeComments: true
}
})
module.exports = {
entry: {
app: './src/main.js'
},
output: {
path: path.resolve(__dirname, './dist'),
filename: '[name].[hash].js',
chunkFilename:'[id].[name].[chunkhash].js'
},
resolve: {
extensions: ['.js', '.vue'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'public': path.resolve(__dirname, './public')
}
},
externals: {
'vue':'Vue',
"vuetify":"Vuetify"
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
loaders: {
}
// other vue-loader options go here
}
},
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: 'file-loader',
options: {
objectAssign: 'Object.assign'
}
},
{
test: /\.css$/,
loader: ['style-loader', 'css-loader']
},
{
test: /\.styl$/,
loader: ['style-loader', 'css-loader', 'stylus-loader']
}
]
},
devServer: {
historyApiFallback: true,
noInfo: true
},
performance: {
hints: false
},
devtool: '#eval-source-map',
plugins: [
new CleanWebpackPlugin(['dist']),
generateHtml
]
}
if (process.env.NODE_ENV === 'production') {
module.exports.devtool = '#source-map'
module.exports.plugins = (module.exports.plugins || []).concat([
new BundleAnalyzerPlugin(),
new webpack.optimize.CommonsChunkPlugin({
name: 'ventor',
minChunks: ({ resource }) => (
resource &&
resource.indexOf('node_modules') >= 0 &&
resource.match(/\.js$/)
)
}),
new webpack.optimize.CommonsChunkPlugin({
async: 'used-twice',
minChunks: (module, count) => (
count >= 2
),
}),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
}),
new webpack.optimize.UglifyJsPlugin({
sourceMap: true,
compress: {
warnings: false
}
}),
new webpack.LoaderOptionsPlugin({
minimize: true
})
])
}
Использованная литература:
- Разделение кода Webpack Dafa: https://zhuanlan.zhihu.com/p/26710831
- Vue+webpack реализует асинхронную загрузку компонентов: http://blog.csdn.net/weixin_36094484/article/details/74555017.
- Анализ ленивой загрузки компонентов VUE2: https://www.cnblogs.com/zhanyishu/p/6587571.html
Ниже представлен официальный аккаунт нашей музыкальной фронтенд-команды QQ. Надеюсь, все его поддержат. Мы постараемся написать хорошие статьи и поделиться ими с вами.