следующий24 примера, чтобы начать работу и освоить "Webpack4" (2)Следовать за:
- Конфигурация ПВА
- Конфигурация TypeScript
- Конфигурация Эслинта
- Используйте DLLPlugin для ускорения упаковки
- Конфигурация многостраничной упаковки
- загрузчик записи
- Написать плагин
- Написать пакет
Семнадцать, конфигурация PWA
В этом разделе используетсяdemo15на основе кода
Давайте смоделируем операцию предотвращения отправки упакованного кода на сервер в обычной разработке, сначала упакуем кодnpm run build
затем установите плагинnpm i http-server -D
Настройте команду скрипта в package.json
{
"scripts": {
"start": "http-server dist",
"dev": "webpack-dev-server --open --config ./build/webpack.dev.conf.js",
"build": "webpack --config ./build/webpack.prod.conf.js"
}
}
бегатьnpm run start
Теперь служба запущена, порт 8080, теперь доступhttp://127.0.0.1:8080вы можете увидеть эффект
Если вы запускаете другие проекты и порт также 8080, порт будет конфликтовать.Не забудьте сначала закрыть порт 8080 других проектов, а затем
npm run start
Нажимаем ctrl+c, чтобы закрыть http-сервер для имитацииСервер зависаетСцена, Revisit.http://127.0.0.1:8080это будет так
Страница недоступна, потому что наш сервер не работает. Что такое технология PWA? Она может кэшировать данные при успешном первом посещении. После того, как сервер не работает, вы по-прежнему можете получить доступ к этой странице.
Сначала установите плагин:workbox-webpack-plugin
npm i workbox-webpack-plugin -D
PWA должен обрабатывать только код для подключения к Интернету, открытьwebpack.prod.conf.js
const WorkboxPlugin = require('workbox-webpack-plugin') // 引入 PWA 插件
const prodConfig = {
plugins: [
// 配置 PWA
new WorkboxPlugin.GenerateSW({
clientsClaim: true,
skipWaiting: true
})
]
}
Перепакуйте, в каталоге dist будет большеservice-worker.js
а такжеprecache-manifest.js
Два файла, с помощью которых наша веб-страница может поддерживать технологию PWA,service-worker.jsМожно понимать как альтернативный кеш
Его также необходимо использовать в бизнес-коде.service-worker
Добавьте следующий код в app.js
// 判断该浏览器支不支持 serviceWorker
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker
.register('/service-worker.js')
.then(registration => {
console.log('service-worker registed')
})
.catch(error => {
console.log('service-worker registed error')
})
})
}
перепаковать, затем запуститьnpm run start
Для имитации работы на сервере лучше всего открывать его в режиме инкогнитоhttp://127.0.0.1:8080, откройте консоль
Теперь, когда файл кэширован, нажмите ctrl + c, чтобы закрыть службу, и снова обновите страницу.
Конфигурация TypeScript
TypeScriptэто расширенный набор типов JavaScript, который компилируется в чистый JavaScript
новая папка,npm init -y
,npm i webpack webpack-cli -D
, создайте новый каталог src, создайтеindex.tsфайл, этот код не может работать в браузере, нам нужно его упаковать, скомпилировать и преобразовать в js
class Greeter {
greeting: string
constructor(message: string) {
this.greeting = message
}
greet() {
return 'Hello, ' + this.greeting
}
}
let greeter = new Greeter('world')
alert(greeter.greet())
npm i ts-loader typescript -D
Создайте новый webpack.config.js и настройте
const path = require('path')
module.exports = {
mode: 'production',
entry: './src/index.ts',
module: {
rules: [
{
test: /\.ts?$/,
use: 'ts-loader',
exclude: /node_modules/
}
]
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
}
Настройте скрипт в package.json
{
"scripts": {
"build": "webpack"
}
}
бегатьnpm ruh build
, сообщил об ошибке, отсутствуетtsconfig.jsonдокумент
При упаковке машинописных файлов необходимо создать файл tsconfig.json в корневом каталоге проекта.
Ниже приведена простая конфигурация, см. подробнееОфициальный сайт
{
"compileerOptions": {
"outDir": "./dist", // 写不写都行
"module": "es6", // 用 es6 模块引入 import
"target": "es5", // 打包成 es5
"allowJs": true // 允许在 ts 中也能引入 js 的文件
}
}
Снова упакуйте, откройте файл bundle.js,Скопируйте весь код в консоль браузера, используя этот код, вы можете увидеть, что Hello, world появляется во всплывающем окне, указывая на то, что ts был успешно скомпилирован и упакован
Импорт сторонних библиотек
npm i lodash
import _ from 'lodash'
class Greeter {
greeting: string
constructor(message: string) {
this.greeting = message
}
greet() {
return _.join()
}
}
let greeter = new Greeter('world')
alert(greeter.greet())
Метод соединения lodash требует от нас передачи параметров, но теперь мы ничего не передаем и об ошибке не сообщается.Мы используем typescript для проверки типов, что также можно сделать при внедрении сторонних библиотек, но теперь ошибки нет или подскажите.
Нам также необходимо установить плагин машинописного текста для lodash, чтобы параметры в методе lodash можно было распознать, и при неправильном использовании выдавалось сообщение об ошибке.
npm i @types/lodash -D
После установки вы можете найти ошибку подчеркивания _
необходимо изменить наimport * as _ from 'lodash'
, удалите параметры, переданные методом соединения, и вы также можете найти отчет об ошибках метода соединения, который отражает преимущества машинописного текста.Точно так же, когда вводится jQuery, следует также ввести подключаемый модуль типа, соответствующий jQuery.
Как узнать, что используемая библиотека требует установки плагина соответствующего типа?
ОткрытьTypeSearch, здесь соответственно ищите библиотеку, которую хотите использовать, если есть какой-то плагин типа, если да, то нужно толькоnpm i @types/jquery -D
Только что
Девятнадцать, конфигурация Эслинта
Создайте пустую папку,npm init -y
,npm webpack webpack-cli -D
Запустите, затем установите зависимости eslint
npm i eslint -D
Используйте npx для запуска eslint в этом проекте для инициализации конфигурации,npx eslint --init
Будет выбор React/Vue/JavaScript, мы все сначала выбираем JavaScript. После выбора будет создан новый в корневом каталоге проекта.eslintrc.js
конфигурационный файл
module.exports = {
env: {
browser: true,
es6: true
},
extends: 'eslint:recommended',
globals: {
Atomics: 'readonly',
SharedArrayBuffer: 'readonly'
},
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module'
},
rules: {}
}
Существуют некоторые спецификации eslint, а также могут быть определены некоторые правила.правила настройки eslint
Просто напишите код в index.js для тестирования eslint.
eslint сообщает об ошибке, но переменная не используется после определения переменной.Если в редакторе нет сообщения об ошибке, вам необходимо сначала установить расширение eslint в vscode, оно будет основано на вашем текущем каталоге..eslintrc.js
файл как правило проверки
Вы также можете использовать командную строку, чтобы позволить eslint проверять файлы во всем каталоге src.
Если вы считаете правило очень проблематичным и хотите его заблокировать, вы можете сделать это в соответствии с сообщением об ошибке eslint, таким как приведенное выше.no-unused-vars
, скопируйте это правило в.eslintrc.js
Настройте его в правилах в"no-unused-vars": 0
, 0 означает отключено, после сохранения сообщение об ошибке не будет, но этот метод подходит дляглобальная конфигурация, если вы хотите заблокировать проверку eslint только для определенной строки кода, вы можете сделать это
/* eslint-disable no-unused-vars */
let a = '1'
Это расширение eslint vscode не имеет ничего общего с webpack.То, о чем мы сейчас поговорим, это как использовать eslint в webpack.Сначала установите плагин
npm i eslint-loader -D
Настроить в webpack.config.js
/* eslint-disable no-undef */
// eslint-disable-next-line no-undef
const path = require('path')
module.exports = {
mode: 'production',
entry: {
app: './src/index.js' // 需要打包的文件入口
},
module: {
rules: [
{
test: /\.js$/, // 使用正则来匹配 js 文件
exclude: /nodes_modules/, // 排除依赖包文件夹
use: {
loader: 'eslint-loader' // 使用 eslint-loader
}
}
]
},
output: {
// eslint-disable-next-line no-undef
publicPath: __dirname + '/dist/', // js 引用的路径或者 CDN 地址
// eslint-disable-next-line no-undef
path: path.resolve(__dirname, 'dist'), // 打包文件的输出目录
filename: 'bundle.js' // 打包后生产的 js 文件
}
}
Так как конфигурационный файл webpack тоже будет проверяться eslint, то здесь я напишу комментарий и отключу проверку
Если вы используете babel-loader для транспиляции, загрузчик должен быть написан так:
loader: ['babel-loader', 'eslint-loader']
Порядок выполнения правил — справа налево и снизу вверх, сначала проверяется eslint на соответствие кода спецификации, а затем передается через babel
После настройки webpack.config.js восстанавливаем index.js до состояния предыдущего отчета об ошибке, не используем комментарии для отключения проверки, а затем запускаем команду упаковки, не забываем зайти в package.json для настройки скрипта
При упаковке будет указано, что код является неквалифицированным, можно настроить не только производственную среду, но и среду разработки, вы можете настроить eslint-loader в общедоступном модуле веб-пакета, что более удобно для нас для проверки кода Технические характеристики
Например: установите для исправления значение true, оно автоматически исправит некоторые ошибки для вас, если вы не можете исправить это автоматически, вам все равно нужно исправить это вручную.
{
loader: 'eslint-loader', // 使用 eslint-loader
options: {
fix: true
}
}
Что касается eslint-loader, официальный сайт webpack также даетнастроить, друзья, кому интересно, идем смотреть сами
20. Используйте DLLPlugin для ускорения упаковки
В этом разделе используетсяdemo15на основе кода
Давайте сначала установим плагин lodashnpm i lodash
, и напишите в файле app.js
import _ from 'lodash'
console.log(_.join(['hello', 'world'], '-'))
Создайте новый файл webpack.dll.js в папке сборки.
const path = require('path')
module.exports = {
mode: 'production',
entry: {
vendors: ['lodash', 'jquery']
},
output: {
filename: '[name].dll.js',
path: path.resolve(__dirname, '../dll'),
library: '[name]'
}
}
использовать здесьlibrary, Для тех, кто забыл, вы можете просмотреть содержимое библиотеки пользовательских функций в разделе 16. Определение библиотеки эквивалентно монтированию этой глобальной переменной.Пока вы вводите имя глобальной переменной в консоли, вы можете отображать содержание внутри, например, здесь мыlibrary: '[name]'
Соответствующее имя — это то, что мы определили в записиvendors
Добавьте еще одну команду в скрипт в package.json
{
"scripts": {
"dev": "webpack-dev-server --open --config ./build/webpack.dev.conf.js",
"build": "webpack --config ./build/webpack.prod.conf.js",
"build:dll": "webpack --config ./build/webpack.dll.js"
}
}
бегатьnpm run build:dll
, создается папка dll и файлvendors.dll.js
Откройте файл, и вы обнаружите, что lodash был упакован в файл dll.
Так как же нам использовать этот файлvendors.dll.js?
Необходимо установить еще одну зависимостьnpm i add-asset-html-webpack-plugin
, он вставит наш упакованный файл dll.js в наш сгенерированный index.html.
Представлено в файле webpack.base.conf.js.
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin')
module.exports = {
plugins: [
new AddAssetHtmlWebpackPlugin({
filepath: path.resolve(__dirname, '../dll/vendors.dll.js') // 对应的 dll 文件路径
})
]
}
использоватьnpm run dev
чтобы открыть веб-страницу
Теперь мы упаковали сторонний модуль в dll-файл отдельно и используем
Но теперь при использовании сторонних модулей используйтеdllфайл вместо использования/node_modules/Библиотека в , продолжаю модифицироватьwebpack.dll.jsнастроить
const path = require('path')
const webpack = require('webpack')
module.exports = {
mode: 'production',
entry: {
vendors: ['lodash', 'jquery']
},
output: {
filename: '[name].dll.js',
path: path.resolve(__dirname, '../dll'),
library: '[name]'
},
plugins: [
new webpack.DllPlugin({
name: '[name]',
// 用这个插件来分析打包后的这个库,把库里的第三方映射关系放在了这个 json 的文件下,这个文件在 dll 目录下
path: path.resolve(__dirname, '../dll/[name].manifest.json')
})
]
}
Переупаковать dll после сохранения,npm run build:dll
Измените файл webpack.base.conf.js и добавьтеwebpack.DllReferencePluginплагин
module.exports = {
plugins: [
// 引入我们打包后的映射文件
new webpack.DllReferencePlugin({
manifest: path.resolve(__dirname, '../dll/vendors.manifest.json')
})
]
}
Затем, когда веб-пакет упаковывается, вы можете комбинировать предыдущие глобальные переменныеvendorsи это вновь созданноеvendors.manifest.jsonОтображение файлов, а затем анализ нашего исходного кода, как только анализ использования сторонних библиотек находится вvendors.dll.js, буду использоватьvendors.dll.js, не буду использовать/node_modules/сторонняя библиотека
упаковать сноваnpm run build
, вы можете поставитьwebpack.DllReferencePluginПосле того, как модуль аннотирован, он упаковывается и сравнивается
Около 4000 мс до комментария и около 4300 мс после комментария, хотя это всего на 300 мс быстрее, но сейчас мы только экспериментальная демонстрация.В реальных проектах, таких как vue, vue, vue-router, vuex, element-ui, axios, и т. д. Все сторонние библиотеки могут быть упакованы в dll.js, и скорость упаковки может быть значительно улучшена в то время.
Вы также можете продолжить разбивать и изменять файл webpack.dll.js.
const path = require('path')
const webpack = require('webpack')
module.exports = {
mode: 'production',
entry: {
lodash: ['lodash'],
jquery: ['jquery']
},
output: {
filename: '[name].dll.js',
path: path.resolve(__dirname, '../dll'),
library: '[name]'
},
plugins: [
new webpack.DllPlugin({
name: '[name]',
path: path.resolve(__dirname, '../dll/[name].manifest.json') // 用这个插件来分析打包后的这个库,把库里的第三方映射关系放在了这个 json 的文件下,这个文件在 dll 目录下
})
]
}
бегатьnpm run build:dll
можно упаковать передvendors.dll.jsа такжеvendors.manifest.jsonФайл сопоставления удален
Затем измените webpack.base.conf.js
module.exports = {
plugins: [
new AddAssetHtmlWebpackPlugin({
filepath: path.resolve(__dirname, '../dll/lodash.dll.js')
}),
new AddAssetHtmlWebpackPlugin({
filepath: path.resolve(__dirname, '../dll/jquery.dll.js')
}),
new webpack.DllReferencePlugin({
manifest: path.resolve(__dirname, '../dll/lodash.manifest.json')
}),
new webpack.DllReferencePlugin({
manifest: path.resolve(__dirname, '../dll/jquery.manifest.json')
})
]
}
запустить после сохраненияnpm run dev
, и посмотрите, успешно ли он работает
Это просто разделение двух сторонних модулей, и нам нужно настроить их один за другим. Есть ли способ сделать это проще? Да!
Здесь модули API и fs узла используются для чтения содержимого папки, а массив плагинов создается для хранения общедоступных плагинов.
const fs = require('fs')
const plugins = [
// 开发环境和生产环境二者均需要的插件
new HtmlWebpackPlugin({
title: 'webpack4 实战',
filename: 'index.html',
template: path.resolve(__dirname, '..', 'index.html'),
minify: {
collapseWhitespace: true
}
}),
new webpack.ProvidePlugin({ $: 'jquery' })
]
const files = fs.readdirSync(path.resolve(__dirname, '../dll'))
console.log(files)
После написания можно сначала вывести, закомментировать плагины,npm run build
Упакуйте и посмотрите на выходное содержимое, вы можете видеть, что содержимое в папке распечатывается в виде массива, а затем мы можем выполнять некоторые циклические операции с этим массивом.
Полный код:
const path = require('path')
const fs = require('fs')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin')
// 存放公共插件
const plugins = [
// 开发环境和生产环境二者均需要的插件
new HtmlWebpackPlugin({
title: 'webpack4 实战',
filename: 'index.html',
template: path.resolve(__dirname, '..', 'index.html'),
minify: {
collapseWhitespace: true
}
}),
new webpack.ProvidePlugin({ $: 'jquery' })
]
// 自动引入 dll 中的文件
const files = fs.readdirSync(path.resolve(__dirname, '../dll'))
files.forEach(file => {
if (/.*\.dll.js/.test(file)) {
plugins.push(
new AddAssetHtmlWebpackPlugin({
filepath: path.resolve(__dirname, '../dll', file)
})
)
}
if (/.*\.manifest.json/.test(file)) {
plugins.push(
new webpack.DllReferencePlugin({
manifest: path.resolve(__dirname, '../dll', file)
})
)
}
})
module.exports = {
entry: {
app: './src/app.js'
},
output: {
path: path.resolve(__dirname, '..', 'dist')
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader'
}
]
},
{
test: /\.(png|jpg|jpeg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
name: '[name]-[hash:5].min.[ext]',
limit: 1000, // size <= 1KB
outputPath: 'images/'
}
},
// img-loader for zip img
{
loader: 'image-webpack-loader',
options: {
// 压缩 jpg/jpeg 图片
mozjpeg: {
progressive: true,
quality: 65 // 压缩率
},
// 压缩 png 图片
pngquant: {
quality: '65-90',
speed: 4
}
}
}
]
},
{
test: /\.(eot|ttf|svg)$/,
use: {
loader: 'url-loader',
options: {
name: '[name]-[hash:5].min.[ext]',
limit: 5000, // fonts file size <= 5KB, use 'base64'; else, output svg file
publicPath: 'fonts/',
outputPath: 'fonts/'
}
}
}
]
},
plugins,
performance: false
}
использоватьnpm run dev
Нет проблем с открытием веб-страницы, поэтому также выполняется автоматическая инъекция файла dll, а затем необходимо запаковать стороннюю библиотеку, пока она добавляется вwebpack.dll.jsвнутриentry
в свойствах
Двадцать одна многостраничная конфигурация упаковки
В этом разделе используетсяdemo20на основе кода
Создайте новый файл list.js в каталоге src и напишите в немconsole.log('这里是 list 页面')
Настройте запись в webpack.base.conf.js, настройте две записи
module.exports = {
entry: {
app: './src/app.js',
list: './src/list.js'
}
}
Если теперь мы прямоnpm run build
Упаковка: в автоматически сгенерированном файле index.html вы обнаружите, что list.js также представлен, что указывает на то, что упаковка с несколькими элементами выполнена успешно, но не реализована.несколько страницПакет, который я хочу упаковатьindex.htmlа такжеlist.htmlДве страницы, представленные в index.htmlapp.js, представленный в list.htmllist.js,Как сделать?
Для удобства демонстрации сначалаwebpack.prod.conf.js
серединаcacheGroups
добавить одинdefault
атрибут, пользовательское имя
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
jquery: {
name: 'jquery', // 单独将 jquery 拆包
priority: 15,
test: /[\\/]node_modules[\\/]jquery[\\/]/
},
vendors: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors'
},
default: {
name: 'code-segment'
}
}
}
}
Открытьwebpack.base.conf.js
файл, будетHtmlWebpackPlugin
сделать копию, использоватьchunks
атрибут, напишите соответствующий модуль для упаковки
// 存放公共插件
const plugins = [
new HtmlWebpackPlugin({
title: 'webpack4 实战',
filename: 'index.html',
template: path.resolve(__dirname, '..', 'index.html'),
chunks: ['app', 'vendors', 'code-segment', 'jquery', 'lodash']
}),
new HtmlWebpackPlugin({
title: '多页面打包',
filename: 'list.html',
template: path.resolve(__dirname, '..', 'index.html'),
chunks: ['list', 'vendors', 'code-segment', 'jquery', 'lodash']
}),
new CleanWebpackPlugin(),
new webpack.ProvidePlugin({ $: 'jquery' })
]
Два html генерируются в упакованном каталоге dist
Откройте index.html, и вы увидите, что app.js импортирован, а list.html импортирован. List.js, этоHtmlWebpackPlugin
плагинchunks
атрибут, пользовательский импортированный js
Если вы хотите упаковать три страницы, перейдите к копированиюHtmlWebpackPlugin
, настроив в записи, если их четыре или пять, копировать вручную хлопотнее, можно написать метод для автоматической генерацииHtmlWebpackPlugin
настроить
Исправлятьwebpack.base.conf.js
const path = require('path')
const fs = require('fs')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin')
const CleanWebpackPlugin = require('clean-webpack-plugin')
const makePlugins = configs => {
// 基础插件
const plugins = [
new CleanWebpackPlugin(),
new webpack.ProvidePlugin({ $: 'jquery' })
]
// 根据 entry 自动生成 HtmlWebpackPlugin 配置,配置多页面
Object.keys(configs.entry).forEach(item => {
plugins.push(
new HtmlWebpackPlugin({
title: '多页面配置',
template: path.resolve(__dirname, '..', 'index.html'),
filename: `${item}.html`,
chunks: [item, 'vendors', 'code-segment', 'jquery', 'lodash']
})
)
})
// 自动引入 dll 中的文件
const files = fs.readdirSync(path.resolve(__dirname, '../dll'))
files.forEach(file => {
if (/.*\.dll.js/.test(file)) {
plugins.push(
new AddAssetHtmlWebpackPlugin({
filepath: path.resolve(__dirname, '../dll', file)
})
)
}
if (/.*\.manifest.json/.test(file)) {
plugins.push(
new webpack.DllReferencePlugin({
manifest: path.resolve(__dirname, '../dll', file)
})
)
}
})
return plugins
}
const configs = {
entry: {
index: './src/app.js',
list: './src/list.js'
},
output: {
path: path.resolve(__dirname, '..', 'dist')
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader'
}
]
},
{
test: /\.(png|jpg|jpeg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
name: '[name]-[hash:5].min.[ext]',
limit: 1000, // size <= 1KB
outputPath: 'images/'
}
},
// img-loader for zip img
{
loader: 'image-webpack-loader',
options: {
// 压缩 jpg/jpeg 图片
mozjpeg: {
progressive: true,
quality: 65 // 压缩率
},
// 压缩 png 图片
pngquant: {
quality: '65-90',
speed: 4
}
}
}
]
},
{
test: /\.(eot|ttf|svg)$/,
use: {
loader: 'url-loader',
options: {
name: '[name]-[hash:5].min.[ext]',
limit: 5000, // fonts file size <= 5KB, use 'base64'; else, output svg file
publicPath: 'fonts/',
outputPath: 'fonts/'
}
}
}
]
},
performance: false
}
makePlugins(configs)
configs.plugins = makePlugins(configs)
module.exports = configs
После повторной упаковки эффект тот же.Если вы хотите добавить страницы, вам нужно только ввести еще один файл js в запись в качестве записи.
Многостраничная конфигурация фактически предназначена для определения нескольких записей и создания нескольких html-страниц с помощью htmlWebpackPlugin.
Двадцать два, напиши загрузчик
новая папка,npm init -y
,npm i webpack webpack-cli -D
, создайте новый src/index.js, напишитеconsole.log('hello world')
новыйloaders/replaceLoader.js
документ
module.exports = function(source) {
return source.replace('world', 'loader')
}
Параметр source это наш исходник, здесь нужно заменить мир в исходнике на loader
новыйwebpack.config.js
const path = require('path')
module.exports = {
mode: 'development',
entry: {
main: './src/index.js'
},
module: {
rules: [
{
test: /.js/,
use: [path.resolve(__dirname, './loaders/replaceLoader.js')] // 引入自定义 loader
}
]
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js'
}
}
Структура каталога:
После упаковки открываем файл dist/main.js Внизу видно, что мир изменен на загрузчик, и написан простейший загрузчик.
Добавить свойство параметров
const path = require('path')
module.exports = {
mode: 'development',
entry: {
main: './src/index.js'
},
module: {
rules: [
{
test: /.js/,
use: [
{
loader: path.resolve(__dirname, './loaders/replaceLoader.js'),
options: {
name: 'xh'
}
}
]
}
]
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js'
}
}
Измените файл replaceLoader.js, сохраните его, упакуйте и выведите, чтобы увидеть результат.
module.exports = function(source) {
console.log(this.query)
return source.replace('world', this.query.name)
}
Файл, созданный после упаковки, также изменяется на имя, указанное в параметрах.
Дополнительные настройки см. на официальном сайтеAPI, найдите интерфейс загрузчика, который имеет this.query
Если ваши параметры не являются объектом, а записаны в виде строки, могут быть некоторые проблемы, что здесь официально рекомендуетсяloader-utilsчтобы получить содержимое опций
Установитьnpm i loader-utils -D
, изменить replaceLoader.js
const loaderUtils = require('loader-utils')
module.exports = function(source) {
const options = loaderUtils.getOptions(this)
console.log(options)
return source.replace('world', options.name)
}
console.log(options)
а такжеconsole.log(this.query)
Результат согласован
Если вы хотите передать дополнительную информацию, возврат не прост в использовании, официальный сайт предоставляет намthis.callbackAPI, использование выглядит следующим образом
this.callback(
err: Error | null,
content: string | Buffer,
sourceMap?: SourceMap,
meta?: any
)
Изменить replaceLoader.js
const loaderUtils = require('loader-utils')
module.exports = function(source) {
const options = loaderUtils.getOptions(this)
const result = source.replace('world', options.name)
this.callback(null, result)
}
Два необязательных параметра sourceMap (должен быть анализируемой исходной картой этого модуля) и meta (может быть любой контент (например, некоторые метаданные)) в настоящее время не используются.Возвращается только результат, а после сохранения и переупаковки эффект и возвращение такое же
Что если написать асинхронный код в загрузчике
const loaderUtils = require('loader-utils')
module.exports = function(source) {
const options = loaderUtils.getOptions(this)
setTimeout(() => {
const result = source.replace('world', options.name)
return result
}, 1000)
}
Ошибка загрузчика не вернулась, используйте его здесьthis.asyncписать асинхронный код
const loaderUtils = require('loader-utils')
module.exports = function(source) {
const options = loaderUtils.getOptions(this)
const callback = this.async()
setTimeout(() => {
const result = source.replace('world', options.name)
callback(null, result)
}, 1000)
}
Симулируйте синхронный загрузчик и асинхронный загрузчик
создать новыйreplaceLoaderAsync.js
файл, поместите асинхронный код, написанный ранее, и измените егоreplaceLoader.js
для синхронного кода
// replaceLoaderAsync.js
const loaderUtils = require('loader-utils')
module.exports = function(source) {
const options = loaderUtils.getOptions(this)
const callback = this.async()
setTimeout(() => {
const result = source.replace('world', options.name)
callback(null, result)
}, 1000)
}
// replaceLoader.js
module.exports = function(source) {
return source.replace('xh', 'world')
}
Исправлятьwebpack.config.js
Порядок выполнения загрузчика снизу, сначала выполнить асинхронный код, изменить World на XH, затем выполнить синхронный код, изменить XH на World
module: {
rules: [
{
test: /.js/,
use: [
{
loader: path.resolve(__dirname, './loaders/replaceLoader.js')
},
{
loader: path.resolve(__dirname, './loaders/replaceLoaderAsync.js'),
options: {
name: 'xh'
}
}
]
}
]
}
После сохранения упакуйте его, вы можете увидеть в mian.js, что он был изменен наhello world
, использование нескольких загрузчиков тоже делается
Если есть несколько пользовательских загрузчиков, проходите каждый разpath.resolve(__dirname, xxx)
Есть ли лучший способ написать так?
использоватьresolveLoader
, определите модули, когда вы используете загрузчик, он будет идти первымnode_modules
Ищите, если не найдете, идите туда./loaders
найти в
const path = require('path')
module.exports = {
mode: 'development',
entry: {
main: './src/index.js'
},
resolveLoader: {
modules: ['node_modules', './loaders']
},
module: {
rules: [
{
test: /.js/,
use: [
{
loader: 'replaceLoader.js'
},
{
loader: 'replaceLoaderAsync.js',
options: {
name: 'xh'
}
}
]
}
]
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js'
}
}
Двадцать три, напиши плагин
Во-первых, создайте новую папку, и npm запустит операцию.Особенности были упомянуты в предыдущих разделах, поэтому я не буду их повторять.
Создайте новую папку plugins в корневом каталоге, создайте новыйcopyright-webpack-plugin.js
, обычно мы используемxxx-webpack-plugin
, поэтому мы называем его так, определение плагина — это класс
class CopyrightWebpackPlugin {
constructor() {
console.log('插件被使用了')
}
apply(compiler) {}
}
module.exports = CopyrightWebpackPlugin
Используется в webpack.config.js, поэтому каждый раз, когда вы используете плагин, вам нужно использоватьnew, потому что плагин по сути является классом
const path = require('path')
const CopyrightWebpackPlugin = require('./plugins/copyright-webpack-plugin')
module.exports = {
mode: 'development',
entry: {
main: './src/index.js'
},
plugins: [new CopyrightWebpackPlugin()],
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js'
}
}
После сохранения и упаковки плагин используется, но мы ничего не делали
Если мы хотим передать параметры, мы можем сделать это
new CopyrightWebpackPlugin({
name: 'xh'
})
в то же времяcopyright-webpack-plugin.js
Получено
class CopyrightWebpackPlugin {
constructor(options) {
console.log('插件被使用了')
console.log('options = ', options)
}
apply(compiler) {}
}
module.exports = CopyrightWebpackPlugin
Давайте положимconstructorЗакомментируйте результат, который будет упакован,перед помещением в каталог distВ этот момент давайте проделаем некоторые операции
apply(compiler) {}
компилятор можно рассматривать как экземпляр веб-пакета, подробности см. на официальном сайте.compiler-hooks
Хуки есть хуки, как жизненный цикл vue и реагировать, находитьemit
В этот момент упакованный результат выполняется перед тем, как поместить его в каталог dist.AsyncSeriesHook
асинхронный метод
class CopyrightWebpackPlugin {
apply(compiler) {
compiler.hooks.emit.tapAsync(
'CopyrightWebpackPlugin',
(compilation, cb) => {
console.log(11)
cb()
}
)
}
}
module.exports = CopyrightWebpackPlugin
потому чтоemitдаасинхронныйДа, черезtapAsyncЧтобы написать, когда вы помещаете код в каталог dist, этот хук сработает и перейдет к определенной нами функции, если вы используетеtapAsyncфункция, не забудьте использовать ее в концеcb(), Tapasync должен пройти два параметра, первый параметр передает имя плагина, который мы определены
После сохранения снова упаковать, и контент, который мы написали, также выводится
compilationВ этом параметре хранится все содержимое данного пакета, его можно вывестиcompilation.assets
посмотри
Возвращаемый результат - объект,main.js
Это ключ, то есть имя файла и суффикс файла, сгенерированный после упаковки, мы можем его имитировать
class CopyrightWebpackPlugin {
apply(compiler) {
compiler.hooks.emit.tapAsync(
'CopyrightWebpackPlugin',
(compilation, cb) => {
// 生成一个 copyright.txt 文件
compilation.assets['copyright.txt'] = {
source: function() {
return 'copyright by xh'
},
size: function() {
return 15 // 上面 source 返回的字符长度
}
}
console.log('compilation.assets = ', compilation.assets)
cb()
}
)
}
}
module.exports = CopyrightWebpackPlugin
Генерируется в каталоге distcopyright.txt
документ
Теперь представлен асинхронный хук, теперь используйте синхронизирующий хук
class CopyrightWebpackPlugin {
apply(compiler) {
// 同步钩子
compiler.hooks.compile.tap('CopyrightWebpackPlugin', compilation => {
console.log('compile')
})
// 异步钩子
compiler.hooks.emit.tapAsync(
'CopyrightWebpackPlugin',
(compilation, cb) => {
compilation.assets['copyright.txt'] = {
source: function() {
return 'copyright by xh'
},
size: function() {
return 15 // 字符长度
}
}
console.log('compilation.assets = ', compilation.assets)
cb()
}
)
}
}
module.exports = CopyrightWebpackPlugin
24. Написать пакет
модульный анализ
Создайте три новых файла в каталоге srcword.js
,message.js
,index.js
, соответствующий код:
// word.js
export const word = 'hello'
// message.js
import { word } from './word.js'
const message = `say ${word}`
export default message
// index.js
import message from './message.js'
console.log(message)
новыйbundle.js
const fs = require('fs')
const moduleAnalyser = filename => {
const content = fs.readFileSync(filename, 'utf-8')
console.log(content)
}
moduleAnalyser('./src/index.js')
используя узелfsмодуль, прочитать информацию о файле и вывести ее на консоль, здесь глобально установлен плагин для отображения подсветки кода,npm i cli-highlight -g
,бегатьnode bundle.js | highlight
Код в index.js выведен на консоль, код выделен для удобства чтения Чтение информации о файле записи завершено
Теперь мы хотим прочитать зависимость message.js, используемую в файле index.js,import message from './message.js'
Установите сторонний плагинnpm i @babel/parser
@babel/parser— парсер JavaScript, используемый в Babel.
Официальный веб-сайт также предоставляет соответствующий пример кода, в соответствии с образцом кода для имитации, изменения наших файлов.
const fs = require('fs')
const parser = require('@babel/parser')
const moduleAnalyser = filename => {
const content = fs.readFileSync(filename, 'utf-8')
console.log(
parser.parse(content, {
sourceType: 'module'
})
)
}
moduleAnalyser('./src/index.js')
Мы используем синтаксис модуля es6, поэтомуsourceType: 'module'
После сохранения и запуска выводAST (абстрактное синтаксическое дерево), у которого есть поле body, мы выводим это поле
const fs = require('fs')
const parser = require('@babel/parser')
const moduleAnalyser = filename => {
const content = fs.readFileSync(filename, 'utf-8')
const ast = parser.parse(content, {
sourceType: 'module'
})
console.log(ast.program.body)
}
moduleAnalyser('./src/index.js')
Печатаются два узла Node, и тип первого узлаImportDeclaration(введено объявление), по сравнению с тем, что мы написали в index.jsimport message from './message.js'
, тип второго узлаExpressionStatement(объявление выражений), по сравнению с тем, что мы написалиconsole.log(message)
Используйте babel, чтобы помочь нам сгенерировать абстрактное синтаксическое дерево, и мы импортируем егоimport message1 from './message1.js'
бежать снова
Абстрактное синтаксическое дерево преобразует наш js-код в форму объекта, и теперь мы можем пройтись по типу в объекте узла, сгенерированному абстрактным синтаксическим деревом, независимо от того, является ли онImportDeclaration
, вы можете найти зависимости, представленные в коде
А потом с помощью инструментаnpm i @babel/traverse
const fs = require('fs')
const parser = require('@babel/parser')
const traverse = require('@babel/traverse').default
const moduleAnalyser = filename => {
const content = fs.readFileSync(filename, 'utf-8')
const ast = parser.parse(content, {
sourceType: 'module'
})
traverse(ast, {
ImportDeclaration({ node }) {
console.log(node)
}
})
}
moduleAnalyser('./src/index.js')
напечатал только дваImportDeclaration, в конце обхода нам нужно только получить зависимое имя файла.В напечатанном содержимом каждый узел имеетsource
свойства, естьvalue
поле, указывающее путь к файлу и имя файла
const fs = require('fs')
const parser = require('@babel/parser')
const traverse = require('@babel/traverse').default
const moduleAnalyser = filename => {
const content = fs.readFileSync(filename, 'utf-8')
const ast = parser.parse(content, {
sourceType: 'module'
})
const dependencise = []
traverse(ast, {
ImportDeclaration({ node }) {
dependencise.push(node.source.value)
}
})
console.log(dependencise)
}
moduleAnalyser('./src/index.js')
После сохранения и повторного запуска результаты вывода:
['./message.js', './message1.js']
Таким образом, анализ зависимостей файла ввода анализируется, и теперь представленный в index.jsmessage1.js
Зависимости удалены.Вот примечание.Распечатанный путь к файлуотносительный путь, относительноsrc/index.js
файл, но когда мы его упаковываем, это не может быть относительный путь файла записи (index.js), а должен бытьотносительный путь к корневому каталогу(или скорееабсолютный путь), с помощью API узла введите путь
const fs = require('fs')
const path = require('path')
const parser = require('@babel/parser')
const traverse = require('@babel/traverse').default
const moduleAnalyser = filename => {
const content = fs.readFileSync(filename, 'utf-8')
const ast = parser.parse(content, {
sourceType: 'module'
})
const dependencise = []
traverse(ast, {
ImportDeclaration({ node }) {
const dirname = path.dirname(filename)
console.log(dirname)
dependencise.push(node.source.value)
}
})
// console.log(dependencise)
}
moduleAnalyser('./src/index.js')
Выход./src
, продолжайте изменять
ImportDeclaration({ node }) {
const dirname = path.dirname(filename)
const newFile = path.join(dirname, node.source.value)
console.log(newFile)
dependencise.push(node.source.value)
}
Выходsrc\message.js
Windows и Unix-подобные (linux/mac) пути разные. Windows использует обратную косую черту \ для разделения каталогов или файлов, в то время как в Unix-подобных системах она используется/.
Поскольку я работаю в системе Windows, вывод здесьsrc\message.js
, в то время как Unix-подобный выводsrc/message.js
.\src\message.js
Этот путь мы будем использовать, когда будем упаковывать
newFile .\src\message.js
[ '.\\src\\message.js' ]
Существует как относительный путь, так и абсолютный путь.
const fs = require('fs')
const path = require('path')
const parser = require('@babel/parser')
const traverse = require('@babel/traverse').default
const moduleAnalyser = filename => {
const content = fs.readFileSync(filename, 'utf-8')
const ast = parser.parse(content, {
sourceType: 'module'
})
const dependencise = {}
traverse(ast, {
ImportDeclaration({ node }) {
const dirname = path.dirname(filename)
const newFile = '.\\' + path.join(dirname, node.source.value)
console.log('newFile', newFile)
dependencise[node.source.value] = newFile
}
})
console.log(dependencise)
return {
filename,
dependencise
}
}
moduleAnalyser('./src/index.js')
newFile .\src\message.js
{ './message.js': '.\\src\\message.js' }
Поскольку код, который мы написали, имеет формат es6, браузер не может его распознать, и нам по-прежнему нужен Babel для преобразования.
npm i @babel/core @babel/preset-env
'use strict'
var _message = _interopRequireDefault(require('./message.js'))
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : { default: obj }
}
console.log(_message.default)
const fs = require('fs')
const path = require('path')
const parser = require('@babel/parser')
const traverse = require('@babel/traverse').default
const babel = require('@babel/core')
const moduleAnalyser = filename => {
const content = fs.readFileSync(filename, 'utf-8')
const ast = parser.parse(content, {
sourceType: 'module'
})
const dependencise = {}
traverse(ast, {
ImportDeclaration({ node }) {
const dirname = path.dirname(filename)
const newFile = '.\\' + path.join(dirname, node.source.value)
dependencise[node.source.value] = newFile
}
})
const { code } = babel.transformFromAst(ast, null, {
presets: ['@babel/preset-env']
})
return {
filename,
dependencise,
code
}
}
const moduleInfo = moduleAnalyser('./src/index.js')
console.log(moduleInfo)
Результаты анализа выводятся на консоль
{ filename: './src/index.js',
dependencise: { './message.js': '.\\src\\message.js' },
code:
'"use strict";\n\nvar _message = _interopRequireDefault(require("./message.js"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nconsole.log(_message.default);' }
В настоящее время мы анализируем только один модуль, а затем нам нужно проанализировать весь проект, поэтому сначала мы анализируем входной файл, а затем анализируем зависимости, используемые в входном файле.
график зависимости
Создайте функцию для круговых зависимостей и сгенерируйте график
// 依赖图谱
const makeDependenciesGraph = entry => {
const entryModule = moduleAnalyser(entry)
const graphArray = [entryModule]
for (let i = 0; i < graphArray.length; i++) {
const item = graphArray[i]
const { dependencise } = item
// 如果入口文件有依赖就去做循环依赖,对每一个依赖做分析
if (dependencise) {
for (const j in dependencise) {
if (dependencise.hasOwnProperty(j)) {
graphArray.push(moduleAnalyser(dependencise[j]))
}
}
}
}
console.log('graphArray = ', graphArray)
}
Проанализируйте зависимости записи и зависимости в зависимостях и поместите их вgraphArray, результат печати вывода консоли
можно увидетьgraphArrayВсего в проекте три объекта, то есть три файла, которые мы ввели в проект, все они проанализированы.Для удобства чтения мы создаем объект графика и выкладываем результаты анализа по очереди.
// 依赖图谱
const makeDependenciesGraph = entry => {
const entryModule = moduleAnalyser(entry)
const graphArray = [entryModule]
for (let i = 0; i < graphArray.length; i++) {
const item = graphArray[i]
const { dependencise } = item
// 如果入口文件有依赖就去做循环依赖,对每一个依赖做分析
if (dependencise) {
for (const j in dependencise) {
if (dependencise.hasOwnProperty(j)) {
graphArray.push(moduleAnalyser(dependencise[j]))
}
}
}
}
// console.log('graphArray = ', graphArray)
// 创建一个对象,将分析后的结果放入
const graph = {}
graphArray.forEach(item => {
graph[item.filename] = {
dependencise: item.dependencise,
code: item.code
}
})
console.log('graph = ', graph)
return graph
}
выходgraphдля:
Наконец вmakeDependenciesGraph
генерал-лейтенантgraphвернуть, назначитьgraphInfo, вывод такой же, как график
const graghInfo = makeDependenciesGraph('./src/index.js')
console.log(graghInfo)
сгенерировать код
Теперь, когда все результаты генерации кода получены, мы можем теперь использоватьЗависимостиГрафикдля генерации кода, который фактически работает в браузере
Лучше поставить его в большую закрытие.Избегайте загрязнения глобальной окружающей среды
const generateCode = entry => {
// makeDependenciesGraph 返回的是一个对象,需要转换成字符串
const graph = JSON.stringify(makeDependenciesGraph(entry))
return `
(function (graph) {
})(${graph})
`
}
const code = generateCode('./src/index.js')
console.log(code)
Сначала я отформатировал код выходного графика здесь, вы можете найти его вindex.js
использовалrequire
метод,message.js
используется не только вrequire
метод, также используйтеexports
Object, но в браузере таких нет, если зайти напрямую, то сообщит об ошибке.
let graph = {
'./src/index.js': {
dependencise: { './message.js': '.\\src\\message.js' },
code: `
"use strict";\n\n
var _message = _interopRequireDefault(require("./message.js"));\n\n
function _interopRequireDefault(obj){ return obj && obj.__esModule ? obj : { default: obj }; } \n\n
console.log(_message.default);
`
},
'.\\src\\message.js': {
dependencise: { './word.js': '.\\src\\word.js' },
code:
'"use strict";\n\nObject.defineProperty(exports, "__esModule", {\n value: true\n});\nexports.default = void 0;\n\nvar _word = require("./word.js");\n\nvar message = "say ".concat(_word.word);\nvar _default = message;\nexports.default = _default;'
},
'.\\src\\word.js': {
dependencise: {},
code:
'"use strict";\n\nObject.defineProperty(exports, "__esModule", {\n value: true\n});\nexports.word = void 0;\nvar word = \'hello\';\nexports.word = word;'
}
}
Следующим шагом является создание метода require и экспорт объекта.
const generateCode = entry => {
console.log(makeDependenciesGraph(entry))
// makeDependenciesGraph 返回的是一个对象,需要转换成字符串
const graph = JSON.stringify(makeDependenciesGraph(entry))
return `
(function (graph) {
// 定义 require 方法
function require(module) {
};
require('${entry}')
})(${graph})
`
}
const code = generateCode('./src/index.js')
console.log(code)
graph является графом зависимостей и выполняет его после получения записи./src/index.js
Код в , то есть код в выделенной ниже части, для наглядности я взял для ознакомления код графа, выведенный ранее:
let graph = {
'./src/index.js': {
dependencise: { './message.js': '.\\src\\message.js' },
code: `
"use strict";\n\n
var _message = _interopRequireDefault(require("./message.js"));\n\n
function _interopRequireDefault(obj){ return obj && obj.__esModule ? obj : { default: obj }; } \n\n
console.log(_message.default);
`
}
}
Чтобы получить код в коде для выполнения, здесьиспользовать другое закрытие, пусть код в каждом модуле выполняется в замыкании, чтобы переменные модуля не влияли на внешние переменные
return `
(function (graph) {
// 定义 require 方法
function require(module) {
(function (code) {
eval(code)
})(graph[module].code)
};
require('${entry}')
})(${graph})
`
То, что передается в закрытии,graph[module].code
, теперь запись./src/index.js
Этот файл будет передан в переменную модуля в запросе и собственно найдет граф зависимостей./src/index.js
Соответствующий объект, а затем найти в коде соответствующий код, то есть следующий код, который был отформатирован мной, чтобы продемонстрировать эффект
'use strict'
var _message = _interopRequireDefault(require('./message.js'))
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : { default: obj }
}
console.log(_message.default)
Но мы найдем это здесь_interopRequireDefault(require('./message.js'))
Представлено./message.js
Относительный путь, дождитесь второго выполнения,require(module)
здесьmodule
соответствует./message.js
это будет выглядеть на графике./message.js
Соответствующий код ниже, но то, что мы храним на графике, это'.\\src\\message.js'
абсолютный путь, поэтому объект не будет найден
Поскольку относительный путь был введен, когда мы писали код ранее, теперь нам нужно преобразовать относительный путь в абсолютный путь для правильного выполнения и определить метод localRequire, чтобы в следующий раз, когда мы будем его искать, мы перешли к нашему собственный определенный localRequire, который на самом деле является методом преобразования относительного пути
return `
(function (graph) {
// 定义 require 方法
function require(module) {
// 相对路径转换
function localRequire(relativePath) {
return require(graph[module].dependencise[relativePath])
}
(function (require, code) {
eval(code)
})(localRequire, graph[module].code)
};
require('${entry}')
})(${graph})
`
Мы определяем метод localRequire и передаем его в замыкание при выполненииeval(code)
выполняется, когдаrequire
метод вместо выполнения внешнегоrequire(module)
Этот метод, но выполняет метод localRequire, который мы передали в
Мы ввели это в анализируемый кодmessage.js
из
var _message = _interopRequireDefault(require('./message.js'))
звонил сюдаrequire('./message.js')
, о чем мы писали вышеrequire
метод, то естьlocalRequire(relativePath)
Итак, относительный путь'./message.js'
Этот метод возвращаетrequire(graph[module].dependencise[relativePath])
Здесь я ввожу параметры, и все:
graph('./src/index.js').dependencise['./message.js']
let graph = {
'./src/index.js': {
dependencise: { './message.js': '.\\src\\message.js' },
code: `
"use strict";\n\n
var _message = _interopRequireDefault(require("./message.js"));\n\n
function _interopRequireDefault(obj){ return obj && obj.__esModule ? obj : { default: obj }; } \n\n
console.log(_message.default);
`
}
}
Глядя на карту, вы можете обнаружить, что окончательный возврат'.\\src\\message.js'
Абсолютный путь, после возврата абсолютного пути мы вызываемrequire(graph('./src/index.js').dependencise['./message.js'])
заключается в выполнении внешнего определенногоrequire(module)
Этот метод выполняется рекурсивно, этого недостаточно, он просто реализует метод require, а объект экспорта по-прежнему отсутствует, поэтому мы определяем другой объект экспорта.
return `
(function (graph) {
// 定义 require 方法
function require(module) {
// 相对路径转换
function localRequire(relativePath) {
return require(graph[module].dependencise[relativePath])
}
var exports = {};
(function (require, exports, code) {
eval(code)
})(localRequire, exports, graph[module].code)
return exports
};
require('${entry}')
})(${graph})
`
Наконец помнитеreturn exports
Экспортируйте экспорт, чтобы следующий модуль мог получить результаты экспорта, когда модуль будет введен.Теперь процесс генерации кода завершен, и окончательный результат - большая строка, сохраните ее и запустите снова.node bundle.js | highlight
Вот я в среде windows.Недостаточно поместить код вывода прямо в браузер.Я форматирую сжатый код следующим образом, а затем помещаю его в браузер и вывод будет успешным.
;(function(graph) {
function require(module) {
function localRequire(relativePath) {
return require(graph[module].dependencise[relativePath])
}
var exports = {}
;(function(require, exports, code) {
eval(code)
})(localRequire, exports, graph[module].code)
return exports
}
require('./src/index.js')
})({
'./src/index.js': {
dependencise: { './message.js': '.\\src\\message.js' },
code:
'"use strict";\n\nvar _message = _interopRequireDefault(require("./message.js"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nconsole.log(_message.default);'
},
'.\\src\\message.js': {
dependencise: { './word.js': '.\\src\\word.js' },
code:
'"use strict";\n\nObject.defineProperty(exports, "__esModule", {\n value: true\n});\nexports.default = void 0;\n\nvar _word = require("./word.js");\n\nvar message = "say ".concat(_word.word);\nvar _default = message;\nexports.default = _default;'
},
'.\\src\\word.js': {
dependencise: {},
code:
'"use strict";\n\nObject.defineProperty(exports, "__esModule", {\n value: true\n});\nexports.word = void 0;\nvar word = \'hello\';\nexports.word = word;'
}
})
Поместите приведенный выше код в консоль браузера и нажмите Enter для выводаsay hello
Суммировать
Это упакованное содержимое инструмента упаковки.В этот период задействованы знания узлов, для перевода ast (абстрактного синтаксического дерева) используется babel, а финальная функция generateCode включаетрекурсияа такжеЗакрытие,формальный параметра такжеАргументы, вам нужно прочитать его несколько раз, чтобы углубить ваше понимание