В 2020 код не будет благоухать без вебпака (почти 10000 символов)

JavaScript
В 2020 код не будет благоухать без вебпака (почти 10000 символов)

предисловие

Приближается 2020 год, среди множества требований к набору персонала,webpack,工程化Эти термины встречаются все чаще. В повседневной разработке мы часто используем такие вещи, какvue-cli,create-react-appСтроительные леса для нашего проекта. Но если вы хотите выделиться в коллективе (выделиться из толпы) и получить более выгодное предложение (погасить ипотеку), то вы должны иметь глубокое понимание людей, с которыми мы часто имеем делоwebpack

Эта статья разделена на три части для быстрого освоения webpack, на прочтение этой статьи уходит около 60 минут. Если есть какие-либо недостатки, пожалуйста, поправьте меня

kaiche.gif

Эта статья написана на основе

  • webpack 4.41.2Версия
  • node: 10.15.3Версия

1 Начало работы (используйте эти небольшие примеры, чтобы ознакомиться с конфигурацией веб-пакета)


1.1 Инициализировать проект

Создайте новый каталог и инициализируйте npm

npm init

webpack работает в среде узла, нам нужно установить следующие два пакета npm

npm i -D webpack webpack-cli
  • npm i -D — это сокращение от npm install --save-dev
  • npm i -S — это сокращение от npm install --save

Создать новую папкуsrc, а затем создайте новый файлmain.js, напишите небольшой код для проверки

console.log('call me 老yuan')

Настройте команду package.json

20191211104852887.png
воплощать в жизнь

npm run build

На этом этапе, если папка dist создана и содержит main.js внутри, это означает, что пакет был успешно выполнен.

1.2 Запуск собственной конфигурации

Простой пример выше - это просто собственная конфигурация веб-пакета по умолчанию, здесь мы должны получить более богатую пользовательскую конфигурацию.
создать новыйbuildпапку, создайте новуюwebpack.config.js

// webpack.config.js

const path = require('path');
module.exports = {
    mode:'development', // 开发模式
    entry: path.resolve(__dirname,'../src/main.js'),    // 入口文件
    output: {
        filename: 'output.js',      // 打包后的文件名称
        path: path.resolve(__dirname,'../dist')  // 打包后的目录
    }
}

Изменить нашу команду упаковки

p2.png
воплощать в жизньnpm run buildВы обнаружите, что создаются следующие каталоги (изображения).
вdistв папкеmain.jsэто файл, который нам нужно запустить в браузере
Конечно, это не единственный случай практического применения, давайте проведем вас по быстрому запуску вебпака через практические кейсы.

1.3 Настройка HTML-шаблона

js запакованы, но мы не можемhtmlВручную импортируйте упакованные js в файл

Некоторые друзья здесь могут подумать, что имя нашего упакованного файла js не всегда фиксировано (output.js)? То есть вам не нужно каждый раз менять имя импортируемого файла? Фактически, мы часто настраиваем это в нашей повседневной разработке:

module.exports = {
    // 省略其他配置
    output: {
      filename: '[name].[hash:8].js',      // 打包后的文件名称
      path: path.resolve(__dirname,'../dist')  // 打包后的目录
    }
}

генерируется в это времяdistФайлы каталога следующие

p3.png
Для кэширования вы обнаружите, что имя упакованного js-файла каждый раз разное. Нам нужно импортировать файл js, упакованный webpack, в html, но каждый раз вручную изменять имя файла js очень проблематично, поэтому нам нужен плагин, который поможет нам это сделать.

npm i -D html-webpack-plugin

создать новыйbuildродственная папкаpublic, создайте новый index.html внутри
Конкретные файлы конфигурации следующие

// webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
    mode:'development', // 开发模式
    entry: path.resolve(__dirname,'../src/main.js'),    // 入口文件
    output: {
      filename: '[name].[hash:8].js',      // 打包后的文件名称
      path: path.resolve(__dirname,'../dist')  // 打包后的目录
    },
    plugins:[
      new HtmlWebpackPlugin({
        template:path.resolve(__dirname,'../public/index.html')
      })
    ]
}

Сгенерированный каталог выглядит следующим образом (рисунок)

p4.png
Может быть обнаружено, что файл JS, сгенерированный упаковкой, был автоматически введен в файл HTML

1.3.1 Как разрабатывать файлы с несколькими записями

Создайте несколько экземпляров html-webpack-plugin, чтобы решить эту проблему.

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
    mode:'development', // 开发模式
    entry: {
      main:path.resolve(__dirname,'../src/main.js'),
      header:path.resolve(__dirname,'../src/header.js')
  }, 
    output: {
      filename: '[name].[hash:8].js',      // 打包后的文件名称
      path: path.resolve(__dirname,'../dist')  // 打包后的目录
    },
    plugins:[
      new HtmlWebpackPlugin({
        template:path.resolve(__dirname,'../public/index.html'),
        filename:'index.html',
        chunks:['main'] // 与入口文件对应的模块名
      }),
      new HtmlWebpackPlugin({
        template:path.resolve(__dirname,'../public/header.html'),
        filename:'header.html',
        chunks:['header'] // 与入口文件对应的模块名
      }),
    ]
}

