Вью — этосильно инкапсулированный,из коробки,Стек интерфейсных фреймворков, который можно комбинировать с веб-пакетом для скомпилированной внешней разработки или напрямую подключать к автоматизированным инструментам, таким как gulp и grunt.全局window
использовать. Эта статья была написана в начале выпуска Vue2.4.x, и последняя версия, используемая в настоящее время автором в производственной среде, — 2.5.2. Имея опыт разработки нескольких интерфейсных проектов с тяжелым взаимодействием, автор всесторонне проанализировал, обобщил и аннотировал стек технологии Vue в сочетании с официальными документами, поэтому эту статью можно использовать в качестве дополнительного чтения к официальному руководству по Vue2. Студентам, у которых нет опыта разработки Vue2, рекомендуется временно прочитать эту статью после прохождения официального руководства.
В версиях после Vue2.2.x фреймворк Vue и его функции стека технологий становятся все более совершенными.По сравнению с комбинацией React+Reflux/Redux/MobX, Vue ближе к техническим спецификациям W3C (например, реализация все еще находится на стадии проекта W3C.<template>
,<slot>
,is
и другие новые функции, обеспечивающие хорошую и простую в использовании среду написания шаблонов), а технологический стек и экосистема с открытым исходным кодом стали более полными и простыми в настройке, интегрируя множество мест в React, требующих ручного кодирования, в лучшие практики и абстрагируя их в простой синтаксический сахар (такой как в Vuexstore
модульность), позволяя разработчикам всегда сосредотачиваться на самой бизнес-логике.
По сравнению с Angular2, структура API Vue2 является более лаконичной и может свободно комбинироваться с TypeScript или ECMAScript6.Это не зависит от конкретного языка предварительной обработки для получения наилучшего пользовательского опыта.Характеристики самого фреймворка не заставляют полагаться на различный крутой синтаксический сахар. Vue2 в целом оченьЛегкийтехнологический стек, дизайн и реализация точно соответствуют техническим спецификациям W3C, уделяя особое внимание обработкеКомпонентизация шаблона HTML,Разделение событий и данных по областям,Многоуровневая связь компонентовТри ключевых вопроса одностраничной фронтенд-разработки. В процессе написания этой статьи описываются сходства, различия и сравнения интерфейсных фреймворков, таких как Angular и React, для справки разработчиков, блуждающих по выбору различных фронтенд-технологий.
Vue против Углового
составной
Идея дизайна Angular имитирует концепцию многослойности MVC в веб-разработке на Java.Controller
Вырезать и контролировать область страницы, затем передатьService
Чтобы добиться повторного использования, это своего рода страницапортретИдея послойной развязки. Vue позволяет разработчикам абстрагировать страницу на несколько независимых компонентов, то есть DOM-структуру страницы.горизонтальныйСокращение, полное повторное использование функций и контроль объема посредством сборки компонентов. Каждый компонент обеспечивает толькоprops
Как единый интерфейс и использование Vuex дляstate tree
управления, чтобы облегчить связь и синхронизацию состояния между компонентами.
составной
Angular доступен с версии 1.6.x.component()
Методы иComponent Router
чтобы обеспечить компонентный опыт разработки, но по-прежнему необходимо полагаться наcontroller
а такжеservice
По сути, он до сих пор не избавился от оков вертикальной многоуровневой идеи MVC.
Двустороннее связывание и реактивное связывание
Vue перебирает все свойства объекта данных и передает собственныйObject.defineProperty()
метод преобразует эти свойства вgetter/setter
(Поддерживает только браузеры IE9 и выше.). Vue внутренне отслеживает зависимости через эти геттеры/сеттеры и инициирует соответствующие изменения при изменении свойств, тем самым завершая двустороннюю привязку моделей к представлениям. Вызывается автоматически при создании экземпляра каждого компонента Vue.$watch()
Пройдите через себя и запишите его как зависимость.Когда эти зависимости сеттера срабатывают, Watcher уведомляется о необходимости пересчитать новое значение, а затем запускает компонент VUE.render()
Функция повторно отображает компонент.
Жизненный цикл реактивных привязок
В отличие от двусторонней привязки данных Aangular, компоненты Vue не могут обнаруживать добавление и удаление свойств данных после создания экземпляра, потому что компоненты Vue выполняют обработку получения/установки свойств при создании экземпляра, поэтому свойства объектов данных должны быть созданы. правильно. Следовательно, то, что предоставляет Vue, не является истинной двусторонней привязкой, более точное описание должно бытьОдносторонняя привязка, быстрое обновление, и Angular может пройти$scope
Влияет на привязку данных к представлению, а также может управляться через слой представления.$scope
Свойства объекта на , принадлежат реальному смыслуДвусторонняя привязка вида и модели.
var vm = new Vue({
data:{
a:1
}
})
vm.a = 1 // 响应的
vm.b = 2 // 非响应的
Поэтому Vue не позволяет добавлять новые динамические реактивные свойства корневого уровня для уже созданных компонентов (То есть атрибуты, непосредственно монтируемые под данными), но вы можете использоватьVue.set(object, key, value)
метод добавления реактивных свойств.
Vue.set(vm.someObject, "b", 2)
// vm.$set()实例方法是Vue.set()全局方法的别名
this.$set(this.someObject, "b",2)
// 使用Object.assign()或_.extend()也可以添加响应式属性,但是需要创建同时包含原属性、新属性的对象,从而有效触发watch()方法
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })
Vue обновляет DOM асинхронно.После наблюдения за изменениями данных Vue открывает очередь и буферизует ее в том же цикле событий (цикл событий Vue называетсяtick[tɪk] п. Все изменения данных, происходящие в марке, жетоне). Если один и тот же наблюдатель запускается несколько раз, он будет помещен в эту очередь только один раз.
Vue будет передавать собственный JavaScript внутри
Promise.then
,MutationObserver
,setTimeout(fn, 0)
для выполнения наблюдателя в асинхронной очереди.
В сценариях, требующих ручного управления DOM, чтобы обновить DOM после того, как Vue отреагирует на изменения данных, вы можете вручную вызватьVue.nextTick(callback)
, и поместите логику операции DOM в функцию обратного вызова, чтобы гарантировать выполнение операции DOM после завершения адаптивного обновления.
<div id="example">{{message}}</div>
<script>
// 使用Vue实例上的.$nextTick()
var vue = new Vue({
el: "#example",
data: {
message: "123"
}
})
vue.message = "new message" // 更改数据
vue.$el.textContent === "new message" // false
vue.nextTick(function () {
vm.$el.textContent === "new message" // true
})
</script>
<script>
// 组件内使用vm.$nextTick(),不需要通过全局Vue,且回调函数中this自动指向当前Vue实例
Vue.component("example", {
template: "<span>{{ message }}</span>",
data: function () {
return {
message: "没有更新"
}
},
methods: {
updateMessage: function () {
this.message = "更新完成"
console.log(this.$el.textContent) // 没有更新
this.$nextTick(function () {
console.log(this.$el.textContent) // 更新完成
})
}
}
})
</script>
виртуальный DOM
Vritual DOMЭта концепция была впервые введена React.Это схема сравнения дифференциации объектов DOM, которая абстрагирует объекты DOM в объекты Virtual DOM (То есть результат рендеринга функцией render()), затем сравните виртуальный DOM с помощью алгоритма различия и верните разницу, и, наконец, примените возвращенный объект различия к реальному узлу DOM с помощью алгоритма исправления.
Виртуальные объекты DOM в Vue называютсяVNode(template
Содержимое будет скомпилировано в функцию render(), а функция render() получит функцию createElement() и, наконец, вернет объект VNode), а алгоритм исправления взят из другого проекта с открытым исходным кодом.snabbdom, который сопоставляет операции реального DOM с операциями виртуального DOM и повышает производительность за счет сокращения количества операций с реальным DOM.
➜ vdom git:(dev) tree
├── create-component.js
├── create-element.js
├── create-functional-component.js
├── helpers
│ ├── extract-props.js
│ ├── get-first-component-child.js
│ ├── index.js
│ ├── is-async-placeholder.js
│ ├── merge-hook.js
│ ├── normalize-children.js
│ ├── resolve-async-component.js
│ └── update-listeners.js
├── modules
│ ├── directives.js
│ ├── index.js
│ └── ref.js
├── patch.js
└── vnode.js
Начальная точка дизайна VNode такая же, как и у Angular.$digest
Цикл аналогичен, как черезУменьшите количество операций с реальным DOM для повышения производительности., но реализация Vue более легкая, отказ от Angular обеспечивает двустороннюю привязку$apply()
,$eval()
Инкапсулировать функции, выборочно реализованные в Angular$compile()
,$watch()
аналогичная функция.
Опции для объектов Vue
путем вызова конструктораnew Vue()
проходить вoption
объект для создания экземпляра Vue.
var vm = new Vue({
// 数据
data: "声明需要响应式绑定的数据对象",
props: "接收来自父组件的数据",
propsData: "创建实例时手动传递props,方便测试props",
computed: "计算属性",
methods: "定义可以通过vm对象访问的方法",
watch: "Vue实例化时会调用$watch()方法遍历watch对象的每个属性",
// DOM
el: "将页面上已存在的DOM元素作为Vue实例的挂载目标",
template: "可以替换挂载元素的字符串模板",
render: "渲染函数,字符串模板的替代方案",
renderError: "仅用于开发环境,在render()出现错误时,提供另外的渲染输出",
// 生命周期钩子
beforeCreate: "发生在Vue实例初始化之后,data observer和event/watcher事件被配置之前",
created: "发生在Vue实例初始化以及data observer和event/watcher事件被配置之后",
beforeMount: "挂载开始之前被调用,此时render()首次被调用",
mounted: "el被新建的vm.$el替换,并挂载到实例上之后调用",
beforeUpdate: "数据更新时调用,发生在虚拟DOM重新渲染和打补丁之前",
updated: "数据更改导致虚拟DOM重新渲染和打补丁之后被调用",
activated: "keep-alive组件激活时调用",
deactivated: "keep-alive组件停用时调用",
beforeDestroy: "实例销毁之前调用,Vue实例依然可用",
destroyed: "Vue实例销毁后调用,事件监听和子实例全部被移除,释放系统资源",
// 资源
directives: "包含Vue实例可用指令的哈希表",
filters: "包含Vue实例可用过滤器的哈希表",
components: "包含Vue实例可用组件的哈希表",
// 组合
parent: "指定当前实例的父实例,子实例用this.$parent访问父实例,父实例通过$children数组访问子实例",
mixins: "将属性混入Vue实例对象,并在Vue自身实例对象的属性被调用之前得到执行",
extends: "用于声明继承另一个组件,从而无需使用Vue.extend,便于扩展单文件组件",
provide&inject: "2个属性需要一起使用,用来向所有子组件注入依赖,类似于React的Context",
// 其它
name: "允许组件递归调用自身,便于调试时显示更加友好的警告信息",
delimiters: "改变模板字符串的风格,默认为{{}}",
functional: "让组件无状态(没有data)和无实例(没有this上下文)",
model: "允许自定义组件使用v-model时定制prop和event",
inheritAttrs: "默认情况下,父作用域的非props属性绑定会应用在子组件的根元素上。当编写嵌套有其它组件或元素的组件时,可以将该属性设置为false关闭这些默认行为",
comments: "设为true时会保留并且渲染模板中的HTML注释"
});
Обычно используются экземпляры Vue.
vm
(View Model) переменная, чтобы назвать его.
свойство вычислено вычислено
Вкладывание слишком большого количества бизнес-логики в выражения шаблонов HTML может сделать шаблоны слишком тяжелыми и сложными в обслуживании. Таким образом, вы можете разделить более сложные выражения в шаблоне на вычисляемые свойства для вычисления.
<!-- 不使用计算属性 -->
<div id="example">
{{ message.split("").reverse().join("") }}
</div>
<!-- 将表达式抽象到计算属性 -->
<div id="example">
<p>Original message: "{{ message }}"</p>
<p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>
<script>
var vm = new Vue({
el: "#example",
data: {
message: "Hello"
},
computed: {
reversedMessage: function () {
return this.message.split("").reverse().join("")
}
}
})
</script>
Вычисляемое свойство будет переоцениваться только при изменении связанных зависимостей, что означает, что пока сообщение в приведенном выше примере не изменилось, многократный доступ к вычисляемому свойству reversedMessage всегда будет возвращать предыдущий вычисленный результат без необходимости выполнения функция снова, которая вычисляется Важное отличие от метода.
Вычисляемые свойства по умолчанию имеют толькоgetterметод, но вы можете настроитьsetterметод.
<script>
... ... ...
computed: {
fullName: {
// getter
get: function () {
return this.firstName + " " + this.lastName
},
// setter
set: function (newValue) {
var names = newValue.split(" ")
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
... ... ...
// 下面语句触发setter方法,firstName和lastName也会被相应更新
vm.fullName = "John Doe"
</script>
Наблюдатель за собственностью
С помощью свойства watch вы можете вручную наблюдать за изменениями данных в экземпляре Vue и, конечно же, вы также можете вызвать методvm.$watch
достичь той же цели.
<div id="watch-example">
<p>Ask a yes/no question: <input v-model="question"></p>
<p>{{ answer }}</p>
</div>
<script>
var watchExampleVM = new Vue({
el: "#watch-example",
data: {
question: "",
answer: "I cannot give you an answer until you ask a question!"
},
watch: {
// 如果question发生改变,该函数就会运行
question: function (newQuestion) {
this.answer = "Waiting for you to stop typing..."
this.getAnswer()
}
},
methods: {
// _.debounce是lodash当中限制操作频率的函数
getAnswer: _.debounce(
function () {
if (this.question.indexOf("?") === -1) {
this.answer = "Questions usually contain a question mark. ;-)"
return
}
this.answer = "Thinking..."
var vm = this
axios.get("https://yesno.wtf/api")
.then(function (response) {
vm.answer = _.capitalize(response.data.answer)
})
.catch(function (error) {
vm.answer = "Error! Could not reach the API. " + error
})
},
// 这是用户停止输入等待的毫秒数
500
)
}
})
</script>
Гибкость использования атрибута наблюдения заключается в том, что при отслеживании изменений данных вы можете выполнять некоторую обработку перехода, например установку промежуточных состояний.
Миксины со смешанными свойствами
Используется для повторного использования указанного объекта миксина в компоненте Vue.
// mixin对象
var mixin = {
created: function () {
console.log("混合对象的钩子被调用")
},
methods: {
foo: function () {
console.log("foo")
},
conflicting: function () {
console.log("from mixin")
}
}
}
// vue属性
var vm = new Vue({
mixins: [mixin],
created: function () {
console.log("组件钩子被调用")
},
methods: {
bar: function () {
console.log("bar")
},
conflicting: function () {
console.log("from self")
}
}
})
// => "混合对象的钩子被调用"
// => "组件钩子被调用"
vm.foo() // => "foo"
vm.bar() // => "bar"
vm.conflicting() // => "from self"
Свойства объекта опции компонента с тем же именем будут объединены в массив и вызывают последовательность, а свойства в объекте Mixin будут называться первыми. Если значение атрибута объекта опции компонента является объектом, атрибуты в микне будут игнорироваться.
Функция рендеринга render()
Используется для создания VNode, эта функция получаетcreateElement()
метод в качестве первого параметра, после вызова метода он вернет виртуальный DOM (то есть VNode).
использовать выражение напрямую или вrender()
пройти через функциюcreateElement()
Для ручного рендеринга Vue автоматически поддерживаетblogTitle
Реактивные обновления свойств.
<h1>{{ blogTitle }}</h1>
<script>
render: function (createElement) {
return createElement("h1", this.blogTitle)
}
</script>
Если компонент является функциональным компонентом, функция render() также принимает параметр контекста для предоставления контекстной информации для функциональных компонентов, не имеющих экземпляра.
Реализовать виртуальный DOM с помощью функции render() сложно, поэтому вы можете использовать плагин Babel.babel-plugin-transform-vue-jsx
Примените синтаксис JSX в функции render().
import AnchoredHeading from "./AnchoredHeading.vue"
new Vue({
el: "#demo",
render (h) {
return (
<AnchoredHeading level={1}>
<span>Hello</span> world!
</AnchoredHeading>
)
}
})
Глобальный API объекта Vue
Vue.extend(options) // 通过继承一个option对象来创建一个Vue实例。
Vue.nextTick([callback, context]) // 在下次DOM更新循环结束之后执行延迟回调。
Vue.set(target, key, value) // 设置对象的属性,如果是响应式对象,将会触发视图更新。
Vue.delete(target, key) // 删除对象的属性,如果是响应式对象,将会触发视图更新。
Vue.directive(id, [definition]) // 注册或获取全局指令。
Vue.filter(id, [definition]) // 注册或获取全局过滤器。
Vue.component(id, [definition]) // 注册或获取全局组件。
Vue.use(plugin) // 安装Vue插件。
Vue.mixin(mixin) // 全局注册一个mixin对象。
Vue.compile(template) // 在render函数中编译模板字符串。
Vue.version // 提供当前使用Vue的版本号。
Vue.mixin(mixin)
Использование глобальных миксинов повлияет на всесоздать послеЭкземпляр Vue.
// 为自定义选项myOption注入一个处理器。
Vue.mixin({
created: function () {
var myOption = this.$options.myOption
if (myOption) {
console.log(myOption)
}
}
})
new Vue({
myOption: "hello!"
})
// => "hello!"
Vue.directive(id, [definition])
Vue позволяет регистрировать пользовательские директивы для операций с базовым DOM.
Vue.directive("focus", {
bind: function() {
// 指令第一次绑定到元素时调用,只会调用一次,可以用来执行一些初始化操作。
},
inserted: function (el) {
// 被绑定元素插入父节点时调用。
},
update: function() {
// 所在组件的VNode更新时调用,但是可能发生在其子VNode更新之前。
},
componentUpdated: function() {
// 所在组件VNode及其子VNode全部更新时调用。
},
unbind: function() {
// 指令与元素解绑时调用,只会被调用一次。
}
})
Обмен данными между хуками может осуществляться через
HTMLElement
изdataset
атрибут (т. е. в теге HTML черезdata-
определяемые форматом свойства).
Вышеупомянутая функция ловушки имеет следующие параметры:
- el: HTML-элемент, связанный с директивой, может использоваться для прямого управления DOM.
- vnode: виртуальный узел, сгенерированный компиляцией Vue.
- oldVnode: предыдущий виртуальный узел, только в
update
,componentUpdated
В наличии крючки. - привязка: объект, содержащий следующие свойства:
- name: название инструкции, не включая
v-
приставка. - value: значение привязки директивы, например.
v-my-directive="1 + 1"
серединаvalue
Значение2
. - oldValue: предыдущее значение, к которому была привязана директива, только если
update
,componentUpdated
В наличии крючки. - выражение: строковая форма связанного значения, например.
v-my-directive="1 + 1"
средиexpression
ценность"1 + 1"
. - arg: параметр, передаваемый инструкции, например
v-my-directive:foo
серединаarg
Значение"foo"
. - модификаторы: объект, содержащий модификаторы, например.
v-my-directive.foo.bar
изmodifiers
Значение{foo: true, bar: true}
.
- name: название инструкции, не включая
В дополнение к вышеперечисленным параметрам
el
Кроме того, другие параметры должны быть доступны только для чтения, старайтесь их не изменять.
Vue.filter(id, [definition])
Vue может выполнять некоторое обычное форматирование текста, определяя фильтры, которые можно использовать в интерполяции усов и выражениях v-bind, а также использовать символ вертикальной черты при использовании.|
добавляется в конце выражения.
<!-- in mustaches -->
{{ message | capitalize }}
<!-- in v-bind -->
<div v-bind:id="rawId | formatId"></div>
<!-- capitalize filter -->
<script>
new Vue({
filters: {
capitalize: function (value) {
if (!value) return ""
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
}
}
})
</script>
Фильтры можно использовать последовательно или передавать в параметрах.
<span>{{ message | filterA | filterB }}</span>
<span>{{ message | filterA("arg1", arg2) }}</span>
Vue.use(plugin)
Vue добавляет некоторые глобальные функции через плагины, а плагины Vue переопределяют их.install()
метод, первый параметр методаVue构造器
, второй параметр является необязательнымoption对象
:
MyPlugin.install = function (Vue, options) {
// 1. 添加全局方法或属性
Vue.myGlobalMethod = function () {}
// 2. 添加全局资源
Vue.directive("my-directive", {
bind (el, binding, vnode, oldVnode) {}
})
// 3. 注入组件
Vue.mixin({
created: function () {}
})
// 4. 添加实例方法
Vue.prototype.$myMethod = function (methodOptions) {}
}
Глобальный методVue.use()
Чтобы использовать указанный плагин, вы также можете передать объект опции при его использовании.
Vue.use(MyPlugin, {someOption: true})
Плагины, такие как vue-router, будут автоматически вызываться, когда обнаружат, что Vue является глобальным объектом.
Vue.use()
, если в среде модуля CommonJS вам нужно явно вызватьVue.use()
.
Свойства и методы экземпляра
Экземпляры Vue предоставляют список префиксов$свойства экземпляра и методы .
let vm = new Vue();
vm = {
// Vue实例属性的代理
$data: "被watch的data对象",
$props: "当前组件收到的props",
$el: "Vue实例使用的根DOM元素",
$options: "当前Vue实例的初始化选项",
$parent: "父组件Vue对象的实例",
$root: "根组件Vue对象的实例",
$children: "当前实例的直接子组件",
$slots: "访问被slot分发的内容",
$scopedSlots: "访问scoped slots",
$refs: "包含所有拥有ref注册的子组件",
$isServer: "判断Vue实例是否运行于服务器",
$attrs: "包含父作用域中非props的属性绑定",
$listeners: "包含了父作用域中的v-on事件监听器",
// 数据
$watch: "观察Vue实例变化的表达式、计算属性函数",
$set: "全局Vue.set的别名",
$delete: "全局Vue.delete的别名",
// 事件
$on: "监听当前实例上的自定义事件,事件可以由vm.$emit触发",
$once: "监听一个自定义事件,触发一次之后就移除监听器",
$off: "移除自定义事件监听器",
$emit: "触发当前实例上的事件",
// 生命周期
$mount: "手动地挂载一个没有挂载的Vue实例",
$forceUpdate: "强制Vue实例重新渲染,仅影响实例本身和插入插槽内容的子组件",
$nextTick: "将回调延迟到下次DOM更新循环之后执行",
$destroy: "完全销毁一个实例",
}
Свойство $refs
сынобозначение компонентаref
После атрибута можно передатьотецкомпонент$refs
Свойства экземпляра для доступа к нему.
<div id="parent">
<user-profile ref="profile"></user-profile>
</div>
<script>
var parent = new Vue({ el: "#parent" })
var child = parent.$refs.profile // 访问子组件
</script>
$refs будет заполнен после рендеринга компонента.Он не отвечает и используется только как план на случай непредвиденных обстоятельств для прямого доступа к дочерним компонентам, поэтомуИзбегайте использования $refs в шаблонах или вычисляемых свойствах..
Жизненный цикл
Когда создается каждый экземпляр Vue, он должен пройти ряд процессов инициализации (Настройте мониторинг данных, компилируйте шаблоны, монтируйте экземпляры в DOM и обновляйте DOM при изменении данных.) и запускать несколько функций ловушек одновременно, что позволяет разработчикам выполнять свой собственный код в течение определенного жизненного цикла.
жизненный цикл компонента
Не используйте стрелочные функции для свойств и обратных вызовов экземпляра Vue, например
created: () => console.log(this.a)
илиvm.$watch("a", newValue => this.myMethod())
. Поскольку this функции стрелки привязано к родительскому контексту и не указывает на сам экземпляр Vue, поэтому в предыдущем кодеthis.a
илиthis.myMethod
будетundefined
.Манипуляции с DOM через jQuery можно поместить в
Mounted
Прогрессивное свойство, т. е. когда сборка завершена Vue при монтировании в DOM.
Привязка данных
Слой просмотра Vue черезMustache["mʌstæʃ]
Синтаксис реактивно привязан к свойству данных в экземпляре Vue, а также через встроенные директивы.v-once
Выполните одностороннюю привязку илиv-html
Директива выводит связанную строку в виде HTML, хотя это уязвимо для XSS-атак.
<span>Message: {{ result }}</span>
<span v-once>一次性绑定: {{ msg }}</span>
<div v-html="rawHtml"></div>
Усы нельзя использовать для атрибутов HTML, в этом случае вам нужно прибегнуть кv-bind
инструкция.
<div v-bind:id="dynamicId"></div>
<button v-bind:disabled="isButtonDisabled">Button</button>
Привязать класс и стиль HTML
Прямое управлениеclass
а такжеstyle
Атрибуты являются общим требованием при разработке внешнего интерфейса, Vue передаетv-bind:class
а такжеv-bind:style
Директивы улучшают эти две операции целенаправленным образом.
v-bind:class
Связывание HTMLclass
Атрибуты.
<!-- Vue对象中的data -->
<script>
... ...
data: {
isActive: true,
hasError: false,
classObject: {
active: true,
"text-danger": false
}
}
... ...
</script>
<!-- 直接绑定class到一个对象 -->
<div v-bind:class="classObject"></div>
<!-- 直接绑定class到对象的属性 -->
<div class="static" v-bind:class="{ active: isActive, "text-danger": hasError }"></div>
<!-- 渲染结果 -->
<div class="static active"></div>
Вы можете передать массив вv-bind:class
Тем самым устанавливая несколько атрибутов класса одновременно.
<!-- Vue对象中的data -->
<script>
... ...
data: {
activeClass: "active",
errorClass: "text-danger"
}
... ...
</script>
<!-- 绑定class到计算属性 -->
<div v-bind:class="[activeClass, errorClass]"></div>
<!-- 渲染结果 -->
<div class="active text-danger"></div>
<!-- 使用三目运算符,始终添加errorClass,只在isActive为true时添加activeClass -->
<div v-bind:class="[isActive ? activeClass : "", errorClass]"></div>
<!-- 在数组中使用对象可以避免三目运算符的繁琐 -->
<div v-bind:class="[{ active: isActive }, errorClass]"></div>
при использовании на пользовательских компонентахclass
свойства, эти свойства будут добавлены к корневому элементу компонента, эта возможность также относится кv-bind:class
.
<!-- 声明一个组件 -->
<script>
Vue.component("my-component", {
template: "<p class="foo bar">Hi</p>",
data: {
isActive: true
},
})
</script>
<!-- 添加2个class属性 -->
<my-component class="baz boo"></my-component>
<!-- 渲染结果 -->
<p class="foo bar baz boo">Hi</p>
<!-- 使用v-bind:class -->
<my-component v-bind:class="{ active: isActive }"></my-component>
<!-- 渲染结果 -->
<p class="foo bar active">Hi</p>
v-bind:style
Связывание HTMLstyle
Атрибуты.
<script>
... ...
data: {
styleObject: {
color: "red",
fontSize: "13px"
},
styleHeight: {
height: 10rem;
}
styleWidth: {
width: 20rem;
}
}
... ...
</script>
<div v-bind:style="styleObject"></div>
<!-- 使用数组可以将多个样式合并到一个HTML元素上面 -->
<div v-bind:style="[styleHeight, styleWidth]"></div>
использоватьv-bind:style
Vue автоматически добавитпрефикс префикс, общие префиксы префиксов следующие:
-
-webkit-
Chrome, Safari, новые версии Opera, все браузеры iOS (включая Firefox для iOS), почти все браузеры с ядром WebKit. -
-moz-
Для браузера Фаерфокс. -
-o-
Старые версии Opera, не использующие ядро WebKit. -
-ms-
Браузеры Microsoft IE и Edge.
Использование выражений JavaScript
Vue обеспечивает поддержку выражений JavaScript для всех привязок данных, но каждая привязка может использовать только1выражение.
<span>{{ number + 1 }}</span>
<button>{{ ok ? "YES" : "NO" }}</button>
<p>{{ message.split("").reverse().join("") }}</p>
<div v-bind:id=""list-" + id"></div>
<!-- 这是语句,不是表达式 -->
{{ var a = 1 }}
<!-- if流程控制属于多个表达式,因此不会生效,但可以使用三元表达式 -->
{{ if (ok) { return message } }}
Двусторонняя привязка данных v-модели
v-model
Инструкция по сутиv-on
а такжеv-bind
подслащенный синтаксис, директива получаетvalue属性
, запускает новое значение, когда есть новое значениеinput事件
.
<!-- 使用v-model的版本 -->
<input v-model="something">
<!-- 使用v-on和v-bind的版本 -->
<input v-bind:value="something"
v-on:input="something = $event.target.value">
<!-- 也可以自定义输入域的响应式绑定 -->
<custom-input
v-bind:value="something"
v-on:input="something = arguments[0]">
</custom-input>
Поля ввода, такие как переключатели и флажки, используют атрибут value для других целей, поэтому их можно передавать через интерфейс компонента.
model
Варианты предотвращения конфликтов:
встроенные директивы
с участиемv-
Префикс, который будет реагировать на DOM при изменении значения выражения. Команды могут быть получены с последующим:
указанопараметр(Получается атрибутом arg внутри инструкции), или с.
началомодификатор(Указывает, что директива связана особым образом).
<p v-if="seen">Hello world!</p>
<!-- 绑定事件 -->
<a v-bind:href="url"></a>
<!-- 绑定属性 -->
<a v-on:click="doSomething">
<!-- .prevent修饰符会告诉v-on指令对于触发的事件调用event.preventDefault() -->
<form v-on:submit.prevent="onSubmit"></form>
Vue этоv-bind
а такжеv-on
Эти две часто используемые директивы обеспечивают сокращенные формы:
а также@
.
<!-- v-bind -->
<a v-bind:href="url"></a>
<a :href="url"></a>
<!-- v-on -->
<a v-on:click="doSomething"></a>
<a @click="doSomething"></a>
В настоящее время Vue находится в2.4.2Версия предоставляет следующие встроенные директивы:
<html
v-text = "更新元素的textContent"
v-html = "更新元素的innerHTML"
v-show = "根据表达式的true/false,切换HTML元素的display属性"
v-for = "遍历内部的HTML元素"
v-pre = "跳过表达式渲染过程,可以显示原始的Mustache标签"
v-cloak = "保持在HTML元素上直到关联实例结束编译,可以隐藏未编译的Mustache"
v-once = "只渲染元素和组件一次"
></html>
<!-- 根据表达式的true和false来决定是否渲染元素 -->
<div v-if="type === "A"">A</div>
<div v-else-if="type === "B"">B</div>
<div v-else-if="type === "C"">C</div>
<div v-else>Not A/B/C</div>
<!-- 动态地绑定属性或prop到表达式 -->
<p v-bind:attrOrProp
.prop = "被用于绑定DOM属性"
.camel = "将kebab-case特性名转换为camelCase"
.sync = "语法糖,会扩展成一个更新父组件绑定值的v-on监听器"
></p>
<!-- 绑定事件监听器 -->
<button
v-on:eventName
.stop = "调用event.stopPropagation()"
.prevent = "调用event.preventDefault()"
.capture = "添加事件监听器时使用capture模式"
.self = "当事件是从监听器绑定的元素本身触发时才触发回调"
.native = "监听组件根元素的原生事件"-
.once = "只触发一次回调"
.left = "点击鼠标左键触发"
.right = "点击鼠标右键触发"
.middle = "点击鼠标中键触发"
.passive = "以{passive: true}模式添加监听器"
.{keyCode | keyAlias} = "触发特定键触事件"
>
</button>
<!-- 表单控件的响应式绑定 -->
<input
v-model
.lazy = "取代input监听change事件"
.number = "输入字符串转为数字"
.trim = "过滤输入的首尾空格" />
компоненты
Компоненты могут расширять функциональность HTML-элементов и инкапсулировать многократно используемый код. в состоянии пройтиVue.component( id, [definition] )
Зарегистрируйтесь или получите глобальные компоненты.
// 注册组件,传入一个扩展过的构造器
Vue.component("my-component", Vue.extend({ ... }))
// 注册组件,传入一个option对象(会自动调用Vue.extend)
Vue.component("my-component", { ... })
// 获取注册的组件(始终返回构造器)
var MyComponent = Vue.component("my-component")
Следующий код создает экземпляр Vue и помещает пользовательский компонентmy-component
Монтировать в HTML.
<script>
// 注册自定义组件
Vue.component("my-component", {
template: "<div>A custom component!</div>"
})
// 创建Vue根实例
new Vue({
el: "#example"
})
</script>
<!-- 原始模板 -->
<div id="example">
<my-component></my-component>
</div>
<!-- 渲染结果 -->
<div id="example">
<div>A custom component!</div>
</div>
- является собственностью
Выражения Vue не отображаются до тех пор, пока браузер не проанализирует HTML, но такие вещи, как<ul> <ol> <table> <select>
ограничивает элементы HTML, которые можно обернуть, в то время как<option>
Может отображаться только внутри определенных элементов HTML, что приводит к неправильному отображению выражений Vue. Таким образом, Vue предоставляетis
как псевдоним свойства для решения проблемы.
<!-- 不正确的方式 -->
<table>
<my-row>...</my-row>
</table>
<!-- 使用is的正确方式 -->
<table>
<tr is="my-row"></tr>
</table>
- данные должны быть функцией
Vue.component()
Свойство входящих данных не может быть объектом, оно должно быть функцией. Цель этого состоит в том, чтобы избежать повторного использования компонента в нескольких местах одного и того же шаблона, просто возврат объекта приведет к тому, что данные между компонентами будут загрязнены друг другом.Эту проблему можно полностью избежать, возвращая новые данные объект каждый раз через функцию.
Vue.component("simple-counter", {
template: "<button v-on:click="counter += 1">{{ counter }}</button>",
data: function () {
return {
a: "",
b: ""
}
}
});
- Связь между родительским и дочерним компонентами
родительский компонент черезprops
Передайте данные дочерним компонентам, а дочерние компоненты передаютevents
Отправить сообщение родительскому компоненту, т.е.props down, events up.
Взаимодействие компонентов родитель-потомок
props
Хотя область действия каждого компонента независима, к нему можно получить доступ черезprops属性
Передать данные дочерним компонентам, что являетсяОднонаправленный поток данныхформа проявления.
Vue.component("child", {
// 声明props
props: ["message"],
// 和data属性一样,prop也可以在vm通过this.message进行引用
template: "<span>{{ message }}</span>"
})
Не изменяйте реквизиты внутри подкомпонентов, это вызовет ошибку в фоновом режиме.
изменение имени
Поскольку HTML не чувствителен к регистру, kebab-case(горб) Стиль имени props, в сборке будет в стиле camelCased(разделены тире) стиль принят.
<!-- camelCase in JavaScript -->
<script>
Vue.component("child", {
props: ["myMessage"],
template: "<span>{{ myMessage }}</span>"
})
<script>
<!-- kebab-case in HTML -->
<child my-message="hello!"></child>
динамический реквизит
в состоянии пройтиv-bind
Директивы, которые реактивно связывают данные родительского компонента с реквизитами дочернего компонента. Когда данные родительского компонента изменяются, это изменение также распространяется на дочерний компонент.
<div>
<input v-model="parentMsg">
<br>
<child v-bind:my-message="parentMsg"></child>
</div>
использоватьv-bind
Значение параметра может быть проанализировано как выражение JavaScript, иначе все входящие реквизиты будут рассматриваться дочерним компонентом как строковый тип.
<!-- 传递的是字符串"1" -->
<comp some-prop="1"></comp>
<!-- 传递实际的 number -->
<comp v-bind:some-prop="1"></comp>
Проверить реквизиты
Для реквизита компонента можно указать правила валидации, если поступающие данные не соответствуют требованиям, Vue выдаст соответствующее предупреждение, что может эффективно повысить надежность компонента.
Vue.component("example", {
props: {
// 基础类型检测
propA: Number,
// 多种类型
propB: [String, Number],
// 必传且是字符串
propC: {
type: String,
required: true
},
// 数字,有默认值
propD: {
type: Number,
default: 100
},
// 数组或对象的默认值由1个工厂函数返回
propE: {
type: Object,
default: function () {
return { message: "hello" }
}
},
// 自定义验证函数
propF: {
validator: function (value) {
return value > 10
}
}
}
});
props
Проверка выполняется перед созданием экземпляра компонента.
Нереквизитные свойства компонента
Компоненты могут получать любые входящие атрибуты, и эти атрибуты будут добавлены к корневому элементу HTML-шаблона компонента (Независимо от того, определено ли это в реквизитах).
<!-- 带有属性的自定义组件 -->
<bs-date-input
data-3d-date-picker="true"
class="date-picker-theme-dark">
</bs-date-input>
<!-- 渲染出来的组件,class属性被合并 -->
<input type="date" data-3d-date-picker="true" class="form-control date-picker-theme-dark">
Свойства, передаваемые от родительского компонента к дочернему, могут переопределять свойства самого дочернего компонента, что приводит к повреждению и загрязнению дочернего компонента.
мероприятие
Дочерние компоненты могут взаимодействовать с родительскими компонентами через пользовательские события Vue.
Каждый экземпляр Vue реализует следующий API, но вы не можете прослушивать всплывающее событие дочерних компонентов напрямую через $on, а должны использовать директиву v-on.
-
$on(eventName)
прослушать событие -
$emit(eventName)
триггерное событие
$on
а также$emit
нетaddEventListener
а такжеdispatchEvent
псевдоним.
<div id="counter-event-example">
<p>{{ total }}</p>
<button-counter v-on:increment="incrementTotal"></button-counter>
<button-counter v-on:increment="incrementTotal"></button-counter>
</div>
<script>
Vue.component("button-counter", {
template: "<button v-on:click="incrementCounter">{{ counter }}</button>",
data: function () {
return {
counter: 0
}
},
methods: {
// 子组件事件
incrementCounter: function () {
this.counter += 1
this.$emit("increment") //向父组件冒泡事件
}
},
})
new Vue({
el: "#counter-event-example",
data: {
total: 0
},
methods: {
// 父组件事件
incrementTotal: function () {
this.total += 1
}
}
})
</script>
-
.native
модификатор
Разработчики также могут прослушивать собственные события в корневом элементе компонента, что требует помощи.native
модификатор.
<my-component v-on:click.native="doTheThing"></my-component>
-
.sync
модификатор
во Вьюprops
По сути, реактивное связывание не может быть выполнено, чтобы предотвратить разрушение одностороннего потока данных и привести к тому, что несколько дочерних компонентов загрязнят состояние родительского компонента. Но в производственных условияхprops
Потребность в реактивных привязках реальна. Поэтому Vue будет.sync
Модификатор инкапсулирован как синтаксис с сахарной оболочкой.После того, как родительский компонент использует модификатор в реквизитах дочернего компонента, родительский компонент автоматически привязывает реквизитыv-on
событие, дочерний компонент будет сообщать родительскому компоненту при прослушивании изменений реквизита$emit
обновить событие, чтобы родительский компонентprops
Возможность синхронизации с дочерними компонентами.
<!-- 使用.sync修饰符 -->
<comp :foo.sync="bar"></comp>
<!-- 被自动扩展为如下形式,该组件的子组件会通过this.$emit("update:foo", newValue)显式触发更新事件 -->
<comp :foo="bar" @update:foo="val => bar = val"></comp>
- Параллельная связь компонентов
Когда взаимодействуют компоненты, которые не находятся в отношениях родитель-потомок, вы можете использоватьнулевойЭкземпляр Vue какцентральный автобус событий.
var bus = new Vue()
// 触发组件A中的事件
bus.$emit("id-selected", 1)
// 在组件B监听事件
bus.$on("id-selected", function (id) {
... ... ...
})
Лучшим способом является использование библиотеки управления состоянием потока, такой как VueX или Redux.
slot
Вы можете смешать содержимое родительского компонента с шаблоном дочернего компонента, и вы можете использовать его в дочернем компоненте<slot>
Слот, который является содержимым родительского компонента.
Содержимое шаблона родительского компонента компилируется в области родительского компонента, а содержимое шаблона дочернего компонента компилируется в области дочернего компонента.
анонимный слот
Когда дочерний компонент имеет только один без атрибутов<slot>
, весь фрагмент содержимого родительского компонента будет вставлен в позицию DOM, где находится слот, заменив саму метку слота.
<!-- 子组件my-component的模板 -->
<div>
<h2>Child</h2>
<slot>
父组件没有需要插入的内容时显示
</slot>
</div>
<!-- 父组件模板中使用my-component -->
<div>
<h1>Parent</h1>
<child>
<p>Content 1</p>
<p>Content 2</p>
</child>
</div>
<!-- 渲染结果 -->
<div>
<h1>Parent</h1>
<div>
<h2>Child</h2>
<p>Content 1</p>
<p>Content 2</p>
</div>
</div>
<slot>
Содержимое тега компилируется в области действия дочернего компонента и отображается только тогда, когда родительскому компоненту нечего вставлять.
именованный слот
в состоянии пройти<slot>
элементальname
свойства, чтобы настроить способ распространения контента.
<!-- 子组件 -->
<div id="app">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
<!-- 父组件 -->
<app>
<div slot="header">Header</div>
<p>Content 1</p>
<p>Content 2</p>
<div slot="footer">Footer</div>
</app>
<!-- 渲染结果 -->
<div id="app">
<header>
<div>Header</div>
</header>
<main>
<p>Content 1</p>
<p>Content 2</p>
</main>
<footer>
<p>Footer</p>
</footer>
</div>
Анонимный слот действует как слот для фрагмента родительского компонента без соответствующего содержимого.
слот с прицелом
дочерний компонент черезprops
передавать данные в<slot>
слот, родительский компонент используетscope
атрибут<template>
для представления шаблона, представляющего текущий слот,scope
Переменная, соответствующая значению, получит объект реквизита, переданный дочерним компонентом.
<!-- 子组件通过props传递数据给插槽 -->
<div class="child">
<slot text="hello from child"></slot>
</div>
<!-- 父组件使用带有scope属性的<template> -->
<div class="parent">
<child>
<template scope="props">
<span>hello from parent</span>
<span>{{ props.text }}</span>
</template>
</child>
</div>
<!-- 渲染结果 -->
<div class="parent">
<div class="child">
<span>hello from parent</span>
<span>hello from child</span>
</div>
</div>
функциональный компонент
то есть без гражданства (нет данных) нет экземпляра (нет этого контекста) компоненты, накладные расходы на рендеринг невелики и не будут отображаться вVue devtools
среди.
Vue.component("my-component", {
functional: true,
// 通过提供context参数为没有实例的函数组件提供上下文信息
render: function (createElement, context) {},
// Props可选
props: {}
})
динамические компоненты
использовать<component>
элемент и динамически привязать егоis
Атрибут, который позволяет нескольким компонентам использовать одну и ту же точку монтирования объекта Vue и обеспечивать динамическое переключение.
<script>
var vm = new Vue({
el: "#example",
data: {
currentView: "home"
},
components: {
home: { /* ... */ },
posts: { /* ... */ },
archive: { /* ... */ }
}
})
</script>
<component v-bind:is="currentView">
<!-- 组件在vm.currentview变化时改变! -->
</component>
Если вам нужно сохранить переключаемый компонент в памяти, сохранить его состояние и избежать повторного рендеринга, вы можете использовать встроенный в Vuekeep-alive
инструкция.
<keep-alive>
<component :is="currentView">
<!-- 非活动组件将被缓存! -->
</component>
</keep-alive>
Компоненты загружаются асинхронно
Vue позволяет определять компоненты как фабричные функции для асинхронного разрешения определений компонентов. Vue запускает фабричную функцию только тогда, когда компонент выполняет рендеринг, и кэширует результат для последующего рендеринга. Фабричная функция, определяющая компонент, получит разрешение(Вызывается при получении параметров компонента Vue, загруженных с сервера.) и отклонить (Вызывается, когда не удалось загрузить параметры удаленного компонента Vue.) функция обратного вызова в качестве параметра.
Vue.component("async-example", function (resolve, reject) {
setTimeout(function () {
// 将组件定义传递到resolve回调函数当中
resolve({
template: "<div>I am async!</div>"
})
}, 1000)
})
Можно комбинировать с предоставленным Webpackвырезание кодаФункция извлекает объект параметров компонента Vue в отдельный файл JavaScript для обеспечения асинхронной загрузки по запросу.
// 使用webpack的require()来进行异步代码块切割
Vue.component("async-webpack-example", function (resolve) {
require(["./my-async-component"], resolve)
})
// 使用webpack的import()来进行异步代码块切割
Vue.component(
"async-webpack-example", () => import("./my-async-component")
)
Начиная с Vue 2.3.0, асинхронный компонент можно определить следующим образом.
const AsyncWebpackExample = () => ({
component: import("./MyComp.vue"), // 需要加载的组件
loading: LoadingComp, // loading时渲染的组件
error: ErrorComp, // 出错时渲染的组件
delay: 200, // 渲染loading组件前的等待时间(默认:200ms)
timeout: 3000 // 最长等待时间,超出则渲染error组件(默认:Infinity)
})
Чтобы использовать этот метод записи на компонент маршрутизации, вам необходимо использовать Vue-маршрутизатор версии 2.4.0 или более поздней версии.
Циклические ссылки на компоненты
Циклическая ссылка, то есть два компонента ссылаются друг на друга, например, в следующем кодеtree-folder
,tree-folder-contents
Два компонента становятся родительским или дочерним узлом друг друга одновременно, если вы используете инструмент модульного управления Webpack.requiring
/importing
компонент, он сообщитFailed to mount component: template or render function not defined.
ошибка.
<template>
<p>
<span>{{ folder.name }}</span>
<tree-folder-contents :children="folder.children"/>
</p>
</template>
<template>
<ul>
<li v-for="child in children">
<tree-folder v-if="child.children" :folder="child"/>
<span v-else>{{ child.name }}</span>
</li>
</ul>
</template>
потому чтоtree-folder
,tree-folder-contents
После обращения друг к другу невозможно определить порядок загрузки компонентов в бесконечный цикл, поэтому необходимо заранее указать приоритет загрузки компонентов вебпака. Чтобы решить проблему циклической ссылки компонентов Vue в приведенном выше примере, вы можетеtree-folder
компонентbeforeCreate()
Проблемы, вызванные регистрацией в функции жизненного циклаtree-folder-contents
компоненты.
beforeCreate: function () {
this.$options.components.TreeFolderContents = require("./tree-folder-contents.vue").default
}
Соглашение об именах компонентов
Может использоваться при именовании компонентов в JavaScript.kebab-case
,camelCase
,PascalCase
, но только в шаблонах HTMLkebab-case
Формат.
<kebab-cased-component></kebab-cased-component>
<camel-cased-component></camel-cased-component>
<pascal-cased-component></pascal-cased-component>
<!-- 也可以通过自关闭方式使用组件 -->
<kebab-cased-component />
<script>
components: {
"kebab-cased-component": {},
"camelCasedComponent": {},
"PascalCasedComponent": {}
}
</script>
Рекомендуется передать в JavaScript
PascalCase
способ объявления компонентов в HTML черезkebab-case
способ использования компонентов.
компонентная рекурсия
Когда локально зарегистрированный компонент Vue рекурсивно вызывает сам себя, его необходимо добавить при создании компонента.name
вариант, вы можете опустить это свойство для глобально зарегистрированных компонентов, потому что Vue добавит его автоматически.
// 局部注册
new Vue({
el: "#my-component",
name: "my-component",
template: "<div><my-component></my-component></div>"
})
// 全局注册
Vue.component("my-component", {
// name: "my-component", 可以省略name属性
template: "<div><my-component></my-component></div>"
})
Когда компонент рекурсивно появляется в бесконечном цикле, он предложит
max stack size exceeded
ошибка, поэтому вам нужно убедиться, что рекурсивные операции имеют условие завершения (Например, используйте v-if и верните false).
шаблон компонента
- Может использоваться на компонентах Vue
inline-template
атрибут, компонент будет отображать встроенный HTML-контент как шаблон для самого компонента, а не какslot
распространяемый контент.
<my-component inline-template>
<div>
<p>These are compiled as the component"s own template.</p>
<p>Not parent"s transclusion content.</p>
</div>
</my-component>
- также через
<script>
Использование на этикеткеtype="text/x-template"
а такжеid
атрибут для определения встроенного шаблона.
<script type="text/x-template" id="hello-world-template">
<p>Hello hello hello</p>
</script>
<script>
Vue.component("hello-world", {
template: "#hello-world-template"
})
</script>
Управление состоянием Vuex
Vuex — это режим управления состоянием, специально предназначенный для приложений Vue.Ядром каждого приложения Vuex являетсяstore
(склад), который загружает приложениеstate
(условие) контейнеры, обычно только один на приложениеstore
пример.
Процесс выполнения Vuex
вексstate
является отзывчивым, т.е.store
серединаstate
При возникновении изменений соответствующие компоненты также будут обновлены, измененыstore
средиstate
Единственный способ - податьmutations
.
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
}
})
store.commit("increment") // 通过store.state来获取状态对象
console.log(store.state.count) // 通过store.commit()改变状态
State
отstore
Залезайstate
Самый простой способ - вернуть указанное в вычисляемом свойствеstate
,в любое времяstate
Когда происходит изменение, вычисляемое свойство выполняется повторно, и связанная с ним модель DOM обновляется.
const Counter = {
template: `<div>{{ count }}</div>`,
computed: {
count () {
return store.state.count
}
}
}
Vuex предоставляетstore
вариант, будетstate
из корневого компонентаинъекцияв каждый подкомпонент, чтобы избежать частыхimport store
.
// 父组件中注册store属性
const app = new Vue({
el: "#app",
store: store,
components: { Counter },
template: `
<div class="app">
<counter></counter>
</div>`
})
// 子组件,store会注入到子组件,子组件可通过this.$store进行访问
const Counter = {
template: `<div>{{ count }}</div>`,
computed: {
count () {
return this.$store.state.count
}
}
}
Vuex предоставляетmapState()
Вспомогательная функция, чтобы избежать использования несколькихstate
В случае , объявите вычисляемое свойство несколько раз.
// 在单独构建的版本中辅助函数为 Vuex.mapState
import { mapState } from "vuex"
export default {
computed: mapState({
count: state => state.count,
// 传递字符串参数"count"等同于`state => state.count`
countAlias: "count",
countPlusLocalState (state) {
return state.count + this.localCount
}
})
}
// 当计算属性名称与state子节点名称相同时,可以向mapState传递一个字符串数组
computed: mapState([
"count" // 映射this.count到store.state.count
])
mapState()
Функция возвращаетstate
Объект связанного вычисляемого свойства, здесь вы можете использовать оператор расширения объекта ES6....
Сравните этот объект с собственным компонентом Vue.computed
свойства объединены.
computed: {
localComputed () {},
...mapState({})
}
Vuex позволяетstore
определено вgetters
(Вычисляемые свойства, которые можно рассматривать как хранилища),getters
Возвращаемое значение кэшируется в соответствии с его зависимостями и будет пересчитываться только в том случае, если значение зависимости изменилось. Метод получаетstate
В качестве первого параметра другиеgetters
как второй параметр. прямо вstore
звонитьgetters
чтобы получить указанное расчетное значение.
const store = new Vuex.Store({
state: {
todos: [
{ id: 1, text: "...", done: true },
{ id: 2, text: "...", done: false }
]
},
getters: {
doneTodos: (state, getters) => {
return state.todos.filter(todo => todo.done)
}
}
})
// 获取doneTodos = [{ id: 1, text: "...", done: true }]
store.getters.doneTodos
Это упрощает основуstore
существующий вstate
производный новыйstate
, чтобы избежать избыточности кода при повторном использовании в нескольких компонентах.
computed: {
doneTodosCount () {
return this.$store.getters.doneTodos // 现在可以方便的在Vue组件使用store中定义的doneTodos
}
}
предоставлено VuexmapGetters()
Вспомогательная функция будетstore
серединаgetters
Сопоставляется с локальными вычисляемыми свойствами.
import { mapGetters } from "vuex"
export default {
computed: {
// 使用对象展开运算符将getters混入computed计算属性
...mapGetters([
"doneTodosCount",
doneCount: "doneTodosCount" // 映射store.getters.doneTodosCount到别名this.doneCount
])
}
}
Mutations
Единственный способ изменить хранилище в состоянии — отправить мутацию ([mjuː”teɪʃ(ə)n] п. Изменение), мутации аналогичны пользовательским событиям со строковым типом события и функцией обратного вызова (Получение состояния в качестве параметра - это то место, где состояние изменяется).
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
// 触发类型为increment的mutation时被调用
increment (state) {
state.count++ // 变更状态
}
}
})
// 触发mutation
store.commit("increment")
можно через магазинcommit()
Метод запускает указанные мутации, которые также можно передать черезstore.commit()
Передать параметры в мутацию.
// commit()
store.commit({
type: "increment",
amount: 10
})
// store
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
Рекомендуется использовать константы для типов событий мутации и размещать эти константы в отдельных файлах для облегчения управления и предотвращения дублирования.
// mutation-types.js
export const SOME_MUTATION = "SOME_MUTATION"
// store.js
import Vuex from "vuex"
import { SOME_MUTATION } from "./mutation-types"
const store = new Vuex.Store({
state: { ... },
mutations: {
// 可以通过ES6的计算属性命名特性去使用常量作为函数名
[SOME_MUTATION] (state) {
// mutate state
}
}
})
mutation()
Должна быть синхронной функцией, потому что devtool не может отслеживать обратные вызовы.state
Произведена асинхронная модификация.
Компоненты Vue могут использоватьthis.$store.commit("xxx")
Отправьте мутацию или используйтеmapMutations()
Поместите компонент Vue вmethods
карты наstore.commit
вызов (необходимо ввести в корневом узлеstore
).
import { mapMutations } from "vuex"
export default {
methods: {
...mapMutations([
"increment" // 映射this.increment()为this.$store.commit("increment")
]),
...mapMutations({
add: "increment" // 映射this.add()为this.$store.commit("increment")
})
}
}
Actions
Действие используется для отправки изменения, а действие может содержать асинхронные операции. Функция Action принимает экземпляр с теми же методами и свойствами, что и экземпляр хранилища.context
объект, поэтому к нему можно получить доступ, вызвавcontext.commit
отправить мутацию или черезcontext.state
а такжеcontext.getters
чтобы получить государство и быту.
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit("increment")
}
}
})
В производственной среде код может быть упрощен с использованием параметров деструктации ES6.
actions: {
// 直接向action传递commit方法
increment ({ commit }) {
commit("increment")
}
}
ActionPassstore.dispatch()
способ распространения,mutationв котором толькоСинхронизироватьоперация, при этомactionможно сделать внутриасинхронныйоперация. Ниже приведен пример корзины покупок с несколькими мутациями, распределенными в коде и асинхронными операциями API.
actions: {
checkout ({ commit, state }, products) {
const savedCartItems = [...state.cart.added] // 把当前购物车的物品备份起来
commit(types.CHECKOUT_REQUEST) // 发出结账请求,然后清空购物车
// 购物Promise分别接收成功和失败的回调
shop.buyProducts(
products,
() => commit(types.CHECKOUT_SUCCESS), // 成功操作
() => commit(types.CHECKOUT_FAILURE, savedCartItems) // 失败操作
)
}
}
можно использовать компонентыthis.$store.dispatch("xxx")
отправить действие или использоватьmapActions()
компонентаmethods
карты наstore.dispatch
(Необходимо вводить в корневой узелstore
).
import { mapActions } from "vuex"
export default {
methods: {
...mapActions([
"increment" // 映射this.increment()为this.$store.dispatch("increment")
]),
...mapActions({
add: "increment" // 映射this.add()为this.$store.dispatch("increment")
})
}
}
store.dispatch
может справитьсяaction
возвращается функцией обратного вызоваPromise
,а такжеstore.dispatch
сам по-прежнему возвращаетPromise
.
actions: {
// 定义一个返回Promise对象的actionA
actionA ({ commit }) {
return new Promise((resolve, reject) => {
setTimeout(() => {
commit("someMutation") // 触发mutation
resolve()
}, 1000)
})
},
// 也可以在actionB中分发actionA
actionB ({ dispatch, commit }) {
return dispatch("actionA").then(() => {
commit("someOtherMutation") // 触发另外一个mutation
})
}
}
// 现在可以分发actionA
store.dispatch("actionA").then(() => {
... ... ...
})
Вы можете испытать функции асинхронной обработки ES7.async
/await
совмещать действия.
actions: {
async actionA ({ commit }) {
commit("gotData", await getData())
},
async actionB ({ dispatch, commit }) {
await dispatch("actionA") //等待actionA完成
commit("gotOtherData", await getOtherData())
}
}
Module
В случае единого дерева состояний для всего приложения все состояния будут централизованы в одном объекте хранилища, поэтому хранилище может сильно раздуться. Поэтому Vuex позволяет разрезать хранилище на модули (module), каждый модуль имеет свой собственныйstate
,mutation
,action
,getter
или даже вложенные подмодули.
const moduleA = {
state: {},
mutations: {},
actions: {},
getters: {}
}
const moduleB = {
state: {},
mutations: {},
actions: {}
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // moduleA的状态
store.state.b // moduleB的状态
внутри модуляmutations()
а такжеgetters()
Первый полученный параметр — это локальный объект состояния модуля.
const moduleA = {
state: { count: 0 },
mutations: {
increment (state) {
state.count++ // 这里的state是模块的局部状态
}
},
getters: {
doubleCount (state) {
return state.count * 2
}
}
}
Среди внутренних действий модуля можно пройтиcontext.state
получить местное состояние иcontext.rootState
Получить глобальное состояние.
const moduleA = {
// ...
actions: {
incrementIfOddOnRootSum ({ state, commit, rootState }) {
if ((state.count + rootState.count) % 2 === 1) {
commit("increment")
}
}
}
}
внутри модуляgetters()
метод, который может получать глобальное состояние через свой третий параметр.
const moduleA = {
getters: {
sumWithRootCount (state, getters, rootState) {
return state.count + rootState.count
}
}
}
строгий режим
В строгом режиме, если изменение состояния не вызваноmutation()
функция вызывает выдачу ошибки. Просто создайтеstore
при входящемstrict: true
включить строгий режим.
const store = new Vuex.Store({
strict: true
})
Не включайте строгий режим в рабочей среде, потому что он глубоко обнаружит незаконные изменения состояния, что приведет к ненужным потерям производительности.Мы можем избежать этого, добавив следующие суждения в инструмент сборки.
const store = new Vuex.Store({
strict: process.env.NODE_ENV !== "production"
})
В строгом режиме использование инструкции v-model для состояния, принадлежащего Vuex, вызовет ошибку.В этом случае вам нужно вручную привязать значение и прослушать события ввода и изменения, а также вручную отправить действие в обратном вызове события. . Другой способ сделать это — напрямую переопределить методы get и set вычисляемых свойств.
Суммировать
- Состояние уровня приложения должно быть централизовано водин объект магазинасередина.
- представить
mutation
единственный способ изменить состояние, и процессСинхронизироватьиз. -
асинхронныйлогика должна быть заключена в
action
в.
Webpack Vue Loader
vue-loaderэто загрузчик Webpack, предоставляемый сообществом Vue с открытым исходным кодом для.vue
Однофайловые компоненты Postfix преобразуются в модули JavaScript, каждый.vue
Однофайловый компонент может включать в себя следующие части:
- Один
<template>
. - Один
<script>
. - несколько
<style>
.
<template>只能有1个</template>
<script>只能有1个</script>
<style>可以有多个</style>
<style>可以有多个</style>
<style>可以有多个</style>
Область действия CSS
К.vue
компонент одного файла<style>
Добавить на этикеткуscoped
свойства, которые позволяют<style>
Стили в теге применяются только к текущему компоненту. При использовании области действия селектор стиля должен попытаться использовать класс или идентификатор, чтобы улучшить производительность рендеринга страницы.
<!-- 单文件组件定义 -->
<style scoped>
.example {
color: red;
}
</style>
<template>
<div class="example">Hank</div>
</template>
<!-- 转换结果 -->
<style>
.example[data-v-f3f3eg9] {
color: blue;
}
</style>
<template>
<div class="example" data-v-f3f3eg9>Hank</div>
</template>
Ленты можно использовать одновременно в одном компоненте
scoped
с атрибутом и без атрибута<style/>
, которые используются для определения личных стилей компонентов и глобальных стилей соответственно.
Модульность CSS
в одном файловом компоненте.vue
из<style>
добавить на ярлыкmodule
properties, чтобы включить модульность CSS.CSS Modulesдля модульной композиции CSS,vue-loaderБыла интегрирована функция модульности CSS.
<style module>
.red {
color: red;
}
.bold {
font-weight: bold;
}
</style>
Модуль CSS внедряет компонент Vue с именем$style
Вычисляемые свойства, которые реализованы в компоненте<template/>
использовать динамическийclass
свойство для привязки.
<template>
<p :class="$style.red">
This should be red
</p>
</template>
анимация
Vue вставляет, обновляет, удаляетDOMКогда доступны следующие способы отображения записи (entering) и уходи (leaving) Эффекты перехода.
- Применяйте классы в переходах и анимации CSS.
- DOM непосредственно управляется в функции перехода ловушки.
- Используйте библиотеки анимации CSS, JavaScript, такие какAnimate.css,Velocity.js.
переходный компонент
Vue предоставляет встроенные компоненты<transition/>
Чтобы добавить эффекты анимации перехода к элементам HTML и компонентам Vue, вы можетеУсловное отображение(использоватьv-if
илиv-show
),динамические компоненты,Показать корневой узел компонентаРендеринг в деле.<transition/>
В основном используется для обработки одного узла или одновременного рендеринга одного из нескольких узлов.
Имя класса автоматически переключаемого класса
Введите компонент или HTML (entering) и уходи (leaving) в эффекте перехода Vue автоматически переключится и применит шесть имен классов, показанных на рисунке ниже.
Класс для автоматического переключения эффектов перехода
можно использовать<transition/>
изname
атрибут для автоматического создания имен переходных классов, например, в следующем примереname: "fade"
автоматически расширится до.fade-enter
,.fade-enter-active
Ждать,name
По умолчанию имя класса свойства по умолчанию —v
.
<div id="demo">
<button v-on:click="show = !show"> Toggle </button>
<transition name="fade">
<p v-if="show">hello</p>
</transition>
</div>
<script>
new Vue({
el: "#demo",
data: {
show: true
}
})
</script>
<style>
.fade-enter-active, .fade-leave-active {
transition: opacity .5s
}
.fade-enter, .fade-leave-to {
opacity: 0
}
</style>
Пользовательское имя класса CSS
комбинироватьAnimate.css
При использовании вы можете<transition/>
Среди них имя класса настраивается с помощью следующих атрибутов.
<transition
enter-class = "animated"
enter-active-class = "animated"
enter-to-class = "animated"
leave-class = "animated"
leave-active-class = "animated"
leave-to-class = "animated">
</transition>
Пользовательские хуки JavaScript
комбинироватьVelocity.js
При использовании установите функцию ловушки в свойстве через v-on.
<transition
v-on:before-enter="beforeEnter"
v-on:enter="enter"
v-on:after-enter="afterEnter"
v-on:enter-cancelled="enterCancelled"
v-on:before-leave="beforeLeave"
v-on:leave="leave"
v-on:after-leave="afterLeave"
v-on:leave-cancelled="leaveCancelled">
</transition>
<script>
// ...
methods: {
beforeEnter: function (el) {},
enter: function (el, done) { done() },
afterEnter: function (el) {},
enterCancelled: function (el) {},
beforeLeave: function (el) {},
leave: function (el, done) { done() },
afterLeave: function (el) {},
leaveCancelled: function (el) {} // 仅用于v-show
}
</script>
Явно задайте продолжительность перехода
можно использовать<transition>
Вверхduration属性
Установите явную продолжительность перехода в миллисекундах.
<transition :duration="1000"> Hank </transition>
<!-- 可以分别定制进入、移出的持续时间 -->
<transition :duration="{ enter: 500, leave: 800 }"> Hank </transition>
Переход при первом рендеринге компонента
пройти через<transition>
Вверхappear属性
Задает анимацию перехода при первом отображении узла компонента.
<!-- 自定义CSS类名 -->
<transition
appear
appear-class="custom-appear-class"
appear-to-class="custom-appear-to-class"
appear-active-class="custom-appear-active-class">
</transition>
<!-- 自定义JavaScript钩子 -->
<transition
appear
v-on:before-appear="customBeforeAppearHook"
v-on:appear="customAppearHook"
v-on:after-appear="customAfterAppearHook"
v-on:appear-cancelled="customAppearCancelledHook">
</transition>
Эффекты перехода для HTML-элементов
Ключевое свойство компонента Vue
Атрибут key в основном используется в алгоритме виртуального DOM Vue для различения старых и новых VNodes и не используется явно.key
, Vue будет использовать самый производительный алгоритм автоматического сравнения. явное использованиеkey
, будет основываться наkey
Изменения для изменения порядка элементов и удаления несуществующихkey
Элементы. Дочерние элементы с одним и тем же родительским элементом должны иметь уникальныеkey
, потому что повторяетсяkey
приведет к ошибкам рендеринга.
<ul>
<!-- 最常见的用法是在使用v-for的时候 -->
<li v-for="item in items" :key="item.id">...</li>
</ul>
поочередный переход элементов
Доступно через Vuev-if
а такжеv-else
Свойства для достижения альтернативных переходов нескольких компонентов, наиболее распространенным эффектом перехода является список и сообщение, описывающее, когда список пуст.
<transition>
<table v-if="items.length > 0">
<!-- ... -->
</table>
<p v-else>Sorry, no items found.</p>
</transition>
При переключении элементов с одинаковыми именами в Vue необходимо передать ключевое словоkey
В качестве отличительного знака, в противном случае Vue будет заменять содержимое только внутри одного и того же тега из соображений эффективности, поэтому<transition>
Настройки одноименных элементов в компонентахkey
ЯвляетсяЛучшие практики.
<transition>
<button v-if="isEditing" key="save"> Save </button>
<button v-else key="edit"> Edit </button>
</transition>
В некоторых сценариях это можно сделать, задав одному и тому же HTML-элементуkey
свойства устанавливают разные состояния вместо подробногоv-if
а такжеv-else
.
<!-- 通过v-if和v-else来实现 -->
<transition>
<button v-if="isEditing" key="save"> Save </button>
<button v-else key="edit"> Edit </button>
</transition>
<!-- 设置动态的key属性来实现 -->
<transition>
<button v-bind:key="isEditing"> {{ isEditing ? "Save" : "Edit" }} </button>
</transition>
А для использования несколькихv-if
Многоэлементные переходы также могут быть динамическими.key
свойства сильно упрощаются.
<!-- 多个v-if实现的多元素过渡 -->
<transition>
<button v-if="docState === "saved"" key="saved"> Edit </button>
<button v-if="docState === "edited"" key="edited"> Save </button>
<button v-if="docState === "editing"" key="editing"> Cancel </button>
</transition>
<!-- 通过动态key属性可以大幅简化模板代码 -->
<transition>
<button v-bind:key="docState"> {{ buttonMessage }} </button>
</transition>
<script>
...
computed: {
buttonMessage: function () {
switch (this.docState) {
case "saved": return "Edit"
case "edited": return "Save"
case "editing": return "Cancel"
}
}
}
</script>
Эффект перехода компонента Vue
Переходы между несколькими компонентами Vue не нужно использовать.key
свойства, просто используйтединамические компонентыВот и все.
<transition name="component-fade" mode="out-in">
<component v-bind:is="view"></component>
</transition>
<script>
new Vue({
el: "#transition-components-demo",
data: {
view: "v-a"
},
components: {
"v-a": {
template: "<div>Component A</div>"
},
"v-b": {
template: "<div>Component B</div>"
}
}
})
<script>
<style>
.component-fade-enter-active, .component-fade-leave-active {
transition: opacity .3s ease;
}
.component-fade-enter, .component-fade-leave-to {
opacity: 0;
}
<style>
Выберите режим перехода для элементов HTML или компонентов Vue.
<transition>
запись по умолчанию (enter) и уходи (leave) поведение происходит одновременно, поэтому, когда несколько элементов HTML или компонентов Vue, которые необходимо переключать и отображать, находятся в одном и том же положении, этот одновременный переход входа и выхода не может удовлетворить все потребности, Vue может пройти<transition-gruop>
компонентmode
свойства, чтобы выбрать следующие режимы перехода.
-
in-out
: сначала сменяется новый элемент, а затем после завершения смещается отображаемый в данный момент элемент. -
out-in
: Текущий отображаемый элемент переходит первым, а затем после завершения переходит новый элемент.
<transition name="fade" mode="out-in">
<button v-if="docState === "saved"" key="saved"> Edit </button>
<button v-if="docState === "edited"" key="edited"> Save </button>
<button v-if="docState === "editing"" key="editing"> Cancel </button>
</transition>
компонент переходной группы
<transition-group>
Используется для установки эффектов перехода для нескольких элементов HTML или компонентов Vue, отличных от<transition>
, компонент будет отображаться как реальный<span>
элемент, но разработчики также могут передавать<transition-group>
компонентtag
Атрибуты заменяются другими допустимыми элементами HTML.<transition-group>
Элементы внутри компонента должны предоставлять уникальныйkey
стоимость имущества.
<div id="list-demo" class="demo">
<button v-on:click="add">Add</button>
<button v-on:click="remove">Remove</button>
<transition-group name="list" tag="p">
<span v-for="item in items" v-bind:key="item" class="list-item">
{{ item }}
</span>
</transition-group>
</div>
<script>
new Vue({
el: "#list-demo",
data: {
items: [1, 2, 3, 4, 5, 6, 7, 8, 9],
nextNum: 10
},
methods: {
randomIndex: function () {
return Math.floor(Math.random() * this.items.length)
},
add: function () {
this.items.splice(this.randomIndex(), 0, this.nextNum++)
},
remove: function () {
this.items.splice(this.randomIndex(), 1)
},
}
})
</script>
<style>
.list-item {
display: inline-block;
margin-right: 10px;
}
.list-enter-active, .list-leave-active {
transition: all 1s;
}
.list-enter, .list-leave-to {
opacity: 0;
transform: translateY(30px);
}
</style>
<transition-group>
Реализованный эффект перехода по списку. При добавлении или удалении HTML-элемента другие соседние HTML-элементы мгновенно перемещаются на новую позицию, и этот процесс не является плавным переходом. Для решения этой проблемы,<transition-group>
поставкаv-moveфункция для переопределенияВо время мобильного переходаИспользуемое имя класса CSS. Включите эту функцию, вы можете пройтиname
свойства задаются вручную (в примере нижеname="flip-list"
а также.flip-list-move
), также можно использовать напрямуюmove-class
Атрибуты.
<div id="flip-list-demo" class="demo">
<button v-on:click="shuffle">Shuffle</button>
<transition-group name="flip-list" tag="ul">
<li v-for="item in items" v-bind:key="item">
{{ item }}
</li>
</transition-group>
</div>
<script>
new Vue({
el: "#flip-list-demo",
data: {
items: [1,2,3,4,5,6,7,8,9]
},
methods: {
shuffle: function () {
this.items = _.shuffle(this.items)
}
}
})
</script>
<style>
.flip-list-move {
transition: transform 1s;
}
<style>
в состоянии пройтиОтзывчивыйсвязывание<transition>
а также<transition-gruop>
Атрибут имени компонента, чтобы можно было достичь эффекта динамического перехода в соответствии с состоянием самого компонента.
<transition v-bind:name="transitionName"></transition>