Сухой товар! Создайте плагин веб-пакета (включая подробный процесс Tapable + Webpack)

Webpack

содержание

  • Что такое Табличный?
  • Использование таблиц
  • продвигать
  • Другие методы Таблицы
  • процесс веб-пакета
  • Суммировать
  • Настоящий бой! написать плагин

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

То, что управляет этими плагинами для запуска в потоке событий webapck, — это базовый класс, написанный самим webpack.Tapable.

Tapable выставляет крепленияpluginметод, который позволяет нам Управляйте плагином для запуска в потоке событий webapack (как показано ниже). Позже мы увидим основные объектыCompiler,Compilationи т.д. наследуются отTabableДобрый. (Как показано ниже)

Что такое Табличный?

нажимаемая библиотекаМногие классы хуков (hook) доступны для предоставления хуков для монтирования плагинов.

const {
	SyncHook,
	SyncBailHook,
	SyncWaterfallHook,
	SyncLoopHook,
	AsyncParallelHook,
	AsyncParallelBailHook,
	AsyncSeriesHook,
	AsyncSeriesBailHook,
	AsyncSeriesWaterfallHook
 } = require("tapable");

Использование таблиц

  • 1.новый крючок Новый крючок
    • Все tapable предоставляют методы класса, а новый метод класса получает нужные нам хуки.
    • класс принимает опции параметров массива, которые не требуются. Метод класса примет такое же количество параметров в соответствии с переданными параметрами.
const hook1 = new SyncHook(["arg1", "arg2", "arg3"]);
  • 2. Привязать хуки с помощью tap/tapAsync/tapPromise

tabpack предоставляет同步&异步методы привязки крючков, и все они имеют绑定事件а также执行事件соответствующий метод.

Async* Sync*
Привязка: tapAsync/tapPromise/tap Привязка: нажмите
Выполнить: callAsync/обещание Выполнить: позвонить
  • 3.call/callAsync выполнить событие привязки
const hook1 = new SyncHook(["arg1", "arg2", "arg3"]);

//绑定事件到webapck事件流
hook1.tap('hook1', (arg1, arg2, arg3) => console.log(arg1, arg2, arg3)) //1,2,3

//执行绑定的事件
hook1.call(1,2,3)

  • взять каштан
    • 定义一个Car方法,在内部hooks上新建钩子。 соответственно同步钩子ускорить, сломать (ускорение принимает параметр),异步钩子calculateRoutes
    • Используйте соответствующий крючок绑定和执行方法
    • calculateRoutes используетtapPromiseможет вернутьpromiseобъект.
//引入tapable
const {
    SyncHook,
    AsyncParallelHook
} = require('tapable');

//创建类
class Car {
    constructor() {
        this.hooks = {
            accelerate: new SyncHook(["newSpeed"]),
            break: new SyncHook(),
            calculateRoutes: new AsyncParallelHook(["source", "target", "routesList"])
        };
    }
}

const myCar = new Car();

//绑定同步钩子
myCar.hooks.break.tap("WarningLampPlugin", () => console.log('WarningLampPlugin'));

//绑定同步钩子 并传参
myCar.hooks.accelerate.tap("LoggerPlugin", newSpeed => console.log(`Accelerating to ${newSpeed}`));

//绑定一个异步Promise钩子
myCar.hooks.calculateRoutes.tapPromise("calculateRoutes tapPromise", (source, target, routesList, callback) => {
    // return a promise
    return new Promise((resolve,reject)=>{
        setTimeout(()=>{
            console.log(`tapPromise to ${source}${target}${routesList}`)
            resolve();
        },1000)
    })
});

//执行同步钩子
myCar.hooks.break.call();
myCar.hooks.accelerate.call('hello');

console.time('cost');

//执行异步钩子
myCar.hooks.calculateRoutes.promise('i', 'love', 'tapable').then(() => {
    console.timeEnd('cost');
}, err => {
    console.error(err);
    console.timeEnd('cost');
})

результат операции

WarningLampPlugin
Accelerating to hello
tapPromise to ilovetapable
cost: 1003.898ms

Также можно использовать calculateRoutestapAsyncКрючок, примечание: используйте на этот разcallbackЗавершите асинхронный обратный вызов.

myCar.hooks.calculateRoutes.tapAsync("calculateRoutes tapAsync", (source, target, routesList, callback) => {
    // return a promise
    setTimeout(() => {
        console.log(`tapAsync to ${source}${target}${routesList}`)
        callback();
    }, 2000)
});