На этом этапе вы обнаружите, что создается следующий каталог

p5.png

1.3.2 clean-webpack-plugin

Каждый раз, когда вы выполняете сборку npm run, вы обнаружите, что последние упакованные файлы остаются в папке dist.Здесь мы рекомендуем плагин, который поможет нам очистить папку перед упаковкой и выводом.clean-webpack-plugin

const {CleanWebpackPlugin} = require('clean-webpack-plugin')
module.exports = {
    // ...省略其他配置
    plugins:[new CleanWebpackPlugin()]
}

1.4 Цитирование CSS

Наш входной файл — js, поэтому мы вводим наш css-файл в запись js.

p6.png
В то же время нам также нужны некоторые загрузчики для разбора наших файлов css.

npm i -D style-loader css-loader

Если мы используем меньше стилей для создания, нам нужно установить еще два

npm i -D less less-loader

Файл конфигурации выглядит следующим образом

// webpack.config.js
module.exports = {
    // ...省略其他配置
    module:{
      rules:[
        {
          test:/\.css$/,
          use:['style-loader','css-loader'] // 从右向左解析原则
        },
        {
          test:/\.less$/,
          use:['style-loader','css-loader','less-loader'] // 从右向左解析原则
        }
      ]
    }
} 

браузер открытhtmlследующим образом

p7.png

1.4.1 Добавить префикс браузера в css

npm i -D postcss-loader autoprefixer  

Конфигурация выглядит следующим образом

// webpack.config.js
module.exports = {
    module:{
        rules:[
            {
                test:/\.less$/,
                use:['style-loader','css-loader','postcss-loader','less-loader'] // 从右向左解析原则
           }
        ]
    }
} 

Далее нам также необходимо представитьautoprefixerЧтобы сделать его эффективным, есть два способа

1. Создайте проект в корневом каталоге проектаpostcss.config.jsфайл, настроенный следующим образом:

module.exports = {
    plugins: [require('autoprefixer')]  // 引用该插件即可了
}

2, прямо вwebpack.config.jsконфигурация

// webpack.config.js
module.exports = {
    //...省略其他配置
    module:{
        rules:[{
            test:/\.less$/,
            use:['style-loader','css-loader',{
                loader:'postcss-loader',
                options:{
                    plugins:[require('autoprefixer')]
                }
            },'less-loader'] // 从右向左解析原则
        }]
    }
}

В это время мы обнаружили, что CSS, добавленные в файл HTML в теге стиля, но если есть много стилей файлов, все добавляют в HTML, неизбежно, что это хаотично. В это время мы хотим использовать разделение CSS для введения файла CSS в форме внешней цепи. В это время нам нужно помочь нам с плагинами.

1.4.2 Разделить CSS

npm i -D mini-css-extract-plugin

До webpack 4.0 мы прошлиextract-text-webpack-pluginПлагин, который извлекает стили css из файлов js в отдельные файлы css. После webpack 4.0 официально рекомендуется использоватьmini-css-extract-pluginПлагин для упаковки css файлов

Файл конфигурации выглядит следующим образом

const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
  //...省略其他配置
  module: {
    rules: [
      {
        test: /\.less$/,
        use: [
           MiniCssExtractPlugin.loader,
          'css-loader',
          'less-loader'
        ],
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
        filename: "[name].[hash].css",
        chunkFilename: "[id].css",
    })
  ]
}

1.4.3 Разделить несколько css

Здесь нужно сказать немного больше, вышеизложенное мы использовалиmini-css-extract-pluginВсе стили css будут объединены в один файл css. Если вы хотите разделить на несколько файлов css, соответствующих один к одному, нам нужно использоватьextract-text-webpack-plugin, в то время как в настоящее времяmini-css-extract-pluginЭта функция пока не поддерживается. Нам нужно установить @next версиюextract-text-webpack-plugin

npm i -D extract-text-webpack-plugin@next
// webpack.config.js

const path = require('path');
const ExtractTextWebpackPlugin = require('extract-text-webpack-plugin')
let indexLess = new ExtractTextWebpackPlugin('index.less');
let indexCss = new ExtractTextWebpackPlugin('index.css');
module.exports = {
    module:{
      rules:[
        {
          test:/\.css$/,
          use: indexCss.extract({
            use: ['css-loader']
          })
        },
        {
          test:/\.less$/,
          use: indexLess.extract({
            use: ['css-loader','less-loader']
          })
        }
      ]
    },
    plugins:[
      indexLess,
      indexCss
    ]
}

1.5 Упаковка картинок, шрифтов, мультимедиа и других файлов

file-loaderТо есть после некоторой обработки файла (в основном обработки имени файла и пути, анализа URL-адреса файла) и перемещения файла в выходной каталог.
url-loaderобычно сfile-loaderИспользуется в комбинации, работает как загрузчик файлов, если размер файла меньше предельного размера. вернет кодировку base64, в противном случае используйте загрузчик файлов для перемещения файла в выходной каталог

