vue笔记
官网 https://cn.vuejs.org
API 相当于字典,使用的时候可以查询下
尚硅谷vue技术学习
vue基础、vue-cli、vue-router、vuex、element-ui、vue3
学习vue之前要掌握的知识
ES6语法规范
ES6模块化
包管理器
原型、原型链
数组常用方法
axios
promise
<script type="text/javascript" src="../js/vue.js">script>
<script type="text/javascript">
//设置为 false 以阻止 vue 在启动时生成生产提示
Vue.config.productionTip = false;
script>
初识Vue
注意区分:js表达式 和 js代码(语句)
1.表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方;
如:a,a+b,demo(1),x===y?‘a’:‘b’
2.js代码(语句)
如:if(){},for(){}
<div id="root">
<h1>helle,{{name}}h1>
div>
<script type="text/javascript">
//设置为 false 以阻止 vue 在启动时生成生产提示
Vue.config.productionTip = false;
// 创建vue实例
const x = new Vue({
el: '#root', //el用于指定当前Vue实例为哪个容器服务,值通常为css选择器字符串
data: {//data中用于存储数据,数据供el所指定的容器去使用,值我们暂时先写成一个对象
name: '尹礼胜'
}
})
script>
vue模板语法有两大类
插值语法
功能:用于解析标签提内容
写法:{{xxx}},xxx是js表达式,且可以直接读取到data中的所有属性
指令语法
功能:用于解析标签(包括:标签属性、标签体内容、绑定事件…)
举例:v-bind:href=“xxx” 或 简写为 :href=“xxx”,xxx同样要写js表达式,且可以直接读取到data中的所有属性
备注:vue中有很多的指令,切形式都是:v-???,此处我们只是拿v-bind举个例子
<div id="root">
<h3>helle,{{name}}h3>
<a v-bind:href="school.url" :x="url">点我去{{school.name}}学习a>
div>
<script type="text/javascript">
Vue.config.productionTip = false;
const x = new Vue({
el: '#root',
data: {
name: '尹礼胜',
school: {
name: "上海大学",
url: "https://www.shu.edu.cn/"
},
url: "www.baidu.com"
}
})
script>
DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>数据绑定title>
<script type="text/javascript" src="../js/vue.js">script>
head>
<body>
<div id="root">
单向数据绑定:<input type="text" :value="name"><br/>
双向数据绑定:<input type="text" v-model="name"><br/>
div>
body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
new Vue({
el:'#root',
data:{
name:'尚硅谷'
}
})
script>
html>
1.Vue中的数据代理:
通过vm对象来代理data对象中属性的操作(读/写)
2.Vue中数据代理的好处:
更加方便的操作data中的数据
3.基本原理:
通过Object.defineProperty()把data对象中所有属性添加到vm上。
为每一个添加到vm上的属性,都指定一个getter/setter。
在getter/setter内部去操作(读/写)data中对应的属性。
vm.name vm._data.name vm.options.name
options 创建vue实例vm时输入对象
<div id="root">
<h3>{{username}}h3>
<h3>{{userId}}h3>
div>
<script>
Vue.config.productionTip = false;
const vm = new Vue({
el: "#root",
data: {
username: "ZhangSan",
userId: "10001000"
}
})
console.log(vm)
script>
事件修饰符,捕获:由外往内,冒泡:由内往外
event 事件对象
事件的基本使用:
1.使用v-on:xxx 或 @xxx 绑定事件,其中xxx是事件名;
2.事件的回调需要配置在methods对象中,最终会在vm上;
3.methods中配置的函数,不要用箭头函数!否则this就不是vm了;
4.methods中配置的函数,都是被Vue所管理的函数,this的指向是vm 或 组件实例对象;
5.@click=“demo” 和 @click=“demo($event)” 效果一致,但后者可以传参;
<body>
<div id="root">
<h2>欢迎来到{{name}}学习h2>
<button @click="showInfo1">点我提示信息1(不传参)button>
<button @click="showInfo2($event,66)">点我提示信息2(传参)button>
div>
body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el: '#root',
data: {
name: '尚硅谷',
},
methods: {
showInfo1(event) {
// console.log(event.target.innerText)
// console.log(this) //此处的this是vm
alert('同学你好!')
},
showInfo2(event, number) {
console.log(event, number)
// console.log(event.target.innerText)
// console.log(this) //此处的this是vm
alert('同学你好!!')
}
}
})
script>
Vue中的事件修饰符:
1.prevent:阻止默认事件(常用);
2.stop:阻止事件冒泡(常用);
3.once:事件只触发一次(常用);
4.capture:使用事件的捕获模式;
5.self:只有event.target是当前操作的元素时才触发事件;
6.passive:事件的默认行为立即执行,无需等待事件回调执行完毕;
DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>事件修饰符title>
<script type="text/javascript" src="../js/vue.js">script>
<style>
* {
margin-top: 20px;
}
.demo1 {
height: 50px;
background-color: skyblue;
}
.box1 {
padding: 5px;
background-color: skyblue;
}
.box2 {
padding: 5px;
background-color: orange;
}
.list {
width: 200px;
height: 200px;
background-color: peru;
overflow: auto;
}
li {
height: 100px;
}
style>
head>
<body>
<div id="root">
<h2>欢迎来到{{name}}学习h2>
<a href="http://www.atguigu.com" @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 class="demo1" @click.self="showInfo">
<button @click="showInfo">点我提示信息button>
div>
<ul @wheel.passive="demo" class="list">
<li>1li>
<li>2li>
<li>3li>
<li>4li>
ul>
div>
body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
new Vue({
el: '#root',
data: {
name: '尚硅谷'
},
methods: {
showInfo(e) {
alert('同学你好!')
// console.log(e.target)
},
showMsg(msg) {
console.log(msg)
},
demo() {
for (let i = 0; i < 100000; i++) {
console.log('#')
}
console.log('累坏了')
}
}
})
script>
html>
计算属性:
1.定义:要用的属性不存在,要通过已有属性计算得来。
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 /><br />
名:<input type="text" v-model="lastName"> <br /><br />
全名:<span>{{fullName}}span> <br /><br />
div>
<script type="text/javascript">
const vm = new Vue({
el: '#root',
data: {
firstName: '张',
lastName: '三',
},
computed: {
//完整写法
/* fullName:{
get(){
console.log('get被调用了')
return this.firstName + '-' + this.lastName
},
set(value){
console.log('set',value)
const arr = value.split('-')
this.firstName = arr[0]
this.lastName = arr[1]
}
} */
//简写
fullName() {
console.log('get被调用了')
return this.firstName + '-' + this.lastName
}
}
})
script>
immediate 初始化调用
deep 深度监视
几种写法
<body>
<div id="root">
<h2>今天天气很{{info}}h2>
<button @click="changeWeather">切换天气button>
div>
body>
<script type="text/javascript">
const vm = new Vue({
el: '#root',
data: {
isHot: true,
},
computed: {
info() {
return this.isHot ? '炎热' : '凉爽'
}
},
methods: {
changeWeather() {
this.isHot = !this.isHot
}
},
watch: {
//正常写法
/* isHot:{
// immediate:true, //初始化时让handler调用一下
// deep:true,//深度监视
handler(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
}
}, */
//简写
/* isHot(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue,this)
} */
}
})
//正常写法
/* vm.$watch('isHot',{
immediate:true, //初始化时让handler调用一下
deep:true,//深度监视
handler(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
}
}) */
//简写
/* vm.$watch('isHot',(newValue,oldValue)=>{
console.log('isHot被修改了',newValue,oldValue,this)
}) */
script>
绑定样式:
<style>
.basic {
width: 400px;
height: 100px;
border: 1px solid black;
}
.happy {
border: 4px solid red;
;
background-color: rgba(255, 255, 0, 0.644);
background: linear-gradient(30deg, yellow, pink, orange, yellow);
}
.sad {
border: 4px dashed rgb(2, 197, 2);
background-color: gray;
}
.normal {
background-color: skyblue;
}
.atguigu1 {
background-color: yellowgreen;
}
.atguigu2 {
font-size: 30px;
text-shadow: 2px 2px 10px red;
}
.atguigu3 {
border-radius: 20px;
}
style>
<div id="root">
<div class="basic" :class="mood" @click="changeMood">{{name}}div> <br /><br />
<div class="basic" :class="classArr">{{name}}div> <br /><br />
<div class="basic" :class="classObj">{{name}}div> <br /><br />
<div class="basic" :style="styleObj">{{name}}div> <br /><br />
<div class="basic" :style="styleArr">{{name}}div>
div>
<script type="text/javascript">
const vm = new Vue({
el: '#root',
data: {
name: '尚硅谷',
mood: 'normal',
classArr: ['atguigu1', 'atguigu2', 'atguigu3'],
classObj: {
atguigu1: false,
atguigu2: false,
},
styleObj: {
fontSize: '40px',
color: 'red',
},
styleObj2: {
backgroundColor: 'orange'
},
styleArr: [
{
fontSize: '40px',
color: 'blue',
},
{
backgroundColor: 'gray'
}
]
},
methods: {
changeMood() {
const arr = ['happy', 'sad', 'normal']
const index = Math.floor(Math.random() * 3)
this.mood = arr[index]
}
},
})
script>
v-if 会销毁 v-show 只是隐藏
条件渲染:
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一定可以获取到。
<div id="root">
<h2>当前的n值是:{{n}}h2>
<button @click="n++">点我n+1button>
<template v-if="n === 1">
<h2>你好h2>
<h2>尚硅谷h2>
<h2>北京h2>
template>
div>
<script type="text/javascript">
const vm = new Vue({
el: '#root',
data: {
name: '尚硅谷',
n: 0
}
})
script>
v-for指令:
1.用于展示列表数据
2.语法:v-for=“(item, index) in xxx” :key=“yyy”
3.可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)
<div id="root">
<h2>人员列表(遍历数组)h2>
<ul>
<li v-for="(p,index) of persons" :key="index">
{{p.name}}-{{p.age}}
li>
ul>
<h2>汽车信息(遍历对象)h2>
<ul>
<li v-for="(value,k) of car" :key="k">
{{k}}-{{value}}
li>
ul>
<h2>测试遍历字符串(用得少)h2>
<ul>
<li v-for="(char,index) of str" :key="index">
{{char}}-{{index}}
li>
ul>
<h2>测试遍历指定次数(用得少)h2>
<ul>
<li v-for="(number,index) of 5" :key="index">
{{index}}-{{number}}
li>
ul>
div>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
persons: [
{ id: '001', name: '张三', age: 18 },
{ id: '002', name: '李四', age: 19 },
{ id: '003', name: '王五', age: 20 }
],
car: {
name: '奥迪A8',
price: '70万',
color: '黑色'
},
str: 'hello'
}
})
script>
面试题:react、vue中的key有什么作用?(key的内部原理)
虚拟DOM中key的作用:
key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】,
随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:
对比规则:
(1).旧虚拟DOM中找到了与新虚拟DOM相同的key:
①.若虚拟DOM中内容没变, 直接使用之前的真实DOM!
②.若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM。
(2).旧虚拟DOM中未找到与新虚拟DOM相同的key
创建新的真实DOM,随后渲染到到页面。
用index作为key可能会引发的问题:
(1)若对数据进行:逆序添加、逆序删除等破坏顺序操作:
会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。
(2)如果结构中还包含输入类的DOM:
会产生错误DOM更新 ==> 界面有问题。
开发中如何选择key?:
(1)最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
(2)如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,
使用index作为key是没有问题的。
DOCTYPE html>
<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>
<div id="root">
<h2>人员列表h2>
<input type="text" placeholder="请输入名字" v-model="keyWord">
<ul>
<li v-for="(p,index) of filPerons" :key="index">
{{p.name}}-{{p.age}}-{{p.sex}}
li>
ul>
div>
<script type="text/javascript">
Vue.config.productionTip = false
//用watch实现
//#region
/* new Vue({
el:'#root',
data:{
keyWord:'',
persons:[
{id:'001',name:'马冬梅',age:19,sex:'女'},
{id:'002',name:'周冬雨',age:20,sex:'女'},
{id:'003',name:'周杰伦',age:21,sex:'男'},
{id:'004',name:'温兆伦',age:22,sex:'男'}
],
filPerons:[]
},
watch:{
keyWord:{
immediate:true,
handler(val){
this.filPerons = this.persons.filter((p)=>{
return p.name.indexOf(val) !== -1
})
}
}
}
}) */
//#endregion
//用computed实现
new Vue({
el: '#root',
data: {
keyWord: '',
persons: [
{ id: '001', name: '马冬梅', age: 19, sex: '女' },
{ id: '002', name: '周冬雨', age: 20, sex: '女' },
{ id: '003', name: '周杰伦', age: 21, sex: '男' },
{ id: '004', name: '温兆伦', age: 22, sex: '男' }
]
},
computed: {
filPerons() {
return this.persons.filter((p) => {
return p.name.indexOf(this.keyWord) !== -1
})
}
}
})
script>
<div id="root">
<h2>人员列表h2>
<input type="text" placeholder="请输入名字" v-model="keyWord">
<button @click="sortType = 2">年龄升序button>
<button @click="sortType = 1">年龄降序button>
<button @click="sortType = 0">原顺序button>
<ul>
<li v-for="(p,index) of filPerons" :key="p.id">
{{p.name}}-{{p.age}}-{{p.sex}}
<input type="text">
li>
ul>
div>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
keyWord: '',
sortType: 0, //0原顺序 1降序 2升序
persons: [
{ id: '001', name: '马冬梅', age: 30, sex: '女' },
{ id: '002', name: '周冬雨', age: 31, sex: '女' },
{ id: '003', name: '周杰伦', age: 18, sex: '男' },
{ id: '004', name: '温兆伦', age: 19, sex: '男' }
]
},
computed: {
filPerons() {
const arr = this.persons.filter((p) => {
return p.name.indexOf(this.keyWord) !== -1
})
//判断一下是否需要排序
if (this.sortType) {
arr.sort((p1, p2) => {
return this.sortType === 1 ? p2.age - p1.age : p1.age - p2.age
})
}
return arr
}
}
})
script>
Vue监视数据的原理:
vue会监视data中所有层次的数据。
如何监测对象中的数据?
通过setter实现监视,且要在new Vue时就传入要监测的数据。
(1).对象中后追加的属性,Vue默认不做响应式处理
(2).如需给后添加的属性做响应式,请使用如下API:
Vue.set(target,propertyName/index,value) 或
vm.$set(target,propertyName/index,value)
如何监测数组中的数据?
通过包裹数组更新元素的方法实现,本质就是做了两件事:
(1).调用原生对应的方法对数组进行更新。
(2).重新解析模板,进而更新页面。
在Vue修改数组中的某个元素一定要用如下方法:
1.使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
2.Vue.set() 或 vm.$set()
特别注意:Vue.set() 和 vm.$set() 不能给vm 或 vm的根数据对象 添加属性!!!
<div id="root">
<h1>学生信息h1>
<button @click="student.age++">年龄+1岁button> <br />
<button @click="addSex">添加性别属性,默认值:男button> <br />
<button @click="student.sex = '未知' ">修改性别button> <br />
<button @click="addFriend">在列表首位添加一个朋友button> <br />
<button @click="updateFirstFriendName">修改第一个朋友的名字为:张三button> <br />
<button @click="addHobby">添加一个爱好button> <br />
<button @click="updateHobby">修改第一个爱好为:开车button> <br />
<button @click="removeSmoke">过滤掉爱好中的抽烟button> <br />
<h3>姓名:{{student.name}}h3>
<h3>年龄:{{student.age}}h3>
<h3 v-if="student.sex">性别:{{student.sex}}h3>
<h3>爱好:h3>
<ul>
<li v-for="(h,index) in student.hobby" :key="index">
{{h}}
li>
ul>
<h3>朋友们:h3>
<ul>
<li v-for="(f,index) in student.friends" :key="index">
{{f.name}}--{{f.age}}
li>
ul>
div>
<script type="text/javascript">
const vm = new Vue({
el: '#root',
data: {
student: {
name: 'tom',
age: 18,
hobby: ['抽烟', '喝酒', '烫头'],
friends: [
{ name: 'jerry', age: 35 },
{ name: 'tony', age: 36 }
]
}
},
methods: {
addSex() {
// Vue.set(this.student,'sex','男')
this.$set(this.student, 'sex', '男')
},
addFriend() {
this.student.friends.unshift({ name: 'jack', age: 70 })
},
updateFirstFriendName() {
this.student.friends[0].name = '张三'
},
addHobby() {
this.student.hobby.push('学习')
},
updateHobby() {
// this.student.hobby.splice(0,1,'开车')
// Vue.set(this.student.hobby,0,'开车')
this.$set(this.student.hobby, 0, '开车')
},
removeSmoke() {
this.student.hobby = this.student.hobby.filter((h) => {
return h !== '抽烟'
})
}
}
})
script>
表单:form 作为元素的呈现,一个结构,通过ajax把数据发给后端
ajax优势:页面不刷新获取数据,页面不刷新把数据送走
收集表单数据:
若:,则v-model收集的是value值,用户输入的就是value值。
若:,则v-model收集的是value值,且要给标签配置value值。
若:
1.没有配置input的value属性,那么收集的就是checked(勾选 or 未勾选,是布尔值)
2.配置input的value属性:
(1)v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值)
(2)v-model的初始值是数组,那么收集的的就是value组成的数组
备注:v-model的三个修饰符:
lazy:失去焦点再收集数据
number:输入字符串转为有效的数字
trim:输入首尾空格过滤
<div id="root">
<form @submit.prevent="demo">
账号:<input type="text" v-model.trim="userInfo.account"> <br /><br />
密码:<input type="password" v-model="userInfo.password"> <br /><br />
年龄:<input type="number" v-model.number="userInfo.age"> <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="study">
打游戏<input type="checkbox" v-model="userInfo.hobby" value="game">
吃饭<input type="checkbox" v-model="userInfo.hobby" value="eat">
<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.lazy="userInfo.other">textarea> <br /><br />
<input type="checkbox" v-model="userInfo.agree">阅读并接受<a href="http://www.atguigu.com">《用户协议》a>
<button>提交button>
form>
div>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
userInfo: {
account: '',
password: '',
age: 18,
sex: 'female',
hobby: [],
city: 'beijing',
other: '',
agree: ''
}
},
methods: {
demo() {
console.log(JSON.stringify(this.userInfo))
}
}
})
script>
过滤器:
定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)。
语法:
1.注册过滤器:Vue.filter(name,callback) 或 new Vue{filters:{}}
2.使用过滤器:{{ xxx | 过滤器名}} 或 v-bind:属性 = “xxx | 过滤器名”
备注:
1.过滤器也可以接收额外参数、多个过滤器也可以串联
2.并没有改变原本的数据, 是产生新的对应的数据
<div id="root">
<h2>显示格式化后的时间h2>
<h3>现在是:{{fmtTime}}h3>
<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>
<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>
<style>
[v-cloak] {
display: none;
}
style>
<div id="root">
<h2>ZTEh2>
<h2>{{name}}h2>
<h2 v-text="name">helloh2>
<h2>"<h3>shanghaih3>"h2>
<h2>{{city}}h2>
<h2 v-html="city">h2>
<h2 v-cloak>{{name}}h2>
<h2 v-once>初始化的n值是:{{n}}h2>
<h2>当前的n值是:{{n}}h2>
<button @click="n++">点我n+1button>
<h2>{{name}}h2>
<h2 v-pre>{{name}}h2>
div>
<script>
new Vue({
el: '#root',
data: {
name: "ZTE",
city: "shanghai
",
n: 1
},
})
script>
需求1:定义一个v-big指令,和v-text功能类似,但会把绑定的数值放大10倍。
需求2:定义一个v-fbind指令,和v-bind功能类似,但可以让其所绑定的input元素默认获取焦点。
自定义指令总结:
一、定义语法:
(1).局部指令:
new Vue({directives:{指令名:配置对象})
new Vue({directives{指令名:回调函数}})
(2).全局指令:
Vue.directive(指令名,配置对象) 或 Vue.directive(指令名,回调函数)
二、配置对象中常用的3个回调:
(1).bind:指令与元素成功绑定时调用。
(2).inserted:指令所在元素被插入页面时调用。
(3).update:指令所在模板结构被重新解析时调用。
三、备注:
1.指令定义时不加v-,但使用时要加v-;
2.指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名。
//定义全局指令
/* Vue.directive('fbind',{
//指令与元素成功绑定时(一上来)
bind(element,binding){
element.value = binding.value
},
//指令所在元素被插入页面时
inserted(element,binding){
element.focus()
},
//指令所在的模板被重新解析时
update(element,binding){
element.value = binding.value
}
}) */
<div id="root">
<h2>{{name}}h2>
<h2>当前的n值是:<span v-text="n">span> h2>
<h2>放大10倍后的n值是:<span v-big="n">span> h2>
<button @click="n++">点我n+1button>
<input type="text" v-fbind:value="n">
div>
<script type="text/javascript">
//定义全局指令
/* 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(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
}
}
}
})
script>
8个
beforeCreate
created
beforeMount
mounted
beforeUpdate
updated
beforeDestroy
destroyed
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NlLJcATI-1693138193624)(vx_images/430224174948648.jpg)]
对应视频教程:P53
存在的问题:
1.依赖关系混乱,不好维护
2.代码复用率不高
组件的基本使用
Vue中使用组件的三大步骤:
一、定义组件(创建组件)
二、注册组件
三、使用组件(写组件标签)
一、如何定义一个组件?
使用Vue.extend(options)创建,其中options和new Vue(options)时传入的那个options几乎一样,但也有点区别;
区别如下:
1.el不要写,为什么? ——— 最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器。
2.data必须写成函数,为什么? ———— 避免组件被复用时,数据存在引用关系。
备注:使用template可以配置组件结构。s
二、如何注册组件?
1.局部注册:靠new Vue的时候传入components选项
2.全局注册:靠Vue.component('组件名',组件)
三、编写组件标签:
一个文件中包含有n个组件
一个文件中只包含有1个组件 a.vue
一个重要的内置关系
vue-cli vite
lintOnSave:false, //关闭语法检查
3.1.1说明
vue脚手架是vue官方提供的标准化开发工具(开发平台)
cd [指定目录]
vue create [项目名字]
cd [项目名字]
npm run serve
关于不同版本的vue:
vue.js与vue.runtime.xxx.js的区别
vue.js是完整版的vue,包含:核心功能+模板解析器
vue.runtime.xxx.js是运行版的vue,只包含核心功能,没有模板解析器
以为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项,需要使用render函数接收到的createElement函数去指定具体内容
类比装修铺瓷砖
第一种:买瓷砖(vue核心)+买工人(模板解析器)====>铺好的瓷砖+工人
第二种:买瓷砖(vue核心)+雇工人(模板解析器)====>铺好的瓷砖
创建vue实例对象
修改默认配置
脚手架默认配置
vue.config.js
ref属性
1.被用来给元素或子组件注册引用信息(id的替代者)
2.应用在html标签上获取的是真实dom元素,应用在组件标签上是组件实例对象(vc)
3.使用方式
打标识:
或 ...
获取:this.$refs.xxx
props
组合式api访问参数
props: ['addStep'],
setup(props) {
// console.log("props", props);
// console.log("props", props.addStep);
const addStep = ref(props.addStep)
安装vuex
npm install vuex@3
vue2只能用vuex3
npm install vuex 或者 npm install vuex@4
vue3只能用vuex4
官网
vuex
概念
专门在 Vue 中实现集中式状态(数据)管理的一个 Vue 插件,对 vue 应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信
什么时候用vuex
多个组件依赖于同一状态
来自不同组件的行为需要变更同一状态
vuex原理图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C88h91KM-1693138193626)(vx_images/84333353744099.png)]
vuex管理的状态对象
它应该是唯一的
const state = {
xxx: initValue
}
const actions = {
zzz({commit, state}, data1){
commit('yyy', {data1})
}
}
const mutations = {
yyy(state, data1){
// 更新state的某个属性
}
}
值为一个对象,包含多个用于返回数据的函数
2. 如何使用?—— $store.getters.xxx
const getters = {
bigSum(state){
return state.sum*10
}
}
一个store包含多个子模块
默认情况下
state是局部命名空间
mutation、action、getter 是全局命名空间
命名空间的作用是为了减少模块之间定义的方法命名冲突的问题
比如说在a模块getter 中定义一个getter1,在b模块getter 中也定义一个getter1 ,在同一空间内,拥有两个相同的名称,程序会报错。
语法 namespaced:true
在模块定义时使用
state 默认就是命名空间
调用的时候需要加上模块名
state
{{ $store.state.模块名.sum }} 或者 this.$store.state.模块名.sum
action(使用dispatch调用)
this.$store.dispatch('模块名/方法名',参数)
mutations(使用commit调用)
this.$store.commit('模块名/方法名', '参数')
getters
{{$store.getters['personAbout/firstPersonName']}}
或者
this.$store.getters['personAbout/firstPersonName']
调用时,state加上模块名,其他不需要加上模块名
state
{{ $store.state.模块名.sum }} 或者 this.$store.state.模块名.sum
getters
{{$store.getters['参数]}}
或者
this.$store.getters['模块名/firstPersonName']
action(使用dispatch调用)
this.$store.dispatch('方法名',参数)
mutations(使用commit调用)
this.$store.commit('方法名', '参数')
// @/store/index.js
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex插件
Vue.use(Vuex)
//准备actions——用于响应组件中的动作
const actions = {
jiaOdd(context, value) {
context.commit('JIA', value)
}
}
//准备mutations——用于操作数据(state)
const mutations = {
JIA(state, value) {
state.sum += value
}
}
//准备state——用于存储数据
const state = {
sum: 0 //当前的和
}
//准备getters——用于将state中的数据进行加工
const getters = {
bigSum(state) {
return state.sum * 10
}
}
//创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state,
getters
})
import Vue from 'vue'
import App from './App.vue'
//引入store
import store from './store'
new Vue({
el:'#app',
render: h => h(App),
store
})
index.js
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//引入模块
import countOptions from './count'
import personOptions from './person'
//应用Vuex插件
Vue.use(Vuex)
//创建并暴露store
export default new Vuex.Store({
modules:{
countAbout:countOptions,
personAbout:personOptions
}
})
count模块
// count模块
export default {
actions: {},
mutations: {},
state: {},
getters: {},
}
person模块
// person模块
export default {
actions: {},
mutations: {},
state: {},
getters: {},
}
state
{{ $store.state.sum }} 或者 this.$store.state.sum
action(使用dispatch调用)
this.$store.dispatch('方法名字', '参数')
mutations(使用commit调用)
this.$store.commit('方法名字', '参数')
getters
{{$store.getters.bigSum}} 或者 this.$store.getters.bigSum
示例
<template>
<div>
<h1>当前求和为:{{ $store.state.sum }}h1>
<h3>当前求和放大10倍为:{{ $store.getters.bigSum }}h3>
div>
template>
<script>
export default {
name: 'Count',
data() {
return { n: 1, }
},
methods: {
increment() {
this.$store.commit('JIA', this.n)
},
incrementOdd() {
this.$store.dispatch('jiaOdd', this.n)
},
},
}
script>
state
{{ $store.state.countAbout.sum }} 或者 this.$store.state.countAbout.sum
action(使用dispatch调用)
this.$store.dispatch('模块名/方法名',参数)
mutations(使用commit调用)
this.$store.commit('模块名/方法名', '参数')
getters
{{$store.getters['personAbout/firstPersonName']}}
或者
this.$store.getters['personAbout/firstPersonName']
示例
<template>
<div>
<h1>人员列表h1>
<h3 style="color:red">Count组件求和为:{{sum}}h3>
<h3>列表中第一个人的名字是:{{firstPersonName}}h3>
<input type="text" placeholder="请输入名字" v-model="name">
<button @click="add">添加button>
<button @click="addWang">添加一个姓王的人button>
<button @click="addPersonServer">添加一个人,名字随机button>
<ul>
<li v-for="p in personList" :key="p.id">{{p.name}}li>
ul>
div>
template>
<script>
import {nanoid} from 'nanoid'
export default {
name:'Person',
data() {
return {
name:''
}
},
computed:{
personList(){
return this.$store.state.personAbout.personList
},
sum(){
return this.$store.state.countAbout.sum
},
firstPersonName(){
return this.$store.getters['personAbout/firstPersonName']
}
},
methods: {
add(){
const personObj = {id:nanoid(),name:this.name}
this.$store.commit('personAbout/ADD_PERSON',personObj)
this.name = ''
},
addWang(){
const personObj = {id:nanoid(),name:this.name}
this.$store.dispatch('personAbout/addPersonWang',personObj)
this.name = ''
},
addPersonServer(){
this.$store.dispatch('personAbout/addPersonServer')
}
},
}
script>
import { createStore } from 'vuex';
export default createStore({
actions: {},
mutations: {},
state: {},
getters: {},
});
import { createApp } from 'vue'
import { createStore } from 'vuex'
//引入store
import store from './store'
const app = createApp({ /* 根组件 */ })
// 将 store 实例作为插件安装
app.use(store)
index.js
import { createStore } from 'vuex';
//引入模块
import countOptions from './count'
import personOptions from './person'
//创建并暴露store
export default createStore({
modules:{
countAbout:countOptions,
personAbout:personOptions
}
})
count模块
// count模块
export default {
actions: {},
mutations: {},
state: {},
getters: {},
}
person模块
// person模块
export default {
actions: {},
mutations: {},
state: {},
getters: {},
}
非组合式API访问与vue2一致,这里展示的是组合API访问
可以通过调用 useStore 函数,来在 setup 钩子函数中访问 store。这与在组件中使用选项式 API 访问 this.$store 是等效的
import { useStore } from 'vuex'
export default {
setup () {
const store = useStore()
}
}
state 和 getter
为了访问 state 和 getter,需要创建 computed 引用以保留响应性,这与在选项式 API 中创建计算属性等效
import { computed } from 'vue'
import { useStore } from 'vuex'
export default {
setup () {
const store = useStore()
return {
// 在 computed 函数中访问 state
count: computed(() => store.state.count),
// 在 computed 函数中访问 getter
double: computed(() => store.getters.double)
}
}
}
Mutation 和 Action
要使用 mutation 和 action 时,只需要在 setup 钩子函数中调用 commit 和 dispatch 函数
import { useStore } from 'vuex'
export default {
setup () {
const store = useStore()
return {
// 使用 mutation
increment: () => store.commit('increment'),
// 使用 action
asyncIncrement: () => store.dispatch('asyncIncrement')
}
}
}
action方法调用,不用写子模块名字 store.dispatch(“方法名”)
state参数调用,需要写子模块名字 store.$state.模块名.参数
解决开发环境ajax跨域问题
使用代理服务器
vue项目中常用的2个ajax库
axios
通用的 Ajax 请求库, 官方推荐,使用广泛
vue-resource
一个非常轻量的用于处理HTTP请求的插件。Vue2.0之后,就不再对vue-resource更新,而是推荐使用axios
https://github.com/axios/axios#axios-api
安装
npm i axios
使用
示例
// Send a POST request
axios({
method: 'post',
url: '/user/12345',
data: {
firstName: 'Fred',
lastName: 'Flintstone'
}
});
axios({
method: 'get',
url: 'https://bit.ly/2mTM3nY'
})
axios.get(url).then(
response => {
console.log('请求成功了', response)
},
error => {
console.log('请求失败了', error)
}
)
axios.post('/user', {
firstName: 'Fred',
lastName: 'Flintstone'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
npm install vue-router@3
npm install vue-router@4
注:vue-router4只能在vue3中使用
vue 的一个插件库,专门用来实现 SPA 的
SPA的理解
//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
//引入VueRouter
import VueRouter from 'vue-router'
//引入路由器
import router from './router'
//关闭Vue的生产提示
Vue.config.productionTip = false
//应用插件
Vue.use(VueRouter)
//创建vm
new Vue({
el:'#app',
render: h => h(App),
router:router
})
About
Home
// 该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
//引入组件
import About from '../pages/About'
import Home from '../pages/Home'
import News from '../pages/News'
import Message from '../pages/Message'
//创建并暴露一个路由器
export default new VueRouter({
routes:[
{
path:'/about',
component:About
},
{
path:'/home',
component:Home,
children:[
{
path:'news',
component:News,
},
{
path:'message',
component:Message,
}
]
}
]
})
相关 API:
掌握:
安装部署、完整引入和部分引入、本地搭建
注:如果访问网络不给力,可以下载到本地,借助Nginx本地搭建服务
Vant
https://youzan.github.io/vant
Cube UI
https://didi.github.io/cube-ui
Mint UI
http://mint-ui.github.io
ElementPlus 基于vue3
https://element-plus.gitee.io/zh-CN/
ElementUI 基于vue2
https://element.eleme.cn/#/zh-CN
IViewUI
https://www.iviewui.com/
npm i element-ui -S
在 main.js 中写入以下内容:
import Vue from 'vue';
import App from './App.vue';
//完整引入
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
//完整引入应用插件
Vue.use(ElementUI);
new Vue({
el: '#app',
render: h => h(App)
});
注意事项:
如果后续运行过程中报错,出现少包的情况,直接执行npm install [包名] 安装所需要的包
“presets”: [[“es2015”, { “modules”: false }]] 修改为 “presets”: [[“@babel/preset-env”, { “modules”: false }]]
安装
npm install babel-plugin-component -D
修改配置文件babel.config.js
{
"presets": [["es2015", { "modules": false }]],
"plugins": [
[
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]
]
}
import Vue from 'vue'
import App from './App.vue'
// 按需引入
import { Button, Row, DatePicker } from 'element-ui';
// 按需引入应用插件
Vue.component(Button.name, Button);
Vue.component(Row.name, Row);
Vue.component(DatePicker.name, DatePicker);
Vue.config.productionTip = false
new Vue({
render: h => h(App),
}).$mount('#app')
npm install element-plus --save
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
const app = createApp(App)
app.use(ElementPlus)
app.mount('#app')
不要使用scoped样式
数据量过大,导致前端组件渲染时间过长,卡死
tsconfig.json
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"strict": false,
"jsx": "preserve",
"moduleResolution": "node"
}
}
less-loader 安装失败问题
原因:版本匹配问题
解决:npm install [email protected] [email protected]
表格固定表头,需要设置高度,但是表格内容会随着每页显示条目而变化,如何动态计算表格高度
这个弹出框的样式不好调整,怎么处理和解决
参考:https://www.jb51.net/article/263720.htm
getCurrentInstance只能在setup或生命周期钩子中使用