В последнее время возникла необходимость написать SDK для апплета для мониторинга вызовов API и ошибок страниц апплета (аналогичноfundebug)
SDK, который кажется высоким, на самом деле представляет собой JS-файл, похожий на стороннюю библиотеку, которую мы вводим в нашу обычную разработку:
const moment = require('moment');
moment().format();
Модульность апплета соответствует спецификации Commonjs. То есть мне нужно предоставитьmonitor.js
файл, и файл должен поддерживать Commonjs, чтобы его можно было использовать в файле ввода апплетаapp.js
импортировать в:
// 导入sdk
const monitor = require('./lib/monitor.js');
monitor.init('API-KEY');
// 正常业务逻辑
App({
...
})
Итак, вопрос в том, как мне разработать этот SDK? (Примечание. В этой статье конкретно не обсуждается, как реализовать апплет мониторинга.)
Решений много: например, написать всю логику сразу в одномmonitor.js
файл и экспорт
module.exports = {
// 各种逻辑
}
Но, учитывая количество кода, чтобы уменьшить связанность, я все же стараюсь разбивать код на разные модули и, наконец, упаковывать все JS-файлы в один.monitor.js
. Студенты, которые использовали разработку Vue и React в обычное время, должны оценить преимущества модульной разработки.
Ниже приведена определенная структура каталогов:
Исходный код хранится в каталоге src, а каталог dist упаковывает окончательныйmonitor.js
src/main.js
Входной файл SDK
import { Engine } from './module/Engine';
let monitor = null;
export default {
init: function (appid) {
if (!appid || monitor) {
return;
}
monitor = new Engine(appid);
}
}
src/module/Engine.js
import { util } from '../util/';
export class Engine {
constructor(appid) {
this.id = util.generateId();
this.appid = appid;
this.init();
}
init() {
console.log('开始监听小程序啦~~~');
}
}
src/util/index.js
export const util = {
generateId() {
return Math.random().toString(36).substr(2);
}
}
Итак, как упаковать эту кучу js в финалmonitor.js
Файл, и программа выполняется правильно?
webpack
То, что я впервые подумал, что упасть с WebPack. В конце концов, работа часто разрабатывается, и, наконец, используется пакет.
На основе версии webpack4.x
npm i webpack webpack-cli --save-dev
Опираясь на мои неглубокие знания метафизики веб-пакетов,слезливыйЗапишите несколько строк конфигурации:webpack.config.js
var path = require('path');
var webpack = require('webpack');
module.exports = {
mode: 'development',
entry: './src/main.js',
output: {
path: path.resolve(__dirname, './dist'),
publicPath: '/dist/',
filename: 'monitor.js',
}
};
бегатьwebpack
, пакет запакован, но попробуйте внедрить его в апплет
файл входа апплетаapp.js
var monitor = require('./dist/monitor.js');
Консоль напрямую сообщает об ошибке. . .
Причина проста: упакованоmonitor.js
использовалeval
ключевое слово, а апплет не поддерживает eval.
Нам просто нужно изменить конфигурацию веб-пакетаdevtool
Только что
var path = require('path');
var webpack = require('webpack');
module.exports = {
mode: 'development',
entry: './src/main.js',
output: {
path: path.resolve(__dirname, './dist'),
publicPath: '/dist/',
filename: 'monitor.js',
},
devtool: 'source-map'
};
source-map
режим не будет использоватьсяeval
ключевое слово для облегчения отладки, оно сгенерирует еще одинmonitor.js.map
файл для облегчения отладки
опять такиwebpack
Упаковывая, а затем импортируя апплет, проблема возникает снова:
var monitor = require('./dist/monitor.js');
console.log(monitor); // {}
То, что распечатывает, является пустым объектом!
src/main.js
import { Engine } from './module/Engine';
let monitor = null;
export default {
init: function (appid) {
if (!appid || monitor) {
return;
}
monitor = new Engine(appid);
}
}
monitor.js
не экспортирует объект с помощью метода инициализации!
на что мы надеемсяmonitor.js
Он соответствует спецификации commonjs, но мы не указали это в конфигурации, поэтому файлы, упакованные webpack, ничего не экспортируют.
В нашей обычной разработке нам не нужно экспортировать переменную при упаковке, главное, чтобы запакованный файл можно было запустить сразу в браузере. Вы пролистываете проект Vue или React и видите, как написан входной файл?
main.js
import Vue from 'vue'
import App from './App'
new Vue({
el: '#app',
components: { App },
template: '<App/>'
})
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App.js';
ReactDOM.render(
<App />,
document.getElementById('root')
);
Похожа ли она на эту подпрограмму, в конце концов, она просто сразу выполняет метод, а не экспортирует переменную.
libraryTarget
libraryTarget — это ключ к проблеме.Установив это свойство, мы можем сообщить webpack, какую спецификацию использовать для экспорта переменной.
var path = require('path');
var webpack = require('webpack');
module.exports = {
mode: 'development',
entry: './src/main.js',
output: {
path: path.resolve(__dirname, './dist'),
publicPath: '/dist/',
filename: 'monitor.js',
libraryTarget: 'commonjs2'
},
devtool: 'source-map'
};
commonjs2
это спецификация commonjs, которую мы хотим
Перепакуйте, на этот раз это правильно
var monitor = require('./dist/monitor.js');
console.log(monitor);
Наши экспортированные объекты смонтированы наdefault
свойства, потому что когда мы изначально экспортировали:
export default {
init: function (appid) {
if (!appid || monitor) {
return;
}
monitor = new Engine(appid);
}
}
Теперь мы можем с радостью импортировать SDK
var monitor = require('./dist/monitor.js').default;
monitor.init('45454');
Вы могли заметить, что я не использовал babel при упаковке, потому что апплет поддерживает синтаксис es6, поэтому вам не нужно проходить его снова при разработке SDK.Если разрабатываемая вами библиотека классов должна быть совместима с браузерами, вы можно добавить babel-загрузчик
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
}
]
}
будь осторожен:
- Вы можете напрямую разрабатывать и отлаживать SDK
webpack -w
- При окончательной упаковке используйте
webpack -p
сжимать
полныйwebpack.config.js
var path = require('path');
var webpack = require('webpack');
module.exports = {
mode: 'development', // production
entry: './src/main.js',
output: {
path: path.resolve(__dirname, './dist'),
publicPath: '/dist/',
filename: 'monitor.js',
libraryTarget: 'commonjs2'
},
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
}
]
},
devtool: 'source-map' // 小程序不支持eval-source-map
};
На самом деле использовать webpack для упаковки библиотек классов на чистом JS очень просто.По сравнению с нашей обычной разработкой приложения, настройки намного меньше.Ведь нет необходимости упаковывать статические ресурсы такие как css,html, картинки , и шрифты, и нет необходимости загружать их по запросу.
rollup
На этом статью можно было бы закончить, но когда я исследовал, как упаковывать модули на ранней стадии, я специально смотрел, как упаковывается код Vue и React, оказалось, что ни один из них не использует веб-пакет, а только роллап.
Rollup — это сборщик модулей JavaScript, который компилирует небольшие фрагменты кода в большие сложные фрагменты кода, такие как библиотеки или приложения.
Официальный сайт роллапаЭто введение показывает, что накопительный пакет используется для упаковки библиотеки.
Если вам интересно, вы можете взглянуть на упакованный веб-пакетmonitor.js
, точно будут жаловаться, что это за кусок кода?
module.exports =
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
// 以下省略1万行代码
webpack реализует собственный набор__webpack_exports__
__webpack_require__
module
механизм
/***/ "./src/util/index.js":
/*!***************************!*\
!*** ./src/util/index.js ***!
\***************************/
/*! exports provided: util */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "util", function() { return util; });
const util = {
generateId() {
return Math.random().toString(36).substr(2);
}
}
/***/ })
Он оборачивает каждый файл js в функцию для получения ссылки и экспорта между модулями.
Если вы используете накопительную упаковку, вы будете удивлены, обнаружив, что читаемость упакованного кода не на том же уровне, что и в веб-пакете!
npm install --global rollup
создать новыйrollup.config.js
export default {
input: './src/main.js',
output: {
file: './dist/monitor.js',
format: 'cjs'
}
};
format: cjs
Указывает, что упакованный файл соответствует спецификации commonjs.
бегатьrollup -c
В это время он сообщит об ошибке, говоря[!] Error: Could not resolve '../util' from src\module\Engine.js
Это связано с тем, что накопительный пакет распознает../util/
, он не будет автоматически находить файлы в каталоге utilindex.js
файл (webpack будет искать его по умолчанию), поэтому нам нужно изменить его на../util/index
Упакованный файл:
'use strict';
const util = {
generateId() {
return Math.random().toString(36).substr(2);
}
};
class Engine {
constructor(appid) {
this.id = util.generateId();
this.appid = appid;
this.init();
}
init() {
console.log('开始监听小程序啦~~~');
}
}
let monitor = null;
var main = {
init: function (appid) {
if (!appid || monitor) {
return;
}
monitor = new Engine(appid);
}
}
module.exports = main;
Разве это не супер просто!
И при импорте не нужно прописывать атрибут по умолчанию упаковка веб-пакета
var monitor = require('./dist/monitor.js').default;
monitor.init('45454');
рулонная упаковка
var monitor = require('./dist/monitor.js');
monitor.init('45454');
Точно так же во время нормального развития мы можем напрямуюrollup -c -w
, а также сжимается при окончательной упаковке
npm i rollup-plugin-uglify -D
import { uglify } from 'rollup-plugin-uglify';
export default {
input: './src/main.js',
output: {
file: './dist/monitor.js',
format: 'cjs'
},
plugins: [
uglify()
]
};
Однако запуск таким образом сообщит об ошибке, потому что плагин uglify поддерживает только сжатие es5, а sdk, который я разработал на этот раз, не нужно конвертировать в es5, поэтому измените плагин
npm i rollup-plugin-terser -D
import { terser } from 'rollup-plugin-terser';
export default {
input: './src/main.js',
output: {
file: './dist/monitor.js',
format: 'cjs'
},
plugins: [
terser()
]
};
Конечно, вы также можете использовать babel для перекодирования.
npm i rollup-plugin-terser babel-core babel-preset-latest babel-plugin-external-helpers -D
.babelrc
{
"presets": [
["latest", {
"es2015": {
"modules": false
}
}]
],
"plugins": ["external-helpers"]
}
rollup.config.js
import { terser } from 'rollup-plugin-terser';
import babel from 'rollup-plugin-babel';
export default {
input: './src/main.js',
output: {
file: './dist/monitor.js',
format: 'cjs'
},
plugins: [
babel({
exclude: 'node_modules/**'
}),
terser()
]
};
UMD
Пакет SDK, который мы только что упаковали, не использует API конкретной среды, то есть этот код может выполняться на стороне узла и на стороне браузера.
Если мы хотим, чтобы упакованный код был совместим с различными платформами, нам необходимо соответствовать спецификации UMD (совместимость с AMD, CMD, Commonjs, iife)
import { terser } from 'rollup-plugin-terser';
import babel from 'rollup-plugin-babel';
export default {
input: './src/main.js',
output: {
file: './dist/monitor.js',
format: 'umd',
name: 'monitor'
},
plugins: [
babel({
exclude: 'node_modules/**'
}),
terser()
]
};
установивformat
а такжеname
, поэтому мы упаковываем егоmonitor.js
Совместимость с различными операционными средами
на стороне узла
var monitor = require('monitor.js');
monitor.init('6666');
на стороне браузера
<script src="./monitor.js"></srcipt>
<script>
monitor.init('6666');
</srcipt>
Принцип на самом деле очень прост, вы можете посмотреть упакованный исходный код или прочитать тот, который я написал ранее.статья
Суммировать
Rollup обычно подходит для упаковки библиотек классов JS.Код, упакованный с помощью rollup, имеет небольшой размер и не содержит избыточного кода. накопительный пакет по умолчанию поддерживает только модульность ES6.Если вам нужна поддержка Commonjs, вам необходимо загрузить соответствующий плагинrollup-plugin-commonjs
webpack обычно подходит для упаковки приложения, если вам нужно разделение кода (Code Splitting) или у вас есть много статических ресурсов для обработки, то рассмотрите возможность использования webpack