// webpack.config.js
module.exports = {
  // 省略其它配置 ...
  module: {
    rules: [
      // ...
      {
        test: /\.(jpe?g|png|gif)$/i, //图片文件
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 10240,
              fallback: {
                loader: 'file-loader',
                options: {
                    name: 'img/[name].[hash:8].[ext]'
                }
              }
            }
          }
        ]
      },
      {
        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, //媒体文件
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 10240,
              fallback: {
                loader: 'file-loader',
                options: {
                  name: 'media/[name].[hash:8].[ext]'
                }
              }
            }
          }
        ]
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i, // 字体
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 10240,
              fallback: {
                loader: 'file-loader',
                options: {
                  name: 'fonts/[name].[hash:8].[ext]'
                }
              }
            }
          }
        ]
      },
    ]
  }
}

1.6 Экранирование js-файлов с помощью babel

Чтобы сделать наш код js совместимым с большим количеством сред, нам нужно установить зависимости

npm i -D babel-loader @babel/preset-env @babel/core
  • Уведомлениеbabel-loaderа такжеbabel-coreверсия соответствует
  1. babel-loader8.x соответствующийbabel-core 7.x
  2. babel-loader7.х перепискаbabel-core 6.x
    Конфигурация выглядит следующим образом
// webpack.config.js
module.exports = {
    // 省略其它配置 ...
    module:{
        rules:[
          {
            test:/\.js$/,
            use:{
              loader:'babel-loader',
              options:{
                presets:['@babel/preset-env']
              }
            },
            exclude:/node_modules/
          },
       ]
    }
}

надbabel-loaderСинтаксис ES6/7/8 будет преобразован только в синтаксис ES5, но новый API не конвертирует, например (Promise, Generator, SET, MAPS, Proxy и т. д.)
На этом этапе нам нужно использовать babel-polyfill, чтобы помочь нам преобразовать