myCar.hooks.calculateRoutes.callAsync('i', 'like', 'tapable', err => {
    console.timeEnd('cost');
    if(err) console.log(err)
})

результат операции

WarningLampPlugin
Accelerating to hello
tapAsync to iliketapable
cost: 2007.850ms

Продвинуть это ~

Возможно, вы уже научились использовать tapable, но как это связано с плагином webapck/webpack?

Мы только что немного изменили код и разделили его на два файла: Compiler.js, Myplugin.js.

Compiler.js

  • Измените имя класса Class Car на ядро ​​веб-пакета.Compiler
  • Принимать плагины, переданные в опциях
  • Передайте компилятор в качестве параметра плагину
  • Выполните функцию запуска и на каждом этапе компиляции инициируйте выполнение соответствующей функции ловушки.
const {
    SyncHook,
    AsyncParallelHook
} = require('tapable');

class Compiler {
    constructor(options) {
        this.hooks = {
            accelerate: new SyncHook(["newSpeed"]),
            break: new SyncHook(),
            calculateRoutes: new AsyncParallelHook(["source", "target", "routesList"])
        };
        let plugins = options.plugins;
        if (plugins && plugins.length > 0) {
            plugins.forEach(plugin => plugin.apply(this));
        }
    }
    run(){
        console.time('cost');
        this.accelerate('hello')
        this.break()
        this.calculateRoutes('i', 'like', 'tapable')
    }
    accelerate(param){
        this.hooks.accelerate.call(param);
    }
    break(){
        this.hooks.break.call();
    }
    calculateRoutes(){
        const args = Array.from(arguments)
        this.hooks.calculateRoutes.callAsync(...args, err => {
            console.timeEnd('cost');
            if (err) console.log(err)
        });
    }
}

module.exports = Compiler

MyPlugin.js

  • Введение компилятора
  • Определите собственный плагин.
  • Метод применения принимает параметр компилятора.

Плагин webpack представляет собойapplyОбъект JavaScript метода.apply 属性会被 webpack compiler 调用, а объект компилятора доступен на протяжении всего времени компиляции.

  • Привязать методы к хукам компилятора.
  • Следуя правилам веб-пакета,向 plugins 属性传入 new 实例.
const Compiler = require('./Compiler')

class MyPlugin{
    constructor() {

    }
    apply(conpiler){//接受 compiler参数
        conpiler.hooks.break.tap("WarningLampPlugin", () => console.log('WarningLampPlugin'));
        conpiler.hooks.accelerate.tap("LoggerPlugin", newSpeed => console.log(`Accelerating to ${newSpeed}`));
        conpiler.hooks.calculateRoutes.tapAsync("calculateRoutes tapAsync", (source, target, routesList, callback) => {
            setTimeout(() => {
                console.log(`tapAsync to ${source}${target}${routesList}`)
                callback();
            }, 2000)
        });
    }
}


//这里类似于webpack.config.js的plugins配置
//向 plugins 属性传入 new 实例

const myPlugin = new MyPlugin();

const options = {
    plugins: [myPlugin]
}
let compiler = new Compiler(options)
compiler.run()

результат операции

Accelerating to hello
WarningLampPlugin
tapAsync to iliketapable
cost: 2015.866ms

После преобразования работает нормально.Следуя идеям плагинов Compiler и webpack, логика плагина постепенно успешно выправляется.

Другие методы Таблицы

type function
Hook суффикс для всех крючков
Waterfall Синхронный метод, но он передаст значение следующей функции
Bail Слияние: когда функция имеет какое-либо возвращаемое значение, она останавливает текущую функцию выполнения.
Loop Функция слушателя возвращает значение true, чтобы продолжить цикл, и возвращает значение undefined, чтобы завершить цикл.
Sync Синхронный метод
AsyncSeries Async последовательный крючок
AsyncParallel Хук асинхронного параллельного выполнения

Мы можем выбрать подходящие синхронные/асинхронные хуки в соответствии с нашими собственными потребностями в разработке.

процесс веб-пакета

Благодаря вышеприведенному чтению мы знаем, как монтировать перехватчики в потоке событий webapck.

Предположим теперь, что мы хотим настроить плагин для изменения содержимого последнего выходного ресурса, в какой хук мы должны добавить событие? Какой шаг может изменить ресурсы, скомпилированные webpack?

Итак, следующая задача: понять процесс webpack.

Команда Taobao опубликовала блок-схему классического веб-пакета, а затем медленно проанализировала ~

