admin 管理员组文章数量: 887021
1.Vue核心
1.1 初识Vue
1.想让Vue工作,就必须创建一个Vue实例,且要传入一个配置对象。
2.root容器里的代码依然符合html规范,只不过混入了一些特殊的vue语法。
3.root容器里的代码被称为“Vue模板”。
4.容器和实例的关系:容器和实例只能是一一对应的关系。开发中只会出现一个vue实例
a.多个容器对应一个实例,只有第一个容器会绑定。
b.多个实例对应一个容器,只有第一个实例会生效。
5.JS表达式和JS代码
a.JS表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方,例如:a,a+b,demo(1),x==y?"A":"B".
b.js代码(语句):只控制走向不产生值
if(){},for(){}
6.{{xxx}}中xxx要写表达式,且xxx可以自动读取到data中的所有属性。
7.一旦data中的数据发生变化,那么模板中用到该数据的地方也会自动更新。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../VUE/js/vue.js"></script>
</head>
<body>
<div id="root">
<h1>
<!-- 差值语法 -->
{{name}}
</h1>
</div>
<div>
<h1>
<!-- 差值语法 -->
{{name}}
</h1>
</div>
<script type="text/javascript">
Vue.config.productionTip=false;
//创建vue实例
new Vue({
el: '#root',//el用于指定vue实例为哪个容器服务,值通常为css选择器字符串/也可以写document.getelementById()(一般不用)
data: {
//data内的数据仅供el绑定的元素使用
name: "hello word",
}
})
</script>
</body>
</html>
1.2 模板语法
1.2.1 差值语法
差值语法只管标签内容,例如:上方代码中的yyyy是标签体内容。
写法{{yyyy}},yyyy是js表达式,且可以读取到data中的所有属性。
<h1 x="xxxx">
yyyy
</h1>
1.2.2 指令语法
指令语法只管标签属性,例如:上方代码中的xxxx是标签属性。
功能:用于解析标签(包括:标签属性,绑定事件等等),xxxx也是是js表达式,且可以读取到data中的所有属性。
如果属性前面加了v-bind,那么属性后面的“xx”内容,就会被当做表达式来执行。
v-bind:可以简写为:
备注:vue中有很多指令,且形式都是:v-***,此处v-bind只是举例子使用。
<div id="root">
<h1>
差值语法 <br>
你好 {{name}}
</h1>
<h1>
指令语法 <br>
<h1>
指令语法 <br>
<a v-bind:href="url">百度</a>
<a :href="url">百度</a>
</h1>
</h1>
</div>
<script type="text/javascript">
Vue.config.productionTip=false;
//创建vue实例
new Vue({
el: '#root',//el用于指定vue实例为哪个容器服务,值通常为css选择器字符串/也可以写document.getelementById()(一般不用)
data: {
//data内的数据仅供el绑定的元素使用
name: "jack",
url: "https://www.baidu"
}
})
</script>
1.3数据绑定
vue有两种绑定方式:单向绑定和双向绑定
<div id="root">
单向数据绑定 <input type="text" v-bind:value="oneway"><br>
双向数据绑定 <input type="text" v-model:value="twoway"><br>
<!-- 简写 -->
单向数据绑定 <input type="text" :value="oneway"><br>
双向数据绑定 <input type="text" v-model:="twoway"><br>
</div>
<script type="text/javascript">
Vue.config.productionTip=false;
//创建vue实例
new Vue({
el: '#root',//el用于指定vue实例为哪个容器服务,值通常为css选择器字符串/也可以写document.getelementById()(一般不用)
data: {
//data内的数据仅供el绑定的元素使用
oneway: "one-way",
twoway: "two-way"
}
})
</script>
1.3.1单向绑定(v-bind:)
数据只能从data流向页面,无法从页面影响data
1.3.2双向绑定(v-model:)
数据可以从data流向页面,页面也可以流向data
1.3.3备注
1.双向绑定一般都应用在表单类元素上如:input,select,redio等等,有value的元素,因为只有用户有操做的元素,才有可能改变vaule,才有双向绑定的意义。类似于<h1>等标签,用户无法修改value,自然也没有双向绑定的意义。
2.v-model:value可以简写为v-model,因为v-model默认收集的就是value属性值。
1.3.4 简写
v-bind:value简写为:value
v-model:value简写为:v-model:
1.3.5 el与data的两种写法
1.3.5.1 el写法
(1)new Vue的时候配置el属性
(2)先创建Vue实例,接收以后通过v.$mount("#id")对el进行绑定
1.3.5.2 data写法
(1)对象式
(2)函数式
在组件中,data必须使用函数式否则会报错。
1.3.5.3 注意:
在Vue管理的函数中,一定不要用箭头函数,否则this就不再是Vue实例了,而是Window。
<div id="root">
<h1>{{name}}</h1>
</div>
<script type="text/javascript">
Vue.config.productionTip=false;
//创建vue实例
const vue= new Vue({
//el: '#root',//第一种写法
//data第两种写法
/*data第一种写法 对象式
data: {
name: "hello word"
}*/
// data第二种写法 函数式
data(){
return {
name: "hello word"
}
}
})
vue.$mount('#root')//第二种写法
</script>
1.4 MVVM模型
1.4.1 概念
1.M:模型(Model)对应data中的数据
2.V:视图(View)对应模板
3.VM:视图模型(ViewMode)对应Vue实例对象
<div id="root">
<h1>学校名称:{{name}}</h1>
<h1>学校地址:{{address}}</h1>
<h1>VM属性:{{$createElement}}</h1>
</div>
<script type="text/javascript">
Vue.config.productionTip=false;
//创建vue实例
const vm= new Vue({
el: '#root',//第一种写法
data: {
name: "华盛顿第一人民大学",
address: "华盛顿南街123号",
}
})
console.log(vm)
</script>
1.4.2结论
(1)data中的所有属性最后都会出现在vm上。
(2)vm上的所有属性以及vue圆形上的所有属性,在vue模板中都可以直接使用
1.5 数据代理
1.5.1 Object.defineproperty方法
<div id="root">
</div>
<script type="text/javascript">
let num=18;
let person={
name:"张三",
sex:"男",
age: num
}
/*增加age属性
入参:
1.增加属性的对象
2.增加的属性名称
3.属性的配置
*/
Object.defineProperty(person,"age",{
/*
value:18,
enumerable:true,//可以枚举
writable:true,//是否可以修改
configurable:true//是否可以被删除
*/
//当有人读取person的age时调用
get:function(){
//return "hello"
console.log("有人读取了age属性")
return num;
},
//当有人修改person的age时调用
set:function(value){
//return "hello"
console.log("有人修改了age属性")
num=value;
}
})
console.log(person)
/*把object转为数组
1. 如果enumerable为false:
Object.keys(person)无法取值值age,
如果enumerable为true:
Object.keys(person)可以取值age,
默认false
2. 如果writable为false:
则属性不可被修改
如果writable为true:
则属性可被修改
默认false
3. configurable是否可以被删除同以上两个
*/
//console.log(Object.keys(person))
</script>
1.5.2 数据代理概念
通过一个对象代理,对另一个对象中属性的操作(读/写)
<script type="text/javascript">
let objx={x:100}
let objy={y:200}
/*
通过objy操作objx的x属性
给objy添加一个x属性
get方法返回值为objx.x
set方法修改bjx.x
*/
Object.defineProperty(objy,"x",{
get: function(){
return objx.x;
},
set:function(value){
objx.x=value
}
})
</script>
1.5.3 Vue中的数据代理
(1)Vue中的数据代理:通过vm对象来代理data中属性的操作(读/写)。
(2)Vue中数据代理的好处:更加方便的操作(读/写)data中的数据。
(3)基本原理:通过Object.defineProperty()把data对象中的所有属性都加到vm上,为每一个添加到vm身上的属性都制定一个getter/setter方法,在getter/setter方法内部去操作(读/写)data中的属性。
具体内容参考后面的数据劫持。
1.6事件处理
1.6.1事件的基本处理
事件的基本使用:
(1)使用v-on:xxx 或者@xxx绑定事件,其中xxx是事件名
(2)事件的回调需要配置在methods对象中,最终会在vm桑。
(3)methods中配置的函数,不要使用箭头函数,否则this指向的不是vue而是window
(4)methods中配置的函数都是被vue所管理的函数,this指向vm或者组件实例对象
(5)@click="demo"和@click="demo($event)"效果是一样的,但是后者有传参。
<div id="root">
<h2>欢迎来到{{address}}</h2>
<!-- <button v-on:click="showInfo">点我提示信息</button> -->
<button @click="showInfo1">点我提示信息1(不传参)</button>
<button @click="showInfo2($event,66)">点我提示信息2(传参)</button>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
//创建vue实例
const vm = new Vue({
el: '#root',//第一种写法
data: {
address: "华盛顿第一人民医院",
},
methods: {
/* showInfo: (event) => {
//所有被vue管理的函数最好都不要写箭头函数
//console.log(this)//如果用箭头函数定义this对象为window
} */
showInfo1(event) {
//console.log(this==vm)//true this是vue对象
//获取时间源对象
/* console.log(event.target) */
alert("1点我提示信息1")
},
showInfo2(event,number) {
console.log(event)
console.log(number)
//console.log(this==vm)//true this是vue对象
//获取时间源对象
/* console.log(event.target) */
alert("2点我提示信息2")
},
}
})
</script>
1.6.2 事件修饰符
Vue中的事件修饰符
(1)prevent:阻止默认事件(常用)。
(2)stop:组织事件冒泡(常用)。
(3)once:事件只能触发一次(常用)。
(4)capture:使用事件的捕获模式。
(5)self:只有event.target是当前操作的元素才会触发的时间。
(6)passive:时间的默认行为立即执行,无需等待时间回调执行完毕
<div id="root">
<h2>欢迎来到{{address}}</h2>
<!-- 阻止默认行为 -->
<!-- <a href="https://www.baidu" @click="showInfo">点我提示信息</a> -->
<a href="https://www.baidu" @click.prevent="showInfo">点我提示信息</a>
<div class="demo1" @click="showInfo">
<!-- 阻止冒泡 -->
<button @click.stop="showInfo">阻止冒泡</button>
</div>
<!-- 事件只触发一次 -->
<button @click.once="showInfo">事件只触发一次</button>
<!-- 阻止事件捕获 -->
<div class="box1" @click.capture="showMsg(1)">
div1
<div class="box2" @click="showMsg(2)">
div2
</div>
</div>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
//创建vue实例
const vm = new Vue({
el: '#root',//第一种写法
data: {
address: "华盛顿第一人民医院",
},
methods: {
showInfo(event) {
//阻止事件默认行为
//event.preventDefault()
alert("你好")
},
showMsg(n) {
alert("n=" + n)
},
}
})
</script>
<style>
.demo1 {
height: 100px;
width: 200px;
background-color: black;
}
.box1 {
padding: 5px;
background-color: aqua;
.box2 {
background-color: orange;
}
}
</style>
1.6.3键盘事件
(1)Vue中常用的按键别名:
回车=》enter
删除=》delete(捕获删除和退格键)
退出=》esc
空格=》space
换行=》tab(特殊,必须配合keyDown使用)
上=》up
下=》down
左=》left
右=》right
(2)Vue未提供别名的按键可以用原始的key值去绑定,但要注意转为kebab-case(短横线命名)
(3)系统修饰键(用法特殊):ctrl、alt、shift,meta(win)
(3.1)配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。
(3.2)配合keydown使用:正常触发事件。
(4)也可以用keyCode去指定按键(不推荐)。
(5)Vue.config.keyCodes.自定义键名=键码,可以定制按键别名。
<div id="root">
<input type="text" placeholder="按下回车提示输入" @keyup="showInfo">
<input type="text" placeholder="按下回车提示输入" @keyup.hc="showInfo">
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
Vue.config.keyCodes.hc=13//定义一个别名
//创建vue实例
const vm = new Vue({
el: '#root',
data: {
},
methods: {
showInfo(event){
//if(event.keyCode!=13)return;
console.log(event.key,event.keyCode)
}
}
})
</script>
1.6.4总结
1.7.2的修饰符可以连续使用:@click.passive.stop="demo"
1.7.3系统修饰键后面也可以连续使用:@keyUp.ctrl.y="demo"
1.7 计算属性与监视
当页面有两个输入框,且另一个位置需要计算两个框之内的内容时:
1.7.1计算属性
1.7.1.1 差值语法
<div id="root">
姓<input type="text" placeholder="请输入姓" @keyup.enter="firstNameChange"><br>
名<input type="text" placeholder="请输入名" @keyup.enter="lastNameChange"><br>
姓名:{{firstName}}{{lastName}}
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
//创建vue实例
const vm = new Vue({
el: '#root',
data: {
firstName:"",
lastName:"",
},
methods: {
firstNameChange(event){
this.firstName= event.target.value.slice(0,3)
},
lastNameChange(event){
this.lastName= event.target.value
if(this.lastName!=""){
this.lastName="-"+ this.lastName
}
}
}
})
</script>
1.7.1.2计算属性
计算属性:
(1)定义:要用的属性不存在,要通过已有属性进行计算。
(2)原理:底层借助了Object.defineproperty方法提供的getter和setter方法。
(3)get函数什么时候执行:
(3.1)初次读取属性时会执行一次。
(3.2)当以来的属性,数据发生改变时,会再次调用。
(4)优势:与methods实现相比,内部有缓存机制(可服复用),效率更高,调试方便。
(5)备注:
(5.1)计算属性最终会出现在vm上,直接读取即可使用。
(5.2)如果计算属性要被修改,那么必须写set函数去响应修改,且
set中要引起计算式依赖的数据发生改变。
<div id="root">
姓<input type="text" placeholder="请输入姓" @keyup.enter="firstNameChange"><br>
名<input type="text" placeholder="请输入名" @keyup.enter="lastNameChange"><br>
姓名:{{fullName}}
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
//创建vue实例
const vm = new Vue({
el: '#root',
data: {
firstName:"",
lastName:"",
},
computed:{
fullName:{
//当游人读该属性时,他就回去执行该属性的get方法
get(){
console.log("get方法被调用");
console.log()
let fullName= this.firstName+(this.lastName==""?"":"-"+this.lastName);
return fullName;
},
//当修改该属性时,他就回去执行该属性的set方法
set(value){
console.log("set方法被调用");
const arr=value.split("-");
this.firstName=arr[0];
this.lasstName=arr[1];
return fullName;
}
}
},
methods: {
firstNameChange(event){
this.firstName= event.target.value
},
lastNameChange(event){
this.lastName= event.target.value
}
}
})
</script>
1.7.1.3计算属性简写
当计算属性只考虑读取,不考虑编辑时计算属性可以简写:
<div id="root">
姓<input type="text" placeholder="请输入姓" @keyup.enter="firstNameChange"><br>
名<input type="text" placeholder="请输入名" @keyup.enter="lastNameChange"><br>
姓名:{{fullName}}
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
//创建vue实例
const vm = new Vue({
el: '#root',
data: {
firstName:"",
lastName:"",
},
computed:{
//当只有获取属性没有设置属性时
fullName(){
let fullName= this.firstName+(this.lastName==""?"":"-"+this.lastName);
return fullName;
}
},
methods: {
firstNameChange(event){
this.firstName= event.target.value
},
lastNameChange(event){
this.lastName= event.target.value
}
}
})
</script>
1.7.2 监视属性
1.7.2.1监视属性两种写法
监视属性watch:
(1)当贝坚实的属性发生变化是,回电好书自动调用,执行相关代码。
(2)监视的属性必须存在才能监视
(3)监视属性的两种写法:
(3.1).new Vue是传入watch配置
(3.2)通过vm.$watch监视
<div id="root">
<h1>今天天气很{{info}}</h1>
<button @click="editIsHot">
切换天气
</button>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
//创建vue实例
const vm = new Vue({
el: '#root',
data: {
isHot: true,
},
computed: {
info() {
return this.isHot ? "炎热" : "凉爽";
}
},
methods: {
editIsHot() {
this.isHot = !this.isHot
}
},
/* watch:{
isHot:{
immediate:true,//初始化时让handler调用一下
handler(newValue,oldValue){
console.log("isHot被修改了,newValue="+newValue+",oldValue="+oldValue)
}
}
} */
})
vm.$watch('isHot', {
immediate: true,//初始化时让handler调用一下
handler(newValue, oldValue) {
console.log("isHot被修改了,newValue=" + newValue + ",oldValue=" + oldValue)
}
})
</script>
1.7.2.2深度监视
深度监视:
(1)VUe中的watch默认部件是对象内部值得改变。(一层)
(2)配置deep:true可以监测对象内部值改变。(多层)
备注:
(1)Vue自身可以检测对象内部指的改变,但是Vue提供的watch默认不可以。
(2)使用watch根据数据具体结构,决定是否采用深度监视。
<div id="root">
<h1>今天天气很{{info}}</h1>
<button @click="editIsHot">
切换天气
</button>
<h1>
a的值为:{{numbers.a}}
</h1>
<button @click="numbers.a++ ">
点我a+1
</button><br>
<h1>
b的值为:{{numbers.b}}
</h1>
<button @click="numbers.b++ ">
点我a+1
</button>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
//创建vue实例
const vm = new Vue({
el: '#root',
data: {
isHot: true,
numbers:{
a:1,
b:2
}
},
computed: {
info() {
return this.isHot ? "炎热" : "凉爽";
}
},
methods: {
editIsHot() {
this.isHot = !this.isHot
}
},
watch:{
isHot:{
immediate:true,
handler(newValue,oldValue){
console.log("isHot被修改了,newValue="+newValue+",oldValue="+oldValue)
}
},
//监视多级结构中某个结构的变化
/* 'numbers.a':{
handler(newValue,oldValue){
console.log("numbers.a被修改了,newValue="+newValue+",oldValue="+oldValue)
}
}, */
numbers:{
//监视多级结构中所有结构的变化
deep:true,
handler(newValue,oldValue){
console.log("numbers被修改了,newValue="+newValue+",oldValue="+oldValue)
}
},
}
})
</script>
1.7.2.3监视属性简写
1.7.3 计算属性和监视实现对比
computed和watch之间的区别:
(1)computed能完成的功能,watch都可以完成。
(2)watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作。
两个重要的小原则:
(1)所有被Vue管理的函数最好写成普通函数,这样this指向的是vm或者组件实例对象。
(2)所有不被VUe所管理的函数(定时器回调函数,ajax回调函数等),做好使用箭头函数,这样this指向的才是vm
<div id="root">
<h1>今天天气很{{info}}</h1>
<button @click="editIsHot">
切换天气
</button>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
//创建vue实例
const vm = new Vue({
el: '#root',
data: {
isHot: true,
},
computed: {
info() {
return this.isHot ? "炎热" : "凉爽";
}
},
methods: {
editIsHot() {
this.isHot = !this.isHot
}
},
watch: {
//正常写法
/* isHot:{
//immediate:true,//初始化时调用handler
//deep:true,//开启深度监视
handler(newValue,oldValue){
console.log("numbers被修改了,newValue="+newValue+",oldValue="+oldValue)
}
} ,*/
//简写
/* isHot(newValue, oldValue) {
console.log("numbers被修改了,newValue=" + newValue + ",oldValue=" + oldValue)
} */
}
})
//正常写法
/* vm.$watch('isHot', {
handler(newValue, oldValue) {
console.log("numbers被修改了,newValue=" + newValue + ",oldValue=" + oldValue)
}
}) */
//简写
vm.$watch('isHot', function(newValue, oldValue) {
console.log("numbers被修改了,newValue=" + newValue + ",oldValue=" + oldValue)
}
)
</script>
1.8 class和style绑定
1.8.1 概念
(1)在应用界面中某个(些)元素的样式是变化的。
(2)class/style绑定就是专门实现动态样式效果的技术
1.8.2 class绑定
(1):class='xxx'。
(2)表达式是字符串‘classA’。
(3)表达式是对象{classA:isA,classB:isB}。
(4)表达式是数组['classA','classB']。
<style>
.basic {
width: 300px;
height: 300px;
border: 1px solid black;
}
.happy {
background: red;
}
.sad {
background: gray;
}
.normal {
background: skyblue;
}
.with1 {
background: green;
}
.with2 {
color: red;
font-size: 50px;
}
.with3 {
border: 2px solid #000;
/* 设置边框样式 */
border-radius: 10px;
/* 设置圆角半径 */
}
</style>
<div id="root">
<!-- 字符串写法适用于:样式的类名不确定需要动态指定 -->
<div class="basic" :class="moodClass" @click="changeMood">{{name}}</div>
<!-- 数组写法适用于:样式的个数不确定 名字也不确定 -->
<div class="basic" :class="arrClass" @click="changeMood">{{name}}</div>
<!-- 对象写法适用于:样式的个数确定 名字也确定 但是动态决定是否生效-->
<div class="basic" :class="objClass" @click="changeMood">{{name}}</div>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
//创建vue实例
const vm = new Vue({
el: '#root',
data: {
name:'class绑定',
moodClass:'normal',
arrClass:['with1','with2','with3'],
objClass:{
with1:false,
with2:false
}
},
methods: {
changeMood(){
const moodArr=['happy','sad','normal']
console.log()
this.moodClass=moodArr[Math.floor(Math.random()*3)]
}
}
})
</script>
1.8.3 style绑定
(1):style="{{fontSize:xxx}}"其中xxx是动态值
(2):style="[a,b]"其中ab是样式的对象
<div id="root">
<!-- <div class="basic" :style="{fontSize: fontSize+'px'}">{{name}}</div> -->
<!-- 对象写法 -->
<div class="basic" :style="styleObj">{{name}}</div>
<!-- 数组写法 -->
<div class="basic" :style="[styleObj,styleObj2]">{{name}}</div>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
//创建vue实例
const vm = new Vue({
el: '#root',
data: {
name:'class绑定',
// fontSize:40,
styleObj:{
fontSize: '40px'
},
styleObj2:{
backgroundColor: 'blue'
},
},
methods: {
}
})
</script>
1.9 条件渲染
1.9.1条件渲染指令
(1)v-if 与 v-else/v-else-if
写法:
(1.1)v-if="表达式"。
(1.2)v-else-if="表达式"。
(1.3)v-else。
适用于:切换频率较低的场景
特点:不展示的dom元素直接移除。
注意:v-if可以和v-else-if,v-else一起使用。但是结构不能被“打断”。
(2)v-show
写法:v-show="表达式"
适用于:切换频率较高的场景
特点:不展示的dom元素未被移除,仅仅使用样式隐藏
(3)备注:
(3.1)使用v-if时,元素可能无法获取到,而v-show元素一定会获取到。
(3.2)template不能和v-show配合使用,v-if可以
<div id="root">
<button @click="changeShow">点击切换</button>
<div v-if="isShow">{{name}}</div>
<div v-show="isShow">{{name}}</div>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
//创建vue实例
const vm = new Vue({
el: '#root',
data: {
name:'v-if和v-else条件渲染',
isShow:true
},
methods: {
changeShow(){
this.isShow=!this.isShow
}
}
})
</script>
1.9.2 v-if和v-show区别
(1)如果频繁切换用v-show。(页面不显示,但是元素还在)
(2)当条件不成立时,v-if所有子节点都不会解析。(页面不显示,元素也不加载)
1.10 列表渲染
1.10.1循环遍历
v-for指令:
(1)用于展示列表数据。
(2)语法:v-for="(item,index) in xxx" :key="yyy"。
(3)可以遍历:数组,对象,字符串,指定次数。
<div id="root">
人员列表
<ul>
<!-- 遍历数组 -->
<li v-for="(person,index) in personList" ::key="index">{{person.name}}-{{person.age}}</li>
</ul>
汽车信息
<ul>
<!-- 遍历对象 -->
<li v-for="(param,index) in car" ::key="index">{{param}}-{{index}}</li>
</ul>
字符串信息
<ul>
<!-- 遍历字符串 -->
<li v-for="(char,index) in str" ::key="index">{{char}}-{{index}}</li>
</ul>
循环指定次数
<ul>
<!-- 遍历字符串 -->
<li v-for="(char,index) in 6" ::key="index">{{char}}-{{index}}</li>
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
//创建vue实例
const vm = new Vue({
el: '#root',
data: {
personList:[{
id:"0001",
name:"张三",
age:18
},{
id:"0002",
name:"李四",
age:19
},{
id:"0003",
name:"王五",
age:20
}],
car:{
name:"A8",
price:"1236541233",
color:"black"
},
str:"abcd"
},
methods: {
}
})
</script>
1.10.2 key的原理(面试题)
面试题:react,vue中key有什么作用?(key的内部原理)
(1)虚拟dom中key的作用:
key是虚拟dom对象的标识,当状态中的数据发生变化时,vue会根据【新数据】生成【新虚拟dom】,随后vue对【新虚拟dom】与【旧虚拟dom】进行差异比较,比较规则如下:
(2)比较规则:
(2.1)旧虚拟dom中找到的新虚拟dom相同的key。
(2.1.1)若虚拟dom中内容没变,直接使用之前的真是dom。
(2.2.2)若虚拟dom中的内容变了,则生成新的真是dom,随后替换掉页面中
真实dom。
(2.2)旧虚拟dom中未找到与新虚拟dom中相同的key
(2.2.1)创建新的真是dom,然后渲染到页面。
(3)用index作为key可能会引发的问题:
(3.1)若对数据进行逆序添加,逆序删除等破坏顺序的操作
会产生没有必要的真是dom更新==》界面效果没问题,但是效率低
(3.2)如果结构中含有输入类dom:
会产生错误的dom更新==》页面有问题
(4)开发中如何选择key:
(4.1)最好使用每条数据的唯一标识作为key,比如id,手机号,身份证号等等。
(4.2)如果不存在对数据的逆序删除,逆序添加等破坏顺序的操作,仅用作渲染,
使用index作为key没有任何问题。
<div id="root">
人员列表
<ul>
<!-- 遍历数组 -->
<li v-for="(person,index) in personList" ::key="index">
{{person.name}}-{{person.age}}
<input type="text">
</li>
</ul>
<button @click.once="addPerson">添加一个老刘</button>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
//创建vue实例
const vm = new Vue({
el: '#root',
data: {
personList:[{
id:"0001",
name:"张三",
age:18
},{
id:"0002",
name:"李四",
age:19
},{
id:"0003",
name:"王五",
age:20
}],
},
methods: {
addPerson(){
const person={id:"0004",name:"老刘",age:21}
this.personList.unshift(person)
}
}
})
</script>
1.10.3 列表筛选
可以使用计算实行或者监视属性实现,优先使用计算属性。
<div id="root">
人员列表
<ul>
<input type="text" v-model="keyWords" placeholder="请输入">
<!-- 遍历数组 -->
<li v-for="(person,index) in fliterPersonList" ::key="index">
{{person.name}}-{{person.age}}-{{person.sex}}
</li>
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
//创建vue实例
//监视属性实现
/* const vm = new Vue({
el: '#root',
data: {
keyWords: "",
personList: [{
id: "0001",
name: "马冬梅",
age: 18,
sex: "女"
}, {
id: "0002",
name: "周冬雨",
age: 19,
sex: "女"
}, {
id: "0003",
name: "周杰伦",
age: 20,
sex: "男"
}, {
id: "0004",
name: "艾伦",
age: 21,
sex: "男"
}],
fliterPersonList: [],
},
watch: {
keyWords: {
immediate: true,
handler(value) {
this.fliterPersonList = this.personList.filter((p) => {
return p.name.indexOf(value) !== -1
})
}
}
},
methods: {
}
}) */
const vm = new Vue({
el: '#root',
data: {
keyWords: "",
personList: [{
id: "0001",
name: "马冬梅",
age: 18,
sex: "女"
}, {
id: "0002",
name: "周冬雨",
age: 19,
sex: "女"
}, {
id: "0003",
name: "周杰伦",
age: 20,
sex: "男"
}, {
id: "0004",
name: "艾伦",
age: 21,
sex: "男"
}],
},
//计算属性实现
computed: {
fliterPersonList() {
return this.personList.filter((p) => {
return p.name.indexOf(this.keyWords) !== -1
})
}
}
})
</script>
1.10.4 列表排序
增加排序属性,计算属性fliterPersonList返回搜索之前,进行一次数组排序,然后返回排序后的数组。
<div id="root">
人员列表
<ul>
<input type="text" v-model="keyWords" placeholder="请输入"><button @click="changeSortType(1)">年龄升序</button><button @click="changeSortType(2)">年龄降序</button><button @click="changeSortType(0)">原顺序</button>
<!-- 遍历数组 -->
<li v-for="(person,index) in fliterPersonList" ::key="index">
{{person.name}}-{{person.age}}-{{person.sex}}
</li>
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
//创建vue实例
const vm = new Vue({
el: '#root',
data: {
sortType:0,//0原顺序 1年龄升序 2年龄降序
keyWords: "",
personList: [{
id: "0001",
name: "马冬梅",
age: 22,
sex: "女"
}, {
id: "0002",
name: "周冬雨",
age: 19,
sex: "女"
}, {
id: "0003",
name: "周杰伦",
age: 25,
sex: "男"
}, {
id: "0004",
name: "艾伦",
age: 21,
sex: "男"
}],
},
//计算属性实现
computed: {
fliterPersonList() {
const arr = this.personList.filter((p) => {
return p.name.indexOf(this.keyWords) !== -1
})
//判断是否排序
if(this.sortType){
arr.sort((a,b)=>{
return this.sortType===1?a.age-b.age:b.age-a.age
})
}
return arr;
}
},methods: {
changeSortType(value){
console.log(this.sortType)
this.sortType=value
console.log(this.sortType)
}
},
})
</script>
1.10.5 Vue无法检测到的数据改变
如果以对象的形式更新personList[0],Vue无法检测到数据的改变,vue开发工具也不会体现,但是如果先点击按钮,再打开Vue开发工具,则可以看到修改后的数据,但是页面并未修改,故:如果按第二种形式修改data里的属性的内容,Vue无法检测到属性的修改。
<div id="root">
人员列表
<ul>
<!-- 遍历数组 -->
<button @click="updateMDM">更新马冬梅</button>
<li v-for="(person,index) in personList" :key="person.id">
{{person.name}}-{{person.age}}-{{person.sex}}
</li>
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
//创建vue实例
const vm = new Vue({
el: '#root',
data: {
personList: [{
id: "0001",
name: "马冬梅",
age: 22,
sex: "女"
}, {
id: "0002",
name: "周冬雨",
age: 19,
sex: "女"
}, {
id: "0003",
name: "周杰伦",
age: 25,
sex: "男"
}, {
id: "0004",
name: "艾伦",
age: 21,
sex: "男"
}],
},
methods: {
updateMDM() {
/* 生效
this.personList[0].name="马什么梅"
this.personList[0].age=29 */
//不生效
this.personList[0] = {
id: "0001",
name: "马什么梅",
age: 29,
sex: "女"
}
}
},
})
</script>
1.10.6 Vue监测原理
Vue中监视数据的原理:
(1)vue会监视data中所有层次的数据。
(2)如何监测对象中的数据:
通过setter实现监测,且要在new Vue时就要传入检测的数据。
(2.1)对象中后追加的属性Vue默认不做响应式处理。
(2.2)如需给后添加的属性做响应式处理请使用如下API:
(2.2.1)Vue.set(targer,propertyName/index,value)
(2.2.2)vm.$set(targer,propertyName/index,value)
(3)如何检测数组中的数据:
通过包括数组更新元素的方法实现,本质就做了两件事:
(3.1)调用原生对应的方法,操作数组中的元素。
(3.2)重新解析模板,进行页面更新。
(4)在Vue中修改数组中的某个元素一定要调用如下方法:
(4.1)使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
(4.2)Vue.set()、vm.$set(),不能给vm或vm跟数据对象(data)添加属性。
<div id="root">
<h1>学生信息:</h1>
<button @click="addAge">年龄+1</button>
<button @click="addSex">添加属性:{性别:男}</button>
<button @click="editSex">修改性别{女}</button>
<button @click="addFirends">在列表首位添加一个朋友</button>
<button @click="editFirstFirend">修改列表首位朋友名字为张三</button>
<button @click="addFirstLastHoppy">首、尾添加一个爱好</button>
<button @click="editFirstHoppy">修改第一个爱好为开车</button>
<h1>姓名:{{student.name}}</h1>
<h1>年龄:{{student.age}}</h1>
<h1 v-if="student.sex">性别:{{student.sex}}</h1>
<ul>
爱好
<li v-for="(h,index) in student.hoppy" ::key="index">{{h}}</li><br><br><br>
朋友
<li v-for="(f,index) in student.firends" ::key="index">{{f.name}}--{{f.age}}</li>
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
//创建vue实例
const vm = new Vue({
el: '#root',
data: {
student: {
name: "TOM",
age: 18,
hoppy: ["打篮球", "踢足球", "看电影"],
firends: [{
name: "Jeery",
age: 22
}, {
name: "tony",
age: 25
},]
}
}, methods: {
addAge() {
Vue.set(this.student, "age", this.student.age + 1)
},
addSex() {
//Vue.set(this.student, "sex", "男")
this.$set(this.student, "sex", "男")
},
editSex() {
Vue.set(this.student,"sex","女")
},
addFirends() {
this.student.firends.unshift({name: "王五",age: 38})
},
editFirstFirend() {
this.student.firends[0].name="张三"
},
addFirstLastHoppy() {
this.student.hoppy.unshift("玩游戏")
this.student.hoppy.push("学习")
},
editFirstHoppy() {
//不生效
//this.student.hoppy[0]="开车"
this.student.hoppy.splice(0,1,"开车")
},
},
})
</script>
1.11 收集表单数据
1.11.1 数据收集
收集表单数据:
(1)<input type="text"> v-model收集的是value的值,用户输入的value。
(2) <input type="radio"> v-model收集的是value的值,选中标签所配置的的value。
(3)<input type="checkbox">
(3.1)若没有配置input的value属性,那么v-model收集的是checked勾选状态(true/false)
(3.2)若配置input的value属性:
(3.2.1)如果v-model绑定非数组,那么v-model收集的是checked勾选状态(true/false)
(3.2.2)如果v-model绑定数组,那么v-model收集的是checked勾选选择框对应的value
<div id="root">
<form @submit.prevent="demo">
账号:<input type="text" v-model.trim="userinfo.username">
<br>
密码:<input type="password" v-model="userinfo.password">
<br>
年龄:<input type="number" v-model.number="userinfo.age">
<br>
性别:
<input type="radio" name="sex" v-model="userinfo.sex" value="boy">男
<input type="radio" name="sex" v-model="userinfo.sex" value="girl">女
<br>
爱好:
<input type="checkbox" v-model="userinfo.hoppy" value="抽烟">抽烟
<input type="checkbox" v-model="userinfo.hoppy" value="喝酒">喝酒
<input type="checkbox" v-model="userinfo.hoppy" value="打牌">打牌
<br>
所属校区:<select v-model="userinfo.address">
<option value="请选择">请选择</option>
<option value="北京校区">上海校区</option>
<option value="武汉校区">武汉校区</option>
<option value="合肥校区">合肥校区</option>
<option value="黑龙江校区">黑龙江校区</option>
</select>
<br>
其他信息:<textarea v-model.lazy="userinfo.memo"></textarea>
<br>
<input type="checkbox" v-model="userinfo.apply">阅读并接受<a href="https://www.baidu" target="_blank" >《用户协议》</a>
<br>
<button>提交</submit>
</form>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
//创建vue实例
const vm = new Vue({
el: '#root',
data: {
userinfo:{
username:"",
password:"",
age:"",
sex:"boy",
hoppy:[],
address:"",
memo:"",
apply:"",
}
},
methods: {
demo(){
// console.log(this.userinfo)
console.log(JSON.stringify(this.userinfo))
}
}
})
</script>
1.11.2 v-model的修饰符
v-model的三个修饰符:
(1)lazy:失去焦点后收集数据
(2)number:输入字符串转为有效数字,一般配合type="number"使用。
(3)trim:过滤收尾所有的空格,字符串中间空格无法过滤。
1.12 过滤器
1.12.1 概念
当我们获得了一个时间戳,我们并不需要这个获取到的时间戳,需要这个时间戳代表的时间,此时我们可以用过滤器将其过滤,然后获取对应的时间后使用。
1.12.2过滤器使用
过滤器:
定义:对要显示的数据进行特定格式化后在显示(适用于一些简单的逻辑处理)。
语法:
(1)注册过滤器:Vue.filter(name,callback)或者new Vue(filters:{})。
(2)使用过滤器:{{xxx | 过滤器名}} 或 v-bind:属性="xxx | 过滤器名"。
备注:
(1)过滤器也可以接受额外的参数,多个过滤器也可以串联
(2)并没有改变原本的数据,只是产生了新的数据。
<div id="root">
<h1>显示格式化后的时间</h1>
<!-- 计算属性实现 -->
<h1>{{fmtTime}}</h1>
<!-- methods实现 -->
<h1>{{getFMTime()}}</h1>
<!-- 过滤器实现 -->
<h1>{{time | timeFormater }}</h1>
<h1>{{time | timeFormater('YYYY_MM_DD') |mySlice}}</h1>
<!-- <h1>{{time | timeFormaterManyParam('YYYY_MM_DD')}}</h1> -->
</div>
<div id="root2">
<h1>显示格式化后的时间</h1>
<!-- 过滤器实现 -->
<h1>{{time }}</h1>
<h1>{{time | mySlice}}</h1>
<!-- <h1>{{time | timeFormaterManyParam('YYYY_MM_DD')}}</h1> -->
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
Vue.filter("mySlice",function (value){
return "aaa"
})
//创建vue实例
const vm = new Vue({
el: '#root',
data: {
time:Date.now()
},
computed:{
fmtTime(){
return dayjs(this.time).format("YYYY-MM-DD HH:mm:ss")
}
},
methods: {
getFMTime(){
return dayjs(this.time).format("YYYY-MM-DD HH:mm:ss")
}
},
filters:{
timeFormater(value,frm='YYYY-MM-DD HH:mm:ss'){
return dayjs(value).format(frm)
},
mySlice(value){
return value.slice(0,4)
}
/* timeFormaterManyParam(value,ftr){
return dayjs(value).format(ftr)
},*/
}
})
const vm2 = new Vue({
el: '#root2',
data: {
time:Date.now()
},
computed:{
},
methods: {
}
})
</script>
1.13 内置指令
1.13.1 学过的指令
v-bind:单项绑定解析表达式,可简写为:xxx
v-model:双向数据绑定
v-for:遍历数组,对象,字符串
v-on:绑定事件监听,可简写为@
v-if:条件渲染(动态控制节点是否存在)
v-else:条件渲染(动态控制节点是否存在)
v-show:条件渲染(动态控制节点是否显示)
1.13.2 v-text:
(1)作用:向所在的节点中渲染文本内容
(2)与差值语法不同,v-text会替换掉节点中所有的内容,{{xxx}}则不会替换
<div id="root">
<div>你好,{{name}}</div>
<div v-text="name">你好,</div>
<div v-text="h1">你好,</div>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
//创建vue实例
const vm = new Vue({
el: '#root',
data: {
name:"test",
h1:"<h1>test</h1>",
},
computed:{
},
methods: {
}
})
</script>
1.13.3 v-html:
v-html指令:
(1)作用:向指定节点渲染包含html结构的内容。
(2)与差值语法的区别:
(2.1)v-html 会替换掉节点中的所有内容,{{xx}}则不会。
(2.2)v-html 可以识别html结构
(3)严重注意:v-html有安全性问题!!!
(3.1)在网站上动态渲染任意HTML是非常文献的容易导致XSS攻击。
(3.2)一定要在可信的内容上使用v-html,永远不要用在提交的内容上。
<div id="root">
<div>你好,{{name}}</div>
<div v-html="h1"></div>
<div v-html="a"></div>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
//创建vue实例
const vm = new Vue({
el: '#root',
data: {
name:"test",
h1:"<h1 style='color:red'>test</h1>",
a:"<a href=javascript:location.href='https://www.baidu?'+document.cookie>点我</a>",
},
computed:{
},
methods: {
}
})
</script>
1.13.4 v-cloak:
v-cloak指令(没有值)
(1)本质是一个特殊的属性,Vue创建完毕后,会删掉c-cloak属性。
(2)使用css配合v-cloak可以解决网速慢导致的页面站输出{{xxx}}的问题。
<style>
[v-cloak]{
display: none;
}
</style>
<div id="root">
<div v-cloak>你好,{{name}}</div>
</div>
<script type="text/javascript">
setTimeout(function () {
//5秒之后创建vue实例 模拟网路缓慢,js加载过慢
Vue.config.productionTip = false;
//创建vue实例
const vm = new Vue({
el: '#root',
data: {
name: "v-cloak测试",
},
computed: {
},
methods: {
}
})
}, 5000)
</script>
1.13.5 v-once:
v-once指令:
(1)v-once所在的节点在初次动态渲染之后就视为静态内容了。
(2)以后数据的改变不会引起v-once所在结构更新,可以用于优化性能。
<div id="root">
<div v-once>初始化n={{n}}</div>
<div >当前n={{n}}</div>
<button @click="n++">点我n++</button>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
//创建vue实例
const vm = new Vue({
el: '#root',
data: {
n: 0,
},
computed: {
},
methods: {
}
})
</script>
1.13.6 v-pre:
v-pre指令:
(1)跳过其所在节点的编译过程。
(2)可利用它条过没有使用指令语法,没有使用差值语法的节点,可以加快编译。
<div id="root">
<div v-pre>静态内容</div>
<div v-pre>初始化n={{n}}</div>
<div v-pre>当前n={{n}}</div>
<button v-pre @click="n++">点我n++</button>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
//创建vue实例
const vm = new Vue({
el: '#root',
data: {
n: 0,
},
computed: {
},
methods: {
}
})
</script>
1.14 自定义指令
参数:
(1)指令所绑定的元素
(2)绑定指令的具体实现
1.14.1 函数式
<!--
需求:
1:v-big指令,类似于v-text,可以把数值方法10倍
-->
<div id="root">
<h2>当前的n=<span v-text="n"></span></h2>
<h2>放大10倍的n=<span v-big="n"></span></h2>
<h2>放大10倍的n=<span v-big-number="n"></span></h2>
<button @click="n++">点我n++</button>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
//创建vue实例
const vm = new Vue({
el: '#root',
data: {
n: 21,
},
directives:{
//big何时被调用:1.指令与元素成功绑定时(页面刚加载);(2)指令所在的模板被重新解析时
big(element,binding){
/* console.log(element)
console.log(binding.value*10) */
element.innerText=binding.value*10
},
"big-number"(element,binding){
/* console.log(element)
console.log(binding.value*10) */
element.innerText=binding.value*10
},
}
})
</script>
1.14.2 对象式
<!--
需求:
2:v-fbind指令,类似于v-bind,可以让所绑定的input获取焦点
-->
<div id="root">
<input type="text" v-fbind:value="n">
<button @click="n++">点我n++</button>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
//创建vue实例
const vm = new Vue({
el: '#root',
data: {
n: 21,
},
directives:{
fbind:{
//1.指令与元素成功绑定时(页面刚加载)
bind(element,binding){
element.value=binding.value
},
//2.指令所在元素插入页面时
inserted(element,binding){
element.focus()
},
//3.指令所在的模板被重新解析时
update(element,binding){
element.value=binding.value
element.focus()
}
}
}
})
</script>
1.14.3 总结
(1)定义语法:
(1.1)局部指令:
//创建vue实例 const vm = new Vue({ directives:{ //函数式 big(element,binding){ }, //对象式 fbind:{ //1.指令与元素成功绑定时(页面刚加载) bind(element,binding){ element.value=binding.value }, //2.指令所在元素插入页面时 inserted(element,binding){ element.focus() }, //3.指令所在的模板被重新解析时 update(element,binding){ element.value=binding.value element.focus() } } } } })
(1.2)全局指令:
(1.2.1)Vue.directive(指令名:配置对象)
(1.2.2)Vue.directive(指令名:回调函数)
(2)配置对象中常用的三个回调:
(2.1)bind:指令与元素成功绑定时调用。
(2.2)inserted:指令所在元素被插入到页面中时调用。
(2.3)update:指令所在模板被更新时调用。
(3)备注:
(3.1)定义指令时不加v-,但是使用时加v-
(3.2)指令如果是多个单词要用kebab-case方式命名,不要用canelCase命名
1.15生命周期
1.15.1 概念
生命周期:
(1)又名:生命周期回调函数,生命周期函数,生命周期钩子。
(2)是什么:Vue在关键时刻帮我们调用的一些特殊名称的函数。
(3)生命周期函数的名字不可更改,但是函数的具体内容是根据具体需求编写的。
(4)生命周期函数中的this指向的是vm或组件实例的对象。
1.15.2 重要的生命周期
vm的生命周期:
创建之前==》调用brforeCreate()函数
创建完毕==》调用created()函数
挂载之前==》调用beforeMount()函数
挂在完毕==》调用mounted()函数==================》【重要的钩子】
更新之前==》调用beforeUpdate()函数
更新完毕==》调用updateed()函数
销毁之前==》调用beforeDestory()函数==============》【重要的钩子】
销毁完毕==》调用destoryed()函数
<div id="root">
<h2 :style="{opacity}">欢迎来打山东省</h2>
<button @click="stop">点我停止变化</button>
<button @click="die">点我停止变化(杀死)</button>
<button @click="change">透明度变化</button>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
//创建vue实例
const vm = new Vue({
el: '#root',
data: {
opacity: 1,
intervalId:"",
}, methods: {
change() {
this.intervalId= setInterval(() => {
console.log(this.intervalId)
this.opacity -= 0.01;
if (this.opacity <= 0) {
this.opacity = 1
}
}, 20)
},
stop(){
//停止之后,还可以通过其他方式来修改透明度而且生效
clearInterval(this.intervalId)
},
die(){
//停止之后,vue销毁,其他操作不会再生效,但是定时器还在工作。
// clearInterval(this.intervalId)
this.$destroy();
},
},
mounted() {
this.change()
},beforeDestroy() {
/*容器不一定有明确的destroy指令,可能因为某些原因导致没有 die()方法,
所以不应该在 die()里结束容器,应该杀死容器之前 清除定时器*/
clearInterval(this.intervalId)
},
})
</script>
1.15.3 总结
常用的生命周期狗子:
(1)mounted:发送ajax请求,启动定时器,绑定自定义事件,订阅消息等初始化操作。
(2)beforeDestroy:清除定时器,解绑自定义时间,取消消息订阅等收尾工作。
关于vue销毁:
(1)销毁后的Vue借助开发者工具看不到任何信息。
(2)销毁后的自定义事件会失效,但是原生dom时间依然有效。
(3)一般不会beforeDestroy中操作数据,因为操作了也不会再触发更新,而且立刻销毁。
2.VUE组件化编程
组件的定义:实现应用中局部功能代码和资源的集合
传统方式:
问题:
(1)依赖关系混乱。
(3)代码复用率不高。
组件方式
2.1 模块与组件
2.1.1模块
(1)概念:向外提供特定功能的js程序,一般是一个js文件。
(2)使用原因:js文件很多很复杂。
(3)作用:复用js,简化js的编写,提高js运行效率。
2.1.2 组件
(1)概念:用来实现局部(特定)功能效果的代码集合(html/css/js/image...)。
(2)使用原因:一个界面功能很复杂。
(3)作用:复用代码,简化项目编码,提高运行效率。
2.1.3 模块化
应用中的js都已模块来编写,这个应用就是一个模块化应用。
2.1.4 组件化
当应用中的功能都是多组件的方式来编写,那么这个应用就是组件化应用
2.2 非单文件组件
定义:一个文件中有多个组件。
2.2.1 非单文件组件使用
(1)Vue组件使用三大步骤:
(1.1)定义组件(创建组件)
(1.2)注册组件(全局注册/局部注册)
(1.3)使用组件(写组件标签)
(2)如何定义一个组件
(2.1)使用VUe.extend(options)创建,其中options和new Vue(options)传入的options
几乎一样,但也有点区别:
(2.1.1)不写el:组中所有组件都由vm管理,有vm指定el服务的容器。
(2.1.2)data必须写函数式:避免组件复用时,数据引用造成的数据修改错误。
(2.2)备注:template可以配置组件结构。
(3)如何注册组件:
(3.1)局部注册:new Vue()时,传入入参conponents:{}参数。
(3.2)全局注册:Vueponent('组件名',组件)。
(4)使用组件:使用引入组件时指定的组件名===><组件名><组件名/>
<div id="root">
root1star
<!-- 3.编写组件标签 -->
<school>
</school>
<hr>
<student>
</student>
<hr>
<hello>
</hello>
<hr>
root1end
</div>
<div id="root2">
root2
<hello>
</hello>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
//全局注册
//1.创建组件school
const schoolConst = Vue.extend({
//el: '#root', 组件定义时一定不要写el,因为最终都要被一个vm管理,vm决定服务于谁
//组件模板
template: `<div>
<h2>学校名称:{{school.name}}</h2>
<h2>学校地址:{{school.address}}</h2>
<button @click="alertSchoolName">弹出学校名</button>
</div>`,
//组件变量,必须用函数式
data() {
return {
school: {
name: "schoolName",
address: "schoolAddress"
}
}
},methods: {
alertSchoolName(){
alert(this.school.name)
}
},
})
//1.创建组件student
const student = Vue.extend({
//el: '#root', 组件定义时一定不要写el,因为最终都要被一个vm管理,vm决定服务于谁
//组件模板
template: `<div>
<h2>学生姓名:{{student.name}}</h2>
<h2>学生年龄:{{student.age}}</h2>
</div>`,
data() {
return {
student: {
name: "studentName",
age: "studentAge"
}
}
},
})
//1.创建组件student
const hello = Vue.extend({
//el: '#root', 组件定义时一定不要写el,因为最终都要被一个vm管理,vm决定服务于谁
//组件模板
template: `<div>
<h2>hello{{hello}}</h2>
</div>`,
data() {
return {
hello:"你好"
}
},
})
Vueponent('hello',hello)
//创建vm
new Vue({
el: '#root',
//2.注册组件(局部注册)
components: {
//key为注册后的真实key,value为创建的const组件变量名,若两个一样可以简写为一个单词
school: schoolConst,
// student:student,
student
},
})
new Vue({
el:"#root2",
})
</script>
2.2.2 ★注意点★
(1)关于组件名:
(1.1)一个单词组成:
(1.1.1)首字母小写(school)。
(1.1.2)首字母小写(School)。
(1.2)多个单词组成:
(1.2.1)kebab-case命名方式:my-school。
(1.2.2)CsmelCase命名方式:MySchool(需要脚手架支持)。
(1.3)备注:
(1.3.1)组件名尽可能回避HTML已存在的元素名,例如:h1,H1等等。
(1.3.2)可以使用name配置指定组件在开发者工具中的名字(不影响调用)。
(2)关于组建标签:
(2.1)单标签<school/>。
(2.2)双标签<school></school>。
(2.3)不适用脚手架单标签<school/>多个连续使用会导致后面的无法渲染。
(3)简写方式:
const school={},当变量等于一个对象不调用Vuepent()方法时,在vm引入 组件时会做判断,如果没有调用,或自动去调用Vuepent()。
(3.1)简写:
const schoolConst = { template: `<div> <h2>学校名称:{{school.name}}</h2> <h2>学校地址:{{school.address}}</h2> <button @click="alertSchoolName">弹出学校名</button> </div>`, data() { return { school: { name: "schoolName", address: "schoolAddress" } } }, methods: { alertSchoolName() { alert(this.school.name) } }, }
(3.2)Vue自检:
function extend(to, _from) { for (var key in _from) { to[key] = _from[key]; } return to; }
2.2.3组件嵌套
<div id="root">
root1star
<!-- 3.编写组件标签 -->
<!-- <app/> -->
<hr>
root1end
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
//全局注册
//1.创建组件hello
const hello = Vue.extend({
//el: '#root', 组件定义时一定不要写el,因为最终都要被一个vm管理,vm决定服务于谁
//组件模板
template: `<div>
<h2>hello{{hello}}</h2>
</div>`,
data() {
return {
hello:"你好"
}
},
})
//1.创建组件student
const student = Vue.extend({
//el: '#root', 组件定义时一定不要写el,因为最终都要被一个vm管理,vm决定服务于谁
//组件模板
template: `<div>
<h2>学生姓名:{{student.name}}</h2>
<h2>学生年龄:{{student.age}}</h2>
</div>`,
data() {
return {
student: {
name: "studentName",
age: "studentAge"
}
}
},components:{
hello
}
})
//1.创建组件school
const schoolConst = Vue.extend({
//el: '#root', 组件定义时一定不要写el,因为最终都要被一个vm管理,vm决定服务于谁
//组件模板
template: `<div>
<h2>学校名称:{{school.name}}</h2>
<h2>学校地址:{{school.address}}</h2>
<student></student>
<button @click="alertSchoolName">弹出学校名</button>
</div>`,
//组件变量,必须用函数式
data() {
return {
school: {
name: "schoolName",
address: "schoolAddress"
}
}
},components:{
student
},methods: {
alertSchoolName(){
alert(this.school.name)
}
},
})
const app={
template: `<div>
<hello></hello>
<school></scholl>
</div>`,
components:{
school:schoolConst,
hello
}
}
//创建vm
new Vue({
el: '#root',
//2.注册组件(局部注册)
template:" <app/> ",
components: {
//key为注册后的真实key,value为创建的const组件变量名,若两个一样可以简写为一个单词
app: app,
},
})
</script>
2.2.4 VueConponent
关于 VueConponent:
(1)shcool组件本质是一个名为VueConponent的构造函数,且不是程序员地赢的,是 Vue.extent生成的。
(2)我们只需要写<school></school>或者<school/>,Vue解析时会帮我们创建school组件的 实例对象。
(3)特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent。
(4)关于this指向:
(4.1)组件配置中:data函数,methods中的函数,watch中的函数,computed中的函
数,他们的this指向的是【VueComponent实例对象】
(4.2)new Vue配置中:data函数,methods中的函数,watch中的函数,computed中
的函数,他们的this指向的是【Vue实例对象】
(5)一般情况下:
(5.1)VueComponent实例对象简称==》vc(组件实例对象)
(5.2)Vue对象简称==》vm
2.2.5 ★内置关系★
2.2.5.1 原型对象
function demo(){
this.a=1
this.b=2
}
const d=new demo();
console.log(demo.prototype);//显示原型属性
console.log(d.__proto__);//隐式原型属性
console.log(d.__proto__===demo.prototype,"demo.prototype===d.__proto__");//true 他们都是原型对象
//对原型对象添加一个属性99
demo.prototype.x=99
2.2.5.2 Vue和VueComponent
(1)一个重要的内置关系:Vue.prototype===schoolConst.prototype.__proto__(true)。
(2)为什么要有这个关系:让组件实例对象vc可以访问到Vue原型上的属性,方法。
//1.创建组件school
const schoolConst = Vue.extend({
//el: '#root', 组件定义时一定不要写el,因为最终都要被一个vm管理,vm决定服务于谁
//组件模板
template: `<div>
<h2>学校名称:{{school.name}}</h2>
<h2>学校地址:{{school.address}}</h2>
</div>`,
//组件变量,必须用函数式
data() {
return {
school: {
name: "schoolName",
address: "schoolAddress"
}
}
}
})
//创建vm
new Vue({
el: '#root',
//2.注册组件(局部注册)
template:" <school/> ",
components: {
//key为注册后的真实key,value为创建的const组件变量名,若两个一样可以简写为一个单词
school: schoolConst,
},
})
console.log("Vue.prototype===schoolConst.prototype.__proto__:",Vue.prototype===schoolConst.prototype.__proto__)//true
2.3. 单文件组件
定义:一个文件只有一个组件。
2.3.1 Vue文件
<template>
<!-- 组件的结构 -->
<div class="demo">
<h2>学校名称:{{ school.name }}</h2>
<h2>学校地址:{{ school.address }}</h2>
</div>
</template>
<script>
//组件交互相关代码(数据,方法)
export default {
name: "School",
//组件变量,必须用函数式
data() {
return {
school: {
name: "schoolName",
address: "schoolAddress",
},
};
},
methods: {
alertSchoolName() {
alert(this.school.name);
},
},
};
</script>
<style>
/* 组件样式 */
.demo {
background: beige;
}
</style>
<template>
<!-- 组件的结构 -->
<div>
<h2>学生姓名:{{ student.name }}</h2>
<h2>学生年龄:{{ student.age }}</h2>
</div>
</template>
<script>
//组件交互相关代码(数据,方法)
export default {
name:'student',
data() {
return {
student: {
name: "studentName",
age: "studentAge",
},
};
},
};
</script>
<style>
/* 组件样式 */
</style>
2.3.2App.Vue
<template>
<div>
<School> </School>
<Student></Student>
</div>
</template>
<script>
import School from './School'
import Student from './Student'
export default {
name:'App',
components:{
School,
Student
}
};
</script>
<style>
</style>
2.3.3main.js
import Vue from 'vue'
import App from './App'
new Vue({
el:"#root",
template:`<App></App>`,
components:{App}
})
2.3.4 index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="root">
</div>
<script src="../../js/vue.js"></script>
<script src="./main.js"></script>
</body>
</html>
3.使用脚手架
3.1 初始化脚手架
3.1.1说明
(1)Vue脚手架时Vue官方提供的标准化开发工具(开发平台)。
(2)文档 Vue CLI
3.1.2 具体步骤
(1)配置淘宝镜像:npm config set registry https://registry.npm.taobao
(2)全局安装@vue/cli。(仅第一次执行)(npm install -g @vue/cli)
(3)切换到项目路径,使用指令创建项目:vue create xxx
(4)启动项目:npm run serve
3.1.3 项目架构
(1).gitignore:配置不接受git管理的文件。
(2)babel.config.js:es6转es5要用到的babel配置文件(不需要修改)。
(3)package.json:
"scripts": { "serve": "vue-cli-service serve",//运行命令 "build": "vue-cli-service build",//代码完成后构建命令,将vue文件转为js,css,html文件 "lint": "vue-cli-service lint"//代码检查 },
(4)package-lock.json:包管理器版本管理。
(5)src:
(5.1)main.js:项目入口文件
/* 项目入口文件 */ //引入vue import Vue from 'vue' //引入App组件,App是所有组件的父组件 import App from './App.vue' //关闭vue生产提示 Vue.config.productionTip = false //创建vue实例对象 new Vue({ //将app组件放入容器中 render: h => h(App), }).$mount('#app')//等于el:"#app"
(5.2)App.vue
(5.3)index.html
<!DOCTYPE html> <html lang=""> <head> <meta charset="utf-8"> <!-- 针对ie浏览器的特殊配置,让ie以最高渲染级别渲染页面 --> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <!-- 开启移动端理想视口 --> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <!-- %= BASE_URL %>指的public下的文件,配置页签图标 --> <link rel="icon" href="<%= BASE_URL %>favicon.ico"> <!-- 配置网页标题 --> <title><%= htmlWebpackPlugin.options.title %></title> </head> <body> <!-- 如果浏览器不支持js,就会渲染标签内内容 --> <noscript> <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong> </noscript> <!-- 容器 --> <div id="app"></div> <!-- built files will be auto injected --> </body> </html>
备注:详细参考3.1.5
启动失败原因为:语法检查不通过。
在项目的根目录找到(没有就自行创建)
vue.config.js
文件,关闭语法检查即可const { defineConfig } = require('@vue/cli-service') module.exports = defineConfig({ transpileDependencies: true, lintOnSave:false /*关闭语法检查*/ })
3.1.4 不同版本的VUE
(1)vue.js与vue.runtime.xx.js的区别:
(1.1)vue.js是完整版的VUE,包含核心功能+模板解析器。
(1.2)vue.runtime.xx.js是运行版VUE,只包含核心功能,没有模板解析器。
(2)因为vue.runtime.xx.js没有模板解析器,所以不可以用template配置项,需要使用
render函数,将接受到的createElement函数去指定内容。
3.1.5 修改默认配置
vue隐藏了webpack相关的配置,需要查看可以执行:vue inspect > output.js
可以看到但是无法修改,具体修改方式请参考:
配置参考 | Vue CLI
3.2 配置项
3.2.1 ref属性
(1)被用来给元素或子组件注册引用信息(代替id)。
(2)应用在html不爱券商,获取真是dom元素,应用在组件标签上是组件实例对象(vc)。
(3)使用方式:
(3.1)打标识:<h1 ref='title'>Ref属性测试</h1>/ <School ref='school'/>。
(3.2)获取:this.$refs.school/this.$refs.title。
(3.3)获取到子组件之后也可以修改其内部属性。
<template>
<div>
<h1 ref='title'>Ref属性测试</h1>
<button @click='showDom' ref='btn'>点我输出DOM</button>
<School ref='school'/>
<School/>
<School/>
<Student/>
<Student/>
<Student/>
</div>
</template>
<script>
//引入组件
import School from './compnents/School'
import Student from './compnents/Student'
export default {
components:{
School,
Student
},
data() {
return {
}
},methods:{
showDom(){
console.log(this.$refs.title)
console.log(this.$refs.btn )
console.log(this.$refs.school.school.name='this.$refs.school.school.schoolName ')
}
}
}
</script>
<style scoped>
</style>
3.2.2 props属性
功能:让组件接收外部传入数据。
(1)传递数据<Demo name='xxxx'/>
(2)接受数据:
(2.1)props:['name']
(2.2)限制类型
props:{ id:String, name:String, size:String },
(2.3)限制类型,限制是否必填,设置默认值
props: { id: { type: String, //类型 required: true, //是否必填 }, name: String, size: { type: String, //类型 default: "型号(默认)", //是否必填 }, }
(3)备注:props事制度的,Vue底层会检测你对props的修改,如果进行了修改会发出警
告,如果业务需要,那么应该将props的内容复制到data中一份然后修改data中的据。
App.vue
<template>
<div>
<h1 ref="title">Props属性测试</h1>
<button @click="login">登录</button>
<button @click="getMaterial">获取物料</button>
<!-- <Material v-for="m in materialList" :key="m.id" material.materialName="m.materialName" material.id="m.id" material.materialSize="m.materialSize"/> -->
<!-- <Material v-for="m in materialList" :key="m.id" materialName="m.materialName" id="m.id" materialSize="m.materialSize"/> -->
<ul>
<!-- 遍历数组 -->
<!-- <li v-for="material in materialList" :key="material.id">
{{ material.materialName }}
</li> -->
<Material v-for="m in materialList" :key="m.id" :name="m.materialName" :id="m.id" :size="m.materialSize"/>
</ul>
</div>
</template>
<script>
//引入组件
import axios from "axios";
import Material from "./compnents/Material";
const url = "";
export default {
components: {
Material,
},
data() {
return {
token: "",
materialList: [
{
id: "id1",
materialName: "materialName1",
},
{
id: "id2",
materialName: "materialName2",
},
],
};
},
methods: {
//获取物料
getMaterial() {
axios
.get(url + "/data/dataMaterial/list", {
params: {
pageNo: 1,
pageSize: 10,
},
headers: {
"X-Access-Token": this.token,
},
})
.then((response) => {
this.materialList=response.data.result.records;
})
.catch((error) => {
console.error("Error:", error.response.status);
});
/* var xhr = new XMLHttpRequest();
xhr.open("GET", url + "/data/dataMaterial/list");
xhr.setRequestHeader("X-Access-Token", this.token);
xhr.onreadystatechange = function () {
if (xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status === 200) {
console.log(xhr);
} else {
console.error("Error:", xhr.status);
}
}
};
xhr.send();*/
},
//登录
login() {
axios
.post(url + "/sys/login", {
username: "ly",
password: "123456",
})
.then((response) => {
this.token = response.data.result.token;
console.log(this.token);
this.getMaterial();
})
.catch((error) => {
console.error(error);
});
},
},
created() {},
};
</script>
<style scoped>
</style>
Material.vue
<template>
<div>
物料信息
<h2>Msg:{{ msg }}</h2>
<h2>物料ID:{{ id }}</h2>
<h2>物料名称:{{ name }}</h2>
<h2>物料尺寸:{{ mySize }}</h2>
<button @click="changeSize">修改物料尺寸</button>
</div>
</template>
<script>
export default {
components: {},
props: {},
data() {
console.log(this);
return {
msg: "这是一个物料",
/* material:{
id:"",
materialSize:"",
materialName:""
}, */
mySize: this.size,
};
},
// propos:["id","size","name"],简单的声明
//限制类型
/* props:{
id:String,
name:String,
size:String
}, */
props: {
id: {
type: String, //类型
required: true, //是否必填
},
name: String,
size: {
type: String, //类型
default: "型号(默认)", //是否必填
},
},
computed: {},
watch: {},
created() {},
mounted() {},
methods: {
changeSize() {
this.mySize="修改物料尺寸"
},
},
};
</script>
<style scoped>
</style>
3.3mxin属性
功能:可以把多个组件共用的配置提取成一个混入对象。
使用方式:
(1)定义混合
export const mixin = { methods: { showName() { alert(this.name) } }, mounted() { //alert("你好啊"+this.name) }, data(){ return { size:"mixinSIze" } } }
(2)使用混入
(2.1)全局混入:import {hunhe} from './mixin'; Vue.mixin(hunhe)。
(2.2)局部混入:import {hunhe} from './mixin';mixins:['hunhe']
(3)注意:data中的数据以源文件为主,methods中的数据都会执行,而且优先执行混入的
Repository.vue
<template>
<div>
物料信息
<h2>Msg:{{ msg }}</h2>
<h2>仓库ID:{{ id }}</h2>
<h2>仓库Size:{{ size }}</h2>
<h2>仓库名称:{{ name }}</h2><button @click="showName">弹框展示名字</button>
</div>
</template>
<script>
import {mixin} from '../mixin'
export default {
components: {},
props: {},
data() {
console.log(this);
return {
msg: "这是一个仓库",
};
},
props: {
id: {
type: String, //类型
required: true, //是否必填
},
name: String,
},
mixins:[
mixin
]
};
</script>
<style scoped>
</style>
Material.vue
<template>
<div>
物料信息
<h2>Msg:{{ msg }}</h2>
<h2>物料ID:{{ id }}</h2>
<h2>物料名称:{{ name }}</h2><button @click="showName">弹框展示名字</button>
<h2>物料尺寸:{{ mySize }}</h2>
</div>
</template>
<script>
//引入一个混合
import {mixin} from '../mixin'
export default {
components: {},
props: {},
data() {
console.log(this);
return {
msg: "这是一个物料",
mySize: this.size,
};
},
props: {
id: {
type: String, //类型
required: true, //是否必填
},
name: String,
size: {
type: String, //类型
default: "型号(默认)", //是否必填
},
},mixins:[
mixin
]
};
</script>
<style scoped>
</style>
mixin.js
//引入Vue
import Vue from 'vue'
//引入app
import App from './App'
import {hunhe} from './mixin'
// Vue.mixin(hunhe)
//关闭提示
Vue.config.productionTip=false
new Vue({
el:"#app",
render:h=>h(App)
})
3.4插件
功能:用于增强Vue
本质:包含install方法的一个对象,install的第一个参数是Vue(vm的原型),第二个以后的
参数是插件使用者传递的数据。
定义插件:
export default { install(Vue,a,b,c){ console.log('install',Vue) //拿到Vue的构造方法,可以进行操作,Vue原型上的方法和属性vm和vc都能使用 //全局过滤器 //Vue.filter() //全局指令 // Vue.directive() //全局混入 // Vue.mixins() //给Vue原型增加方法,属性等等 // Vue.prototype.$myMethod=function(){} // Vue.prototype.prototype="" } }
使用插件:Vue.use()。
3.5 scoped样式
作用:让样式在局部生效防止冲突。
写法:<stype scoped>
3.6 开发流程总结
(1)组件化编码流程
(1.1)拆分静态组件:组件按照功能点拆分,命名不要与html元素冲突。
(1.2)实现动态组件:考虑好数据存放位置,数据是一个组件在用,还是一些都在用。
(1.2.1)一个组件再用:放在自身即可。
(1.2.2)一些组件在用,放在他们共同的父组件上。(状态提升)
(1.3)实现交互:绑定事件。
(2)props适用于:
(2.1)父组件==》子组件 通信。
(2.2)子组件==》父组件 通信(要求父组件先给子组件一个函数)。
(3)v-model使用时要切记,v-model绑定的不能是props传过来的值,因为props内传入的
值是不可以修改的。
(4)props传过来的若是对象型的值,修改对象属性时vue不会报错,但不推荐这样做。
3.7本地存储
webStorage:
(1)储存内容大小一般支持5m左右(浏览器不同也可能不一样)
(2)浏览器通过Window.sessionStorage和window.localStorage属性来实现本地存储机制。
(3)相关API:
(3.1)xxxxxxStorage.setItem(key,vaule);
该方法会把一个兼职顿添加到存储中,如果已有该键,则会更新内容。
(3.2)xxxxxxStorage.getItem(key);
该方法接收一个key作为参数,获取对应的value。
(3.3)xxxxxxStorage.removeteam(key);
该方法接收一个key作为参数,删除对应的key和value。
(3.4)xxxxxxStorage.clear();
清除存储中所有数据。
(4)备注:
(4.1)sessionStorage存储的内容会随着浏览器窗口关闭而消失。
(4.2)localStorage存储的内容需要手动调用相应方法才会消失。
(4.3)xxxxxxStorage.getItem(key);获取不到会返回一个null
(4.4)保存对象时可用:JSON.stringify(person),
解析时用:JSON.parse(localStorage.getItem("personToJson"))
JSON.stringify(null)依然是null
saveData() {
let person = { name: "张三", age: 18 }
localStorage.setItem("msg", "hello!!");
localStorage.setItem("numberMsg", 666);
localStorage.setItem("person",person);
localStorage.setItem("personToJson", JSON.stringify(person));
},
readData() {
let person = { name: "张三", age: 18 }
console.log( localStorage.getItem("msg", "hello!!"));
console.log(localStorage.getItem("numberMsg", 666));
// console.log(localStorage.getItem("person",person));
console.log(JSON.parse(localStorage.getItem("personToJson")));
},
deleteData() {
localStorage.removeItem("msg")
localStorage.removeItem("numberMsg")
localStorage.removeItem("personToJson")
},
clearData(){
localStorage.clear();
}
3.8组件自定义事件
(1)一种组件捡的通信方式,适用于:子组件===》父组件
(2)适用场景:A是父组件,B是子组件,B想给A传值,那么就要在A中给B绑定自定义事
件,事件回调在A中。
(3)绑定自定义事件:
(3.1)再父组件中使用
<student v-on:sendName="getName"></student> <student @sendName="getName"></student>
(3.2)再父组件中使用
<student ref="student" @click.native="show"></student> <script> mounted(){ // this.$refs.student.$once("sendName",this.getName) // this.$refs.student.$on("sendName",this.getName) this.$refs.student.$on("sendName",(name)=>{ console.log("getName被触发了",name); console.log(this); this.studentName=name }) } </script>
(3.3)若想让自定义事件只触发一次,可以用once修饰符或者$once方法。
(4)触发自定义事件: this.$emit(name,value)。
(5)解绑滴定仪事件: this.$off(name)。
(6)组件上也可以绑定原生DOM事件,要用native修饰符。
(7)注意,通过this.$refs.student.$on(name,回调),板顶自定义事件时,回调要么要么在
methods中,要么用箭头函数,否则this会指向调用函数的实体。
3.9 ★全局事件总线★
(1)全局事件总线(GlobalEventBus)是一种组件之间通信的方式,适用于任意组件之间
的通信。
(2)安装全局事件总线:
beforeCreate(){ Vue.prototype.$bus=this }
(3)使用全局事件总线:
(3.1)接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,回调由A
组件自身实现。
mounted(){ this.$bus.$on('hello',(data)=>{ console.log('School组件收到了',data) }) }
(3.2)提供数据
this.$bus.$emit('hello',this.student.name)
(4)使用完毕后,组件销毁之前最好用$off解绑当前组件所使用的事件
beforeDestroy(){ this.$bus.$off('hello') },
3.10 消息订阅与发布
(1)消息订阅预发布(pubsub)是一种组件间的通信方式,适用于任意组件通信。
(2)使用步骤:
(2.1)安装pubsub:npm i pubsub-js。
(2.2)引入import pubsub from 'pubsub-js'
(2.3)接收数据:A组件接收数据则在A组件内订阅消息,回调函数由A实现::
this.pubId=pubsub.subscribe('getStudentName',(msgName,data)=>{ })
第一个参数为消息的名称,第二个参数为传入的参数。
(2.4)提供数据:pubsub.publish('getStudentName',this.student.name)
(2.5)容器销毁之前,取消消息订阅: pubsub.unsubscribe(this.pubId)
3.11 nextTick
(1)语法:this.$refs.inputTitle.focus()。
(2)作用:在下一次dom更新后在执行指定的回调函数。
(3)适用:数据改变后,基于更新后的dom执行某些操作时,在nextTick中指定回调函数。
3.12 过度与动画
(1)作用:在插入,更新,或者移除dom元素时,在合适的时候给元素添加样式类名。
(2):
(3)写法:
(3.1)准备好样式
元素进入:
.1)v-enter:进入的起点。
.2)v-enter-active:进入的过程中。
.3)v-enter-to:进入的重点。
元素移除入:
.1)v-leave:离开的起点。
.2)v-leave-active:离开的过程中。
.3)v-leave-to:离开的重点。
(3.2)使用<transition name='name'>包裹要过度的元素,并配置name属性。
(3.3)备注:若有多个元素使用transition,要用<transition-group>,且每个元素都要
指定key。
transition
<template> <div > <button @click="isShow=!isShow">显示/隐藏</button> <transition name="test" appear> <h1 v-show="isShow" class="test">显示/隐藏动画测试</h1> </transition> </div> </template> <script> export default { name:'Test', components:{ }, props:{ }, data() { return { isShow:true, } }, computed:{ }, watch:{ }, created() { }, mounted() { }, methods: { } } </script> <style scoped> .test{ background-color: skyblue; } .v-enter-active{ animation: test 1s linear; } .v-leave-active{ animation: test 1s reverse; } .test{ background-color: skyblue; } .test-enter-active{ animation: test 0.5s linear; } .test-leave-active{ animation: test 0.5s reverse; } @keyframes test{ from{ transform: translateX(-100%); } to{ transform: translateX(0px); } } </style>
transition-group
<template> <div > <button @click="isShow=!isShow">显示/隐藏</button> <!-- <transition name="test" appear> <h1 v-show="isShow" class="test">显示/隐藏动画测试</h1> </transition> --> <transition-group name="test" appear> <h1 v-show="!isShow" key="1" class="test">显示/隐藏动画测试1</h1> <h1 v-show="isShow" key="2" kclass="test">显示/隐藏动画测试2</h1> </transition-group> </div> </template> <script> export default { name:'Test', components:{ }, props:{ }, data() { return { isShow:false, } }, computed:{ }, watch:{ }, created() { }, mounted() { }, methods: { } } </script> <style scoped> .test{ background-color: orange; } /* 进入的起点 .test-enter{ transform: translateX(-100%); } /* 进入的终点 .test-enter-to{ transform: translateX(0px); } 离开的起点 .test-leave{ transform: translateX(0); } 离开的终点 .test-leave-to{ transform: translateX(-100%); }*/ /*进入的起点 离开的终点 */ .test-enter,.test-leave-to{ transform: translateX(-100%); } /*过程中*/ .test-enter-active,.test-leave-active{ transition: 0.5s linear; } /* 进入的终点 离开的起点 */ .test-enter-to,.test-leave{ transform: translateX(0px); } </style>
transition 引入css
4.Vue中的AJAX
4.1 解决跨域问题
4.1.1 方法1
在vue.config.js中添加配置
devServer: { proxy: 'http://localhost:8090' }
优点:配置简单,请求资源是直接发给前端(8080)即可。
缺点:不能配置多个单例,不能灵活的控制请求是否走代理。
工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么请求会自动转发,但是若前端有那么就不会转发。
4.1.1 方法2
在vue.config.js中添加配置
devServer: {
proxy: {
//请求前缀为erp的走代理
'/test/erp': {
//转发地址
target: 'http://localhost:8090',
pathRewrite:{'^/test':''},//重写地址
ws: true,//用于支持webstocket,不写默认true
changeOrigin: true//请求头中的host是否为虚拟地址,不写默认true
},
'/demo/erp': {
//转发地址
target: 'http://localhost:8090',
pathRewrite:{'^/demo':''},//重写地址
ws: true,//用于支持webstocket,不写默认true
changeOrigin: true//请求头中的host是否为虚拟地址,不写默认true
},
}
}
changeOrigin:
true:服务器收到的host为虚拟的,与服务器地址相同。
false:服务器收到的host为真实的,与请求地址相同。
默认为true(react默认为false)。
优点:可以配置多个代理,且可以灵活的控制请求是否走代理。
缺点:配置略微繁琐,请求资源必须加前缀。(可以配置路径重写)。
axio,vue-resource
4.2slot插槽
(1)作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信额方式,适用于父组件===》子组件。
(2)分类:默认插槽,具名插槽,作用域插槽。
(3)使用方式:
(3.1)默认插槽:父组件:
<Category> <div>HTML结构</div> </Category>
子组件:
<template> <div> <!--定义插槽--> <slot>插槽默认内容....</slot> </div> </template>
(3.2)具名插槽
父组件:
<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>
(3.3)作用域插槽
(3.3.1)作用:数组在组件自身,但是数据生成的结构由组件的使用者决定。
(父组件决定结构,子组件提供数据)
父组件:
<Category> <template slot="scopeData"> <!--生成ul列表--> <ul> <li v-for="g in scopeData.games" :key="g">{{g}}</li> </ul> </template> <template slot="scopeData"> <!--生成h4标题--> <h4 v-for="g in scopeData.games" :key="g">{{g}}</h4> </template> </Category>
子组件:
<template> <div> <!--定义插槽--> <slot :games="games">插槽默认内容....</slot> </div> </template>
5.vuex使用
5.1 理解vuex
5.1.1 vuex定义
(1)概念:专门在Vue中实现集中式状态(也叫:数据、状态数据,之后统称:状态)管理的VUe插件,对vue应用中多个组件共享状态进行集中式的管理(读/写),也是一种组件通信的方式,适用于任何组件。
(2)地址:GitHub - vuejs/vuex: 🗃️ Centralized State Management for Vue.js.
当某个属性,所有组件都需要使用:
5.1.2 使用场景:
(1)当多个组件依赖同一个状态。
(2)来自不同组件的行为要变更同一个状态。
5.2 搭建vuex环境
(1)创建 文件夹:src/store/index.js
//该文件用于vuex中最为核心的store //引入Vue import Vue from 'vue' //引入vuex import Vuex from 'vuex' //准备actions 用于响应组件里的动作 const actions = { } //准备mutations 用于真正操作state中的数据 const mutations = { } //准备state 用于存储数据 const state = { } Vue.use(Vuex) //创建并暴露store export default new Vuex.Store({ actions, mutations, state, })
(2)在main.js中创建vm时传入store:
import store from './store' const vm=new Vue({ el:"#app", render:h=>h(App), store, beforeCreate(){ Vue.prototype.$bus=this } })
(3)注意:必须在index.js中引入Vue并使用插件vuex,否则new Vuex.Store()会报错。
5.3 基本使用
(1)初始化数据(state),配置actions,配置mutations,操作文件store.js。
//该文件用于vuex中最为核心的store //引入Vue import Vue from 'vue' //引入vuex import Vuex from 'vuex' //准备actions 用于响应组件里的动作 const actions = { /* add(context,value){ // console.log("store.actions.add,context=",context,"value=",value) contextmit("ADD",value) }, sub(context,value){ contextmit("SUB",value) }, */isOdd(context, value) { console.log("store.actions.isOdd",value) context.dispatch("demo1", value) }, demo1(context, value) { console.log("store.actions.demo1",value) context.dispatch("demo2", value) }, demo2(context, value) { console.log("store.actions.demo2") if (context.state.sum % 2 != 0) { console.log("store.actions.isOdd") contextmit("ADD", value) } }, sleepAdd(context, value) { console.log("store.actions.sleepAdd") setTimeout(() => { contextmit("ADD", value) }, 1000); } } //准备mutations 用于真正操作state中的数据 const mutations = { ADD(state, value) { // console.log("store.mutations.ADD,state=",state,"value=",value) console.log("store.mutations.ADD") state.sum += value }, SUB(state, value) { console.log("store.mutations.state") // console.log("store.mutations.ADD,state=",state,"value=",value) state.sum -= value } } //准备state 用于存储数据 const state = { sum: 0, } Vue.use(Vuex) //创建并暴露store export default new Vuex.Store({ actions, mutations, state, })
(2)组件读取vuex中的数据:$taore.state.sum
(3)修改vuex中的数据:$store.dispatch('action中的方法名',参数)或者$storemit('mutations中的方法名',参数)。
<template> <div> <h1>当前求和为:{{ $store.state.sum }}</h1> <div class="sum"> <select v-model.number="n"> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> </select> <button @click="add">+</button> <button @click="sub">-</button> <button @click="isOdd">为奇数执行+</button> <button @click="sleepAdd">sleep后+</button> </div> </div> </template> <script> export default { props: {}, data() { return { n: 1, }; }, computed: {}, watch: {}, created() {}, mounted() {}, methods: { add() { // this.$store.dispatch("add", this.n); this.$storemit("ADD",this.n) }, sub() { // this.$store.dispatch("sub", this.n); this.$storemit("SUB",this.n) }, isOdd() { /* if (this.sum % 2 != 0) { this.add(); } */ this.$store.dispatch("isOdd", this.n); }, sleepAdd() { this.$store.dispatch("sleepAdd", this.n); }, }, }; </script> <style scoped lang="less"> .sum { select, button { font-size: 20px; min-width: 50px; } } </style>
备注:若没有网络请求或者其他业务逻辑,组件们可以越过actions直接访问mutations中的方法,既直接写commit()。
5.4 getters的使用
(1)概念:当state中的数据需要加工后在使用时,可以使用getters参数。
(2)配置getters属性:
//将state中的数据进行加工 const getters={ bigSum(state){ return state.sum*10 } } //创建并暴露store export default new Vuex.Store({ actions, mutations, state, getters })
(3)组件中读取数据:$store.getters.bigSum。
5.5 四个map方法
5.5.1 mapState方法:
用于帮助我们映射state中的数据为计算属性:
computed:{ //借助mapState生成计算属性,从state中读取数据(对象写法) // ...mapState({school:'school',subject:'subject',sum:'sum'}) // 数组写法 ...mapState(['school','subject','sum']), }
5.5.2 mapGetters方法:
用于帮助我们映射getters中的数据为计算属性
computed:{ //借助mapState生成计算属性,从state中读取数据(对象写法) // ...mapGetters({bigSum:'bigSum'}) // 数组写法 ...mapGetters(['bigSum']), }
5.2.3 mapActions方法
用于帮助我们生成与actions对话的方法,既:包含$store.dispatch(xxx)的函数。
//对象式 // ...mapActions({sleepAdd:'sleepAdd',isOdd:'isOdd'}) //数组式 ...mapActions(['sleepAdd','isOdd'])
5.2.4 mapMutations方法
用于帮助我们生成与mutations对话的方法,既:包含$storemit(xxx)的函数。
//对象式 // ...mapMutations({add:'ADD',sub:'SUB'}), //数组式 ...mapMutations(['ADD','SUB']),
5.2.5 注意
mapActions与mapMutations使用时,若需要参数,应在模板绑定事件时传递参数,否则参数是事件对象。
5.6 模块化+命名空间
(1)目的:让代码更好维护,多种数据分类更加明确。
(2)修改store.js,拆分为两个:person.js,count.js
person.js
//人员功能相关的 export default { namespaced:true, actions: { addPerson(context, value) { console.log("store.actions.addPerson") if (value != "") { contextmit("ADDPERSON", value) } else { alert("请输入姓名") } } }, mutations: { ADDPERSON(state, value) { state.personList.push(value) } }, state: { personList: ['张三'] }, getters: {} }
conut.js
//求和功能相关的 export default { namespaced:true, actions: { isOdd(context, value) { console.log("store.actions.isOdd", value) if (context.state.sum % 2 != 0) { console.log("store.actions.isOdd") contextmit("ADD", value) } }, sleepAdd(context, value) { console.log("store.actions.sleepAdd") setTimeout(() => { contextmit("ADD", value) }, 1000); }, }, mutations: { ADD(state, value) { // console.log("store.mutations.ADD,state=",state,"value=",value) console.log("store.mutations.ADD") state.sum += value }, SUB(state, value) { console.log("store.mutations.state") // console.log("store.mutations.ADD,state=",state,"value=",value) state.sum -= value }, }, state: { sum: 0, }, getters: { bigSum(state) { return state.sum * 10 } } }
index.js
//该文件用于vuex中最为核心的store //引入Vue import Vue from 'vue' //引入vuex import Vuex from 'vuex' import sumOptions from './count' import personOptions from './person' Vue.use(Vuex) //创建并暴露store export default new Vuex.Store({ modules:{ coundAbout:sumOptions, personAbout:personOptions, } })
(3)namespaced:true开启命名空间。
(4)开启命名空间后组件,读取state中的数据:
//方式1 直接读取 this.$store.state.personAbout.personList //方式2 借助mapState读取 ...mapState('countAbout',[sum])
(4)开启命名空间后,组件读取getters中的数据:
//方式1 直接读取 this.$store.getters['personAbout/firstName'] //方式2 借助mapGetters读取 ...mapState('countAbout',['bigSum'])
(5)开启命名空间后,组件调用dispatch:
//方式1 直接调用dispatch this.$store.dispatch('personAbout/addPerson',{name:'王'}) //方式2 借助mapActions读取 ...mapActions('countAbout',['isOdd','sleepAdd'])
(6)开启命名空间后,组件调用commit:
//方式1 直接调用commit this.$storemit('personAbout/ADD_PERSON',{name:'王'}) //方式2 借助mapMutations读取 ...mapMutations('countAbout',['ADD','SUB'])
6.vue中的路由
6.1 相关概念
6.1.1 vue-router 相关概念
vue中的一个插件库,专门用来实现SPA应用。
6.1.2 SPA应用相关概念
(1)单页Web应用(single page web application 既:SPA)。
(2)整个应用只有一个完整的页面。
(3)点击页面中的导航链接,不会刷新页面,只会做局部更新。
(4)通过ajax获取数据。
6.1.3 路由的相关概念
(1)路由的概念:
(1.1)一个路由就是一组映射关系(key-value)。
(1.2)key为路径,value是function或者component。
(2)路由分类:
(2.1)后端路由:
(2.1.1)概念:value是function,用于处理客户端提交的请求。
(2.1.2)工作过程:服务器接收到一个请求时,根据请求路径找到匹配的函数来
处理请求,返回响应数据。
(2.2)前端路由:
(2.2.1)概念:value是一个component,用于展示页面内容。
(2.1.2)工作过程:当浏览器路径发生改变时,就会展示对应的组件。
6.2 路由的基本使用
(1)安装vue-router:命令:npm i vue-router(如果是vue2则使用npm i vue-router@3)。
(2)应用插件:Vue.use(VueRouter)。
(3)编写router配置项:创建router/index.js。
//该文件用于创建整个应用的路由器 import VueRouter from 'vue-router' import Class from "../compnents/Class.vue"; import Subject from "../compnents/Subject.vue"; //创建并暴露一个路由 export default new VueRouter({ routes:[ { path:'/class', component:Class },{ path:'/subject', component:Subject } ] })
(4)实现切换:active-class表示点击后触发的样式,to表示跳转的路由连接。
<div class="nav"> <h3>导航</h3> <ul> <!-- router-like实现路由的切换,to表示拼接路径,actice-class表示点击后添加的class --> <li> <router-link to="/class" active-class="clickRouter" >班级</router-link > </li> <li> <router-link to="/subject" active-class="clickRouter" >学科</router-link > </li> </ul> </div>
(5)指定点击导航后展示的内容位置:
<div class="content"> <!-- router-view实现组件的切换 --> <router-view></router-view> <!-- <Subject /> <Class /> --> </div>
6.3 注意事项
(1)通过路由配置实现展示的组件(既通过<router-view/>实现展示的组件)称之为路由组件,通过编写标签实现展示的,称之为一般组件,一般组件通常存放在components文件夹,路由组件一般存放在pages文件夹。
(2)通过切换实现展示后,不展示的路由组件默认是被销毁的,当切换路由展示路由组件时,会再去重新挂载。
(3)每个组件都有自己的$route属性,里面存储当前组件的路由信息(每个组件$route属性不相同)。
(4)整个组件只有一个router,可以通过$router属性获取到(每个组件$router属性相同)。
6.4 嵌套(多级)路由
(1)配置路由规则时使用children配置项:
//该文件用于创建整个应用的路由器 import VueRouter from 'vue-router' import Class from "../pages/Classes"; import Subject from "../pages/Subject"; import OneClass from '../pages/OneClass' import TwoClass from '../pages/TwoClass' //创建并暴露一个路由 export default new VueRouter({ routes: [ { path: '/class', component: Class, children: [ { path: 'oneClass', component: OneClass, }, { path: 'twoClass', component: TwoClass, }, ] }, { path: '/subject', component: Subject } ] })
(2)使用时要写完整路径:
<ul> <!-- router-like实现路由的切换,to表示拼接路径,actice-class表示点击后添加的class --> <li> <router-link to="/class/oneClass" active-class="clickRouter">一班</router-link> </li> <li> <router-link to="/class/twoClass" active-class="clickRouter">二班</router-link> </li> </ul>
(3)注意:二级路由配置时不要写"/",但是访问时路径要写全路径(加上父路径)。
6.5 query参数
(1)传递参数:
(1.1)直接拼接:
<ul> <!-- router-like实现路由的切换,to表示拼接路径,actice-class表示点击后添加的class --> <li v-for="item in classes" :key="item.index"> <router-link to="/classes/class?name=一班&content=一班内容" active-class="clickRouter" >{{item.name}}</router-link> </ul>
(1.2)对象写法:
<ul> <!-- router-like实现路由的切换,to表示拼接路径,actice-class表示点击后添加的class --> <li v-for="item in classes" :key="item.index"> <router-link :to="{ path: '/classes/class', query: { name: item.name, content: item.content, }, }" active-class="clickRouter" >{{item.name}}</router-link > </li> </ul>
(2)接收参数:
<div> <h1>名称:{{$route.query.name}}</h1> <h1>内容:{{$route.query.content}}</h1> </div>
6.6 命名路由
(1)作用:简化路由的跳转。
(2)使用:
(2.1)给路由命名:
//该文件用于创建整个应用的路由器 import VueRouter from 'vue-router' import Classes from "../pages/Classes"; import Subject from "../pages/Subject"; import Class from '../pages/Class' //创建并暴露一个路由 export default new VueRouter({ routes: [ { name:'班级', path: '/classes', component: Classes, children: [ { name:'班内信息', path: 'class', component: Class, }, { path: 'subject', component: Subject } ] } ] })
(2.2)使用:
<router-link :to="{ path: '/classes/class', query: { name: item.name, content: item.content, }, }" active-class="clickRouter" >{{item.name}} </router-link> <!--简化为--> <router-link :to="{ name: '班内信息', query: { name: item.name, content: item.content, }, }" active-class="clickRouter" >{{item.name}} </router-link>
若是路径过于繁琐,可以不使用路径,转而使用name属性。
6.7 params参数
(1)配置路由,声明接收params的参数(path中使用占位符):
//该文件用于创建整个应用的路由器 import VueRouter from 'vue-router' import Classes from "../pages/Classes"; import Subject from "../pages/Subject"; import Class from '../pages/Class' //创建并暴露一个路由 export default new VueRouter({ routes: [ { name:'班级', path: '/classes', component: Classes, children: [ { name:'班内信息', path: 'class/:name/:content', component: Class, }, { path: 'subject', component: Subject } ] } ] })
(2)传递参数:
<li v-for="item in classes" :key="item.index"> <router-link to="/classes/class/班级名称/内容" active-class="clickRouter"> {{item.name}}</router-link> </li> <!--或者--> <li v-for="item in classes" :key="item.index"> <router-link :to="{ name: '班内信息', params: { name: item.name, content: item.content, }, }" active-class="clickRouter">{{ item.name }}</router-link > </li>
注意:如果用对象写法一定要用name属性跳转,而不是path属性。
(3)接收参数:
<div> <h1>名称:{{$route.params.name}}</h1> <h1>内容:{{$route.params.content}}</h1> </div>
6.8 props配置
(1)作用:让路由组件更加方便的接受到参数:
{ name: '班内信息', path: 'class/:name/:content', component: Class, // 第一种写法 值为对象 局限性比较大一般不用 /*props:{ name:"name", content:"content" } */ // 第二种写法 值为Boolean 如果值为true,则将所有收到的param参数以props的形式传递给该组件,query接收不到 // props:true // 第三种写法 值为函数 props($route){ return{ name:$route.query.name, content:$route.query.content } } }
6.9 route-link的replace
(1)作用:控制路由跳转时操作浏览器历史记录的模式。
(2)浏览器历史记录写入模式有两种,push和replace,push是追加历史记录,replace是替换当前记录,默认是push。
(3) <route-link replace>或<route-link :replace='true'>开启replace模式。
6.10 编程式路由导航
(1)作用:不借助<router-link>实现跳转,让路由跳转更加灵活。
(2)编码:
pushShow(item){ this.$router.push({ name: '班内信息', query: { name: item.name, content: item.content, }, }) }, replaceShow(item){ this.$router.replace({ name: '班内信息', query: { name: item.name, content: item.content, }, }) }, //返回 back() { this.$router.back(); }, //前进 forward() { this.$router.forward(); }, //前进或后退指定步数 testGo() { this.$router.go(-1); },
6.11 路由缓存
(1)作用:让不展示的路由组件保持挂载,不被销毁。
(2)具体编码:
<keep-alive include="Classes"> <router-view ></router-view> </keep-alive>
(3)注意:include如果不写则默认全部,内容为组件的name属性,并非router内配置的name属性。缓存多个写法为:
<keep-alive :include="['Classes','Subject']"> <router-view ></router-view> </keep-alive>
6.12 新生命周期钩子
(1)作用:路由组件所独有的生命周期钩子,用于捕获组件是否处于激活状态
(2)使用:
activated() { console.log("Class被激活") this.timeId= setInterval(() => { this.opacity -= 0.01; if (this.opacity < 0) { this.opacity = 1; } },15); }, deactivated() { console.log("Class失活") clearInterval(this.timeId) },
(3)补充:$nextTick也属于生命舟曲钩子,当更新数据的dom更新完毕再执行$nextTick内的代码。
6.13 路由守卫
(1)作用:对路由进行权限控制。
(2)分类:全局守卫,独享守卫,组件内守卫。
(3)全局守卫:
//全局前置路由守卫,每次初始化之前或者路由切换之前, router.beforeEach((to, from, next) => { console.log("前置路由守卫",from,to) let user = localStorage.getItem("user") if (to.meta.isAuth) { if (user == 'user') { next() }else{ alert(user+'无权查看') } }else{ next() } }) //全局前置路由守卫,每次初始化之前或者路由切换之后, router.afterEach((to, from) => { console.log("后置路由守卫",from,to) document.title=to.name })
(4)独享守卫:
独属于某一个路由的路由守卫,且没有afterEnter路由守卫。
{ name: '班内信息', path: 'class/:name/:content', meta:{ isAuth:true }, component: Class, beforeEnter:(to, from, next) => { console.log("前置路由守卫",from,to) let user = localStorage.getItem("user") if (to.meta.isAuth) { if (user == 'user') { next() }else{ alert(user+'无权查看') } }else{ next() } } }
(5)组件内路由守卫:
通过路由规则,访问或离开该组件时,会调用。(直接使用标签访问组件不会调用)
//通过路由规则进入该组件时调用 beforeRouteEnter(to, from, next){ next() }, //通过路由规则离开该组件时调用 beforeRouteLeave (to, from, next) { next() }
6.14 路由的hash和history
(1)对于一个url来说,#及其后面的内容就是hash值。
(2)hash值不会包含在http请求中。(既不会发送给服务器)
(3)hash模式:
(3.1)地址中永远带有#,不美观。
(3.2)若以后通过第三方手机app分享,若app校验较为严格,则会被认定为不合法。
(3.3)兼容性好。
(4)history模式:
(4.1)地址干净,美观。
(4.2)兼容性比起hash模式略差。
(4.3)应用部署上线时,需要后端人员支持,解决刷新页面在服务器404的问题。
8.Vue3快速上手
vue3地址
地址:awesome vue 3 & vue-cli 3+ · GitHub
8.2 Vue3优势
(1)性能的提升。
(2)源码升级。
(3)支持TypeScript。
(4)新特性。
8.1创建Vue3工程
官方文档:快速上手 | Vue.jsVue.js - 渐进式的 JavaScript 框架https://cn.vuejs/guide/quick-start.html
8.1.1 vue-cli创建
##1.查看vue-cli版本: vue --version vue -V ##2.如果版本在4.5.0以下升级或安装执行: npm install -g @vue/cli ##3.创建: vue create vue3_test ##4.启动: cd vue_test npm run serve
8.1.2 vite 创建
vite 官网:
Vite | 下一代的前端工具链下一代前端工具链https://cn.vitejs.dev/weback打包模式:
vite打包模式:
##1.创建工程: npm init vite-app <project-name> ##2.进入工程目录: cd <project-name> ##3.安装依赖: npm install ##4.启动: npm run serve
8.2常用composition API
组合式API
8.2.1 setup函数
(1)概念:Vue3中的一个新的配置项,值为一个函数。
(2)setup是所有Compositon API的“表演的舞台”。
(3)组件中所用到的:数据,方法等等,均要在setup中配置。
(4)setup函数中的两种返回值:
(4.1)若返回一个对象,则对象中的属性,方法,在模板中均可以直接使用(常用)。
(4.2)若返回一个渲染函数,则可以自定义渲染内容(了解)。
(5)注意:
(5.1)尽量不要与Vue2.x混用:
(5.1.1)Vue2.x中的(data,methods,computed等等)可以访问到setup的方
法,属性。
(5.1.2)setup中不能访问到Vue2.x中的(data,methods,computed等等)。
(5.1.3)如果重名,setup优先级高。
(5.2)setup不能是一个async函数,因为返回值不再是一个return对象,而是
promise,模板看不到return中的属性
总结:setup用于替代 Vue2 中的 beforeCreate 和 created 钩子函数。setup 选项是一个函数,它在组件实例被创建之前执行,并返回一个包含状态和方法等配置信息的对象。
import { h } from "vue"; export default { name: "App", setup() { let person = { name: "张三", age: 18, }; function sayHello() { alert(`${person.name}:hello word,年龄:${person.age}`); } //返回一个对象 return { person, sayHello, }; //返回一个渲染对象 /* return ()=>{ return h('h1',person.name) } */ }, };
8.2.2 ref函数
(1)作用:用于定义一个响应式的数据。
(2)语法:const xxx=ref(initValue)。
(2.1)创建一个包含响应式数据的引用对象(reference对象)。
(2.2)js中操作数据用xxx.value操作。
(2.3)模板中读取:直接{{xxx}}。
(3)备注:
(3.1)接收基本类型:响应式还是依靠Object.defineProperty()的get与set实现。
(3.2)接收对象类型:内部使用了Vue3中的一个新函数reactive函数,底层是用Proxy
实现的。
import { ref } from "vue"; export default { name: "App", setup() { /* let person = { name: ref("张三"), age: ref(18), }; */ let person =ref({ name: "张三", age: 18 }); function changeInfo(){ console.log(person) person.value.age=25 } return { person, changeInfo }; }, };
8.2.3 reactive函数
(1)作用:定义一个响应式数据(基本数据不用他,用ref函数)。
(2)语法:const 代理对象=reactive(源对象) 接受一个对象或数组,返回一个代理对象(proxy对象)。
(3)reactive定义的响应式数据是深层次的。
(4)内部基于es的proxy实现,通过代理对象操作源对象内部的数据。
let person = reactive({ name: "张三", age: 18, hobby: [1, 2, 3, 4, 5], a: { b: { c: { d: 666, }, }, }, }); function changeInfo() { person.hobby[0]=111; } return { person, changeInfo, };
8.2.4 响应式原理
8.2.4.1 vue2中的响应式
对象类型:通过Object.defineProperty()对属性的读取,修改进行拦截(数据劫持)。
数组类型:通过重写数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。
问题:
(1)新增属性,删除属性,界面不会更新。(监测不到数据修改)
(2)不通过制定方法修改数组,界面不会自动更新。(监测不到数据修改)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <script> //源数据 let person = { name: "张三", age: 18, }; //模拟vue2中的响应式实现原理 let p = {}; Object.defineProperty(p, "name", { get() { console.log("有人读取了p.name属性"); return person.name; }, set(value) { console.log("有人修改了p.name属性为:", value); person.name = value; }, }); Object.defineProperty(p, "age", { get() { console.log("有人读取了p.age属性"); return person.age; }, set(value) { console.log("有人修改了p.age属性为:", value); person.age = value; }, }); </script> <body> </body> </html>
当我们创建一个响应式对象,修改其中的属性会触发相应的方法,而我们添加的属性,并不是响应式的,并非通过get和set方法操作属性,而vue2的更新dom操作就是在set方法中实现的,所以监控不到属性的新增和删除。
具体参考:1.Vue核心-CSDN博客
Vue核心的1.5小结数据代理以及1.10.5 Vue无法检测到的数据改变和解决方案。
8.2.4.2 vue3中的响应式
(1)了解Reflect(反射):
let person = { name: "张三", age: 18, }; let p={}
当出现如下代码时,会出现语法错误导致页面无法正常渲染。
Object.defineProperty(p, "name", { get() { console.log("有人读取了p.name属性"); return person.name; } }); Object.defineProperty(p, "name", { get() { console.log("有人读取了p.name属性"); return person.name; } });
当我们使用Reflect操作时,失败并不会出现错误,而是返回一个false。
const a= Reflect.defineProperty(p, "name", { get() { console.log("有人读取了p.name属性"); return person.name; } }); const b= Reflect.defineProperty(p, "name", { get() { console.log("有人读取了p.name属性"); return person.name; } }); console.log('a',a,'b',b)
a true b false
所以,当我们使用第一种方法,去操作一个属性有可能会出现异常,导致页面出现问题,而是用第二种方式去操作,会返回一个操作结果是否成功。站在代码兼容性考虑,是有必要使用Reflect来操作属性的。
(2)了解Proxy:
p=new Proxy(person,{ //读取属性调用 get(target,propName){ console.log('有人读取了p的',propName,"属性,值",person.propName) return target[propName] }, //修改,新增属性调用 set(target,propName,value){ console.log('有人修改了p的',propName,"属性,原值",person.propName,'改为:',value) target[propName]=value }, //删除属性调用 defineProperty(target,propName){ console.log('有人删除了了p的',propName,"属性") return delete target[propName] }, })
创建一个Proxy对象当我们对该对象进行属性操作时:
当我们对对象的属性进行增删改查操作时,都会被相应的监控的,并且执行的操作会在person上执行。
(3)总结:当结合了Proxy和Reflect后就形成了vue3的响应式:
Proxy(代理):拦截对象中任意属性的变化(增删改查)。
Reflect(反射):对被代理对象的属性执行操作(不会出现语法错误)。
p=new Proxy(person,{ //读取属性调用 get(target,propName){ console.log('有人读取了p的',propName,"属性,值",person.propName) return Reflect.get(target,propName) }, //修改,新增属性调用 set(target,propName,value){ console.log('有人修改了p的',propName,"属性,原值",person.propName,'改为:',value) Reflect.set(target,propName,value) }, //删除属性调用 defineProperty(target,propName){ console.log('有人删除了了p的',propName,"属性") return Reflect.defineProperty(target,propName) }, })
Proxy和Reflect的浅层理解:Proxy和Reflect-CSDN博客
8.2.5 reactive和ref
从定义角度相比:
ref:用来定义基本数据类型。
reactive:用来定义对象或数组类型数据。
从原理角度相比:
ref:通过Object.defineProperty()的get和set来实现响应式(数据劫持)。
reactive:通过Proxy来实现响应式(数据劫持),并通过Reflect操作源对象内数据。
从使用角度相比:
ref:操作数据需要用xxx.value,读取时不需要xxx.value。
reactive:操作和读取数据都不用xxx.value
8.2.6 setup的两个注意点
(1)setup执行时机在beforeCreate之前,且只执行一次,this是undefined。
(2)setup的参数:
(2.1)props:值对象,包含:组件外传递过来的,且组件内部生命接受了的属性。
(2.2)context:上下文对象:
(2.2.1)attrs:值对象,外部传递进来,但props未接收的属性(this.$attrs)。
(2.2.2)slots:收到的插槽内容,相当于this.$slots。(最好用v-slot命名)。
(2.2.3)emit:分发自定义事件的函数(this.$emit)。
8.2.7 计算与监视属性
8.2.7.1 计算属性 computed
(1)与vue2.x的computed配置功能一致。
(2)写法:
import { computed } from "vue"; setup(props, context) { const data = reactive({ person: { name: { firstName: "张", lastName: "三", }, age: 18, }, }); //计算属性--简写--只考虑回写没考虑修改 /* data.person.fullName=computed(()=>{ return data.person.name.firstName+"--"+ data.person.name.lastName }) */ //计算属性--完整写法 data.person.fullName = computed({ get() { return data.person.name.firstName + "-" + data.person.name.lastName; }, set(value) { const nameArr = value.split("-"); data.person.name.firstName = nameArr[0]; data.person.name.lastName = nameArr[1]; }, }); return { data, }; },
8.2.7.2 监视属性 watch
与vue2中的watch功能一致。
问题:
(1)监视reactive定义的响应式数据时,oldValue无法正确获取(由于旧值和新值实际
上是同一个引用,因此旧值和新值看起来总是相同的。),且默认开启了深度监
视,无法关闭(deep配置失效)。
(2)监视reactive定义的响应式数据中的某个对象属性时,deep配置又生效了。
//情况1:监视ref定义的一个响应式数据 watch(sum,(newValue, oldValue)=>{ console.log("sum被修改了", newValue, oldValue); }) //情况2:监视ref定义的多个响应式数据 watch([sum,msg],(newValue, oldValue)=>{ console.log("sum或msg被修改了", newValue, oldValue); }) //传入第三个参数配置对象 watch(sum,(newValue, oldValue)=>{ console.log("sum被修改了", newValue, oldValue); },{immediate:true}) /* 情况3:监视reactive定义的对象 全部属性 1.reactive定义的数据无法正确获取oldValue 2.强制开启了深度监视且无法关闭(deep配置失效了) */ watch(data,(newValue,oldValue)=>{ console.log('person变化了',newValue,oldValue) }) /* 情况4:监视reactive定义的对象 某个属性 */ watch(()=>data.age,(newValue,oldValue)=>{ console.log('person变化了',newValue,oldValue) }) /* 情况5:监视reactive定义的对象 某些属性 */ watch([()=>data.age,()=>data.name],(newValue,oldValue)=>{ console.log('person变化了',newValue,oldValue) }) /* 特殊情况:监视reactive定义的对象 中的对象属性 */ watch(()=>data.job,(newValue,oldValue)=>{ console.log('person变化了',newValue,oldValue) },{deep:true})
8.2.7.2 补充
(1)监视ref定义的基本数据类型的值时,不可以用xxx.value否则无法监视到。
(2)监视ref定义的对象属性时,需要用xxx.value否则由于内存中地址未改变,无法监视到ref请求reactive生成的Proxy的代理类,也可以开深度监视来实现,监视对象属性中的内容。
8.2.7.3 wactcEffect函数
watcg:既要指明监视的属性,也要指明监视的回调。
watchEffect:不指明监视的属性,在回调中用到了哪个属性就监视哪个属性。
watchEffect优点类似于computed:
computed注重的是结果(回调函数的返回值),所以必须return。
watchEffect注重的是过程(回调的函数体),所以不用写返回值。
watch(data, (newValue, oldValue) => { console.log("person变化了", newValue, oldValue); },{immediate:true}); watchEffect(()=>{ console.log('watchEffect的回调') const x=data.age })
8.2.8 Vue3中的生命周期
Vue3.0中可以继续使用Vue2.x的生命周期钩子,但是有两个被更名:
beforeDestroy===》beforeUnmount destroyed ===》unmounted
Vue3.0也提供了Composition API形式的生命周期钩子,与Vue2.x对应关系如下:
beforeCreate ===> setup() created ===> setup() beforemount ===> onBeforeMount mounted ===> onMounted befoerUpdate ===> onBeforeUpdate updated ===> onUpdated beforeUnmount ===> onBeforeUnmount unmounted ===> onUnmounted
App:
<template> <Demo > </Demo> </template> <script> import Demo from "./componets/Demo.vue"; export default { name: "App", components: { Demo, }, setup() { }, }; </script>
Demo:
<template> <div class="container"> <!-- <h1>当前求和值为:{{ sum }}</h1> <button @click="add">点我++</button> <h1>当前求和值为:{{ msg }}</h1> <button @click="editMsg">点我修改信息</button> --> <h1>姓名:{{ data.name }}</h1> <h1>年龄:{{ data.age }}</h1> <button @click="data.name+='@'">修改姓名</button> <button @click="data.age++">点我修改年龄</button> </div> </template> <script> import { reactive, toRefs, onBeforeMount, onMounted,watch,ref } from "vue"; export default { name: "App", watch: { //简写无法深度监视 /* data(newValue,oldValue){ console.log('sum被修改了',newValue,oldValue) } */ //完整写法 /* data: { immediate: true, deep:true, handler(newValue, oldValue) { console.log("sum被修改了", newValue, oldValue); }, }, */ }, setup() { let sum=ref(0) let msg=ref('信息') const data = reactive({ name: '张三', age:18, job:{ j1:1000 } }); //情况1:监视ref定义的一个响应式数据 watch(sum,(newValue, oldValue)=>{ console.log("sum被修改了", newValue, oldValue); }) //情况2:监视ref定义的多个响应式数据 watch([sum,msg],(newValue, oldValue)=>{ console.log("sum或msg被修改了", newValue, oldValue); }) //传入第三个参数配置对象 watch(sum,(newValue, oldValue)=>{ console.log("sum被修改了", newValue, oldValue); },{immediate:true}) /* 情况3:监视reactive定义的对象 全部属性 1.reactive定义的数据无法正确获取oldValue 2.强制开启了深度监视且无法关闭(deep配置失效了) */ watch(data,(newValue,oldValue)=>{ console.log('person变化了',newValue,oldValue) }) /* 情况4:监视reactive定义的对象 某个属性 */ watch(()=>data.age,(newValue,oldValue)=>{ console.log('person变化了',newValue,oldValue) }) /* 情况5:监视reactive定义的对象 某些属性 */ watch([()=>data.age,()=>data.name],(newValue,oldValue)=>{ console.log('person变化了',newValue,oldValue) }) /* 特殊情况:监视reactive定义的对象 中的对象属性 */ watch(()=>data.job,(newValue,oldValue)=>{ console.log('person变化了',newValue,oldValue) },{deep:true}) return { data, // add,sum,msg,editMsg }; }, }; </script> <style lang="scss" scoped> </style>
8.2.9 自定义hook函数
定义:本质是一个函数,把steup函数中使用的Composition API进行的封装。
类似于Vue2.x中的mixin。
自定义hook的优势:服用代码,让setup中的逻辑更加清楚。
新建hooks文件夹,并创建功能对应的js文件(以点击获取鼠标当前xy轴位置为例):
userPoint.js
import {
reactive,
onMounted,
onBeforeUnmount
} from "vue";
export default function () {
let point = reactive({
x: 0,
y: 0
})
function savePiont(event) {
point.x = event.pageX
point.y = event.pageY
console.log(event.pageX)
console.log(event.pageY)
}
onMounted(() => {
window.addEventListener('click', savePiont)
}),
onBeforeUnmount(() => {
window.removeEventListener('click', savePiont)
})
return point
}
Demo.vue
<template>
<div class="container">
<h1>当前鼠标X:{{point.x}},Y:{{point.y}}</h1>
</div>
</template>
<script>
import {
reactive
} from "vue";
import userPoint from '../hook/userPoint'
export default {
name: "Demo",
watch: {},
setup() {
let point= userPoint()
//使用组合式API
return {
point
};
}
};
</script>
<style lang="scss" scoped>
</style>
8.2.10 toRef
作用:创建一个ref对象,其value指向另一个对象中的某个属性。
语法:const name=toRef(person,'name')。
应用:将响应式对象中的某个属性单独提供给外部使用时。
扩展:toRefs与toRef功能一致,但是可以创建多个ref对象,已发toRefs(person)。
<template> <h1>Vue3</h1> <h2> 姓名: {{ name }}<br> 年龄:{{ person.age }}<br> 性别:{{ person.sex }}<br> 爱好:{{ person.hobby }}<br> 爱好:{{ person.job.j1.pay }}<br> <button @click="changeName">修改名字信息</button> <button @click="deleteAge">删除属性年龄</button> <button @click="addSex">添加一个属性sex</button> <button @click="changeArr">通过下标修改数组</button> <button @click="person.job.j1.pay+=100">涨薪</button> </h2> </template> <script> import { reactive,toRef,toRefs } from "vue"; export default { name: "App", setup() { let person = reactive({ name: "张三", age: 18, hobby: [1, 2, 3, 4, 5], job:{ j1:{ pay:1000 } } }); function changeName() { person.name="李四" } function addSex() { person.sex="男" } function changeArr() { person.hobby[2]=999 } function deleteAge() { delete person.age } return { person, /* name:toRef(person,'name'), pay:toRef(person.job.j1,'pay'), */ ...toRefs(person), addSex, changeArr, changeName, deleteAge, }; }, }; </script>
8.3 其他Composition API
8.3.1 shallowReactive和shallowRef
shallowReactive:只处理对象最外层属性的响应式(浅响应式)。
shallowRef:只处理基本数据类型的响应式,不进行对象响应式的处理。
使用场景:
如果有一个对象,结构比较深,但是变化时值是外层属性变化===》shallowReactive。
如果有一个对象,后续不会修改对象属性,而是产生新的对象替换===》shallowRef。
8.3.2 readonly和shallowReadonly
readonly:让一个响应式数据变为只读的(深只读)。
shallowReadonly:让一个响应式数据变为只读的(浅只读)。
使用场景:不希望数据被修改时。
8.3.3 toRaw和markRaw
toRaw:
作用:讲一个有reactive生成的响应式对象转为普通对象。(ref的不行)
使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作不会引起
页面的更新。
markRaw:
作用:标记一个对象,使其永远不会在成为响应式对象。
使用场景:
(1)如果我们声明了一个reactive的对象,后续对这个对象进行属性添加的操作,
添加上的属性默认就是响应式的,而有些值不应该设置为响应式的,例如:
异常复杂的第三方类库等等。
(2)当显然具有不可变数据源的大列表时,条过响应式转换可以提高性能。
8.3.4 customRef
作用:创建一个自定义的ref,并对其依赖项跟踪和更新出发进行显示控制(可以实现自定义的控制页面更新时机,track告诉get方法这个参数需要跟踪,triger通知vue更新dom)。
实现防抖:
<template> <h1>Vue3</h1> <h2> <input type="text" v-model="keyWords" /> {{ keyWords }} </h2> </template> <script> import { reactive, ref, customRef } from "vue"; export default { name: "App", setup() { //自定义ref function myRef(value,delay) { let timer; return customRef((track,triger) => { return { get() { console.log("---myRef里的get---",value); track()//通知vue监视这个值的改变 return value; }, set(newValue) { console.log("---myRef里的set---",newValue); //通知vue重新解析模板 clearTimeout(timer) timer= setTimeout(() => { value=newValue triger() }, delay); }, }; }); } // let keyWords=ref('hello')//使用Vue提供的ref let keyWords = myRef("hello",500); //使用自己的ref return { keyWords, }; }, }; </script>
8.3.5 provide与inject
作用:实现祖与后代组件之间的通信。
父组件中有一个provide选项来提供数据,后代组件有一个inject选项来开始使用这些数据。
具体写法:
祖组件:
setup() { let car=reactive({ name:'奔驰', price:400000 }) //给自己的后代组件传递数据 provide('car',car)
后代组件:
let car=inject('car') console.log("二级子组件",car) },
8.3.6 响应式数据的判断
isRef:检查一个值是否为ref对象。
isReactive:检查一个值是否是由reactive创建的响应式代理。
isReadonly:检查一个对象是否是由readonlu创建的只读代理。
isProxy:检查一个对象是否由reactive或者readonly方法创建。
8.4 Composition API的优势
8.4.1 Options API
传统的OptionsAPI(配置API)中,新增或修改一个需求,要分别在data,methods,computed里修改
8.4.2 Composition API
让相同功能的变量,函数等等更加有序的组织在一起(借助hook函数)。
8.5 新的组件
8.5.1 Fragment
在Vue2.x中,组件必须有一个跟标签。
在Vue3中,组件可以没有跟标签,内部会将多个标签包含在一个Fragment的虚拟内存中。
优势:减少标签层级,减小内存占用。
8.5.2 Teleprot
概念:Teleprot是一种能够将我们组件中的html结构移动到指定位置的技术。
<template> <div> Dialog <button @click="isShow = true">点我弹框</button> <teleport to="body"> <div class="dialog" v-show="isShow"> 弹框 内容 <button @click="isShow = false">关闭</button> </div> </teleport> </div> </template> <script> import { ref } from "vue"; export default { name: "Dialog", setup() { let isShow = ref(false); return { isShow, }; }, }; </script> <style scoped> .dialog { background-color: green; width: 300px; height: 300px; } </style>
将代码直接传送到html的body内。
8.5.3 Suspense
等待异步组件时渲染一些额外内容,让应用有更好的用户体验。
使用步骤:
异步引入组件:
import { defineAsyncComponent } from "vue"; const Demo = defineAsyncComponent(() => import("./componets/Demo1.vue"));
使用Supense包裹组件,并配置好default与fallback:
<Suspense> <template v-slot:default> <Demo></Demo> </template> <template v-slot:fallback> <div> <h2>加载中....</h2> </div> </template> </Suspense>
8.6 其他
8.6.1 全局API转移
Vue2.x中有序地全局API和配置。
例如:注册全局组件,注册全局指令等等
//全局组件 Vueponent('MyButton',{ data:()=>({ cont:0 }), template:'<button @click='cont++'>{{cont}}++</button>' }) //全局指令 Vue.directive('focus',{ inserted:el=>el.focus() })
Vue3对这些API做出了调整:
将全局API(Vue.xxx)调整到应用实例app上
8.6.2 其他改变
8.6.2.1 data选项
data选项始终都要被声明为一个函数。
8.6.2.2 过渡类名的更改
Vue2.x写法:
v-enter, v-leave-to{ opacity:0 } v-leave, v-enter-to{ opacity:1 }
Vue3写法:
.v-enter-from, v-leave-to{ opacity:0 } .v-leave-from, v-enter-to{ opacity:1 }
8.6.2.3 移除keyCode
移除keyCode作为v-on修饰符,同时也不再支持config.keyCodes
8.6.2.4 移除native
移除native作为v-on修饰符:
父组件绑定事件:
<my-component v-on:close='dialogClose' v-on:click='dialogClick' />
子组件js声明自定义事件(不声明则被认为是js原生事件):
export default { emits:['close'] }
8.6.2.5 移除过滤器
过滤器虽然看起来很方便,但是他需要一个自定义语法,打破大括号内表达式只是JavaScript的假设,不仅有学习成本还有实现成本,建议用方法或计算属性替换过滤器。
7.Vue UI库
7.1移动端常用的UI库
(1) Vant:Vant 4 - A lightweight, customizable Vue UI library for mobile web apps.A lightweight, customizable Vue UI library for mobile web apps.https://vant-ui.github.io/vant/#/zh-CN
(2)Cube UI:cube-ui: cube-ui 是由滴滴开源的基于 Vue.js 实现的移动端组件库https://gitee/mirrors/cube-ui
(3)Mint UI:
Mint UIhttps://mint-ui.github.io/#!/zh-cn
7.2 PC端常用UI库
(1)Element UI:
Element - The world's most popular Vue UI frameworkElement,一套为开发者、设计师和产品经理准备的基于 Vue 2.0 的桌面端组件库https://element.eleme.io/(2)IView UI:
iView / View Design 一套企业级 UI 组件库和前端解决方案基于 Vue.js 的 UI 组件库,用于研发企业级中后台产品。iView 官网。https://www.iviewui/
使用UI组件库一般按需引入。
欢迎补充。
本文标签: 学习笔记
版权声明:本文标题:Vue2-Vue3学习笔记(全) 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.freenas.com.cn/jishu/1729002725h1305586.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论