предисловие
Большинство проектов Vue должны поддерживать SSR из соображений SEO, ведь для веб-приложений поисковые системы — это портал с большим трафиком. Vue SSR сейчас относительно зрелый, но если нужно преобразовать приложение SPA в приложение SSR, стоимость все еще немного высока, а рабочая нагрузка эквивалентна рефакторингу внешнего интерфейса. Кроме того, технические требования к фронтенду достаточно высоки, и вам необходимо быть знакомым с Vue, а также иметь опыт работы с приложениями в Node.js и webpack.
представлять
Vue — это фреймворк для создания клиентских приложений, то есть компоненты vue отображаются в браузере. Так называемый рендеринг на стороне сервера относится к рендерингу компонентов vue на стороне сервера в собранные строки HTML, а затем отправке их непосредственно в браузер. Наконец, эти статические теги необходимо «активировать» как полностью интерактивные приложения на клиентская программа.
Преимущества рендеринга на стороне сервера
-
Лучшее SEO, сканеры поисковых систем могут сканировать отображаемые страницы
-
Более быстрое время поступления контента (более быстрая загрузка на первом экране), поскольку серверу нужно вернуть только отрендеренный HTML, эта часть кода небольшая, поэтому пользовательский опыт лучше.
Недостатки рендеринга на стороне сервера
-
Во-первых, стоимость разработки относительно высока.Например, некоторые функции ловушки цикла объявления (такие как beforeCreate и created) могут выполняться как на стороне сервера, так и на стороне клиента, поэтому сторонние библиотеки требуют специальной обработки, прежде чем они смогут работать в серверные приложения.
-
Поскольку рендеринг на стороне сервера использует Nodejs в качестве среднего уровня, при развертывании проекта он должен находиться в рабочей среде сервера Node.js. В среде с высоким трафиком также необходимо хорошо выполнять стратегии загрузки сервера и кэширования.
Принципиальный анализ
Сначала прикрепите демо-адрес:GitHub.com/UUI/vUE-в любое время…
Шаг 1: Напишите entry-client.js и entry-server.js
entry-client.js выполняется только в среде браузера, поэтому необходимо отобразить вызов метода $mount для монтирования узла DOM.
import Vue from 'vue';
import App from './App.vue';
import createStore from './store/index.js';
function createApp() {
const store = createStore();
const app = new Vue({
store,
render: h => h(App)
});
return {app, store}
}
const { app, store } = createApp();
// 使用window.__INITIAL_STATE__中的数据替换整个state中的数据,这样服务端渲染结束后,客户端也可以自由操作state中的数据
if (window.__INITIAL_STATE__) {
store.replaceState(window.__INITIAL_STATE__);
}
app.$mount('#app');
entry-server.js необходимо экспортировать функцию, которая будет вызываться во время рендеринга на стороне сервера.
import Vue from 'vue';
import App from './App.vue';
import createStore from './store/index.js';
export default function(context) {
// context是上下文对象
const store = createStore();
let app = new Vue({
store,
render: h => h(App)
});
// 找到所有 asyncData 方法
let components = App.components;
let asyncDataArr = []; // promise集合
for (let key in components) {
if (!components.hasOwnProperty(key)) continue;
let component = components[key];
if (component.asyncData) {
asyncDataArr.push(component.asyncData({store})) // 把store传给asyncData
}
}
// 所有请求并行执行
return Promise.all(asyncDataArr).then(() => {
// context.state 赋值成什么,window.__INITIAL_STATE__ 就是什么
// 这下你应该明白entry-client.js中window.__INITIAL_STATE__是哪来的了,它是在服务端渲染期间被添加进上下文的
context.state = store.state;
return app;
});
};
Для чего используется вышеуказанный asyncData? На самом деле эта функция специально используется для запроса данных, вы можете спросить, почему данных запроса нетbeforeCreateилиcreatedЗавершено в , но также нужно определить функцию? несмотря на то чтоbeforeCreateа такжеcreatedОн также будет выполняться на стороне сервера (другие периодические функции будут выполняться только на стороне клиента), но все мы знаем, что запрос асинхронный, что приводит к тому, что после отправки запроса данные не возвращены, а рендеринг закончился, поэтому использование Ajax невозможно. Возвращенные данные также рендерятся. Поэтому вам нужно найти способ дождаться возврата всех данных перед рендерингом компонента.
asyncData должен возвращать обещание, чтобы вы могли дождаться выполнения всех запросов перед рендерингом компонента. Ниже приведен пример использования asyncData в компоненте foo, где выполняется запрос данных.
export default {
asyncData: function({store}) {
return store.dispatch('GET_ARTICLE') // 返回promise
},
computed: {
article() {
return this.$store.state.article
}
}
}
Шаг 2. Настройте веб-пакет
Конфигурация веб-пакета относительно проста, но ее также необходимо настраивать отдельно для клиента и сервера.
webpack.client.conf.js явно используется для упаковки клиентских приложений.
module.exports = merge(base, {
entry: {
client: path.join(__dirname, '../entry-client.js')
}
});
webpack.server.conf.js используется для упаковки серверных приложений, где необходимо указать среду узла.
module.exports = merge(base, {
target: 'node', // 指定是node环境
entry: {
server: path.join(__dirname, '../entry-server.js')
},
output: {
filename: '[name].js', // server.js
libraryTarget: 'commonjs2' // 必须按照 commonjs规范打包才能被服务器调用。
},
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname, '../index.ssr.html'),
filename: 'index.ssr.html',
files: {
js: 'client.js'
}, // client.js需要在html中引入
excludeChunks: ['server'] // server.js只在服务端执行,所以不能打包到html中
})
]
});
Шаг 3: Запустите службу
После завершения упаковки можно запускать сервис.start.jsНам нужно загрузить server.js, а затем вернуть отрендеренный html в браузер через метод renderToString.
const bundle = fs.readFileSync(path.resolve(__dirname, 'dist/server.js'), 'utf-8');
const renderer = require('vue-server-renderer').createBundleRenderer(bundle, {
template: fs.readFileSync(path.resolve(__dirname, 'dist/index.ssr.html'), 'utf-8') // 服务端渲染数据
});
server.get('*', (req, res) => {
renderer.renderToString((err, html) => {
// console.log(html)
if (err) {
console.error(err);
res.status(500).end('服务器内部错误');
return;
}
res.end(html);
})
});
визуализация
Демо загружено на github:GitHub.com/UUI/vUE-в любое время…
Эпилог
Я практиковал Vue SSR некоторое время, и я обнаружил, что создать полную структуру службы SSR по-прежнему очень сложно. Возможно, Nuxt — хороший выбор. Друзья, которые заинтересованы в Nuxt, могут обратиться к одной из моих работ с открытым исходным кодом.Essay
Выше, спасибо за чтение!