Vue笔记

基础

声明式渲染

<div id="app">
  {{ message }}
</div>
var app = new Vue({
  el: "#app",
  data: {
    message: "hello"
  }
});

异步向data新增对象

<div id="app">
  <!-- 可以不在data里声明并且在dom里写上去,然后用js异步声明 -->
  <div v-if="showflag">
  	{{ info }}
  </div>
</div>
var app = new Vue({
  el: "#app",
  data: {
    showflag: false
  },
  mounted() {
  	this.info = "hello";
  	this.showflag= true;	// dom可以访问到
  	this.test();	// 方法可以访问到
  },
  methods: {
    test() {
	  console.log(this.info);
    }
  }
});

一次性插值(v-once)

<div id="app">
  <div v-once> {{ a }} </div>	<!-- 永远输出 1 -->
  <div> {{ b }} </div>
</div>
var app = new Vue({
  el:"#app",
  data: {
    a: 1,
    b: 2
  },
  mounted() {
    this.a = 3	// 不会影响dom中 v-once 标记的数据 
  }
});

解析为HTML/文本

<div id="app">
  {{message}}  <!-- 被解析为文本 -->
  <span v-html="htmlmessage"></span>  <!-- 被解析为HTML -->
</div>
var app = new Vue({
  el:"#app",
  data:{
    message:"<p style='border:1px solid red'>hello</p>",
    htmlmessage:"<p style='border:1px solid red'>world</p>"
  }
});
app.htmlmessage="<p style='border:1px solid red'>hello</p>";  //v-html指令也支持响应

使用js表达式

可以在{{ }}v-bindv-html里使用表达式,注意不可以调用自定义方法,因为它是运行在沙盒中的,只能访问白名单内的系统方法

<div id="app" v-bind:title="num+5">  <!-- v-bind里使用 -->
  {{message+" world"}}
  {{parseInt(num)}}  <!-- 调用系统方法 -->
  {{ok ? "Yes" : "No"}}  <!-- 三元表达式 -->
  <span v-html="num+5"></span>  <!-- v-html里使用 -->
</div>

动态参数

<div id="app">
  <!-- attr1 作为 JS表达式传入,value1 作为 data属性传入 -->
  <input type="button" v-bind:[attr1]="value1" />
  <!-- 注意 Attr2在 html中会被解析为小写,应尽量避免属性名大写 -->
  <input type="button" v-bind:[Attr2]="value2" />
  <!-- attr3 作为 JS表达式传入,注意值不能动态设置 -->
  <input type="button" v-on:[attr3]="sc" />
  <!-- 表达式示例,注意,根据 html语法,表达式不能使用空格、单引号等 -->
  <input type="button" v-on:[ok?type1:type2]="sj" />
</div>
var app=new Vue({
  el:"#app",
  data:{
    attr1:"value",
    value1:"button1",
    attr2:"title",
    value2:"button2",
    attr3:"click",
    ok:false,
    type1:"click",
    type2:"dblclick"
  },
  methods:{
    sc:function(){
      console.log(event.target.tagName);
    },
    sj:function(){
      console.log(this.ok?this.type1:this.type2);
    }
  }
});

v-bind指令

基本格式

<div id="app" v-bind:title="biaoti"></div>
<!-- !不仅支持所有系统属性,用户自定义的属性也可以设置 -->
<!-- 缩写形式:<div id="app" :title="biaoti"></div> -->
var app = new Vue({
  el:"#app",
  data:{
    biaoti:"this is title"
  }
});

布尔型属性特性

复习:<div hidden="hidden">表示隐藏元素,不隐藏则不加这个属性(只有这一个方式,赋其他值照样隐藏)
Vue能够自动判断这类属性,当绑定的值为"Truthy(真值)"时,会删除这个属性,否则不会

<div id="app" v-bind:hidden="isHidden">
  hello
</div>
var app = new Vue({
  el:"#app",
  data:{
    isHidden:false  //app不会显示
  }
});

需要注意Vue内的Truthy(真值)假值与实际略有不同:载自:MDN web docs

//浏览器默认Truthy(真值)
if (true)
if ({})
if ([])
if (42)
if (-42)
if (3.14)
if (-3.14)
if ("foo")
if (new Date())
if (Infinity)
if (-Infinity)
//以上都支持,且Vue增加以下真值
if ("")
if (0)
if (-1)
//浏览器默认假值
if (false)
if (null)
if (undefined)
if (0)  //不支持
if (0n) //不支持
if (NaN)
if ('') //不支持
if ("") //不支持
if (``) //不支持
if (document.all)

v-is(动态标签名)

动态指定标签/组件名

<div id="app">
  <!-- 动态指定标签名,这里的标签名最好是component(官方文档是这样写的,可能是因为一般用于组件?,但其他地方也能用) -->
  <!-- 点击后标变成div -->
  <component v-bind:is="current" @click="current='div'">current tag is {{current}}</component>
  
  <!-- 正经的用法:动态组件名,上面的用法不是很正经 -->
  <component v-bind:is="currentTab" @click.native="currentTab='tab-main'"></component>
</div>
Vue.component("tab-header",{
  template:'<div>header component</div>'
});
Vue.component("tab-main",{
  template:'<div>main component</div>'
});
Vue.component("tab-bottom",{
  template:'<div>bottom component</div>'
});

var app=new Vue({
  el:"#app",
  data:{
    current:"a",
    currentTab:"tab-header"
  }
});

属性和数据获取方式

//获取data数据
app.message;

//获取Vue属性
//错误示例:app.el ==> notdefined
app.$el === document.getElementById("app");  //true
app.$data === data;  //true

Vue生命周期

//示例
var data={
  message:"hello"
};
var app = new Vue({
  el:"#app",
  data:data,
  created:function(){  //created 阶段执行
    console.log("message="+this.message);  //输出 hello
  }
});

vue生命周期

计算属性和侦听器

基本格式

<div id="app">
  <div id="demo">{{fullName}}</div>
</div>
var app=new Vue({
  el:"#app",
  data: {
    firstName: 'Foo',
    lastName: 'Bar'
    //注意这里不能声明fullName
  },
  computed:{
    fullName:function(){
      return this.firstName+" "+this.lastName;
    }
  }
});

缓存特性

计算属性只有相关响应式依赖发生改变时它们才会重新求值

<div id="app">
  <!-- 方法 -->
  {{ m1() }}  <!-- 运行后显示 1567954020656 -->
    {{ ys() }}  <!-- 延时一会儿 -->
  {{ m1() }}  <!-- 运行后显示 1567954020712 -->
  
   <!-- 计算属性 -->
  {{ m2 }}  <!-- 运行后显示 1567954020712 -->
    {{ ys() }}  <!-- 延时一会儿 -->
  {{ m2 }}  <!-- 运行后显示 1567954020712 -->
</div>
var app=new Vue({
  el:"#app",
  methods:{
    m1:function(){
      return Date.now();
    },
    ys:function(){
      for(var i=0;i<100;i++){
        var abc=1.23456*2.34567;
        console.log(abc);
      }
    }
  },
  computed:{
    m2:function(){
      return Date.now();
    }
  }
});

set方法(覆盖缓存)

若要重新执行上一小节的m2方法,覆盖缓存,需要用到set方法

var app=new Vue({
  el:"#app",
  data:{
    dateTime:null  //声明m2的dateTime
  },
  methods:{
    m1:function(){
      return Date.now();
    },
    ys:function(){
      for(var i=0;i<100;i++){
        var abc=1.23456*2.34567;
      }
    }
  },
  computed:{
    m2:{
      get:function(){
        this.dateTime=Date.now();
        return this.dateTime;
      },
      //设置m2=?后会执行这个函数,然后重新执行get方法并缓存
      set:function(newValue){  //newValue是传来的值,用不到可以不写这个参数
        this.dateTime=Date.now();
      }
    }
  }
});
//点击document更新m2
addEventListener("click",function(){
  app.m2="";  //参数,因为没有需要传入参数,所以随便传(null、undefined、1...)
  console.log(app.m2);
});

侦听器

v-model用来给表单提供双向数据绑定,会智能判断对象来提供不同的属性和事件(例如为text提供input事件和value属性)

<div id="app">
  <input type="text" v-model="textContent"><br/>
</div>
var app=new Vue({
  el:"#app",
  data:{
    textContent:""
  },
  watch:{  //侦听事件,发生后执行相应函数
    textContent:function(){
      console.log("侦听到textContent值被改变");
    }
  },
  created:function(){  //创建时执行一次
    console.log("请输入");
  }
});

class和style绑定

对象格式

class

<div id="app">
  <!-- 可以和普通的class并存 -->
  <div class="default"
    :class="{bgRed:isBgRed,'colorBlue':isColorBlue}">text</div>
    <!-- 对象名是绑定的类名,值是data中的一个布尔值 -->
</div>
.default{font-size:25px;}
.bgRed{background:red;}
.colorBlue{color:blue;}
var app=new Vue({
  el:"#app",
  data:{
    isBgRed:true,  //表示添加
    isColorBlue:false  //表示不添加
  }
});

style
和class相同类似:

<div id="app">
  <div :style="{border:redBorder,backgroundColor:blackBG,'font-size':big+'px'}">text</div>
  <!--                               驼峰式写法         非驼峰需加引号   可使用表达式   -->
</div>
... ...
data:{
  redBorder:"1px solid red",
  blackBG:"#888",
  big:50
}
... ...

计算属性绑定class

这是一个常用且强大的模式:

<div id="app">
  <div class="default" :class="vueClass">text</div>
</div>
var app=new Vue({
  el:"#app",
  data:{
    isBgRed:true,
    isColorBlue:false
  },
  computed:{
    vueClass:function(){
      return {
        bgRed:this.isBgRed ? true : false,
        colorBlue:this.isColorBlue ? true : false
      }
    }
  }
});

通常,style也结合计算属性使用

数组格式

class