1. запись веб-пакета(webpack.config.js+shell options)

Прочитайте и объедините параметры из файла конфигурации package.json и операторов Shell, чтобы получить окончательные параметры;

Каждый раз, когда вы вводите webpack в командной строке, операционная система будет вызывать./node_modules/.bin/webpackэтот сценарий оболочки. Этот скрипт вызовет./node_modules/webpack/bin/webpack.jsИ добавьте входные параметры, такие как -p , -w .

2. Парсинг с параметром yargs(optimist)

yargs.parse(process.argv.slice(2), (err, argv, output) => {})

Адрес источника

3. инициализация веб-пакета

(1) Создайте объект компилятора

let compiler = new Webpack(options)

Адрес источника

(2) Зарегистрируйте плагин NOdeEnvironmentPlugin

new NodeEnvironmentPlugin().apply(compiler);

Адрес источника

(3) Основной плагин висит в опциях, звонитеWebpackOptionsApplyБиблиотека инициализирует базовый плагин.

if (options.plugins && Array.isArray(options.plugins)) {
	for (const plugin of options.plugins) {
		if (typeof plugin === "function") {
			plugin.apply(compiler);
		} else {
			plugin.apply(compiler);
		}
	}
}
compiler.hooks.environment.call();
compiler.hooks.afterEnvironment.call();
compiler.options = new WebpackOptionsApply().process(options, compiler);

Адрес источника

4. runначать компиляцию

if (firstOptions.watch || options.watch) {
	const watchOptions = firstOptions.watchOptions || firstOptions.watch || options.watch || {};
	if (watchOptions.stdin) {
		process.stdin.on("end", function(_) {
			process.exit(); // eslint-disable-line
		});
		process.stdin.resume();
	}
	compiler.watch(watchOptions, compilerCallback);
	if (outputOptions.infoVerbosity !== "none") console.log("\nwebpack is watching the files…\n");
} else compiler.run(compilerCallback);

Здесь есть два случая:

1) Наблюдение: отслеживание изменений в файлах

2) запустить: выполнить компиляцию

Адрес источника

5. Триггерcompile

(1) В процессе запуска сработали некоторые хуки:beforeRun->run->beforeCompile->compile->make->seal(При написании плагина можно повесить пользовательский метод на соответствующий хук и выполнять его в порядке компиляции)

(2) Создайте ключCompilationобъект

В методе run() выполняется this.compile()

Компиляция создается в this.compile()

this.hooks.beforeRun.callAsync(this, err => {
    ...
	this.hooks.run.callAsync(this, err => {
        ...
		this.readRecords(err => {
            ...
			this.compile(onCompiled);
		});
	});
});

...

compile(callback) {
	const params = this.newCompilationParams();
	this.hooks.beforeCompile.callAsync(params, err => {
		...
		this.hooks.compile.call(params);
		const compilation = this.newCompilation(params);
		this.hooks.make.callAsync(compilation, err => {
            ...
			compilation.finish();
			compilation.seal(err => {
                ...
				this.hooks.afterCompile.callAsync(compilation, err 
				    ...
					return callback(null, compilation);
				});
			});
		});
	});
}

Адрес источника

const compilation = this.newCompilation(params);

CompilationОтвечает за весь процесс компиляции, включая методы, соответствующие каждой ссылке на сборку. Ссылка на компилятор хранится внутри объекта.

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

Фокус: Компиляция важна! Это зависит от компиляции файлов преобразования производственных ресурсов.

6.addEntry() make 分析入口文件创建模块对象

Запускается при компиляцииmakeмероприятие и звонокaddEntry

В хуке make веб-пакета tapAsync регистрируетDllEntryPlugin, заключается в вызове модуля ввода через компиляцию.

Эта регистрация выполняется в методе Compiler.compile().

Метод addEntry добавляет все входные модули в очередь сборки и запускает процесс сборки.

DllEntryPlugin.js

compiler.hooks.make.tapAsync("DllEntryPlugin", (compilation, callback) => {
	compilation.addEntry(
		this.context,
		new DllEntryDependency(
			this.entries.map((e, idx) => {
				const dep = new SingleEntryDependency(e);
				dep.loc = {
					name: this.name,
					index: idx
				};
				return dep;
			}),
			this.name
		),
		this.name,
		callback
	);
});

Адрес источника

Мне кажется очень странным, что процесс пришел сюда: я только что выполнил компиляцию в Compiler.js, почему он вдруг пришел к DllEntryPlugin.js?

