VUE 学习笔记
意识 Vue
<body>
<!--
初识 Vue:1. 想让 Vue 工作,就必须创立一个 Vue 实例,且要传入一个配置对象;2.root 容器里的代码仍然合乎 html 标准,只不过混入了一些非凡的 Vue 语法;3.root 容器里的代码被称为【Vue 模板】;4.Vue 实例和容器是一一对应的;5. 实在开发中只有一个 Vue 实例,并且会配合着组件一起应用;6.{{xxx}}中的 xxx 要写 js 表达式,且 xxx 能够主动读取到 data 中的所有属性;7. 一旦 data 中的数据产生扭转,那么页面中用到该数据的中央也会自动更新;留神辨别:js 表达式 和 js 代码(语句)
1. 表达式:一个表达式会产生一个值,能够放在任何一个须要值的中央:(1). a
(2). a+b
(3). demo(1)
(4). x === y ? 'a' : 'b'
2.js 代码(语句)
(1). if(){}
(2). for(){}
-->
<!-- 筹备好一个容器 -->
<div id="demo">
<h1>Hello,{{name.toUpperCase()}},{{address}}</h1>
</div>
<script type="text/javascript" >
Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提醒。// 创立 Vue 实例
new Vue({
el:'#demo', //el 用于指定以后 Vue 实例为哪个容器服务,值通常为 css 选择器字符串。data:{ //data 中用于存储数据,数据供 el 所指定的容器去应用,值咱们临时先写成一个对象。name:'atguigu',
address:'北京'
}
})
</script>
</body>
1、Vue 的模板语法
重点阐明:
如果在标签中应用属性绑定值,是字符串,动静绑定则是会去解析表达式。例如
// 字符串
<h1 value="1"></h1>
// 动静绑定是解析表达式 数字 1
<h1 :value="1"></h1>
Vue 模板语法有 2 大类:1. 插值语法:性能:用于解析标签体内容
写法:{{xxxx}},xxx 会作为表达式去解析,且能够主动读取到 data 中的属性
2. 指令语法:性能:用于解析标签(包含:标签属性、标签体内容、绑定事件.....)举例:v-bind:href="xxxxxx" 或 简写为:
备注:Vue 中有很多的指令,此处咱们只是拿 v -bind 举个例子
<body>
<div id="root">
<h2> 插值语法 </h2>
<h4>{{msg}}</h4>
<h4>{{msg.toUpperCase()}}</h4>
<h2> 指令语法 </h2>
<!-- 动态属性间接写 或者绑定应用 v -bind 后应用字符串 -->
<a :href="'http://www.baidu.com'"> 动静绑定属性是字符串 </a>
<!-- 属性是动静的必须要应用 v -bind:href="" 双引号中的内容是 data 中的变量 -->
<a v-bind:href="url"> 百度一下 </a>
</div>
<script>
new Vue({
el:"#root",
data:{
msg:"插值语法 Insert",
url:"http://www.baidu.com",
},
})
</script>
</body>
2、数据绑定及双向绑定原理
Vue 中有 2 种数据绑定的形式:1. 单向绑定(v-bind):数据只能从 data 流向页面。2. 双向绑定(v-model):数据不仅能从 data 流向页面,还能够从页面流向 data。备注:1. 双向绑定个别都利用在表单类元素上(如:input、select 等)2.v-model:value 能够简写为 v-model,因为 v -model 默认收集的就是 value 值。
<div id="root">
<p>{{msg}}</p>
单项数据绑定(v-bind):<input type="text" :value="msg"><br>
<!-- v-model:value="msg" 针对表单类元素 -->
双向数据绑定(v-model):<input type="text" v-model="msg"><br>
双向数据绑定(v-model):<input type="text" v-model:value="msg">
</div>
<script>
new Vue({
el:"#root",
data:{msg:"",}
});
v-model 的底层原理:
v-model 指令在表单 input、textarea、select 等元素上创立双向数据绑定,实质是语法糖,v-model 在外部为不同的输出元素应用不同的属性并抛出不同的事件:text 和 textarea 元素应用 value 属性和 input 事件
checkbox 和 radio 应用 checked 属性和 change 事件
select 字段将 value 作为 prop 并将 change 作为事件
以 input 表单作为案例
<!-- v-model 的底层原理 通过 vm.msg 向 input 标签的 value 单向传递 反向传递:通过 input 事件对 msg 进行赋值 $event 为事件的对象 target.value 获取输出的值 -->
<!-- :value 动静绑定 value 值 触发 input 事件 -->
<input type="text" v-bind:value="msg" v-on:input="msg=$event.target.value">
<textarea cols="30" rows="10" :value="msg" @input='msg=$event.target.value'></textarea>
<!-- :checked 动静绑定 触发 change 事件 -->
<input type="checkbox" :checked='msg' @change="msg=$event.target.value">
<input type="radio" :checked='msg' @change="msg=$event.target.value">
<input type="radio" :checked='!msg' @change="msg=!$event.target.value">
<!-- :value 值 触发 change 事件 -->
<select :value="msg" @change="msg=$event.target.value">
<option value="0">0</option>
<option value="1">1</option>
</select>
</div>
<script>
var app = new Vue({
el:"#app",
data:{msg:"",},
如果在自定义组件中,v-model 默认会利用名为 value 的 prop 和名为 input 的事件
<div id="app">
<p>{{msg}}</p>
<!-- 默认是给 my-input 动静绑定 value 值 并增加一个 input 事件 -->
<!-- <my-input :value="msg" v-on:input="msg=$event"></my-input> -->
<my-input v-model="msg"></my-input>
</div>
<script>
Vue.component("my-input",{props:["value"],
template:`
<div>
<p> 我是组件 </p>
<input type="text" :value="value" v-on:input="$emit('input',$event.target.value)">
</div>
`
});
var app =new Vue({
el:"#app",
data:{msg:"初始值",}
})
3、data 和 el 的两种写法
data 与 el 的 2 种写法:1.el 有 2 种写法:(1).new Vue 时候间接传递 el 属性 ---- 罕用
(2). 先 new Vue 再通过 vm.$mount('#root')指定 el 属性 ---- 不罕用
// 写 template 会代替 el 中的全部内容,替换时必须有根节点蕴含。如果没有 template 会默认 el 中的内容为渲染对象
2.data 有 2 种写法:(1). 对象式: 非组件化编码时能够写对象,也能够写函数。(2). 函数式:组件化编码必须应用函数式的 data, 组件数据能够复用。Vue 中的一个最最重要的准则:由 Vue 所调用的函数,都不要写成箭头函数,一旦写了箭头函数,this 就不对了(或者是 undefined,或者是 Window)
<script>
// 第一种写法
const vm = new Vue({
el:"#root",
data:{msg:"MVVM",},
});
// 第二种写法
const vm = new Vue({data(){
return {msg:"MVVM"}
},
template:`
<div>
<h1>{{msg}}</h1>
</div>
`
})
vm.$mount('#root')
setTimeout(()=>vm.msg="hello",1000)
// data 的第一种写法 对象
// data 的第二种写法 函数 返回一个对象(组件中写的多)</script>
4、MVVM 模型
MVVM 模型
1. M:模型(Model):data 中的数据
2. V:视图(View):模板代码
3. VM:视图模型(ViewModel):Vue 实例
察看发现:1.data 中所有的属性,最初都呈现在了 vm 身上。2.vm 身上所有的属性 及 Vue 原型上所有属性,在 Vue 模板中都能够间接应用。
5、数据代理
5.1 Object.defineProperty 办法
Object.defineProperty() 办法会间接在一个对象上定义一个新属性,或者批改一个对象的现有属性,并返回此对象。
备注:该当间接在 Object
结构器对象上调用此办法,而不是在任意一个 Object
类型的实例上调用。
语法:Object.defineProperty(obj, prop, descriptor)
obj: 要定义属性的对象
prop: 要定义或批改的属性的名称或 Symbol
descriptor: 要定义或批改的属性描述符, 简写就是默认的 value 值。
描述符默认值汇总
- 领有布尔值的键
configurable
、enumerable
(调用 Object.keys()获取不到) 和writable
的默认值都是false
。 - 属性值和函数的键
value
、get
和set
字段的默认值为undefined
。
let _data = {msg: "小明"};
let vm = {};
// 通过对象办法给 vm 对象赋值属性 msg
Object.defineProperty(vm, "msg", {set(value) {
// value 值就是内部赋值的值
console.log(value);
// this 指向 vm 对象
console.log(this);
// 调用一次进行赋值会有限递归
this.msg = value;
},
get() {return _data.msg;}
})
vm.msg = "123"
<!-- 数据代理:通过一个对象代理对另一个对象中属性的操作(读 / 写)-->
<script type="text/javascript" >
let obj = {x:100}
let obj2 = {y:200}
// 通过 Getter 读取 obj.x 的值,批改 obj2 的新增 x 属性是,理论调用 Setter 是对 obj.x 的赋值
Object.defineProperty(obj2,'x',{get(){return obj.x},
set(value){obj.x = value}
})
</script>
5.2 对于 Vue 中的数据代理:
1. 什么是数据代理?(1). 配置对象 data 中的数据,会被收集到 vm._data 中,而后通过,Object.defineProperty 让 vm 上领有 data 中所有的属性。(2). 当拜访 vm 的 msg 时,返回的是_data 当中同名属性的值
(3). 当批改 vm 的 msg 时,批改的是_data 当中同名属性的值
2. 为什么要数据代理?为了更加不便的读取和批改_data 中的数据,不做数据代理,就要:vm._data.xxx 拜访数据
3. 扩大思考?—— 为什么要先收集在_data 中,而后再代理进来呢?更高效的监督数据(间接收集到 vm 上会导致监督效率太低)4. 基本原理:通过 Object.defineProperty()把 data 对象中所有属性增加到 vm 上。为每一个增加到 vm 上的属性,都指定一个 getter/setter。在 getter/setter 外部去操作(读 / 写)data 中对应的属性。vue 底层,当 data 的数据变动,去更新 dom,双向绑定 dom 元素的 data 变动也更新 dom,实质是数据变动
验证 data 中的数据就是 vm 下的_data
const data = {
name: "尚硅谷",
address: "宏福科技园",
};
const vm = new Vue({
el: "#root",
data
});
// 管制台上
vm._data === data //true
数据代理的原理
// 当初获取 msg 只能通过 vm._data 获取
// 如何实现通过 vm.msg 进行获取,将 data 下的键值挂载到 vm 下,每扭转 data 中 msg 的值,vm.msg 也能同步扭转?// 模仿 Vue 实例下 vm 的_data 参数对象,配置对象 data 中的数据,会被收集到 vm._data 中
let _data = {msg: "小明"};
// vm 模仿 Vue 实例化的对象
let vm = {};
// 实现形式:为 vm 对象增加属性值
Object.defineProperty(vm, "msg", {set(value) {
// value 值就是内部赋值的值
console.log(value);
// this 指向 vm 对象
console.log(this);
// 实现双向绑定 批改任意的一个值 两个都会产生扭转
_data.msg=value;
},
get() {return _data.msg;}
})
// 内部扭转_data.msg 的值
_data.msg = "123"
console.log(vm.msg);
6、事件处理
事件的根本应用:1. 应用 v -on:xxx 或 @xxx 绑定事件,其中 xxx 是事件名;2. 事件的回调须要配置在 methods 对象中,最终会在 vm 上;3.methods 中配置的函数,不要用箭头函数!否则 this 就不是 vm 了;4.methods 中配置的函数,都是被 Vue 所治理的函数,this 的指向是 vm 或 组件实例对象;5.@click="demo" 和 @click="demo($event)" 成果统一,但后者能够传参;
6.1 事件的修饰符
Vue 中的事件修饰符:
1.prevent:阻止默认事件(罕用);
2.stop:阻止事件冒泡(罕用);
3.once:事件只触发一次(罕用);
4.capture:应用事件的捕捉模式,默认是事件冒泡形式
5.self:只有 event.target 是以后操作的元素时才触发事件;
6.passive:事件的默认行为立刻执行,无需期待事件回调执行结束;
1.Vue 中罕用的按键别名:
回车 => enter
删除 => delete (捕捉“删除”和“退格”键)
退出 => esc
空格 => space
换行 => tab (非凡,必须配合 keydown 去应用)
上 => up
下 => down
左 => left
右 => right
2.Vue 未提供别名的按键,能够应用按键原始的 key 值去绑定,但留神要转为 kebab-case(短横线命名)
3. 零碎润饰键(用法非凡):ctrl、alt、shift、meta
(1). 配合 keyup 应用:按下润饰键的同时,再按下其余键,随后开释其余键,事件才被触发。
(2). 配合 keydown 应用:失常触发事件。
4. 也能够应用 keyCode 去指定具体的按键(不举荐)
5.Vue.config.keyCodes. 自定义键名 = 键码,能够去定制按键别名
应用修饰符时,程序很重要;相应的代码会以同样的程序产生。因而,用
v-on:click.prevent.self
会阻止 所有的点击,而v-on:click.self.prevent
只会阻止对元素本身的点击。不要把
.passive
和.prevent
一起应用,因为.prevent
将会被疏忽,同时浏览器可能会向你展现一个正告。请记住,.passive
会通知浏览器你 不想阻止事件的默认行为
<div id="root">
<h2> 欢送来到 {{school}} 学习 </h2>
<!-- 绑定事件 --- 规范形式 -->
<button v-on:click="show1"> 点我提醒:信息 1(v-on 绑定)</button> <br /><br />
<!-- 绑定事件 --- 简写形式 -->
<button @click="show1"> 点我提醒:信息 1(@绑定)</button> <br /><br />
<!-- 绑定事件 --- 传递参数 -->
<button @click="show2($event,666)"> 点我提醒:信息 2 + 传递的参数 </button> <br /><br />
<!-- 绑定事件 --- 阻止默认行为,prevent 叫事件修饰符 -->
<a href="https://www.baidu.com" @click.prevent="show3"> 点我提醒:信息 3(阻止默认行为)</a> <br /><br />
<!-- 绑定事件 --- 阻止冒泡,事件修饰符能够连写,且程序能够随便扭转 -->
<div @click="show4">
<a href="https://www.baidu.com" @click.stop.prevent="show4"> 点我提醒:信息 4(阻止冒泡)</a>
</div><br />
<!-- 只有 event.target 是以后操作的元素时才触发事件;点击外面时不会触发外层事件,阻止冒泡 -->
<div class="demo1" @click.self="showInfo">
<button @click="showInfo"> 点我提示信息 </button>
</div>
// 组合键
<input type="text" placeholder="按下回车提醒输出" @keydown.ctrl.13="showInfo">
<!-- 键盘事件 -->
<input @keyup.enter="show5" type="text" placeholder="按下回车提醒数据">
<!-- <input @keyup.13="show5" type="text" placeholder="按下回车提醒数据"> -->
<!-- <input @keyup.37="show5" type="text" placeholder="按下左箭头提醒数据"> -->
<!-- <input @keyup.arrow-left="show5" type="text" placeholder="按下左箭头提醒数据"> -->
<!-- <input @keyup.left="show5" type="text" placeholder="按下左箭头提醒数据"> -->
<!-- <input @keyup.16="show5" type="text" placeholder="按下 shift 提醒数据"> -->
</div>
<script type="text/javascript">
new Vue({
el: '#root',
data: { // 配置数据
school: '尚硅谷',
},
methods: { // 用于配置办法
show1(event) { // 此处的 show1 肯定肯定不要写成箭头函数,否则 this 就是 Window 了
//console.log(this) //this 是 vm
//console.log('信息 1',event.target.innerText)
alert('信息 1')
},
show2(event, number) {console.log(event)
alert('信息 2 ---' + number)
},
show3(event) {//event.preventDefault(); // 靠程序员手动阻止默认行为
alert('信息 3')
},
show4(event) {// event.stopPropagation(); // 靠程序员手动阻止冒泡
alert('信息 4')
},
show5(event) {// if(event.keyCode !== 13) return // 靠程序员本人判断按键
// console.log(event.keyCode) // 输入按键编码值
// console.log(event.key) // 输入按键名称
alert(event.target.value)
}
}
})
</script>
7、计算属性 computed 和数据监督 watch
7.1 计算属性
计算属性:
1. 定义:要用的属性不存在,要通过已有属性计算得来,默认是 getter 读取
2. 原理:底层借助了 Objcet.defineproperty 办法提供的 getter 和 setter。
3.get 函数什么时候执行?
(1). 首次读取时会执行一次。
(2). 当依赖的数据产生扭转时会被再次调用。
4. 劣势:与 methods 实现相比,外部有缓存机制(复用),效率更高,调试不便。
5. 备注:
1. 计算属性最终会呈现在 vm 上,间接读取应用即可。
2. 如果计算属性要被批改,那必须写 set 函数去响应批改,且 set 中要引起计算时依赖的数据产生扭转。
姓名案例:
-
应用插值语法实现
<div id="root"> 姓:<input type="text" v-model="firstName"> <br/> 名:<input type="text" v-model="lastName"><br/> <span> 全名:{{firstName + '-' + lastName}}</span> </div> <script type="text/javascript" > new Vue({ el:'#root', data:{ firstName:'张', lastName:'三' } }) </script>
-
methods 办法实现
<div id="root"> 姓:<input type="text" v-model="firstName"> <br/> 名:<input type="text" v-model="lastName"><br/> <span> 全名:{{getFullName()}}</span> </div> <script type="text/javascript" > new Vue({ el:'#root', data:{ firstName:'张', lastName:'三' }, methods:{getFullName(){console.log('getFullName 被调用了') return this.firstName + '-' + this.lastName } } }) </script>
-
computed 办法实现
<div id="root"> 姓:<input type="text" v-model="firstName"> <br/><br/> 名:<input type="text" v-model="lastName"><br/><br/> <span> 全名:{{fullName}}</span><br/><br/> 全名: <input type="text" v-model="fullName"> </div> <script type="text/javascript" > const vm = new Vue({ el:'#root', data:{ firstName:'张', lastName:'三', }, computed:{ /* 1.fullName 是谁在调用?---Vue 2.fullName 什么时候调用?首次渲染会调用、当依赖的属性值发生变化 */ // 简写 --- 相当与只指定了 get,没有指定 set 只能实现单向数据绑定由 fullName--》全名的 input fullName(){console.log('fullName') return this.firstName + '-' + this.lastName } // 残缺写法 ----set 和 get 都指定了 实现双向数据绑定 /* fullName:{set(value){ //fullName 被批改时 set 被调用,set 中的 this 是 vm,set 会收到批改的值 const arr = value.split('-') this.firstName = arr[0] this.lastName = arr[1] }, get(){ //fullName 被读取时 get 被调用,get 中的 this 是 vm console.log('get') return this.firstName + '-' + this.lastName } } */ } }) console.log(vm) </script>
methods 和 computed 比照
<div id="root">
<h2>{{x}}</h2>
<h2>{{y()}}</h2>
<h2>{{z}}</h2>
<button @click="y"> 点我调用 y 办法 </button> <br/><br/>
<button @click="y()"> 点我调用 y 办法 </button> <br/><br/>
展现 x 的值:<input type="text" v-model="x">
</div>
<script type="text/javascript" >
const vm = new Vue({
el:'#root', // 指定 vm 为哪个容器服务
data:{ // 驱动页面显示的数据都放在这里
x:100 // x 最终会通过数据代理的形式放在 vm 身上
},
methods:{ // 所有用到的函数都配置在这里
y(){ // y 间接被放在 vm 身上
console.log('y 被调用了')
return 200
}
},
computed:{
z:{ // z 间接被放在 vm 身上了
set(value){ // z 的值被扭转时,set 执行,set 中的 this 是 vm,set 会收到批改的值
console.log('有人批改 z 了,批改的值为:',value)
},
get(){ // z 的值被读取时,或 z 依赖的值发生变化时,get 执行,get 中的 this 是 vm,前提是:页面中要用到 z
console.log('get 被调用了')
return this.x*1 +1
}
}
}
})
console.log(vm)
</script>
7.2 监督办法
-
watch 办法实现
监督属性 watch:1. 当被监督的属性变动时, 回调函数主动调用, 进行相干操作 2. 属性必须存在,能力进行监督!!3. 监督的两种写法:(1).new Vue 时传入 watch 配置 (2). 通过 vm.$watch 监督 深度监督:(1).Vue 中的 watch 默认不监测对象外部值的扭转(一层)。(2). 配置 deep:true 能够监测对象外部值扭转(多层)。备注:(1).Vue 本身能够监测对象外部值的扭转,但 Vue 提供的 watch 默认不能够!(2). 应用 watch 时依据数据的具体构造,决定是否采纳深度监督。
<div id="root">
<input type="text" v-model="firstName"> <br>
<input type="text" v-model="lastName"><br>
<span> 全名:{{fullName}}</span><br>
</div>
<script>
const vm = new Vue({
el:"#root",
data:{
firstName:'张',
lastName:'三',
fullName:''
},
watch:{
/*
1.watch 中的 firstName 什么时候调用?data 中的 firstName 被扭转的时调用
2.watch 中的 firstName 的 this 是谁?---vm
*/
// 监测姓 ----- 精简写法
// firstName(newVal,oldVal){// console.log('firstName 被批改',newVal,oldVal);
// this.fullName = newVal+'-'+this.lastName;
// },
// 监测名 ----- 精简写法
// lastName(newVal,oldVal){// console.log('lastName 被批改',newVal,oldVal);
// this.fullName=this.firstName+'-'+newVal
// },
// 监测姓 ----- 残缺写法
// 残缺写法
firstName:{
immediate:true,
handler(newVal,oldVal){this.fullName=newVal+'-'+this.lastName}
}
}
});
// 监测名 ----- 残缺写法
vm.$watch('lastName',{
immediate:true,// 若 immediate 为 true 则 handler 在初始化时,就会调用一次,当前就看 firstName 的扭转了
handler(newVal,oldVal){setTimeout(()=>{this.fullName = this.firstName+'-'+newVal;},2000)
},
deep:true
})
// 简写
vm.$watch('lastName',(newVal,oldVal)=>{setTimeout(()=>{this.fullName = this.firstName+'-'+newVal;},2000)
}
})
</script>
computed 和 watch 之间的区别:1. 只有是 computed 能实现的性能,watch 都能够实现
2.watch 能实现的性能,computed 不肯定能实现,例如:watch 能够进行异步操作
备注:1. 所有被 Vue 所调用(治理)的函数,都不要写箭头函数 ----- 例如:watch 中的函数、computed 中的函数
2. 所有不是被 Vue 所调(治理)的函数,都要写成箭头函数 --- 例如:定时器的回调、ajax 的回调等等
3.watch 就是 Vue 给我提供的一个监测数据扭转的伎俩,至于数据产生扭转后,要做什么,得看具体的业务了逻辑。例如:须要新的值、旧的值作比拟,决定接下来要干什么
不要值,只有数据扭转了,就要发申请等等
8、绑定款式
绑定款式:1. class 款式
写法:class="xxx" xxx 能够是字符串、对象、数组。字符串写法实用于:类名不确定,要动静获取。对象写法实用于:要绑定多个款式,个数不确定,名字也不确定。数组写法实用于:要绑定多个款式,个数确定,名字也确定,但不确定用不必。2. style 款式
:style="{fontSize: xxx}" 其中 xxx 是动静值。:style="[a,b]" 其中 a、b 是款式对象。
<div id="root">
<!-- class 的字符串写法,类名不确定动静获取 -->
<h2 class="box" :class="myStyle">{{title}}</h2>
<!-- 对象 -->
<h2 class="box" :class="{classB:hasB,classC:hasC}">{{title}}</h2>
<!-- 三元 -->
<h2 class="box" :class="hasB?'classB':''">{{title}}</h2>
<!-- 数组 -->
<h2 class="box" :class="[a,b,c]">{{title}}</h2>
<!-- 绑定 style -->
<h2 class="box" :class="[a,b,c]" :style="{fontSize:size+'px'}">{{title}}</h2>
<h2 class="box" :class="[a,b,c]" :style="obj">354563</h2>
<h2 class="box" :class="[a,b,c]" :style="style">354563</h2>
</div>
<script>
new Vue({
el:"#root",
data:{
title:"2021-07-11",
myStyle:'classA',
hasB:true,
hasC:true,
a:"classA",
b:"classB",
c:"classC",
size:40,
style:"border:10px solid",
obj:{
fontSize:"16px",
color:"blue",
}
}
})
</script>
9、条件渲染
条件渲染:1.v-if
写法:(1).v-if="表达式"
(2).v-else-if="表达式"
(3).v-else="表达式"
实用于:切换频率较低的场景。特点:不展现的 DOM 元素间接被移除。留神:v-if 能够和:v-else-if、v-else 一起应用,但要求构造不能被“打断”。2.v-show
写法:v-show="表达式"
实用于:切换频率较高的场景。特点:不展现的 DOM 元素未被移除,仅仅是应用款式暗藏掉
3. 备注:应用 v -if 的时,元素可能无奈获取到,而应用 v -show 肯定能够获取到。
<body>
<div id="root">
<h2> 今天天气很{{isHot ? '酷热':'凉快'}}</h2>
<button @click="isHot=!isHot"> 切换天气 </button>
<!-- v-show 条件渲染 -->
<div v-show="isHot">
<img src="https://s3.ax1x.com/2020/12/13/reC1IA.jpg" alt="">
<span> 倡议:心情天然就会凉 </span>
</div>
<div v-show="!isHot">
<img src="https://s3.ax1x.com/2020/12/13/reCaqg.jpg" alt="">
<span> 倡议:妈妈通知你要穿秋裤了 </span>
</div>
<!-- v-if v-else-if v-else 必须是间断的 -->
<div v-if="isHot">
<img src="https://s3.ax1x.com/2020/12/13/reC1IA.jpg" alt=""><br/>
<span> 倡议:心情天然就会凉 </span>
</div>
<div v-else>
<img src="https://s3.ax1x.com/2020/12/13/reCaqg.jpg" alt=""><br/>
<span> 倡议:妈妈通知你要穿秋裤了 </span>
</div>
</div>
<script>
const vm = new Vue({
el:"#root",
data:{isHot:true,}
})
</script>
</body>
10、列表渲染
10.1 根本列表
v-for 指令:
1. 用于展现列表数据
2. 语法:v-for="(item, index) in xxx" :key="yyy"
3. 可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)
<body>
<div id="root">
<!-- v-for 遍历数组 -->
<ul>
<li v-for="(p,index) in persons" :key="p.id">{{p.name}}--{{p.sex}}--{{p.age}}</li>
</ul>
<!-- v-for 遍历对象 -->
<ol>
<li v-for="(value,key) in car" :key="value">{{value}}</li>
</ol>
<!-- v-for 遍历字符串 -->
<ol>
<li v-for="(value,index) in str" :key="value">{{value}}---{{index}}</li>
</ol>
<!-- v-for 遍历数字 -->
<ol>
<li v-for="(value,index) in num" :key="value">{{value}}---{{index}}</li>
</ol>
</div>
<script>
const vm = new Vue({
el: "#root",
data: {
persons: [{
id: '001',
name: '老刘',
age: 20,
sex: '男'
},
{
id: '002',
name: '老李',
age: 19,
sex: '女'
},
{
id: '003',
name: '老王',
age: 18,
sex: '男'
},
{
id: '004',
name: '老张',
age: 17,
sex: '女'
},
],
car: {
name: '飞驰 c63',
price: '60 万',
color: '灰色'
},
str: 'abcde',
num:5,
}
})
</script>
</body>
应用 template 进行 v -for 遍历:template 会不显示
[
v-for
与v-if
一起应用]:
当它们处于同一节点,v-for
的优先级比 v-if
更高,这意味着 v-if
将别离反复运行于每个 v-for
循环中
<div id="app">
<template v-for="n in 5">
<p>{{n}}------ 你好 </p>
</template>
</div>
<script>
var app = new Vue({el:"#app",});
</script>
10.2 key 的原理
面试题:react、vue 中的 key 有什么作用?(key 的外部原理)1. 虚构 DOM 中 key 的作用:key 是虚构 DOM 对象的标识,当数据发生变化时,Vue 会依据【新数据】生成【新的虚构 DOM】,
随后 Vue 进行【新虚构 DOM】与【旧虚构 DOM】的差别比拟,比拟规定如下:2. 比照规定:(1). 旧虚构 DOM 中找到了与新虚构 DOM 雷同的 key:①. 若虚构 DOM 中内容没变, 间接应用之前的实在 DOM!②. 若虚构 DOM 中内容变了, 则生成新的实在 DOM,随后替换掉页面中之前的实在 DOM。(2). 旧虚构 DOM 中未找到与新虚构 DOM 雷同的 key
创立新的实在 DOM,随后渲染到到页面。3. 用 index 作为 key 可能会引发的问题:1. 若对数据进行:逆序增加、逆序删除等毁坏程序操作:
会产生没有必要的实在 DOM 更新 ==> 界面成果没问题, 但效率低。2. 如果构造中还蕴含输出类的 DOM:会产生谬误 DOM 更新 ==> 界面有问题。4. 开发中如何抉择 key?:
1. 最好应用每条数据的惟一标识作为 key, 比方 id、手机号、身份证号、学号等惟一值。2. 如果不存在对数据的逆序增加、逆序删除等毁坏程序操作,仅用于渲染列表用于展现,应用 index 作为 key 是没有问题的。
如果 input 输入框不加 key 属性,input 输入框会复用,呈现凌乱的景象
<div id="root">
<!-- 遍历数组 -->
<h2> 人员列表(遍历数组)</h2>
<button @click.once="add"> 增加一个老刘 </button>
<ul>
<li v-for="(p,index) of persons" :key="index">
{{p.name}}-{{p.age}}
<input type="text">
</li>
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el:'#root',
data:{
persons:[{id:'001',name:'张三',age:18},
{id:'002',name:'李四',age:19},
{id:'003',name:'王五',age:20}
]
},
methods: {add(){const p = {id:'004',name:'老刘',age:40}
this.persons.unshift(p)
}
},
})
</script>
10.3 列表过滤
<body>
<!--
想要对数据加工后再展现,且不想毁坏原数据,最好用计算属性。-->
<!-- 筹备好一个容器 -->
<div id="root">
<h2> 人员列表 </h2>
<input v-model="keyWord" type="text" placeholder="请输出姓名">
<ul>
<li v-for="(p,index) in fmtPersons" :key="p.id">
{{p.name}}--{{p.sex}}--{{p.age}}岁
</li>
</ul>
</div>
<script type="text/javascript">
var app = new Vue({
el: '#root',
data: {
keyWord: '',
persons: [{
id: '001',
name: '马冬梅',
age: 35,
sex: '女'
},
{
id: '002',
name: '周冬雨',
age: 20,
sex: '女'
},
{
id: '003',
name: '周杰伦',
age: 41,
sex: '男'
},
{
id: '004',
name: '温兆伦',
age: 25,
sex: '男'
},
]
},
// 应用 computed 过滤,劣势:不影响原数据
computed: {
//fmtPersons 是挂载在 Vue 实例上的属性
fmtPersons() {
const {
persons,
keyWord
} = this
return persons.filter(p => p.name.indexOf(keyWord) !== -1)
}
}
// 在 watch 批改原数据,会导致原数据的失落
/* watch:{keyWord(value){const arr = this.persons.filter( p => p.name.indexOf(value) !== -1)
this.persons = arr
}
} */
})
</script>
显示过滤排序后的后果
1、能够应用计算属性
2、应用办法 v -for=‘item in set(data)’<ul v-for="set in sets">
<li v-for="n in even(set)">{{n}}</li>
</ul>
// 数组中嵌套数组不适宜应用计算属性
data: {sets: [[ 1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]
},
methods: {even: function (numbers) {return numbers.filter(function (number) {return number % 2 === 0})
}
}
10.4 列表排序
<body>
<div id="root">
<!--
想要对数据加工后再展现,且不想毁坏原数据,最好用计算属性。-->
<input type="text" v-model="keyWord" placeholder="请输出姓名">
<button @click="sortType=1"> 年龄升序↑</button>
<button @click="sortType=2"> 年龄降序↓</button>
<button @click="sortType=0"> 原程序 </button>
<ul>
<li v-for="(p,index) in fmtPersons" :key="p.id">
{{p.name}}---{{p.sex}}---{{p.age}}
</li>
</ul>
</div>
<script>
var vm = new Vue({
el:"#root",
data:{
keyWord:'',
sortType:0, // 0 原程序 1 升序 2 降序
persons: [{
id: '001',
name: '马冬梅',
age: 35,
sex: '女'
},
{
id: '002',
name: '周冬雨',
age: 20,
sex: '女'
},
{
id: '003',
name: '周杰伦',
age: 41,
sex: '男'
},
{
id: '004',
name: '温兆伦',
age: 25,
sex: '男'
},
]
},
computed:{fmtPersons(){
// 1. 获取 data 中的数据
const {
persons,
keyWord,
sortType
}=this;
// 2. 依据关键词过滤
let arr = persons.filter(p=>p.name.indexOf(keyWord)!==-1)
// 3. 按须要排序为 0 时不进行排序
if(sortType){arr.sort((a,b)=>{
// 排序时进行判断如果为 1 则升序
if(sortType===1){
return a.age-b.age;
// 为 2 进行降序
}else{return b.age-a.age;}
})
}
return arr;
}
},
});
</script>
</body>
10.5 列表更新的问题
v-for 遍历数组或对象,VUE 无奈监测数组或对象中的值的变动,失常状况批改数组元素的值,vue 不能动静的更新。起因是:简单数据类型保留的是对象的援用,当扭转对象的键或值,对象的援用并没有发生变化。vue 对于数组的办法作出一层封装,能够扭转原数组的办法,vue 是能够监测到变动,有 push pop shift unshift reverse splice sort
对于非变更的办法,如 filter concat slice 返回新数组 将新的值赋给原数组即可
// 管制台上间接批改 app.list[]元素,不会动静刷新
// 解决方案
// 1. 调用 splice 办法,Vue 进行包裹可间接动静刷新
// 2. 调用 $forceUpdate()办法强制进行刷新
// 3.Vue.set(app.list,indexOf,newValue)
// 4.app.$set(app.list,indexOf,newValue)
<body>
<!--
Vue 数据绑定的原理
1. vue 会监督 data 中所有档次对象的属性
2. 对象中的属性数据通过增加 set 办法来来实现监督
3. 数组中也实现了监督: 重写数组一系列更新元素的办法,做了两件事:1). 调用原生对应的办法对数组进行解决
2). 去更新界面
-->
<!-- 筹备好一个容器 -->
<div id="root">
<h2> 人员列表 </h2>
<input v-model="keyWord" type="text" placeholder="请输出姓名">
<button @click="sortType = 1"> 年龄升序↓</button>
<button @click="sortType = 2"> 年龄降序↓</button>
<button @click="sortType = 0"> 原程序 </button>
<button @click="updateMei"> 更改马冬梅的信息 </button>
<ul>
<li v-for="(p,index) in fmtPersons" :key="p.id">
{{p.name}}--{{p.sex}}--{{p.age}}岁
</li>
</ul>
</div>
<script type="text/javascript">
const vm = new Vue({
el: '#root',
data: {
keyWord: '',
sortType: 0, // 0 原程序 1 升序 2 降序
persons: [{
id: '001',
name: '马冬梅',
age: 35,
sex: '女',
a: {
b: {
c: {
d: {e: 1}
}
}
}
},
{
id: '002',
name: '周冬雨',
age: 20,
sex: '女'
},
{
id: '003',
name: '周杰伦',
age: 41,
sex: '男'
},
{
id: '004',
name: '温兆伦',
age: 25,
sex: '男'
},
]
},
methods: {updateMei() {console.log(this)
// 为每个属性增加了 get set 函数
// this.persons[0].name = '小佩奇' // 代码见效
// this.persons[0].age = 99 // 代码见效
// this.persons[0].sex = '男' // 代码见效
this.persons[0] = {
name: '小佩奇',
age: 99,
sex: '男'
} // 不见效
}
},
// 应用 computed 过滤,劣势:不影响原数据
computed: {fmtPersons() {
const {
persons,
keyWord,
sortType
} = this
// 依据关键词过滤
let arr = persons.filter(p => p.name.indexOf(keyWord) !== -1)
// 若须要排序
if (sortType) {
// 排序
arr.sort((a, b) => {if (sortType === 1) return a.age - b.age
else return b.age - a.age
})
}
return arr
}
}
})
</script>
</body>
10.6 模仿数据监测
<script type="text/javascript">
// 1.data 中创立的数据
let data = {
name: 'fyl',
age: 18
}
// 2. 创立一个监督的实例对象,用于监督 data 中属性的变动
const obs = new Observer(data)
console.log(obs);
// 3. 筹备一个 vm 实例化对象,收集实例化下的 data 放入_data 中,而后再挂在到 vm 上
let vm = {}
// data 领有 get set 下次应用 vm._data 扭转,理论扭转 data 的数据,去解析
vm._data = data = obs
//4. 类
function Observer(obj) {
// 汇总对象中所有的属性造成一个数组
const keys = Object.keys(obj)
// 遍历加上 getter 和 setter
keys.forEach(k => {
Object.defineProperty(this, k, {get() {return obj[k]
},
set(newVal) {console.log(`obj 的 ${k}被批改了,我要去解析模板,生成虚构 DOM`);
obj[k] = newVal
}
})
})
}
</script>
10.7 Vue.set 的应用
向响应式对象中增加一个 property,并确保这个新 property 同样是响应式的,且触发视图更新。它必须用于向响应式对象上增加新 property,因为 Vue 无奈探测一般的新增 property (比方 this.myObject.newProperty = 'hi')
留神对象不能是 Vue 实例,或者 Vue 实例的根数据对象。
this.student.sex = '女' // 间接应用不是响应式
// 两种形式
Vue.set(this.student,'sex','男')
this.$set(this.student,'sex','男')
10.8 总结 Vue 数据监测
Vue 监督数据的原理:1. vue 会监督 data 中所有档次的数据。2. 如何监测对象中的数据?通过 setter 实现监督,且要在 new Vue 时就传入要监测的数据。(1). 对象中后追加的属性,Vue 默认不做响应式解决
(2). 如需给后增加的属性做响应式,请应用如下 API:Vue.set(target,propertyName/index,value) 或
vm.$set(target,propertyName/index,value)
3. 如何监测数组中的数据?通过包裹数组更新元素的办法实现,实质就是做了两件事:(1). 调用原生对应的办法对数组进行更新。(2). 从新解析模板,进而更新页面。4. 在 Vue 批改数组中的某个元素肯定要用如下办法:1. 应用这些 API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
2.Vue.set() 或 vm.$set() (对象, 数组的索引, 增加索引的内容)
特地留神:Vue.set() 和 vm.$set() 不能给 vm 或 vm 的根数据对象 增加属性!!!
11、收集表单信息
收集表单数据:若:<input type="text"/>,则 v -model 收集的是 value 值,用户输出的就是 value 值。若:<input type="radio"/>,则 v -model 收集的是 value 值,且要给标签配置 value 值。不加 name 属性也可
若:<input type="checkbox"/>
1. 没有配置 input 的 value 属性,那么收集的就是 checked(勾选 or 未勾选,是布尔值)2. 配置 input 的 value 属性:
(1)v-model 的初始值是非数组,那么收集的就是 checked(勾选 or 未勾选,是布尔值)(2)v-model 的初始值是数组,那么收集的的就是 value 组成的数组
备注:v-model 的三个修饰符:lazy:失去焦点再收集数据
number:输出字符串转为无效的数字
trim:输出首尾空格过滤
<body>
<!-- 筹备好一个容器 -->
<div id="root">
<form @submit.prevent="submit">
账号:<input type="text" v-model="userInfo.account">
<br /><br />
明码:<input type="password" v-model="userInfo.password">
<br /><br />
性别:男 <input type="radio" name="sex" v-model="userInfo.sex" value="male">
女 <input type="radio" name="sex" v-model="userInfo.sex" value="female">
<br /><br />
喜好:抽烟 <input type="checkbox" v-model="userInfo.hobby" value="smoke">
喝酒 <input type="checkbox" v-model="userInfo.hobby" value="drink">
开车 <input type="checkbox" v-model="userInfo.hobby" value="drive">
<br /><br />
所属校区:<select v-model="userInfo.city">
<option value=""> 请抉择校区 </option>
<option value="beijing"> 北京 </option>
<option value="shanghai"> 上海 </option>
<option value="shenzhen"> 深圳 </option>
<option value="wuhan"> 武汉 </option>
</select>
<br /><br />
其余信息:<textarea v-model="userInfo.other" cols="30" rows="10"></textarea>
<br /><br />
<input v-model="userInfo.agree" type="checkbox"> 浏览并承受 <a href="http://www.atguigu.com">《用户协定》</a>
<br /><br />
<button> 提交 </button>
</form>
</div>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
userInfo: {
account: '',
password: '',
sex: '',
hobby: [],
city: '',
other: '',
agree: false,
}
},
methods: {submit() {console.log(this.userInfo)
}
}
})
</script>
</body>
12、过滤器
过滤器:定义:对要显示的数据进行特定格式化后再显示(实用于一些简略逻辑的解决)。语法:1. 注册过滤器:Vue.filter(name,callback) 或 new Vue{filters:{}}
2. 应用过滤器:{{xxx | 过滤器名}} 或 v-bind: 属性 = "xxx | 过滤器名"
备注:1. 过滤器也能够接管额定参数、多个过滤器也能够串联(如 time | timeFormater('YYYY_MM_DD') | mySlice)
2. 并没有扭转本来的数据, 是产生新的对应的数据
3. 部分有,优先应用部分过滤器
<div id="root">
<h2> 显示格式化后的工夫 </h2>
<!-- 计算属性实现 -->
<h3> 当初是:{{fmtTime}}</h3>
<!-- methods 实现 -->
<h3> 当初是:{{getFmtTime()}}</h3>
<!-- 过滤器实现 -->
<h3> 当初是:{{time | timeFormater}}</h3>
<!-- 过滤器实现(传参)-->
<h3> 当初是:{{time | timeFormater('YYYY_MM_DD') | mySlice}}</h3>
<h3 :x="msg | mySlice"> 尚硅谷 </h3>
</div>
<div id="root2">
<h2>{{msg | mySlice}}</h2>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
// 全局过滤器
Vue.filter('mySlice',function(value){return value.slice(0,4)
})
new Vue({
el:'#root',
data:{
time:1621561377603, // 工夫戳
msg:'你好,尚硅谷'
},
computed: {fmtTime(){return dayjs(this.time).format('YYYY 年 MM 月 DD 日 HH:mm:ss')
}
},
methods: {getFmtTime(){return dayjs(this.time).format('YYYY 年 MM 月 DD 日 HH:mm:ss')
}
},
// 部分过滤器
filters:{timeFormater(value,str='YYYY 年 MM 月 DD 日 HH:mm:ss'){// console.log('@',value)
return dayjs(value).format(str)
}
}
})
new Vue({
el:'#root2',
data:{msg:'hello,atguigu!'}
})
</script>
13、内置指令及自定义指令
13.1 内置指令
罕用内置指令
v-text : 更新元素的 innerText 相似于插值
1. 作用:向其所在的节点中渲染文本内容。2. 与插值语法的区别:v-text 会替换掉节点中的内容,{{xx}}则不会。v-html : 更新元素的 innerHTML 内容按一般 HTML 插入 - 不会作为 Vue 模板进行编译
1. 作用:向指定节点中渲染蕴含 html 构造的内容。2. 与插值语法的区别:(1).v-html 会替换掉节点中所有的内容,{{xx}}则不会。(2).v-html 能够辨认 html 构造。3. 重大留神:v-html 有安全性问题!!!!(1). 在网站上动静渲染任意 HTML 是十分危险的,容易导致 XSS 攻打。(2). 肯定要在可信的内容上应用 v -html,永不要用在用户提交的内容上!v-cloak : 这个指令放弃在元素上直到关联实例完结编译(没有值)1. 实质是一个非凡属性,Vue 实例创立结束并接管容器后,会删掉 v -cloak 属性。2. 应用 css 配合 v -cloak 能够解决网速慢时页面展现出 {{xxx}} 的问题。v-once 指令:1.v-once 所在节点在首次动静渲染后,就视为动态内容了。2. 当前数据的扭转不会引起 v -once 所在构造的更新,能够用于优化性能。v-pre 指令:1. 跳过其所在节点的编译过程。2. 可利用它跳过:没有应用指令语法、没有应用插值语法的节点,会放慢编译。v-if : 条件渲染(动态控制节点是否存在)v-else : 条件渲染(动态控制节点是否存在)v-else-if:
v-show : 条件渲染 (动态控制 display)
v-for : 遍历数组 / 对象
v-on : 绑定事件监听, 可简写为 @ 修饰符
v-bind:xxx : 强制绑定解析表达式, 可简写为 :xxx 修饰符
v-model: 双向数据绑定 修饰符 .lazy .number .trim
v-slot(#) : 插槽名
<style>
[v-cloak]{display:none;}
</style>
<!-- 引入 Vue -->
</head>
<body>
<!--
v-cloak 指令(没有值):1. 实质是一个非凡属性,Vue 实例创立结束并接管容器后,会删掉 v -cloak 属性。2. 应用 css 配合 v -cloak 能够解决网速慢时页面展现出 {{xxx}} 的问题。-->
<!-- 筹备好一个容器 -->
<div id="root">
<h2 v-cloak>{{name}}</h2>
</div>
<script type="text/javascript" src="http://localhost:8080/resource/5s/vue.js"></script>
</body>
<script type="text/javascript">
console.log(1)
Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提醒。new Vue({
el:'#root',
data:{name:'尚硅谷'}
})
</script>
13.2 自定义指令
需要 1:定义一个 v -big 指令,和 v -text 性能相似,但会把绑定的数值放大 10 倍。需要 2:定义一个 v -fbind 指令,和 v -bind 性能相似,但能够让其所绑定的 input 元素默认获取焦点。自定义指令总结:一、定义语法:(1). 部分指令:new Vue({directives:{指令名: 配置对象} }) 或 new Vue({directives{指令名: 回调函数}}) (2). 全局指令:Vue.directive(指令名, 配置对象) 或 Vue.directive(指令名, 回调函数)
留神:配置对象应用函数的写法,默认是 bind 和 update 外面的内容
二、配置对象中罕用的 3 个回调:(1).bind:指令与元素胜利绑定时调用。(2).inserted:指令所在元素被插入页面时调用。(3).update:指令所在模板构造被从新解析时调用。不必 (4)componentUpdated:指令所在组件的 VNode 及其子 VNode 全副更新后调用。不必 (5)unbind:只调用一次,指令与元素解绑时调用。三、备注:1. 指令定义时不加 v -,但应用时要加 v -;多个单词用 v -big-number
2. 指令名如果是多个单词,要应用 kebab-case 命名形式,不要用 camelCase 命名。指令名多个单词用双引号
<div id="root">
<h2>{{name}}</h2>
<h2> 以后的 n 值是:<span v-text="n"></span> </h2>
<!-- <h2> 放大 10 倍后的 n 值是:<span v-big-number="n"></span> </h2> -->
<h2> 放大 10 倍后的 n 值是:<span v-big="n"></span> </h2>
<button @click="n++"> 点我 n +1</button>
<hr />
<input type="text" v-fbind:value="n">
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
// 定义全局指令
/* Vue.directive('fbind',{
// 指令与元素胜利绑定时(一上来)bind(element,binding){element.value = binding.value},
// 指令所在元素被插入页面时
inserted(element,binding){element.focus()
},
// 指令所在的模板被从新解析时
update(element,binding){element.value = binding.value}
}) */
new Vue({
el: '#root',
data: {
name: '尚硅谷',
n: 1
},
directives: {
//big 函数何时会被调用?1. 指令与元素胜利绑定时(一上来)。2. 指令所在的模板被从新解析时。/* 'big-number'(element,binding){// console.log('big')
element.innerText = binding.value * 10
}, */
big(element, binding) {console.log('big', this) // 留神此处的 this 是 window
// console.log('big')
element.innerText = binding.value * 10
},
fbind: {
// 指令与元素胜利绑定时(一上来)bind(element, binding) {element.value = binding.value},
// 指令所在元素被插入页面时
inserted(element, binding) {element.focus()
},
// 指令所在的模板被从新解析时
update(element, binding) {element.value = binding.value}
},
// fbind:{
// // 只调用一次,指令第一次绑定到元素时调用。在这里能够进行一次性的初始化设置。// bind(el,binding,vnode,oldVnode){
// // 指令所绑定的元素,能够用来间接操作 DOM
// console.log(el);
// // 一个对象
// console.log(binding);
// // Vue 编译生成的虚构节点。// console.log(vnode);
// // 上一个虚构节点
// console.log(oldVnode);
// el.value = binding.value
// },
// // 被绑定元素插入父节点时调用 (仅保障父节点存在,但不肯定已被插入文档中)。// inserted(el){// el.focus()
// },
// // 所在组件的 VNode 更新时调用,然而可能产生在其子 VNode 更新之前。// update(el,binding){
// el.value = binding.value
// },
// 指令所在组件的 VNode 及其子 VNode 全副更新后调用。// componentUpdated() {//},
// 只调用一次,指令与元素解绑时调用。// unbind() {//}
// }
}
})
14、生命周期
罕用的生命周期钩子:1.mounted: 发送 ajax 申请、启动定时器、绑定自定义事件、订阅音讯等【初始化操作】。2.beforeDestroy: 革除定时器、解绑自定义事件、勾销订阅音讯等【收尾工作】。对于销毁 Vue 实例
1. 销毁后借助 Vue 开发者工具看不到任何信息。2. 销毁后自定义事件会生效,但原生 DOM 事件仍然无效。3. 个别不会在 beforeDestroy 操作数据,因为即使操作数据,也不会再触发更新流程了。
15、非单文件组件
以往是多个 html 文件有 css 和 js 文件,头部能够复制应用,不是很不便,可不可以通过导入的模式,间接应用。那么应用组件
组件:实现利用中部分性能代码和资源的汇合 .vue 很多组件
15.1 组件的根本应用
Vue 中应用组件的三大步骤:一、定义组件(创立组件)
二、注册组件
三、应用组件(写组件标签)
一、如何定义一个组件?应用 Vue.extend(options)创立,其中 options 和 new Vue(options)时传入的那个 options 简直一样,但也有点区别;区别如下:1.el 不要写,为什么?——— 最终所有的组件都要通过一个 vm 的治理,由 vm 中的 el 决定服务哪个容器。2.data 必须写成函数,为什么?———— 防止组件被复用时,数据存在援用关系。备注:应用 template 能够配置组件构造。二、如何注册组件?1. 部分注册:靠 new Vue 的时候传入 components 选项 Vue.extend({}) 或者间接写选项对象{},在 components 中会调用 extend
2. 全局注册:靠 Vue.component('组件名', 组件)
三、编写组件标签:<school></school>
<!-- 筹备好一个容器 -->
<div id="root">
<hello></hello>
<hr>
<h1>{{msg}}</h1>
<hr>
<!-- 第三步:编写组件标签 -->
<school></school>
<hr>
<!-- 第三步:编写组件标签 -->
<student></student>
</div>
<div id="root2">
<hello></hello>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
// 第一步:创立 school 组件
const school = Vue.extend({
template:`
<div class="demo">
<h2> 学校名称:{{schoolName}}</h2>
<h2> 学校地址:{{address}}</h2>
<button @click="showName"> 点我提醒学校名 </button>
</div>
`,
// el:'#root', // 组件定义时,肯定不要写 el 配置项,因为最终所有的组件都要被一个 vm 治理,由 vm 决定服务于哪个容器。data(){
return {
schoolName:'尚硅谷',
address:'北京昌平'
}
},
methods: {showName(){alert(this.schoolName)
}
},
})
// 第一步:创立 student 组件
const student = Vue.extend({
template:`
<div>
<h2> 学生姓名:{{studentName}}</h2>
<h2> 学生年龄:{{age}}</h2>
</div>
`,
data(){
return {
studentName:'张三',
age:18
}
}
})
// 第一步:创立 hello 组件
const hello = Vue.extend({
template:`
<div>
<h2> 你好啊!{{name}}</h2>
</div>
`,
data(){
return {name:'Tom'}
}
})
// 第二步:全局注册组件
Vue.component('hello',hello)
// 创立 vm
new Vue({
el:'#root',
data:{msg:'你好啊!'},
// 第二步:注册组件(部分注册)components:{
school,
student
}
})
new Vue({el:'#root2',})
</script>
15.2 留神点
几个留神点:1. 对于组件名:
一个单词组成:第一种写法(首字母小写):school
第二种写法(首字母大写):School
多个单词组成:第一种写法(kebab-case 命名):my-school
第二种写法(CamelCase 命名):MySchool (须要 Vue 脚手架反对)
备注:(1). 组件名尽可能回避 HTML 中已有的元素名称,例如:h2、H2 都不行。(2). 能够应用 name 配置项指定组件在开发者工具中出现的名字。2. 对于组件标签:
第一种写法:<school></school>
第二种写法:<school/>
备注:不必应用脚手架时,<school/> 会导致后续组件不能渲染。3. 一个简写形式:const school = Vue.extend(options) 可简写为:const school = options
15.3 组件的嵌套
<body>
<!-- 筹备好一个容器 -->
<div id="root">
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提醒。// 定义 student 组件
const student = Vue.extend({
name:'student',
template:`
<div>
<h2> 学生姓名:{{name}}</h2>
<h2> 学生年龄:{{age}}</h2>
</div>
`,
data(){
return {
name:'尚硅谷',
age:18
}
}
})
// 定义 school 组件
const school = Vue.extend({
name:'school',
template:`
<div>
<h2> 学校名称:{{name}}</h2>
<h2> 学校地址:{{address}}</h2>
<student></student>
</div>
`,
data(){
return {
name:'尚硅谷',
address:'北京'
}
},
// 注册组件(部分)components:{student}
})
// 定义 hello 组件
const hello = Vue.extend({template:`<h1>{{msg}}</h1>`,
data(){
return {msg:'欢送来到尚硅谷学习!'}
}
})
// 定义 app 组件
const app = Vue.extend({
template:`
<div>
<hello></hello>
<school></school>
</div>
`,
components:{
school,
hello
}
})
// 创立 vm
new Vue({
template:'<app></app>',
el:'#root',
// 注册组件(部分)components:{app}
})
</script>
15.3 VueComponent 构造函数
对于 VueComponent:1.school 组件实质是一个名为 VueComponent 的构造函数,且不是程序员定义的,是 Vue.extend 调用生成的。2. 咱们只须要写 <school/> 或 <school></school>,Vue 解析时会帮咱们创立 school 组件的实例对象,即 Vue 帮咱们执行的:new VueComponent(options)。3. 特地留神:每次调用 Vue.extend,返回的都是一个全新的 VueComponent!!!!4. 对于 this 指向:(1). 组件配置中:data 函数、methods 中的函数、watch 中的函数、computed 中的函数 它们的 this 均是【VueComponent 实例对象】。(2).new Vue(options)配置中:data 函数、methods 中的函数、watch 中的函数、computed 中的函数 它们的 this 均是【Vue 实例对象】。5.VueComponent 的实例对象,当前简称 vc(也可称之为:组件实例对象)。Vue 的实例对象,当前简称 vm。
15.4 一个重要的内置关系 vc 和 vm 的原型
1. 一个重要的内置关系:VueComponent.prototype.__proto__ === Vue.prototype
2. 为什么要有这个关系:让组件实例对象(vc)能够拜访到 Vue 原型上的属性、办法。
16、单文件组件
非单文件组件
通过 Vue.extend 去注册组件,在 new Vue 中的 components 配置,间接在 html 中应用 <school>
不易复用,当初将功能模块拆分
单文件组件
vue 文件 1.webpack 解析 2. 脚手架
|-main.js 注册 app 组件 挂载 app 应用 template 注册 app 配置 template(<app/>)不必在 html 中写 <App/>
|-App.vue app 中引入 components 中组件 注册 school 和 student
|-index.html 引入 main.js 和 Vue
|-components |-student
有 template script export default const student = Vue.extend({}) 可简写为 export default{}
|-school
17、剖析脚手架
脚手架
不应用 render 函数 在 main.js 中写
原先写法
html:
<body>
<!-- 筹备一个容器 -->
<div id="root"></div>
<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript" src="./main.js"></script>
</body>
main.js:
import App from './App.vue'
new Vue({
el:'#root',
template:`<App></App>`,
components:{App},
})
App.vue:
<template>
<div>
<School></School>
<Student></Student>
</div>
</template>
<script>
// 引入组件
import School from './School.vue'
import Student from './Student.vue'
export default {
name:'App',
components:{
School,
Student
}
}
</script>
脚手架写法 短少模板解析器
只在 main.js 中写
// 只引入了实时运行版的 vue
//import Vue from 'vue'
// 完整版的蕴含模板解析器 就能够运行
import Vue from 'vue/dist/vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({// render: h => h(App),
components:{App},
template:`<App/>`
}).$mount('#app')
为什么:Vue = Vue 外围(2/3) + 模板解析器(1/3) 所以用 render 进行渲染 打包后生成最终代码不须要模板解析器的局部
Vue 选项中的 render
函数若存在,则 Vue 构造函数不会从 template
选项或通过 el
选项指定的挂载元素中提取出的 HTML 模板编译渲染函数。
// 引入 Vue
import Vue from 'vue'
// 引入 App 组件,它是所有组件的父组件
import App from './App.vue'
// 敞开 vue 的生产提醒
Vue.config.productionTip = false
/*
对于不同版本的 Vue:1.vue.js 与 vue.runtime.xxx.js 的区别:(1).vue.js 是完整版的 Vue,蕴含:外围性能 + 模板解析器。(2).vue.runtime.xxx.js 是运行版的 Vue,只蕴含:外围性能;没有模板解析器。2. 因为 vue.runtime.xxx.js 没有模板解析器,所以不能应用 template 配置项,须要应用
render 函数接管到的 createElement 函数去指定具体内容。*/
// 创立 Vue 实例对象 ---vm
new Vue({
el:'#app',
//render 函数实现了这个性能:将 App 组件放入容器中 返回的是虚构 dom
render: h => h(App),
// render:q=> q('h1','你好啊')
// template:`<h1> 你好啊 </h1>`,
// template:`<App/>`,
// components:{App},
})
18、罕用办法
18.1 ref 属性
ref 在一般元素上就是实在的 Dom 节点
组件上就是组件的示例对象(vc)
18.2 props 属性
prop 承受的值切实 data 赋值之前
// 简略申明接管
// props:['name','age','sex']
// 接管的同时对数据进行类型限度
/* props:{
name:String,
age:Number,
sex:String
} */
// 接管的同时对数据:进行类型限度 + 默认值的指定 + 必要性的限度
props:{
name:{
type:String, //name 的类型是字符串
required:true, //name 是必要的
},
age:{
type:Number,
default:99 // 默认值
},
sex:{
type:String,
required:true
}
}
18.3 混入 Mixin
混入的形式:办法本身有用本身的 生命周期是都会执行,混入的钩子会提前调用
还能够自定义混入
- 性能:能够把多个组件共用的配置提取成一个混入对象
-
应用形式:
第一步定义混合:
{data(){....}, methods:{....} .... }
第二步应用混入:
全局混入:
Vue.mixin(xxx)
部分混入:mixins:['xxx']
18.4 插件
- 性能:用于加强 Vue
- 实质:蕴含 install 办法的一个对象,install 的第一个参数是 Vue,第二个当前的参数是插件使用者传递的数据。
通过 install 办法给 Vue 或 Vue 实例增加办法, 定义全局指令 混入 过滤器
- 定义插件:
对象.install = function (Vue, options) {
// 1. 增加全局过滤器
Vue.filter(....)
// 2. 增加全局指令
Vue.directive(....)
// 3. 配置全局混入(合)
Vue.mixin(....)
// 4. 增加实例办法
Vue.prototype.$myMethod = function () {...}
Vue.prototype.$myProperty = xxxx
}
- 应用插件:
Vue.use()
import Vue from 'vue'
import loadingComponent from '@/components/Loading/index.vue'
// 应用 extend 创立 组件的 vc 构造函数
const LoadingConstructor = Vue.extend(loadingComponent)
// 实例化 vc 挂载在 div 中
const instance = new LoadingConstructor({el: document.createElement('div')
})
// 在实例中挂载属性
instance.show = false // 默认暗藏
// 组件中应用 props 接管
const loading = {show(txt = '') { // 显示办法
instance.show = true
instance.text = txt || '拼命加载中'
document.body.appendChild(instance.$el)
},
hide() { // 暗藏办法
instance.show = false
}
}
export default {
//install 办法 有一个 Vue 形参
install() {if (!Vue.$loading) {Vue.$loading = loading}
// 通过混入 在 created 中 将实例化 $loading 挂载在 this 上
Vue.mixin({created() {this.$loading = Vue.$loading}
})
}
}
18.5 nextTick
- 语法:
this.$nextTick(回调函数)
- 作用:在下一次 DOM 更新完结后执行其指定的回调。
- 什么时候用:当扭转数据后,要基于更新后的新 DOM 进行某些操作时,要在 nextTick 所指定的回调函数中执行。
// 解析机会的问题 当 this.isEdit 数据变动, 并没有立刻更新 dom 而是执行完 focus 所以必须在 dom 更新后进行执行
if(todo.hasOwnProperty('isEdit')){todo.isEdit = true}else{// console.log('@')
this.$set(todo,'isEdit',true)
}
this.$nextTick(function(){this.$refs.inputTitle.focus()
})
19、事件总线
main.js
new Vue({render: h => h(App),
beforeCreate(){Vue.prototype.$bus = this}
}).$mount('#app')
绑定 触发 销毁
this.$bus.$on('name',callback=>{})
this.$bus.$emit('name',params)
this.$bus.$off()全副敞开(name)
公布订阅 pubsub.js
// 首先是先订阅音讯 hello, 定义触发的回调 第一个参数的回调的函数名,第二个是承受的数据
this.pubId = pubsub.subscribe('hello',(msgName,data)=>{console.log(this)
// console.log('有人公布了 hello 音讯,hello 音讯的回调执行了',msgName,data)
})
// 发送数据方,须要公布音讯,触发回调
pubsub.publish('hello',666)
// 勾销订阅 是勾销订阅号
pubsub.unsubscribe(this.pubId)
20、动画和适度
- 作用:在插入、更新或移除 DOM 元素时,在适合的时候给元素增加款式类名。
- 图示:<img src=”https://img04.sogoucdn.com/app/a/100520146/5990c1dff7dc7a8fb3b34b4462bd0105″ style=”width:60%” />
-
写法:
-
筹备好款式:
-
元素进入的款式:
- v-enter:进入的终点
- v-enter-active:进入过程中
- v-enter-to:进入的起点
-
元素来到的款式:
- v-leave:来到的终点
- v-leave-active:来到过程中
- v-leave-to:来到的起点
-
-
应用
<transition>
包裹要适度的元素,并配置 name 属性:<transition name="hello"> <h1 v-show="isShow"> 你好啊!</h1> </transition>
- 备注:若有多个元素须要适度,则须要应用:
<transition-group>
,且每个元素都要指定key
值。
-
<transition-group name="hello" appear>
<h1 v-show="!isShow" key="1"> 你好啊!</h1>
<h1 v-show="isShow" key="2"> 尚硅谷!</h1>
</transition-group>
应用 animate 动画库
<transition-group
appear
name="animate__animated animate__bounce"
enter-active-class="animate__swing"
leave-active-class="animate__backOutUp"
>
<h1 v-show="!isShow" key="1"> 你好啊!</h1>
<h1 v-show="isShow" key="2"> 尚硅谷!</h1>
</transition-group>
21、vue 脚手架配置代理
办法一
在 vue.config.js 中增加如下配置:
// 如果本地存在则不会转发
devServer:{proxy:"http://localhost:5000"}
阐明:
- 长处:配置简略,申请资源时间接发给前端(8080)即可。
- 毛病:不能配置多个代理,不能灵便的管制申请是否走代理。
- 工作形式:若依照上述配置代理,当申请了前端不存在的资源时,那么该申请会转发给服务器(优先匹配前端资源)
办法二
编写 vue.config.js 配置具体代理规定:
module.exports = {
devServer: {
proxy: {
'/api1': {// 匹配所有以 '/api1' 结尾的申请门路
target: 'http://localhost:5000',// 代理指标的根底门路
changeOrigin: true,
pathRewrite: {'^/api1': ''}// 将申请地址中的 api1 替换为空
},
'/api2': {// 匹配所有以 '/api2' 结尾的申请门路
target: 'http://localhost:5001',// 代理指标的根底门路
changeOrigin: true,
pathRewrite: {'^/api2': ''}
}
}
}
}
/*
changeOrigin 设置为 true 时,服务器收到的申请头中的 host 为:localhost:5000
changeOrigin 设置为 false 时,服务器收到的申请头中的 host 为:localhost:8080
changeOrigin 默认值为 true
*/
阐明:
- 长处:能够配置多个代理,且能够灵便的管制申请是否走代理。
- 毛病:配置稍微繁琐,申请资源时必须加前缀。
代码申请写法:
axios.get('http://localhost:8080/demo/cars')
//vue.config.js
'/demo': {
target: 'http://localhost:5001',
pathRewrite:{'^/demo':''},
// ws: true, // 用于反对 websocket
// changeOrigin: true // 用于管制申请头中的 host 值 true:host 值不是理论的
}
// 申请本地服务器,会转换撑 localhost:5001/cars
22、插槽
- 作用:让父组件能够向子组件指定地位插入 html 构造,也是一种组件间通信的形式,实用于 父组件 ===> 子组件。
- 分类:默认插槽、具名插槽、作用域插槽
-
应用形式:
-
默认插槽:
父组件中:<Category> <div>html 构造 1 </div> </Category> 子组件中:<template> <div> <!-- 定义插槽 --> <slot> 插槽默认内容...</slot> </div> </template>
-
具名插槽:
父组件中:<Category> <template slot="center"> <div>html 构造 1 </div> </template> <template v-slot:footer> <div>html 构造 2 </div> </template> </Category> 子组件中:<template> <div> <!-- 定义插槽 --> <slot name="center"> 插槽默认内容...</slot> <slot name="footer"> 插槽默认内容...</slot> </div> </template>
-
作用域插槽:
- 了解:<span style=”color:red”> 数据在组件的本身,但依据数据生成的构造须要组件的使用者来决定。</span>(games 数据在 Category 组件中,但应用数据所遍历进去的构造由 App 组件决定)
-
具体编码:
在 slot 上绑定属性,能够在父组件中应用子组件的数据,承受形式:
scope=“”
slot-scope=””
能够构造赋值
v-slot:default=””
default=””
父组件中:<Category> <template scope="scopeData"> <!-- 生成的是 ul 列表 --> <ul> <li v-for="g in scopeData.games" :key="g">{{g}}</li> </ul> </template> </Category> <Category> <template slot-scope="scopeData"> <!-- 生成的是 h4 题目 --> <h4 v-for="g in scopeData.games" :key="g">{{g}}</h4> </template> </Category> 子组件中:<template> <div> <slot :games="games"></slot> </div> </template> <script> export default { name:'Category', props:['title'], // 数据在子组件本身 data() { return {games:['红色警戒','穿梭前线','劲舞团','超级玛丽'] } }, } </script>
-
23、Vuex
1. 概念
在 Vue 中实现集中式状态(数据)治理的一个 Vue 插件,对 vue 利用中多个组件的共享状态进行集中式的治理(读 / 写),也是一种组件间通信的形式,且实用于任意组件间通信。
2. 何时应用?
多个组件须要共享数据时
3. 搭建 vuex 环境
-
创立文件:
src/store/index.js
// 引入 Vue 外围库 import Vue from 'vue' // 引入 Vuex import Vuex from 'vuex' // 利用 Vuex 插件 Vue.use(Vuex) // 筹备 actions 对象——响应组件中用户的动作 const actions = {} // 筹备 mutations 对象——批改 state 中的数据 const mutations = {} // 筹备 state 对象——保留具体的数据 const state = {} // 创立并裸露 store export default new Vuex.Store({ actions, mutations, state })
-
在
main.js
中创立 vm 时传入store
配置项...... // 引入 store import store from './store' ...... // 创立 vm new Vue({ el:'#app', render: h => h(App), store })
4. 根本应用
-
初始化数据、配置
actions
、配置mutations
,操作文件store.js
// 引入 Vue 外围库 import Vue from 'vue' // 引入 Vuex import Vuex from 'vuex' // 援用 Vuex Vue.use(Vuex) const actions = { // 响应组件中加的动作 jia(context,value){// console.log('actions 中的 jia 被调用了',miniStore,value) context.commit('JIA',value) }, } const mutations = { // 执行加 JIA(state,value){// console.log('mutations 中的 JIA 被调用了',state,value) state.sum += value } } // 初始化数据 const state = {sum:0} // 创立并裸露 store export default new Vuex.Store({ actions, mutations, state, })
- 组件中读取 vuex 中的数据:
$store.state.sum
-
组件中批改 vuex 中的数据:
$store.dispatch('action 中的办法名', 数据)
或$store.commit('mutations 中的办法名', 数据)
备注:若没有网络申请或其余业务逻辑,组件中也能够越过 actions,即不写
dispatch
,间接编写commit
5.getters 的应用
- 概念:当 state 中的数据须要通过加工后再应用时,能够应用 getters 加工。
-
在
store.js
中追加getters
配置...... const getters = {bigSum(state){return state.sum * 10} } // 创立并裸露 store export default new Vuex.Store({ ...... getters })
- 组件中读取数据:
$store.getters.bigSum
6. 四个 map 办法的应用
-
mapState 办法:用于帮忙咱们映射
state
中的数据为计算属性computed: { // 借助 mapState 生成计算属性:sum、school、subject(对象写法)...mapState({sum:'sum',school:'school',subject:'subject'}), // 借助 mapState 生成计算属性:sum、school、subject(数组写法)...mapState(['sum','school','subject']), },
-
mapGetters 办法:用于帮忙咱们映射
getters
中的数据为计算属性computed: { // 借助 mapGetters 生成计算属性:bigSum(对象写法)...mapGetters({bigSum:'bigSum'}), // 借助 mapGetters 生成计算属性:bigSum(数组写法)...mapGetters(['bigSum']) },
-
mapActions 办法:用于帮忙咱们生成与
actions
对话的办法,即:蕴含$store.dispatch(xxx)
的函数methods:{ // 靠 mapActions 生成:incrementOdd、incrementWait(对象模式)...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'}) // 靠 mapActions 生成:incrementOdd、incrementWait(数组模式)...mapActions(['jiaOdd','jiaWait']) }
-
mapMutations 办法:用于帮忙咱们生成与
mutations
对话的办法,即:蕴含$store.commit(xxx)
的函数,传参写在事件后 @click=xxx(data)methods:{ // 靠 mapActions 生成:increment、decrement(对象模式)...mapMutations({increment:'JIA',decrement:'JIAN'}), // 靠 mapMutations 生成:JIA、JIAN(对象模式)...mapMutations(['JIA','JIAN']), }
备注:mapActions 与 mapMutations 应用时,若须要传递参数须要:在模板中绑定事件时传递好参数,否则参数是事件对象。
7. 模块化 + 命名空间
- 目标:让代码更好保护,让多种数据分类更加明确。
-
批改
store.js
const countAbout = { namespaced:true,// 开启命名空间 state:{x:1}, mutations: {...}, actions: {...}, getters: {bigSum(state){return state.sum * 10} } } const personAbout = { namespaced:true,// 开启命名空间 state:{...}, mutations: {...}, actions: {...} } const store = new Vuex.Store({ modules: { countAbout, personAbout } })
-
开启命名空间后,组件中读取 state 数据:
// 形式一:本人间接读取 this.$store.state.personAbout.list // 形式二:借助 mapState 读取:...mapState('countAbout',['sum','school','subject']), // 对象写法 ...mapState('countAbout',{sum:'sum'),
-
开启命名空间后,组件中读取 getters 数据:
// 形式一:本人间接读取 this.$store.getters['personAbout/firstPersonName'] // 形式二:借助 mapGetters 读取:...mapGetters('countAbout',['bigSum']) // 对象写法
-
开启命名空间后,组件中调用 dispatch
// 形式一:本人间接 dispatch this.$store.dispatch('personAbout/addPersonWang',person) // 形式二:借助 mapActions:...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'}) ...mapActions('user',['addCash'])
-
开启命名空间后,组件中调用 commit
// 形式一:本人间接 commit this.$store.commit('personAbout/ADD_PERSON',person) // 形式二:借助 mapMutations:...mapMutations('countAbout',{increment:'JIA',decrement:'JIAN'}), ...mapMutations('user',['addCash'])
## 24、路由
- 了解:一个路由(route)就是一组映射关系(key – value),多个路由须要路由器(router)进行治理。
- 前端路由:key 是门路,value 是组件。
1. 根本应用
- 装置 vue-router,命令:
npm i vue-router
- 利用插件:
Vue.use(VueRouter)
-
编写 router 配置项:
// 引入 VueRouter import VueRouter from 'vue-router' // 引入 Luyou 组件 import About from '../components/About' import Home from '../components/Home' // 创立 router 实例对象,去治理一组一组的路由规定 const router = new VueRouter({ routes:[ { path:'/about', component:About }, { path:'/home', component:Home } ] }) // 裸露 router export default router
-
实现切换(active-class 可配置高亮款式)
<router-link active-class="active" to="/about">About</router-link>
-
指定展现地位
<router-view></router-view>
2. 几个留神点
- 路由组件通常寄存在
pages
文件夹,个别组件通常寄存在components
文件夹。 - 通过切换,“暗藏”了的路由组件,默认是被销毁掉的,须要的时候再去挂载。
- 每个组件都有本人的
$route
属性,外面存储着本人的路由信息。 - 整个利用只有一个 router,能够通过组件的
$router
属性获取到。
3. 多级路由(多级路由)
-
配置路由规定,应用 children 配置项:
routes:[ { path:'/about', component:About, }, { path:'/home', component:Home, children:[ // 通过 children 配置子级路由 { path:'news', // 此处肯定不要写:/news component:News }, { path:'message',// 此处肯定不要写:/message component:Message } ] } ]
-
跳转(要写残缺门路):
<router-link to="/home/news">News</router-link>
4. 路由的 query 参数
-
传递参数
<!-- 跳转并携带 query 参数,to 的字符串写法 --> <router-link :to="/home/message/detail?id=666&title= 你好"> 跳转 </router-link> <!-- 跳转并携带 query 参数,to 的对象写法 --> <router-link :to="{ path:'/home/message/detail', query:{ id:666, title:'你好' } }" > 跳转 </router-link>
-
接管参数:
$route.query.id $route.query.title
5. 命名路由
- 作用:能够简化路由的跳转。
-
如何应用
-
给路由命名:
{ path:'/demo', component:Demo, children:[ { path:'test', component:Test, children:[ { name:'hello' // 给路由命名 path:'welcome', component:Hello, } ] } ] }
-
简化跳转:
<!-- 简化前,须要写残缺的门路 --> <router-link to="/demo/test/welcome"> 跳转 </router-link> <!-- 简化后,间接通过名字跳转 --> <router-link :to="{name:'hello'}"> 跳转 </router-link> <!-- 简化写法配合传递参数 --> <router-link :to="{ name:'hello', query:{ id:666, title:'你好' } }" > 跳转 </router-link>
-
6. 路由的 params 参数
-
配置路由,申明接管 params 参数
{ path:'/home', component:Home, children:[ { path:'news', component:News }, { component:Message, children:[ { name:'xiangqing', path:'detail/:id/:title', // 应用占位符申明接管 params 参数 component:Detail } ] } ] }
-
传递参数
<!-- 跳转并携带 params 参数,to 的字符串写法 --> <router-link :to="/home/message/detail/666/ 你好"> 跳转 </router-link> <!-- 跳转并携带 params 参数,to 的对象写法 --> <router-link :to="{ name:'xiangqing', params:{ id:666, title:'你好' } }" > 跳转 </router-link>
特地留神:路由携带 params 参数时,若应用 to 的对象写法,则不能应用 path 配置项,必须应用 name 配置!
-
接管参数:
$route.params.id $route.params.title
7. 路由的 props 配置*
作用:让路由组件更不便的收到参数
{
name:'xiangqing',
path:'detail/:id',
component:Detail,
// 第一种写法:props 值为对象,该对象中所有的 key-value 的组合最终都会通过 props 传给 Detail 组件
// props:{a:900} 不必 props 承受会放在 $attrs 中
// 第二种写法:props 值为布尔值,布尔值为 true,则把路由收到的所有 params 参数通过 props 传给 Detail 组件
// props:true
// 第三种写法:props 值为函数,该函数返回的对象中每一组 key-value 都会通过 props 传给 Detail 组件
props(route){
return {
id:route.query.id,
title:route.query.title
}
}
}
8.<router-link>
的 replace 属性
- 作用:管制路由跳转时操作浏览器历史记录的模式
- 浏览器的历史记录有两种写入形式:别离为
push
和replace
,push
是追加历史记录,replace
是替换以后记录。路由跳转时候默认为push
- 如何开启
replace
模式:<router-link replace .......>News</router-link>
9. 编程式路由导航
- 作用:不借助
<router-link>
实现路由跳转,让路由跳转更加灵便 -
具体编码:
//$router 的两个 API this.$router.push({ name:'xiangqing', params:{ id:xxx, title:xxx } }) this.$router.replace({ name:'xiangqing', params:{ id:xxx, title:xxx } }) this.$router.forward() // 后退 this.$router.back() // 后退 this.$router.go() // 可后退也可后退
10. 缓存路由组件
- 作用:让不展现的路由组件放弃挂载,不被销毁。
-
具体编码:
<keep-alive include="News"> <router-view></router-view> </keep-alive>
11. 两个新的生命周期钩子
- 作用:路由组件所独有的两个钩子,用于捕捉路由组件的激活状态。
-
具体名字:
activated
路由组件被激活时触发。deactivated
路由组件失活时触发。
12. 路由守卫
- 作用:对路由进行权限管制
- 分类:全局守卫、独享守卫、组件内守卫
-
全局守卫:
// 全局前置守卫:初始化时执行、每次路由切换前执行 router.beforeEach((to,from,next)=>{console.log('beforeEach',to,from) if(to.meta.isAuth){ // 判断以后路由是否须要进行权限管制 if(localStorage.getItem('school') === 'atguigu'){ // 权限管制的具体规定 next() // 放行}else{alert('暂无权限查看') // next({name:'guanyu'}) } }else{next() // 放行 } }) // 全局后置守卫:初始化时执行、每次路由切换后执行 router.afterEach((to,from)=>{console.log('afterEach',to,from) if(to.meta.title){document.title = to.meta.title // 批改网页的 title}else{document.title = 'vue_test'} })
-
独享守卫:
beforeEnter(to,from,next){console.log('beforeEnter',to,from) if(to.meta.isAuth){ // 判断以后路由是否须要进行权限管制 if(localStorage.getItem('school') === 'atguigu'){next() }else{alert('暂无权限查看') // next({name:'guanyu'}) } }else{next() } }
-
组件内守卫:
// 进入守卫:通过路由规定,进入该组件时被调用 beforeRouteEnter (to, from, next) { }, // 来到守卫:通过路由规定,来到该组件时被调用 beforeRouteLeave (to, from, next) {}
13. 路由器的两种工作模式
- 对于一个 url 来说,什么是 hash 值?—— #及其前面的内容就是 hash 值。
- hash 值不会蕴含在 HTTP 申请中,即:hash 值不会带给服务器。
-
hash 模式:
- 地址中永远带着 #号,不美观。
- 若当前将地址通过第三方手机 app 分享,若 app 校验严格,则地址会被标记为不非法。
- 兼容性较好。
-
history 模式:
- 地址洁净,好看。
- 兼容性和 hash 模式相比略差。
- 利用部署上线时须要后端人员反对,解决刷新页面服务端 404 的问题。
index.html :html 的入口文件
引入 css 文件
引入 index.js 此时的 src 指向实例化 Vue 的 js 文件
<script type='module' src='js/index.js'></script>
css:style.css
js:
index.js
定义实例化的 Vue 应用 render 模板去替换 el 挂载的 DOM 节点
导入 root 文件,指向 components 中的 root
render:(h){return h(root)}
components:
root.js:定义 render 的返回值
先导入页面须要的组件 和数据(如果须要则要导入事件总线){
template:`
<div id="app">// 如果 css 中定义了 app 的款式,id=app 须要加上
<todo-list></todo-list>
<done-list></done-list>
</div>
`,
components:{
"todo-list":todolist,
"done-list":donelist
}
}
todolist.js
donelist.js
先导入事件总线中须要的数据
定义组件的性能:获取数据应用 computed 计算属性 定义的办法 须要去触发总线中的办法 eventBus.$emit("事件总线中的办法",传入的参数)
store
store.js
先定义须要的数据,在 eventBus 上注册子组件须要的办法 this.$on("事件总线中的办法",this.eventBus 中的办法)
Jenkins 装置
nginx 配置
yum install gcc-c++
yum install -y pcre pcre-devel
yum install -y zlib zlib-devel
yum install -y openssl openssl-devel
wget http://nginx.org/download/ngi…
tar zxvf nginx-1.16.1.tar.gz
先进入解压文件夹 ./configure && make && make install
whereis nginx 查看 nginx 装置门路
在 usr/local/nginx/html 下
启动
[root@localhost ~]# /usr/local/nginx/sbin/nginx
进行 / 重启
[root@localhost ~]# /usr/local/nginx/sbin/nginx -s stop(quit、reload)
命令帮忙
[root@localhost ~]# /usr/local/nginx/sbin/nginx -h
验证配置文件
[root@localhost ~]# /usr/local/nginx/sbin/nginx -t
配置文件
[root@localhost ~]# vim /usr/local/nginx/conf/nginx.conf
装置 yarn
应用 yum 装置 Yarn
Yarn 官网提供的有 Yarn RPM 软件包,在增加 yum 源配置之后可应用 yum 装置:
增加 yum 源配置
curl -sL https://dl.yarnpkg.com/rpm/ya… | sudo tee /etc/yum.repos.d/yarn.repo
应用 yum 装置
sudo yum -y install yarn
查看装置的 Yarn 版本:
yarn -v
Jenkins 装置
jekins+gitee+nginx
sudo wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat…
sudo rpm –import https://pkg.jenkins.io/redhat…
sudo yum upgrade
sudo yum install epel-release java-11-openjdk-devel
sudo yum install jenkins
sudo systemctl daemon-reload
sudo systemctl start jenkins 启动
查看状态 sudo systemctl status jenkins
批改编辑权限 vim /etc/sysconfig/jenkins
service jenkins restart 重启
这时拜访 jenkins 有不能够,要凋谢阿里云 8080 端口
1. 登录胜利
为了确保管理员平安地装置 Jenkins,明码已写入到日志中(不晓得在哪里?)该文件在服务器上:
/var/lib/jenkins/secrets/initialAdminPassword
请从本地复制明码并粘贴到上面。
d65c81a6952341dfac53d9efaf61e0cc
2. 依照默认配置装置插件
3. 创立一个管理员账户,实现配置后,就能够登录 Jenkins 了
4. 装置插件
上面来装置 nodejs 插件
gitee 令牌 2e724699896926f21b301598d10d91d7
gitee 试验 jenkins 教程
装置 rz:yum install -y lrzsz
gcc 版本过低
4. 装置 gcc
yum install gcc -y #默认装置旧版本 4.85
yum -y install centos-release-scl
yum -y install devtoolset-9-gcc devtoolset-9-gcc-c++ devtoolset-9-binutils #装置新版本
切换为新版本
scl enable devtoolset-9 bash #长期切换,退出服务器复原
echo “source /opt/rh/devtoolset-9/enable” >>/etc/profile #永恒切换
jenkins
须要配置 webhook gitee 须要配置 参考 jenkins+gitee 实现前端我的项目自动化部署 – 简书 (jianshu.com)
装置 nodejs
- 第一步, 到官网查看最新源码,并下载
cd /home/downloads
wget https://nodejs.org/dist/v10.16.0/node-v10.16.0.tar.gz
下载编译好的 node Linux
ln -s /usr/local/src/node-v16.5.0-linux-x64/bin/node /usr/local/bin/node
ln -s /usr/local/src/node-v16.5.0-linux-x64/bin/npm /usr/local/bin/npm
课堂常识内容回顾
1、应用 template 会将 el 挂载的节点内容替换. 留神 template 模板的应用
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<body>
<div id="app"></div>
</body>
<script>
// 模型层
var obj = {message:"hello,world 1"}
// 视图层 替换 id=app 区域
var template = `
<div class='Find'>{{message}}</div>
`;
// 视图模型层
var app = new Vue({
el:"#app",
data:obj,
template:template
});
2、解冻属性 Object.freeze(data), 解冻后不能实时响应数据
3、生命周期
<div id="app">
<p title="nihao" ref="hello">{{msg}}</p>
</div>
<script>
var app =new Vue({// 外部没有 el,能够手动进行 $mount("#app")挂载
// el:"#app",
data:{msg:"你好"},
beforeCreate(){
// 在实例初始化之后, 进行数据侦听和事件 / 侦听器的配置之前同步调用
console.log("生命周期:beforeCreate");
console.log('加载 data 中的 msg 数据 ---'+this.msg);
console.log('获取节点 $el---'+this.$el);
this.initMethod()},
created(){
// 数据 injections 和 reactivity 注入 响应
// 在实例创立实现后被立刻同步调用。在这一步中,实例已实现对选项的解决,意味着以下内容已被配置结束:数据侦听、计算属性、办法、事件 / 侦听器的回调函数
console.log("生命周期:created");
console.log('加载 data 中的 msg 数据 ---'+this.msg);
console.log('获取节点 $el---'+this.$el);
this.initMethod()},
beforeMount(){
// 生成虚构 dom 在挂载开始之前被调用:相干的 render 函数首次被调用。console.log("生命周期:beforeMount");
console.log(this.$el);
console.log(this.$refs);
},
mounted(){
// 实在的 dom 实例被挂载后调用,这时 el 被新创建的 vm.$el 替换了
console.log("生命周期:mounted");
console.log(this.$el);
console.log(this.$refs);
},
beforeUpdate(){
// data 数据扭转 beforeUpdate 函数中的数据没有扭转 app.msg = 123 this.msg=123
console.log("生命周期:beforeUpdate");
console.log(this._vnode.children[0].children[0].text);
},
updated(){
// 通过 patch 算法 比照理论 dom 和渲染虚构 dom 更新数据
console.log("生命周期:beforeUpdate");
console.log(this._vnode.children[0].children[0].text);
},
beforeDestroy(){
// 应用 app.$destroy 卸载对象与 vue 的绑定
console.log("生命周期:beforeDestroy");
},
destroyed(){console.log("生命周期:destroyed");
},
methods: {initMethod(){console.log('初始化办法调用胜利');
}
},
}).$mount("#app");
4、组件
组件中的 data 为什么是一个函数的返回值?
<div id="app">
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
</div>
<script>
Vue.component("button-counter",{
// 如果不应用函数返回,复用的组件的 data 数据是雷同的援用,通过函数的返回对象,每次创立的都是一个新的数据援用
// data:{
// count:0
// },
data(){
return {count:0}
},
created(){console.log("我是组件");
console.log(this);
},
template:`
<button v-on:click="count++"> 你点击了 {{count}} 次 </button>
`
})
var app = new Vue({
el:"#app",
created(){console.log("我是根");
console.log(this);
},
})
组件的注册形式
// 组件的名称:不应用单文件组件时(.vue):举荐遵循 W3C 标准中的自定义组件名 (字母全小写且必须蕴含一个连字符)比方 my-component-name
如果组件名称是大驼峰或者小驼峰
Vue.component('MyComponent'/'myComponent', {// ... 选项 ...})
能够在父组件中这样调用是等价的
<my-component></my-component> <MyComponent></MyComponent>/<myComponent></myComponent>
注册形式:// 全局注册 间接在任意中央应用
Vue.component('my-component', {// ... 选项 ...})
// 部分注册
<div id="app">
<my-component></my-component>
</div>
<script>
var options = {data(){
return{msg:"我是子组件中的 data 值"}
},
template:"<div>{{msg}}</div>"
}
new Vue({
el:"#app",
components:{"my-component":options,// 前面接入选项对象}
});
</script>
组件间的通信形式有 3 类:
第一类:父子组件通信
第二类:隔代组件通信
第三类:兄弟组件通信
(1)props/$emit 实用父子组件通信
父 –> 子传值 props
<div id="app">
<book v-for="item in bookList" :name="item.name" :author="item.author"></book>
</div>
<script>
Vue.component("book", {props: ["name", "author"],
template: `
<div>
<p> 书名:{{name}}</p>
<p> 作者:{{author}}</p>
</div>
`
})
var app = new Vue({
el:"#app",
data:{
bookList:[
{
name:"西游记",
author:"吴承恩"
},
{
name:"水浒传",
author:"施耐庵"
},
{
name:"红楼梦",
author:"曹雪芹"
},
{
name:"三国演义",
author:"罗贯中"
}
]
}
})
</script>
<div id="app">
<!-- v-bind 当做表达式解析 -->
<my-book :name="book.name" :price="book.price"></my-book>
<my-book v-bind="book"></my-book>
</div>
<script>
Vue.component("my-book",{props:["name","price"],
template:`
<div>
<p>{{name}}</p>
<p>{{price}}</p>
</div>
`
})
new Vue({
el:"#app",
data:{
book:{
name:"西游记",
price:15
}
}
});
子父传值 通过 $emit(组件上的自定义办法,传入父组件的值)
<div id="app">
<p>{{count}}</p>
<my-counter v-on:add="add1" :msg="msg"></my-counter>
</div>
<script>
Vue.component("my-counter", {props:['msg'],
data(){
return{son:"子组件"}
},
template: `
<div>
<p>{{son}}</p>
<p>{{msg}}</p>
<button v-on:click="btnClick"> 减少 </button>
</div>
`,
beforeCreate() {console.log("子组件的 beforeCreated");
},
created(){console.log("子组件的 created");
},
beforeMount() {console.log("子组件的 beforeMount");
},
mounted() {console.log("子组件的 mounted");
},
beforeUpdate() {console.log('子组件的 beforeUpdate');
},
updated() {console.log("子组件的 updated");
},
beforeDestroy() {console.log("子组件的 beforeDestroy");
},
destroyed() {console.log("子组件的 destroyed");
},
methods: {btnClick(){console.log("子组件的 btn 事件");
this.$emit("add",{
addCount:10,
name:"二蛋"
});
}
},
})
var app = new Vue({
el: "#app",
data: {
count: 0,
msg:"父子传值"
},
beforeCreate() {console.log("父组件的 beforeCreated");
},
created(){console.log("父组件的 created");
},
beforeMount() {console.log("父组件的 beforeMount");
},
mounted() {console.log("父组件的 mounted");
},
beforeUpdate() {console.log('父组件的 beforeUpdate');
},
updated() {console.log("父组件的 updated");
},
beforeDestroy() {console.log("父组件的 beforeDestroy");
},
destroyed() {console.log("父组件的 destroyed");
},
methods: {add1(e){console.log(e.name);
console.log("根组件 count++");
this.count+=e.addCount;
}
},
})
(2)ref 与 $parent/$children 应用父子组件通信
(3)$attrs/$listeners 应用隔代组件通信
(4)provide / inject 应用于隔代组件通信
<div id="app">
<p> 我是根元素 </p>
<my-one></my-one>
</div>
<script>
Vue.component("my-one",{inject:["msg","getStr"],
// 拜访
template:`
<div style="border:1px solid red">
<p>{{msg}}</p>
<p>{{getStr()}}</p>
</div>
`,
});
var app = new Vue({
el:"#app",
data:{msg:'依赖提供的数据',},
methods: {getStr(){console.log("依赖提供的办法");
return "依赖办法返回的值"
}
},
provide(){
return {
msg:this.msg,
getStr:this.getStr,
}
}
})
</script>
(5)EventBus($emit/$on)实用于父子、隔代、兄弟组件通信
<div id="app">
<p>{{msg}}</p>
<my-one></my-one>
</div>
<script>
// 公共仓库治理 msg 的状态
var eventBus = new Vue({created() {
// 为事件总线增加 change-msg 的事件
this.$on("change-msg",this.print)
},
data:{msg:'父组件的 msg',},
methods: {print(str){
this.msg=str;
console.log(this.msg);
},
},
})
// 2. 子组件的数据不须要通过 props 进行传递,通过 eventBus 进行触发总线的 change-msg 事件,传递的参数在 print 中承受,通过 print 批改 msg 中的值
Vue.component("my-one",{
template:`
<div style="border:1px solid red">
<p>{{msg}}</p>
<button @click="btnClick"> 批改 </button>
</div>
`,
computed:{msg(){return eventBus.msg;}
},
methods: {btnClick(){
// 3.$emit 触发 change-msg,调用 print 办法
eventBus.$emit("change-msg","我是子组件批改的")
}
},
})
// 1. 获取 msg 间接通过 eventBus 进行获取
var app =new Vue({
el:"#app",
computed:{msg(){return eventBus.msg;}
},
});
</script>
(6)Vuex 实用于父子、隔代、兄弟组件通信
组件的插槽 slot
<div id="app">
<my-slot>
<div> 你好 </div>
<div> 你好 </div>
</my-slot>
</div>
<script>
Vue.component("my-slot",{
template:`
<div>
<p> 组件插槽 </p>
<slot></slot>
<p> 组件插槽 </p>
</div>
`
});
var app =new Vue({el:"#app",})
</script>
编译作用域
父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
<div id="app">
<my-one>
<!-- 规定谁的模板 数据就是谁的 -->
<!-- 编译时在 app 中的 msg 应用根组件的作用域的数据 -->
<!-- 利用 根组件的数据通过 slot 间接渲染在子组件中 不须要通过 props 属性进行传递 -->
<div>{{msg}}</div>
</my-one>
</div>
<script>
Vue.component("my-one",{data(){
return {msg:"我是子组件的"}
},
template:`
<div>
<p> 我是子组件 </p>
<slot></slot>
<p> 我是子组件 </p>
</div>
`,
});
var app =new Vue({
el:"#app",
data:{msg:"我是根组件的"}
});
</script>
后背内容 默认的内容
<div id="app">
<my-slot>
<!-- 如果此处有内容, 子组件中的 slot 默认内容将不会显示 -->
</my-slot>
</div>
<script>
Vue.component("my-slot",{
template:`
<div>
<p> 组件插槽 </p>
<slot> 默认内容 </slot>
<p> 组件插槽 </p>
</div>
`
});
var app =new Vue({el:"#app",})
</script>
具名插槽
<div id="app">
<my-one>
<template v-slot:head>
<div> 头部 </div>
</template>
<template #middle>
<div> 两头 </div>
</template>
<!-- <div> 尾部 </div> 等价于上面的写法 -->
<template v-slot:default>
<div> 尾部 </div>
</template>
</my-one>
</div>
<script>
Vue.component("my-one", {
template: `
<div>
<slot name="head"></slot>
<p> 我是子组件 </p>
<slot name="middle"></slot>
<p> 我是子组件 </p>
<slot></slot>
</div>
`,
});
var app = new Vue({el: "#app",});
</script>
作用域插槽
<div id="app">
<!-- 让插槽内容可能拜访子组件中才有的数据是很有用 -->
<current-user>
<!-- 将蕴含所有插槽 prop 的对象命名为 slotProps -->
<template v-slot:default="slotProps">
{{slotProps.user.firstName}}
</template>
</current-user>
</div>
<script>
Vue.component("current-user", {data(){
return{user:{firstName:"姓",lastName:"名"}
}
},
// 绑定在 <slot> 元素上的 attribute 被称为插槽 prop
// 当初在父级作用域中,咱们能够应用带值的 v-slot 来定义咱们提供的插槽 prop 的名字:template: `
<div>
<slot v-bind:user="user">{{user.lastName}}</slot>
</div>
`,
});
var app = new Vue({el: "#app",});
</script>
// 补充:当被提供的内容只有默认插槽时 组件的标签才能够被当作插槽的模板来应用
<current-user v-slot:default="slotProps">
{{slotProps.user.firstName}}
</current-user>
// 能够简写为
<current-user v-slot="slotProps">
{{slotProps.user.firstName}}
</current-user>
// 构造赋值
<!-- 构造赋值 别名只能在这里应用 -->
<template v-slot:foot="{user:person}">
{{person}}
</template>
// 这里还是只能写 user
<slot name="foot" :user="user">
<div>{{user}}</div>
</slot>
要扭转插槽的值
// 第一种传入的是对象 子组件中应用 user.lastName
<!-- 将蕴含所有插槽 prop 的对象命名为 slotProps 相当与 data 的数据 -->
<template v-slot:default="slotProps">
{{slotProps.user}}
</template>
<slot v-bind:user="user.firstName">
<div>{{user}}</div>
</slot>
<slot v-bind:user="user.lastName">
<div>{{user}}</div>
</slot>
// 第二种形式 在组件中间接传入
<template v-slot:default="slotProps">
{{slotProps.user.lastName}}
</template>
<slot v-bind:user="user">
<div>{{user}}</div>
</slot>
动静组件 keep-alive
应用 keep-alive 时,组件将缓存
<div id="app">
<!-- 切换后放弃原页面的数据 -->
<button @click="msg='my-one'"> 组件一 </button>
<button @click="msg='my-two'"> 组件二 </button>
<!-- 所有被 keep-alive 包裹都有新的两个生命周期 -->
<!-- <keep-alive> -->
<component :is="msg"></component>
<!-- </keep-alive> -->
</div>
<script>
Vue.component("my-one", {created() {console.log("组件一创立了")
},
destroyed() {console.log("组件一销毁了")
},
activated() {console.log("activated 生命周期被调用");
},
deactivated() {console.log("deactivated 生命周期被调用");
},
template: `
<div>
<p> 我是组件一 </p>
<input type='text'>
</div>
`,
});
Vue.component("my-two", {created() {console.log("组件二创立了");
},
destroyed() {console.log("组件二销毁了");
},
data(){
return {msg:'哈哈'}
},
template: `
<div>
<p> 我是组件二 </p>
<p>{{msg}}</p>
<button @click="msg=' 你好 '"> 扭转 </button>
</div>
`,
});
var app = new Vue({
el: "#app",
data:{msg:"my-one"}
});
</script>
内联模板 inline-template
<div id="app">
<p>{{msg}}</p>
<!-- 不举荐应用 inline-template 应用后将当中的内容作为子组件的模板,作用域是子组件 -->
<my-one inline-template>
<div style="border:1px solid red">
<p>{{msg}}</p>
</div>
</my-one>
</div>
<script>
Vue.component("my-one",{data(){
return {msg:"子组件的 msg"}
}
});
var app = new Vue({
el:"#app",
data:{msg:'父组件的 msg',},
})
</script>
x-template
<div id=”app”>
<p>{{msg}}</p>
<my-one> </my-one>
</div>
<!-- 应用 script 去写子组件的模板,通过 id 赋值给子组件的 template -->
<script type="text/x-template" id="one">
<div style="border:1px solid red">
<p>{{msg}}</p>
</div>
</script>
<script>
Vue.component("my-one",{
template:"#one",
data(){
return {msg:"子组件的 msg"}
}
});
var app = new Vue({
el:"#app",
data:{msg:'父组件的 msg',},
})
</script>
混入 mixin
混入 (mixin) 提供了一种非常灵活的形式,来散发 Vue 组件中的可复用性能。一个混入对象能够蕴含任意组件选项。当组件应用混入对象时,所有混入对象的选项将被“混合”进入该组件自身的选项。
<div id="app">
<p>{{msg}}</p>
<button @click="print"> 打印 </button>
<p>----------------</p>
<my-one></my-one>
</div>
<script>
/*
混入就是抽取组件雷同的局部
通过 mixins 导入组件 语法 mixins:[mixin]
*/
var mixin = {created() {console.log("混入对象的生命周期");
},
// 当数据对象与组件的数据对象抵触时(除了生命周期会合并),以组件为准 办法是挂载在组件上
// 例如 methods、components 和 directives,将被合并为同一个对象。两个对象键名抵触时,取组件对象的键值对。data(){
return {msg:"混入对象的 msg 你好"}
},
methods: {print(){console.log(this.msg);
}
},
}
Vue.component("my-one",{mixins:[mixin],
// 同名的钩子函数将合并为一个数组,因而都将被调用
// 混入的钩子函数会先于组件的钩子函数执行
created() {console.log("组件本人的生命周期钩子函数");
},
data(){
return {msg:"组件的数据你好"}
},
template:`
<div>
<p> 我是子组件 ---{{msg}}</p>
<button @click="print"> 打印 </button>
</div>
`
})
var app = new Vue({
el:"#app",
mixins:[mixin],
})
</script>
过滤器 filter
过滤器能够用在两个中央:** 双花括号插值和 `v-bind` 表达式 ** (后者从 2.1.0+ 开始反对)。过滤器应该被增加在 JavaScript 表达式的尾部,由“管道”符号批示。当全局过滤器和部分过滤器重名时,会采纳部分过滤器。全局过滤器: 在创立 Vue 实例之前全局定义过滤器
// 全局过滤器
Vue.filter("replaceMinus",function(value){return value.replace(/-/g," ");
})
部分过滤器
var app = new Vue({
el:"#app",
data:{msg:'hello-world-haha-heihei'},
filters:{"replaceMinus":function(value){return value.replace(/-/g," ");
},
"upperCase":function(value){return value.toUpperCase();
}
}
})
案例:
<div id="app">
<p>{{msg | replaceMinus}}</p>
<p>{{msg | upperCase}}</p>
</div>
<script>
// 全局过滤器
Vue.filter("replaceMinus",function(value){return value.replace(/-/g," ");
})
var app = new Vue({
el:"#app",
data:{msg:'hello-world-haha-heihei'},
filters:{// "replaceMinus":function(value){// return value.replace(/-/g," ");
// },
"upperCase":function(value){return value.toUpperCase();
}
}
})
</script>
自定义指令 directive
<div id=”app”>
<input type="text" v-focus> 自定义指令 </input>
<input type="text" v-focu>
</div>
<script>
// 全局指令注册 bg inserted 的函数
Vue.directive('focus',{inserted:function(el){
// el 是以后的 dom 节点
console.log(el);
// window 对象
console.log(this);
el.focus();}
})
// 部分指令注册应用 directives
var app = new Vue({
el: "#app",
directives:{
focu:{inserted:function(el){
// 同上
console.log(this);
console.log(el);
el.focus();}
}
}
})
</script>
自定义指令的钩子函数
一个指令定义对象能够提供如下几个钩子函数 (均为可选):
bind:只调用一次,指令第一次绑定到元素时调用。在这里能够进行一次性的初始化设置。
inserted:被绑定元素插入父节点时调用 (仅保障父节点存在,但不肯定已被插入文档中)。
update:所在组件的 VNode 更新时调用,然而可能产生在其子 VNode 更新之前。指令的值可能产生了扭转,也可能没有。然而你能够通过比拟更新前后的值来疏忽不必要的模板更新。
componentUpdated:指令所在组件的 VNode 及其子 VNode 全副更新后调用。
unbind:只调用一次,指令与元素解绑时调用。
钩子函数参数
指令钩子函数会被传入以下参数:
el:指令所绑定的元素,能够用来间接操作 DOM。
binding:一个对象,蕴含以下 property:
name:指令名,不包含 v- 前缀。value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2。oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否扭转都可用。expression:字符串模式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"。arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"。modifiers:一个蕴含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 {foo: true, bar: true}。
vnode:Vue 编译生成的虚构节点。移步 VNode API 来理解更多详情。
oldVnode:上一个虚构节点,仅在 update 和 componentUpdated 钩子中可用。
留神:除了 el 之外,其它参数都应该是只读的,切勿进行批改。如果须要在钩子之间共享数据,倡议通过元素的 dataset 来进行。
<div id=”app”>
<div id="hook-arguments-example" v-demo:foo.a.b="msg"></div>
</div>
<script>
// 全局注册 bg inserted 的函数
Vue.directive('demo', {bind: function (el, binding, vnode) {
// el 是以后的 dom 节点
console.log(el);
// 一个对象蕴含属性
console.log(binding);
// Vue 编译生成的虚构节点。console.log(vnode);
var s = JSON.stringify
el.innerHTML =
// 示意 demo
'name:' + s(binding.name) + '<br>' +
// 示意 msg
'value:' + s(binding.value) + '<br>' +
//msg
'expression:' + s(binding.expression) + '<br>' +
//foo
'argument:' + s(binding.arg) + '<br>' +
//{a:true,b:true}
'modifiers:' + s(binding.modifiers) + '<br>' +
'vnode keys:' + Object.keys(vnode).join(',')
}
})
var app = new Vue({
el: "#app",
data: {msg: "hello"}
})
</script>
### [动静指令参数](https://cn.vuejs.org/v2/guide/custom-directive.html# 动静指令参数)
创立一个自定义指令,用来通过固定布局将元素固定在页面上。咱们能够像这样创立一个通过指令值来更新竖直地位像素值的自定义指令:这会把该元素固定在间隔页面顶部 200 像素的地位。但如果场景是咱们须要把元素固定在左侧而不是顶部又该怎么办呢?这时应用动静参数就能够十分不便地依据每个组件实例来进行更新。
<div id=”dynamicexample”>
<h3>Scroll down inside this section ↓</h3>
<p v-pin:[direction]=”200″>I am pinned onto the page at 200px to the left.</p>
</div>
// 通过
Vue.directive(‘pin’, {
bind: function (el, binding, vnode) {
el.style.position = 'fixed'
var s = (binding.arg == 'left' ? 'left' : 'top')
el.style[s] = binding.value + 'px'
}
})
new Vue({
el: ‘#dynamicexample’,
data: function () {
return {direction: 'left'}
}
})
渲染函数 render
`createElement` 到底会返回什么呢?其实不是一个 * 理论的 * DOM 元素。它更精确的名字可能是 `createNodeDescription`,因为它所蕴含的信息会通知 Vue 页面上须要渲染什么样的节点,包含及其子节点的形容信息。咱们把这样的节点形容为“虚构节点 (virtual node)”,也常简写它为“**VNode**”。“虚构 DOM”是咱们对由 Vue 组件树建设起来的整个 VNode 树的称说
<div id=”app”>
</div>
<div>
<anchored-heading :level="2">Hello-world!</anchored-heading>
</div>
<script>
Vue.component('anchored-heading', {render: function (createElement) {console.log(this.level);
console.log(this.$slots);
return createElement(
'h' + this.level, // 标签名称
this.$slots.default // 子节点数组
)
},
props: {
level: {
type: Number,
required: true
}
}
})
var app = new Vue({
el: "#app",
// 通过渲染函数
render:function(createElement){
let vnode = createElement(
"h1",// 第一个参数
{ // 第二个参数
class:{foo:true,},
style:{color:"red",},
attrs:{title:"题目属性"}
},
"你好")// 第三个参数
console.log(vnode);
return vnode
}
})
</script>
## vue-router
index.html
链接到 vue 的 router 中
<router-link to="/about">About</router-link>
<router-link to="/home">Home</router-link>
展现跳转显示的模板地位
<router-view></router-view>
js/index.js
导入 router
定义 router:router
router/index.js
导入组件 About 组件中导入的地址是绝对本身文件的
new VueRouter({
routes:[
{
path:'/about',
component:About
}
]
})