<div id="app">
  <div class="default"
    :class="['bgRed',isColorBlue ? colorBlue : '']">text</div>
    <!-- 可以用三元运算符实现是否添加,直接写则表示始终添加 -->
</div>
var app=new Vue({
  el:"#app",
  data:{
    isColorBlue:true,
    colorBlue:"colorBlue"
  }
});

style

<div id="app">
  <div :style="[baseStyle,smallSize]">text</div>
  <!--            必须是对象类型     -->
</div>
... ...
data:{
  baseStyle:{
    background:"#888",
    color:"#fff",
    fontSize:"50px"
  },
  smallSize:{
    width:"140px",
    height:"80px",
    textAlign:"center"
  }
}
... ...

组件中绑定

例如声明了一个添加有class的组件

Vue.component('c1', {
  template:"<div class='default'></div>"
})

使用时,新添加的class不会覆盖原class

<c1 class="redColor"></c1> 
<!-- class = redColor default -->
<c1 :class="{redColor:isRedColor}">
<!-- 同样支持数据绑定class -->

style多重值

可以为style绑定中的属性提供一个包含多个值的数组,常用于提供多个带前缀的值,例如:

<!-- 优先倒序使用,若支持则使用,若不支持则检测上一个 -->
<div :style="{display:['-webkit-flex', '-o-flex', 'flex']}">text</div>

事件处理

基本格式

<div id="app">
  <!-- 执行简单的js代码 -->
  <input type="button" value="按钮" v-on:click="clickNum++" /> {{ clickNum }}
  <!-- 执行方法 -->
  <input type="button" value="按钮" v-on:click="m1" />
  <!-- 执行带参方法 -->
  <input type="button" value="按钮" v-on:click="m2(msg)" />
  <!-- 带参方法加事件对象 -->
  <input type="button" value="按钮" v-on:click="m2(msg,$event)" />
  <!-- 缩写形式:<input @click="m1" /> -->
</div>
var app = new Vue({
  el:"#app",
  data:{
    msg:"hello",
    clickNum:0
  },
  methods:{
    m1:function (){
      console.log(event.target.tagName);  //带有一个event(事件对象)参数 快捷链接:https://blog.csdn.net/yjl15517377095/article/details/96839606#event_141
    },
    m2:function (message){
      console.log(message);
    },
    m3:function (message,e){
      console.log(message+" "+e.target.tagName);
    }
  }
});

事件修饰符

阻止跳转/冒泡

原生js传送门

<div id="app">
  <!-- event.stopPropagation 响应本次事件 阻止事件继续冒泡 可以不写参数 也可以写方法名 -->
  <a href="https://www.baidu.com" v-on:click.stop="">跳转</a>
  <!-- event.preventDefault 阻止本次事件 事件继续冒泡 也可以只写修饰符 -->
  <a href="https://www.baidu.com" v-on:click.prevent>跳转</a>
  <!-- 修饰符串联 相当于return false; -->
  <a href="https://www.baidu.com" v-on:click.stop.prevent>跳转</a>
</div>

dom事件流

原生js传送门

<div id="app">
  <div v-on:click="sc(1)" style="width:100px;height:100px;background: #007BFF;">
    <div v-on:click="sc(2)" style="width:75px;height:75px;background: #138496;">
      <div v-on:click="sc(3)" style="width:50px;height:50px;background: #E0A800;">d3
      </div>d2
    </div>d1
  </div>
</div>
<!-- 函数功能为:sc(n){console.log(n)} -->

默认为冒泡阶段触发,点击3,结果为: 3 2 1

<!-- 可以设置某个事件在捕获阶段触发 -->
<div v-on:click="sc(1)" style="width:100px;height:100px;background: #007BFF;">
  <div v-on:click.capture="sc(2)" style="width:75px;height:75px;background: #138496;">
    <div v-on:click.capture="sc(3)" style="width:50px;height:50px;background: #E0A800;">

点击3,结果为:2 3 1

<!-- Vue还多了一个功能:只当在event是当前元素自身时触发处理函数,否则不触发 -->
<div v-on:click="sc(1)" style="width:100px;height:100px;background: #007BFF;">
  <div v-on:click.self="sc(2)" style="width:75px;height:75px;background: #138496;">
    <div v-on:click="sc(3)" style="width:50px;height:50px;background: #E0A800;">

点击3,结果为:3 1,没触发2
点击2,结果为:2 1,2被触发

这些修饰符都能够互相串联,例如:v-on:click.self.prevent …等
官方说 “使用修饰符时,顺序很重要,相应的代码会以同样的顺序产生”,但实际测试没影响,可能存在偶然性和兼容性

addEventListener参数三

传送门

<!-- 事件只被触发一次,然后 removeEventListener -->
<input v-on:click.once="sc" type="button" value="按钮" />

传送门

<!-- 忽略.prevent -->
<div v-on:scroll.passive></div>

键盘按键修饰符

事件类型

传送门

<div id="app">
  <!-- 键盘弹起 -->
  <input type="text" v-on:keyup="submit" />
  <!-- 键盘按下 -->
  <input type="text" v-on:keydown="submit" />
  <input type="text" v-on:keypress="submit" />
</div>
var app=new Vue({
  el:"#app",
  methods:{
    submit(){
      console.log("你按下了:"+event.key);
    }
  }
});

keyCode

keyCode 的事件用法已经被废弃了并可能不会被最新的浏览器支持。

<!-- keyCode形式 -->
<input type="text" @keyup.13="submit" />
<!-- keyCode别名形式,详情看表 -->
<input type="text" @keyup.enter="submit" />

最好用别名,Vue已经处理好了keyCode兼容性问题

备注
enter
tab
delete退格删除,event具体值不同
esc
space
up / down / left / right上下左右
page-up / page-down
home / end
pause暂停
controlctrl键
*
+小键盘+shift+=,event具体值不同
-小键盘-大键盘-,event具体值不同

组合键

<!-- ctrl+a event返回的是a -->
<input type="text" @keyup.ctrl.a="submit" />
<!-- ctrl+alt+a event返回的是a -->
<input type="text" @keyup.ctrl.alt.a="submit" />
<!-- alt和shift -->
<input type="text" @keyup.alt.a="submit" />
<input type="text" @keyup.shift.a="submit" />
<!-- 注意其他键不能组合,会分别触发 a和b的keyup事件 -->
<input type="text" @keyup.a.b="submit" />

串联

<!-- 按下a/b/c都会触发,并返回对应event -->
<input type="text" @keyup.a.b.c="submit" />

自定义别名

//新建自定义别名
Vue.config.keyCodes.huiche=13;
//更改已存在别名keyCode
Vue.config.keyCodes.enter=65;

exact修饰符

<!-- 按下ctrl+shift+a也能触发 -->
<input type="text" @keyup.ctrl.a="submit" />
<!-- 仅按下ctrl+a时触发 -->
<input type="text" @keyup.ctrl.exact.a="submit" />
<!-- 注意:仅针对组合键 -->

鼠标按键修饰符

事件类型传送门

<!-- 左键单击 -->
<div @click.left="submit"></div>
<!-- 滚轮键单击 -->
<div @click.middle="submit"></div>
<!--  右键单击 -->
<div @click.right="submit"></div>

条件渲染

If,elseIf,else

v-ifv-else-ifv-else顺序不能乱,否则不能识别

<div id="app">
  <!-- if -->
  <div v-if="age<22">学生</div>
  
  <!-- if elseIf else -->
  <div v-if="age<10">儿童</div>
  <div v-else-if="age<45">青年</div>
  <div v-else>中年</div>
  
  <!-- if else -->
  <div v-if="age<25">无脱发</div>
  <div v-else>二级脱发</div>
</div>
age=20   ==>   学生 青年 无脱发

template元素条件渲染用法

注意:和原生标签不同,放在Vue App内的template会被去除<template>标签,内部当作普通html内容,所以默认是显示的,还要注意Vue不会编译模板内的style标签

<!-- isHidden是一个布尔值数据,若`v-if`为`真值`则显示,否则不显示(移除整个`template`) -->
<template v-if="isHidden">
  <div>
    <p>hello</p>
  </div>
</template>

复用特性

示例

<div id="app">
  <template v-if="ok">
    <label>name</label>
    <input placeholder="输入name">
  </template>
  <template v-else>
    <label>age</label>
    <input placeholder="输入age">
  </template>
  <input type="button" @click="qiehuan" value="切换" />
</div>
var app = new Vue({
  el:"#app",
  data:{
    ok:true
  },
  methods:{
    qiehuan:function(){
      this.ok=!this.ok;
    }
  }
});

按下按钮时两个template会互相切换,从渲染上看,两个模板内容高度相同,若完全重新渲染会造成不必要的浪费,所以Vue会尽可能复用这些元素,例如示例代码:

.labelinput
正常创建label,更改innerText创建input,创建属性,设置属性
Vue复用更改innerText设置属性

所以在切换时不会消除上次输入的内容,这是因为复用造成的

管理复用(key)

<!-- ... -->
<input placeholder="输入name" key="name">
<!-- ... -->
<input placeholder="输入age" key="age">
<!-- ... -->

只需要在不需要复用的地方指定不同的key值就可以避免复用,同时label还是复用的

v-show

v-show会切换元素的display,来达到显示/隐藏

<div id="app">
  <div v-show="isShow"></div>
</div>

v-show和v-if区别

  1. v-show在运行时就直接渲染元素,然后根据用户指示操作元素的display=none/block
  2. v-if在运行时若为假值则不会渲染元素,直到为真值时才进行渲染,且当再次切换真/假值时会适当的进行销毁和重建
  3. v-if在切换时开销比v-show大,因此,切换频繁的元素建议用v-show,不频繁或需要销毁/重建的元素用v-if

列表渲染

循环数组

var app = new Vue({
  el:"#app",
  data:{
    students:[
      {id:1,name:"sam",age:18},
      {id:2,name:"tom",age:21}
    ]
  }
});