npm i @babel/polyfill
// webpack.config.js
const path = require('path')
module.exports = {
    entry: ["@babel/polyfill",path.resolve(__dirname,'../src/index.js')],    // 入口文件
}
  • Полезнее читать следующие статьи, вручную постукивая по демо выше.Студентам-новичкам рекомендуется постучать три раза и более. [
    学习.png

Вышеупомянутая практика заключается в том, что у нас есть предварительное понимание функции вебпака, но для того, чтобы быть опытным в разработке, нам нужна система реального боя. Давайте избавимся от строительных лесов и попробуем создать среду разработки vue самостоятельно.

2 Создайте среду разработки vue

Приведенный выше небольшой пример помог, и мы реализовали упаковку css, изображений, js, html и других файлов. Но нам также нужна следующая конфигурация

2.1 Разбор файла .vue

npm i -D vue-loader vue-template-compiler vue-style-loader
npm i -S vue

vue-loaderдля разбора.vueдокумент
vue-template-compilerдля составления шаблонов Конфигурация выглядит следующим образом

const vueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = {
    module:{
        rules:[{
            test:/\.vue$/,
            use:['vue-loader']
        },]
     },
    resolve:{
        alias:{
          'vue$':'vue/dist/vue.runtime.esm.js',
          ' @':path.resolve(__dirname,'../src')
        },
        extensions:['*','.js','.json','.vue']
   },
   plugins:[
        new vueLoaderPlugin()
   ]
}

2.2 Настройте webpack-dev-server для горячего обновления

npm i -D webpack-dev-server

Конфигурация выглядит следующим образом

const Webpack = require('webpack')
module.exports = {
  // ...省略其他配置
  devServer:{
    port:3000,
    hot:true,
    contentBase:'../dist'
  },
  plugins:[
    new Webpack.HotModuleReplacementPlugin()
  ]
}

Полная конфигурация выглядит следующим образом

// webpack.config.js
const path = require('path');
const {CleanWebpackPlugin} = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const ExtractTextWebpackPlugin = require('extract-text-webpack-plugin')
const vueLoaderPlugin = require('vue-loader/lib/plugin')
const Webpack = require('webpack')
module.exports = {
    mode:'development', // 开发模式
    entry: {
      main:path.resolve(__dirname,'../src/main.js'),
    }, 
    output: {
      filename: '[name].[hash:8].js',      // 打包后的文件名称
      path: path.resolve(__dirname,'../dist')  // 打包后的目录
    },
    module:{
      rules:[
        {
          test:/\.vue$/,
          use:['vue-loader']
        },
        {
          test:/\.js$/,
          use:{
            loader:'babel-loader',
            options:{
              presets:[
                ['@babel/preset-env']
              ]
            }
          }
        },
        {
          test:/\.css$/,
          use: ['vue-style-loader','css-loader',{
            loader:'postcss-loader',
            options:{
              plugins:[require('autoprefixer')]
            }
          }]
        },
        {
          test:/\.less$/,
          use: ['vue-style-loader','css-loader',{
            loader:'postcss-loader',
            options:{
              plugins:[require('autoprefixer')]
            }
          },'less-loader']
        }
      ]
    },
    resolve:{
      alias:{
        'vue$':'vue/dist/vue.runtime.esm.js',
        ' @':path.resolve(__dirname,'../src')
      },
      extensions:['*','.js','.json','.vue']
    },
    devServer:{
      port:3000,
      hot:true,
      contentBase:'../dist'
    },
    plugins:[
      new CleanWebpackPlugin(),
      new HtmlWebpackPlugin({
        template:path.resolve(__dirname,'../public/index.html'),
        filename:'index.html'
      }),
      new vueLoaderPlugin(),
      new Webpack.HotModuleReplacementPlugin()
    ]
}

2.3 Настройка команды упаковки

c4.png
Файл пакета настроен, давайте проверим его
Сначала создайте новый main.js в src
c1.png
Создайте новый App.vue
c2.png
Создайте новую общую папку и создайте внутри новый index.html.
c3.png
воплощать в жизньnpm run devВ это время, если браузер показывает, что среда разработки Vue работает успешно, поздравляем, вы успешно сделали первый шаг.

2.4 Различие между средой разработки и производственной средой

При практическом применении к проекту нам необходимо отличать среду разработки от производственной среды.Добавляем еще два файла на основе оригинального webpack.config.js

  • webpack.dev.jsФайл конфигурации среды разработки
开发环境主要实现的是热更新,不要压缩代码,完整的sourceMap
  • webpack.prod.jsФайл конфигурации рабочей среды
生产环境主要实现的是压缩代码、提取css文件、合理的sourceMap、分割代码
需要安装以下模块:
npm i -D  webpack-merge copy-webpack-plugin optimize-css-assets-webpack-plugin uglifyjs-webpack-plugin
  • webpack-mergeОбъединить конфигурацию
  • copy-webpack-pluginкопировать статические ресурсы
  • optimize-css-assets-webpack-pluginсжать css
  • uglifyjs-webpack-pluginСжатие JS

webpack modeнастраиватьproductionjs код автоматически сжимается. вводить в принципе не надоuglifyjs-webpack-pluginПовторяющаяся работа. ноoptimize-css-assets-webpack-pluginСжатие css уничтожит оригинальное сжатие js, поэтому здесь мы вводимuglifyjsсжимать

2.4.1 webpack.config.js

const path = require('path')
const {CleanWebpackPlugin} = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const vueLoaderPlugin = require('vue-loader/lib/plugin')
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
const devMode = process.argv.indexOf('--mode=production') === -1;
module.exports = {
  entry:{
    main:path.resolve(__dirname,'../src/main.js')
  },
  output:{
    path:path.resolve(__dirname,'../dist'),
    filename:'js/[name].[hash:8].js',
    chunkFilename:'js/[name].[hash:8].js'
  },
  module:{
    rules:[
      {
        test:/\.js$/,
        use:{
          loader:'babel-loader',
          options:{
            presets:['@babel/preset-env']
          }
        },
        exclude:/node_modules/
      },
      {
        test:/\.vue$/,
        use:[{
          loader:'vue-loader',
          options:{
            compilerOptions:{
              preserveWhitespace:false
            }
          }
        }]
      },
      {
        test:/\.css$/,
        use:[{
          loader: devMode ? 'vue-style-loader' : MiniCssExtractPlugin.loader,
          options:{
            publicPath:"../dist/css/",
            hmr:devMode
          }
        },'css-loader',{
          loader:'postcss-loader',
          options:{
            plugins:[require('autoprefixer')]
          }
        }]
      },
      {
        test:/\.less$/,
        use:[{
          loader:devMode ? 'vue-style-loader' : MiniCssExtractPlugin.loader,
          options:{
            publicPath:"../dist/css/",
            hmr:devMode
          }
        },'css-loader','less-loader',{
          loader:'postcss-loader',
          options:{
            plugins:[require('autoprefixer')]
          }
        }]
      },
      {
        test:/\.(jep?g|png|gif)$/,
        use:{
          loader:'url-loader',
          options:{
            limit:10240,
            fallback:{
              loader:'file-loader',
              options:{
                name:'img/[name].[hash:8].[ext]'
              }
            }
          }
        }
      },
      {
        test:/\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
        use:{
          loader:'url-loader',
          options:{
            limit:10240,
            fallback:{
              loader:'file-loader',
              options:{
                name:'media/[name].[hash:8].[ext]'
              }
            }
          }
        }
      },
      {
        test:/\.(woff2?|eot|ttf|otf)(\?.*)?$/i,
        use:{
          loader:'url-loader',
          options:{
            limit:10240,
            fallback:{
              loader:'file-loader',
              options:{
                name:'media/[name].[hash:8].[ext]'
              }
            }
          }
        }
      }
    ]
  },
  resolve:{
    alias:{
      'vue$':'vue/dist/vue.runtime.esm.js',
      ' @':path.resolve(__dirname,'../src')
    },
    extensions:['*','.js','.json','.vue']
  },
  plugins:[
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      template:path.resolve(__dirname,'../public/index.html')
    }),
    new vueLoaderPlugin(),
    new MiniCssExtractPlugin({
      filename: devMode ? '[name].css' : '[name].[hash].css',
      chunkFilename: devMode ? '[id].css' : '[id].[hash].css'
    })
  ]
}

2.4.2 webpack.dev.js

