Это 128-я оригинальная статья без воды.Если вы хотите получить больше оригинальных статей, выполните поиск в официальном аккаунте и подпишитесь на нас~ Эта статья была впервые опубликована в блоге Zhengcaiyun:Серверный рендеринг SSR и принцип его реализации
предисловие
В повседневной фронтенд-разработке, в сценариях, требующих оптимизации скорости рендеринга первого экрана, все более-менее слышали о рендеринге на стороне сервера (SSR). В этой статье Vue будет сочетаться с интерпретацией логики реализации SSR. Прочитав эту статью, вы узнаете:
- Сценарии использования рендеринга на стороне сервера
- Принцип реализации Vue SSR
- Готовые строительные леса SSR
рендеринг на стороне сервера
Рендеринг на стороне сервера SSR (рендеринг на стороне сервера) означает завершение процесса сращивания html страницы на стороне сервера, а затем отправку его в браузер для привязки структуры html без интерактивной возможности к событиям и состояниям и ее отображения. на стороне клиента как имеющие Приложения с полной интерактивностью.
Применимая сцена
SSR может обеспечить хорошую поддержку сцены в следующих двух ситуациях.
- Нужна лучшая поддержка для SEO
ПреимуществоСинхронизировать. Сканеры поисковых систем не будут ждать завершения данных асинхронного запроса, прежде чем сканировать информацию.Если SEO имеет решающее значение для приложения, но ваша страница запрашивает данные асинхронно, SSR может очень хорошо помочь вам решить эту проблему.
- более быстрое время прибытия
ПреимуществоСценарии медленной сети и медленных устройств. Традиционный SPA требует, чтобы была выполнена полная загрузка JS, в то время как тег рендеринга сервера SSR может отображаться после того, как сервер рендерит html, и пользователь быстрее увидит первую страницу рендеринга экрана. Если скорость преобразования времени рендеринга в верхней части экрана критична для приложения, ее можно оптимизировать с помощью SSR.
Неприменимые сценарии
Использование SSR в следующих трех сценариях требует осторожности.
- Работа с однородными ресурсами
Недостатком является то, что программа должна быть универсальной. В сочетании с хуками Vue единственными жизненными циклами, которые можно вызывать в SSR, являютсяbeforeCreateа такжеcreated, что делает необходимым убедиться, что операция не сообщает об ошибках при использовании стороннего API. Для ссылки на стороннюю библиотеку требуется специальная обработка, чтобы могли работать как поддерживающий сервер, так и клиент.
- Поддержка развертывания ресурсов конфигурации сборки
Недостатком является то, что операционная среда едина. Программа должна быть вnode.js serverрабочая среда.
- Сервер больше кеш готов
Недостатком является то, что сценарии с высоким трафиком требуют стратегии кэширования. Код приложения должен выполнять синтаксический анализ на обоих концах, что требует большей производительности ЦП и требует большей подготовки для балансировки нагрузки и обработки многосценового кэша, чем SPA.
Давайте объединим Vue.js, чтобы увидеть, как VUE реализует SSR.
Принцип реализации Vue SSR
предпосылки
Компоненты визуализируются на основе VNodes
VNode сам по себе является js-объектом,Чрезвычайно совместимый, не зависит от текущей среды выполнения, поэтому его можно отображать на сервере и отображать в собственном коде. Виртуальный DOM часто модифицируется, и, наконец, реальный DOM необходимо изменить путем сравнения, что может быть достигнуто.локальный рендерингцель,Уменьшить потери производительности.
vue-server-renderer
этоАвтономное приложение для рендерингаПакет возможностей — это основной код рендеринга Vue на стороне сервера.
Исходный код ниже этой статьи также объединен с этим пакетом, и здесь не так уж много лишнего введения.
Архитектура рендеринга SSR
мы объединяемОфициальная карта сайтаа такжеАрхитектура проектаДва измерения для понимания общей картины SSR
Архитектура проекта
src
├── components
├── App.vue
├── app.js ----通用 entry
├── entry-client.js ----仅运行于浏览器
└── entry-server.js ----仅运行于服务器
app.jsЭкспортируйте фабрику функций createApp, эта функция может выполняться повторно, внедряться из корневого экземпляра Vue, использоваться для создания маршрутизатора, хранилища и экземпляров приложения.
import Vue from 'vue'
import App from './App.vue'
// 导出一个工厂函数,用于创建新的应用程序、router 和 store 实例
export function createApp () {
const app = new Vue({
render: h => h(App)
})
return { app }
}
entry-client.jsОтвечает за создание приложения, монтирование экземпляра DOM и запуск только в браузере.
import { createApp } from './app'
const { app } = createApp()
// #app 为根元素,名称可替换
app.$mount('#app')
entry-server.jsСоздайте и верните экземпляр приложения, а также выполните сопоставление маршрутов и предварительную обработку данных, которые выполняются только на сервере.
import { createApp } from './app'
export default context => {
const { app } = createApp()
return app
}
Принципы написания кода на стороне сервера и на стороне клиента
Как изоморфная структура, процесс компиляции кода приложения Vue SSR предоставляет две записи компиляции, чтобы сгладить различия кода из-за разных сред. Разница между написанием логики кода в записи клиента и записи сервера заключается в следующем.два принципа
- Универсальный код
Для Универсального кода из-за различных конфигурации логики аутентификации и конфигурации шлюза различные приложения среды модуля необходимо настроить в WebPack Resolve.alias.
- необщий код
Клиентская запись отвечает за монтирование кода узла DOM, а также за внедрение сторонних пакетов и загрузку библиотек совместимости. Запись сервера генерирует только объекты Vue.
два скомпилированных продукта
После упаковки с помощью веб-пакета будет два продукта в комплекте.
Серверный бандл используется для генерации vue-ssr-server-bundle.json, знакомый всем sourceMap и список кодов, которые нужно запустить на сервере есть в этом продукте.
vue-SSR-server-bundle.json
{
"entry": ,
"files": {
A:包含了所有要在服务端运行的代码列表
B:入口文件
}
}
Клиентский пакет используется для создания vue-SSR-client-manifest.json, который содержит все статические ресурсы, теги сценариев, которые необходимо загрузить для первого рендеринга, и код, который необходимо запустить на стороне клиента.
vue-SSR-client-manifest.json
{
"publicPath": 公共资源路径文件地址,
"all": 资源列表
"initial":输出 html 字符串
"async": 异步加载组件集合
"modules": moduleIdentifier 和 all 数组中文件的映射关系
}
существуетпредпосылкиВ мы упомянули важный пакетvue-server-renderer, то давайте сосредоточимся на содержимом этого пакета, которое заслуживает нашего изучения и внимания.
vue-server-renderer
Это основной код Vue SSR, который заслуживает нашего внимания.Инициализация приложенияа такжеВывод приложения. Два этапа обеспечивают полную компиляцию кода прикладного уровня и логику сборки.
Инициализация приложения
В процессе инициализации приложения сосредоточьтесь на вводной части.созданный процесса такжеПредотвратите перекрестное загрязнение.
Во-первых, давайте посмотрим, как инициализируется приложение Vue SSR.
созданный процесс
- Создание объектов Vue
const Vue = require('vue')
const app = new Vue()
- Сгенерируйте рендерер, два объекта, на которые стоит обратить внимание, это рендер и templateRenderer.
const renderer = require('vue-server-renderer').createRenderer()
// createRenderer 函数中有两个重要的对象: render 和 templateRenderer
function createRenderer (ref) {
// render: 渲染 html 组件
var render = createRenderFunction(modules, directives, isUnaryTag, cache);
// templateRenderer: 模版渲染,clientManifest 文件
var templateRenderer = new TemplateRenderer({
template: template,
inject: inject,
shouldPreload: shouldPreload,
shouldPrefetch: shouldPrefetch,
clientManifest: clientManifest,
serializer: serializer
});
После этого процесса render и templateRenderer не вызываются, настоящий вызов этих двух функций происходит при создании экземпляра проекта.createBundleRendererфункция, то есть функция, созданная на третьем шаге.
- Создайте песочницу vm, создайте экземпляр файла записи Vue
var vm = require('vm');
// 调用 createBundleRunner 函数实例对象,rendererOptions 支持可配置
var run = createBundleRunner(
entry, ----入口文件集合
files, ----打包文件集合
basedir,
rendererOptions.runInNewContext。
);}
В исходном коде метода createBundleRunner создается экземпляр метода compileModule, и в этом методе есть две функции:getCompiledScriptа такжеevaluateModule
function createBundleRunner (entry, files, basedir, runInNewContext) {
//触发 compileModule 方法,找到 webpack 编译形成的 code
var evaluate = compileModule(files, basedir, runInNewContext);
}
getCompiledScript: скомпилируйте оболочку, найдите имя файла файла входа, скомпилируйте и выполните сценарий сценария.
function getCompiledScript (filename) {
if (compiledScripts[filename]) {
return compiledScripts[filename]
}
// 在入口文件 files 中找到对应的文件名称
var code = files[filename];
var wrapper = NativeModule.wrap(code);
// 在沙盒上下文中执行构建 script 脚本
var script = new vm.Script(wrapper, {
filename: filename,
displayErrors: true
});
compiledScripts[filename] = script;
return script
}
AssessmentModule: определите, следует ли выполнять в текущем контексте или в отдельном контексте в соответствии с элементами конфигурации в runInThisContext.
function evaluateModule (filename, sandbox, evaluatedFiles) {
if ( evaluatedFiles === void 0 ) evaluatedFiles = {};
if (evaluatedFiles[filename]) {
return evaluatedFiles[filename]
}
var script = getCompiledScript(filename);
// 用于判断是在当前的那种模式下面执行沙盒上下文,此时存在两个函数的相互调用
var compiledWrapper = runInNewContext === false
? script.runInThisContext()
: script.runInNewContext(sandbox);
// m: 函数导出的 exports 数据
var m = { exports: {}};
// r: 替代原生 require 用来解析 bundle 中通过 require 函数引用的模块
var r = function (file) {
...
return require(file)
};
}
После того, как вышеуказанная функция будет выполнена, будет вызван compileWrapper.call, и будут переданы параметры, соответствующие экспорту, требованию и модулю выше, и мы сможем получить функцию входа.
- Отказоустойчивость при передаче ошибок и прослушивание глобальных ошибок
renderToString: когда нет функции cb, возвращается обещание, что означает, что мы можем напрямую сделать try catch при вызове вторичной функции, которая используется для глобальной отказоустойчивости.
renderToString: function (context, cb) {
var assign;
if (typeof context === 'function') {
cb = context;
context = {};
}
var promise;
if (!cb) {
((assign = createPromiseCallback(), promise = assign.promise, cb = assign.cb));
}
...
return promise
},
}
renderToStream: реализован механизм мониторинга для генерирования ошибок, и в этом методе будет запущена функция ловушки генерирования ошибки.
renderToStream: function (context) {
var res = new PassThrough();
run(context).catch(function (err) {
rewriteErrorTrace(err, maps);
// 此处做了监听器的容错
process.nextTick(function () {
res.emit('error', err);
});
}).then(function (app) {
if (app) {
var renderStream = renderer.renderToStream(app, context);
...
}
}
}
предотвратить перекрестное загрязнение
Сервер Node.js — это длительный процесс, когда код, написанный на клиенте, входит в процесс, контекст переменной будет сохранен, что приведет к загрязнению состояния перекрестного запроса. Следовательно, экземпляр нельзя использовать совместно, поэтому createApp — это функция, которую можно выполнять многократно. На самом деле внутри пакета также есть возможность предотвратить перекрестное заражение между переменными.
Возможность предотвращения перекрестного загрязнения обеспечивается элементом конфигурации rendererOptions.runInNewContext, который поддерживает три элемента конфигурации: true, false и один раз.
// rendererOptions.runInNewContext 可配置项如下
true:
新上下文模式:创建新上下文并重新评估捆绑包在每个渲染上。
确保每个应用程序的整个应用程序状态都是新的渲染,但会产生额外的评估成本。
false:
直接模式:
每次渲染时,它只调用导出的函数。而不是在上重新评估整个捆绑包
模块评估成本较高,但需要结构化源代码
once:
初始上下文模式
仅用于收集可能的非组件vue样式加载程序注入的样式。
В частности, для сценариев «ложь» и «один раз», чтобы предотвратить перекрестное загрязнение, требования к объему в процессе рендеринга очень строгие, чтобы гарантировать, что разные объекты не будут загрязнять друг друга.
if (!runner) {
var sandbox = runInNewContext === 'once'
? createSandbox()
: global;
initialContext = sandbox.__VUE_SSR_CONTEXT__ = {};
runner = evaluate(entry, sandbox);
//在后续渲染中,_VUE_SSR_CONTEXT_uu 将不可用
//防止交叉污染
delete sandbox.__VUE_SSR_CONTEXT__;
if (typeof runner !== 'function') {
throw new Error(
'bundle export should be a function when using ' +
'{ runInNewContext: false }.'
)
}
}
Вывод приложения
На этапе вывода приложения SSR будет уделять больше вниманиязагрузить содержимое скриптаа такжеРендеринг шаблона, независимо от того, определен ли исходный код механизма шаблонов в коде при отображении шаблона, будет предоставлен другой HTML-код.структура сращивания.
загрузить содержимое скрипта
Этот процесс реализует привязку данных для методов чтения и templateRender, созданных на предыдущем этапе.
templateRenderer: отвечает за инкапсуляцию html, в его прототипе есть следующие методы, и функции этих функций показаны на рисунке ниже. Стоит отметить, что функция bindRenderFns привязывает четыре функции рендеринга к контексту пользовательского контекста, и пользователь может выполнять пользовательскую сборку и рендеринг контента после получения контента.
render: функция будет вызываться рекурсивно для преобразования всех компонентов в html в порядке от родительского к дочернему.
function createRenderFunction (
modules,
directives,
isUnaryTag,
cache
) {
return function render (
component,
write,
userContext,
done
) {
warned = Object.create(null);
var context = new RenderContext({
activeInstance: component,
userContext: userContext,
write: write, done: done, renderNode: renderNode,
isUnaryTag: isUnaryTag, modules: modules, directives: directives,
cache: cache
});
installSSRHelpers(component);
normalizeRender(component);
// 渲染 node 节点,绑定用户作用上下文
var resolve = function () {
renderNode(component._render(), true, context);
};
// 等待组件 serverPrefetch 执行完成之后,_render 生成子节点的 vnode 进行渲染
waitForServerPrefetch(component, resolve, done);
}
}
После описанного выше процесса компиляции мы получили строку html, но если мы хотим отобразить страницу в браузере, нам нужно собрать js, css и другие теги с этим html, чтобы сформировать полное сообщение и вывести его в браузер, так что нам нужно Этап рендеринга шаблона используется для сборки этих элементов.
Рендеринг шаблона
После фазы инициализации приложения код компилируется для получения строки html, а отрисовка контекста должна зависеть от ресурсов, таких как состояние, сценарий, стили и т. д., привязанных в templateRenderer.prototype.bindRenderFns.
TemplateRenderer.prototype.bindRenderFns = function bindRenderFns (context) {
var renderer = this
;['ResourceHints', 'State', 'Scripts', 'Styles'].forEach(function (type) {
context[("render" + type)] = renderer[("render" + type)].bind(renderer, context);
});
context.getPreloadFiles = r**erer.ge****:**reloadFiles.bind(renderer, context);
};
При рендеринге шаблона возможны две ситуации:
- Неопределенный шаблон двигателя
Результат рендеринга будет напрямую возвращен функции обратного вызова renderToString, а скрипт, требуемый страницей, зависит от функций renderStyles, renderResourceHints, renderState и renderScripts пользовательского контекста.
- Механизм шаблонов определен
templateRender поможет нам со сборкой html
TemplateRenderer.prototype.render = function render (content, context) {
// parsedTemplate 用于解析函数得到的包含三个部分的 compile 对象,
// 按照顺序进行字符串模版的拼接
var template = this.parsedTemplate;
if (!template) {
throw new Error('render cannot be called without a template.')
}
context = context || {};
if (typeof template === 'function') {
return template(content, context)
}
if (this.inject) {
return (
template.head(context) +
(context.head || '') +
this.renderResourceHints(context) +
this.renderStyles(context) +
template.neck(context) +
content +
this.renderState(context) +
this.renderScripts(context) +
template.tail(context)
)
} else {
...
}
};
Пока мы понимаем Vue SSRОбщая логика архитектурыИ **основной код vue-server-renderer**, конечно же, SSR также имеет много готовых каркасов, которые мы можем выбрать.
Готовые строительные леса SSR
В настоящее время три популярных передовых технологических стека React, Vue и Angula вывели соответствующие серверные фреймворки рендеринга, готовые к использованию «из коробки», и заинтересованные студенты могут изучать и использовать их самостоятельно.
- React: Next.js
- Vue: Nuxt.js
- Angula: Nest.js
Суммировать
Рендеринг на стороне сервера (SSR) — это изоморфная программа, и использование SSR зависит отвремя поступления контентаВажность для приложения. Если несколько сотен миллисекунд для начальной загрузки допустимы, использование SSR становится чем-то вроде рутинной работы.
Изучение исходного кода может помочь лучше изучить отличный метод написания программ и стимулировать размышления о ежедневной структуре программирования кода.Если вы больше склоняетесь к готовому решению, вы можете использовать существующие леса SSR для создания проекта.Шаблоны этих каркасов Абстракция и дополнительные функциональные расширения могут обеспечить гладкую нестандартную работу.
использованная литература
вне никнейма
У вас есть один голос, у меня есть один голос, Чжэн Цайюнь дебютирует завтра, а список авторов Nuggets 2021 горяч!
Пожалуйста, подвигайте мизинцами и проголосуйте за нас. Ваша поддержка является самой большой движущей силой для нас, чтобы двигаться вперед,
- веб-запись:нажмите на меня, чтобы проголосовать
- Вход в приложение: прокрутите статью до верха, а затем следуйте картинке ниже↓
Рекомендуемое чтение
- Руководство по разработке плагинов для Sketch
- Почему не рекомендуется использовать индекс в качестве ключа в Vue
- Анализ технической схемы и реализация записи веб-экрана
работы с открытым исходным кодом
- Zhengcaiyun интерфейсный таблоид
адрес с открытым исходным кодомwww.zoo.team/openweekly/(На главной странице официального сайта таблоида есть группа обмена WeChat)
- Плагин выбора товара
адрес с открытым исходным кодомGitHub.com/Chinese Patent Medicine-Inc/Reservoir…
Карьера
ZooTeam, молодая, увлеченная и творческая команда, связанная с отделом исследований и разработок продукции Zhengcaiyun, базируется в живописном Ханчжоу. В настоящее время в команде более 60 фронтенд-партнеров, средний возраст которых составляет 27 лет, и почти 40% из них — инженеры полного стека, настоящая молодежная штурмовая группа. В состав членов входят «ветераны» солдат из Ali и NetEase, а также первокурсники из Чжэцзянского университета, Университета науки и технологий Китая, Университета Хандянь и других школ. В дополнение к ежедневным деловым связям, команда также проводит технические исследования и фактические боевые действия в области системы материалов, инженерной платформы, строительной платформы, производительности, облачных приложений, анализа и визуализации данных, а также продвигает и внедряет ряд внутренних технологий. Откройте для себя новые горизонты передовых технологических систем.
Если вы хотите измениться, вас забрасывают вещами, и вы надеетесь начать их бросать; если вы хотите измениться, вам сказали, что вам нужно больше идей, но вы не можете сломать игру; если вы хотите изменить , у вас есть возможность добиться этого результата, но вы не нужны; если вы хотите изменить то, чего хотите достичь, вам нужна команда для поддержки, но вам некуда вести людей; если вы хотите изменить установившийся ритм, это будет "5 лет рабочего времени и 3 года стажа работы"; если вы хотите изменить исходный Понимание хорошее, но всегда есть размытие того слоя оконной бумаги.. , Если вы верите в силу веры, верьте, что обычные люди могут достичь необыкновенных вещей, и верьте, что они могут встретить лучшего себя. Если вы хотите участвовать в процессе становления бизнеса и лично способствовать росту фронтенд-команды с глубоким пониманием бизнеса, надежной технической системой, технологиями, создающими ценность, и побочным влиянием, я думаю, что мы должны говорить. В любое время, ожидая, пока вы что-нибудь напишете, отправьте это наZooTeam@cai-inc.com