Статьи

Что я узнал о VueJS от создания расширения Chrome

Я хотел поэкспериментировать с созданием расширения Google Chrome с Vue.js, поэтому я решил взять Vue TodoMVC и попытаться сделать его доступным из панели инструментов моего браузера:

chrome_1.gif

Создание расширения для браузера с помощью 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!