const Webpack = require('webpack')
const webpackConfig = require('./webpack.config.js')
const WebpackMerge = require('webpack-merge')
module.exports = WebpackMerge(webpackConfig,{
  mode:'development',
  devtool:'cheap-module-eval-source-map',
  devServer:{
    port:3000,
    hot:true,
    contentBase:'../dist'
  },
  plugins:[
    new Webpack.HotModuleReplacementPlugin()
  ]
})

2.4.3 webpack.prod.js

const path = require('path')
const webpackConfig = require('./webpack.config.js')
const WebpackMerge = require('webpack-merge')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
module.exports = WebpackMerge(webpackConfig,{
  mode:'production',
  devtool:'cheap-module-source-map',
  plugins:[
    new CopyWebpackPlugin([{
      from:path.resolve(__dirname,'../public'),
      to:path.resolve(__dirname,'../dist')
    }]),
  ],
  optimization:{
    minimizer:[
      new UglifyJsPlugin({//压缩js
        cache:true,
        parallel:true,
        sourceMap:true
    }),
    new OptimizeCssAssetsPlugin({})
    ],
    splitChunks:{
      chunks:'all',
      cacheGroups:{
        libs: {
          name: "chunk-libs",
          test: /[\\/]node_modules[\\/]/,
          priority: 10,
          chunks: "initial" // 只打包初始时依赖的第三方
        }
      }
    }
  }
})

2.5 Оптимизировать конфигурацию WebPack

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

qiong111.jpg
Оптимизация конфигурации очень важна для нас, что на самом деле связано с размером файлов, которые вы упаковываете, скоростью упаковки и т. д. Конкретную оптимизацию можно разделить на следующие пункты:

2.5.1 Оптимизация скорости упаковки

Скорость сборки означает, насколько быстро мы можем выполнять горячее обновление каждый раз, когда вносим изменения в код, и насколько быстро мы можем упаковывать файлы перед публикацией.

2.5.1.1 Параметры режима рационального конфигурации и параметры Devtool

modeМожет быть установленdevelopment productionдва параметра
Если не установлено,webpack4будуmodeПо умолчанию установлено значениеproduction
productionрежим будетtree shaking(удаление бесполезного кода) иuglifyjs(обфускация сжатия кода)

2.5.1.2 Сужение области поиска файлов (настройка включения псевдонима и исключения расширений noParse)

  • alias: когда появится наш кодimport 'vue', webpack будет использовать восходящий рекурсивный поиск для поискаnode_modulesНайдите его в каталоге. Чтобы уменьшить область поиска, мы можем напрямую указать webpack, какой путь искать. он же псевдоним(alias) Конфигурация.
  • include excludeтакая же конфигурацияinclude excludeтакже может уменьшитьwebpack loaderвремя конверсии поиска.
  • noParse когда наш код используетimport jq from 'jquery'час,webpackОн проанализирует, зависит ли библиотека jq от других пакетов. Но у нас есть похожиеjqueryТакие зависимые библиотеки обычно считаются не ссылающимися на другие пакеты (кроме специальных, судите сами). УвеличиватьnoParseатрибут, расскажитеwebpackНет необходимости парсить, тем самым увеличивая скорость упаковки.
  • extensions webpackбудет основываться наextensionsНайти файлы с заданным суффиксом (сначала записываются типы файлов с более высокой частотой)
    carbon-2.png

2.5.1.3 Используйте HappyPack, чтобы включить преобразование многопроцессорного загрузчика

В процессе создания веб-пакета большая часть времени фактически тратится на синтаксический анализ загрузчика, преобразование и сжатие кода. В ежедневной разработке нам нужно использовать Loader для преобразования js, css, изображений, шрифтов и других файлов, и объем преобразованных файловых данных также очень велик. Из-за однопоточной природы js эти операции преобразования не могут обрабатывать файлы одновременно, а должны обрабатывать файлы один за другим. Основной принцип HappyPack заключается в декомпозиции этой части задачи на несколько подпроцессов для параллельной обработки, после обработки подпроцессов результаты отправляются в основной процесс, тем самым сокращая общее время построения.

npm i -D happypack

carbon-3.png

2.5.1.4 Использование плагина webpack-parallel-uglify для улучшения сжатия кода

Преобразование погрузчика была оптимизирована выше, поэтому существует еще одна сложность оптимизации времени сжатия кода.

npm i -D webpack-parallel-uglify-plugin

carbon-4.png

2.5.1.5 Извлечение сторонних модулей

Для статических файлов зависимостей, которые редко изменяются в проектах разработки. похожий на нашelementUi、vueСемейное ведро и так далее. Поскольку изменения происходят редко, мы не хотим, чтобы эти зависимости были интегрированы в каждую логику сборки. Преимущество этого в том, что каждый раз, когда я изменяю файл своего локального кода,webpackМне нужно только запаковать код файла самого моего проекта без компиляции сторонних библиотек. В будущем, пока мы не будем обновлять сторонние пакеты, тоwebpackЭти библиотеки не будут упакованы, что может быстро повысить скорость упаковки.

Здесь мы используемwebpackВстроенныйDllPlugin DllReferencePluginБыть вытащенным
в сwebpackСоздайте новый файл конфигурации в том же каталогеwebpack.dll.config.jsкод показывает, как показано ниже

