第3章 Vue 语法
3.1 插值绑定
3.1.1 文本插值
{{}}
直接输出内容
3.1.2 HTML插值
用v-html
,会被直接渲染成视图节点,
3.2 属性绑定v-bind:
/:
3.2.2 类名、样式绑定
三种方式:
- 字符串
classStr: 'class1 class2 class3'
- 数组
classArr: ['class1', 'class2', 'class3']
- 对象
classObj: {'class1': true/false,....}
3.3 事件绑定v-on:
/@
3.3.2 常见修饰符
可后缀于事件名称之后:@click.prevent=""
等
.stop
-当事件触发时,阻止事件冒泡;.prevent
-当事件触发时,阻止默认行为capture
-当事件触发时,阻止事件捕获.self
-限制事件仅作用于节点自身.once
-事件被触发一次后即解除监听.passive
-移动端,限制事件用不调用preventDefault()方法
3.3.3 按键修饰符
.delete
.tab
.enter
.esc
.space
.left
.up
.right
.down
如@click.enter=""
3.3.3 组合修饰符
.ctrl
.alt
.shift
.meta
如@click.ctrl="" @click=""
3.4 双向绑定v-model
3.4.2 v-model与修饰符
.lazy
-将输入的数据赋值于变量的时机由输入时延迟到数据改变时.number
-自动转换输入为数值类型.trim自动过滤输入的首尾空白字符
如v-model.trim.number=""
3.4.3 v-model与自定义组件
??????
3.5 条件渲染v-if
/v-show
与列表渲染v-for
3.5.1 v-if和v-show
v-if=""
v-show=""
只是简单的切换 元素的CSS属性:display,其不支持template元素
3.5.2 v-for
v-for="item in items"
或v-for="item of items"
- 可以用index作为第二个参数标识下标
(item, index) in/of items
- 渲染一个对象的键值对:
如:
<ul>
<li v-for="(user, index) in users">
...
<ul>
<li v-for="(value, key) of user">
...
</li>
</ul>
</li>
</ul>
与数据响应有关的吗数组方法:
push
-将一个或多个元素添加至数组末尾,并返回新数组的长度pop
-从数组中删除并返回最后一个元素shift
-从数组中删除并返回第一个元素unshift
-将一个或多个元素添加至在数组开头,并返回新数组长度splice
-从数组中删除火元素或向数组添加元素sort
-对数组元素排序,默认按照Unicode编码排序,返回排序后的数组reverse
将数组中的元素位置颠倒,返回颠倒后的数组
注意:
直接使用下标/键名为数组 /对象设置成员时:
arr[0] = 99
obj['key'] = 'value'
Vue并不会将其加入数据响应式系统,因此视图并不会改变
3.5.3列表渲染中的key
为每个迭代元素提供一个值不重复的key,不要使用index
<li v-for="item in items" :key="item.id"></li>
第4章 Vue选项
4.1 数据方法
4.1.1 数据选项data
数据(data)可接受的类型有对象(data: {}
)和函数(data () {}
)两种,不过在定义组件时只能使用函数类型。
注意:不要将已声明的对象用于data选项,应该创建新的对象,或者使用JSON.parse(JSON.stringify(obj))
深拷贝已有对象:
let Jack = {
counter: 0
}
Vue.component('button-counter', {
data() {
return JSON.parse(JSON.stringify(jack)) //深拷贝
},
template: '<button @click="++counter && (console.log(\'click\'))">click {{counter}} times</button>'
})
选项属性props
为组件注册动态特性,可以是数组或者对象类型,用于接收从父组件传递过来的参数,并允许为其设置默认值(default
)、类型检测(type
)、校验规则(validator
)等
方法选项methods
methods选项,不要用箭头函数在其中定义方法,否则this将无法正确指向Vue实例,因为使用箭头函数不会创建函数作用域。
计算属性computed
也不能用箭头函数,其依赖于响应式属性,所以当且仅当响应式属性变化时,计算属性才被重新计算,得到的结果被缓存,直到响应式属性再次被改变。
侦听属性watch
watch更重于处理数据变化时的业务逻辑,而computed更注重衍生数据,watch还可以异步修改数据
4.3 DOM渲染
4.2.1 指定被挂载元素el
1.CSS选择器
el: '#app'
2.DOM节点对象
el: document.getElementById('app2)
3.$mount方法
vm.$mount('#app')
4.2.2 视图的字符串模板template
4.2.3 渲染函数render
createElement
来创建DOM节点,可接收三个参数:
- HTML标签字符串 | 组件选项对象 | 节点解析函数
- 定义节点特性的对象
- 子节点
render (createElement) {
return createElement('div',{
节点特性对象(style,class,attes,domProps,on等) // domProps用于绑定DOM属性,innerHTML、innerText等
},
'子节点'
)
}
4.2.4 选项的优先级
优先级:render
>template
>el
4.3 封装复用
4.3.1 过滤器filters
可在双括号插值(Mustache语法)中添加在JavaScript表达式的尾部,以|
间隔,表达式的值将作为形参传入。
4.3.2 自定义指令
关注bind和update两个钩子函数
4.3.3 组件的注册components
4.3.4 混入的使用mixins
灵活的分发组建中一些可复用的功能。
混入冲突时的默认策略:
冲突选项 | 合并策略 | 冲突策略 |
---|---|---|
data | 合并根节点数据 | 优先采用组件的数据 |
mounted 等钩子函数 | 混合为数组 | 全部调用且优先调用mixin的钩子函数 |
methods /components /directives 等 | 混为同一对象 | 优先采用组件的键值对 |
watch | 混合为数组 | 全部调用且优先调用mixin的watch方法 |
第5章 Vue内置组件
5.1 组件服务
5.1.1 动态组件
component接收一个名为is
的属性,is的值应为父组件中注册过的组件名称,用法如下:
<component :is=""view></component> <!--view为变量-->
5.1.2 使用内置插槽分发内容slot
定义多个插槽时:使用name
进行区分,如未指定name,则会将插槽内容至于默认插槽中。
5.1.3 组件的缓存keep-alive
keep-alive应出现在组件被移除后需要再次被挂载的地方,
如使用动态组件
时:
<keep-alive>
<component :is="view"></component>
</keep-alive>
或使用v-if
时:
<keep-alive>
<one v-if=""isOne></one>
<two v-else></two>
</keep-alive>
它还可以接收include
和exclude
两个props属性:
include
-只有匹配的组件会被缓存exclude
-被匹配的组件不会被缓存
5.2 过渡效果
5.2.1 单节点的的过渡transition
style:
v-enter,v-leave-to;v-enter-active,v-leave-active;
<transition>
<div>...</div>
</transition>
自定义动画:transitionName-enter.....
style:
transitionName-enter.......
<transition name="transitionName">
<div>...</div>
</transition>
引入动画库:enter-class
,enter-active-class
…
<transition
name="custom"
enter-active-class="animated 动画名字"
leave-active-class="animated 动画名字">
......
</transition>
专门的初始渲染过度:appear
<transition
appear
appear-class="custom-appear-class"
appear-to-class="custom-appear-to-class"
appear-active-class="custom-appear-active-class">
......
</transition>
复用时
Vue元素复用时,需要赋予元素唯一的key
值重建新的元素,并且用in-out
或out-in
防止两个元素同时出现:
<transition mode="out-in">
<div :key="isMaster ? 'master : 'other'">...</div>
</transition>
5.2.2 多节点过渡transition-group
- 将以真实元素呈现,默认为span,可以通过tag属性更换为其他
- 过渡模式不可用
- 内部元素必须提供唯一的key
<transition-group name="list" tag="ul">
<li>...</li>
</transition-group>
用于改变元素定位的动画v-move
改变元素定位,定义方式与v-enter等一致,
第6章 Vue项目化
6.1 快速构建项目
6.1.2 使用Vue CLI构建项目
详见另一篇快速开始一个Vue项目
6.2 前端路由
6.2.1 前端路由的简单实现
Node.js创建服务端文件,服务端文件app.js代码如下:
const http = require('http'); // http模块
const fs = require('fs'); // 文件处理模块
const hostName = '127.0.0.1';
const port = 3000;
const server = http.createServer(function (req, res) { // 创建http服务
let content = fs.readFileSyns('index.html'); // 读取文件
res.writeHead(200, { // 设置响应内容类型
'content-type': 'text/html;charset="utf-8'
});
res.write(content); // 返回index.html文件内容
res.end()
});
server.listen(port, hostName, function() { // 启动服务监听
console.log(`Server is running here: http://${hostName}: ${port}`)
})
安装Node后,在app.js文件所处目录下输入命令node app.js
,当控制台出现Server is running here: http://127.0.0.1:3000
时,即表示服务器启动成功。
使用Vue实现前端路由:
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<div id="app">
<ul>
<li><router-link to="/">Home</router-link></li>
<li><router-link to="/about">About</router-link></li>
</ul>
<router-view></router-view>
</div>
<script>
let Home = {
template: '<h1>This is Home!</h1>'
};
let About = {
template: '<h1>This is About!</h1>'
};
let routes = [ //定义路由规则
{
path: '/',
component: Home
},
{
path: '/about',
component: About
}
];
let RouterLink = {
props: ['to'],
template: '<a :href="to"><slot name="default"></slot></a>'
};
let RouterView = {
data () {
return {
url: window.location.pathname // 获取浏览器地址
}
},
computed: {
ViewComponent () { // 根据浏览器地址返回相应组件
return routes.find(route => route.path === this.url).component
}
},
render (h) {
return h(this.ViewComponent)
}
};
/* eslint-disable*/
let vm = new Vue ({
el: '#app',
components: {
RouterLink,
RouterView
}
})
</script>
使用原生JS实现前端路由:
<div>
<ul>
<li><a href="#/">Home</a></li>
<li><a href="#/about">About</a></li>
</ul>
<!-- 动态视图被挂载的元素 -->
<div id="view"></div>
</div>
<script>
let Home = '<h1>This is Home!</h1>'; // 视图模板Home
let About = '<h1>This is About</h1>'; // 视图模板About
let Router = function (el) { // 定义路由类
let view = document.getElementById(el);
let routes = []; // 路由规则列表
let load = function (route) { // 加载视图
route && (view.innerHTML = route.template)
};
let redirect = function () { // 分发视图
let url = window.location.hash.slice(1) || '/';
for (let route of routes) {
url === route.url && load(route)
}
};
this.push = function (route) { // 添加路由规则
routes.push(route)
};
window.addEventListener('load',redirect, false); // 页面加载时
window.addEventListener('hashchange', redirect, false) // URL变化时
};
let router = new Router('view'); // 实例化路由
router.push({
url: '/',
template: Home
});
router.push({ // 添加路由规则
url: '/about',
template: About
})
</script>
6.2.2 Vue中的前端路由
Vue Router
RouterLink
: 默认渲染成一个<a>
标签,to
属性用于指定跳转连接
<router-link to=""></router-link>
RouterView
: 负责挂载路由匹配到的视图组件
<router-view></router-view>
1.基础路由
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<ul>
<li>
<router-link to="/">Home</router-link>
</li>
<li>
<router-link to="/about">About</router-link>
</li>
</ul>
<router-view></router-view>
</div>
<script>
let Home = {
template: '<h1>This is Home!</h1>' // Home组件
};
let About = {
template: '<h1>This is About!</h1>' // About组件
};
let routes = [ // 定义路由规则,每一个路由规则应该映射一个视图组件
{
path: '/',
component: Home
},
{
path: '/about',
component: About
}
];
let router = new VueRouter({ // 创建Vue Router实例,并传入routes配置
routes
});
let app = new Vue ({
router
}).$mount('#app')
</script>
2. 动态路由
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<ul>
<li><router-link to="/">Home</router-link></li>
<li @click="add">
<!--2. 参数num由实例传入路由-->
<router-link :to="'/about/' + num">About</router-link>
</li>
</ul>
<router-view></router-view>
</div>
<script>
let Home = {
template: '<h1>This is Home!</h1>'
};
let About = {
template: `<div>
<h1>This is About!</h1>
<p>num: {{$route.params.num}}</p> // 3. 在组件中显示参数num
</div>`
};
let routes = [
{
path: '/',
component: Home
},
{
path: '/about/:num', // 1. 定义了参数num,格式如:/:num
component: About
}
];
let router = new VueRouter ({
routes
});
let app = new Vue ({
data() {
return {
num: 0
}
},
methods: { // 当点击About时,num值自增1
add() {
this.num++
}
},
router
}).$mount('#app')
</script>
3. 嵌套路由
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<ul>
<li><router-link to="/">Home</router-link></li>
<li>
<div><router-link to="/about">About</router-link></div>
<ul>
<!--3. 使用嵌套路由-->
<li><router-link to="/about/author">About-Author</router-link></li>
<li><router-link to="/about/email">About-Email</router-link></li>
</ul>
</li>
</ul>
<router-view></router-view>
</div>
<script>
let Home = {
template: '<h1>This is Home!</h1>'
};
let About = {
template: `<div>
<h1>This is About!</h1>
<router-view></router-view> // 1. 嵌套的动态视图区
</div>`
};
let Author = {
template: '<p>Author: lonely dawn</p>'
};
let Email = {
template: '<p>Email: lonelydawn@sina.com</p>'
};
let routes = [
{
path: '/',
component: Home
},
{
path: '/about',
component: About,
children: [ // 2. 嵌套子路由
{
path: 'author',
component: Author
},
{
path: 'email',
component: Email
}
]
}
];
let router = new VueRouter ({
routes
});
let app = new Vue ({
router
}).$mount('#app')
</script>
- 嵌套路由可以实现在动态视图中嵌套动态视图;
- 多层的动态视图也可以使用component来实现,不过刷新页面后会回到初始状态,而使用路由分发的视图刷新后会保持当前视图,并会在
history
留下记录。
4. 编程式路由
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<ul>
<!--默认字符串为路径参数-->
<li @click="redirectByPath('/')">Home</li>
<li>
<!--指定参数为路径-->
<div @click="redirectByPath('/about')">About</div>
<ul>
<!--嵌套路由-->
<li @click="redirectByPath('/about/author')">About-Author</li>
<!--嵌套路由,动态路由,当使用path时params参数不生效-->
<li @click="redirectByPath('/about/email',{email: 'lonelydawn@sina.com'})">About-Email</li>
<!--嵌套路由,动态路由,可以直接将参数写入path-->
<li @click="redirectByPath('/about/email/lonelydawn@sina.com')">About-Email</li>
<!--嵌套路由,动态路由,使用命名路由跳转视图-->
<li @click="redirectByName('Email', {email: 'sinledawn@sina.com'})">About-Email</li>
</ul>
</li>
</ul>
<router-view></router-view>
</div>
<script>
let Home = {
template: '<h1>This is Home!</h1>'
};
let About = {
template: `<div>
<h1>This is About!</h1>
<router-view></router-view> // 嵌套的动态视图区
</div>`
};
let Author = {
template: '<p>Author: lonely dawn</p>'
};
let Email = {
template: '<p>Email: {{$route.params.email}}</p>'
};
let routes = [
{
path: '/',
component: Home
},
{
path: '/about',
component: About,
children: [
{
name: 'Author',
path: 'author',
component: Author
},
{
name: 'Email',
path: 'email/:email',
component: Email
}
]
}
];
let router = new VueRouter ({
routes
});
let app = new Vue ({
methods: {
redirectByPath(path, params) {
this.$router.push({path, params})
},
redirectByName (name, params) {
this.$router.push({name, params: })
}
},
router,
}).$mount('#app')
</script>
- 没有使用RouterLink组件,而是在JS中使用router.push方法跳转视图;
- 还可以赋予路由
name
属性,通过name跳转视图; - 动态参数应放在
params
中,当使用path
时,params不生效,此时应将参数值直接写进path。
5. Vue ClI快速构建项目中的router/index.js
- 使用
Vue.use(Router)
安装Vue Router插件 export
返回路由规则
6.3 状态管理
6.3.1 对象引用
6.3.2 状态管理器Vuex
State
vuex中的数据源,需要保存的数据就保存在这里,要确保应用只有唯一的数据源。
用法:
new Vuex.Store({ // 创建仓库
state: {
count: 1
}
})
可以在页面通过$store.state.count
来获取定义的数据,
Getters
相当于vue中的computed
计算属性,Getter 中的派生状态在被计算之后会被缓存。
用法:
new Vuex.Store({
state: {
count: 1
},
getters: {
tenTimesCount (state) { // Vuex为其注入state对象
return state.count * 10
}
}
})
getStateCount
方法接收一个参数state,这个参数就是我们用来保存数据的那个对象;
在组件中,可以使用$store.getters.tenTimesCount
,
Mutations
提供修改State状态的方法。
用法:
new Vuex.Store({
state: {
count: 0
},
mutations: {
addCount (State, num) {
state.count += num || 1
}
}
})
在组件中,直接使用$store.commit('funName')
提交mutation:
methods: {
addCount() {
this.$store.commit('addCount')
}
}
Actions
类似Mutation,但:
- Action 不能直接修改状态,只能通过提交mutation来修改
- Action可以包含异步操作
用法:
new Vuex.Store({
state: {
count: 0
},
mutations: {
addCount (state, num) {
state.count += num || 1
}
},
actions: {
// contex具有和store实例相同的属性和方法
// 可以通过context获取state和getter中的值,或者提交mutation和分发其他的action
addCountAsync (context, num) {
setInterval(function(){
if (context.state.count < 2000) {
context.commit('addCount', num || 100)
}
}, num || 100)
}
}
})
在组件中,可以直接使用$store.dispatch('funName')
分发action.
Moudules
定义模块
const counter = {
namespaced: true, // 定义为独立的命名空间
state:{},
......
}
注册模块
new Vuex.Store({
moudules: {
counter
}
})
mapState、mapGetters、mapActions
在组件中,除了使用$stroe.state`这种写法,还可以用辅助函数映射
import {mapState, mapGetters, mapMutations, mapActions} from 'vuex'
export default {
// 辅助函数的第一个参数为模块的名称
computed: {
...mapState('counter', ['count']), // ...是ES6中对象展开运算符
...mapGetters('counter', ['tenTimesCount'])
},
methods: {
...mapMutations('counter', ['addCount']),
...mapActions('counter', ['addCountAsync'])
}
}
6.3.3 在项目中使用Vuex
在项目目录命令行输入cnpm install vuex --save-dev
安装插件,
在src
目录下创建store
、store/index.js
、store/modules
、store/modules/counter.js
…