Я хотел поэкспериментировать с созданием расширения 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!