Vue2.0文档学习及案例总结之----Components
鉴于官方文档案例不够齐全,结合文档笔者总结了一下相对齐全的案例并均可运行出结果。可以更加深入具体地理解2.0文档的魅力,方便开发运用到实际项目中。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="https://unpkg.com/vue/dist/vue.js"></script> </head> <body> <h4>Using Compenents(使用组件)</h4> <div id="example"> <my-component></my-component> </div> <script> // Vue.component('my-component', { // template: '<div>A custom component!</div>', // }); new Vue({ el: '#example', components: { myComponent: { template: '<div>A custom component!</div>', } } }) </script> <h4>Local Registration(局域注册)! data在模板组件??</h4> <div id="test"></div> <div id="demo"> <parent></parent> </div> <script> // var Child = Vue.extend({ // template: '<div>A local component!></div>' // }); // var Parent = Vue.extend({//将子组件嵌套进父组件内 // template: '<div>A Global component!<child></child></div>', // components: { // child: Child // } // }); new Vue({ el: '#demo', data: { msg: "hello" }, components: { 'parent': {//将子组件嵌套进父组件内 template: '<div>A Global component!<child></child></div>', components: { child: { template: '<div>A local component!</div>' } } }, } }); </script> <h4>DOM Template Parsing Caveats</h4> <div><p>一些 HTML 元素对什么元素可以放在它里面有限制。常见的限制: a 不能包含其它的交互元素(如按钮,链接) ul 和 ol 只能直接包含 li select 只能包含 option 和 optgroup table 只能直接包含 thead, tbody, tfoot, tr, caption, col, colgroup tr 只能直接包含 th 和 td</p> <p>另一个结果是,自定义标签(包括自定义元素和特殊标签)不能用在 ul, select, table 等对内部元素有限制的标签内。放在这些元素内部的自定义标签将被 提到元素的外面,因而渲染不正确。 </p> <p>prefer using string templates whenever possible. data Must Be a Function</p> </div> <h4>data MUST BE a Function</h4> <div id="example-2"> <simple-counter></simple-counter> <simple-counter></simple-counter> <simple-counter></simple-counter> </div> <script> // data MUST BE a Function Vue.component('simple-counter', { template: '<button v-on:click="counter += 1">{{ counter }}</button>', data: function () { return { counter: 0 } } }); new Vue({ el: '#example-2', }) </script> <h4>Composing Components 组件建构props down, events up</h4> <p>Passing Data with Props(父到子)</p> <hr> <div id="demo2"> <child1 message="134a"></child1> </div> <script> //组件实例的作用域是独立的 Vue.component('child1', { props: { message:{ type:String, default:'13sd' } },//“信使RNA”,把值父到子单向传递过去 template: '<p>{{message}}</p>' }); let demo2 = new Vue({ el: '#demo2' }) </script> <h4>camelCase vs.kebab-case</h4> <div id="demo20"> <!--html里边kebab-case--> <child1 my-message="hello 20!"></child1> </div> <script> Vue.component('child1', { props: ['my-message'], template: '<p>{{myMessage }}</p>' }); let demo20 = new Vue({ el: '#demo20' }) </script> <h4> Dynamic Props </h4> <div id="demo21"> <!--子组件自定义message值传递不到父组件,只对自己有用--> <child2 message="child 自定义的 message,只对自己用!"></child2> <div> <input v-model="message"> <br> <child2 message="'message'+freeDom"></child2> <child2 :message="':message'+message"></child2> <child2 :message.once="':message.once'+message"></child2> <child2 :message.sync="':message.sync'+message"></child2> <!--直接引用引用父组件message--> <child2 v-text="'v-text'+message"></child2> </div> </div> <script> Vue.component('child2', { props: ['message'],//听父级传递数据 template: '<p>{{message}}</p>' }); let demo21 = new Vue({ el: '#demo21', data: { message: 'parent message' } }) </script> <h4>One-way Data Flow</h4> <p>:msg(default),:msg.sync(double),:msg.once(once) 效果测试</p> <p style="color:red">注意如果 prop 是一个对象或数组,是按引用传递。在子组件内修改它会影 响父组件的状态,不管是使用哪种绑定类型。</p>
<h4>Prop验证</h4>
<div id="demo22">
<demo22 v-text:prop-b="'aaa'"></demo22>
</div>
<script>
Vue.component('demo22', {
template: '<div>{{propA}}</div>',
props: {
// 基础类型检测 (null
意思是任何类型都可以)
propA: Number,
// 多种类型 (1.0.21+)
propM: [String, Number],
// 必需且是字符串
propB: {
type: String,
required: false
},
// 数字,有默认值
propC: {
type: Number,
default: 100
},
// 对象/数组的默认值应当由一个函数返回
propD: {
type: Object,
default: function () {
return {msg: 'hello'}
}
},
// 指定这个 prop 为双向绑定
// 如果绑定类型不对将抛出一条警告
propE: {
twoWay: true
},
// 自定义验证函数
propF: {
validator: function (value) {
return value > 10
}
},
// 转换函数(1.0.12 新增)
// 在设置值之前转换值
propG: {
coerce: function (val) {
return val + '' // 将值转换为字符串
}
},
propH: {
coerce: function (val) {
return JSON.parse(val) // 将 JSON 字符串转换为对象
}
}
}
});
// create a root instance(构建根实例)
new Vue({
el: '#demo22',
})
</script>
<h4>Custom Events--定制事件,子向父通信</h4>
<div id="counter-event-example">
<p>每个 Vue 实例都是一个事件触发器:
使用 $on() 监听事件;
使用 $emit() 在它上面触发事件;
使用 $dispatch() 派发事件,事件沿着父链冒泡;
使用 $broadcast() 广播事件,事件向下传导给所有的后代。</p>
<p>{{ total }}</p>
<button-counter v-on:increment="incrementTotal"></button-counter>
<button-counter v-on:increment="incrementTotal"></button-counter>
<hr>
<p>Binding Native Events to Components</p>
<button-counter v-on:click.native="test"></button-counter>
</div>
<script>
Vue.component('button-counter', {
template: '<button v-on:click="increment">{{ counter }}</button>',
data: function () {
return {
counter: 0
}
},
methods: {
increment: function () {
this.counter += 1;
this.$emit('increment')
},
}
});
let aa = new Vue({
el: '#counter-event-example',
data: {
total: 0
},
methods: {
incrementTotal: function () {
this.total += 1
},
test: function () {
alert('test');
}
}
})
</script>
<h4>Form Input Components using Custom Events</h4>
<p>This interface can be used not only to connect with form inputs inside a component, but also to easily integrate
input types that you invent yourself. Imagine these possibilities:</p>
<div id="v-model-example">
<p>{{ message }}</p>
<my-input
label="Message"
v-model="message"
</my-input>
</div>
<script>
Vue.component('my-input', {
template: '\
<div class="form-group">\
<label v-bind:for="randomId">{{ label }}:</label>\
<input v-bind:id="randomId" v-bind:value="value" v-on:input="onInput">\
</div>\
',
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'
}
})
</script>
<p>父组件模板的内容在父组件作用域内编译;子组件模板的内容在子组件作用域内编译</p>
<h4>Non Parent-Child Communication</h4>
<div class="bus">
<display></display>
<increment></increment>
</div>
<script>
var bus = new Vue();
Vue.component('increment', {
template:<button @click="increment">increment</button>
,
data: function () {
return ({count: 0})
},
methods: {
increment: function () {
var increment = ++this.count;
//触发该组件中的事件
bus.$emit('inc', increment)
}
}
});
Vue.component('display', {
template:<span>Clicked: {{count}} times</span>
,
data: function () {
return {count: 0}
},
created: function () {
//在该组件中创建钩子监听事件
bus.$on('inc', function (num) {
this.count = num
}.bind(this));
}
});
vm = new Vue({
el: ".bus",
})
</script>
<h4>Content Distribution with Slots(内容分发)</h4>
<div id="appa">
<my-component>
<h1>Hello Vue.js!</h1>
</my-component>
<my-component>
</my-component>
</div>
<template id="myComponent">
<div>
<h2>slot 其实只是个替补</h2>
<slot>如果没有分发内容,则显示我slot中的内容</slot>
<h3>无则上位</h3>
</div>
</template>
<script>
Vue.component('my-component', {
template: '#myComponent'
});
new Vue({
el: '#appa'
})
</script>
<hr>
<div id="appb">
<button class="btn btn-open" @click="openDialog">dialog</button>
<modal-dialog v-show.sync="show">
<header class="dialog-header" slot="header">
<h1 class="dialog-title">header</h1>
</header>
<main class="dialog-body" slot="body">
<h3>main</h3>
</main>
<footer class="dialog-footer" slot="footer">
<button @click="closeDialog">footer关闭</button>
</footer>
</modal-dialog>
</div>
<template id="dialog-template">
<div><!--让谁显示谁显示-->
<slot name="header"></slot>
<slot name="body"></slot>
<slot name="footer"></slot>
</div>
</template>
<script>
Vue.component('modal-dialog', {
template: '#dialog-template',
props: ['show'],
methods: {
close: function () {
this.show = false
}
}
});
new Vue({
el: '#appb',
data: {
show: false
},
methods: {
openDialog: function () {
this.show = true
},
closeDialog: function () {
this.show = false
}
}
})
</script>
<hr>
<div id="app">
<script type="text/x-template" id="modal-template">
<transition name="modal">
<div class="modal-mask">
<div class="modal-wrapper">
<div class="modal-container">
<div class="modal-header">
<slot name="header">
default header
</slot>
</div>
<div class="modal-body">
<slot name="body">
default body
</slot>
</div>
<div class="modal-footer">
<slot name="footer">
default footer
<button class="modal-default-button" @click="$emit('close')">
OK
</button>
</slot>
</div>
</div>
</div>
</div>
</transition>
</script>
<button id="show-modal" @click="showModal = true">Show Modal</button>
<modal v-if="showModal" @close="showModal = false">
<h3 slot="header">custom header</h3>
</modal>
</div>
<script>
Vue.component('modal', {
template: '#modal-template'
});
new Vue({
el: '#app',
data: {
showModal: true
}
})
</script>
<hr>
<h4>Dynamic Components</h4>
<div id="app1">
<p>如果把切换出去的组件保留在内存中,可以保留它的状态或避免重新渲染</p>
<keep-alive>
<!--<component :is="currentView"></component>-->
<!--<home :is="currentView"></home>-->
<home :is="currentView1"></home>
<!--<home :is="currentView2"></home>-->
<!--<home :is="currentView3"></home>-->
</keep-alive>
<hr>
<home></home>
<home :is="currentView"></home>
<posts :is="currentView1"></posts>
<posts :is="currentView2"></posts>
</div>
<script>
var choose2 = {
template: '<p>Welcome home!</p>'
};//组件可以是全局的也可以是局部的
new Vue({
el: '#app1',
data: {
currentView: 'home',
currentView1: 'posts',
currentView2: choose2,
currentView3: 'archive'
},
components: {
home: {
template: '<main>home</main>'
},
posts: {
template: '<div>posts</div>'
},
archive: {
template: '<h1>archive</h1>'
}
}
})
</script>
<h4>Misc 杂项</h4>
<hr>
<h4>Authoring Reusable Components:编写可复用组件</h4>
<p>在编写组件时,记住是否要复用组件有好处。一次性组件跟其它组件紧密耦合没关系,
但是可复用组件应当定义一个清晰的公开接口。</p>
<p>
Vue.js 组件 API 来自三部分——prop,事件和 slot:
prop 允许外部环境传递数据给组件;
事件 允许组件触发外部环境的 action;
slot 允许外部环境插入内容到组件的视图结构内。
</p>
<div id="misc1">
<p>使用 v-bind 和 v-on 的简写语法,模板的缩进清楚且简洁</p>
<!--<my-component-->
<!--:foo="baz"-->
<!--:bar="qux"-->
!--@event-a="doThis"--
!--@event-b="doThat"-->
<!--<!– content –>-->
<!--<img slot="icon" class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="...">-->
<!--<p slot="main-text">Hello!</p>-->
<!--</my-component>-->
<p>Async Components:异步组件</p>
<p>在大型应用中,我们可能需要将应用拆分为小块,只在需要时才从服务器下载。为了让事情更简单,Vue.js 允许将组件定义为一个工厂函数,动态地解析组件的定义。Vue.js 只在组件需要渲染时触发工厂函数,
并且把结果缓存起来,用于后面的再次渲染。</p>
<script>
Vue.component('async-example', function (resolve, reject) {
setTimeout(function () {
resolve({
template: '<div>I am async!</div>'
})
}, 1000)
});
Vue.component('async-webpack-example', function (resolve) {
// 这个特殊的 require 语法告诉 webpack
// 自动将编译后的代码分割成不同的块,
// 这些块将通过 ajax 请求自动下载。
// Webpack’s code-splitting(代码分隔) feature:
require(['./my-async-component'], resolve)
});
// You can also return a Promise in the resolve function, so with
// Webpack 2 + ES2015 syntax you can do:
// Vue.component(
// 'async-webpack-example',
// () => System.import('./my-async-component')
// );
// If you’re a Browserify user that would like to use async components,
// it’s unfortunately not possible and probably never will be, as its creator has made it clear that async loading “is not something that Browserify will ever support.” If this is a feature
// that’s important to you, we recommend using Webpack instead.
</script>
<p>Component Naming Conventions</p>
<script>
// When registering components (or props), you can use kebab-case,
// camelCase, or TitleCase. Vue doesn’t care.
// in a component definition
// components: {
// 'kebab-cased-component': { / ... / },
// 'camelCasedComponent': { / ... / },
// 'TitleCasedComponent': { / ... / }
// }
</script>
<div>
<!-- alway use kebab-case in HTML templates -->
<!-- When using string templates however, <my-component/>也行
we’re not bound by HTML’s case-insensitive restrictions. -->
<kebab-cased-component></kebab-cased-component>
<camel-cased-component></camel-cased-component>
<title-cased-component></title-cased-component>
</div>
</div>
<h4>Recursive(递归) Components</h4>
<div id="misc2">
<!--<stack-overflow></stack-overflow>-->
<!--<script>-->
<!--Vue.component('stack-overflow', {-->
<!--name: 'stack-overflow',-->
<!--template: --> <!--<div>--> <!--<stack-overflow v-if="false">hello</stack-overflow>--> <!--</div>--> <!--
-->
<!--});-->
<!--//vue.js:2224 Uncaught RangeError: Maximum call stack size exceeded(…)-->
<!--new Vue({-->
<!--el: '#misc2',-->
<!--})-->
<!--</script>-->
</div>
<h6>Inline Templates</h6>
<p>如果子组件有 inline-template 特性,组件将把它的内容当作它的模板,而不是把它当作分发内容。这让模板更灵活。</p>
<p>但是 inline-template 让模板的作用域难以理解。</p>
<div class="inline">
<my-component inline-template>
<div>
<p>These are compiled as the component's own template.</p>
<p>Not parent's transclusion content.</p>
<mark> 这些被编译为组件自己的模板,而非父组件的过渡内容</mark>
</div>
</my-component>
</div>
<script>
</script>
<h4 style="color:red">X-Templates</h4>
<mark>这在有很多模版或者小的应用中有用,否则应该避免使用,因为它将模版和组件的其他定义隔离了。</mark>
<div id="misc3">
<script type="text/x-template" id="modal-template-a" name="modal-a">
<div>hello x-template</div>
</script>
<!-- use the modal component-->
<modal-a></modal-a>
</div>
<script>
Vue.component('modal-a', {
template: '#modal-template-a'
});
// start app
new Vue({
el: '#misc3',
})
</script>
<h4>Cheap Static Components with v-once:一次性组件</h4>
<mark>当组件中包含大量静态内容时,可以考虑使用 v-once 将渲染结果缓存起来</mark>
<div id="misc4">
<p>Rendering plain HTML elements is very fast in Vue, but sometimes
you might have a component that contains a lot of static content.
In these cases, you can ensure that it’s only evaluated once(只评估一次) and then cached by
adding the v-once directive to the root element, like this:</p>
<hr>
<terms-of-service></terms-of-service>
</div>
<script>
var aas = Vue.component('terms-of-service', {
template: <div v-once> <h3>Terms of Service---'v-once'</h3> <mark>I am a cheap component which is rendered only once</mark> </div>
});
new Vue({
el: '#misc4',
components: {
termsOfService: "aas"
}
})
</script>
</body>
</html>
共同学习,写下你的评论
评论加载中...
作者其他优质文章