第6章 其他知识点汇总
6.1 计算属性与侦听器
6.1.1 计算属性
<div id="div">
<input type="text" v-model="xing">
<input type="text" v-model="ming">
{{xing + ming}}
</div>
<script>
var app = new Vue({
el: '#div',
data: {
xing:'',
ming:'',
}
})
</script>
模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。因此我们可以使用方法,来进行运算并返回数据:
<div id="div">
<input type="text" v-model="xing">
<input type="text" v-model="ming">
{{ fullname() }}
<!-- 一百次调用,观察时间结果-->
{{ fullname() }}
</div>
<script>
var app = new Vue({
el: '#div',
data: {
xing:'',
ming:'',
},
methods:{
fullname(){
return this.xing+this.ming+Date.now();
}
}
})
</script>
注意,每次在模板中使用 {{ fullname() }}
fullname方法就会被调用执行一次;所以,对于任何复杂逻辑,你都应当使用计算属性 ,因为计算属性,会自动缓存数据:
<div id="div">
<input type="text" v-model="xing">
<input type="text" v-model="ming">
<br>
{{fulln}}
<!-- 一百次调用 -->
{{fulln}}
</div>
<script>
var app = new Vue({
el: '#div',
data: {
xing:'',
ming:'',
},
computed:{
fulln(){
return this.xing+this.ming+Date.now();
}
}
})
</script>
我们可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。然而,不同的是计算属性是基于它们的依赖进行缓存的。只在相关依赖发生改变时它们才会重新求值;多次调用,计算属性会立即返回之前的计算结果,而不必再次执行函数。
6.1.2 利用计算属性获取未完成任务个数
<span class="todo-count"><strong>{{getNu}}</strong> item left</span>
computed: {
// 未完成任务个数
getNu() {
return (this.list_data.filter((v) => !v.stat)).length;
}
}
6.1.3 使用侦听器
<div id="div">
<input type="text" v-model="xing">
<input type="text" v-model="ming">
{{ fullname }}
</div>
<script>
var app = new Vue({
el: '#div',
data: {
xing: '',
ming: '',
fullname:''
},
// 设置侦听器
watch: {
// 侦听器中的方法名和要真挺的数据属性名必须一致
// xing 发生变化,侦听器就会被执行,且将变化后的值和变化前的值传入
xing:function(newVal,old_val){
this.fullname = newVal+this.ming;
},
ming:function(newVal,oldVal){
this.fullname = this.xing+newVal;
}
}
})
</script>
通过上面的案例,我们基本掌握了侦听器的使用,但是我们也发现,与计算属性相比,侦听器并没有优势;也不见得好用,直观上反而比计算属性的使用更繁琐;
虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。这就是为什么 Vue 通过 watch
选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。
<div id="div">
<input type="text" v-model="xing">
<input type="text" v-model="ming">
{{ fullname }}
</div>
<script src="./jq.js"></script>
<script>
var app = new Vue({
el: '#div',
data: {
xing: '',
ming: '',
fullname:''
},
// 设置侦听器
watch: {
// 侦听器中的方法名和要真挺的数据属性名必须一致
// xing 发生变化,侦听器就会被执行,且将变化后的值和变化前的值传入
xing:function(newVal,old_val){
// this.fullname = newVal+this.ming;
var t = this;
// 在侦听器中执行异步网络请求
$.get('./xx.php',(d)=>{
t.fullname = d;
})
},
}
})
</script>
6.2 使用ref操作DOM
在学习 jq 时,我们首要任务就是学习选择的使用,因为选择可以极其方便帮助我们获取节点查找dom,因为我们要通过dom展示处理数据。而在Vue中,我们的编程理念发生了变化,变为了数据驱动dom;但有时我们因为某些情况不得不脱离数据操作dom,因此vue为我们提供了 ref 属性获取dom节点;
<div id="app">
<input type="button" @click='click' value="按钮"> <br>
<p ref="pv">123</p>
</div>
<script>
var app = new Vue({
el: '#app',
methods: {
click: function () {
// 使用原生JS获取dom数据
// var p = document.getElementsByTagName('p')[0].innerHTML;
// console.log(p);
// 使用vue ref 属性获取dom数据
var d = this.$refs.pv.innerHTML;
console.log(d);
}
}
})
console.log(app.$refs);
</script>
但是在项目开发中,尽可能不要这样做,因为从一定程度上,ref 违背的mvvm设计原则;
6.3 过滤器的使用
6.3.1 私有(局部)过滤器
定义过滤器
var app = new Vue({
el: '#app',
data:{msg:'UP'},
//定义过滤器
filters:{
// 过滤器的名称及方法
myFilters:function(val){
return val.toLowerCase();
}
}
})
过滤器的使用:
Vue.js 允许你自定义过滤器,可被用于一些常见的文本格式化转义等操作。过滤器可以用在两个地方:双花括号插值和 v-bind 表达式 (后者从 2.1.0+ 开始支持)。过滤器要被添加到操作值得后面,使用 管道符 |
分割;vue会自动将操作值,以实参的形式传入过滤器的方法中;
{{msg|myFilters}}
过滤敏感词汇
<div id="app">
<input type="text" v-model="msg"> <br>
{{msg|myFilters|get3}}
</div>
<script>
var app = new Vue({
el: '#app',
data:{
msg:''
},
//定义过滤器
filters:{
// 过滤器的名称及方法
myFilters:function(val){
return val.toLowerCase();
},
get3:function(val){
// 遇到数字替换为 0
// var reg = /\d/g;
// return val.replace(reg,0);
return val.replace('苍井空','***');
}
}
})
</script>
6.3.2 全局过滤器
上面的代码中,myFilters
及 get3
两个过滤器,仅在当前 vue 实例中可用;如果在代码 再次 var app2 = new Vue()
得到变量为 app2
的 vue 实例,则两个过滤器在 app2中都不可用;如果需要过滤器在所有实例对象中可用,我们需要声明 全局过滤器
Vue.filter(名称,处理器)
<div id="app">
<input type="text" v-model="msg"> <br>
{{msg|myFilters}}
</div>
<!-- 定义两个DOM节点 -->
<div id="app2">
<input type="text" v-model="msg"> <br>
{{msg|myFilters|get3}}
</div>
<script>
Vue.filter('myFilters', function (val) {
return val.toLowerCase();
})
// 定义两个全局过滤器
Vue.filter('get3', function (val) {
return val.replace('苍井空','***');
})
// 两个Vue 实例
var app = new Vue({
el: '#app',
data: {
msg: ''
}
})
var app2 = new Vue({
el: '#app2',
data: {
msg: ''
}
})
</script>
6.4 自定义指令
前面我们学过 v-on 、v-model、v-show
等指令,在操作 dom 时使用了 ref 属性,其实之前学过的指令也是操作dom 的一种方式,但有时,这些指令并不能满足我们的需求,因此 vue 允许我们自定义指令来操作 dom
6.4.1 全局自定义指令
<div id="app">
<p v-setcolor>自定义指令的使用</p>
</div>
<script>
// 注册一个全局自定义指令 `v-focus`
Vue.directive('setcolor', {
// 当被绑定的元素插入到 DOM 中时……
inserted: function (el) {
// 聚焦元素
el.style.color = 'red';
}
})
var app = new Vue({
el: '#app',
})
</script>
6.4.2 私有(局部)自定义指令
<div id="app">
<p v-setcolor>自定义指令的使用</p>
</div>
<script>
var app = new Vue({
el: '#app',
// 注册 局部(私有)指令
directives: {
// 定义指令名称
setcolor: {
// 当被绑定的元素插入到 DOM 中时……
inserted: function (el) {
// 聚焦元素
el.style.color = 'red';
}
}
}
})
</script>
6.4.3 利用自定义指令使TodoList获取焦点
<input @keyup.enter="addTodo" v-getfocus class="new-todo" placeholder="请输入" >
// 注册 局部(私有)指令
directives: {
// 定义指令名称
getfocus: {
// 当被绑定的元素插入到 DOM 中时……
inserted: function (el) {
// 聚焦元素
el.focus()
}
}
},
6.4.4 为自定义指令传值
之前学习的指令中,有的指令可以传值,有的则没有,而我们自定的指令中是没有值的,如果想为自定义指令赋值,如下即可:
<div id="app">
<p v-setcolor='colors'>自定义指令的使用</p>
</div>
<script>
var app = new Vue({
el: '#app',
data:{
colors:'yellow'
},
// 注册 局部(私有)指令
directives: {
// 定义指令名称
setcolor: {
// 自定义指令可以接受第二个参数
inserted: function (el,val) {
// 第二个参数中包含了指令名称、挂载名称及数据键值
console.log(val);
// 聚焦元素
el.style.color = val.value;
}
}
}
})
</script>
6.5 过度及动画
我们可以使用v-if或者v-show控制dom元素的显示和隐藏
<div id="app">
<button @click="go">显示/隐藏</button>
<p v-show="is">pppppp1111</p>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
isShow: true,
},
methods: {
go() {
this.isShow = !this.isShow;
}
}
})
</script>
而在显示和隐藏的过程中,我们加入一些动画效果:
在进入/离开的过渡中,会有 6 个 class 切换。
v-enter
:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。v-enter-active
:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。v-enter-to
: 2.1.8版及以上 定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时v-enter
被移除),在过渡/动画完成之后移除。v-leave
: 定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。v-leave-active
:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。v-leave-to
: 2.1.8版及以上 定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时v-leave
被删除),在过渡/动画完成之后移除。
对于这些在过渡中切换的类名来说,如果你使用一个没有名字的 <transition>
,则 v-
是这些类名的默认前缀。如果你使用了 <transition name="my-transition">
,那么 v-enter
会替换为 my-transition-enter
。
<style>
.fade-enter-active,
.fade-leave-active {
transition: opacity 1s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
.man-enter-active,
.man-leave-active {
transition: opacity 4s;
}
.man-enter,
.man-leave-to {
opacity: 0;
}
</style>
<div id="app">
<button @click="go">显示/隐藏</button>
<transition name="fade">
<p v-show="isShow">pppppp1111</p>
</transition>
<transition name="man">
<p v-show="isShow">pppppp222</p>
</transition>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
isShow: true,
},
methods: {
go() {
this.isShow = !this.isShow;
}
}
})
</script>
这就是Vue中动画及过渡的基本使用方式,因为这些动画效果都需要我们自己写CSS样式,相对比较麻烦,在项目中,大多情况下,我们会借助第三方 CSS 动画库来实现,如:Animate.css ;后面项目中具体使用时,我们在进一步学习第三方 CSS 动画库的使用;
第7章 json-server与axios
一个项目从立项开始,一般都是前后端同时进行编码工作的,而此时前端需要的接口和数据后台都是无法提供的;
7.1 json-server 使用
使用全局安装 :npm install json-server -g
json-server 会将一个json文件作为数据库来存储数据,对json数据的格式是有要求的,如data.json的内容:
{
"tb1": [
{
"id": 1,
"title": "标题1",
"author": "描述信息1"
},
{
"id": 2,
"title": "标题2",
"author": "描述信息2"
}
],
"tb2": [
{
"id": 1,
"body": "some comment",
"postId": 1
}
],
"tb3": {
"name": "typicode"
}
}
启动服务: json-server --watch data.json
启动成功后,提示信息如下:
$ json-server --watch data.json
\{^_^}/ hi!
Loading data.json
Done
Resources
http://localhost:3000/tb1
http://localhost:3000/tb2
http://localhost:3000/tb3
Home
http://localhost:3000
Type s + enter at any time to create a snapshot of the database
Watching...
得到tb1所有的数据 GET: http://localhost:3000/tb1
根据id得到数据 GET : http://localhost:3000/tb1/2
添加一条数据 POST: http://localhost:3000/tb1
删除一条数据 DELETE: http://localhost:3000/tb1/2
模糊查找 GET : http://localhost:3000/tb1?title_like=标题
根据id修改数据 PUT: http://localhost:3000/tb1/1
注意:json-server 严格遵循 HTTP 请求语义进行数据处理
7.2 axios
我们在构建应用时需要访问一个 API 并展示其数据。做这件事的方法有好几种,而使用基于 Promise 的 HTTP 客户端 axios 则是其中非常流行的一种。
<script src="./axios.js"></script>
<script>
// 获取全部数据
axios.get('http://localhost:3000/list_data')
.then((data)=>{
console.log(data);
});
// 获取一条数据
axios.get('http://localhost:3000/list_data/2')
.then((data)=>{
console.log(data);
})
// 添加一条数据
axios.post('http://localhost:3000/list_data',{stat:false,title:'喝水'})
.then((d)=>{
console.log(d);
}).catch(error => console.log(error))
// 删除一条数据
axios.delete('http://localhost:3000/list_data/4')
.then((d)=>{
console.log(d);
}).catch(error => console.log(error))
// 修改一条数据
axios.put('http://localhost:3000/list_data/6',{title:'hhhhhh'})
.then((d)=>{
console.log(d);
}).catch(error => console.log(error))
</script>
第8章 重构TodoList案例
8.1 启动API接口及数据
db.json:
{
"list_data": [
{
"id": 1,
"title": "吃饭",
"stat": true
},
{
"id": 2,
"title": "睡觉",
"stat": false
},
{
"id": 3,
"title": "打豆豆",
"stat": true
}
]
}
启动服务: json-server --watch db.json
8.2 获取全部任务
el: '#todoapp',
data: {
// list_data:list_data,
list_data:[]// es6属性简写
},
// 当vue实例获取到 el:'#todoapp' 自动调用执行 mounted 方法
mounted:function(){
let url = 'http://localhost:3000/list_data';
axios.get(url).then((backdata)=>{
// console.log(backdata.data);
this.list_data = backdata.data;
})
},
8.3 添加任务
……
methods: {
// 添加任务事件处理器
// addTodo:function(){}
// 简写形式
addTodo(ev) {
// 获取当前触发事件的元素
var inputs = ev.target;
// 获取value值,去除空白后判断,如果为空,则不添加任务
if (inputs.value.trim() == '') {
return;
}
// 组装任务数据
var todo_data = {
// 通过服务器添加数据时,不需要id值
// id: this.list_data.length + 1 + 1,
title: inputs.value,
stat: false
};
let url = 'http://localhost:3000/list_data';
// 将数据提交保存到服务器
axios.post(url,todo_data).then((back_data)=>{
let {data,status} = back_data;
if(status == 201){
// console.log(this.list_data);
// 数据保存成功后,将数据添加到任务列表展示
this.list_data.push(data);
}
})
// 清空文本框
inputs.value = '';
},
……
8.4 删除任务
<button @click="removeTodo(key,val.id)" class="destroy"></button>
// 删除操作
removeTodo(key,id) {
let url = 'http://localhost:3000/list_data/'+id;
axios.delete(url).then((back_data)=>{
// 结构对象
let {data,status} = back_data;
// console.log(back_data);
if(status == 200){
this.list_data.splice(key, 1);
}
})
},
8.5 完成任务
<li v-for="(val,key) in list_data" @click="todoDone(key,val.id)" v-bind:class="{completed:val.stat}">
// 完成任务 事件处理器(新添加,原案例中没有)
todoDone(key,id){
let url = 'http://localhost:3000/list_data/'+id;
// 组装数据准备修改服务器数据
setdata = {};
// 注意:事件优先于浏览器渲染执行,获取当前状态
var chestat = this.list_data[key].stat;
// 状态取反
setdata.stat = !chestat;
setdata.title = this.list_data[key].title;
// console.log(setdata);
axios.put(url,setdata).then((backdata)=>{
var {data,status} = backdata;
// 如果服务器修改失败,则重新渲染DOM节点样式,改回原始状态
// 服务器返回状态有误
if(status != 200){
this.list_data[key].stat = chestat;
}
// 如果异步执行失败失败,则重新渲染DOM节点样式,改回原始状态
}).catch((err)=>{
if(err){
this.list_data[key].stat = chestat;
}
})
},
8.6 案例中的Bug
修改:<button @click.stop="removeTodo(key,val.id)" class="destroy"></button>
遇到Bug,垮住腚稳住裆,千万不要慌,找到原因才能想到对应解决方案;
第9章 组件
https://cn.vuejs.org/v2/guide/components.html
https://cn.vuejs.org/v2/guide/components-registration.html
9.1 认识组件
组件系统是 Vue 的一个重要概念,因为它是一种抽象,允许我们使用小型、独立和通常可复用的组件构建大型应用。通常一个应用会以一棵嵌套的组件树的形式来组织:
例如,你可能会有页头、侧边栏、内容区等组件,每个组件又包含了其它的像导航链接、博文之类的组件。
9.2 基本使用
组件是可复用的 Vue 实例,且带有一个名字。把这个组件作为自定义元素来使用。组件的好处是写一次可以进行任意次数的复用。
<div id="app">
<!-- 使用组件 -->
<!-- 将组件名直接当做标签名在html代码中使用即可 -->
<mytemp></mytemp>
</div>
<script>
// 定义一个名为 mytemp 的新组件
Vue.component('mytemp',{
// template属性的值,作为组件的内容
// vue 会把这个值替换到html中并会被浏览器渲染
template:"<h2>我是一个组件</h2>"
})
var app = new Vue({
el: '#app',
})
</script>
上面代码中我们直接使用 Vue.component()
方法定义了组件,而这个 mytemp
组件可以用在所有 vue 实例中,
这种组件被称为 全局组件
在具体的某个vue实例中,也可以定义组件,但是组件仅会在具体的 vue 实例中起作用