文章目录
Vue组件的简单使用和传值操作
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello World</title>
<script src="js/vue.js"></script>
</head>
<body>
<div id="app">
<input type="text" v-model="inputValue" />
<button @click="handlerBtnClick">添加</button>
<ul>
<!--父组件:遍历的item数据绑定在item属性上-->
<!-- @delete 监听子组件传递触发的事件,并把子组件传递的值携带到调用处,当然在这里不用显示指定 -->
<!-- 绑定三个属性,key,item,index,对应的值来源于v-for中的循环的值 -->
<todo-item :key="item" :item="item" :index="index" v-for="(item,index) of list" @delete="handlerDeleteItem"></todo-item>
</ul>
</div>
<script>
//局部组件,子组件名称,驼峰命名在使用的时候需要变成小写且下划线分割
let TodoItem = {
//接受父组件绑定的属性值,名称一致即可
props:['item','index'],
template : '<li @click="handlerItemClick">{{item}}</li>',
methods:{
handlerItemClick:function(){
//对上一层触发指定的事件(delete),并传递一个参数,让父组件去监听该事件执行相应的操作
this.$emit('delete',this.index);
}
}
};
let vm = new Vue({
el:"#app",
//注册局部组件
components:{TodoItem:TodoItem},
data:{
inputValue:'',
list:[]
},
methods:{
handlerBtnClick : function(){
this.list.push(this.inputValue);
this.inputValue = '';
},
handlerDeleteItem:function(index){
this.list.splice(index,1);
}
}
});
</script>
</body>
</html>
Vue模板表达式-侦听器-计算属性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>计算属性</title>
<script src="js/vue.js"></script>
</head>
<body>
<div id="app">
<!--
v-开头的数据不再是字符串,它们都变为了模板表达式,都是使用变量来完成对应数据显示
当然他们都可以进行方法调用,算术运算等...
-->
<div v-text="info"></div> <!-- HTML标签会以文本的形式显示-->
<div v-html="info"></div> <!-- HTML标签会被解析 -->
<div :title="info"> <!-- 绑定标签属性值-->
{{info}} <!-- 类似于v-text -->
</div>
<!-- 计算属性,方法,以及侦听器使用 -->
<hr />
{{fullName}}
<br/>
{{myFullName()}}
<br/>
{{age}}
</div>
<script>
let vm = new Vue({
el:"#app",
data:{
info:"<h1>AAAAA</h1>",
firstName : 'Hello',
lastName : 'World',
age:18
},
//侦听器,监听对应的属性,如果发生改变则出发,如果没有改变则依然使用缓存数据,不会触发
watch:{
//监听firstName变量
firstName : function(){
console.log(this.firstName);
}
},
//方法计算:没有缓存效果,页面对应数据发生改变,则会再次触发,效率没有计算属性高
methods:{
myFullName:function(){
console.log("myFullName");
return this.firstName +" "+ this.lastName;
}
},
//计算属性: 有缓存效果,如果计算的值没有改变,则不会重复计算
computed:{
fullName : function(){
console.log("fullName");
return this.firstName +" "+this.lastName;
}
}
});
</script>
</body>
</html>
Style&Class绑定
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Class与Style</title>
<script src="js/vue.js"></script>
<style>
.active{
background: green;
width: 50px;
height: 50px;
}
.isBool{
background: skyblue;
}
</style>
</head>
<body>
<div id="app">
<!-- 绑定 HTML Class -->
<!-- 对象语法 -->
<div class="active" :class="{ other : isBool }"></div>
<div :class="{ active : isActive , other : isBool }"></div>
<div :class="clsObj"></div>
<!-- 数组语法 -->
<div :class="[active,other]"></div>
<div :class="[isActive?active:'',other]"></div>
<div :class="[{active:isActive},other]"></div>
<hr />
<!-- 绑定内联样式 -->
<!--
对象语法
v-bind:style 的对象语法十分直观——看着非常像 CSS,但其实是一个 JavaScript 对象。
CSS 属性名可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用引号括起来) 来命名:
-->
<div :class="{active:isActive}" :style="{color:activeColor,fontSize:fontSize+'px','background-color':activeColor}">AAA</div>
<div class="active" :style="styleObj"></div>
</div>
<script>
let vm = new Vue({
el:"#app",
data:{
isActive:true,
isBool:true,
clsObj:{
active:true,
other:true
},
active:'active',
other:'other',
activeColor:'gray',
fontSize:12,
styleObj:{
color:'gray',
fontSize:'12px',
'background-color':'green'
}
}
});
</script>
</body>
</html>
Vue注意事项
解析 DOM 模板时的注意事项
有些 HTML 元素,诸如<ul>
、<ol>
、<table>
和<select>
,对于哪些元素可以出现在其内部是有严格限制的。而有些元素,诸如<li>
、<tr>
和<option>
,只能出现在其它某些特定的元素内部。
这会导致我们使用这些有约束条件的元素时遇到一些问题。例如:
HTML代码
<div id="app">
<table>
<tbody>
<tr-row></tr-row>
<tr-row></tr-row>
<tr-row></tr-row>
</tbody>
</table>
</div>
js代码
let trRow = {
template:'<tr><td>This is row !</td></tr>'
}
let vm = new Vue({
el:"#app",
components:{trRow},
data:{
}
});
结果这个自定义组件
<tr-row>
会被作为无效的内容提升到外部,并导致最终渲染结果出错。不过Vue给我们提供了一个is
特性的变通办法:
依然使用HTML5的规范,tbody中就写tr,而我们的模板绑定就在tr上添加一个is属性去替换tr标签信息即可。
<div id="app">
<table>
<tbody>
<tr is="tr-row"></tr>
<tr is="trRow"></tr>
<tr is="trRow"></tr>
</tbody>
</table>
</div>
在子组件中使用data
当我们定义一个子组件时,data
不能直接提供一个对象,而是通过函数的形式,返回一个对象,因此每个实例可以维护一份被返回对象的独立的拷贝,如果 Vue 没有这条规则,就可能会影响到其它所有实例:
let trRow = {
data:function(){
return {
content:'This is row!'
}
},
template:'<tr><td>{{content}}</td></tr>'
}
通过Vue获取DOM节点和子组件实例
vm.$refs
一个对象,持有注册过ref特性的所有DOM元素和组件实例。
vm.$emit( eventName, […args] )
触发当前实例上的事件。附加参数都会传给监听器回调。即子组件向父组件传值,可以放在args中,父组件监听这个方法的时候可以通过参数获取.
<div id="app">
<!--
父组件监听子组件触发的divChange事件,并通过handlerClick进行处理
通过ref属性,把counter组件实力绑定到实例属性$refs上
-->
<counter ref="counter" @divChange="hanlderClick"></counter>
</div>
let counter = {
data:function(){
return {
number:0
}
},
//div上绑定一个点击事件
template:'<div @click="change">{{number}}</div>',
methods:{
//点击div的时候当前div数据的值++,并对外触发一个divChange事件
change:function(){
this.number++;
this.$emit('divChange'); //名字可以自定义
}
}
}
let vm = new Vue({
el:"#app",
components:{counter},
methods:{
//处理counter组件上的事件,并获取该组件上data中的number信息
hanlderClick:function(){
console.log(this.$refs.counter.number);
}
}
});
父子组件之间的数据传递
- 通过 prop 向子组件传递数据
HTML 中的特性名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名.
prop 是你可以在组件上注册的一些自定义特性。当一个值传递给一个 prop 特性的时候,它就变成了那个组件实例的一个属性,一个组件默认可以拥有任意数量的 prop,任何值都可以传递给任何 prop。在子组件中,你会发现我们能够在组件实例中访问这个值,就像访问data
中的值一样。
- 通过实例方法参数
vm.$emit( eventName, […args] )
向父组件传值,可以放在args中,父组件监听这个方法的时候可以通过参数
<!-- 在 HTML 中传值属性是 kebab-case 的 -->
<counter :data-num="num" @div-change="hanlderClick"></counter>
<div>{{num}}</div>
let counter = {
// 在 JavaScript 中传值属性是 camelCase 的
props:['dataNum'], //父组件传递过来的参数值,通过prop进行获取
data:function(){
return {
number:this.dataNum,
step:2
}
},
template:'<div @click="change">{{number}}</div>',
methods:{
change:function(){
this.number += this.step;
//HTML属性不区分大小写,并且在使用组件模板时不能使用v-on来侦听camelCase事件,需要用中划线连接
this.$emit('div-change',this.step); //名字可以自定义
}
}
}
let vm = new Vue({
el:"#app",
components:{counter},
data:{
num:2
},
methods:{
hanlderClick:function(step){
this.num += step;
}
}
});
prop 参数验证
父组件在向子组件传递数据的是,我们可以对这些数据进行一些验证,如果不满足需求,则 Vue 会在浏览器控制台中警告你。这在开发一个会被别人用到的组件时尤其有帮助。
定制 prop 的验证方式,你可以为props
中的值提供一个带有验证需求的** 对象** ,而不是一个字符串数组。
Vue.component('my-component', {
props: {
// 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
propA: Number,
// 多个可能的类型
propB: [String, Number],
// 必填的字符串
propC: {
type: String,
required: true
},
// 带有默认值的数字
propD: {
type: Number,
default: 100
},
// 带有默认值的对象
propE: {
type: Object,
// 对象或数组默认值必须从一个工厂函数获取
default: function () {
return { message: 'hello' }
}
},
// 自定义验证函数
propF: {
validator: function (value) {
// 这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
},
propG: {
validator: function (value) {
// 这个值字符串长度需要大于5
return value.length>5
}
}
}
})
类型检查: type可以是下列原生构造函数中的一个:
String
、Number
、Boolean
、Date
、Function
、Symbol
需要注意,如果父组件给子组件传递值的时候,如果子组件没有还是用props来接收,则对应传递的值会绑定在子组件根元素上的属性上
在组件上使用原生事件
在父组件上,如果子组件没有进行$.emit
进行对外暴露的话,父组件是没有办法进行事件绑定的,因为在父组件上定义的事件都是自定义事件,即都是子组件的监听事件,那么想要绑定js的原生组件怎么办呢?很简单,只需要在添加一个 .native
就可以了,如下所示:
<div id="app">
<counter @click.native="handlerCiick"></counter>
</div>
let counter = {
template:'<div>Child</div>'
}
let vm = new Vue({
el:"#app",
components:{counter},
methods:{
handlerCiick:function(){
alert('click');
}
}
});
非父子组件之间的传值
使用Vue.prototype.bus=new Vue();,在借助$.emit()对外触发事件,并传递值,通过事件钩子mounted在Vue实例加载完毕后,去监听传递数值组件对外触发的事件,就可以获取到传递的值了.
在Vue中使用插槽
插槽就是定义一套组件模板,使用相应的任易内容去填充对应的模板中标注插槽的位置数据.如下是一个简单的案例,定义了简单的按钮组件,使用插槽去动态填充对应按钮显示的文字信息,当然在插槽内部可以写相应的信息来作为默认值,如果父组件没有传递值,则显示默认值.
<div id="app">
<submit-btn>保存</submit-btn>
</div>
let submitBtn = {
template:"<button type='submit'><slot>提交</slot></button>"
}
let vm = new Vue({
el:"#app",
components:{submitBtn}
});
具名插槽:就是可以给插槽取一个名字,不同的数据填充到不同的插槽中,插槽取名用slot中的属性 name 给定具体的值即可.而父级元素填充值的时候,我们可以在一个 元素上使用 v-slot: 指令,提供其名称,简写是#号
注意 v-slot 只能添加在一个 ,默认插槽除外,可以多次使用
<div id="app">
<base-layout>
<template v-slot:header>
<h1>Here's Header</h1>
</template>
This is context
<template #footer>
<h1>Here's Footer</h1>
</template>
</base-layout>
</div>
特别注意: 使用模板一定要有一个
根元素
let baseLayout={
template:`
<div class='container'>
<header>
<slot name='header'></slot>
</header>
<section>
<slot></slot>
</section>
<footer>
<slot name='footer'></slot>
</footer>
</div>
`
}
let vm = new Vue({
el:"#app",
components:{baseLayout}
});
作用域插槽 : 子组件通过插槽给特定的父组件传递对应的值,并使用该值进行填充数据
插槽中通过属性进行绑定,父组件中通过定义一个prop对象来接收,这里可以使用解构的形式来进行动态赋值,简化步骤
<div id="app">
<base-layout>
<!-- 使用子组件传递过来的数据 -->
<template #header="listProp">
{{listProp.list[0]}}
<h1>Here's Header</h1>
</template>
This is context
<!-- 解构写法--把子组件传递过来的值通过一个item接收,就不需要定义一个prop对象来指定获取了 -->
<template #footer="{item}">
<li>{{item}}</li>
</template>
</base-layout>
</div>
let baseLayout={
data:function(){
return {
list:['apple','banana','other']
}
},
template:`
<div class='container'>
<header>
<slot name='header' :list='list'></slot>
</header>
<section>
<slot></slot>
</section>
<footer>
<ul>
<slot name='footer' v-for="item of list" :item='item'></slot>
</ul>
</footer>
</div>
`
}
let vm = new Vue({
el:"#app",
components:{baseLayout}
});
动态组件
我们之前曾经在一个多标签的界面中使用is
特性来切换不同的组件:
<component v-bind:is="currentTabComponent"></component>
当在这些组件之间切换的时候他其实是在不停的创建和销毁,不光性能浪费,且有的时候你想在来回切换的时候保留之前选中的一些信息以及组件状态,这个时候你要实现相对来说就比较麻烦,这个时候我们就可以使用keep-alive来弥补以上缺陷,很简单只需要使用keep-alive
进行包裹即可。
注意这个<keep-alive>
要求被切换到的组件都有自己的名字,不论是通过组件的name
选项还是局部/全局注册。
<!-- 失活的组件将会被缓存!-->
<keep-alive>
<component v-bind:is="currentTabComponent"></component>
</keep-alive>
超链接
不再是使用a标签,vue中有自己的一套标签,使用方式如下:
<router-link to="/info">
点击我跳转
</router-link>
文件路径别名
在build/webpack.base.conf.js中可以配置文件的别名,如果文件路径太深,那么使用这种方式无疑是一个比较好的选择:
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src'),
'style':resolve('src/assets/style/')
}
}
请求代理
想要隐藏项目的真实路径,这个时候可以使用webpack提供的代理模式完成,在conf/index.js中进行配置即可,如下,项目的真实路径是static/json ,配置代理路径为/api ,这样所有api开头的路径都会去找static/json下对应的文件
proxyTable: { //代理转发
'/api':{
target:'http://localhost:8080',
pathRewrite:{
'^/api':'/static/json'
}
}
vue中的ajax --> axios
在vue中官方推荐使用axios来完成ajax的操作,使用比较简单,先使用npm命令安装该模块,主要需要在项目的根目录下运行:
npm install axios --save //--save表示安装并配置到当前项目的package.json中
在项目中使用,通过import进行模块引入,然后就可以使用了,代码如下:
<script>
import axios from "axios"
export default {
name : 'Home',
methods:{
getAjaxInfo(){
axios.get('/api/info.json').then(function(data){
console.log(data.data);
});
}
},
mounted() {
this.getAjaxInfo()
}
}
</script>