Создание рабочей среды веб-пакета
Плагин babel, который мы написали, относится к babel-loader, а babel-loader в основном работает со средой webpack, поэтому, чтобы определить, работает ли плагин babel, мы должны создать среду webpack.
Структура каталогов
|-- babel-plugin-wyimport
|-- .editorconfig
|-- .gitignore
|-- package.json
|-- README.md
|-- build
| |-- app.be45e566.js
| |-- index.html
|-- config
| |-- paths.js
| |-- webpack.dev.config.js
| |-- webpack.prod.config.js
|-- scripts
| |-- build.js
| |-- start.js
|-- src
|-- index.js
webpack.prod.config.js
Файл конфигурации не сжимает и не обфусцирует код, в основном для удобства сравнения содержимого файлов до и после компиляции
'use strict'
process.env.BABEL_ENV = 'production';
process.env.NODE_ENV = 'production';
const path = require('path');
const paths = require("./paths");
const fs = require('fs');
const webpack = require("webpack");
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const { WebPlugin } = require('web-webpack-plugin');
module.exports = {
output: {
path: paths.build,
filename: '[name].[chunkhash:8].js',
chunkFilename: 'static/js/[name].[chunkhash:8].chunk.js',
publicPath: "/"
},
entry: {
"app":path.resolve(paths.src, "index.js")
},
resolve:{
extensions:[".js", ".json"],
modules: ["node_modules", paths.src]
},
module: {
rules: [
{
test:/\.css$/,
include:paths.src,
loader: ExtractTextPlugin.extract({
use: [
{
options:{
root: path.resolve(paths.src, "images")
},
loader: require.resolve('css-loader')
}
]
})
},
{
test:/\.less$/,
include:paths.src,
use:[
require.resolve('style-loader'),
{
loader:require.resolve('css-loader'),
options:{
root: path.resolve(paths.src, "images")
}
},
{
loader: require.resolve('less-loader')
}
]
},
{
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
loader: require.resolve('url-loader'),
options: {
limit: 1000,
name: 'static/images/[name].[hash:8].[ext]',
},
},
{
test:/\.(js|jsx)$/,
include:paths.src,
loader: require.resolve("babel-loader"),
options:{
presets:["react-app"],
plugins:[
//["wyimport", {libraryName:"lodash"}]
],
compact: true
//cacheDirectory: true
}
},
{
exclude: [
/\.html$/,
/\.(js|jsx)$/,
/\.css$/,
/\.less$/,
/\.json$/,
/\.bmp$/,
/\.gif$/,
/\.jpe?g$/,
/\.png$/,
/\.svg$/
],
loader: require.resolve('file-loader'),
options: {
name: 'static/[name].[hash:8].[ext]',
},
}
]
},
plugins: [
new ExtractTextPlugin('[name].[chunkhash:8].css'),
new WebPlugin({
//输出的html文件名称
filename: 'index.html',
//这个html依赖的`entry`
requires:["app"]
}),
]
}
build.js
Запустите файл, в основном рассчитайте размер содержимого файла до и после компиляции.
const webpack = require('webpack');
const path = require('path');
const config = require('../config/webpack.prod.config');
const chalk = require('chalk');
const paths = require('../config/paths');
const fs = require("fs");
// 获取目录大小
const getDirSize = (rootPath, unit ="k") => {
if (!fs.existsSync(rootPath)) {
return 0;
}
let buildSize = 0;
const dirSize = (dirPath) => {
let files = fs.readdirSync(dirPath, "utf-8")
files.forEach((files) => {
let filePath = path.resolve(dirPath, files);
let stat = fs.statSync(filePath) || [];
if (stat.isDirectory()){
dirSize(filePath)
} else {
buildSize += stat.size
}
})
}
dirSize(rootPath)
let map = new Map([["k",(buildSize/1024).toFixed(2)+"k"], ["M",buildSize/1024/1024+"M"]])
return map.get(unit);
}
// 清空目录文件
const rmDir = (path, isDeleteDir) => {
if(fs.existsSync(path)) {
files = fs.readdirSync(path);
files.forEach(function(file, index) {
var curPath = path + "/" + file;
if(fs.statSync(curPath).isDirectory()) { // recurse
rmDir(curPath);
} else { // delete file
fs.unlinkSync(curPath);
}
});
fs.rmdirSync(path);
}
}
const measureFileBeforeBuild = () => {
console.log(`打包之前build文件夹的大小: ${chalk.green(getDirSize(paths.build))}\n`)
rmDir(paths.build) //删除build文件夹
return build().then((stats) => {
console.log(chalk.green(`打包完成\n`))
console.log(`打包之后文件夹大小:${chalk.green(getDirSize(paths.build))}\t花费时间: ${chalk.green((stats.endTime-stats.startTime)/1000)}s`)
}, err => {
console.log(chalk.red('Failed to compile.\n'));
console.log((err.message || err) + '\n');
process.exit(1);
})
}
const build = () => {
const compiler = webpack(config)
return new Promise((resolve, reject) => {
compiler.run((err, stats) => {
console.log(chalk.green("开始打包..."))
if (err) {
return reject(err);
}
const message = stats.toJson({}, true)
if (message.errors.length) {
return reject(message.errors);
}
return resolve(stats)
})
})
}
measureFileBeforeBuild()
Мелкий измельчитель
мы вsrc/index.js
ввод в файл
import { uniq } from "lodash"
потомnpm run build
//import { uniq } from "lodash"
import uniq from "lodash/uniq"
потомnpm run build
Если файл импортируетlodashмногие методы, такие как
import uniq from "lodash/uniq";
import extend from "lodash/extend";
import flatten from "lodash/flatten";
import cloneDeep from "lodash/cloneDeep";
...
Этот способ написания довольно раздут, так что вы можете написать это так?import {uniq, extend, flatten, cloneDeep } from "lodash"
А также реализовать загрузку по требованию?Это очень просто, просто скомпилируй и выведи как
import uniq from "lodash/uniq";
import extend from "lodash/extend";
import flatten from "lodash/flatten";
import cloneDeep from "lodash/cloneDeep";
просто хорошо
Подготовка знаний
записыватьpluginПрежде всего, нам нужно понять следующие два момента
- pluginКогда это работает?
- pluginкак это работает
принцип компиляции webpack
babel-loader
так какwebpack
один изloader
Сначала разберемся с процессом компиляции webpack иloader
существуетwebpack
роль вздесьЕсть статья, которая говорит, что это очень хорошо, все сначала прочитали, а потом дочитали.
Основные понятия вавилона
Зная, что есть статья, которая проясняет ситуацию, студенты, которые не очень хорошо разбираются в Babel, сначалавходитьЧитайте дальше, когда поймете!
Здесь я прежде всего хочу подчеркнуть
babel
конфигурация параметров, если я пишуfiveone
изbabel
Плагин, я его так настраиваю в параметрах
{
presets:["react-app", "es2015"],
plugins:[
["fiveone", {libraryName:"lodash"}],
["transform-runtime", {}]
],
}
起作用的顺序为fiveone->transform-runtime->es2015->react-app
Порядок компиляции первыйplugins
тогда слева направоpresets
справа налево
Принцип компиляции Babel
Два вышеприведенных раздела объясняютplugin
Когда это работает, объясните нижеplugin
Как это работает?
-
babylon
Интерпретатор преобразует строку кода вASTдерево, напримерimport {uniq, extend, flatten, cloneDeep } from "lodash"
превратиться вAST
Дерево -
babel-traverse
правильноAST
Производится разбор дерева и обход всего дерева. - плагин преобразует новый
AST
Дерево. - вывести новую строку кодаЛитературный адрес
Плагин, который мы хотим написать, находится на третьем шаге, конвертируйте новый через путь
AST
Деревья? Давайте начнем, как перейти к третьему шагу!
запустить Babel-плагин
Сначала нам нужно установить два инструментаbabel-core
а такжеbabel-types
;
npm install --save babel-core babel-types
;
-
babel-core
поставкаtransform
метод преобразует строку кода вAST
Дерево -
babel-types
Обеспечить различные операцииAST
Библиотека инструментов Node
мы вsrc/index.js
ввод в
var babel = require('babel-core');
var t = require('babel-types');
const code = `import {uniq, extend, flatten, cloneDeep } from "lodash"`;
const visitor = {
Identifier(path){
console.log(path.node.name)
}
}
const result = babel.transform(code, {
plugins: [{
visitor: visitor
}]
})
бегатьnode src index.js
visitor
вавилонская параAST
Дерево пройдено, и процесс обхода предоставит метод, называемый объектом посетителя, для доступа к определенному этапу, такому как выше
Identifier(path){
console.log(path.node.name)
}
посетить узел идентификатора,AST
Дерево расширяется следующим образом
uniq
, так как этот метод вызывается для каждого входа и выхода узла.
Будет два обхода, один вход как нижний обход, а другой выход как верхний обход.
мы будемsrc/index.js
серединаIdentifier
метод изменен на
Identifier:{
enter(path) {
console.log("我是进入的:",path.node.name)
},
exit(path) {
console.log("我是进入的:",path.node.name)
}
}
бегатьnode src index.js
遍历流程: 向下遍历-进入uniq->退出uniq->向上遍历-进入uniq->退出uniq
path
путь представляет собой соединение между двумя узлами.Через этот объект мы можем получить доступ к текущему узлу, дочернему узлу, родительскому узлу, а также добавить, удалить, изменить, заменить и другие операции над узлами. Следующая демонстрация будетuniq
заменять_uniq
код показывает, как показано ниже:
var babel = require('babel-core');
var t = require('babel-types');
const code = `import {uniq, extend, flatten, cloneDeep } from "lodash"`;
const visitor = {
Identifier(path){
if (path.node.name == "uniq") {
var newIdentifier = t.identifier('_uniq') //创建一个名叫_uniq的新identifier节点
path.replaceWith(newIdentifier) //把当前节点替换成新节点
}
}
}
const result = babel.transform(code, {
plugins: [{
visitor: visitor
}]
})
console.log(result.code) //import { _uniq, extend, flatten, cloneDeep } from "lodash";
Начинать
С учетом приведенных выше концепций мы теперь помещаем строку кодаimport {uniq, extend, flatten, cloneDeep } from "lodash"
Конвертировано в
import uniq from "lodash/uniq";
import extend from "lodash/extend";
import flatten from "lodash/flatten";
import cloneDeep from "lodash/cloneDeep";
код показывает, как показано ниже
var babel = require('babel-core');
var t = require('babel-types');
const code = `import {uniq, extend, flatten, cloneDeep } from "lodash"`;
const visitor = {
ImportDeclaration(path, _ref = {opts:{}}){
const specifiers = path.node.specifiers;
const source = path.node.source;
if (!t.isImportDefaultSpecifier(specifiers[0]) ) {
var declarations = specifiers.map((specifier, i) => { //遍历 uniq extend flatten cloneDeep
return t.ImportDeclaration( //创建importImportDeclaration节点
[t.importDefaultSpecifier(specifier.local)],
t.StringLiteral(`${source.value}/${specifier.local.name}`)
)
})
path.replaceWithMultiple(declarations)
}
}
}
const result = babel.transform(code, {
plugins: [{
visitor: visitor
}]
})
console.log(result.code)
потомnode src/index.js
AST
начальство
Настроить на node_modules
После того, как код написан, его нужно настроить, если он работает.Мы назвали этот плагин Fiveone, поэтому создайте новую папку с именем babel-plugin-fiveone в node_modules
существуетbabel-plugin-fiveone/index.js
ввод в
var babel = require('babel-core');
var t = require('babel-types');
const visitor = {
// 对import转码
ImportDeclaration(path, _ref = {opts:{}}){
const specifiers = path.node.specifiers;
const source = path.node.source;
if (!t.isImportDefaultSpecifier(specifiers[0]) ) {
var declarations = specifiers.map((specifier) => { //遍历 uniq extend flatten cloneDeep
return t.ImportDeclaration( //创建importImportDeclaration节点
[t.importDefaultSpecifier(specifier.local)],
t.StringLiteral(`${source.value}/${specifier.local.name}`)
)
})
path.replaceWithMultiple(declarations)
}
}
};
module.exports = function (babel) {
return {
visitor
};
}
затем изменитьwebpack.prod.config.js
серединаbabel-loader
элемент конфигурации
options:{
presets:["react-app"],
plugins:[
["fiveone", {}]
],
}
потомsrc/index.js
ввод в
import {uniq, extend, flatten, cloneDeep } from "lodash"
npm run build
Однако ввести такое перекодирование для всех библиотек невозможно, поэтому вbabel-loader
Плагин добавляет библиотеку
options:{
presets:["react-app"],
plugins:[
["fiveone", {libraryName:"lodash"}]
],
}
существуетbabel-plugin-fiveone/index.js
Изменить
var babel = require('babel-core');
var t = require('babel-types');
const visitor = {
// 对import转码
ImportDeclaration(path, _ref = {opts:{}}){
const specifiers = path.node.specifiers;
const source = path.node.source;
// 只有libraryName满足才会转码
if (_ref.opts.libraryName == source.value && (!t.isImportDefaultSpecifier(specifiers[0])) ) { //_ref.opts是传进来的参数
var declarations = specifiers.map((specifier) => { //遍历 uniq extend flatten cloneDeep
return t.ImportDeclaration( //创建importImportDeclaration节点
[t.importDefaultSpecifier(specifier.local)],
t.StringLiteral(`${source.value}/${specifier.local.name}`)
)
})
path.replaceWithMultiple(declarations)
}
}
};
module.exports = function (babel) {
return {
visitor
};
}
конец
Пожалуйста, поправьте меня, если что-то не так в статье, большое спасибо! адрес гитхаба:GitHub.com/Aman's Su/Put…Если вы что-то получаете, вы можете дать этоstarВеликолепно!
Ссылка на ссылку