item in items格式:

<div id="app">
  <ul>
    <li v-for="student in students">
      {{student.name+" - "+student.age}}
    </li>
  </ul>
</div>
<!-- 渲染结果为:
  <li>sam - 18</li>
  <li>tom - 21</li>
 -->

(item,index) in items格式:

<div id="app">
  <ul>
    <!-- 索引从0开始,参数名任意,顺序不可变 -->
    <li v-for="(student,index) in students">
      {{index+" - "+student.name+" - "+student.age}}
    </li>
  </ul>
</div>

可以用of替代in,因为它更接近js迭代器的语法:

<li v-for="student of students">

注意事项

列表渲染是响应式的,但由于js限制,无法检测到以下行为:

//列表不会检测到这些行为,因此不会改变
app.students[0]="{id:1,name:"jack",age:18}";  //已更改值但列表没有重新渲染
app.students.length=1;  //已经删掉多余值但列表没有重新渲染

问题1解决方案:

//Vue.set方法
Vue.set(app.students,0,{id:1,name:"jack",age:18});  //全局方法
app.$set(app.students,0,{id:1,name:"jack",age:18});  //实例方法,两个均可

//因为Vue对数组的变异方法进行了包裹,所以这些方法将会触发视图更新
//splice方法        (第0个开始,删掉1个,插入的新数据  )
app.students.splice(0,1,{id:1,name:"jack",age:18});

问题2解决方法:

//splice方法   (第0个开始,删掉1个)
app.students.splice(0,1);

变异方法和非变异方法
官方文档传送门

  1. Vue对数组的变异方法进行了包裹,所以这些方法将会触发视图更新
  2. 非变异方法返回的是一个新数组,将新数组替换旧数组,也能达到触发视图更新的目的
  3. 在新数组替换旧数组时仍采用了复用技术

过滤数组

计算属性
<div id="app">
  <ul>
    <li v-for="student in getBigStudent">
      {{student}}
    </li>
  </ul>
</div>
var app = new Vue({
  el:"#app",
  data:{
    students:[
      {name:"sam",age:18},
      {name:"tom",age:21},
      {name:"jack",age:19}
    ]
  },
  computed:{
    //获取年龄大于18的学生
    getBigStudent:function(){
      return this.students.filter(function(student){
        return student.age>18;
      })
    }
  }
});
方法

更改以下部分即可:

"student in getBigStudent" ⇒ "student in getBigStudent()"
computed ⇒ methods

循环对象

<div id="app">
  <ul>
    <!-- (student) (student,name) (student,name,index) 三种格式均可 -->
    <li v-for="(student,name,index) of students">
      {{index+":"+name+" - "+student.name+" - "+student.age}}
    </li>
  </ul>
</div>
<!-- 渲染结果为:
  <li>0:s1 - sam - 18</li>
  <li>1:s2 - tom - 20</li>
 -->
var app = new Vue({
  el:"#app",
  data:{
    students:{
      s1:{id:1,name:"sam",age:18},
      s2:{id:2,name:"tom",age:20}
    }
  }
});

vue在遍历对象时按Object.keys()结果进行遍历(但不能保证任何情况都一致),因此顺序不一定和data内书写顺序相同

注意事项

由于js限制,无法检测到以下行为:相关问题传送门

app.students.s3={id:3,name:"tom",age:25};

解决方案:

//Vue.set方法
Vue.set(app.students,"s3",{id:3,name:"tom",age:25}); //全局方法
app.$set(app.students,"s3",{id:3,name:"tom",age:25});  //实例方法,两个均可

当需要添加/设置多个属性时,你可能会这样:Object.assign传送门
复习:Object.assign(target,...source)

//直接把Source给app.students
Object.assign(app.students,{
  s1:{id:1,name:"sam",age:19},
  s3:{id:3,name:"jack",age:21}
});

请不要这样,应该这样:(不理解为什么要这样)

//把source和app.students给{}来创建一个新对象,然后再给app.students
app.students=Object.assign({},app.students,{
  s1:{id:1,name:"sam",age:19},
  s3:{id:3,name:"jack",age:21}
});

维护状态(复用问题)

示例:若在文本框输入值后点击更新值按钮,由于复用策略,文本框原本的值不会重新被渲染

<div id="app">
  <div v-for="item in items">
    {{item}}:<input type="text" />
  </div>
  <input type="button" value="chageValue" @click="chageValue" />
</div>
var app = new Vue({
  el:"#app",
  data:{
    items:{
      0:"name",
      1:"age"
    }
  },
  methods:{
    chageValue:function(){
      this.items[0]="userName";
    }
  }
});

使用key禁止复用策略:此时将会重新渲染整个for,注意key应该是字符型或数值型且是唯一的

<div v-for="item in items" :key="item">

整数循环

v-for可以接受一个整数:

<div id="app">
  <span v-for="i in 10"> {{i}} </span>
  <!-- ⇒ 1 2 3 4 5 6 7 8 9 10 -->
</div>

在template中使用

template中同样也可以正常使用

<div id="app">
  <template v-if="isShow" v-for="i in 10">
    {{i}}
  </template >
</div>

v-for和v-if同时使用

vue书写风格不推荐v-for和v-if同时使用
v-for的优先级比v-if高,意味着每一个节点都会运行v-if,这个特性适合只渲染部分节点,示例:

<div id="app">
  <ul>
    <li v-for="todo in 10" v-if="todo<5">
      {{todo}}  <!-- ⇒ 1 2 3 4 -->
    </li>
  </ul>
</div>

可以把v-if放到v-for上一级来提升v-if的优先级:

<div id="app">
  <ul v-if="false">  <!-- 直接在这里控制整个for是否渲染 -->
    <li v-for="todo in 10">
      {{todo}}
    </li>
  </ul>
</div>

表单输入绑定

基础用法和值绑定

v-model实现表单的双向绑定,它会根据元素类型自动判断需要绑定的事件和属性

text,textarea

texttextarea元素使用value属性和input事件

<div id="app">
   <input type="text" v-model="textMessage"/>  {{textMessage}}
   <!-- 等价于:
     <input type="text" :value="textMessage" @input="textMessage=$event.target.value"/>
   -->
   <textarea v-model="textareaMessage"></textarea>  {{textareaMessage}}
</div>
var app = new Vue({
  el:"#app",
  data:{
    textMessage:"",
    textareaMessage:""
  }
});

checkbox

checkboxradio使用checked属性和change事件
单选

<div id="app">
  <input type="checkbox" v-model="checkboxMessage"/>
  {{checkboxMessage}}
</div>
var app = new Vue({
  el:"#app",
  data:{
    checkboxMessage:false  //初始值会影响到html绑定的表单元素
    //单选也可以用数组,返回的是value数组
  }
});

多选

<div id="app">
  <input type="checkbox" name="g1" value="tom" v-model="checkboxMessage"/><label>tom</label>
  <input type="checkbox" name="g1" value="sam" v-model="checkboxMessage"/><label>sam</label>
  <input type="checkbox" name="g2" value="jack" v-model="checkboxMessage"/><label>jack</label>
  你的选择:{{checkboxMessage}}
</div>
var app = new Vue({
  el:"#app",
  data:{
    checkboxMessage:[]  //!必须是数组返回的才是选择项value数组,否则返回true/false,且会影响到其他项
  }
});

注意:上面3个元素的inputname虽不同,但在Vue app内无视name属性,作为同一组对待
值绑定
单选情况下,返回的只有true/false,但可以将一个值绑定到动态属性上

<div id="app">
  <input type="checkbox" v-model="isCheck" true-value="yes" false-value="no"/>
  {{isCheck}}
</div>
var app = new Vue({
  el:"#app",
  data:{
    isCheck:"yes"  //初始值同样会影响到html绑定的表单元素
  }
});

此时yes相当于checked=true,反之为false

radio

<div id="app">
  <input type="radio" value="yes" v-model="radioMessage"/><label>yes</label>
  <input type="radio" value="no" v-model="radioMessage"/><label>no</label>
  你的选择:{{radioMessage}}
  <!-- 返回选定元素的value值 -->
</div>
var app = new Vue({
  el:"#app",
  data:{
    radioMessage:""
  }
});

值绑定
还可以绑定变量实现动态值

<div id="app">
  <input type="radio" v-model="isCheck" :value="sport.a"/>football
  <input type="radio" v-model="isCheck" :value="sport.b"/>basketball
  {{isCheck}}
</div>
var app = new Vue({
  el:"#app",
  data:{
    isCheck:"basketball",
    sport:{
      a:"football",
      b:"basketball"
    }
  }
});

select

select字段将value作为 prop 并将change作为事件
单选

<div id="app">
  <select v-model="select">
    <option value="aaa">a</option>
    <option>b</option>
    <option>c</option>
  </select>
  {{select}}
  <!-- 返回选定元素的value值(没有则返回元素内文本) -->
</div>
var app = new Vue({
  el:"#app",
  data:{
    select:""
  }
});

多选(shift/ctrl)
此时返回的是选择项value数组

<!-- ... -->
<select multiple v-model="select">
<!-- ... -->
var app = new Vue({
  el:"#app",
  data:{
    select:[]  //可以是""或其他,和checkbox的多选不一样,不是必须数组
  }
});

值绑定

<div id="app">
  <select v-model="isCheck">
    <!-- 绑定的值类型任意,不一定必须是字符串,例如这里绑定对象类型 -->
    <option :value="sports">sports</option>  <!-- ==> {a:...,b:...} -->
    <option>food</option>
  </select>
  {{isCheck}}
</div>
var app = new Vue({
  el:"#app",
  data:{
    isCheck:"",
    sports:{
      a:"run",
      b:"football"
    }
  }
});

修饰符

.lazy(input->change)

可以将input事件触发转为change事件触发

<div id="app">
  <input type="text" v-model.lazy="message" />
  {{message}}  <!-- 只有change事件触发才会做出响应 -->
