vue主要功能
- 模块渲染
- 模板化
- 扩展功能
指令与事件的区别
你看到的 v-bind 特性被称为指令——v-on 事件监听器
项目结构暗示
计算属性缓存 vs 方法
<p>Reversed message: "{{ reversedMessage() }}"</p>
// 在组件中
methods: {
reversedMessage: function () {
return this.message.split('').reverse().join('')
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
侦听器——watch
当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。
<div id="watch-example">
<p>
Ask a yes/no question:
<input v-model="question">
</p>
<p>{{ answer }}</p>
</div>
<!-- 因为 AJAX 库和通用工具的生态已经相当丰富,Vue 核心代码没有重复 -->
<!-- 提供这些功能以保持精简。这也可以让你自由选择自己更熟悉的工具。 -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/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: {
// 如果 `question` 发生改变,这个函数就会运行
question: function (newQuestion, oldQuestion) {
this.answer = 'Waiting for you to stop typing...'
this.getAnswer()
}
},
methods: {
// `_.debounce` 是一个通过 Lodash 限制操作频率的函数。
// 在这个例子中,我们希望限制访问 yesno.wtf/api 的频率
// AJAX 请求直到用户输入完毕才会发出。想要了解更多关于
// `_.debounce` 函数 (及其近亲 `_.throttle`) 的知识,
// 请参考:https://lodash.com/docs#debounce
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>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
绑定 HTML Class
对象语法
<div class="static"
v-bind:class="{ active: isActive, 'text-danger': hasError }">
</div>
和如下 data:
data: {
isActive: true,
hasError: false
}
结果渲染为:
<div class="static active"></div>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
数组语法
<div v-bind:class="[activeClass, errorClass]"></div>
data: {
activeClass: 'active',
errorClass: 'text-danger'
}
渲染为:
<div class="active text-danger"></div>
如果你也想根据条件切换列表中的 class,可以用三元表达式:
<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
用 key 管理可复用的元素
因为两个模板使用了相同的元素, 不会被替换掉——仅仅是替换了它的 placeholder。
<template v-if="loginType === 'username'">
<label>Username</label>
<input placeholder="Enter your username" key="username-input">
</template>
<template v-else>
<label>Email</label>
<input placeholder="Enter your email address" key="email-input">
</template>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
列表渲染
用 v-for 把一个数组对应为一组元素
<ul id="example-2">
<li v-for="(item, index) in items">
{{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
</ul>
var example2 = new Vue({
el: '#example-2',
data: {
parentMessage: 'Parent',
items: [
{ message: 'Foo' },
{ message: 'Bar' }
]
}
})
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
一个对象的 v-for
<div v-for="(value, key, index) in object">
{{ index }}. {{ key }}: {{ value }}
</div>
- 1
- 2
- 3
- 4
替换数组——注意事项
由于 JavaScript 的限制,Vue 不能检测以下变动的数组:
1.当你利用索引直接设置一个项时,例如:vm.items[indexOfItem] = newValue
2.当你修改数组的长度时,例如:vm.items.length = newLength
为了解决第一类问题,以下两种方式都可以实现和 vm.items[indexOfItem] = newValue 相同的效果,同时也将触发状态更新:
// Vue.set
Vue.set(example1.items, indexOfItem, newValue)
// Array.prototype.splice
example1.items.splice(indexOfItem, 1, newValue)
为了解决第二类问题,你可以使用 splice:
example1.items.splice(newLength)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
列表渲染——实例
<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:key="todo.id"
v-bind:title="todo.title"
v-on:remove="todos.splice(index, 1)"
></li>
</ul>
</div>
Vue.component('todo-item', {
template: '\
<li>\
{{ title }}\
<button v-on:click="$emit(\'remove\')">X</button>\
</li>\
',
props: ['title']
})
new Vue({
el: '#todo-list-example',
data: {
newTodoText: '',
todos: [
{
id: 1,
title: 'Do the dishes',
},
{
id: 2,
title: 'Take out the trash',
},
{
id: 3,
title: 'Mow the lawn'
}
],
nextTodoId: 4
},
methods: {
addNewTodo: function () {
this.todos.push({
id: this.nextTodoId++,
title: this.newTodoText
})
this.newTodoText = ''
}
}
})
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
注意这里的 is=”todo-item” 属性。这种做法在使用 DOM 模板时是十分必要的,因为在 </ul>
元素内只有 <li>
元素会被看作有效内容。这样做实现的效果与 <todo-item>
相同,但是可以避开一些潜在的浏览器解析错误。查看 DOM 模板解析说明 来了解更多信息。
表单复选框
<div id='example-3'>
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames">
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mike</label>
<br>
<span>Checked names: {{ checkedNames }}</span>
</div>
new Vue({
el: '#example-3',
data: {
checkedNames: []
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
表单下拉列表
<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' }
]
}
})
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
提示
项目的 数据结构 采用简单的 es6 语法
数据结构改用 data类函数 return 出去 目的:防止同一个变量引用时 修改数据 同时改变其他数据
前端路由的区别 : 公共部分不做二次请求
服务器路由: 每次切换页面都会 重新请求所有文件
基本配置项
main.js
import Vue from 'vue'
import Layout from './components/layout' //页面layout
import VueRouter from 'vue-router' // 路由模块
import VueResource from 'vue-resource' // ajax 2.0 以后不再维护
import IndexPage from './pages/index' // 以下为各类模板
import DetailPage from './pages/detail'
import OrderListPage from './pages/orderList'
import DetailAnaPage from './pages/detail/analysis'
import DetailCouPage from './pages/detail/count'
import DetailForPage from './pages/detail/forecast'
import DetailPubPage from './pages/detail/publish'
Vue.use(VueRouter)
Vue.use(VueResource) // 路由的使用
let router = new VueRouter({
mode: 'history', // 采用h5 路由模式
routes: [
{
path: '/',
component: IndexPage // 路由重定向
},
{
path: '/orderList',
component: OrderListPage
},
{
path: '/detail',
component: DetailPage,
redirect: '/detail/analysis',
children: [ // 路由且套
{
path: 'analysis',
component: DetailAnaPage
},
{
path: 'count',
component: DetailCouPage
},
{
path: 'forecast',
component: DetailForPage
},
{
path: 'publish',
component: DetailPubPage
}
]
}
]
})
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
template: '<Layout/>',
components: { Layout }
})
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
layout
<template>
<div>
<div class="app-head">
<div class="app-head-inner">
<router-link :to="{path: '/'}"> //相当于a 标签
<img src="../assets/logo.png">
</router-link>
<div class="head-nav">
<ul class="nav-list">
<li> {{ username }}</li>
<li v-if="username!== ''" class="nav-pile">|</li>
<li v-if="username!== ''" @click="quit">退出</li> // 如果变量username 存在以上 生效 否则 显示如下的内容
<li v-if="username=== ''" @click="logClick">登录</li>
<li class="nav-pile">|</li>
<li v-if="username=== ''" @click="regClick">注册</li>
<li v-if="username=== ''" class="nav-pile">|</li>
<li @click="aboutClick">关于</li> // @click 为绑定的点击事件
</ul>
</div>
</div>
</div>
<div class="container"> //内容区 追加缓存组件
<keep-alive>
<router-view></router-view>
</keep-alive>
</div>
<div class="app-foot">
<p>© 2016 fishenal MIT</p>
</div>
// 以下内容将配合 slot 插槽来使用
<my-dialog :is-show="isShowAboutDialog" @on-close="closeDialog('isShowAboutDialog')">
//默认绑定is-show 为隐藏模式 准备传入子组件
@on-close 为子组件返回的监听 他们合起来也就是 父子通信
<p>本报告在调研数据的基础上,采用定性与定量相结合的方式深入分析了专车市场发展的驱动因素与阻碍因素、专车市场背后的产业格局、专车企业的竞争格局、用户对专车市场的依赖程度、专车对其他交通工具运力的补充效应等,通过这五个章节的研究反映专车市场的发展态势和面临的问题。报告力求客观、深入、准确地反映中国专车市场发展情况,为政府、企事业单位和社会各界提供决策依据。 </p>
</my-dialog>
<my-dialog :is-show="isShowLogDialog" @on-close="closeDialog('isShowLogDialog')">
<log-form @has-log="onSuccessLog"></log-form>
</my-dialog>
<my-dialog :is-show="isShowRegDialog" @on-close="closeDialog('isShowRegDialog')">
<reg-form></reg-form>
</my-dialog>
</div>
</template>
<script>
import Dialog from './base/dialog' //模板引入
import LogForm from './logForm'
import RegForm from './regForm'
export default {
components: {
MyDialog: Dialog,
LogForm,
RegForm
},
data () {
return {
isShowAboutDialog: false,
isShowLogDialog: false,
isShowRegDialog: false,
username: ''
}
},
methods: {
aboutClick () {
this.isShowAboutDialog = true
},
logClick () {
this.isShowLogDialog = true
},
regClick () {
this.isShowRegDialog = true
},
closeDialog (attr) {
this[attr] = false
},
onSuccessLog (data) {
console.log(data)
this.closeDialog ('isShowLogDialog')
this.username = data.username
}
}
}
</script>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
loginform
<template>
<div class="login-form">
<div class="g-form">
<div class="g-form-line">
<span class="g-form-label">用户名:</span>
<div class="g-form-input">
<input type="text"
v-model="usernameModel" placeholder="请输入用户名">
</div>
<span class="g-form-error">{{ userErrors.errorText }}</span>
</div>
<div class="g-form-line">
<span class="g-form-label">密码:</span>
<div class="g-form-input">
<input type="password"
v-model="passwordModel" placeholder="请输入密码">
</div>
<span class="g-form-error">{{ passwordErrors.errorText }}</span>
</div>
<div class="g-form-line">
<div class="g-form-btn">
<a class="button" @click="onLogin">登录</a>
</div>
</div>
<p>{{ errorText }}</p>
</div>
</div>
</template>
<script>
export default {
data () {
return {
usernameModel: '',
passwordModel: '',
errorText: ''
}
},
computed: {
userErrors () {
let errorText, status
if (!/@/g.test(this.usernameModel)) {
status = false
errorText = '不包含@'
}
else {
status = true
errorText = ''
}
if (!this.userFlag) {
errorText = ''
this.userFlag = true
}
return {
status,
errorText
}
},
passwordErrors () {
let errorText, status
if (!/^\w{1,6}$/g.test(this.passwordModel)) {
status = false
errorText = '密码不是1-6位'
}
else {
status = true
errorText = ''
}
if (!this.passwordFlag) {
errorText = ''
this.passwordFlag = true
}
return {
status,
errorText
}
}
},
methods: {
onLogin () {
if (!this.userErrors.status || !this.passwordErrors.status) {
this.errorText = '部分选项未通过'
}
else {
this.errorText = ''
this.$http.get('api/login')
.then((res) => {
this.$emit('has-log', res.data)
}, (error) => {
console.log(error)
})
}
}
}
}
</script>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
首页
<template>
<div class="index-wrap">
<div class="index-left">
<div class="index-left-block">
<h2>全部产品</h2>
<template v-for="product in productList"> // 数据绑定 不用多介绍
<h3>{{ product.title}}</h3>
<ul>
<li v-for="item in product.list">
<a :href="item.url">{{ item.name }}</a>
<span v-if="item.hot" class="hot-tag">HOT</span>
</li>
</ul>
<div v-if="!product.last" class="hr"></div>
</template>
</div>
<div class="index-left-block lastest-news">
<h2>最新消息</h2>
<ul>
<li v-for="item in newsList">
<a :href="item.url" class="new-item">{{ item.title }}</a>
</li>
</ul>
</div>
</div>
<div class="index-right">
<slide-show :slides="slides" :inv="invTime"></slide-show> // 其中 :slider :inv 为传入子组件的 参数
<div class="index-board-list">
<div
class="index-board-item"
v-for="(item, index) in boardList"
:class="[{'line-last' : index % 2 !== 0}, 'index-board-' + item.id]"> // 这一块比较关键 涉及到样式的控制
<div class="index-board-item-inner" >
<h2>{{ item.title }}</h2>
<p>{{ item.description }}</p>
<div class="index-board-button">
<router-link class="button" :to="{path: 'detail/' + item.toKey}">立即购买</router-link> //页面跳转的一种方式 当然还有编程式的
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import slideShow from '../components/slideShow' //这是一个幻灯片组件 详细介绍了 父子组件的通信
export default {
components: {
slideShow
},
created: function () {
this.$http.get('api/getNewsList')
.then((res) => {
this.newsList = res.data
}, (err) => {
console.log(err)
})
},
data () {
return {
invTime: 2000,
slides: [
{
src: require('../assets/slideShow/pic1.jpg'),
title: 'xxx1',
href: 'detail/analysis'
},
{
src: require('../assets/slideShow/pic2.jpg'),
title: 'xxx2',
href: 'detail/count'
},
{
src: require('../assets/slideShow/pic3.jpg'),
title: 'xxx3',
href: 'http://xxx.xxx.com'
},
{
src: require('../assets/slideShow/pic4.jpg'),
title: 'xxx4',
href: 'detail/forecast'
}
],
boardList: [
{
title: '开放产品',
description: '开放产品是一款开放产品',
id: 'car',
toKey: 'analysis',
saleout: false
},
{
title: '品牌营销',
description: '品牌营销帮助你的产品更好地找到定位',
id: 'earth',
toKey: 'count',
saleout: false
},
{
title: '使命必达',
description: '使命必达快速迭代永远保持最前端的速度',
id: 'loud',
toKey: 'forecast',
saleout: true
},
{
title: '勇攀高峰',
description: '帮你勇闯高峰,到达事业的顶峰',
id: 'hill',
toKey: 'publish',
saleout: false
}
],
newsList: [],
productList: {
pc: {
title: 'PC产品',
list: [
{
name: '数据统计',
url: 'http://starcraft.com'
},
{
name: '数据预测',
url: 'http://warcraft.com'
},
{
name: '流量分析',
url: 'http://overwatch.com',
hot: true
},
{
name: '广告发布',
url: 'http://hearstone.com'
}
]
},
app: {
title: '手机应用类',
last: true,
list: [
{
name: '91助手',
url: 'http://weixin.com'
},
{
name: '产品助手',
url: 'http://twitter.com',
hot: true
},
{
name: '智能地图',
url: 'http://maps.com'
},
{
name: '团队语音',
url: 'http://phone.com'
}
]
}
}
}
}
}
//当然以上数据格式太理想 仅供参考学习
</script>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
slideShow
<template>
<div class="slide-show" @mouseover="clearInv" @mouseout="runInv">
<div class="slide-img">
<a :href="slides[nowIndex].href">
<transition name="slide-trans">
<img v-if="isShow" :src="slides[nowIndex].src">
</transition>
<transition name="slide-trans-old">
<img v-if="!isShow" :src="slides[nowIndex].src">
</transition>
</a>
</div>
<h2>{{ slides[nowIndex].title }}</h2>
<ul class="slide-pages">
<li @click="goto(prevIndex)"><</li>
<li v-for="(item, index) in slides"
@click="goto(index)"
>
<a :class="{on: index === nowIndex}">{{ index + 1 }}</a>
</li>
<li @click="goto(nextIndex)">></li>
</ul>
</div>
</template>
<script>
export default {
props: {
slides: {
type: Array,
default: []
},
inv: {
type: Number,
default: 3000
}
},
data () {
return {
nowIndex: 0,
isShow: true
}
},
computed: {
prevIndex () {
if (this.nowIndex === 0) {
return this.slides.length - 1
}
else {
return this.nowIndex - 1
}
},
nextIndex () {
if (this.nowIndex === this.slides.length - 1) {
return 0
}
else {
return this.nowIndex + 1
}
}
},
methods: {
goto (index) {
this.isShow = false
setTimeout(() => {
this.isShow = true
this.nowIndex = index
}, 10)
},
runInv () {
this.invId = setInterval(() => {
this.goto(this.nextIndex)
}, this.inv)
},
clearInv () {
clearInterval(this.invId)
}
},
mounted () {
this.runInv();
}
}
</script>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
注意
以上好多 动画切换 用到 vue 提供的 css 动画 建议查看项目源码