// webpack.dll.config.js
const path = require("path");
const webpack = require("webpack");
module.exports = {
  // 你想要打包的模块的数组
  entry: {
    vendor: ['vue','element-ui'] 
  },
  output: {
    path: path.resolve(__dirname, 'static/js'), // 打包后文件输出的位置
    filename: '[name].dll.js',
    library: '[name]_library' 
     // 这里需要和webpack.DllPlugin中的`name: '[name]_library',`保持一致。
  },
  plugins: [
    new webpack.DllPlugin({
      path: path.resolve(__dirname, '[name]-manifest.json'),
      name: '[name]_library', 
      context: __dirname
    })
  ]
};

существуетpackage.jsonНастройте следующую команду в

"dll": "webpack --config build/webpack.dll.config.js"

Далее в нашемwebpack.config.jsДобавьте следующий код

module.exports = {
  plugins: [
    new webpack.DllReferencePlugin({
      context: __dirname,
      manifest: require('./vendor-manifest.json')
    }),
    new CopyWebpackPlugin([ // 拷贝生成的文件到dist目录 这样每次不必手动去cv
      {from: 'static', to:'static'}
    ]),
  ]
};

воплощать в жизнь

npm run dll

Вы обнаружите, что третье место для нужной нам коллекции сгенерировано кодvendor.dll.jsмы должныhtmlВручную импортируйте это в файлjsдокумент

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>老yuan</title>
  <script src="static/js/vendor.dll.js"></script>
</head>
<body>
  <div id="app"></div>
</body>
</html>

Таким образом, если мы не будем обновлять сторонние зависимости, нам не нужноnpm run dll. прямое исполнениеnpm run dev npm run buildВы обнаружите, что наша скорость упаковки значительно улучшилась. потому что мы прошлиdllPluginИзвлеченные сторонние зависимости.

2.5.1.6 Настройка кэша

Каждый раз, когда мы выполняем сборку, мы будем повторно компилировать все файлы. Можно ли кэшировать такую ​​​​повторяющуюся работу? Ответ - да. В настоящее время большинство файловloaderпри условииcacheэлемент конфигурации. например, вbabel-loader, вы можете установитьcacheDirectoryвключить кэширование,babel-loader?cacheDirectory=trueОн будет записывать каждый результат компиляции в файл на жестком диске (по умолчанию в корневом каталоге проекта).node_modules/.cache/babel-loaderВ каталоге конечно тоже можно настроить)

но еслиloaderОн не поддерживает кеширование? У нас также есть методы, мы можем передатьcache-loader, то, что он делает, простоbabel-loaderвключиcache после того, как вы сделаете то, чтоloaderРезультаты компиляции записываются в кэш жесткого диска. Повторное построение сначала сравнит его, и если файл не изменился по сравнению с предыдущим, кеш будет использоваться напрямую. Метод использования показан в официальной демонстрации, просто добавьте этот загрузчик перед другим загрузчиком с высокой производительностью.

npm i -D cache-loader

carbon-5.png

2.5.2 Оптимизация размера файла пакета

Мы оптимизировали скорость упаковки, но объем упакованного файла очень большой, из-за чего страница загружается медленно и расходуется трафик, поэтому продолжим оптимизировать размер файла.

2.5.2.1 Добавление webpack-bundle-analyzer для анализа упакованных файлов

webpack-bundle-analyzerОтобразите упакованный пакет контента в виде интуитивно понятной древовидной диаграммы для удобства взаимодействия, сообщите нам, что на самом деле представлено в пакете, который мы создаем.

npm i -D webpack-bundle-analyzer

carbon-6.png
следующий вpackage.jsonнастроить команду запуска

"analyz": "NODE_ENV=production npm_config_report=true npm run build" 

окна, пожалуйста, установитеnpm i -D cross-env

"analyz": "cross-env NODE_ENV=production npm_config_report=true npm run build" 

следующийnpm run analyzБраузер автоматически откроет веб-страницу графа зависимостей файлов.

2.5.2.3 externals

Согласно официальной документации, если мы хотим сослаться на библиотеку, но не хотимwebpackупакованы и не влияют на наше использование в программеCMD、AMDилиwindow/globalЕсли он используется глобально, его можно использовать, настроивExternals. Эта функция в основном используется при создании библиотеки, но ее можно полноценно использовать и в разработке нашего проекта.ExternalsТаким образом, мы удаляем из логики сборки эти статические ресурсы, которые не нужно упаковывать, и используемCDNспособ цитировать их.

Иногда мы хотим, чтобы мы прошлиscriptПредставленные библиотеки, например импортированные CDN.jquery, когда мы используем его, мы по-прежнему используемrequireспособ использовать, но не хочуwebpackСнова скомпилируйте его в файл. Случай с официальным сайтом здесь достаточно ясен, если вам интересно, вы можете нажать, чтобы понять

webpackОфициальный сайт дела выглядит следующим образом

<script
  src="https://code.jquery.com/jquery-3.1.0.js"
  integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk="
  crossorigin="anonymous">