</div>

.number

对于type=numberinput元素,即使类型为number,但仍会返回string类型数据,使用.number来解决返回类型问题

<div id="app">
  <input type="number" v-model.number="message"/>
  {{typeof(message)+":"+message}}  <!-- 输入值后返回的将是 number:your number -->
</div>

.trim

去除首尾空格,在触发事件时文本框绑定数据将同步去除首尾空格

<div id="app">
  <input type="text" v-model.trim="message"/>
  {{message}}
</div>

组件注册

基本格式

  1. app声明必须在使用的组件下面
  2. data必须是一个函数,这样可以保证复用组件之间数据互不影响
<div id="app">
  <counter></counter>
  <counter></counter>
</div>
Vue.component("counter",{
  template:'<input type="button" :value="taps+n" @click="n++"/>',
  data:function(){
    return{
      taps:"点击了(次):",
      n:0
    }
  }
});
var app = new Vue({
  el:"#app"
});

组件名

//短横线分隔式
ji-shu-qi
//驼峰式
JiShuQi

在使用时两种方式定义的组件都必须以下形式,否则不识别:

<ji-shu-qi></ji-shu-qi>

Vue书写风格建议组件名全小写,且需有短横线分隔,这样可以和标准HTML节点区分开

全局/局部注册

全局注册
例如基本格式小节的声明方式就是全局注册,全局注册的组件可以在不声明的情况下用于任何vue app,且组件间能够互相嵌套

在vue app内局部注册

<div id="app">
  <ji-shu-qi></ji-shu-qi>
</div>
//局部注册用var声明
var jiShuQi={
  template:'<input type="button" :value="n" @click="n++"/>',
  data:function(){
    return{
      n:0
    }
  }
};
var app = new Vue({
  el:"#app",
  components:{  //在此注册要使用的组件,否则未注册的组件不能使用
    "ji-shu-qi":jiShuQi
    //标签名   : 变量名
  }
});

嵌套局部注册的组件

<div id="app">
  <taps-ji-shu-qi></taps-ji-shu-qi>
</div>
var tapsJiShuQi={
  template:'<div>你点击了(次):<ji-shu-qi></ji-shu-qi></div>',
  components:{  //注册局部组件  并使用它 ↑
    "ji-shu-qi":jiShuQi
  }
};
var app = new Vue({
  el:"#app",
  components:{  //被嵌套的组件不用再次注册
    "taps-ji-shu-qi":tapsJiShuQi
  }
});

父子组件通信(自定义事件)

基础示例($emit)

有时子组件需要触发父组件的某个事件,可以用$emit配合侦听器实现

Vue.component("son",{
  template:`
    <div class="border px-3 py-3 mx-3 my-3">
      <!-- 1、$emit方法可以触发父组件(input的父组件是son)中的指定事件 -->
      <!-- 这里指定的@click是真正触发的事件,后面的是自定义事件 -->
      <input type="button" value="changeFather's name" @click="$emit('son-click')"/>
    </div>
  `
});

Vue.component("father",{
  template:`
    <div>
      <!-- 2、触发父组件(father)的指定事件 -->
	  <son @son-click="$emit('father-click')"></son>
    </div>
  `
});

var app = new Vue({
  el:"#app",
  data:{
    fatherName:"jack"
  }
});
<div id="app">
  <!-- 3、最终此处监听到事件,修改data -->
  <father :name="fatherName" @father-click="fatherName='tom'"></father>
  {{ fatherName }}
</div>

注意事项:

  1. 组件不能指定原始事件,例如:<father @click="funcion"></father>,若指定了也会被当作自定义事件
  2. 所以自定义事件名称可以和原始事件重名
  3. 若要触发原始事件,可以用.native修饰符启用:
<!-- 此时单击father可以触发click事件,同时即使自定义事件名同名也互不影响 -->
<father :name="fatherName" @click.native="fatherName='sam'" @father-click="fatherName='tom'"></father>

官方文档说.native的原始事件自定义监听器冲突的话.native监听器将静默失败,实际测试并没有:传送门

  1. 监听器的名字不支持驼峰式/短横线式互转

带参通信($event)

Vue.component("son",{
  template:`
    <div class="border px-3 py-3 mx-3 my-3">
      <!-- $emit( , ) 带参 -->
      <input type="button" value="changeFather's name" @click="$emit('son-click','tom')"/>
    </div>
  `
});

Vue.component("father",{
  template:`
    <div>
      <!-- $event用来接收参数,这里把参数再次传给父组件 -->
	  <son @son-click="$emit('father-click',$event)"></son>
    </div>
  `
});

传给表达式:

<div id="app">
  <father :name="fatherName" @father-click="fatherName=$event"></father>
  {{ fatherName }}
</div>

传递给方法:

<div id="app">
  <father :name="fatherName" @father-click="changeName"></father>
  {{ fatherName }}
</div>
/* ... ... */
methods:{  //第一个参数就是传过来的值,名字任意
  say:function(name){
    this.fatherName=name;
  }
}
/* ... ... */

父子组件v-model通信

众所周知:v-model等价于… 传送门,所以,可以:

Vue.component("test",{
  template:`
    <!--    接收初始值    自定义事件,这里的$event不是通讯传参那个,是真的event -->
    <input :value="value" @input="$emit('input',$event.target.value)"/>
  `,
  props:["value"]
});

var app = new Vue({
  el:"#app",
  data:{
    message:"hello"
  }
});
<div id="app">
  <test :value="message" @input="message=$event"></test>{{message}}
</div>

<!-- Vue为父子组件通讯专门设置了: -->
<div id="app">
  <test v-model="message"></test>{{message}}
</div>
<!-- 所以可以简写,注意传过来的
  $event.target.value是input,例如checkbox是selected,不要忘了,不是都是value -->

.sync修饰符

在使用带参通信时推荐使用update:myPropName的格式,sync可以简化这种格式:

<div id="app">
  <!-- 和上面都一样,就是监听器名字改成 update:myPropName 的形式           -->
  <!-- <test :name="post.name" @update:name="post.name=$event"></test>{{post.name}} -->
  
  <!-- 可以简化为: -->
  <test :name.sync="post.name"></test>{{post.name}}
</div>
Vue.component("test",{
  template:`
    <div>
      <input type="button" value="change" @click="$emit('update:name','tom')"/>
      {{name}}
    </div>
  `,
  props:["name"]
});

var app=new Vue({
  el:"#app",
  data:{
    post:{
      name:"jack",
      age:20
    }
  }
});

多事件多值简写(.sync结合v-bind)

当需要传多个变量的不同事件时,可以结合v-bind使用:

<div id="app">
  <!-- 正常要写这么多: -->
  <!-- <test :name.sync="post.name" :age.sync="post.age"></test>{{post}} -->
  
  <!-- 简写后:  和props的$attrs功能差不多呀 -->
  <test v-bind.sync="post"></test>{{post}}
</div>
Vue.component("test",{
  template:`
    <div>
      <input type="button" value="change" @click="$emit('update:name','tom')" @mouseover="$emit('update:age',18)"/>
      {{name}}{{age}}
    </div>
  `,
  props:["name","age"]
});

实例事件($on,once,off)

有时候需要在Vue应用外写一个Vue应用的方法和触发它的事件,可以用实例事件

<div id="app">
  <p>count={{count}}</p>
  <add-button @addcount="count++"></add-button>
  <!-- Vue应用内触发 -->
</div>

<!-- 实例事件-1:应用外的一个按钮 -->
<input type="button" value="reduce" onclick="reduce()"/>
Vue.component("add-button",{
  template:`
    <input type="button" value="add" @click="$emit('addcount')"/>
  `
})

var app=new Vue({
  el:"#app",
  data:{
    count:0
  },
  methods:{
    add:function(){
      this.count++;
    }
  }
})

// 实例事件-2:点击后通过$emit触发指定事件 
function reduce(){
  app.$emit("reduceAppCount");
}

//实例事件-3:通过$on指定事件内容
//事件发生一次触发一次
app.$on(/"reduceAppCount",function(){
  this.count--;
})

/*仅能触发一次
app.$once("reduceAppCount",function(){
  this.count--;
})*/

/*关闭事件,关闭后不会再触发
app.$on("reduceAppCount",function(){
  this.count--;  //触发一次
  this.$off("reduceAppCount");  //关闭
})*/

父子组件通信-2

子访问根($root) And 子访问父($parent)

<div id="app">
  <father></father>
</div>
Vue.component("father",{
  //只要是子组件都可以访问到$root,不限于第一代子组件
  template:'<son></son>',
  data:function(){
    return{
      message:"fatherData",
    }
  },
  computed:{
    newMessage:function(){
      return this.message+'_computed'
    }
  },
  methods:{
    say:function(){
      console.log("fatherMethod")
    }
  }
})

Vue.component("son",{
  template:'<p @click="appSay">{{appMessage}},{{appNewMessage}},{{fatherMessage}},{{fatherNewMessage}}</p>',
  data:function(){
    return{
      appMessage:this.$root.message,  //访问根元素的data
      appNewMessage:this.$root.newMessage,  //访问根元素的计算属性
      
      fatherMessage:this.$parent.message,  //访问父元素的data
      fatherNewMessage:this.$parent.newMessage  //访问父元素的计算属性
    }
  },
  methods:{
    appSay:function(){
      this.$root.say();  //访问根元素的方法
      this.$parent.say();  //访问父元素的方法
    }
  }
})

var app=new Vue({
  el:"#app",
  data:{
    message:"rootData"
  },
  methods:{
    say:function(){
      console.log("rootMethod")
    }
  },
  computed:{
    newMessage:function(){
      return this.message+'_computed'
    }
  }
});

父访问子($refs)

<div id="app">
  <base-form></base-form>