Это сказать передWebpackOptionsApply.process()初始化插件的时候, выполненcompiler.hooks.entryOption.call(options.context, options.entry);

WebpackOptionsApply.js

class WebpackOptionsApply extends OptionsApply {
	process(options, compiler) {
	    ...
	    compiler.hooks.entryOption.call(options.context, options.entry);
	}
}

process

entryOption

DllPlugin.js

compiler.hooks.entryOption.tap("DllPlugin", (context, entry) => {
	const itemToPlugin = (item, name) => {
		if (Array.isArray(item)) {
			return new DllEntryPlugin(context, item, name);
		}
		throw new Error("DllPlugin: supply an Array as entry");
	};
	if (typeof entry === "object" && !Array.isArray(entry)) {
		Object.keys(entry).forEach(name => {
			itemToPlugin(entry[name], name).apply(compiler);
		});
	} else {
		itemToPlugin(entry, "main").apply(compiler);
	}
	return true;
});

DllPlugin

На самом деле метод addEntry имеет много записей, а плагин SingleEntryPlugin также регистрирует хукcompiler.hooks.make.tapAsync. Здесь в основном подчеркиваетсяWebpackOptionsApply.process()Процесс (233).

Входов много, если интересно, можете отладить последовательность~

7. Строительные блоки

compilation.addEntryвыполнить в_addModuleChain()Этот метод в основном делает две вещи. Один — получить соответствующую фабрику модулей и создать модуль в соответствии с типом модуля, а другой — сконструировать модуль.

Создайте модуль с помощью метода *ModuleFactory.create (включая NormalModule, MultiModule, ContextModule, DelegatedModule и т. д.), чтобы загрузить загрузчик, используемый модулем. Вызов acorn для анализа исходного файла, обработанного загрузчиком, для создания абстрактного синтаксического дерева AST. Пройдите через AST, чтобы построить модули, от которых зависит этот модуль.

addEntry(context, entry, name, callback) {
	const slot = {
		name: name,
		request: entry.request,
		module: null
	};
	this._preparedEntrypoints.push(slot);
	this._addModuleChain(
		context,
		entry,
		module => {
			this.entries.push(module);
		},
		(err, module) => {
			if (err) {
				return callback(err);
			}

			if (module) {
				slot.module = module;
			} else {
				const idx = this._preparedEntrypoints.indexOf(slot);
				this._preparedEntrypoints.splice(idx, 1);
			}
			return callback(null, module);
		}
	);
}

адрес источника addEntry addModuleChain()

8. Запечатайте результат сборки (запечатайте)

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

template.getRenderMainfest.render()

Создание чанков в формате _webpack_requie() с помощью шаблонов (MainTemplate, ChunkTemplate).

9. Выходной ресурс (эмит)

Выходные активы на путь вывода.

Суммировать

webpack — это набор плагинов, каждый из которых управляется tapable для запуска в потоке событий webpack. Основная зависимость — это модуль компиляции и инкапсуляция компиляции.

Входной файл веб-пакета фактически создает экземпляр компилятора и вызывает метод запуска для запуска компиляции.Основная компиляция веб-пакета выполняется в следующей последовательности вызова хука.

  • Компилятор:beforeRun очищает кеш
  • Компилятор: запустить хук данных кеша регистров
  • Compiler:beforeCompile
  • Компилятор: compile начинает компиляцию
  • Compiler:make анализирует зависимые и косвенно зависимые модули от записи и создает объекты модуля.
  • Компиляция: сборка модуля buildModule
  • Компилятор: сборка normalModuleFactory
  • Компиляция: запечатанный пакет результатов сборки, не может быть изменен
  • Compiler:afterCompile завершает сборку и кэширует данные
  • Компилятор: вывод вывода в каталог dist

Объект Compilation содержит текущие ресурсы модуля, скомпилированные ресурсы, измененные файлы и т. д.

Объект Compilation также предоставляет множество обратных вызовов событий для расширений плагинов.

Более важной частью компиляции являются активы Если мы хотим использовать веб-пакет для создания файлов, нам нужно добавить соответствующую информацию о файле в assets.

CompiLATION.getStats() может получить производственные документы и некоторую информацию о Чунхаше. так далее

Настоящий бой! написать плагин

На этот раз я пытаюсь написать простой плагин, который поможет нам удалить лишние комментарии в bundle.js, сгенерированные упаковкой webpack.

Как написать плагин?

