Я хотел поэкспериментировать с созданием расширения Google Chrome с Vue.js, поэтому я решил взять Vue TodoMVC и попытаться сделать его доступным из панели инструментов моего браузера:
Создание расширения для браузера с помощью Vue очень похоже на создание обычной веб-страницы с помощью Vue, но есть несколько ключевых отличий, о которых я расскажу в этой статье.
Нет шаблонов
Одна вещь, которая нам нравится в Vue — это возможность использовать шаблоны в файле:
<body>
<div id="app">
<div>{{ message }}</div>
</div>
</body>
Или в строке:
new Vue({
template: `<div>{{ message }}</div>`
});
Одна маленькая проблема: вы не можете использовать такие шаблоны в расширениях Chrome!
Но прежде чем сдаться и вернуться к jQuery, стоит понять, почему существует это ограничение и как Vue может помочь вам обойти его.
Расширения браузера похожи только на веб-страницы
Расширения браузера используют HTML, CSS и JavaScript, как обычные веб-страницы. Но есть API-интерфейсы, к которым расширения могут получить доступ, которых нет у веб-страниц, что позволяет им расширять функции закладок, инструментов разработчика и других аспектов браузера.
Этот дополнительный доступ делает пользователей более уязвимыми для дыр в безопасности, поэтому расширения требуют Политики безопасности контента, чтобы сделать их более безопасными, отключив потенциально небезопасные функции.
Политика безопасности контента (CSP)
Никто не любит читать о политиках, поэтому я буду кратким: среди прочего, CSP налагает ограничения на вид кода, который может включать в себя ваше расширение:
- Встроенные скрипты отключены, например
<button onclick="..."> - Контент должен быть загружен локально, то есть без скриптов через CDN.
evalфункции отключены, напримерeval("alert(('hi')")
Именно это последнее ограничение на evalфункции влияет на нас, пользователей Vue.
Примечание:
evalсчитается небезопасным, поскольку его можно использовать для запуска произвольного кода и сделать ваше приложение уязвимым для атак с использованием нескольких сценариев .
Как компилируются шаблоны Vue
Во время выполнения внутренний компилятор шаблона Vue проанализирует документ или строку шаблона и сгенерирует представление шаблона в JavaScript.
Повышение эффективности Vue частично связано с его способностью выполнять манипуляции в JavaScript, прежде чем делать их непосредственно на странице.
К сожалению, компилятор шаблонов полагается на evalфункции для выполнения этой задачи компиляции, и они не разрешены в CSP.
Решение 1. Разрешить «unsafe-eval» (не рекомендуется)
Вы можете переопределить evalограничение в CSP вашего расширения, и это решит проблему. Однако это не рекомендуется, так как теперь оно делает ваше расширение уязвимым для кросс-скриптовых атак.
Решение 2. Не компилируйте шаблоны во время выполнения
На самом деле мы можем просто создать приложение Vue без компилятора шаблонов времени выполнения (к сведению, компилятор — это внутренняя библиотека, vue-template-compilerкоторая может использоваться автономно).
Если вы использовали Vue в качестве модуля ES6, возможно, вы уже делали это, но, возможно, не понимали, что вы делаете!
Как уже говорилось, компилятор шаблонов Vue используется всякий раз, когда вы используете templateстроку, например так:
new Vue({
template: `<div>{{ message }}</div>`
});
Или, когда вы монтируете шаблон с помощью el:
new Vue({
el: '#app'
});
<body>
<div id="app">
<div>{{ message }}</div>
</div>
</body>
В обоих этих сценариях Vue должен проанализировать строку, и именно здесь используется функция.<div>{{ message }}</div>eval
Функции рендеринга
Функции рендеринга — это функции JavaScript, которые можно использовать для создания шаблона. Если вы используете функцию рендеринга для создания шаблона, компилятор шаблонов не нужен:
new Vue({
render (createElement) {
return createElement('div', this.message)
}
}).$mount('#app');
<body>
<div id="app"></div>
</body>
Примечание. Использование пустого узла для монтирования не вызывает компилятор шаблона.
Но … Функции рендеринга сложно использовать
Это правда, функции рендеринга не являются интуитивно понятным способом создания шаблонов.
Но не волнуйтесь, вам не придется писать свои функции рендеринга вручную . Вместо этого вы можете использовать компилятор шаблонов в разработке, чтобы предварительно скомпилировать ваши шаблоны в функции рендеринга.
Очевидно, что CSP не против, если вы компилируете шаблон, он делает это во время выполнения с eval — вот в чем проблема.
Примечание: вы можете использовать JSX для создания функций рендеринга, если вы так склонны.
Отдельные файловые компоненты для спасения
Есть еще одна причина, по которой отдельные файловые компоненты (SFC) являются замечательными: они предварительно скомпилированы и, следовательно, совместимы с CSP.
Когда вы используете vue-loaderдля обработки вашего .vueфайла, одна из вещей, которую он делает, это vue-template-compilerпревращает шаблон вашего компонента в функцию рендеринга.
Так что если у вас есть SFC с этим шаблоном:
<template>
<div id="app">{{ message }}</div>
</template>
После сборки загляните в свой пакет Webpack, и вы увидите что-то вроде этого:
render: function () {
return this.$createElement("div", {attrs: {id: "app"}}, [this.message])
}
Если все ваше приложение состоит из отдельных файловых компонентов, Vue не нужно будет выполнять компиляцию шаблонов во время выполнения.
Сборка только во время выполнения
Возможно, вы заметили в документации Vue что-то о «полной сборке» и «сборке только во время выполнения». Если вы похожи на меня, вы, вероятно, пропустили эту часть!
Сборка только во время выполнения такая же, как полная сборка только безvue-template-compiler . Если шаблоны вашего приложения были предварительно скомпилированы, вы должны использовать эту сборку только во время выполнения. Он не только совместим с CSP, но и на 30% легче!
В настройках ES5 вы можете загрузить библиотеку Vue только для времени выполнения, например:
<script src="vue.runtime.min.js"></script>
Но более вероятно, что вы будете использовать настройку ES6 с Webpack, и вы захотите это
import Vue from 'vue'
to refer to the runtime build, not the full build. Fortunately, it does this by default!