</div>
Vue.component("base-form",{
  template:`
    <!-- 单击时更改子组件value值,鼠标移上去时触发子组件方法 -->
    <div class="base-form" @click="changeValue" @mouseover="changeFocus">
      <!--             把这个子组件存进refs里并命名    -->
      name:<base-input ref="usernameInput"></base-input>
    </div>
  `,
  methods:{
    changeValue:function(){
      //读取$refs中的usernameInput
      //可以访问子组件的数据
      this.$refs.usernameInput.value="tom";
    },
    changeFocus:function(){
      //可以访问子组件的方法
      //总之就和子组件内部一样,随便操作
      this.$refs.usernameInput.getFocus();
    }
  }
})

Vue.component("base-input",{
  template:'<input type="text" ref="input" :value="value"/>',
  data:function(){
    return{
      value:"sam",
    }
  },
  methods:{
    getFocus:function(){
      this.$refs.input.focus();
    }
  }
})

后代访问前代(依赖注入)

使用实例对象provideinject实现依赖注入

<div id="app">
  <father></father>
</div>
Vue.component("father",{
  template:'<son></son>',
})

Vue.component("son",{
  //后代对象可以访问到前代提供的方法和数据
  template:'<p @click="say">root name is {{name}}</p>',
  //在这里指定需要接收的数据/方法
  inject:["name","say"]
})

var app=new Vue({
  el:"#app",
  data:{
    name:"app"
  },
  methods:{
    say:function(){
      console.log("my name is "+this.name)
    }
  },
  //包含一个后代组件都可以访问到的数据/方法对象
  provide:function(){
    return{
      name:this.name,
      say:this.say
    }
  }
});

Prop

基本格式

<div id="app">
  <father :name="name"></father>
</div>
Vue.component("grandson",{
  template:'<p>{{name}}</p>',
  props:["name"]
});

Vue.component("son",{
  template:'<p><grandson :name="name"></grandson></p>',
  props:["name"]
});

Vue.component("father",{
  template:'<p><son :name="name"></son></p>',
  props:["name"]
});

var app = new Vue({
  el:"#app",
  data:{
    name:"jack"
  }
});

$attrs修饰符

$attrs相当于自动把所有props传给下一个,免去手动传递了,替换sonfather内容如下:

Vue.component("son",{
  template:'<p><grandson v-bind="$attrs"></grandson></p>'
});

Vue.component("father",{
  template:'<p><son v-bind="$attrs"></son></p>'
});

prop单向特性

Vue中几乎所有都是双向绑定,但要注意prop

父子的prop都是单向下行绑定,父级prop的更新会流动到子组件中,反之则不行,这样可以防止子组件意外改变父组件

示例

<div id="app">
  <son :name="fatherName"></son>
  my name is {{ fatherName }}
  <!-- 父->子特性:点击按钮修改data,prop参数会向流向子元件,并更新子元件对应的prop参数 -->
  <input type="button" value="change my name" @click="fatherName='sam'"/>
</div>
Vue.component("son",{
template:`
	<div>
	  father's name is {{name}}
	  <!-- 子->×父特性:子元件更新prop参数,只会更新本身,不会流向父组件 -->
	  <!-- 可以这样直接修改prop来改变自身,但Vue会警告,最好不要这样做 -->
	  <input type="button" value="change father's name" @click="name='tom'"/>
    </div>
  `,
  props:["name"]
});

var app = new Vue({
  el:"#app",
  data:{
    fatherName:"jack"
  }
});

prop传递初始值

直接修改prop的做法在Vue里是不被允许的

若要用于初始值(只传过来一次,后续的修改互不影响)的传递,可以存入data

Vue.component("son",{
  template:`
    <div>
	  <!-- 父组件修改prop参数仍会流向子组件,但不会影响到data里的fatherName -->
	  father's history name is {{name}}
	  
	  <!-- 修改自身data不会影响prop参数 -->
	  father's realTime name is {{fatherName}}
      <input type="button" @click="fatherName='sam'"/>
    </div>
  `,
  props:["name"],
  data:function(){
    return{  //将prop参数存入data里,使用时当作自身数据使用
      fatherName:this.name
    }
  }
});

prop传递原始值

若要用于传递原始值且需要进行转换(父组件修改prop仍可影响到子组件),可以用计算属性
根据计算属性双向绑定的特性,父组件修改了prop,会使子组件重新计算,所以子组件的加工后的值也会随之变化

Vue.component("son",{
  template:`
    <div>
	  father's name is {{fatherName}}
	</div>
  `,
  props:["name"],
  computed:{  //prop参数name发生改变时这里会重新计算
    fatherName:function(){  //处理原始值
      return this.name.trim().toUpperCase();
	}
  }
});

prop类型验证

<div id="app">
  <blue-p
    :number-content="post.numberContent"
    :string-content="post.stringContent"
	 ...省略...
    :diy-content="post.diyContent"
  ></blue-p>
 </div>
Vue.component("blueP",{
  template:`
    <p>
      <strong>基础类型:</strong><br/>
        {{ typeof(numberContent)+":"+numberContent }}<br/>
        {{ typeof(stringContent)+":"+stringContent }}<br/>
	    {{ typeof(booleanContent)+":"+booleanContent }}<br/>
        {{ typeof(arrayContent)+":"+arrayContent }}<br/>
        {{ typeof(objectContent)+":"+objectContent }}<br/>
	  <strong>多个可能的类型:</strong><br/>
	    {{ typeof(multipleTypeContent1)+":"+multipleTypeContent1 }}<br/>
		{{ typeof(multipleTypeContent2)+":"+multipleTypeContent2 }}<br/>
	  <strong>修饰参数:</strong><br/>
	    <em>必填</em><br/>
	      {{ typeof(mustContent)+":"+mustContent }}<br/>
		<em>默认值</em><br/>
		  {{ typeof(defaultContent1)+":"+defaultContent1 }}<br/>
		  {{ typeof(defaultContent2)+":"+defaultContent2 }}<br/>
		  {{ typeof(defaultContentObject)+":"+defaultContentObject }}<br/>
	  <strong>自定义验证</strong><br/>
	      {{ typeof(diyContent)+":"+diyContent }}<br/>
	  <strong>自定义type</strong><br/>
	      {{ typeof(diyTypeContent)+":"+diyTypeContent }}<br/>
    </p>
  `,
  props:{
    //基础类型
    "number-content":Number,
    "string-content":String,
    "boolean-content":Boolean,
    "array-content":Array,
    "object-content":Object,
	//多个可能的类型
	"multiple-type-content-1":[String,Boolean],
	"multiple-type-content-2":[String,Boolean],
	//修饰参数-必填
	"must-content":{
  	  type:String,
  	  required:true
	},
    //修饰参数-默认值
	"default-content-1":{
	  type:Number,
	  default:15
	},
	"default-content-2":{
	  type:Number,
	  default:15
	},
	"default-content-object":{
	  type:Object,
	  default:function(){  //必须是工厂函数模式
	    return{
	      name:"sam",
		  age:20
		}
	  }
    },
    //自定义验证
    "diy-content":{
      validator:function(value){
	    //为number类型且大于18
	    return typeof(value)=="number" && value>18;
      }
    },
    //自定义type
	"diy-type-content":Student
  }
});
var app = new Vue({
  el:"#app",
  data:{
    post:{
	  //基础类型
	  numberContent:12,
	  stringContent:"hello",
      booleanContent:true,
      arrayContent:[1,2,3],
      objectContent:{name:"sam",age:20},
	  //多个可能的类型
	  multipleTypeContent1:true,
	  multipleTypeContent2:"hello",
	  //修饰参数-必填
	  mustContent:"hello",
	  //修饰参数-默认值
	  defaultContent1:20,
	  //自定义验证
	  diyContent:19
	  //自定义Type
	  diyTypeContent:new Student("sam",15)
	}
  }
});

一次传入所有属性

对象属性可以一次性传入,html代码可改为:

<div id="app">
  <blue-p v-bind="post"></blue-p>
</div>

需要注意post里的参数名称必须和props里的参数名相同(驼峰式/短横线式任意)

非prop特性

示例

正常情况下prop的传递情况:

<div id="app">
  <test message="world"></test>
</div>
Vue.component("test",{
  //$props['message'] 等同于 message ,注意props属性不能用 $attrs['message']
  template:"<p>hello {{ $props['message'] }}</p>",
  props:["message"]
})
结果 ⇒ <p>hello message</p>

Vue还具有非prop特性:

<div id="app">
  <test message="world"></test>
</div>
Vue.component("test",{
  //非prop属性必须用 $attrs['message']
  template:"<p>hello {{ $attrs['message'] }} </p>"
})
结果 ⇒ <p message="world">hello world </p>

该组件的根元素被vue继承了属性,正常的prop则不会继承属性

替换/合并特性

<div id="app">
  <test message="world" style="background:#eee" class="border"></test>
</div>
Vue.component("test",{
  template:"<p message='bbb' style='font-size:20px'>hello</p>"
})
结果 ⇒ <p message="world" style="font-size:20px;background:#eee" class="border">hello</p>

一般属性被替换style,class属性被智能合并

阻止继承

给组件添加inheritAttrs:false可以阻止继承,但不会影响styleclass

<div id="app">
  <test message="world" message2="aaa" style="background:#eee" class="border"></test>
</div>
Vue.component("test",{
  template:"<p message='bbb' style='font-size:20px'>hello {{ $attrs['message'] }}</p>",
  inheritAttrs:false
})
结果 ⇒ <p message="bbb" style="font-size:20px;background:#eee" class="border">hello world</p>
            未被替换           style和class不受影响
配合$attrs过滤props
<div id="app">
  <father :name="name" :age="20"></father>
</div>
Vue.component("son",{
  //age会传不过来,所以只有name可用
  template:'<p>{{name}} {{age}}</p>',
  props:["name","age"]
});

Vue.component("father",{
  //传递所有参数到子组件
  template:'<p><son v-bind="$attrs"></son></p>',.,
  //禁止继承
  inheritAttrs:false,
  //禁止继承的属性
  props:["age"]
});

