Этот контент авторизован и предоставлен Цао Цзюнем и его командой из отдела исследований и разработок Yinke Holdings. В команде более 10 разработчиков малых программ, глубоко культивированных в области малых программ, и подытожили эту качественную длинную статью. В то же время содержание этой статьи также было объединено с моимпроект с открытым исходным кодом, текущий контент проекта включает в себя JS, сеть, браузер, оптимизацию производительности, безопасность, фреймворк, Git, структуру данных, алгоритм и т. д., будь то базовый или расширенный, или интерпретацию исходного кода, вы можете найти его на этой карте. На собеседовании я получил удовлетворительный ответ, надеюсь, эта карта интервью поможет всем лучше подготовиться к собеседованию.
Мини программа - Вход
юнионид и опенид
Прежде чем узнать о входе в мини-программу, давайте напишем и поймем, что вход в мини-программу / официальную учетную запись включает два наиболее важных идентификатора пользователя:
-
OpenId
Это идентификатор пользователя для небольшой программы/общедоступной учетной записи, и разработчики могут идентифицировать пользователя с помощью этого идентификатора. -
UnionId
Это идентификация пользователя для одного и того же субъекта WeChat апплета/общедоступной учетной записи/приложения, и разработчику необходимо связать предмет той же учетной записи на открытой платформе WeChat. Разработчики могут использовать UnionId для обмена данными между несколькими апплетами, официальными учетными записями и даже приложениями.
Ключевые API
-
wx.login
Официально предоставленные возможности входа в систему -
wx.checkSession
Проверьте, действителен ли текущий session_key пользователя. -
wx.authorize
Инициировать запрос авторизации пользователю заранее -
wx.getUserInfo
Получить основную информацию о пользователе
Дизайн процесса входа
Ниже приведены некоторые процессы входа в систему, с которыми столкнулся автор:
Использовать существующую систему входа
Чтобы напрямую повторно использовать систему входа в существующую систему, вам нужно только разработать страницу ввода имени пользователя, пароля / кода подтверждения на стороне апплета, вы можете легко войти в систему, и вам нужно только поддерживать хороший пользовательский интерфейс.
Создать пользовательскую систему с OpenId
👆 упомянул,OpenId
Это идентификация небольшой программы для пользователя. Используя это, мы можем легко реализовать набор пользовательских систем на основе небольших программ. Стоит отметить, что эта пользовательская система наименее навязчива для пользователей и может обеспечить тихий вход в систему. Конкретные шаги заключаются в следующем:
-
Мини клиент программы через
wx.login
получить код -
Передайте код на сервер, сервер получит код и вызовет интерфейс проверки учетных данных для входа в WeChat, а сервер WeChat вернется.
openid
и сеансовый ключsession_key
, сервер разработчика может использоватьopenid
Создайте пользовательское хранилище, а затем верните настраиваемый статус входа в клиент апплета. -
кэш клиента апплета (через
storage
), чтобы настроить состояние входа в систему (токен), и вы можете использовать состояние входа в качестве идентификатора пользователя при последующем вызове интерфейса.
Создайте пользовательскую систему с Unionid
Если вы хотите реализовать обмен данными между несколькими небольшими программами, общедоступными учетными записями и существующими системами входа в систему, вы можете установить пользовательскую систему, получив пользовательский unionid. Поскольку unionid одинаков для всех приложений на одной и той же открытой платформе, черезunionid
Установленная пользовательская система может реализовать обмен данными на всей платформе, и доступ к исходным функциям более удобен.unionid
Ну, есть два пути:
-
Если пользователь подписался на общедоступную учетную запись того же субъекта или однажды выполнил авторизацию входа в WeChat в приложении или официальной учетной записи того же субъекта через
wx.login
можно получить непосредственно вunionid
-
комбинировать
wx.getUserInfo
а также<button open-type="getUserInfo"><button/>
Эти два метода позволяют пользователю активно авторизоваться, а после активной авторизации взаимодействовать с сервером посредством возвращаемой информации (здесь есть шаг, который требует от сервера расшифровки данных, что очень просто, WeChat предоставляет пример кода), чтобы получить Этоunionid
Пользовательская система установлена, а затем сервер возвращается в состояние входа в систему, и для входа можно использовать локальную запись. Лучшие практики, предоставленные WeChat, прилагаются:-
Вызовите wx.login, чтобы получить код, а затем обменяйтесь session_key с серверной частью WeChat, чтобы расшифровать конфиденциальные данные, возвращенные getUserInfo.
-
Используйте wx.getSetting для авторизации пользователя
- Если пользователь авторизован, напрямую вызовите API wx.getUserInfo, чтобы получить последнюю информацию о пользователе;
- Если пользователь не авторизован, на интерфейсе отображается кнопка, предлагающая пользователю войти в систему. Когда пользователь нажимает и авторизуется, получается самая последняя информация о пользователе.
-
После получения пользовательских данных их можно отобразить или отправить на собственный сервер.
-
Меры предосторожности
- нужно получить
unionid
Формальная система входа в систему была реализована следующим образом в прошлом (до апреля 2018 года), но последующие корректировки были внесены в WeChat (потому что как только вы входите в апплет, активно всплывают различные всплывающие окна авторизации, по сравнению с It легко привести к потере пользователей) и адаптированы к тому, как пользователи должны использовать кнопки, чтобы направлять пользователей для активной авторизации. Эта корректировка оказывает большое влияние на разработчиков. Разработчики должны обращать внимание на соблюдение правил WeChat и общение деловые формы с деловыми сторонами своевременно.Не рискуйте, чтобы не допустить, чтобы небольшая программа не рассмотрела и так далее.
wx.login(获取code) ===> wx.getUserInfo(用户授权) ===> 获取 unionid
-
Потому что апплета не существует
cookie
Концепция состояния входа в систему должна кэшироваться локально, поэтому настоятельно рекомендуется установить время истечения срока действия для состояния входа. -
Стоит отметить, что если вам необходимо поддерживать такие функции, как проверка безопасности управления рисками, многоплатформенный вход в систему и т. д., вам может потребоваться добавить некоторые общедоступные параметры, такие как платформа, канал, параметр устройства и другие параметры. При определении плана с сервером, как передовой студент, вы должны вовремя выдвигать эти разумные предложения и разрабатывать разумную систему.
-
openid
,unionid
Не отправляйте открытый текст в интерфейсе, это опасно и непрофессионально одновременно.
Мини программа - Экспорт изображений
Студенты, которые часто разрабатывают и используют небольшие программы, должны быть знакомы с этой функцией.Это распространенный способ привлечения трафика.Как правило, к картинке прикрепляется QR-код небольшой программы.
Фундаментальный
-
с помощью
canvas
элемент, поместите стили, которые необходимо экспортировать, вcanvas
Нарисуйте его на холсте (api в основном такой же, как и h5, но есть небольшие отличия, просто будьте внимательны при его использовании) -
предоставлено WeChat
canvasToTempFilePath
Экспортируйте изображение и используйте его в концеsaveImageToPhotosAlbum
(требуется авторизация) сохранить изображение на локальный
Как это сделать элегантно
В соответствии с вышеизложенными принципами реализация очень проста, это всего лишь извлечение и отрисовка эскиза проекта, но в качестве общей функции очень неудобно каждый раз писать такой кусок кода. Как апплет разрабатывает общий метод, помогающий нам экспортировать изображения? Идея заключается в следующем:
-
Этот шаг рисования необходимого стиля нельзя пропустить. Но мы можем инкапсулировать библиотеку рисования, включая рисование обычной графики, такой как прямоугольники, прямоугольники со скругленными углами, круги, сектора, треугольники, текст, изображения, чтобы уменьшить код рисования, нужно только извлечь информацию о стиле, вы можете легко рисовать, и, наконец, экспортировать изображение Сохранить в альбом. Автор считает следующий способ отрисовки более элегантным и понятным, на самом деле можно также использовать параметр типа для указания типа отрисовки, а входящий - это массив стилей для реализации отрисовки.
-
В сочетании с реализацией предыдущего шага, если есть несколько требований к экспорту для одного и того же типа карты, вы также можете использовать настраиваемый компонент для инкапсуляции того же типа карты в качестве общего компонента и ввести этот компонент, где функция экспорта требуются фотографии.
class CanvasKit {
constructor() {
}
drawImg(option = {}) {
...
return this
}
drawRect(option = {}) {
return this
}
drawText(option = {}) {
...
return this
}
static exportImg(option = {}) {
...
}
}
let drawer = new CanvasKit('canvasId').drawImg(styleObj1).drawText(styleObj2)
drawer.exportImg()
Меры предосторожности
- Сетевые картинки не могут быть отрисованы на холст в апплете, перед отрисовкой необходимо загрузить картинки в локальный временный файл через downLoadFile.
- Обычно на экспортируемом изображении необходимо нарисовать QR-код, бываютметодПри экспорте QR-кода параметры, которые необходимо передать, должны быть закодированы, и существует ограничение на определенную длину (32 видимых символа), которое может быть сгенерировано сервером.
短链接
способ решить
Мини программа - Статистика
Как широко используемый способ анализа поведения пользователей, статистика данных также важна для небольших программ. Экспозиция, полученная апплетом, и скрытая точка данных о кликах фактически совпадают с принципом h5. Однако, как требование, не связанное с бизнес-логикой, если мы добавим различный скрытый код к каждому событию клика и каждому жизненному циклу, это будет мешать нормальной бизнес-логике и сделает код раздутым.Автор предлагает следующие несколько идей для решения точка захоронения данных:
Дизайн скрытого SDK
Структура кода апплета такова, что каждая страница имеет метод страницы, который принимает функцию жизненного цикла, данные业务逻辑对象
Этот слой данных упакован, а бизнес-логика страницы реализована с помощью базовой логики апплета. Благодаря этому мы можем подумать об идее обернуть страницу один раз, изменить ее жизненный цикл и события кликов, смешать скрытый код, не вмешиваясь в бизнес-логику, просто выполнить некоторую простую настройку, чтобы спрятать точку. реализация кода выглядит следующим образом:
代码仅供理解思路
page = function(params) {
let keys = params.keys()
keys.forEach(v => {
if (v === 'onLoad') {
params[v] = function(options) {
stat() //曝光埋点代码
params[v].call(this, options)
}
}
else if (v.includes('click')) {
params[v] = funciton(event) {
let data = event.dataset.config
stat(data) // 点击埋点
param[v].call(this)
}
}
})
}
Эта идея подходит не только для скрытых точек, но и для глобальной обработки исключений, унифицированной обработки запросов и других сценариев.
Интерфейс анализа
Для какого-то особого дела мы можем взять接口埋点
, Что такое интерфейс скрытой точки? Во многих случаях некоторые из наших API вызываются не в нескольких местах, а только на определенной странице.Благодаря этой идее мы можем проанализировать, что если запрашивается интерфейс, поведение срабатывает, и сервис можно использовать полностью. терминальный журнал используется для получения данных о захороненных точках, но этот метод имеет большие ограничения и относится к процессу получения результатов анализа, могут быть ошибки, но его можно использовать как идею для понимания.
Анализ пользовательских данных WeChat
Сам WeChat предоставляет возможности анализа данных.Сам WeChat предоставляет два метода анализа данных: обычный анализ и пользовательский анализ, который можно настроить в фоновом режиме апплета. с помощью小程序数据助手
Эту небольшую программу можно легко просмотреть.
Мини-программа - Инженерия
что делает инженерия
Инжиниринг является неотъемлемой частью текущего процесса разработки интерфейса.Что необходимо сделать для проектирования небольших программ?Давайте сначала рассмотрим проблемы, которые необходимо решить в текущей разработке небольших программ:
- Не поддерживает прекомпилятор css, как основное решение css, будь то меньше, sass, стилус может повысить эффективность css
- Не поддерживает введение пакетов npm (этот, как я слышал из общедоступного класса WeChat, WeChat готов поддерживать)
- Последующие функции js, такие как ES7, не поддерживаются, и такие функции, как простой в использовании асинхронный ожидание, не могут быть использованы.
- Не поддерживает импорт файлов внешних шрифтов, поддерживает только base64
- Нет инструментов проверки кода, таких как eslint
Выбор схемы
Для широко используемых в настоящее время инженерных решений веб-пакеты, роллапы, парцеллы и т. д. обычно используются при упаковке и обработке одностраничных приложений, в то время как небольшие программы по своей сути являются «многостраничными приложениями» и имеют некоторые специфические конфигурации. В соответствии с решаемой задачей это не что иное, как компиляция, модификация и копирование файлов.Для этих требований мы думаем о потоковойgulp
Он отлично подходит для обработки, и гораздо проще настроить многостраничные приложения, чем веб-пакет. Поэтому рекомендуется использовать инженерное решение малых программ.gulp
Конкретные идеи развития
Реализовано через задачу gulp:
- Скомпилируйте файл less в реальном времени в соответствующий каталог
- Внедрить файлы среды выполнения, поддерживающие асинхронность и ожидание.
- Скомпилируйте файл шрифта в base64 и сгенерируйте соответствующий файл css для удобства использования.
- Анализ зависимостей, в котором упоминается пакет npm, введите пакет npm в файл и скопируйте его в соответствующий каталог.
- Проверьте спецификации кода
Вышеупомянутая реализация на самом деле не сложная, но в данном случае это просто скрипт сборки gulp и согласованная директория.Каждый раз, когда новый апплет приходит копировать этот скрипт, чтобы разобраться с ним? Явно неуместно, как это можно реализовать?小程序工程化
Шерстяная ткань?
Нам могут понадобиться простые леса, леса должны поддерживать функции:
- Поддержите новый проект, создайте страницу, создайте компонент
- Поддержка встроенных скриптов сборки
- Он поддерживает публикацию небольших программ, и вы также можете найти способы доступа к таким инструментам, как Jenkins, для непрерывной интеграции (непрерывная интеграция небольших программ будет упомянута позже). ...
архитектура апплета
Каркас апплета WeChat состоит из двух частей: уровня просмотра и уровня логики службы приложений. Слой View используется для визуализации структуры страницы, а уровень AppService используется для логической обработки, запросов данных и вызовов интерфейса.
они вв две нитибегать.
они вв две нитибегать.
они вв две нитибегать.
Уровень представления и уровень логики взаимодействуют через JSBridage системного уровня, уровень логики уведомляет об изменении данных уровень представления, инициирует обновление страницы уровня представления, а уровень представления уведомляет о инициированном событии на уровне логики для обработка бизнеса.
Пополнить
Уровень представления визуализируется с помощью WebView, встроенный WKWebView используется в iOS, а ядро Tencent x5 (на основе Blink) используется в Android.
Логический уровень работает с использованием встроенного JCore в iOS и ядра Tencent x5 (на основе Blink) в Android.
Инструмент разработки использует nw.js для обеспечения среды выполнения как уровня представления, так и уровня логики.
Используйте js-beautify для пакетного форматирования кода инструмента разработки WeChat @v1.02.1808080 под Mac:
cd /Applications/wechatwebdevtools.app/Contents/Resources/package.nw
find . -type f -name '*.js' -not -path "./node_modules/*" -not -path -exec js-beautify -r -s 2 -p -f '{}' \;
существуетjs/extensions/appservice/index.js
найти в:
267: function(a, b, c) {
const d = c(8),
e = c(227),
f = c(226),
g = c(228),
h = c(229),
i = c(230);
var j = window.__global.navigator.userAgent,
k = -1 !== j.indexOf('game');
k || i(), window.__global.getNewWeixinJSBridge = (a) => {
const {
invoke: b
} = f(a), {
publish: c
} = g(a), {
subscribe: d,
triggerSubscribeEvent: i
} = h(a), {
on: j,
triggerOnEvent: k
} = e(a);
return {
invoke: b,
publish: c,
subscribe: d,
on: j,
get __triggerOnEvent() {
return k
},
get __triggerSubscribeEvent() {
return i
}
}
}, window.WeixinJSBridge = window.__global.WeixinJSBridge = window.__global.getNewWeixinJSBridge('global'), window.__global.WeixinJSBridgeMap = {
__globalBridge: window.WeixinJSBridge
}, __devtoolsConfig.online && __devtoolsConfig.autoTest && setInterval(() => {
console.clear()
}, 1e4);
try {
var l = new window.__global.XMLHttpRequest;
l.responseType = 'text', l.open('GET', `http://${window.location.host}/calibration/${Date.now()}`, !0), l.send()
} catch (a) {}
}
существуетjs/extensions/gamenaitveview/index.js
найти в:
299: function(a, b, c) {
'use strict';
Object.defineProperty(b, '__esModule', {
value: !0
});
var d = c(242),
e = c(241),
f = c(243),
g = c(244);
window.WeixinJSBridge = {
on: d.a,
invoke: e.a,
publish: f.a,
subscribe: g.a
}
},
существуетjs/extensions/pageframe/index.js
найти в:
317: function(a, b, c) {
'use strict';
function d() {
window.WeixinJSBridge = {
on: e.a,
invoke: f.a,
publish: g.a,
subscribe: h.a
}, k.a.init();
let a = document.createEvent('UIEvent');
a.initEvent('WeixinJSBridgeReady', !1, !1), document.dispatchEvent(a), i.a.init()
}
Object.defineProperty(b, '__esModule', {
value: !0
});
var e = c(254),
f = c(253),
g = c(255),
h = c(256),
i = c(86),
j = c(257),
k = c.n(j);
'complete' === document.readyState ? d() : window.addEventListener('load', function() {
d()
})
},
Мы все видели определение WeixinJSBridge. как естьon
,invoke
,publish
,subscribe
несколько ключевых методов.
братьinvoke
Например, вjs/extensions/appservice/index.js
Нашел этот код в:
f (!r) p[b] = s, f.send({
command: 'APPSERVICE_INVOKE',
data: {
api: c,
args: e,
callbackID: b
}
});
существуетjs/extensions/pageframe/index.js
Нашел этот код в:
g[d] = c, e.a.send({
command: 'WEBVIEW_INVOKE',
data: {
api: a,
args: b,
callbackID: d
}
})
Простой анализ показывает, что: полеcommand
используется для дифференциации поведения,invoke
Раньше API называл Native. Используйте разные префиксы в разных источниках.data
Он содержит имя и параметры API. Кроме тогоcallbackID
Указывает дескриптор метода, который принимает обратный вызов. Протокол связи, используемый Appservice и Webview, одинаков.
Мы не можем использовать BOM и DOM в нашем коде, потому что у нас их нет, а с другой стороны, мы не хотим, чтобы JS-код напрямую манипулировал представлениями.
в инструментах разработчикаremote-helper.js
Нашел этот код в:
const vm = require("vm");
const vmGlobal = {
require: undefined,
eval: undefined,
process: undefined,
setTimeout(...args) {
//...省略代码
return timerCount;
},
clearTimeout(id) {
const timer = timers[id];
if (timer) {
clearTimeout(timer);
delete timers[id];
}
},
setInterval(...args) {
//...省略代码
return timerCount;
},
clearInterval(id) {
const timer = timers[id];
if (timer) {
clearInterval(timer);
delete timers[id];
}
},
console: (() => {
//...省略代码
return consoleClone;
})()
};
const jsVm = vm.createContext(vmGlobal);
// 省略大量代码...
function loadCode(filePath, sourceURL, content) {
let ret;
try {
const script = typeof content === 'string' ? content : fs.readFileSync(filePath, 'utf-8').toString();
ret = vm.runInContext(script, jsVm, {
filename: sourceURL,
});
}
catch (e) {
// something went wrong in user code
console.error(e);
}
return ret;
}
Такой многоуровневый дизайн явно преднамеренный, его средний слой полностью контролирует работу программы на интерфейсе, а также может отслеживать передаваемые данные и время отклика. С одной стороны, поведение программы сильно ограничено, а с другой стороны, WeChat может гарантировать, что у них есть абсолютный контроль над содержанием и взаимодействием с мини-программой.
Эта структура также показывает, что API анимации и рисования апплета предназначен для генерации конечного объекта, а не для пошагового выполнения, причина в том, что передача и парсинг данных в формате Json дороже по сравнению с нативным API. вызовы, вероятно, потребляют слишком много производительности, что, в свою очередь, влияет на взаимодействие с пользователем.
Скачать полный пакет апплета
App Service - Life Cylce
вопросы интервью
1. Анимация должна быть привязана к данным, а рисунок — нет. Как вы думаете, почему?
var context = wx.createCanvasContext('firstCanvas')
context.setStrokeStyle("#00ff00")
context.setLineWidth(5)
context.rect(0, 0, 200, 200)
context.stroke()
context.setStrokeStyle("#ff0000")
context.setLineWidth(2)
context.moveTo(160, 100)
context.arc(100, 100, 60, 0, 2 * Math.PI, true)
context.moveTo(140, 100)
context.arc(100, 100, 40, 0, Math.PI, false)
context.moveTo(85, 80)
context.arc(80, 80, 5, 0, 2 * Math.PI, true)
context.moveTo(125, 80)
context.arc(120, 80, 5, 0, 2 * Math.PI, true)
context.stroke()
context.draw()
Page({
data: {
animationData: {}
},
onShow: function(){
var animation = wx.createAnimation({
duration: 1000,
timingFunction: 'ease',
})
this.animation = animation
animation.scale(2,2).rotate(45).step()
this.setData({
animationData:animation.export()
})
}
})
2. Использует ли запрос Http Request апплета API Fetch браузера?
Проверка точки знаний
- Знайте, что Request реализован Native
- В JCore нет Http Request, Websocket, Storage и других функций, которые привносит Webkit.
- апплет
wx.request
Реализовано ли оно в соответствии со спецификацией fetch API? Ответ, очевидно, нет. потому что безPromise
View - WXML
w XML (Wei X на языке разметки)
- Поддержка привязки данных
- Поддержка логической арифметики, операции
- Шаблоны, ссылки поддерживаются
- Поддержка добавления событий (bindtap)
Компилятор Wxml: Wcc преобразует файлы Wxml в JS
Метод выполнения: Wcc index.wxml
Используйте Virtual DOM для локальных обновлений
View - WXSS
WXSS(WeiXin Style Sheets)
Компилятор wxss: wcsc преобразует файлы wxss в js
Метод выполнения: wcsc index.wxss
Поддерживает большинство функций CSS
Про-тест включает, но не ограничивается следующим:
- Transition
- Animation
- Keyframes
- border-radius
- calc()
- селектор, кромеофициальная документацияПеречисленный, по сути, также поддерживает
- element>element
- element+element
- element element
- element:first-letter
- element:first-line
- element:first-child
- element:last-child
- element~element
- element:first-of-type
- element:last-of-type
- element:only-of-type
- element:only-child
- element:nth-child(n)
- element:nth-last-child(n)
- element:nth-of-type(n)
- element:nth-last-of-type(n)
- :root
- element:empty
- :not(element)
- iconfont
Рекомендуется попробовать все возможности CSS3.
единица размера rpx
rpx (отзывчивый пиксель): может быть адаптивным в зависимости от ширины экрана. Указанная ширина экрана составляет 750 пикселей. формула:
const dsWidth = 750
export const screenHeightOfRpx = function () {
return 750 / env.screenWidth * env.screenHeight
}
export const rpxToPx = function (rpx) {
return env.screenWidth / 750 * rpx
}
export const pxToRpx = function (px) {
return 750 / env.screenWidth * px
}
оборудование | rpx в px (ширина экрана/750) | px в rpx (750/ширина экрана) |
---|---|---|
iPhone5 | 1rpx = 0.42px | 1px = 2.34rpx |
iPhone6 | 1rpx = 0.5px | 1px = 2rpx |
iPhone6 Plus | 1rpx = 0.552px | 1px = 1.81rpx |
можно понятьpr2rpx-loader эта библиотека.
импорт стиля
использовать@import
оператор может импортировать внешние таблицы стилей,@import
Далее следует относительный путь к внешней таблице стилей, которую необходимо импортировать, с;
Указывает конец оператора.
встроенный стиль
Статические стили единообразно пишутся в классе. style получает динамические стили и анализирует их во время выполнения,Старайтесь не записывать статические стили в style, чтобы не влиять на скорость рендеринга..
Глобальные и локальные стили
Стили, определенные в app.wxss, являются глобальными стилями, которые применяются к каждой странице. Стили, определенные в файле wxss страницы, являются локальными стилями, которые применяются только к соответствующей странице и переопределяют тот же селектор в app.wxss.
iconfont
По состоянию на 20180810 год
Апплет планирует в будущем поддерживать шрифты. Ссылаться наОткрытый класс WeChat.
Разработка мини-программ аналогична обычной веб-разработке, также можно использовать значки шрифтов, ноsrc:url()
Ни локальный, ни удаленный адрес работать не будут, может отображаться значение base64.
Преобразование файлов ttf в base64. открыть эту платформуTransfonter.org/. нажмитеКнопка «Добавить шрифты», чтобы загрузить этот файл в формате ttf. Измените кодировку base64 ниже на on. Нажмите кнопку «Преобразовать», чтобы преобразовать, и нажмите «Загрузить», чтобы загрузить после преобразования.
Скопируйте содержимое stylesheet.css из загруженного сжатого файла в font.wxss, скопируйте весь код style.css в icomoon, кроме @font-face, в font.wxss и замените селектор i на .iconfont, и, наконец:
<text class="iconfont icon-home" style="font-size:50px;color:red"></text>
View - Component
Апплет предоставляет ряд компонентов для разработки бизнес-функций. Сравнение между функциями и тегами HTML5 выглядит следующим образом:
Компоненты апплета основаны на стандарте веб-компонентов.
Реализация веб-компонентов с помощью Polymer Framework
View - Native Component
В настоящее время Native реализует следующие компоненты:
-
cavnas
-
video
-
map
-
textarea
Слой собственного компонента находится над слоем WebView. В настоящее время это создает некоторые проблемы:
- Компоненты, реализованные Native, будут блокировать другие компоненты.
- Когда представление, отображаемое WebView, прокручивается, компонентам, реализованным Native, необходимо обновить позицию, что приведет к проблемам с производительностью, что более очевидно на машинах Android.
- Нативные компоненты мини-программы
cover-view
Он может перекрывать видео cavnas и т. д., но также имеет некоторые недостатки, такие как перезапись на cavnas.cover-view
, вы обнаружите, что система координат не является единообразной, чтобы справиться с проблемами
Проблемы или ограничения текущего апплета
По состоянию на 20180810 год
В том числе, но не ограничивается:
-
Апплеты по-прежнему отображаются с использованием WebView, а не собственного рендеринга. (частично родной)
-
Заголовок, возвращаемый интерфейсом сервера, не может быть выполнен, например: Set-Cookie.
-
Библиотеки JS, зависящие от среды браузера, использовать нельзя.
-
Вы не можете использовать npm, но вы можете создавать свои собственные инструменты сборки или использовать mpvue. (Официальная поддержка планируется в будущем)
-
Вы не можете использовать ES7, вы можете использовать babel+webpack для создания собственного или использовать mpvue.
-
Использование собственного шрифта не поддерживается (официальная поддержка планируется в будущем).
-
Вы можете использовать iconfont в формате base64.
-
Апплет нельзя отправить в круг друзей (вы можете сохранить картинку локально и отправить картинку своим друзьям. Можно использовать QR-кодинтерфейс В).
-
ПолучатьQR-код/мини-программаОграничения интерфейса.
- Сцена интерфейса B Максимум 32 видимых символа.
- Общее количество кодов, генерируемых интерфейсом AC, ограничено 100 000, звоните с осторожностью.
- Сканирование QR-кода на реальном устройстве позволяет перейти только к онлайн-версии, поэтому в тестовой среде отладку можно выполнить только путем компиляции QR-кода с помощью инструмента разработчика.
- Путь к странице мини-программы, который не опубликован в онлайн-версии, приведет к сбою генерации QR-кода.Вам необходимо сначала опубликовать мини-программу с добавленной страницей в онлайн-версии.
-
Мини-программа Push может использовать только «Уведомление о службе» и требует, чтобы пользователи активно инициировали отправку formId, которая действительна только в течение 7 дней. (Текущая практика заключается в том, чтобы поместить форму на каждую страницу и скрыть ее, чтобы получить больше formId. Принцип внутреннего использования: отдавать приоритет той, у которой самый короткий срок действия)
-
Ограничение размера апплета составляет 2M, а общий размер подпакета не превышает 8M.
-
Апплет пересылки (совместного использования) не может получить успешный результат, но может.Связь(Зло мини-игры)
-
Получение одного и того же unionId должно быть привязано к одной и той же открытой платформе. Ограничения привязки к открытой платформе:
- 50 мобильных приложений
- 10 сайтов
- 50 публичных аккаунтов одной тематики
- 5 различных основных публичных аккаунтов
- 50 мини-программ с одним и тем же основным корпусом
- 5 различных основных мини-программ
-
Апплет, связанный с официальной учетной записью,Связь
- Все официальные учетные записи могут быть связаны с мини-программами.
- Одна официальная учетная запись может быть связана с 10 мини-программами с одной и той же тематикой и 3 мини-программами с разными темами.
- Небольшая программа может быть связана с 500 официальными аккаунтами.
- Официальная учетная запись может добавлять 13 новых связанных мини-программ в месяц, а мини-программа может быть заново связана 500 раз в месяц.
-
10 мини-программ с одинаковым основным телом и 3 мини-программы с одинаковым основным телом, связанные с одним официальным аккаунтом, могут переходить друг к другу
-
Поиск бренда не поддерживает финансовые, медицинские
-
Авторизация в мини-программе требует, чтобы пользователи активно нажимали
-
Апплет не предоставляет тестовaccess_token
-
В системе Android после авторизации апплета для получения информации о пользователе удалите апплет и получите его снова, а также повторно авторизуйте его для получения старой подписи, что приведет к сбою первой авторизации.
-
В инструменте разработчика после авторизации для получения информации о пользователе, если вы решите очистить все кеши, даже если используется wx.checkSession, и в течение срока действия session_key авторизованный для получения информации о пользователе получит новый session_key
Поддержка апплета HTTP2
Поддержка HTTP2: ни эмулятор, ни поддержка реальной машины
Чтобы проверить поддержку и адаптацию апплета к HTTP, я нашел два сервера для тестирования, один — сервер, поддерживающий HTTP2, найденный в Интернете, а другой — сервер HTTP2, который я запустил локально. Все методы запроса в тесте используютсяwx.request
.
-
Онлайн-серверы, поддерживающие HTTP2:
HTTPs://www.snel.com:443
-
Просмотр сервера как HTTP2 в Chrome
-
Запросить интерфейс на эмуляторе,
请求头
Версия HTTP — HTTP1.1, эмулятор не поддерживает HTTP2. -
Поскольку онлайн-среда апплета требует настройки доменного имени запроса в управлении проектом, а это доменное имя не является нужным нам доменным именем запроса, нет необходимости тратить местонахождение доменного имени впустую, поэтому откройте параметр не проверять доменное имя, TSL и другие параметры для запроса этого интерфейса и показа его с помощью инструмента захвата пакетов, аналогичного эмулятору.
Сервер HTTP2 должен быть совместим с апплетом.
Из вышеизложенного видно, что ни реальная машина, ни симулятор не поддерживают HTTP2, но все они успешно запрашиваются, и响应头
Версия HTTP в файле стала версией HTTP1.1, что указывает на то, что сервер выполнил адаптацию совместимости к HTTP1.1.
-
Запустите новый сервер узла локально и верните JSON в качестве HTTP-версии запроса.
-
Если сервер поддерживает только HTTP2,
ALPN
Ошибка протокола. И напомните использовать адаптацию HTTP1 -
когда сервер
allowHTTP1
,Установить какtrue
, и после обработки соответствующих параметров запроса во время запроса симулятор может нормально получить доступ к интерфейсу и распечатать соответствующую версию HTTP-запроса.
Процесс авторизации для получения информации о пользователе
- Session_key имеет срок действия, и срок действия не сообщается разработчику, мы знаем только, что чем чаще пользователь использует апплет, тем дольше будет срок действия session_key.
- При вызове wx.login session_key будет обновлен напрямую, в результате чего старый session_key станет недействительным.
- В апплете сначала вызовите wx.checkSession, чтобы проверить статус входа в систему и убедитесь, что не просроченный session_key не будет обновлен, а затем вызовите wx.login для получения кода. Затем пользователь разрешает аплету получить информацию о пользователе, апплет получает зашифрованные данные пользователя и передает зашифрованные данные и код во внутреннюю службу. Серверная часть получает session_key через код, расшифровывает данные и возвращает расшифрованную информацию о пользователе в апплет.
Вопрос интервью: что происходит, когда я сначала разрешаю получить информацию о пользователе, а затем вхожу в систему?
- Когда пользователь авторизуется, открытая платформа использует старый session_key для шифрования информации о пользователе. Вызов wx.login для повторного входа обновит session_key.В это время серверная служба получает новый session_key с открытой платформы, но не может расшифровать данные, зашифрованные старым session_key, и получение информации о пользователе не удается.
- Как насчет вызова wx.checkSession до авторизации информации пользователя? wx.checkSession проверяет статус входа и гарантирует, что wx.login не обновит session_key, чтобы серверная служба могла правильно расшифровать данные. Однако здесь есть проблема: если апплет не приводит к истечению срока действия session_key в течение длительного времени, wx.login обязательно перегенерирует session_key, что приведет к повторному сбою расшифровки информации о пользователе.
оптимизация производительности
Мы знаем, что часть представления выполняется в веб-представлении, поэтому большинство оптимизаций в области внешнего интерфейса полезны.
Мы знаем, что часть представления выполняется в веб-представлении, поэтому большинство оптимизаций в области внешнего интерфейса полезны.
Мы знаем, что часть представления выполняется в веб-представлении, поэтому большинство оптимизаций в области внешнего интерфейса полезны.
Оптимизация нагрузки
Размер пакета кода является самым непосредственным фактором, влияющим на скорость загрузки и запуска апплета. Чем больше пакет кода, тем больше скорость загрузки и больше время внедрения бизнес-кода. Таким образом, лучший способ оптимизации — уменьшить размер пакета кода.
Представление трех этапов загрузки апплета.
Метод оптимизации
- Сжатие кода.
- Своевременно очищайте бесполезный код и файлы ресурсов.
- Уменьшите размер и количество файлов ресурсов, таких как изображения, в пакете кода.
- Загрузка подпакета.
Ознакомьтесь с предложениями по оптимизации для загрузки в верхней части страницы.
- Ранний запрос: асинхронным запросам данных не нужно ждать рендеринга страницы.
- Использовать кэш: используйте API хранилища для кэширования данных асинхронного запроса и используйте кэшированные данные для отображения страницы при втором запуске и обновления ее в фоновом режиме.
- Избегайте белого экрана: сначала отображайте страницу скелета страницы и основной контент.
- Своевременная обратная связь: давайте немедленные отзывы об интерактивных операциях, которые требуют от пользователей ожидания, чтобы пользователи не думали, что апплет не отвечает.
Используйте оптимизацию загрузки подпакетов
При построении субконтрактного проекта апплета сборка выводит один или несколько подпакетов функций, в которых каждый апплет подпакета должен содержать основной пакет, так называемый основной пакет, то есть размещать начальную страницу/страницу TabBar по умолчанию и некоторые все подпакеты должны использовать общедоступные ресурсы/JS-скрипты, а подпакеты делятся в соответствии с конфигурацией разработчика.
При запуске апплета будет загружен основной пакет по умолчанию и будет запущена страница в основном пакете.Если пользователю нужно открыть страницу в подпакете, клиент загрузит соответствующий подпакет и отобразит его после загрузки полный.
преимущество:
- Для разработчиков это может увеличить размер кода апплета и нести больше функций и сервисов.
- Для пользователей апплет может открываться быстрее, и можно использовать больше функций, не влияя на скорость запуска.
лимит:
- Размер всех подпакетов всего апплета не должен превышать 8M
- Размер одного подпакета/основного пакета не может превышать 2M
Конфигурация загрузки собственного подпакетаПредположим, что структура каталогов вспомогательного подпакета апплета выглядит следующим образом:
├── app.js
├── app.json
├── app.wxss
├── packageA
│ └── pages
│ ├── cat
│ └── dog
├── packageB
│ └── pages
│ ├── apple
│ └── banana
├── pages
│ ├── index
│ └── logs
└── utils
Разработчики объявляют структуру подпакетов проекта в поле subPackages app.json:
{
"pages":[
"pages/index",
"pages/logs"
],
"subPackages": [
{
"root": "packageA",
"pages": [
"pages/cat",
"pages/dog"
]
}, {
"root": "packageB",
"pages": [
"pages/apple",
"pages/banana"
]
}
]
}
Принцип субподряда
- После объявления subPackages он будет упакован в соответствии с путем конфигурации subPackages, а каталоги за пределами пути конфигурации subPackages будут упакованы в приложение (основной пакет).
- Приложение (основной пакет) также может иметь свои собственные страницы (то есть поле самых внешних страниц).
- Корневой каталог подпакета не может быть подкаталогом другого подпакета.
- Страница TAB домашней страницы должна быть в приложении (основной пакет)
Принцип цитирования
- packageA не может требовать JS-файлы packageB, но может требовать приложения и JS-файлы в своем собственном пакете.
- packageA не может импортировать шаблон packageB, но может потребовать приложение, шаблон в своем собственном пакете
- packageA не может использовать ресурсы packageB, но может использовать ресурсы в приложении и собственном пакете
Официально скороПредварительная загрузка подпакета
Независимый субподряд
Оптимизация производительности рендеринга
-
Каждый вызов setData представляет собой процесс обмена данными между процессами, а затраты на связь положительно связаны с объемом данных setData.
-
setData инициирует обновление содержимого страницы слоя представления. Эта трудоемкая операция заблокирует взаимодействие с пользователем на определенный период времени.
-
setData чаще всего используется при разработке апплетов, а также чаще всего вызывает проблемы с производительностью.
Избегайте неправильного использования setData
- Используйте данные для обмена данными между методами,Может увеличить объем данных, передаваемых setData.. данные должны включать только данные, относящиеся к рендерингу страницы.
- Использование setData для передачи большого объема данных положительно влияет на время передачи данных, а задержка обновления страницы может увеличить накладные расходы на обновление страницы. ** Передавайте только те данные, которые изменились на странице, используйте специальный ключ setData для частичного обновления.
- Частые вызовы setData в течение короткого промежутка времени, ** зависание работы, задержка взаимодействия, блокировка связи, задержка рендеринга страницы. ** Избегайте ненужных setData, объединяйте последовательные вызовы setData.
- Выполните setData на фоновой странице, чтобы вытеснить ресурсы рендеринга главной страницы. **Вызов setData после переключения страницы в фоновый режим откладывается до повторного отображения страницы.
Избегайте неправильного использования onPageScroll
- Прислушивайтесь к событиям pageScroll только при необходимости. Если за ним не следить, он не будет распространяться.
- Избегайте сложной логики в onPageScroll
- Избегайте частого вызова setData в onPageScroll
- Избегайте частых запросов информации об узле (SelectQuery) при скольжении, чтобы определить, следует ли отображать ее.В некоторых сценариях вместо этого рекомендуется использовать монитор состояния резинового макета узла (inersectionObserver).
Используйте пользовательские компоненты
В сценариях, которые требуют частых обновлений, обновление пользовательских компонентов выполняется только внутри компонента и не зависит от сложности других частей страницы.
Планирование технических возможностей официальной мини-программы
Пользовательские компоненты 2.0
Между несколькими страницами апплета есть некоторые одинаковые или похожие области, в это время логика этих областей может быть инкапсулирована в пользовательский компонент, а код может использоваться повторно, или для относительно независимой логики он также может быть инкапсулирован в самоопределяемый компонент.Определяемые компоненты, то есть пользовательские компоненты, выпущенные WeChat в прошлом году, позволяют повторно использовать код, уменьшают объем кода, более удобны для модульности, оптимизируют организацию структуры кода, а также делают модули ясно и лучше поддерживать позже, тем самым обеспечивая лучшую производительность.
Но WeChat намерен запустить Custom Component 2.0 на основе оригинала, который будет иметь более продвинутую производительность:
- План usingComponents поддерживает глобальные определения и определения с подстановочными знаками: это означает, что все пользовательские компоненты в каталоге можно импортировать пакетами без необходимости их повторного определения на каждой странице.
- Планируется поддержка таких функций, как Computed и Watch, что может сделать логику кода более понятной.
- Планируется поддержка плагина конструктора компонентов, который позволяет при создании экземпляра пользовательского компонента добавлять некоторую логику на этом этапе конструктора для облегчения некоторых расширений и даже может быть расширен до синтаксиса Vue.
поддержка нпм
В настоящее время проблема разработки апплета заключается в том, что компоненты с открытым исходным кодом необходимо вручную копировать в проект, а последующие компоненты обновления также требуют ручных операций. В ближайшем будущем апплет будет поддерживать управление пакетами npm. Благодаря этому становится очень просто внедрять некоторые проекты с открытым исходным кодом. Просто объявите его в проекте, а затем установите его с помощью простой команды, и вы можете его использовать.
Официальные пользовательские компоненты
Команда апплета WeChat заявила, что рассматривает возможность запуска некоторых официальных пользовательских компонентов, почему бы не встроить их в базовую библиотеку? Поскольку встроенные компоненты должны быть предоставлены разработчикам, этот компонент должен представлять собой возможность, которую разработчикам трудно или невозможно реализовать. Поэтому они предпочитают инкапсулировать их в пользовательские компоненты.На основе этих встроенных компонентов они хотят инкапсулировать некоторые общие компоненты со сложной логикой взаимодействия для всех, чтобы упростить разработку для всех. Как и в случае с компонентом экрана маркеров, разработчикам не нужно обращать внимание на то, как перемещается экран маркеров, что может снизить затраты разработчиков на разработку.
В то же время они также хотят предоставить разработчикам некоторые спецификации и некоторые шаблоны, чтобы разработчики могли создавать простые в использовании пользовательские компоненты, которые могут быть лучше использованы всеми.
Добавить рейтинг опыта
Когда апплет загружается слишком медленно, это может привести к потере пользователей, и разработчик апплета может столкнуться с дилеммой, не зная, как найти проблему или как ее решить.
С этой целью мини-программа вскоре запустит функцию оценки опыта, которая поможет разработчикам проверить плохой опыт мини-программы и в то же время даст рекомендации по оптимизации.
Нативные компоненты визуализируются на том же слое
При первоначальном техническом выборе апплета была введена концепция собственных компонентов, потому что собственные компоненты могут расширить возможности апплета, такие как карты, аудио и видео возможности, но собственные компоненты визуализируются клиентом изначально, что приводит к Уровень нативных компонентов самый высокий, и разработчики могут легко столкнуться с проблемой открытия отладки и обнаружить, что видеокомпонент заблокирован на vConsole.
Чтобы решить эту проблему, WeChat в то время сделал переходное решение: обложку. cover-view можно использовать поверх нативных компонентов, и этот набор решений решает большинство сценариев спроса. Например, на видео компоненте много кнопок, заголовков и даже анимированных заграждений, все это реализовано с помощью cover-view, но это все равно не решает полностью опыт разработки нативных компонентов, потому что у cover-view есть некоторые ограничения:
- Не может быть визуализирован с другими компонентами
- нет событий полного касания
- Обложка ведет себя иначе, чем стили
- cover-view недостаточно хорошо поддерживает стили
Поэтому в WeChat решили заменить обложку на такой же слой рендеринга.Его можно использовать как обычные компоненты.Уровень нативных компонентов уже не самый высокий,а рендерится на том же уровне,что и другие ненативные компоненты,которые можно полностью контролировать с помощью z-index., который полностью поддерживает сенсорные события.
В WeChat сообщили, что рендеринг одного слоя начал внутреннее тестирование на апплете платформы iOS и скоро будет открыт для разработчиков Платформа Android добилась прорывного прогресса и в настоящее время выполняет этап работы по упаковке, и открытие не за горами. .
wepy vs mpvue
управление потоком данных
По сравнению с традиционной структурой апплета это всегда было тем, что мы, как старшие разработчики, ожидали решить.В веб-разработке, с появлением нескольких инструментов потока данных, таких как Flux, Redux и Vuex, мы также ожидаем, что в малом бизнесе комплексы, используемые в программе.
-
WePY по умолчанию поддерживает Redux, который может быть встроен, когда скаффолдинг генерирует проект.
-
Mpvue, как портированная версия Vue, разумеется, поддерживает Vuex, а также может быть встроен, когда скаффолдинг генерирует проекты.
составной
Если вы, как и мы, сталкивались с бизнес-разработкой небольших программ с нуля, рекомендуется прочитать главу [Компонентная разработка небольших программ] для разработки библиотеки компонентов официальной грамматики (начиная с базовой библиотеки 1.6.3, официальный компонент предоставляется решение).
- Подобно Vue, WePY реализует однофайловые компоненты. Самая большая разница заключается в суффиксе файла .wpy, но есть и различия в способе написания. Подробнее см. в главе [Mainstream Framework Use Case 1: WePY]. это определенная стоимость обучения, но это будет очень быстро.
export default class Index extends wepy.page {}
- Как портированная версия Vue, Mpvue поддерживает однофайловые компоненты.Шаблон, сценарий и стиль находятся в файле .vue, который похож на vue, поэтому учащимся, знакомым с разработкой Vue, будет удобнее.
Инжиниринг
Вся разработка апплета опирается на официально предоставленные инструменты разработчика. Инструмент разработчика прост и интуитивно понятен, что очень удобно для отладки апплетов.Теперь он также поддерживает Tencent Cloud (мы еще не использовали его, но он все еще полезен для некоторых новых разработчиков).Вы можете подать заявку на отчет о тестировании. чтобы увидеть, как апплет движется в реальном времени. Производительность и работающий эффект на устройстве, но у него нет концепций и инструментов для разработки интерфейса.
- Wepy имеет встроенную сборку, и проект инициализируется с помощью команды wepy init.Общий процесс выглядит следующим образом:
- wepy-cli определит, находится ли шаблон на удаленном складе или локальном, если он локальный, то сразу перейдет к шагу 3, иначе продолжит.
- Шаблон будет загружен из удаленного репозитория и сохранен локально.
- Задайте разработчику название проекта и другие вопросы и создайте проект на основе ответов разработчика.
- mpvue следует за webpack, который высоко ценится в vue, как инструмент сборки, но также предоставляет некоторые собственные плагины и некоторые модификации файлов конфигурации, такие как:
- html-webpack-plugin больше не требуется
- На основе webpack-dev-middleware, измененного на webpack-dev-middleware-hard-disk
- Самое большое изменение связано с изменением webpack-loader на mpvue-loader.
- Однако метод настройки остается тем же: файлы конфигурации среды в конечном итоге будут скомпилированы в структуру каталогов и суффиксы файлов, поддерживаемые апплетом.
Всестороннее сравнение
Контраст\Рамка | Апплет WeChat | mpvue | wepy |
---|---|---|---|
Спецификация грамматики | Технические требования к разработке мини-программы | vue.js | класс vue.js |
Коллекция этикеток | Апплеты | html + апплет | Апплеты |
гид по стилю | wxss | sass,less,postcss | sass,less,styus |
составной | Нет механизма компонентизации | спецификация vue | Спецификация пользовательских компонентов |
Мультиплексирование | не многоразовый | поддержка h5 | поддержка h5 |
строить автоматически | Нет автоматической сборки | webpack | рама встроенная |
Стоимость начала работы | новое обучение | обучение | вью и вепи |
управление данными | не поддерживается | vuex | redux |
Личное мнение о выборе
Вывод первый: выбирайте mpvue.
wepy против mpvue.
причина:
ИнжинирингПоскольку нативная разработка не имеет инженерных разработок, таких как пакеты NPM (которые будут представлены в будущем), ES7, сжатие изображений, PostCss, pug, ESLint и т. д., нельзя использовать. Если вы хотите построить свою собственную разработку, лучше использовать непосредственно wepy или mpvue. И mpvue, и wepy можно смешивать с нативной разработкой небольших программ.Обратитесь к mpvue-echart,обратитесь к Wepy. Проблема в том, что wepy не вводит вебпак (wepy@2.0.x до сих пор не вводится), а все вышеперечисленное приходится строить на колесах (автор или он сам). Отсутствие Webpack — серьезный недостаток. Поддерживаемый сообществом зрелый Webpack, очевидно, более стабилен и имеет больше колес.
поддерживатьWepy также поддерживается сообществом, это официально? По сути, основным разработчиком wepy является только автор, аcontrubutorsСвязь. Кроме того, быть завербованным чиновником также более позднее дело.Кроме того, у Tencent должна быть энергия, чтобы помочь поддерживать wepy вместе.Почему бы не тратить энергию на нативную разработку небольших программ? Давайте взглянем на mpvue, который поддерживается фронтенд-командой Meituan.
стоимость обученияVue имеет относительно плоскую кривую обучения. mpvue — это подмножество Vue. Таким образом, стоимость обучения mpvue будет ниже, чем у wepy. Особенно те, кто раньше научился использовать Vue в стеке технологий.
план на будущееmpvue уже поддерживает веб и апплеты. Поскольку mpvue основан на AST, в будущем он может поддерживать апплет Alipay и быстрое приложение. У них тоже есть такой план.
Пожалуйста, найдите его самостоятельно под пулом спроса
ямаУ обоих есть свои ямы. Но я думаю, что есть некоторые слезливые ямы, которые нельзя терпеть. НапримерСписок, полученный с использованием вычисленного в компоненте повтора, представляет собой все тот же набор данныхИ 1.x не может это решить. Имея опыт разработки полных небольших программ как для wepy, так и для mpvue, я думаю, что у wepy больше ям, и некоторые из ям wepy не могут быть решены из-за архитектурного дизайна.
mpvue
Версия апплета Vue.js, ответвление от vuejs/vue@2.4.1, сохраняет возможности среды выполнения vue и добавляет поддержку платформы апплета.
mpvue
это использованиеVue.js
Интерфейсный фреймворк для разработки небольших программ. каркас, основанный наVue.js
основной,mpvue
отредактированоVue.js
Реализация среды выполнения и компилятора апплета позволяет ему работать в среде апплета, тем самым вводя полный набор для разработки апплета.Vue.js
опыт разработки.
Рамочный принцип
два основных направления
- пройти через
mpvue
Предоставляет апплет адаптации во время выполнения для mp - пройти через
mpvue-loader
Создайте файловую структуру и содержимое модуля, необходимые для апплета WeChat.
семь конкретных вопросов
Чтобы понять принцип mpvue, необходимо понять принцип Vue, который является основной предпосылкой. Но для ясного объяснения принципа Vue требуется много места, лучше сослаться наlearnVue.
Теперь предположим, что у вас есть общее представление о том, как работает Vue.
Поскольку Vue использует Virtual DOM, Virtual DOM может работать на любой платформе, поддерживающей язык JavaScript, например, Vue в настоящее время поддерживает платформу браузера, weex или mp (небольшая программа). Так как же в конечном итоге виртуальный DOM сопоставляется с реальными узлами DOM? Vue сделал адаптационный слой для платформы, см. платформу браузераruntime/node-ops.js, платформа weex см.runtime/node-ops.js, см. апплетruntime/node-ops.js. Различные платформы предоставляют один и тот же интерфейс наружу через уровень адаптации.Когда виртуальный DOM работает с узлами реального DOM, ему нужно только вызывать интерфейсы этих уровней адаптации, и внутреннюю реализацию не нужно беспокоить.Он будет меняться в зависимости от платформа.и изменить.
Таким образом, идея должна состоять в том, чтобы добавить среду выполнения платформы mp. Но проблема в том, что апплет не умеет работать с DOM, поэтому под mpnode-ops.js
Реализация внутри непосредственноreturn obj
.
Необходимо сделать патч между новым виртуальным DOM и старым виртуальным DOM, чтобы найти разницу. Как обновить вид diff после патчинга, то есть как добавить в эти DOM атрибуты attr, class, style и прочие DOM-атрибуты? В Vue есть концепция nextTick для обновления представления, mpvue для небольших программ.setData
Как с этим обращаться?
Другой вопрос, как сгенерировать виртуальный DOM апплета? То есть, как скомпилировать шаблон вrender function
. Это также включаетсреда выполнения только для компилятора, очевидно, если вы хотите повысить производительность, уменьшить размер пакета, вывести wxml, mpvue также предоставляет возможность предварительной компиляции. Поскольку wxml предварительно напечатан и модель DOM не может быть изменена динамически, динамические компоненты, пользовательские рендеры и<script type="text/x-template">
Шаблоны строк и т.п. не поддерживаются (Ссылаться на).
Есть и другие вопросы, и, наконец, подведены итоги.
- 1. Как предварительно скомпилировать и сгенерировать
render function
- 2. Как прекомпилировать для генерации wxml, wxss, wxs
- 3. Как исправить разницу
- 4. Как обновить вид
- 5. Как установить небольшой механизм прокси-сервера события программы и вызвать соответствующий ответ на событие компонента vue в функции прокси-сервера события.
- 6. Как связать экземпляр vue с экземпляром страницы апплета
- 7. Как установить связь между апплетом и жизненным циклом vue, которая может запустить жизненный цикл vue в жизненном цикле апплета.
Структура каталогов платформы/mp
.
├── compiler //解决问题1,mpvue-template-compiler源码部分
├── runtime //解决问题3 4 5 6 7
├── util //工具方法
├── entry-compiler.js //mpvue-template-compiler的入口。package.json相关命令会自动生成mpvue-template-compiler这个package。
├── entry-runtime.js //对外提供Vue对象,当然是mpvue
└── join-code-in-build.js //编译出SDK时的修复
Следующий контент шаг за шагом ответит на эти вопросы, и вы поймете принцип.
mpvue-loader
mpvue-loaderдаvue-loaderРасширенная версия , похожая на отношение надмножества, за исключениемvue-loaderПомимо собственных возможностей, он также используетmpvue-template-compilerгенерироватьrender function
.
- entry
это начнется сwebpack
Запускает запись в конфигурации, анализирует зависимые модули и упаковывает их отдельно. В записи атрибут приложения и его содержимое будут упакованы как app.js/app.json/app.wxss, необходимые для апплета WeChat, а остальные сгенерируют соответствующую страницу page.js/page.json/page.wxml. /page .wxss, такой как запись примера, создаст следующие файлы, содержание файла будет описано позже:
// webpack.config.js
{
// ...
entry: {
app: resolve('./src/main.js'), // app 字段被识别为 app 类型
index: resolve('./src/pages/index/main.js'), // 其余字段被识别为 page 类型
'news/home': resolve('./src/pages/news/home/index.js')
}
}
// 产出文件的结构
.
├── app.js
├── app.json
├──· app.wxss
├── components
│ ├── card$74bfae61.wxml
│ ├── index$023eef02.wxml
│ └── news$0699930b.wxml
├── news
│ ├── home.js
│ ├── home.wxml
│ └── home.wxss
├── pages
│ └── index
│ ├── index.js
│ ├── index.wxml
│ └── index.wxss
└── static
├── css
│ ├── app.wxss
│ ├── index.wxss
│ └── news
│ └── home.wxss
└── js
├── app.js
├── index.js
├── manifest.js
├── news
│ └── home.js
└── vendor.js
- wxml
Каждый
.vue
Все компоненты будут сгенерированы как шаблон спецификации wxml, а затем переиспользованы через синтаксис импорта спецификации wxml.При этом, если компонент включает в себя данные данных реквизита, мы также сделаем соответствующую обработку, например :
<template>
<div class="my-component" @click="test">
<h1>{{msg}}</h1>
<other-component :msg="msg"></other-component>
</div>
</template>
<script>
import otherComponent from './otherComponent.vue'
export default {
components: { otherComponent },
data () {
return { msg: 'Hello Vue.js!' }
},
methods: {
test() {}
}
}
</script>
Часть шаблона такого компонента Vue сгенерирует соответствующий файл wxml.
<import src="components/other-component$hash.wxml" />
<template name="component$hash">
<view class="my-component" bindtap="handleProxy">
<view class="_h1">{{msg}}</view>
<template is="other-component$hash" wx:if="{{ $c[0] }}" data="{{ ...$c[0] }}"></template>
</view>
</template>
Вы могли заметить, что other-component(:msg="msg") преобразуется в . Во время выполнения mpvue объединит все данные экземпляра компонента в древовидные данные из корневого компонента, а затем передаст setData в appData,$c
сокращение от $детей. Что касается 0, то это отметка, обработанная нашим компилятором, которая будет отмечать конкретную неповторяющуюся отметку для каждого подкомпонента. Структура данных дерева выглядит следующим образом:
// 这儿数据结构是一个数组,index 是动态的
{
$child: {
'0'{
// ... root data
$child: {
'0': {
// ... data
msg: 'Hello Vue.js!',
$child: {
// ...data
}
}
}
}
}
}
- wxss
Обработка этой части мало чем отличается от обработки паутины.Единственное отличие состоит в том, что .css генерируется конфигурацией как .wxss.Некоторая обработка css подробно описана в документах postcss-mpvue-wxss и Введение в px2rpx-loader.
приложение.json/page.json 1.1.1 и выше
Рекомендуется поместить app.json/page.json в запись страницы и использовать copy-webpack-plugin, чтобы скопировать его в соответствующее место генерации, как в апплете.
1.1.1 и ниже
Эта часть содержимого исходит из входных файлов приложения и страницы, обычно main.js, вам нужно экспортировать default { config: {} } в ваш входной файл, это может быть распознано нашим загрузчиком как конфигурация, вам нужно Запись в json-файл.
import Vue from 'vue';
import App from './app';
const vueApp = new Vue(App);
vueApp.$mount();
// 这个是我们约定的额外的配置
export default {
// 这个字段下的数据会被填充到 app.json / page.json
config: {
pages: ['static/calendar/calendar', '^pages/list/list'], // Will be filled in webpack
window: {
backgroundTextStyle: 'light',
navigationBarBackgroundColor: '#455A73',
navigationBarTitleText: '美团汽车票',
navigationBarTextStyle: '#fff'
}
}
};
В то же время в это время мы будем автоматически заполнять поле страниц в app.json в соответствии с данными страницы записи. Поле страниц также можно настраивать, согласовано, что страницы, начинающиеся с символа ^, будут располагаться вверху массива.
стиль ограничен Способ работы со стилем в vue-loader состоит в том, чтобы добавить атрибут к каждому стилю, чтобы отметить идентификатор модуля, а затем добавить [идентификатор модуля] к каждому правилу в css, что, наконец, может сформировать «функцию css». Доменное пространство».
Селектор attr в настоящее время не поддерживается в апплете WeChat, поэтому мы внесли небольшое изменение и записали [module-id] в attr непосредственно в класс следующим образом:
<!-- .vue -->
<template>
<div class="container">
// ...
</div>
</template>
<style scoped>
.container {
color: red;
}
</style>
<!-- vue-loader -->
<template>
<div class="container" data-v-23e58823>
// ...
</div>
</template>
<style scoped>
.container[data-v-23e58823] {
color: red;
}
</style>
<!-- mpvue-loader -->
<template>
<div class="container data-v-23e58823">
// ...
</div>
</template>
<style scoped>
.container.data-v-23e58823 {
color: red;
}
</style>
- compiler
Создаваемый контент:
(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
// mpvue-template-compiler会利用AST预编译生成一个render function用以生成Virtual DOM。
var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;
// _c创建虚拟节点,参考https://github.com/Meituan-Dianping/mpvue/blob/master/packages/mpvue/index.js#L3606
// 以及https://github.com/Meituan-Dianping/mpvue/blob/master/packages/mpvue/index.js#L3680
return _c('div', {
staticClass: "my-component"
}, [_c('h1', [_vm._v(_vm._s(_vm.msg))]), _vm._v(" "), _c('other-component', {
attrs: {
"msg": _vm.msg,
"mpcomid": '0'
}
})], 1)
}
// staticRenderFns的作用是静态渲染,在更新时不会进行patch,优化性能。而staticRenderFns是个空数组。
var staticRenderFns = []
render._withStripped = true
var esExports = { render: render, staticRenderFns: staticRenderFns }
/* harmony default export */ __webpack_exports__["a"] = (esExports);
if (false) {
module.hot.accept()
if (module.hot.data) {
require("vue-hot-reload-api").rerender("data-v-54ad9125", esExports)
}
}
/***/ })
compiler
Связанные с компилятором, то есть предварительная компиляция шаблона, вы можете обратиться к "Разговор о компиляции шаблонов Vue" чтобы понять. Принцип тот же.
mpvue реализовал это самexport { compile, compileToFunctions, compileToWxml }
(Связь)вcompileToWxml
используется для генерации wxml, специального кодаВ этот.
Кроме того, mpvue не нужно предоставлятьвремя выполнения - компиляторДа, хотя это теоретически возможно. Поскольку апплет не может манипулировать DOM, даже если предоставляется компилятор времени выполнения, интерфейс не может быть сгенерирован.
Подробно опишите процесс компиляции:
1. Разберите файл vue в объект шаблона.
// mpvue-loader/lib/loader.js
var parts = parse(content, fileName, this.sourceMap)
Если исходный код файла vue выглядит следующим образом:
<template>
<view class="container-bg">
<view class="home-container">
<home-quotation-view v-for="(item, index) in lists" :key="index" :reason="item.reason" :stockList="item.list" @itemViewClicked="itemViewClicked" />
</view>
</view>
</template>
<script lang="js">
import homeQuotationView from '@/components/homeQuotationView'
import topListApi from '@/api/topListApi'
export default {
data () {
return {
lists: []
}
},
components: {
homeQuotationView
},
methods: {
async loadRankList () {
let {data} = await topListApi.rankList()
if (data) {
this.dateTime = data.dt
this.lists = data.rankList.filter((item) => {
return !!item
})
}
},
itemViewClicked (quotationItem) {
wx.navigateTo({
url: `/pages/topListDetail/main?item=${JSON.stringify(quotationItem)}`
})
}
},
onShow () {
this.loadRankList()
}
}
</script>
<style lang="stylus" scoped>
.container-bg
width 100%
height 100%
background-color #F2F4FA
.home-container
width 100%
height 100%
overflow-x hidden
</style>
передачаparse(content, fileName, this.sourceMap)
Результат, полученный функцией, примерно следующий:
{
template: {
type: 'template',
content: '\n<view class="container-bg">\n <view class="home-container">\n <home-quotation-view v-for="(item, index) in lists" :key="index" :reason="item.reason" :stockList="item.list" @itemViewClicked="itemViewClicked" />\n </view>\n</view>\n',
start: 10,
attrs: {},
end: 251
},
script: {
type: 'script',
content: '\n\n\n\n\n\n\n\n\nimport homeQuotationView from \'@/components/homeQuotationView\'\nimport topListApi from \'@/api/topListApi\'\n\nexport default {\n data () {\n return {\n lists: []\n }\n },\n components: {\n homeQuotationView\n },\n methods: {\n async loadRankList () {\n let {data} = await topListApi.rankList()\n if (data) {\n this.dateTime = data.dt\n this.lists = data.rankList.filter((item) => {\n return !!item\n })\n }\n },\n itemViewClicked (quotationItem) {\n wx.navigateTo({\n url: `/pages/topListDetail/main?item=${JSON.stringify(quotationItem)}`\n })\n }\n },\n onShow () {\n this.loadRankList()\n }\n}\n',
start: 282,
attrs: {
lang: 'js'
},
lang: 'js',
end: 946,
...
},
styles: [{
type: 'style',
content: '\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n.container-bg\n width 100%\n height 100%\n background-color #F2F4FA\n\n.home-container\n width 100%\n height 100%\n overflow-x hidden\n\n',
start: 985,
attrs: [Object],
lang: 'stylus',
scoped: true,
end: 1135,
...
}],
customBlocks: []
}
2. Вызовите интерфейс, экспортированный mpvue-loader/lib/template-compiler/index.js, и передайте полученный выше html-шаблон:
var templateCompilerPath = normalize.lib('template-compiler/index')
...
var defaultLoaders = {
html: templateCompilerPath + templateCompilerOptions,
css: options.extractCSS
? getCSSExtractLoader()
: styleLoaderPath + '!' + 'css-loader' + cssLoaderOptions,
js: hasBuble ? ('buble-loader' + bubleOptions) : hasBabel ? babelLoaderOptions : ''
}
// check if there are custom loaders specified via
// webpack config, otherwise use defaults
var loaders = Object.assign({}, defaultLoaders, options.loaders)
- Вызовите интерфейс компиляции mpvue/packages/mpvue-template-compiler/build.js:
// mpvue-loader/lib/template-compiler/index.js
var compiled = compile(html, compilerOptions)
Метод компиляции создает следующий шаблон ast (абстрактное синтаксическое дерево), функцию рендеринга и staticRenderFns.
{
ast: {
type: 1,
tag: 'view',
attrsList: [],
attrsMap: {
class: 'container-bg'
},
parent: undefined,
children: [{
type: 1,
tag: 'view',
attrsList: [],
attrsMap: {
class: 'home-container'
},
parent: {
type: 1,
tag: 'view',
attrsList: [],
attrsMap: {
class: 'container-bg'
},
parent: undefined,
children: [
[Circular]
],
plain: false,
staticClass: '"container-bg"',
static: false,
staticRoot: false
},
children: [{
type: 1,
tag: 'home-quotation-view',
attrsList: [{
name: ':reason',
value: 'item.reason'
}, {
name: ':stockList',
value: 'item.list'
}, {
name: '@itemViewClicked',
value: 'itemViewClicked'
}],
attrsMap: {
'v-for': '(item, index) in lists',
':key': 'index',
':reason': 'item.reason',
':stockList': 'item.list',
'@itemViewClicked': 'itemViewClicked',
'data-eventid': '{{\'0-\'+index}}',
'data-comkey': '{{$k}}'
},
parent: [Circular],
children: [],
for: 'lists',
alias: 'item',
iterator1: 'index',
key: 'index',
plain: false,
hasBindings: true,
attrs: [{
name: 'reason',
value: 'item.reason'
}, {
name: 'stockList',
value: 'item.list'
}, {
name: 'eventid',
value: '\'0-\'+index'
}, {
name: 'mpcomid',
value: '\'0-\'+index'
}],
events: {
itemViewClicked: {
value: 'itemViewClicked',
modifiers: undefined
}
},
eventid: '\'0-\'+index',
mpcomid: '\'0-\'+index',
static: false,
staticRoot: false,
forProcessed: true
}],
plain: false,
staticClass: '"home-container"',
static: false,
staticRoot: false
}],
plain: false,
staticClass: '"container-bg"',
static: false,
staticRoot: false
},
render: 'with(this){return _c(\'view\',{staticClass:"container-bg"},[_c(\'view\',{staticClass:"home-container"},_l((lists),function(item,index){return _c(\'home-quotation-view\',{key:index,attrs:{"reason":item.reason,"stockList":item.list,"eventid":\'0-\'+index,"mpcomid":\'0-\'+index},on:{"itemViewClicked":itemViewClicked}})}))])}',
staticRenderFns: [],
errors: [],
tips: []
}
Результатом выполнения функции рендеринга является возвратVNode
объект, по сутиrender
Функция должна выглядеть так:
(function() {
with(this){
return _c('div',{ //创建一个 div 元素
attrs:{"id":"app"} //div 添加属性 id
},[
_m(0), //静态节点 header,此处对应 staticRenderFns 数组索引为 0 的 render 函数
_v(" "), //空的文本节点
(message) //三元表达式,判断 message 是否存在
//如果存在,创建 p 元素,元素里面有文本,值为 toString(message)
?_c('p',[_v("\n "+_s(message)+"\n ")])
//如果不存在,创建 p 元素,元素里面有文本,值为 No message.
:_c('p',[_v("\n No message.\n ")])
]
)
}
})
один из них_c
это объект vuecreateElement
метод (создать элемент),_m
даrenderStatic
(рендеринг статических узлов),_v
даcreateTextVNode
(создать текстовый дом),_s
даtoString
(преобразовано в строку)
// src/core/instance/render.js
export function initRender (vm: Component) {
...
// bind the createElement fn to this instance
// so that we get proper render context inside it.
// args order: tag, data, children, normalizationType, alwaysNormalize
// internal version is used by render functions compiled from templates
vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
// normalization is always applied for the public version, used in
// user-written render functions.
vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)
...
}
...
Vue.prototype._s = toString
...
Vue.prototype._m = renderStatic
...
Vue.prototype._v = createTextVNode
...
- Вызовите метод compileWxml для создания шаблона wxml.Этот метод в конечном итоге вызовет метод compileToWxml файла mpvue/packages/mpvue-template-compiler/build.js для преобразования шаблона, скомпилированного на первом этапе, в шаблон wxml апплета.
// mpvue-loader/lib/template-compiler/index.js
compileToWxml.call(this, compiled, html)
Вышеупомянутые ответы на вопросы 1 и 2
runtime
.
├── events.js //解答问题5
├── index.js //入口提供Vue对象,以及$mount,和各种初始化
├── liefcycle //解答问题6、7
├── node-ops.js //操作真实DOM的相关实现,因为小程序不能操作DOM,所以这里都是直接返回
├── patch.js //解答问题3
└── render.js //解答问题4
используется с вьюcreatePatchFunction
Чтобы быть последовательным, старое дерево и новое дерево по-прежнему исправлены для создания diff, но есть дополнительная строка this.$updateDataToMP() для обновления.
два основных методаinitDataToMP
,updateDataToMP
.
initDataToMP
Соберите данные на виртуальной машине, а затем вызовите пример страницы апплетаsetData
оказывать.
updateDataToMP
В каждом патче, то есть при изменении данных в зависимости от коллекции (см. код patch.js), эта часть тоже будет использоватьсяnextTick
и очередь. Дроссельная заслонка наконец-то используетсяthrottleSetData
. 50 миллисекунд используются для управления частотой, чтобы решить проблему с производительностью, вызванную частой модификацией данных, что приведет к передаче большого объема данных.
вcollectVmData
наконец использовалиformatVmData
. Особого внимания заслуживает примечание:
getVmData здесь получает все данные в текущем компоненте, включая реквизиты, вычисляемые данные
Мы также знаем, что связь между сервисом и представлением осуществляется между двумя потоками.Если данные содержат большое количество данных, объем передаваемых данных будет увеличен, а стоимость передачи будет увеличена, что приведет к снижению производительности.
Как говорится на официальном сайте, здесь используйтеeventTypeMap
сделал каждыйнамек на события
import { getComKey, eventTypeMap } from '../util/index'
// 用于小程序的 event type 到 web 的 event
export const eventTypeMap = {
tap: ['tap', 'click'],
touchstart: ['touchstart'],
touchmove: ['touchmove'],
touchcancel: ['touchcancel'],
touchend: ['touchend'],
longtap: ['longtap'],
input: ['input'],
blur: ['change', 'blur'],
submit: ['submit'],
focus: ['focus'],
scrolltoupper: ['scrolltoupper'],
scrolltolower: ['scrolltolower'],
scroll: ['scroll']
}
использовалhandleProxyWithVue
метод проксирования событий апплета для событий vue.
Также посмотрите на собственные комментарии автора к этой частиидеи
Механизм брокера событий: обновление данных, инициированное взаимодействием с пользователем, выполняется через механизм прокси-сервера событий. В коде Vue.js функция ответа на событие соответствует методу компонента, и Vue.js автоматически поддерживает контекст. Однако в апплете нет подобного механизма, и поскольку среда выполнения Vue.js поддерживает виртуальный DOM в реальном времени, который полностью соответствует уровню представления апплета, мы думаем, после того, как событие сработает на компоненте апплета node , пока соответствующий узел в виртуальном DOM найден и соответствующее событие инициировано, не завершено ли оно; с другой стороны, если ответ на событие Vue.js запускает обновление данных, его функция жизненного цикла обновляется автоматически запускаться, и функция будет обновляться синхронно. Небольшие программные данные, также реализована синхронизация данных.
getHandle
Этот метод должен быть таким, как сказал автор: найти соответствующий узел, а затем найти дескриптор.
существуетinitMP
В методе создайте приложение и страницу апплета самостоятельно. Чтобы реализовать методы, связанные с жизненным циклом, используйтеcallHook
Агент совместим с жизненным циклом приложения и страницы апплета.
Жизненный цикл официальной документацииОн сказал:
То же, что и vue, разница в том, что мы запускаем жизненный цикл монтирования vue после того, как апплет onReady
Проверьте эту часть,onReady
будет выполнен послеnext
,этоnext
Обратный вызов в конечном итоге vuemountComponent
. допустимыйindex.jsвидел в. Эта часть кода также решает «запуск жизненного цикла vue в жизненном цикле апплета».
export function initMP (mpType, next) {
// ...
global.Page({
// 生命周期函数--监听页面初次渲染完成
onReady () {
mp.status = 'ready'
callHook(rootVueVM, 'onReady')
next()
},
})
// ...
}
Когда апплет находится в состоянии onShow, используйте $nextTick для рендеринга данных в первый раз, см. упомянутый выше файл render.js.
export function initMP (mpType, next) {
// ...
global.Page({
// 生命周期函数--监听页面显示
onShow () {
mp.page = this
mp.status = 'show'
callHook(rootVueVM, 'onShow')
// 只有页面需要 setData
rootVueVM.$nextTick(() => {
rootVueVM._initDataToMP()
})
},
})
// ...
}
Когда mpvue-loader создает шаблон, например событие клика@click
станетbindtap="handleProxy"
, привязка события будет использоватьhandleProxy
Сюда.
можно посмотреть вышеmpvue-loaderОглядываться.
В конце концов, handleProxy вызывается в event.jshandleProxyWithVue
.
export function initMP (mpType, next) {
// ...
global.Page({
handleProxy (e) {
return rootVueVM.$handleProxyWithVue(e)
},
})
// ...
}
Наконец, index.js отвечает за различные инициализации и монтирования.
Почему Class и Style в настоящее время не поддерживают компоненты
Причина: Текущий компонент реализован с использованием тега шаблона апплета.Класс и стиль, указанные для компонента, монтируются в теге шаблона, а тег шаблона не поддерживает атрибуты класса и стиля.
Решение. Привяжите класс или стиль к свойству props пользовательского компонента.
// 组件ComponentA.vue
<template>
<div class="container" :class="pClass">
...
</div>
</template>
<script>
export default {
props: {
pClass: {
type: String,
default: ''
}
}
}
</script>
<!--PageB.vue-->
<template>
<component-a :pClass="cusComponentAClass" />
</template>
<script>
data () {
return {
cusComponentAClass: 'a-class b-class'
}
}
</script>
<style lang="stylus" scoped>
.a-class
border red solid 2rpx
.b-class
margin-right 20rpx
</style>
Но с этим есть проблема, после добавления стиля в scoped код, сгенерированный при компиляции шаблона, выглядит следующим образом:
.a-class.data-v-8f1d914e {
border: #f00 solid 2rpx;
}
.b-class.data-v-8f1d914e {
margin-right 20rpx
}
Поэтому, если вы хотите, чтобы классы этих компонентов вступили в силу, вы не можете использовать стиль с ограниченной областью действия. Измените его на следующее. Лучше всего использовать префикс a-class и b-class самостоятельно, чтобы другие файлы не ссылались на эти стили:
<style lang="stylus">
.a-class
border red solid 2rpx
.b-class
margin-right 20rpx
</style>
<style lang="stylus" scoped>
.other-class
border red solid 2rpx
...
</style>
- Привяжите свойство стиля к свойству props в определяющем компоненте:
<!--P组件ComponentA.vue-->
<template>
<div class="container" :style="pStyle">
...
</div>
</template>
<script>
export default {
props: {
pStyle: {
type: String,
default: ''
}
}
}
</script>
<!--PageB.vue-->
<template>
<component-a :pStyle="cusComponentAStyle" />
</template>
<script>
const cusComponentAStyle = 'border:red solid 2rpx; margin-right:20rpx;'
data () {
return {
cusComponentAStyle
}
}
</script>
<style lang="stylus" scoped>
...
</style>
Вы также можете определить styleObject, а затем преобразовать его в styleString с помощью служебных функций следующим образом:
const bstyle = {
border: 'red solid 2rpx',
'margin-right': '20rpx'
}
let arr = []
for (let [key, value] of Object.entries(bstyle)) {
arr.push(`${key}: ${value}`)
}
const cusComponentAStyle = arr.join('; ')
- Конечно, пользовательский компонент обязательно изменит только определенный стиль css, передав значение одного стиля через pros, а затем привязав его через :style, это определенно не проблема:
<!--组件ComponentA.vue-->
<template>
<div class="container" :style="{'background-color': backgroundColor}">
...
</div>
</template>
<script>
export default {
props: {
backgroundColor: {
type: String,
default: 'yellow'
}
}
}
</script>
<!-- PageB.vue -->
<template>
<component-a backgroundColor="red" />
</template>
Загрузка подпакета
модификация package.json
- Обновление: "mpvue-loader": "^1.1.2-rc.4" "webpack-mpvue-asset-plugin": "^0.1.1"
- Добавлено: "относительно": "^3.0.2"
Меры предосторожности
- 1.1.2-rc.5 Устранена проблема, связанная с неправильным созданием пути к файлу слота.
- Версия 1.1.x не очень стабильна, рекомендуется временно использовать версию 1.0.x для проектов с повышенными требованиями к стабильности.
Переместите содержимое, связанное с конфигурацией, в src/main.js в main.json (новый) в том же каталоге.
export default {
// config: {...} 需要移动
}
to
{
"pages": [
"pages/index/main",
"pages/logs/main"
],
"subPackages": [
{
"root": "pages/packageA",
"pages": [
"counter/main"
]
}
],
"window": {...}
}
руководство по настройке и обновлению webpack
- Целью этого обновления является корректировка структуры каталогов сгенерированных файлов.Для зависимых файлов исходный абсолютный путь следует изменить на относительный.
- mpvue-loader@1.1.2-rc.4 зависит от webpack-mpvue-asset-plugin@0.1.0 для ссылки на ресурс
- Для информации о конфигурации, записанной ранее в main.js, вам необходимо создать новый файл main.json в том же каталоге, что и main.js, и использовать webapck-copy-plugin, чтобы скопировать его в каталог сборки.
- Изображения, указанные в app.json, не будут автоматически скопированы в каталог dist. Файл конфигурации json копируется плагином webapck-copy-plugin и не будет обрабатывать зависимости.Вы можете поместить изображение в статический каталог корневого каталога и использовать плагин webapck-copy-plugin для копирования прошлого.
build/webpack.base.conf.js
+var CopyWebpackPlugin = require('copy-webpack-plugin')
+var relative = require('relative')
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
-function getEntry (rootSrc, pattern) {
- var files = glob.sync(path.resolve(rootSrc, pattern))
- return files.reduce((res, file) => {
- var info = path.parse(file)
- var key = info.dir.slice(rootSrc.length + 1) + '/' + info.name
- res[key] = path.resolve(file)
- return res
- }, {})
+function getEntry (rootSrc) {
+ var map = {};
+ glob.sync(rootSrc + '/pages/**/main.js')
+ .forEach(file => {
+ var key = relative(rootSrc, file).replace('.js', '');
+ map[key] = file;
+ })
+ return map;
}
plugins: [
- new MpvuePlugin()
+ new MpvuePlugin(),
+ new CopyWebpackPlugin([{
+ from: '**/*.json',
+ to: 'app.json'
+ }], {
+ context: 'src/'
+ }),
+ new CopyWebpackPlugin([ // 处理 main.json 里面引用的图片,不要放代码中引用的图片
+ {
+ from: path.resolve(__dirname, '../static'),
+ to: path.resolve(__dirname, '../dist/static'),
+ ignore: ['.*']
+ }
+ ])
]
}
build/webpack.dev.conf.js
module.exports = merge(baseWebpackConfig, {
devtool: '#source-map',
output: {
path: config.build.assetsRoot,
- filename: utils.assetsPath('js/[name].js'),
- chunkFilename: utils.assetsPath('js/[id].js')
+ filename: utils.assetsPath('[name].js'),
+ chunkFilename: utils.assetsPath('[id].js')
},
plugins: [
new webpack.DefinePlugin({
module.exports = merge(baseWebpackConfig, {
// copy from ./webpack.prod.conf.js
// extract css into its own file
new ExtractTextPlugin({
- filename: utils.assetsPath('css/[name].wxss')
+ filename: utils.assetsPath('[name].wxss')
}),
module.exports = merge(baseWebpackConfig, {
}
}),
new webpack.optimize.CommonsChunkPlugin({
- name: 'vendor',
+ name: 'common/vendor',
minChunks: function (module, count) {
// any required modules inside node_modules are extracted to vendor
return (
module.exports = merge(baseWebpackConfig, {
}
}),
new webpack.optimize.CommonsChunkPlugin({
- name: 'manifest',
- chunks: ['vendor']
+ name: 'common/manifest',
+ chunks: ['common/vendor']
}),
- // copy custom static assets
- new CopyWebpackPlugin([
- {
- from: path.resolve(__dirname, '../static'),
- to: config.build.assetsSubDirectory,
- ignore: ['.*']
- }
- ]),
build/webpack.prod.conf.js
var webpackConfig = merge(baseWebpackConfig, {
devtool: config.build.productionSourceMap ? '#source-map' : false,
output: {
path: config.build.assetsRoot,
- filename: utils.assetsPath('js/[name].js'),
- chunkFilename: utils.assetsPath('js/[id].js')
+ filename: utils.assetsPath('[name].js'),
+ chunkFilename: utils.assetsPath('[id].js')
},
plugins: [
var webpackConfig = merge(baseWebpackConfig, {
}),
// extract css into its own file
new ExtractTextPlugin({
- // filename: utils.assetsPath('css/[name].[contenthash].css')
- filename: utils.assetsPath('css/[name].wxss')
+ // filename: utils.assetsPath('[name].[contenthash].css')
+ filename: utils.assetsPath('[name].wxss')
}),
// Compress extracted CSS. We are using this plugin so that possible
// duplicated CSS from different components can be deduped.
var webpackConfig = merge(baseWebpackConfig, {
new webpack.HashedModuleIdsPlugin(),
// split vendor js into its own file
new webpack.optimize.CommonsChunkPlugin({
- name: 'vendor',
+ name: 'common/vendor',
minChunks: function (module, count) {
// any required modules inside node_modules are extracted to vendor
return (
var webpackConfig = merge(baseWebpackConfig, {
// extract webpack runtime and module manifest to its own file in order to
// prevent vendor hash from being updated whenever app bundle is updated
new webpack.optimize.CommonsChunkPlugin({
- name: 'manifest',
- chunks: ['vendor']
- }),
+ name: 'common/manifest',
+ chunks: ['common/vendor']
+ })
- // copy custom static assets
- new CopyWebpackPlugin([
- {
- from: path.resolve(__dirname, '../static'),
- to: config.build.assetsSubDirectory,
- ignore: ['.*']
- }
- ])
]
})
config/index.js
module.exports = {
env: require('./prod.env'),
index: path.resolve(__dirname, '../dist/index.html'),
assetsRoot: path.resolve(__dirname, '../dist'),
- assetsSubDirectory: 'static', // 不将资源聚合放在 static 目录下
+ assetsSubDirectory: '',
assetsPublicPath: '/',
productionSourceMap: false,
// Gzip off by default as many popular static hosts such as
@@ -26,7 +26,7 @@ module.exports = {
port: 8080,
// 在小程序开发者工具中不需要自动打开浏览器
autoOpenBrowser: false,
- assetsSubDirectory: 'static', // 不将资源聚合放在 static 目录下
+ assetsSubDirectory: '',
assetsPublicPath: '/',
proxyTable: {},
// CSS Sourcemaps off by default because relative paths are "buggy"
Ссылка на ссылку
Часть вышеуказанного контента взята из:
- Анализ архитектуры мини-программы WeChat (часть 1)
- Анализ архитектуры апплета WeChat
- 7-й сезон WeChat Open Class 2018, Шанхайская станция · Мини-программа
- Использовать иконочный шрифт в апплете
- Следующий шаг апплета WeChat: поддержка NPM, облака апплетов, визуального программирования, поддержка субподряда
- mpvue-docs
- Лучшие практики разработки мини-программ WeChat с помощью Mpvue
- Разработка мини-программ WeChat с помощью Vue.js: анализ Open Source Framework mpvue
- learnVue
Если вы хотите узнать больше о интерфейсе, навыках прохождения собеседований или некоторых моих личных мыслях, вы можете подписаться на мой официальный аккаунт, чтобы учиться вместе.