</script>
module.exports = {
  //...
  externals: {
    jquery: 'jQuery'
  }
};
import $ from 'jquery';
$('.my-element').animate(/* ... */);

2.5.2.3 Tree-shaking

Вот отдельное упоминаниеtree-shaking, потому что здесь есть яма.tree-shakingОсновная роль заключается в очистке бесполезных частей кода. Сейчас я вwebpack4мы устанавливаемmodeдляproductionОн автоматически включается, когдаtree-shaking. Но чтобы это работало, сгенерированный код должен быть модулем ES6. Вы не можете использовать другие типы модулей, такие какCommonJSрод. При использованииBabelто здесь есть небольшая проблема, потому чтоBabelПредустановка по умолчанию преобразует любой тип модуля вCommonJSтипа, в результате чегоtree-shakingневерный. Исправить эту проблему также просто, в.babelrcфайл или вwebpack.config.jsустановить в файлmodules: falseДостаточно

// .babelrc
{
  "presets": [
    ["@babel/preset-env",
      {
        "modules": false
      }
    ]
  ]
}

или

// webpack.config.js

module: {
    rules: [
        {
            test: /\.js$/,
            use: {
                loader: 'babel-loader',
                options: {
                    presets: ['@babel/preset-env', { modules: false }]
                }
            },
            exclude: /(node_modules)/
        }
    ]
}

После крещения двух вышеупомянутых серий мы стали квалифицированным инженером по настройке веб-пакетов. Однако заменяемость самого себя все равно очень высока просто завинчиванием шурупов.Далее углубимся в принцип работы вебпака.

TEST3.png

3 серии рукописных веб-пакетов

Опытные два раздела выше, мы можем уже квалифицированным и плагин-загрузчиком, связанным с использованием нашего конвертации кода, разрешения. Далее мы вручную реализуем свой собственный погрузчик и плагин, делая его веселее в обычном развитии.

3.1 Рукописный ввод Webpack Loader

loaderПо сути, этоnodeмодуль. аналог соковыжималки(loader)Преобразование соответствующего типа кода файла(code)дай это. По установленным нами правилам, он возвращает нам переработанный сок после его серии переработок.(code).

loaderпринципы письма

  • Единый принцип: каждыйLoaderделать только одно;
  • Связанные вызовы:Webpackбудет связывать каждый вызов по порядкуLoader;
  • Объединяющий принцип: СледуйтеWebpackУстановленные правила проектирования и структура, вход и выход - строки, каждыйLoaderПолностью независимый, подключи и играй;

В повседневной среде разработки, чтобы облегчить отладку, мы часто добавляем многоconsoleРаспечатать. Но мы не хотим, чтобы печатное значение существовало в производственной среде. Итак, здесь мы реализуемloaderудалить кодconsole

распространение знанийAST.ASTС точки зрения непрофессионала, предположим, что у нас есть файлa.js,Мыa.js1000 строк внутри выполняют некоторые операции, например, для всехawaitУвеличиватьtry catchи другие операции, ноa.jsКод внутри, по сути, представляет собой набор строк. Итак, что мы делаем, то есть преобразуем его в объект с размеченной информацией (абстрактное синтаксическое дерево), и мы можем легко добавлять, удалять, изменять и проверять. Этот помеченный объект (абстрактное синтаксическое дерево)AST. Здесь рекомендуется хорошая статья ASTБыстрый старт АСТ

npm i -D @babel/parser @babel/traverse @babel/generator @babel/types
  • @babel/parserРазобрать исходный код наAST
  • @babel/traverseправильноASTУзлы рекурсивно просматриваются для создания удобной операции и преобразования.pathобъект
  • @babel/generatorБудуASTГенерация декодированияjsкод
  • @babel/typesБлагодаря этому модулю конкретныеASTУзлы добавляются, удаляются, модифицируются и ищутся

новыйdrop-console.js


const parser = require('@babel/parser')
const traverse = require('@babel/traverse').default
const generator = require('@babel/generator').default
const t = require('@babel/types')
module.exports=function(source){
  const ast = parser.parse(source,{ sourceType: 'module'})
  traverse(ast,{
    CallExpression(path){ 
      if(t.isMemberExpression(path.node.callee) && t.isIdentifier(path.node.callee.object, {name: "console"})){
        path.remove()
      }
    }
  })
  const output = generator(ast, {}, source);
  return output.code
}

как использовать

const path = require('path')
module.exports = {
  mode:'development',
  entry:path.resolve(__dirname,'index.js'),
  output:{
    filename:'[name].[contenthash].js',
    path:path.resolve(__dirname,'dist')
  },
  module:{
    rules:[{
      test:/\.js$/,
      use:path.resolve(__dirname,'drop-console.js')
      }
    ]
  }
}

Фактически,webpack4consoleфункция, вminimizerнастраивается вудалить консоль

Прикрепить официальный сайтКак написать загрузчик

3.2 Рукописный плагин веб-пакета