var app = new Vue({
  el:"#app",
  data:{
    name:"jack"
  }
});
结果 ⇒ jack   //age被过滤了

插槽

基础例子

<test>
  <p>html标签内容</p>普通内容
</test>
template:`
  <div>
    <slot></slot>
  </div>
`

渲染结果为:插槽内容替换了<slot>标签,且支持html标签组件

<div>
  <p>html标签内容</p>普通内容
</div>

默认值

<div id="app">
  <test></test>
  <test>456</test>
</div>
Vue.component("test",{
  template:`
    <div>
      <slot>123</slot>
    </div>
  `
})

渲染结果:

<!-- 采用默认值 -->
<div>
  123
</div>

<!-- 替换掉默认值 -->
<div>
  456
</div>

具名插槽

<div id="app">
  <test>
    <template v-slot:header>this is header.</template>
    
    <!-- defalut指向组件中省略name属性的slot -->
    <!-- <template v-slot:default>this is header.</template> -->
    
    <!-- 简写格式:可以不写template标签 -->
    this is header.
    
    <template v-slot:bottom>this is bottom.</template>
  </test>
</div>
Vue.component("test",{
  template:`
    <div>
      <slot name="header"></slot>
      
      <!-- 有一个可以不写template标签就可用的slot,这个slot的名为default -->
      <!-- <slot name="default"></slot> -->
      
      <!-- 简写格式:可以省略name -->
      <slot></slot>
      
      <slot name="bottom"></slot>
    </div>
  `
})

v-bind缩写是:v-on缩写是@一样,插槽名也支持缩写,是#

作用域

<div id="app">
  <!-- 此处的message(fatherMessage)是父组件(相对子组件)作用域 -->
  <test>{{message}}</test>
  <test></test>
</div>
Vue.component("test",{
  template:`
    <div>
      <!-- 此处的message(childMessage)是子组件(相对父组件)作用域 -->
      <slot>{{message}}</slot>
    </div>
  `,
  data:function(){
    return{
      message:"childMessage"
    }
  }
})

var app=new Vue({
  el:"#app",
  data:{
    message:"fatherMessage"
  }
})

父访问子

仅default插槽
<div id="app">
  <!-- 获取子组件的default插槽的参数对象 -->
  <!-- <test v-slot:default="messages">{{messages}}</test> -->
  <!-- 是default的话可以省略指定名字 -->
  <test v-slot="messages">{{messages}}</test>
</div>
Vue.component("test",{
  template:`
    <div>
      <!-- 把message和message2打包为对象给父组件 -->
      <slot :message="message" :message2="message2"></slot>
    </div>
  `,
  data:function(){
    return{
      message:"childMessage",
      message2:"chilaMessage2"
    }
  }
})

渲染结果:

{ "message": "childMessage", "message2": "chilaMessage2" }
多插槽访问语法格式

上面的例子只有一个default插槽,若有多个插槽,需要注意格式:

<div id="app">
  <test>
    <!-- 多插槽下禁止将v-slot写在根标签,但仍支持简写default -->
    <template v-slot="mainMessages">{{mainMessages}}</template>
    <template v-slot:header="messages">{{messages}}</template>
  </test>
</div>
Vue.component("test",{
  template:`
    <div>
      <slot :mainMessage="mainMessage"></slot>
      <slot name="header" :message="message" :message2="message2"></slot>
    </div>
  `,
  data:function(){
    return{
      message:"childMessage",
      message2:"chilaMessage2",
      mainMessage:"childmainMessage"
    }
  }
})
解构赋值结合插槽内容

es6解构赋值传送门

<div id="app">
  <test>
    <!-- 这里的slot将被赋予一个对象:                                      -->
    <!-- { "message": "sam", "message2": 19 }  -->
    <!-- 浏览器如果支持es6的结构赋值,可以:                            -->
    <template v-slot="{message='aaa',message2:age,message3='汉族'}">{{message+age+message3}}</template>
    <!-- 注意是这个v-slot=""被解构赋值 -->
    <!-- {message,message2:age,message3='汉族'}={message:"childMessage",message2:19} -->
  </test>
</div>
Vue.component("test",{
  template:`
    <div>
      <slot :message="message" :message2="message2"></slot>
    </div>
  `,
  data:function(){
    return{
      message:"sam",
      message2:19
    }
  }
});

var app=new Vue({
  el:"#app"
});

支持动态插槽名

动态参数传送门,插槽名也支持动态参数:

<div id="app">
  <test>
    <template v-slot:[slotname]>setter text.</template>
  </test>
</div>
Vue.component("test",{
  template:`
    <div>
      <slot name="header">header default text.</slot>
      <slot>main default text.</slot>
    </div>
  `
});

var app=new Vue({
  el:"#app",
  data:{
    slotname:"default"
  }
});

缓存和异步组件

缓存组件(keep-alive)

在多标签页面中可能需要缓存页面的状态,这时候需要<keep-alive>标签来缓存不活动的组件

<div id="app">
  <input type="button" v-for="tab in tabs" :value="tab" @click="cureentTabs=tab"/>
  <!-- 尝试切换几个标签,component内的内容会被缓存,不会重新渲染 -->
  <keep-alive>  <!-- 在需要缓存的地方包裹上keep-alive标签 -->
    <component :is="currentTabsComputed"></component>
  </keep-alive>
</div>
Vue.component("tab-header",{
  template:'<div>header component.<input /></div>'
});
Vue.component("tab-main",{
  template:'<div>main component.<input /></div>'
});
Vue.component("tab-bottom",{
  template:'<div>bottom component.<input /></div>'
});

var app=new Vue({
  el:"#app",
  data:{
    tabs:["header","main","bottom"],
    cureentTabs:"header"
  },
  computed:{
    currentTabsComputed:function(){
      return 'tab-'+this.cureentTabs
    }
  }
});

异步组件(需要学习webpack)

<div id="app">
  <!-- 在页面加载完毕的2s后异步渲染 -->
  <test></test>
</div>
//resolve表示成功,reject表示失败,变量名任意
Vue.component("test",function(resolve,reject){
  setTimeout(function(){  //等5秒
    resolve({  //成功就返回这个
      template:'<p>aaa</p>'
    })
  },2000);
})

组件模板其他格式

内联模板

子组件加上inline-template属性,组件的template可以内联到DOM中:

<div id="app">
  <test inline-template>  <!-- 加这个属性 -->
    <!-- begin 和app里的一样,就是位置变了 -->
    <div class="test">
      <span>username:{{username}}</span>
      <span>content:{{content}}</span>
    </div>
    <!-- end -->
  </test>
</div>
Vue.component("test",{
  data:function(){
    return{
      username:"sam",
      content:"hello"
    }
  }
})

X模板

可以把模板外置到script标签内,只需要给script标签指定类型和id,并将id传给模板属性

<div id="app">
  <test></test>
</div>
<script type="text/x-template" id="x-template">
  <div class="test">
    <span>username:{{username}}</span>
    <span>content:{{content}}</span>
  </div>
</script>
Vue.component("test",{
  template:"#x-template",
  data:function(){
    return{
      username:"sam",
      content:"hello"
    }
  }
})

低开销静态组件(v-once)

有时候一个组件里都是静态内容,如果每次都渲染会浪费cpu,可以用v-once设置首次渲染后缓存起来,但注意这个组件只能是静态的了。最好不要用,除非渲染真的慢

Vue.component("test",{
  template:`
    <div class="test" v-once>
    <!-- 加上v-once,此时若单击span,不会触发任何事件,这个组件已经是静态的了 -->
      <span @click="username='aaa'">username:{{username}}</span>
    </div>
  `,
  data:function(){
    return{
      username:"sam"
    }
  }
})

继承(混入)

继承方式

有一个phonecomputer对象(Vue组件):

var phone={
  data:function(){
    return{
      name:"phone1"
    }
  },
  created:function(){
    console.log("aaa");
  },
  methods:{
    call:function(){
      console.log(this.name+" calling...");
    }
  }
};

var computer={
  data:function(){
    return{
      name:"computer1"
    }
  },
  created:function(){
    console.log("bbb");
  },
  methods:{
    comput:function(){
      console.log(this.name+" computing...");
    }
  }
};

Vue.extend

//使用extend方法继承Vue组件
var intePhone=Vue.extend({
	mixins:[phone,computer],  //继承的组件名称
	//格式和Vue组件格式都一样
	created:function(){
		console.log("ccc");
	},
	data:function(){
		return{
			name:"intePhone"
		}
	}
});

var intePhone=new intePhone();
intePhone.call();
intePhone.comput();

Vue应用内继承

<div id="intePhoneApp"></div>
var intePhoneApp=new Vue({
	mixins:[phone,computer],
	el:"#intePhoneApp",
	created:function(){
		console.log("ccc");
	},
	data:{
		name:"intePhoneApp"
	}
});

intePhoneApp.comput();

全局继承

表示所有Vue应用,组件…都将继承指定组件

Vue.mixin({
	mixins:[phone,computer],
	created:function(){
		console.log("ccc");
	},
	data:{
		name:"intePhoneApp"
	}
});

Vue应用:

<div id="app"></div>
var app=new Vue({
	el:"#app"
});

app.comput();

Vue组件:

<div id="app">
	<test></test>
</div>
Vue.component("test",{
  template:"<p @click='computByFather'>comput</p>",
  methods:{
    computByFather:function(){
      this.comput();  //继承了父组件
    }
  }
});

var app=new Vue({
	el:"#app"
});

覆盖规则

同名data和同名钩子函数

  1. 同名data:在mixins数组内,后者覆盖前者,然后继承者覆盖数组内最后者;例如上面的例子中,computer覆盖phone,最终应用覆盖computer
  2. 同名钩子函数:参与继承/被继承所有组件的钩子函数将会组合为一个数组,依次执行(mixins数组内前后依次执行, 然后执行继承者),例如上面的例子中,created函数的执行结果为:aaa bbb ccc
  3. 同名钩子函数:例如methods之类的钩子函数,同名子函数采用同data一样的覆盖规则。

