Vue.js
作者:烨竹
本文参考:
https://vuejs.org/v2/guide/list.html
http://www.runoob.com/vue2/vue-forms.html
Vue.js(读音 /vjuː/, 类似于 view)是一个构建数据驱动的 web 界面的库。Vue.js 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件
生命周期详解:https://blog.csdn.net/qq_25186543/article/details/79470184
基本语法:
一、结构:
Vue.js 是个专注在视图层(View) 的框架,帮助开发者切分前端的资料状态和运作逻辑;(类似土豆变葡萄,土豆是一个整体,而葡萄想吃哪部分都容易,由你决定各自独立运作或是相连)
葡萄自然有个供应养分的起始点,也就是根。让我们三秒看完长怎样
//挂载点为#app,内容为data里面的内容
<div id="app">
{{ message }}
</div>
<script>
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
});
</srcipt>
我们可以从根向下延伸,长出更多颗葡萄,这一个个葡萄皆可个别定义,称为元件(Component)。
下面例子加了menu 和description 两个元件
<div id="app">
{{ message }}
<menu-section></menu-section>
<description-section></description-section>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
},
components: {
'menu-section': {
template: '<ul><li v-for="item in menuItems">{{ item.text }}</li></ul>',
data: function() {
return {
menuItems: [{
text: 'About me'
}, {
text: 'Articles'
}, {
text: 'contact'
}]
}
}
},
'description-section': {
template: '<p>{{ text }}</p>',
data: function() {
return {
text: 'Hello, I am Ralph.'
}
}
}
}
});
</script>
二、写法(两种)
1.直接引入Vue.js(template与html混编)
//<menu-section>为例
<body>
<div id="app">
{{ message }}
<menu-section></menu-section>
</div>
<template id="menuTemplate">
<ul>
<li v-for="item in menuItems">{{ item.text }}</li>
</ul>
</template>
<script>
var app = new Vue({
el: '#app',
data: {
message: 'vue rock!'
},
/* 局部註冊 (Local Registration) */
components: {
'menu-section': {
template: '#menuTemplate', /* 樣板選取器 */
data: function() {
return {
menuItems: [{
text: 'About me'
}, {
text: 'Articles'
}, {
text: 'contact'
}]
}
}
}
}
})
</script>
</body>
2.透过Vue-loader 编译(官方工具Vue-cli)
/* Menu.vue */
<template>
<ul>
<li v-for="item in menuItems">{{ item.text }}</li>
</ul>
</template>
<script>
export default {
data: function() {
return {
menuItems: [{
text: 'About me'
}, {
text: 'Articles'
}, {
text: 'contact'
}]
}
}
}
</script>
<style>
/* 樣式也可以包進來 ._. */
.original-white {
color: #fff;
}
</style>
//使用官方工具Vue-cli,透过几行指令生成基本的专案环境
# 全域安装 vue-cli,當成系統命令用
$ npm install --global vue-cli
# 建立webpack樣板專案
$ vue init webpack my-project
# 若你剛開始學或不需要測試,簡化版本比較好懂
$ vue init webpack-simple my-project
# 安裝所需模組
$ cd my-project
$ npm install
# 開啟 http server
$ npm run dev
三、挂载点(Vue Instance)
const vm = new Vue({
el: 'app', /* 掛載點 */
data: { /* ... */ }, /* 初始資料 */
methods: { /* ... */}, /* 方法 */
});
四、资料绑定(模板相关语法)
<!-- 文字绑定 -->
<p>{{ msg }}</p>
<!-- 单次绑定 (仅更新一次) -->
<p v-once>{{ msg }}</p>
<!-- 把內容當成 HTML 解析 -->
<p v-html="raw_html"></p>
<!-- 属性綁定 -->
<a href="{{ pageLink }}"></a>
<a v-bind:href="pageLink"></a>
<div v-bind:id="dynamicId"></div>
<button v-bind:disabled="isDisabled">Submit</button>
<!-- 缩写 -->
<a :href="pageLink"></a>
<div :id="dynamicId"></div>
<button :disabled="isDisabled">Submit</button>
<!-- Filters 过滤器 -->
{{ data | json }}
{{ username | capitalize }}
五、指令
1.样式套用
能取到的Vue属性都能当作传入参数(v-bind)
i.绑定Class
①.简单
//基本写法以JSON传入,当对应的数值为true时套用,false时移除该类别
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
<script src="https://cdn.bootcss.com/vue/2.2.2/vue.min.js"></script>
<style>
.active {
width: 100px;
height: 100px;
background: green;
}
</style>
</head>
<body>
<div id="app">
<div v-bind:class="{ active: isActive }"></div>
</div>
<script>
new Vue({
el: '#app',
data: {
isActive: true
}
})
</script>
</body>
</html>
②、复杂(class并非固定,可改用Array传入)
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
<script src="https://cdn.bootcss.com/vue/2.2.2/vue.min.js"></script>
<style>
.text-danger {
width: 100px;
height: 100px;
background: red;
}
.active {
width: 100px;
height: 100px;
background: green;
}
</style>
</head>
<body>
<div id="app">
<div v-bind:class="[errorClass ,isActive ? activeClass : '']"></div>
</div>
<script>
new Vue({
el: '#app',
data: {
isActive: true,
activeClass: 'active',
errorClass: 'text-danger'
}
})
</script>
</body>
</html>
ii、绑定inline-style
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
<script src="https://cdn.bootcss.com/vue/2.2.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<div v-bind:style="[baseStyles, overridingStyles]">菜鸟教程</div>
</div>
<script>
new Vue({
el: '#app',
data: {
baseStyles: {
color: 'green',
fontSize: '30px'
},
overridingStyles: {
'font-weight': 'bold'
}
}
})
</script>
</body>
</html>
2.条件显示
UI流程常有特定情况才出现/隐藏DOM 或Component 的需求,Vue 对应写法是
v-if / v-show
v-else
v-else-if
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
<script src="https://cdn.bootcss.com/vue/2.2.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<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>
</div>
<script>
new Vue({
el: '#app',
data: {
type: 'a'
}
})
</script>
</body>
</html>
v-if,v-show区别
v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——在条件第一次变为真时才开始局部编译(编译会被缓存起来)
相比之下,v-show 简单得多——元素始终被编译并保留,只是简单地基于 CSS 切换。
一般来说,v-if 有更高的切换消耗而 v-show 有更高的初始渲染消耗。因此,如果需要频繁切换 v-show 较好,如果在运行时条件不大可能改变 v-if 较好
3、列表渲染( v-for又称循环语句)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
<script src="https://cdn.bootcss.com/vue/2.2.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<ul>
<template v-for="site in sites">
<li>{{ site.name }}</li>
<li>--------------</li>
</template>
</ul>
</div>
<script>
new Vue({
el: '#app',
data: {
sites: [
{ name: 'Runoob' },
{ name: 'Google' },
{ name: 'Taobao' }
]
}
})
</script>
</body>
</html>
//对象
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
<script src="https://cdn.bootcss.com/vue/2.2.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<ul>
<li v-for="(value, key, index) in object">
{{ index }}. {{ key }} : {{ value }}
</li>
</ul>
</div>
<script>
new Vue({
el: '#app',
data: {
object: {
name: '菜鸟教程',
url: 'http://www.runoob.com',
slogan: '学的不仅是技术,更是梦想!'
}
}
})
</script>
</body>
</html>
//整数
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
<script src="https://cdn.bootcss.com/vue/2.2.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<ul>
<li v-for="n in 10">
{{ n }}
</li>
</ul>
</div>
<script>
new Vue({
el: '#app'
})
</script>
</body>
</html>
4、事件处理(v-on)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
<script src="https://cdn.bootcss.com/vue/2.2.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<button v-on:click="say('hi')">Say hi</button>
<button v-on:click="say('what')">Say what</button>
</div>
<script>
new Vue({
el: '#app',
methods: {
say: function (message) {
alert(message)
}
}
})
</script>
</body>
</html>
5、表单输入绑定( v-model )
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
<script src="https://cdn.bootcss.com/vue/2.2.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<p>input 元素:</p>
<input v-model="message" placeholder="编辑我……">
<p>消息是: {{ message }}</p>
<p>textarea 元素:</p>
<p style="white-space: pre">{{ message2 }}</p>
<textarea v-model="message2" placeholder="多行文本输入……"></textarea>
</div>
<script>
new Vue({
el: '#app',
data: {
message: 'Runoob',
message2: '菜鸟教程\r\nhttp://www.runoob.com'
}
})
</script>
</body>
</html>
//复选框
<div id="app">
<p>单个复选框:</p>
<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">{{ checked }}</label>
<p>多个复选框:</p>
<input type="checkbox" id="runoob" value="Runoob" v-model="checkedNames">
<label for="runoob">Runoob</label>
<input type="checkbox" id="google" value="Google" v-model="checkedNames">
<label for="google">Google</label>
<input type="checkbox" id="taobao" value="Taobao" v-model="checkedNames">
<label for="taobao">taobao</label>
<br>
<span>选择的值为: {{ checkedNames }}</span>
</div>
六、过滤器
2.0版本中,过滤器只用于插入文本中({{}})
{{ message | capitalize }}
自定义过滤器
<template>
<div>
<input v-model="filterText"/>
<ul>
<li v-for="item in obj">
<span>{{myfilter(item.label)}}</span>
</li>
</ul>
</div>
</template>
<script>
export default {
data: function () {
return{
obj:[
{value:0,label:"男子十年盗窃被抓9次"},
{value:1,label:"中国人拯救地球"},
{value:2,label:"张艺谋电影"},
{value:3,label:"科幻电影排行榜"},
{value:4,label:"香港武打片电影"},
{value:5,label:"zhangwenwu的博客"}
],
filterText:""
}
},
methods:{
myfilter(value){
if(value.indexOf(this.filterText)>-1){
return value
}
}
}
};
</script>
七、Watch
简单理解:我们希望变数改变时,也有人叫对应的处理器起床做事,这就是Watch 的用途
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
<script src="https://cdn.bootcss.com/vue/2.4.2/vue.min.js"></script>
</head>
<body>
<div id = "computed_props">
千米 : <input type = "text" v-model = "kilometers">
米 : <input type = "text" v-model = "meters">
</div>
<p id="info"></p>
<script type = "text/javascript">
var vm = new Vue({
el: '#computed_props',
data: {
kilometers : 0,
meters:0
},
methods: {
},
computed :{
},
watch : {
kilometers:function(val) {
this.kilometers = val;
this.meters = val * 1000;
},
meters : function (val) {
this.kilometers = val/ 1000;
this.meters = val;
}
}
});
// $watch 是一个实例方法
vm.$watch('kilometers', function (newValue, oldValue) {
// 这个回调将在 vm.kilometers 改变后调用
document.getElementById ("info").innerHTML = "修改前值为: " + oldValue + ",修改后值为: " + newValue;
})
</script>
</body>
</html>
八、计算属性(computed)
特性:当我们定义一个computed,其相依data 一变,computed 也会随之更新。好处:收纳Template中的逻辑
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
<script src="https://cdn.bootcss.com/vue/2.2.2/vue.min.js"></script>
</head>
<body>
<div id="app">
{{ message.split('').reverse().join('') }}
</div>
<script>
new Vue({
el: '#app',
data: {
message: 'Runoob!'
}
})
</script>
</body>
</html>
computed vs methods
computed 是基于它的依赖缓存,只有相关依赖发生改变时才会重新取值;而使用 methods (其相依的data改变,computed也随之更新),在重新渲染的时候,函数总会重新调用执行
九、组件(Component)
使用组件有三个步骤:宣告建构子(Constructor);注册组件(Regist Component) (建立组件(Create Component));挂载组件(Mount Component)
1、注册(组件存在意义是为了拆解逻辑、使得结构清晰好懂)
全局组件:所有实例都能用全局组件
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
<script src="https://cdn.bootcss.com/vue/2.2.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<runoob></runoob>
</div>
<script>
// 注册
Vue.component('runoob', {
template: '<h1>自定义组件!</h1>'
})
// 创建根实例
new Vue({
el: '#app'
})
</script>
</body>
</html>
局部组件
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
<script src="https://cdn.bootcss.com/vue/2.2.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<runoob></runoob>
</div>
<script>
var Child = {
template: '<h1>自定义组件!</h1>'
}
// 创建根实例
new Vue({
el: '#app',
components: {
// <runoob> 将只在父模板可用
'runoob': Child
}
})
</script>
</body>
</html>
Vue.extend
<div id="app1">
<menu-section></menu-section>
</div>
<div id="app1">
<my-menu></my-menu>
</div>
<script>
var MenuItem = Vue.extend({
template: '<ul><li v-for="item in menuItems">{{ item.text }}</li></ul>',
data: function() {
return {
menuItems: [{
text: 'About me'
}, {
text: 'Articles'
}, {
text: 'contact'
}]
};
}
});
var app1 = new Vue({
el: '#app1'
components: {
'menu-section': MenuItem
}
});
var app2 = new Vue({
el: '#app2',
components: {
'my-menu': MenuItem
}
});
</script>
2、prop
原本功能间的协同运作都在同一层,拆成组件后,变成各个独立的状态了,我们仍然需要维持彼此间沟通畅通,如同所有漂亮的JavaScript 模组一样,需要订出漂亮的对外介面(Interface),使其改动内部逻辑的同时,又不失其重用性(环保可回收),方便传入外部资料;官方建议使用props down, events up,透过prop传入资料、透过组件事件(event)传递消息,template用于呈现资料
对上图的解释:
右边:data (宣告内部状态);props (宣告对外介面- 用于传入资料);prop:接收父组件传递的值;
左边:自用,$emit (组件内的事件);对外,$broadcast (父对子,向下传递),$dispatch (子对父,向上传递)
<div id="counter-event-example">
<p>總來客量: {{ total }}</p>
<button-counter door="前門" v-on:increment="incrementTotal"></button-counter>
<button-counter door="後門" v-on:increment="incrementTotal"></button-counter>
<!-- 來客數不計算工作人員 -->
<button-counter door="工作人員專用門"></button-counter>
</div>
<script src="vue.min.js"></script>
<script type="text/javascript">
Vue.component('button-counter', {
template: '<button v-on:click="increment">{{ door }}來客+1 ( {{ counter }} )</button>',
props: ['door'],
data: function () {
return {
counter: 0
}
},
methods: {
/* 來客+1 */
increment: function () {
this.counter += 1
/* 通知主任,多了一人來客 */
this.$emit('increment')
}
},
})
new Vue({
el: '#counter-event-example',
data: {
total: 0
},
methods: {
/* 主任得知來客+1,總來客量+1 */
incrementTotal: function () {
this.total += 1
}
}
})
</script>
template
要怎么让组件样板具有弹性
解法一 具名<slot>
<template>
<div class="modal fade">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<slot name="modal-header">
</div>
<div class="modal-body">
<slot name="modal-body">
</div>
<div class="modal-footer">
<slot name="modal-footer">
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
</template>
<modal>
<h4 slot="modal-header" class="modal-title">Modal title</h4>
<p slot="modal-body">One fine body…</p>
<button slot="modal-footer" type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button slot="modal-footer" type="button" class="btn btn-primary">Save changes</button>
</modal>
解法二- inline-template(这种作法会完全覆盖掉<app>原始内容)
<app inline-template>
<div>
<p>These are compiled as the component's own template.</p>
<p>Not parent's transclusion content.</p>
</div>
</app>
<!-- <app>模板原始內容不见了 -->
<div>
<p>These are compiled as the component's own template.</p>
<p>Not parent's transclusion content.</p>
</div>
3、挂载
透过模板(template)的基本挂载:
<script>
Vue.component('viewer', require('./components/Example.vue'));
Vue.component('editor', require('./components/Editor.vue'));
var app = new Vue({
el: '#app',
data: {
state: 'viewer'
},
methods: {
toggle: function() {
return (this.state === 'viewer') ? 'editor' : 'viewer';
}
}
});
</script>
is屬性动态挂载:
<div id="app">
<button type="button" @click="toggle">{{ state }}</button>
<div is="state"></div>
<div is="state"></div>
<!-- state: 'viewer' -->
<!--
<viewer></viewer>
<viewer></viewer>
-->
<!-- state: 'editor' -->
<!--
<editor></editor>
<editor></editor>
-->
</div>
透过Vue建构子或$mountAPI:
<!-- 宣告建構子 -->
var Example = Vue.extend({
template: '<div>Here is Example</div>'
});
透过建构子挂载
<!-- 這會把#app內容完全換掉 -->
new Example({ el: '#app' })
挂载前的等效样板
<div id="app">
<example></example>
<example></example>
</div>
挂载后
<div id="app">
<!-- 前面兩個 <example> 都不見了 -->
<example></example>
</div>
给$mount挂载点
new Example().$mount('#app');
跟(1)相同,完全换掉#app内容
当你加上新的组件,又不想盖掉原内容,参考这种做法
4、其他
i、组件(component)间的其他连结
①、$parent/$children/$root
第一个直接连结是父子组件间的交互参照
父组件可透过$children直接存取其下一层的子组件。
子组件可透过$parent直接存取其上一层的父组件。
不论父子组件都可透过$ root,直接存取最顶层的Vue Instance
②、ref属性& $refs
第二个直接连结是组件的自订索引名- ref,用于父组件对子组件的参照。
<div id="parent">
<user-profile ref="profile"></user-profile>
</div>
定义好的索引名,可以透过父组件的$refs取得
var parent = new Vue({ el: '#parent' })
/* 存取子組件 */
var child = parent.$refs.profile
若是搭配v-for,$refs对应的索引名也会取得阵列/JSON
<div id="parent">
<user-profile v-for="user in users" ref="profile"></user-profile>
</div>
typeof parent.$refs.profile /* Array */
这个属性在画面渲染完才会更新,资料可能不同步,只适合当备用方案
ii、
加速组件编译