Обратитесь к официальному руководству по веб-пакету.Writing a Plugin

Плагин webpack состоит из следующих шагов:

  1. Функция класса JavaScript.
  2. Определить инъекцию в прототипе функции (prototype)compilerобъектapplyметод.
  3. applyВставьте указанный хук события через компилятор в функцию и получите объект компиляции в обратном вызове хука
  4. Измените внутренние данные экземпляра webapack, используя манипуляции с компиляцией.
  5. Асинхронный плагин, используйте обратный вызов обратного вызова после обработки данных

Завершите начальную архитектуру плагина

Говоря о точке раньше, я написал функцию класса MyPlugin, которая уже удовлетворяет первые две точки структуры плагинов WebPack (функция класса JavaScript, которая определяет инъекцию в функциональном прототипе (прототип)compiler)

Теперь нам нужно, чтобы Myplugin удовлетворял последним трем пунктам. Во-первых, используйте обработчик событий, указанный компилятором.

class MyPlugin{
    constructor() {

    }
    apply(conpiler){
        conpiler.hooks.break.tap("WarningLampPlugin", () => console.log('WarningLampPlugin'));
        conpiler.hooks.accelerate.tap("LoggerPlugin", newSpeed => console.log(`Accelerating to ${newSpeed}`));
        conpiler.hooks.calculateRoutes.tapAsync("calculateRoutes tapAsync", (source, target, routesList, callback) => {
            setTimeout(() => {
                console.log(`tapAsync to ${source}${target}${routesList}`)
                callback();
            }, 2000)
        });
    }
}

Общие объекты для плагинов

объект крюк
Compiler run,compile,compilation,make,emit,done
Compilation buildModule,normalModuleLoader,succeedModule,finishModules,seal,optimize,after-seal
Module Factory beforeResolver,afterResolver,module,parser
Module
Parser program,statement,call,expression
Template hash,bootstrap,localVars,render

Написать плагин

class MyPlugin {
    constructor(options) {
        this.options = options
        this.externalModules = {}
    }

    apply(compiler) {
        var reg = /("([^\\\"]*(\\.)?)*")|('([^\\\']*(\\.)?)*')|(\/{2,}.*?(\r|\n))|(\/\*(\n|.)*?\*\/)|(\/\*\*\*\*\*\*\/)/g
        compiler.hooks.emit.tap('CodeBeautify', (compilation)=> {
            Object.keys(compilation.assets).forEach((data)=> {
                let content = compilation.assets[data].source() // 欲处理的文本
                content = content.replace(reg, function (word) { // 去除注释后的文本
                    return /^\/{2,}/.test(word) || /^\/\*!/.test(word) || /^\/\*{3,}\//.test(word) ? "" : word;
                });
                compilation.assets[data] = {
                    source(){
                        return content
                    },
                    size(){
                        return content.length
                    }
                }
            })
        })
    }
}
module.exports = MyPlugin

Первый шаг, используя хук EMIT компилятора

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

compiler.hooks.emit.tap('xxx',(compilation)=>{})

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

При компиляции будет возвращено много внутренних объектов, и неполный снимок экрана выглядит следующим образом:

Среди них то, что нам нужно, этоcompilation.assets

assetsCompilation {
  assets:
   { 'js/index/main.js':
      CachedSource {
        _source: [Object],
        _cachedSource: undefined,
        _cachedSize: undefined,
        _cachedMaps: {} } },
  errors: [],
  warnings: [],
  children: [],
  dependencyFactories:
   ArrayMap {
     keys:
      [ [Object],
        [Function: MultiEntryDependency],
        [Function: SingleEntryDependency],
        [Function: LoaderDependency],
        [Object],
        [Function: ContextElementDependency],
     values:
      [ NullFactory {},
        [Object],
        NullFactory {} ] },
  dependencyTemplates:
   ArrayMap {
     keys:
      [ [Object],
        [Object],
        [Object] ],
     values:
      [ ConstDependencyTemplate {},
        RequireIncludeDependencyTemplate {},
        NullDependencyTemplate {},
        RequireEnsureDependencyTemplate {},
        ModuleDependencyTemplateAsRequireId {},
        AMDRequireDependencyTemplate {},
        ModuleDependencyTemplateAsRequireId {},
        AMDRequireArrayDependencyTemplate {},
        ContextDependencyTemplateAsRequireCall {},
        AMDRequireDependencyTemplate {},
        LocalModuleDependencyTemplate {},
        ModuleDependencyTemplateAsId {},
        ContextDependencyTemplateAsRequireCall {},
        ModuleDependencyTemplateAsId {},
        ContextDependencyTemplateAsId {},
        RequireResolveHeaderDependencyTemplate {},
        RequireHeaderDependencyTemplate {} ] },
  fileTimestamps: {},
  contextTimestamps: {},
  name: undefined,
  _currentPluginApply: undefined,
  fullHash: 'f4030c2aeb811dd6c345ea11a92f4f57',
  hash: 'f4030c2aeb811dd6c345',
  fileDependencies: [ '/Users/mac/web/src/js/index/main.js' ],
  contextDependencies: [],
  missingDependencies: [] }

Оптимизируйте все ресурсы CHUNK (Актив). Ресурс (Актив) будет храниться в виде Key-Valuecompilation.assets.

Третий шаг — пройтись по активам.

1) Ключом в объекте массива ресурсов является имя ресурса.В плагине Myplugin мы проходим через Object.key() и получаем его