自定义选项和覆盖规则

//1、全局自定义选项如何处理
Vue.mixin({
	created:function(){
		//示例:自定义选项info的处理语句
		//创建时输出info选项值
		var info=this.$options.info;
		if(info){
			console.log(info);
		}
	}
});

//2、自定义选项覆盖规则
var animate={
	info:{name:"animate",age:1}
};

var dog=Vue.extend({
	info:{name:"dog"},
	mixins:[animate]
});

var d1=new dog();
//结果:{name:"dog"}
//无论是什么类型,都进行覆盖

//3、自定义"自定义选项"覆盖规则
var animate2={
	info:{name:"animate",age:1}
};

var dog2=Vue.extend({
	info:{name:"dog"},
	mixins:[animate]
});

//Vue.config.optionMergeStrategies是一个存放覆盖规则的对象
Vue.config.optionMergeStrategies.info=function(toVal,fromVal){
	//相当于默认的覆盖规则,还可以写其他自定义的规则
	//fromVal:被覆盖者 toVal:覆盖者
	if(!fromVal){return toVal}
	if(!toVal){return fromVal}
}

var d2=new dog2();
//结果:{name:"dog"}
//和默认一样的规则

自定义指令

注册自定义指令

全局注册

<div id="app">
  <!-- 打开页面获得焦点 -->
  <input v-focus/>
</div>
Vue.directive("focus",{  //全局有效
  inserted:function(el){  //钩子函数:插入时触发
    el.focus();  //使用对象获得焦点
  }
});

var app=new Vue({
  el:"#app"
});

局部注册

var app=new Vue({
  el:"#app",
  directives:{  //仅在当前app内有效
    focus:{
      inserted:function(el){
        el.focus();
      }
    }
  }
});

钩子函数

钩子函数

bind和inserted
  1. bind执行是在dom加载之,所以函数中读取不到父元素,适合做初始化工作
  2. inserted执行是在dom加载完,所以函数中可以读取到父元素,适合正式开始写处理语句
<div id="app">
  <input type="input" v-focus/>
</div>
var app=new Vue({
  el:"#app",
  directives:{
    focus:{
      bind:function(el){  //指令首次绑定到元素时调用
        console.log("bind的父节点:"+el.parentNode); //null
      },
      inserted:function(el){  //被绑定元素插入父节点时调用
        el.focus();  //比如这句放在这里比较合适,不能放在bind里,放这里表示加载完后该元素获得焦点
        console.log("inserted的父节点:"+el.parentNode);  //可读取到
      }
    }
  }
});
update和componentUpdated
  1. update执行是在所在组件的VNode更新时调用(也可能是在其后),指令的值可能发生了变化,也可能没有
  2. componentUpdated执行是在所在组件的VNode及其子VNode更新后调用,指令的值都已被更新
<div id="app">
  <p v-test :name="name" @click="changeName"/>{{name}}</p>
</div>
var app=new Vue({
  el:"#app",
  directives:{
    test:{
      update:function(el){
        console.log("update");
        console.log("innerHTML:"+el.innerHTML);  //==>sam 有些值是更新时的
        console.log("name:"+el.getAttribute("name"));  //==>tom 有些值是更新后的
      },
      componentUpdated:function(el){
        console.log("componentUpdated");
        console.log("innerHTML:"+el.innerHTML);  //==>tom 都是更新后的
        console.log("name:"+el.getAttribute("name"));  //==>tom
      }
    }
  },
  data:{
    name:"sam"
  },
  methods:{
    changeName:function(){
      this.name="tom";
    }
  }
})
unload
<div id="app">
  <p v-test v-if="ok" @click="unload">unload</p>
</div>
var app=new Vue({
  el:"#app",
  directives:{
    test:{
      unbind:function(el){
        console.log("destory");
      }
    }
  },
  data:{
    ok:true
  },
  methods:{
    unload:function(){
      this.ok=false
    }
  }
})
钩子函数简写(update+bind)
<div id="app">
  <p v-test @click="unload">{{name}}</p>
</div>
var app=new Vue({
  el:"#app",
  directives:{
    test:function(){
      console.log("update/bind");
    }
  },
  data:{
    name:"tom"
  },
  methods:{
    unload:function(){
      this.name="sam"
    }
  }
})

/*全局写法
Vue.directive("test",function(){
  console.log("update/bind");
});*/

钩子函数参数

<div id="app">
  <input value="button" v-button:style.preset="1+0"/>
</div>
var app=new Vue({
  el:"#app",
  directives:{
    button:{
       //注意:可修改el,但不要修改binding
       //名字任意,顺序不任意
      inserted:function(el,binding,vnode,oldVnode){
        console.log("指令所绑定的DOM元素:"+el);  //==><input .../>
        console.log("指令名(不带V-):"+binding.name);  //==>button
        console.log("指令值:"+binding.value);  //==>1
        //console.log("旧指令值(仅用于两个更新钩子):"+binding.oldValue);
        console.log("字符串形式的表达式:"+binding.expression);  //==>"1+0"
        console.log("传给指令的参数(String):"+binding.arg);  //==>style,若是多个则用:分隔,如a:b:c
        console.log("传给指令的修饰符(Object):"+binding.modifiers);  //==>{preset:true}
        console.log("Vue编译生成的虚拟节点:"+vnode);  //==>VNode{tag:"input",data:{...},children:undefined...}
        //console.log("旧虚拟节点(仅用于两个更新钩子):"+oldVnode);

        //示例:
        el.setAttribute("type","button");
        //         有style参数               且       有preset修饰符      且     值等于1
        if(binding.arg.indexOf("style")!=-1 && binding.modifiers.preset && binding.value==1){
          el.style="border:2px solid #0af;background:#0bf;color:#fff";
        }
      }
    }
  }
});
动态指令参数值
<div id="app">
  <input value="button" v-red:[arg]/>
</div>
var app=new Vue({
  el:"#app",
  directives:{
    red:{
      bind:function(el,binding){
        var arg=binding.arg;
        el.style[arg]="red";
      }
    }
  },
  data:{
    //动态指定参数,注意这里的值仅在渲染时用一下,不会绑定数据,后期操作互不影响
    arg:"border-color"
  }
});

渲染函数

单元素和多元素格式

<div id="app">
  <anchored-heading :level="2">aa</anchored-heading>  <!-- <h2>aa</h2> -->
  <many-element></many-element>  <!-- /<div> <a>aaa</a> <a>bbb</a> </div> -->
</div>
//渲染函数和普通组件结构都一样,就是template换成了render,由静态渲染换成了js动态渲染
Vue.component('anchored-heading', {  //单元素格式
  render:function(ce){
    //语法:createElement(elementName,innerHTML)
    return ce("h"+this.level,this.$slots.default)
  },
  props:["level"]
})

Vue.component('many-element', {  //多元素格式
  render:function(ce){
    return ce("div",[  //根元素
      ce("a","aaa"),  //aaa,bbb两个子元素
      ce("a","bbb")
    ]);
  }
})

createElement参数

上面的例子的语法为:

createElement(elementName,innerHTML)

innerHTML参数可以还可以是一个对象,对象参数解释如下:

<div id="app">
  <rendering></rendering>
</div>
Vue.component("test",{
  template:`<p :title="prop1" @click="$emit('aaa')"><slot text="hello"></slot></p>`,
  props:["prop1"]
});

Vue.component('rendering', {
  render:function(createElement){
    var t=this;  //注意this.$refs等需要在这里读
    return createElement("test",{  //创建一个组件类型的元素
      props:{  //prop
        prop1:"test"
      },
      attrs:{  //属性
        attr1:"attr1"
      },
      /*domProps:{  //DOM属性
        innerHTML:"test"  //和上面例子的第二个参数作用一样
      },*/
      class:{  //类和样式智能识别
        a:false,
        b:true
      },
      style:{
        fontSize:"20px",
        color:"#08f"
      },
      nativeOn:{  //原生事件
        click:function(){
          console.log("原生事件click");
        }
      },
      on:{  //处理组件类型标签的自定义事件或给普通类型的标签添加原生事件
        aaa:function(){
          console.log("自定义事件aaa");
        }
      },
      directives:[  //要在该组件上添加的自定义指令
        {
          name:"size",  //binding.name
          value:"30",  //binding.value
          arg:"px"  //binding.arg
          //binding.expression,binding.modifiers
          //binding的属性都可以在这里直接用
        }
      ],
      scopedSlots:{  //填充子组件的插槽
        /*default:function(){  //直接填
          return `
            <a>link</a>
          `
        }*/
        /*default:function(props){  //还可以读取子组件插槽属性
          return props.text
        }*/
        default:props => props.text  //js的=>函数,等同于上面那个
      },
      //key:"唯一标识,不可重复",
      //ref:"类似html表单的name,例如有两个ref等于xb,则$refs.xb是一个ref=xb的所有元素的数组"
    })
  },
  directives:{
    size:{  //上面用到的自定义指令
      inserted:function(el,binding,vnode,oldVnode){
        el.style.fontSize=binding.value+binding.arg;
      }
    }
  }
})

上面例子是用一个组件作为标签名,普通html标签除了on,scopedSlots等组件专有属性外,其他参数都可使用
createElement创建的元素简称VNode,是一个虚拟DOM,记录节点的描述信息,用来追踪如何改变真实DOM

VNode唯一性

<div id="app">
  <test>title1</test>
</div>

这样违反了VNode唯一性(可正常渲染,但不符合规范):

Vue.component('test', {
  render:function(createElement){
    var pp=createElement("p","hi");
    return createElement("div",[
      pp,pp
    ])
  }
})

可以采用工厂函数实现:

Vue.component('test', {
  render:function(createElement){
    return createElement("div",[
      Array.apply(null,{length:20}).map(function(){
        return createElement("p","hi")
      })
    ])
  }
})

涉及知识:

Array.map(function(item,index,array){})
/*遍历数组*/

Array.apply(null,{length:20})
/*声明20个undefined变量
0:undefined ... 19:undefined*/

Array.apply(20)
/*仅声明一下占用大小,所以用这个无法遍历
length:20*/

渲染函数实现template功能

v-if和v-for

Vue.component('test', {
  render:function(createElement){
    if(this.students.length){
      return createElement("ul",this.students.map(function(item,index,array){
        return createElement("li",item.name+" "+item.age)
      }))
    }else{
      return createElement("p","no students")
    }
    /*实现了template中的:
    <ul v-if="students.length">
      <li v-for="item in items">{{item.name+" "+item.age}}</li>
    </ul>
    <p v-else>no students</p>
    */
  },
  data:function(){
    return{
      students:[
        {name:"sam",age:20},
        {name:"tom",age:21},
        {name:"jack",age:21}
      ]
    }
  }
})

v-model

<div id="app">
  <test @in="testvalue=$event"></test>{{testvalue}}
</div>
Vue.component('test', {
  render:function(createElement){
    var t=this;
    return createElement("input",{
      on:{
        input:function(e){
          t.$emit("in",e.target.value)
        }
      }
    },t.value)  //参数3等同domProps的innerHTML
  }
})

var app=new Vue({
  el:"#app",
  data:{
    testvalue:""
  }
})

事件修饰符

addEventListener参数3
原生js传送门
Vue事件处理修饰符传送门

修饰符前缀
.capture!
.passive&
.once~
.capture.once.once.capture~!(不可调换符号位置!~)

格式:

on:{
  "~!click":function(){}  //事件类型因为有特殊字符,所以作为字符串传
}

其他事件都没有修饰符,只能用原生js实现

插槽

Vue组件插槽传送门

<div id="app">
  <test>
    <template v-slot:header>this is header.</template>
    main
  </test>
</div>

单个元素直接用this

Vue.component('test', {
  render: function (createElement){
    return createElement('div',this.$slots.default)  //==>main
  }
})

多个元素用需在外面引用this

Vue.component('test', {
  render: function (createElement){
    var self=this;
    return createElement('div',[
      createElement('p',self.$slots.header),
      createElement('p',self.$slots.default)
    ])
  }
})

如果不是在createElement方法里直接写需要写为以下形式:

Vue.component('test', {
  render: function (createElement){
    var self=this;
    return createElement('div',[
      createElement('p',self.$slots.header),
      createElement('p',{
        domProps:{
          innerHTML:self.$slots.default[0].text
        }
      })
    ])
  }
})
作用域插槽

Vue组件template插槽传送门

<div id="app">
  <test>
    <template v-slot="messages">{{messages}}</template>
    <template v-slot:header="messages2">{{messages2}}</template>
  </test>
</div>
Vue.component('test', {
  render: function (createElement){
    return createElement('div',[
      this.$scopedSlots.default({
        mainMessage:"mainMessage"
      }),
      this.$scopedSlots.header({
        message:"message",
        message2:"message2"
      })
    ])
  }
  /*相当于Vue组件template中的:
  template:`
    <div>
      <slot :mainMessage="mainMessage"></slot>
      <slot name="header" :message="message" :message2="message2"></slot>
    </div>
  `*/
})

函数式组件

<div id="app">
  <test name="sam" age="18" @click="say">
    <template v-slot:default><p @click="say">default text</p></template>
    <!-- 插槽里的事件作为输出的独立内容可以正常响应 -->
    <template v-slot:header>header text</template>
    aaa
  </test>
</div>
//函数式组件没有响应式数据(this),如data、props等无法访问
//函数式组件没有上下文,所以渲染更快
Vue.component('test', {
  functional:true,  //设置为函数式组件
  render:function(createElement,context){  //函数式组件不能响应数据,但提供了context参数
    console.log(context.listeners);
    return createElement("div",[
      //插槽
      createElement("p",context.slots().default),  //this.$slots.default,slots()是函数所以要加括号
      createElement("p",context.scopedSlots.header()),  //this.$slots.header,暴露传入的插槽对象,和上面功能一样
      createElement("p",context.data.scopedSlots.header()),  //同上
      //父子
      createElement("p",context.children),  //所有子节点VNode集合,指定名称的插槽(template标签)不会被计入,注意未指定名称的插槽会被计入(不含标签)
      createElement("p",context.parent.$el.id),  //parent:父节点,<div id="app">元素的id
      //数据
      //context里的data不是指组件的data,是传递给组件的整个数据对象,例如上面的scopedSlots和attrs
      createElement("p",context.props.name),  //this.name
      createElement("p",context.data.attrs.age),  //所有attrs,若有props参数,则props内已声明的参数不会在attrs中,若没有则所有参数都在attrs中;例本例attrs中只有age没有name
      //事件
      //函数式组件不能响应test上的事件(例如例题中的@click="say"不能响应)
      createElement("p",{
        on:{  //单独元素的事件可以响应
          click:function(){
            context.listeners.click();  //通过listeners对象获取test上的事件
          }
        }
      },"click"),
      createElement("p",context.injections.text)  //访问inject,传送门:https://blog.csdn.net/yjl15517377095/article/details/100985342#_418
    ])
  },
  props:["name"],  //函数式组件中可以省略props,因为可以自动添加;注意在template组件中仍不可省略
  inject:["text"]
})

var app=new Vue({
  el:"#app",
  methods:{
    say:function(){
      console.log("hello");
    }
  },
  provide:function(){
    return{
      text:"ni hao"
    }
  }
});

传递属性/事件特性

<div id="app">
  <!-- 传递整个data使声明式函数不需要native修饰符就可以响应原生事件 -->
  <test name="sam" age="18" @click="say"></test>
  <test2 name="sam" age="18" @click.native="say"></test2>
</div>
Vue.component('test', {
  functional:true,
  render:function(createElement,context){
    /*函数式组件不会渲染任何属性(包括style,class):<p></p>
    函数式组件仍可在data里读取到:
    return createElement("p",context.data.attrs.age)
    所以可以传递整个data,实现和普通组件一样的效果:<p age="18"></p>*/
    return createElement("p",context.data,"test")
    //把data(包括事件监听器和数据)整个传递下去是非常透明的,甚至原生事件都不需要native修饰符
  },
  props:["name"]
});

Vue.component('test2', {
  //普通组件不会渲染存在于props的属性,其他正常渲染:<p age="18"></p>
  template:'<p>test2</p>',
  props:["name"]
});

var app=new Vue({
  el:"#app",
  methods:{
    say:function(){
      console.log("hello");
    }
  }
});

过滤器

  1. 用管道符表示要使用的过滤器
  2. 局部和全局过滤器重名时优先使用局部过滤器
<div id="app">
  <!-- 在{{}}中使用 -->
  {{message | capitalize}}
  <!-- 在v-bind中使用 -->
  <p :title="message | uppercase">text</p>
  <!-- 过滤器串联,由前至后依次过滤,先全小写再首字母大写 -->
  {{'HELLO' | lowercase | capitalize}}
  <!-- 带参过滤器 -->
  {{'hello' | capitalize_diy(1)}}
</div>
//全局声明
Vue.filter("uppercase",function(value){
  return value.toUpperCase()
});
Vue.filter("lowercase",function(value){
  return value.toLowerCase()
});
Vue.filter("capitalize_diy",function(value,n){  //指定要大写的字符位置
  return value.slice(0,n) + value.charAt(n).toUpperCase() + value.slice(n+1)
});

var app=new Vue({
  el:"#app",
  data:{
    message:"hello"
  },
  //局部声明
  filters:{
    capitalize:function(value){  //首字母大写
      return value.charAt(0).toUpperCase() + value.slice(1)
    }
  }
});

Vuex

axios同步

下面两个写法效果相同:

getTodos(){
    axios({
        url: 'http://jsonplaceholder.typicode.com/todos'
    }).then(response => {
        console.log(response.data);
    })
}

async同步写法

async getTodos({ commit }){
    var todos = await axios.get('http://jsonplaceholder.typicode.com/todos');
    console.log(todos);
}

state,getters,actions,mutations

新建一个存放状态管理的文件夹store(名字任意),新建一个index.js(index表示默认的状态管理入口,引入时只需要写到文件夹路径,起其他名字则需要写到文件名)

import Vue from 'vue'
import Vuex from 'vuex'
import todos from './modules/todos'

Vue.use(Vuex)

export default new Vuex.Store({
    modules:{
        todos
    }
})

modules(任意)创建todos(任意,按用途起名字)

import axios from 'axios';

// 相当于 data
const state = {
    todos:[]
};

// 相当于 computed(计算属性)
const getters = {
    allTodos:state => state.todos
};

// 与后台交互,获取 json数据,传给 mutations
const actions ={
    async getTodos({ commit }){
        var todos = await axios.get('http://jsonplaceholder.typicode.com/todos');
        console.log(todos);
        this.commit('setTodos', todos);
    }
};

// 操作 actions传来的数据,赋给 state
const mutations = {
    setTodos(state, todos){
        state.todos = todos
    }
};

export default{
    state,	// state <==> state:state
    getters,
    actions,
    mutations
};

不直接把todos的东西放在index.js中是为了便于分离各个状态,便于管理

状态的读取和操作

<template>
  <div class="todos">
    {{ allTodos }}
  </div>
</template>

<script>
// 需要哪个就映射哪个
import { mapGetters,mapActions } from 'vuex';

export default {
  name: 'todos',
  // 简便写法
  methods: mapActions(['getTodos']),
  // 正常写法(前面加 ...)
  computed: {
    ...mapGetters(['allTodos'])
  },
  created(){
    this.getTodos();
  }
}
</script>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值