Vue 2.0 出来也有一段时间了,作为一个有志向的全面发展好青年,在征服 Vue 1.x,React,React Native 后,为了之后能更快迁移公司的项目到 Vue 2.x,于是决定先看看 Vue 2.0。
鉴于部分读者可能不了解 Vue,先简单看看各种特性。
本文假设你有一定的 HTML 基础,并熟悉一种或以上编程语言(那就能看懂 JS 了)。
Vue 提供了一堆数据绑定语法。
Vue 实例,实则也就是 ViewModel(数据 + 函数),都是通过构造函数 Vue 创建的:
var data = { a: 1 } var vm = new Vue({ el: '#example', data: data, created: function () { // `this` points to the vm instance console.log('a is: ' + this.a) } }) vm.$data === data // -> true vm.$el === document.getElementById('example') // -> true // $watch is an instance method vm.$watch('a', function (newVal, oldVal) { // this callback will be called when `vm.a` changes })
Vue 实例都有自己的生命周期,比如 created, mounted, updated 以及 destroyed。所有方法被 called 的时候,this 都指向所在的 Vue 实例。
Lifecycle 图如下:
其实就是一个需要计算的 getter:
<div id="example"> <p>Original message: "{{ message }}"p> <p>Computed reversed message: "{{ reversedMessage }}"p> div>
var vm = new Vue({ el: '#example', data: { message: 'Hello' }, computed: { // a computed getter reversedMessage: function () { // `this` points to the vm instance return this.message.split('').reverse().join('') } } }) </div>
和使用 method 的区别在于,计算属性根据它的依赖被缓存,即如果 message 没有被修改,下次 get 不会进行重复计算,而 method 则每次调用都会重新计算。这也意味着如 Date.now() 这样返回的计算属性会永远得不到更新。
默认情况下,计算属性只有一个 getter,我们也可以给它加上 setter:
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] } } }
如此,当我们调用 vm.fullName = 'MarkZhai'
的时候,firstName 和 lastName 都会被更新。
Vue 的 watch 也可以用来做类似的事:
<div id="demo">{{ fullName }}div>
var vm = new Vue({ el: '#demo', data: { firstName: 'Foo', lastName: 'Bar', fullName: 'Foo Bar' }, watch: { firstName: function (val) { this.fullName = val + ' ' + this.lastName }, lastName: function (val) { this.fullName = this.firstName + ' ' + val } } })
对比一下计算属性版本:
var vm = new Vue({ el: '#demo', data: { firstName: 'Foo', lastName: 'Bar' }, computed: { fullName: function () { return this.firstName + ' ' + this.lastName } } })
看上去好像简单了很多,那还要 Watcher 干啥呢。。。主要应用场景是异步或耗时操作:
<script src="https://unpkg.com/axios@0.12.0/dist/axios.min.js">script> <script src="https://unpkg.com/lodash@4.13.1/lodash.min.js">script> <script> var watchExampleVM = new Vue({ el: '#watch-example', data: { question: '', answer: 'I cannot give you an answer until you ask a question!' }, watch: { // whenever question changes, this function will run question: function (newQuestion) { this.answer = 'Waiting for you to stop typing...' this.getAnswer() } }, methods: { // _.debounce is a function provided by lodash to limit how // often a particularly expensive operation can be run. // In this case, we want to limit how often we access // yesno.wtf/api, waiting until the user has completely // finished typing before making the ajax request. To learn // more about the _.debounce function (and its cousin // _.throttle), visit: https://lodash.com/docs#debounce getAnswer: _.debounce( function () { var vm = this if (this.question.indexOf('?') === -1) { vm.answer = 'Questions usually contain a question mark. ;-)' return } vm.answer = 'Thinking...' 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 }) }, // This is the number of milliseconds we wait for the // user to stop typing. 500 ) } }) script>
如此,使用 watch 让我们可以进行异步操作(访问 API),限制操作间隔,并设置中间状态直到获得了真正的答案。
除了使用 watch option,也可以用 vm.$watch API。
除了数据绑定,常见的还有 style、class 的绑定(正如很久以前在 JQuery 中常用的)。
我们可以传递一个对象给 v-bind:class
来动态切换 classes:
<div class="static" v-bind:class="{ active: isActive, 'text-danger': hasError }"> div>
对应的 active 和 text-danger 则通过 data 传递过来。
我们也可直接通过 data 把 class 传递过来
<div v-bind:class="classObject">div>
data: { classObject: { active: true, 'text-danger': false } }
当然我们也能使用上面提到的 computed 来进行对应属性,如 active 的计算。
可以直接传递一个数组给 v-bind:class:
<div v-bind:class="[activeClass, errorClass]">
data: { activeClass: 'active', errorClass: 'text-danger' }
也可以写成
<div v-bind:class="[isActive ? activeClass : '', errorClass]"> <div v-bind:class="[{ active: isActive }, errorClass]">
跟 class 差不多:
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }">div>
或者直接绑定到 style:
"styleObject">div> data: { styleObject: { color: 'red', fontSize: '13px' } }
类似的,也有数组绑定。
其实就是个标签啦
<h1 v-if="ok">Yesh1> <h1 v-if="ok">Yesh1> <h1 v-else>Noh1>
因为 v-if
必须附加到一个单一 element 上,那如果我们想切换多个元素呢?可以使用 template
元素:
<template v-if="ok"> <h1>Titleh1> <p>Paragraph 1p> <p>Paragraph 2p> template>
也可以用 v-show
来做条件显示的逻辑,
<h1 v-show="ok">Hello!h1>
区别在于
v-show
不支持 template
和 v-else
v-if
是 lazy 的,不会渲染没有走到的条件。而 v-show
只是简单的基于 CSS 的切换。所以 v-show
的初始 render 代价较高。
v-if
是真实的渲染,切换后原来的 dom 会被 destroyed,而新的 dom 会被重新创建。所以切换代价更高。
所以如果切换得较为频繁可以使用 v-show
,如果在运行时不太会改变则可以使用v-if
。
其实就是个循环标签啦:
<ul id="example-2"> <li v-for="(item, index) in items"> {{ parentMessage }} - {{ index }} - {{ item.message }} li> ul>
对应的 vm 实例:
var example2 = new Vue({ el: '#example-2', data: { parentMessage: 'Parent', items: [ { message: 'Foo' }, { message: 'Bar' } ] } })
跟 v-if
类似,我们也能在 template
上使用 v-for
:
<ul> <template v-for="item in items"> <li>{{ item.msg }}li> <li class="divider">li> template> ul>
也能使用 v-for
遍历对象的属性:
<ul id="repeat-object" class="demo"> <li v-for="value in object"> {{ value }} li> ul>
new Vue({ el: '#repeat-object', data: { object: { FirstName: 'John', LastName: 'Doe', Age: 30 } } })
看到 value,那肯定还有 key 了:
<div v-for="(value, key) in object"> {{ key }} : {{ value }} div>
如果再加上 index:
<div v-for="(value, key, index) in object"> {{ index }}. {{ key }} : {{ value }} div>
其他还有像是 v-for="n in 10"
这种用法,就不加上例子了。
input 输出内容到 newTodoText,每次点击 enter 都会触发 addNewTodo,然后添加 item 到 todos,触发新的 li 添加进去:
<div id="todo-list-example"> <input v-model="newTodoText" v-on:keyup.enter="addNewTodo" placeholder="Add a todo" > <ul> <li is="todo-item" v-for="(todo, index) in todos" v-bind:title="todo" v-on:remove="todos.splice(index, 1)" >li> ul> div>
Vue.component('todo-item', { template: '\
\ {{ title }}\ \
\ ', props: ['title'] }) new Vue({ el: '#todo-list-example', data: { newTodoText: '', todos: [ 'Do the dishes', 'Take out the trash', 'Mow the lawn' ] }, methods: { addNewTodo: function () { this.todos.push(this.newTodoText) this.newTodoText = '' } } })
当 vue 在更新被 v-for 渲染的列表时候,会使用就地 patch 的策略,而不是根据元素改变的顺序。我们可以提供 key 来做这个排序:
<div v-for="item in items" :key="item.id"> div>
如此,item 会根据 id 来做排序。
这些方法会改变原来的 array,并自动触发 view 的更新。
这几个方法会返回新的 array,如:
example1.items = example1.items.filter(function (item) { return item.message.match(/Foo/) })
如果
都是没法触发更新的,需要使用
Vue.set(example1.items, indexOfItem, newValue) // Array.prototype.splice` example1.items.splice(indexOfItem, 1, newValue) example1.items.splice(newLength)
配合 computed 以及 filter,或者也可以使用 v-for 的条件渲染:
for="n in even(numbers)">{{ n }}
data: { numbers: [ 1, 2, 3, 4, 5 ] }, methods: { even: function (numbers) { return numbers.filter(function (number) { return number % 2 === 0 }) } }
使用 v-on
指令监听 DOM 的各种事件,如:
<div id="example-1"> <button v-on:click="counter += 1">Add 1button> <p>The button above has been clicked {{ counter }} times.p> div>
var example1 = new Vue({ el: '#example-1', data: { counter: 0 } })
除了直接写 JS 语句,也可以直接在 v-on 中调用 methods 中定义的事件,还可以进行传参:
<div id="example-3"> <button v-on:click="say('hi')">Say hibutton> <button v-on:click="say('what')">Say whatbutton> div>
new Vue({ el: '#example-3', methods: { say: function (message) { alert(message) } } })
我们可能也希望直接把 event 给传递到方法中(比如在方法里 preventDefault 或者 stopPropagation),也很 easy,直接使用特殊的 $event 变量就行了。
除了像上面这样,在 method 里面对 event 进行操作,我们还可以使用事件修饰符(Event Modifier):
使用如:
<a v-on:click.stop="doThis">a> <form v-on:submit.prevent="onSubmit">form> <a v-on:click.stop.prevent="doThat">a> <form v-on:submit.prevent>form> <div v-on:click.capture="doThis">...div> <div v-on:click.self="doThat">...div>
通用的有使用 keyCode 的:
<input v-on:keyup.13="submit">
其他 alias 别名有
我们也可以自己通过全局的 config 定义其他别名,如:
// enable v-on:keyup.f1 Vue.config.keyCodes.f1 = 112
<input v-model="message" placeholder="edit me"> <p>Message is: {{ message }}p>
如此,用户的输入会直接反映到 data 中的 message,然后更新到
。
多行的用 textarea
替换 input
就行了。
单个的:
<input type="checkbox" id="checkbox" v-model="checked"> <label for="checkbox">{{ checked }}label>
多个的则可以绑到一个 array :
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames"> <label for="jack">Jacklabel> <input type="checkbox" id="john" value="John" v-model="checkedNames"> <label for="john">Johnlabel> <input type="checkbox" id="mike" value="Mike" v-model="checkedNames"> <label for="mike">Mikelabel> <br> <span>Checked names: {{ checkedNames }}span>
<input type="radio" id="one" value="One" v-model="picked"> <label for="one">Onelabel> <br> <input type="radio" id="two" value="Two" v-model="picked"> <label for="two">Twolabel> <br> <span>Picked: {{ picked }}span>
<select v-model="selected"> <option>Aoption> <option>Boption> <option>Coption> select> <span>Selected: {{ selected }}span> Selected: C
多选的在 select 后面加个 multiple,然后对应的会绑定到数组。
还可以结合 v-for
进行动态渲染:
<select v-model="selected"> <option v-for="option in options" v-bind:value="option.value"> {{ option.text }} option> select> <span>Selected: {{ selected }}span>
new Vue({ el: '...', data: { selected: 'A', options: [ { text: 'One', value: 'A' }, { text: 'Two', value: 'B' }, { text: 'Three', value: 'C' } ] } })
默认地,像上面这样,最后 v-model 绑定到的对象,其值会是一个 静态字符串(或者 true/false),有时候我们想要将其值绑定到一个动态属性,就可以使用 v-bind
来达到目的。
比如对于 input:
<input type="checkbox" v-model="toggle" v-bind:true-value="a" v-bind:false-value="b">
// when checked: vm.toggle === vm.a // when unchecked: vm.toggle === vm.b
甚至对象:
<select v-model="selected"> <option v-bind:value="{ number: 123 }">123option> select>
// when selected: typeof vm.selected // -> 'object' vm.selected.number // -> 123
默认地,v-model 在每次 input 事件后都会同步输入到数据。加上 lazy 修饰符后就会在 change 事件后才同步:
<input v-model.lazy="msg" >
会自动把输入转为 number:
<input v-model.number="age" type="number">
这还是挺有用的,因为就算限制了 input 的 type 为 number,元素的 value 仍然会返回 string。
好像不用多说了?大家都懂吧。
<input v-model.trim="msg">
现代的前端框架,通常都是组件化的了。整个应用的搭建,其实就是组件的拼接。自然 Vue 也不会忘了这个。
注册一个全局组件,只需要 Vue.component(tagName, options)
即可,如:
Vue.component('my-component', { // options })
实际渲染出来的 dom 则定义在 template option 中,如:
// register Vue.component('my-component', { template: '
A custom component!
' }) // create a root instance new Vue({ el: '#example' })
局部注册只需要放在 Vue 实例中:
var Child = { template: '
A custom component!
' } new Vue({ // ... components: { // will only be available in parent's template 'my-component': Child } })
使用则像:
<div id="example"> <my-component>my-component> div>
当使用 Dom 作为模板(比如使用 el
选项来使用已有内容加载元素),将会受到一些因为 HTML 工作原理而导致的限制,因为 Vue 只能在浏览器解析后才获取模板数据并进行处理。比如
可以通过以下方法使用字符串模板,就不会有这些限制:
所以,尽量使用字符串模板(string templates)吧。
大部分被传进 Vue 构造函数的选项都能在组件内使用,除了一个特殊情况:data 必须是函数。
如
data: function () { return { counter: 0 } }
而不能是一个在 parent context 的 var(会被多个组件实例共享)或者 object(控制台会报错)。
组件通常会被一起使用,大部分情况下会有 父——子 关系,如 组件A 在其模板中使用了 组件B。如此,就不免会有相互间的通讯,父亲需要传递数据给儿子,而儿子则需要通知父亲其内部发生的某些事件。
然而,为了让组件能避免耦合从而提高复用性和可维护性,又需要使它们相对隔离。
在 Vue.js 中,这种 父——子 组件关系可以被总结为 props down, events up ,即父组件通过 props 传递数据给子组件,而子组件通过 event 发消息给父组件。
熟悉 React 的话,你可能会想到 props 和 state。
每个组件都是相互隔离的,所以无法在子组件的 template 中引用父组件的数据。数据只能通过 props 传递。
比如我们可以这么注册子组件:
Vue.component('child', { // 申明 props props: ['message'], // 跟 data 一样,可以在 vm (this.message) 和 template 中直接使用 template: '{{ message }}' })
然后如此传递 props:
<child message="hello!">child>
因为 HTML 属性的限制(大小写敏感),所以使用 non-string templates 时,camelCased 的属性必须使用对应的 kebab-case 版本:
Vue.component('child', { // camelCase in JavaScript props: ['myMessage'], template: '{{ myMessage }}' })
<child my-message="hello!">child> Again, if you’re using string templates, then this limitation does not apply.
所以都说了,用字符串模板吧。
<div> <input v-model="parentMsg"> <br> <child v-bind:my-message="parentMsg">child> div>
如此,my-message 在父组件被改变的时候,都会传递更新到子组件。
当我们使用
<comp some-prop="1">comp>
的时候,实际传递的是一个字符串,而不是 number 2,如果要传递 JavaScript number,则需要使用 v-bind
<comp v-bind:some-prop="1">comp>
所有的 props 都是单向往下的,父组件 property 更新会影响子组件的,反过来则不会。这样避免了子组件误更改父组件状态,以及应用数据流难以理解。
另外,每次父组件中对应属性发生改变,子组件中的所有 props 都会被更新为最新的值。所以在子组件中,不应该对 props 进行更改。
你可能会辩解说传进来的只是个初始值,或者是个需要计算才能得出真正要的格式的值,但对前者你应该使用本地 data 属性来引用初始值,后者则应该通过 computed 来做。
可以对组件接受的 props 定义要求,如:
Vue.component('example', { props: { // 基本类型检查 (`null` 表示接受任何类型) propA: Number, // 多种可能的类型 propB: [String, Number], // 一个必须的 string propC: { type: String, required: true }, // 一个带默认值的 number propD: { type: Number, default: 100 }, // 对象/数组的默认值须通过一个工厂方法返回 propE: { type: Object, default: function () { return { message: 'hello' } } }, // 自定义检验器函数 propF: { validator: function (value) { return value > 10 } } } })
我们已经学习了父组件如何传递属性给子组件,那子组件怎么向上发送数据呢?答案就是自定义事件。
所有 Vue 实例都实现了 Events 接口 ,即:
如此,我们可以这样来传递事件(定义了 2 个 button,可以发送 increment 事件给父组件触发 incrementTotal)。
<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>
Vue.component('button-counter', { template: '', data: function () { return { counter: 0 } }, methods: { increment: function () { this.counter += 1 this.$emit('increment') } }, }) new Vue({ el: '#counter-event-example', data: { total: 0 }, methods: { incrementTotal: function () { this.total += 1 } } })
有时候在组件中,我们会有输入,比如 input,其实 input 中的 v-model
就是:
<input v-bind:value="something" v-on:input="something = $event.target.value">
的一个语法糖。
类似地,为了让组件支持 v-model
,它必须:
让我们来看看实践
<div id="v-model-example"> <p>{{ message }}p> <my-input label="Message" v-model="message" >my-input> div>
Vue.component('my-input', { template: '\ class="form-group">\ :for="randomId">{{ label }}:\ <input v-bind:id="randomId" v-bind:value="value" v-on:input="onInput">\
\ ', props: ['value', 'label'], data: function () { return { randomId: 'input-' + Math.random() } }, methods: { onInput: function (event) { this.$emit('input', event.target.value) } }, }) new Vue({ el: '#v-model-example', data: { message: 'hello' } })
这个接口不仅能被用在组件内的表单输入,还能被用在你自己发明的各种组件输入,像是:
<voice-recognizer v-model="question">voice-recognizer> <webcam-gesture-reader v-model="gesture">webcam-gesture-reader> <webcam-retinal-scanner v-model="retinalImage">webcam-retinal-scanner>
有时候两个组件可能需要互相通讯,但却不是父子关系。在简单的场景下,你可以使用一个空的 Vue 实例来作为中央事件总线(event bus):
var bus = new Vue()
// 在组件 A 的方法中 bus.$emit('id-selected', 1) // 在组件 B 的 created 中 bus.$on('id-selected', function (id) { // ... })
在更复杂的场景下,你可能需要考虑使用 状态管理模式 ,其实就是 Vuex 了(Vue 版 Redux)。
在使用组件的时候,经常会像这样组合:
有两点需要注意的:
为了让组件可以组合,我们需要一种方式来混合父组件的内容与子组件自己的模板。这个处理称为内容分发。Vue.js 实现了一个内容分发 API,参照了当前 Web 组件规范草稿 中 Slot 的 proposal,使用特殊的
直接看例子吧:
<div> <h2>I'm the child titleh2> <slot> This will only be displayed if there is no content to be distributed. slot> div>
父组件这样使用它:
<div> <h1>I'm the parent titleh1> <my-component> <p>This is some original contentp> <p>This is some more original contentp> my-component> div>
最后渲染出来的结果是:
<div> <h1>I'm the parent titleh1> <div> <h2>I'm the child titleh2> <p>This is some original contentp> <p>This is some more original contentp> div> div>
也就是外面的内容被插入到了slot里面。
如果你需要多个 slot,也很简单:
<div class="container"> <header> <slot name="header">slot> header> <main> <slot>slot> main> <footer> <slot name="footer">slot> footer> div>
父组件这么使用
<app-layout> <h1 slot="header">Here might be a page titleh1> <p>A paragraph for the main content.p> <p>And another one.p> <p slot="footer">Here's some contact infop> app-layout>
渲染出来的结果是:
<div class="container"> <header> <h1>Here might be a page titleh1> header> <main> <p>A paragraph for the main content.p> <p>And another one.</p> main> <footer> <p>Here's some contact infop> footer> div>
在设计需要组合到一起的组件时,内容分发 API 是非常有用的机制。
你可以使用同一个挂载点,并动态地将其切换为其他 Component。只需要使用保留的
var vm = new Vue({ el: '#example', data: { currentView: 'home' }, components: { home: { /* ... */ }, posts: { /* ... */ }, archive: { /* ... */ } } })
<component v-bind:is="currentView"> component>
如果你希望的话,也可以直接绑定到 component 对象:
var Home = { template: '
Welcome home!
' } var vm = new Vue({ el: '#example', data: { currentView: Home } })
如果把切换出去的组件保留在内存中,可以保留它的状态或避免重新渲染。为此可以添加一个 keep-alive 指令参数:
更多详情可以看
When authoring components, it’s good to keep in mind whether you intend to reuse it somewhere else later. It’s OK for one-off components to be tightly coupled, but reusable components should define a clean public interface and make no assumptions about the context it’s used in.
一个 Vue 组件的 API 由 3 者组成 —— props, events, 以及 slots:
通过 v-bind 和 v-on 的简写语法,template 可以干净简洁地传递意图:
:foo="baz" :bar="qux" @event-a="doThis" @event-b="doThat" > <img slot="icon" src="..."> <p slot="main-text">Hello!</p> my-component>
:foo 是 v-bind:foo 的简写,@event-a 则是 v-on:event-a 的简写。
尽管我们有 props 和 events,有时候你可能仍然需要在 JavaScript 中直接操作子组件。为此你必须使用 ref 分配一个 reference ID 给子组件。
<div id="parent"> <user-profile ref="profile">user-profile> div>
var parent = new Vue({ el: '#parent' }) // 访问子组件实例 var child = parent.$refs.profile
当 ref 和 v-for 一起使用的时候,你得到的 ref 将会是一个包含了从数据源镜像的数组或者对象。
$refs 只有在组件被渲染后才能获得,而且它不是响应式的。也就意味着只是一个直接子组件操作的逃生口 —— 你应该避免在模板或者 computed 属性中使用 $refs。
在大型应用中,我们需要把 app 分成一个个小块,只在真正需要的时候才去加载组件。为了简化这个,Vue 允许把组件定义为一个工厂方法,并异步去解析组件定义。Vue 仅仅会在组件真正需要被渲染的时候才会去触发该工厂方法,然后把结果缓存下来给以后的再渲染。如:
Vue.component('async-example', function (resolve, reject) { setTimeout(function () { resolve({ template: '
I am async!
' }) }, 1000) })
工厂方法接受一个 resolve 回调,会在从服务器获取到组件定义后被触发。也可以使用 reject(reason) 来指出加载失败了。这里的 setTimeout 只是用来做简单的演示,如何去获取 component 完全取决于你。一个推荐的方法是和 Webpack 的 code-splitting 功能一块儿使用异步组件:
Vue.component('async-webpack-example', function (resolve) { // 这个特殊的 require 语法会让 Webpack 去自动把你的编译后代码分割成 通过 Ajax 请求加载的 bundles require(['./my-async-component'], resolve) })
也可以在 resolve 方法中返回一个 Promise,比如通过 Webpack 2 + ES2015 语法可以这么做:
Vue.component( 'async-webpack-example', () => System.import('./my-async-component') )
然后 Browserify 不支持异步组件,拥抱 Webpack 吧。
在注册的时候,使用是随意的:
components: { 'kebab-cased-component': { /* ... */ }, 'camelCasedComponent': { /* ... */ }, 'TitleCasedComponent': { /* ... */ } }
但是在 HTML 模板中,必须使用 kebab-case 的,也就是上面的第一种。但如果是字符串模板(string template)的话,则可以随意使用。如果你的组件不使用 slot 进行属性传递,甚至可以直接写成自闭的(也仅支持字符串模板,因为浏览器不支持自闭合的自定义元素)。
组件可以在它自己的模板中递归自身。然而,他们只能通过 name 选项来这么做:
name: 'stack-overflow', template: '
'
像上面这样的组件会陷入 “max stack size exceeded” 错误,所以需要让递归变成条件性的(比如使用 v-if 指令,并最终返回一个 false)。当你在全局通过 Vue.component 注册一个组件的时候,一个全局的 ID 会被自动设置为组件的 name 选项。
当子组件中存在 inline-template 这个特殊属性的时候,它会使用其内容作为模板,而不会把它当做分发内容。如此模板就变得更灵活了。
<my-component inline-template> <p>These are compiled as the component's own template.p> <p>Not parent's transclusion content.p> my-component>
但是 inline-template 让模板的作用域难以理解,并且不能缓存模板编译结果。最佳实践是通过 template option 在组件内部定义模板,或者在 .vue 文件中的模板元素中定义。
另一个在 script 元素内部定义模板的方法是通过 type text/x-template,然后通过 id 引用模板。像这样:
<script type="text/x-template" id="hello-world-template"> <p>Hello hello hellop> script>
Vue.component('hello-world', { template: '#hello-world-template' })
在极小的应用或者大型模板的 demo 的时候可能会有用,其他情况下应该尽量避免。因为这样会把它和其他模板定义给隔离开。
在 Vue 里面渲染纯净的 HTML 元素是很快的,但有时候你可能需要一个包含了很多静态内容的组件。这种情况下,你可以通过在根元素加上 v-once 指令确保它只被评估了一次然后就被缓存下来了,像是这样:
Vue.component('terms-of-service', { template: '\
\
Terms of Service
\ ... 很多静态内容 ...\
\ ' })
通过使用 vue-migration-helper 可以快速扫出需要替换的代码。
主要有以下几类:
具体一点的话,像是 $index 现在必须使用 index 了(在 v-for 中显示声明),Filters 也不能像以前那样到处用了,等等等等,要升级还是挺痛苦的。
差不多也就是这样了。如果你是一个有一定经验并懂得基本 HTML 和 CSS 的高级工程师,我相信几天你就能看完并上手它了,毕竟对比 React 那一整套东西,还是相对简单的。