main.css
bundle.js
index.html

2) Вызовите метод Object.source(), чтобы получить содержимое ресурса.

compilation.assets[data].source() 

3) Использовать обычные, удалять комментарии

 Object.keys(compilation.assets).forEach((data)=> {
    let content = compilation.assets[data].source() 
    content = content.replace(reg, function (word) { 
        return /^\/{2,}/.test(word) || /^\/\*!/.test(word) || /^\/\*{3,}\//.test(word) ? "" : word;
    })
});

Четвертый шаг, обновите объектcompile.assets[data]

compilation.assets[data] = {
    source(){
        return content
    },
    size(){
        return content.length
    }
}

Шаг 5. Ссылка на плагин в webpack

webpack.config.js

const path  = require('path')
const MyPlugin = require('./plugins/MyPlugin')

module.exports = {
    entry:'./src/index.js',
    output:{
        path:path.resolve('dist'),
        filename:'bundle.js'
    },
    plugins:[
        ...
        new MyPlugin()
    ]
}

Адрес плагина

использованная литература

Объявление о вакансии

ByteDance набирает сотрудников!

Описание вакансии: Front-end development (senior) ToB направление - видео облако (База: Шанхай, Пекин)

1. Отвечает за производство мультимедийных услуг, таких как аудио и видео по запросу / прямая трансляция / общение в реальном времени и создание облачных платформ для бизнеса;

2. Отвечает за мультимедийную систему качества, эксплуатацию и техническое обслуживание, построение и развитие системы;

3. Хорошо разбирается в абстрактном дизайне, инженерном мышлении, фокусируется на взаимодействии и создает максимальный пользовательский опыт.

Профессиональные требования

1. Компьютеры, связь и электронная информатика и другие смежные специальности являются предпочтительными;

2. Владение различными фронтенд-технологиями, включая HTML/CSS/JavaScript/Node.js и т. д.;

3. Глубокое понимание языка JavaScript с использованием основных сред разработки, таких как React или Vue.js;

4. Приветствуется знание Node.js, понимание Express/KOA и других фреймворков, а также опыт разработки крупномасштабных серверных программ;

5. Иметь определенное представление о пользовательском опыте, интерактивной работе и анализе потребностей пользователей, а также иметь опыт разработки продуктов или интерфейсов;

6. Предпочтение отдается участникам со своими собственными техническими продуктами, работами с открытым исходным кодом или активным сообществом с открытым исходным кодом.

Основные моменты работы

Команда видеооблачных сервисов опирается на накопленные аудио- и видеотехнологии и базовые ресурсы Douyin, Xigua Video и других продуктов, чтобы предоставлять клиентам универсальные аудио- и видеомультимедийные услуги, включая аудио- и видеопо запросу, прямую трансляцию, реальную связь времени, обработка изображений и т. д. Внутренне, как центр видеотехнологий, он обслуживает внутренний бизнес; снаружи он создает готовые аудио- и видео-мультимедийные сервисные решения для обслуживания пользователей корпоративного уровня.

У команды есть стандартизированный процесс итерации проекта и идеальная конфигурация ролей проекта; сильная техническая атмосфера, охватывает сообщество открытого исходного кода и регулярно делится, так что каждый может быстро расти вместе с бизнесом и использовать технологии, чтобы изменить мир!

способ доставки

Резюме можно отправлять напрямую по адресу:yuanyuan.wallace@bytedance.com

Вы также можете отсканировать внутренний QR-код для доставки онлайн, с нетерпением жду вашего присоединения! ~