существуетWebpackМногие события транслируются в течение жизненного цикла бега,PluginВы можете слушать эти события и проходить их в нужное времяWebpackкоторый предоставилAPIИзменить результат вывода. С точки зрения непрофессионала: вкусная тарелкаЯичница-болтунья с солью и фасольюОн должен пройти через процесс обжига масла, жарки, приправы до конечной тарелки и т. д.pluginЭто эквивалентно мониторингу каждой ссылки и работе с ней.Например, вы можете написать сценарий с меньшим количеством перца.plugin, мониторwebpackОткрытое событие жизненного цикла (приправа) выполняет операцию без перца при приправе. тогда это то же самое, чтоloaderВ чем разница? Мы также упоминали вышеloaderединый принципloaderтолько одно вродеless-loader, можно только разобратьlessдокумент,pluginОн заключается в выполнении широкого круга задач за весь процесс.

Базовая структура плагина выглядит следующим образом.

class firstPlugin {
  constructor (options) {
    console.log('firstPlugin options', options)
  }
  apply (compiler) {
    compiler.plugin('done', compilation => {
      console.log('firstPlugin')
    ))
  }
}

module.exports = firstPlugin

Что такое компилятор и компиляция?

  • compilerобъект содержитWebpackВся информация о конфигурации для среды. Этот объект запускаетсяwebpackсоздается один раз и настраивается со всеми рабочими параметрами, включаяoptions,loaderа такжеplugin. КогдаwebpackКогда подключаемый модуль применяется в среде, подключаемый модуль получает этоcompilerссылка на объект. можно использовать для доступаwebpackосновное окружение.
  • compilationОбъект содержит текущие ресурсы модуля, скомпилированные ресурсы, измененные файлы и т. д. при бегеwebpackПри разработке ПО промежуточного слоя всякий раз, когда обнаруживается изменение файла, создается новыйcompilationДля создания нового набора скомпилированных ресурсов.compilationОбъект также предоставляет ряд ключевых коррекций синхронизации, выберите, чтобы использовать при выполнении пользовательских плагинов для обработки.

Разница между компилятором и компиляцией в том, что

  • Компилятор представляет собой весь жизненный цикл веб-пакета от запуска до завершения работы, а компиляция просто представляет собой новый процесс компиляции.

  • Компилятор и компилятор выявляют множество хуков, и мы можем настроить обработку в соответствии с реальными потребностями сцены.

документация по хукам компилятора

документация по хуку компиляции

Теперь нам нужно вручную разработать простую, автоматически сгенерированную информацию о размере пакета документов перед созданием файла пакета.

создать новыйwebpack-firstPlugin.js

class firstPlugin{
  constructor(options){
    this.options = options
  }
  apply(compiler){
    compiler.plugin('emit',(compilation,callback)=>{
      let str = ''
      for (let filename in compilation.assets){
        str += `文件:${filename}  大小${compilation.assets[filename]['size']()}\n`
      }
      // 通过compilation.assets可以获取打包后静态资源信息,同样也可以写入资源
      compilation.assets['fileSize.md'] = {
        source:function(){
          return str
        },
        size:function(){
          return str.length
        }
      }
      callback()
    })
  }
}
module.exports = firstPlugin

как использовать

const path = require('path')
const firstPlugin = require('webpack-firstPlugin.js')
module.exports = {
    // 省略其他代码
    plugins:[
        new firstPlugin()
    ]
}

воплощать в жизньnpm run buildможно увидеть вdistФайл, содержащий информацию о файле пакета, создается в папкеfileSize.md

два вышеloaderа такжеpluginСлучай только руководство, фактические потребности развитияloaderа такжеpluginЕсть много аспектов, которые следует учитывать, и рекомендуется попробовать это самостоятельно.

Прикрепить официальный сайтКак написать плагин

3.3 Рукописный веб-пакет

Потому что пространство слишком длинное, а принцип более глубокий. Ввиду принципа этой статьи быстро приступить к работе и применительно к реальной разработке, я решил начать новую статью для ее подробного анализа.webpackпринцип и реализацияdemoВерсия. После калибровки формата ссылка на статью будет размещена ниже

4 Эпоха веб-пакета 5.0

Скорость обновления как интерфейсных фреймворков, так и инструментов сборки выходит далеко за рамки нашего воображения.jqueryПрошли времена шаттла. То, что мы хотим принять, — это постоянное обновление и итерация.vue、react、node、serverless、docker、k8s....

Не желая отставать, недавно также был выпущен webpack.webpack 5.0.0 beta 10 Версия. Ранее автор упоминалwebpack5.0Предназначен для упрощения настройки и облегчения начала работы (webpack4я также сказал это предложение), и некоторые улучшения производительности

  • Используйте постоянный кеш для повышения производительности сборки;
  • Улучшите долгосрочное кэширование с помощью лучших алгоритмов и значений по умолчанию;
  • Очистить внутреннюю структуру без внесения каких-либо критических изменений;
  • Внесите некоторые критические изменения, чтобы оставаться с версией 5 как можно дольше.

В настоящее время сопровождающие обновляются очень часто, я думаю, это не займет много времени.webpack5.0охватит массы. Заинтересованные студенты могут установить его первымbetaверсию попробовать. Но перед этим рекомендуетсяwebpack4Сделайте мастеринг, чтобы дорога позади становилась все лучше и лучше.