Vue
整体要学的
vue基础
vue-cli
vue-router
vuex
element-ui
vue3
简介
特点:
- 组件化
- 声明式编码
- 使用虚拟dom + 优秀Diff算法
在生成真实dom的时候,会先有一部虚拟dom,且新的虚拟dom会和旧的虚拟dom进行diff算法比较
如果有些dom没改,那么就可以不用再重新渲染了,提高性能
环境
安装node
node -> 16.20.2
切换淘宝镜像
npm install -g cnpm -registry=http://registry.npm.taobao.orgnpm config set registry http://registry.npm.taobao.org/
使用了第二个,下一步才有用
安装vue
npm install -g @vue/cli
vscode中不给运行vue解决办法
set-ExecutionPolicy RemoteSigne
问题就是脚本的执行策略不行,应该是安全问题
创建vue项目
create vue jplan
vue还不给输入大写
选自己的配置
Vuex + router + CSS Pre-processors
版本
2.x
路径选择
history router no
css预处理语言 less
配置文件 -> package.json
启动
npm run serve
引入element-ui
引入
npm i element-ui -S
目录下必须有package.json
在 main.js 中写入以下内容:
import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue';Vue.use(ElementUI);new Vue({el: '#app',render: h => h(App)
});
引入Axios
安装
npm install axios
npm install vue-axios
main.js引入
//引入axios
import axios from 'axios'
import VueAxios from 'vue-axios'
Vue.use(VueAxios,axios)
开发者工具devtools
vue2 只能用vue-devtools
Vue核心
模版语法
插值语法
{{ xxx}}
指令语法
v-bind 简写是 :xxx
数据绑定
单向的是 :value=" "
双向的是 v-model:value=" " 简写是 v-model=" "
v-model绑定的值不能是props传来的值,因为props传来的值是不可修改的
特别是对象的值,修改了props,别人还不知道,这不完蛋了吗
data的两种写法
对象式
<script type="text/javascript">Vue.config.productionTip = false; //阻止vue启动时生成生产提示new Vue({el: "#app",data: {msg: 'wo de fuck'}})</script>
函数式
<script type="text/javascript">Vue.config.productionTip = false; //阻止vue启动时生成生产提示new Vue({el: "#app",data() {return {msg: 'hi'}}})</script>
像一个函数返回一个对象
一般使用函数式,组件开发的时候必须写函数式,所以以后写函数式就ok
MVVM
Vue没有完全遵循MVVM,受启发
M 是 Model 模型,也就是我们的数据
V 是 View 视图,模版的代码 ->我们自己写的代码
VM 是视图模型,也就是Vue
Vue类似于数据和视图之间的中间商,我们在js代码中写的data会出现在vm里边,我们在{{}}模版语法里边可以获取到这些数据
在vm里边会有监听器,监听dom
Object.defineProperty()
Object.defineProperty(obj, prop, descriptor)
obj
要定义属性的对象。
prop
一个字符串或 Symbol,指定了要定义或修改的属性键。
descriptor
要定义或修改的属性的描述符。
描述符
1.configurable:表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性,默认值为false。
2.enumerable:能不能枚举,这里就是能不能for遍历,默认值为false
3.writable:表示能否修改属性的值。默认值为false。
4.value:包含这个属性的数据值。默认值为undefined。
举例
let p = {}
object.defineProperty(p,'age',{...
})
configurable 不能delete p.age
writable 不能 p.age = 12,这样是无效的
value是设置值
get,set
读取函数时调用get
写入属性时调用set
例如,假如我们想让对象p的age值变成响应式的,就可以使用get
<script>let num = 1;let p = {name: 'jjking'}Object.defineProperty(p,"age",{enumerable: true,get() {console.log('获取值,此处是相当于是响应式的');return num;}})console.log(p);</script>
需要注意的是,get函数不能和属性value共存,会报错
这里如果我们之后修改num,重新获取age的值的话,就会更新
如何定义多个属性
var student = {};
Object.defineProperties(student,{name:{writable:false,value:"lisi"},age : {writable:true,value : 16,},sex:{get(){return '男';},set(v){p1.sex = v}}
})
数据代理
最简单的数据代理
通过一个对象代理对另一个对象属性的操作(读/写)
<script>let obj1 = {x:100}let obj2 = {y:200}Object.defineProperty(obj2,'x',{get() {return obj1.x;},set(value) {obj1.x = value;}})</script>
此时我们就可以使用obj2来操作obj1了,并且obj1自己的改变,也会改变obj2的x值
- vue先是把data里边的数据放到vm里边,但是名字叫_data
- 然后通过Object.defineProperty()代理所有属性的操作,也就是这里的vm.name,和vm.address,这两个是代理对象
事件处理
基本使用
@xxx: 函数名
阻止默认时间prevent
<div id="app"><a href="www.baidu.com" @click="showInfo">点我</a></div><script type="text/javascript">const vm = new Vue({el: '#app',data: {},methods: {showInfo(e) {e.preventDefault();alert("同学你好!");}}})</script>
a标签的默认事件是跳转
我们可以直接写e.preventDefault();
也可以用vue的@click.prevent
@click.prevent="showInfo"
事件冒泡
原生的写法
<div id="app"><div @click="showDiv"><button @click="showInfo">点我</button></div><script type="text/javascript">const vm = new Vue({el: '#app',data: {},methods: {showDiv() {alert("DIV你好!");},showInfo(e) {e.stopPropagation();alert("同学你好!");}}})</script>
此时点button的话,他包裹的div也会触发时间showDiv,这就是冒泡
使用vue
@click.stop="showInfo"
事件只触发一次once
@click.once="showInfo"
事件捕获
capture
<div @click.capture="showDiv"><button @click.stop="showInfo">点我</button></div>
正常来说,是先捕获,再冒泡
也就是 先div,再button
如果使用.capture的话,就会先执行div,再执行button
<div id="app"><div @click.capture="showDiv"><button @click="showInfo">点我</button></div><script type="text/javascript">const vm = new Vue({el: '#app',data: {},methods: {showDiv() {alert("DIV你好!");},showInfo() {alert("同学你好!");}}})</script>
事件当前操作元素才触发self
如上也是,只有当我们点击的是div的时候,才触发事件,点button冒泡过去的话,是不会触发事件的
<div @click.self="showDiv"><button @click="showInfo">点我</button></div>
这个方法也可以防止冒泡
passive:事件的默认行为立即执行,无需等待事件回调执行完毕:
有一些@事件,他会先去处理方法,如果方法里边耗时很久的话,不执行默认行为就会有卡顿,所以passvie可以解决这个问题
如果需要多个事件处理
@click.prevent.stop
顺序无所谓
键盘事件
keydown,按下去就触发事件
keyup,按下去,抬上来之后才出发事件
event:当前事件
event.target:当前事件操作的元素
event.target.value 这是这个例子中input的value
<div id="app"><input type="text" @keyup.enter="showInfo"></div>
...方法showInfo(e) {console.log(e.target.value);}
keyup.enter 的意思是,检测到回车才触发事件
常用的别名
Vue中常用的按键别名:
回车=>enter
删除=>delete(捕获“删除”和“退格”键)
退出=>esc
空格=>space
换行=>tab
上=>up
下=>down
左=>1eft
右=>right
计算属性
要用的属性不存在,需要自己计算得来
原理是: Object.defineProperty 提供的getter 和 setter
get方法调用的时机(1) 初次读取的时候 (2) 依赖的 数据发生变化的时候
和methods相比,优势是有缓存机制,效率高
计算属性在vm身上,直接调即可
<script type="text/javascript">new Vue({el: '#root',data: {firstName: '',lastName: ''},computed: {fullname: {get() {return this.firstName + '-' + this.lastName}}}})
</script>
简写只有get方法的时候
methods: {fullname(){return this.firstName+this.lastName}}
set方法
set方法里边我们必须使得计算属性的依赖的数据发生改变,整体才会改变
<div id="app"><input type="text" v-model="firstName"> <br><input type="text" v-model="secondName"><div>{{fullName}}</div></div><script type="text/javascript">const vm = new Vue({el: '#app',data: {firstName: "",secondName: ""},computed: {fullName: {get() {return this.firstName + "-" + this.secondName},set(value) {const arr = value.split("-");this.firstName = arr[0];this.secondName = arr[1];}}}})</script>
如果没改变get方法中的firstName 和 secondName,计算属性走的还是缓存
监视属性
写法
<div id="app"><div>今天天气{{info}}</div><button @click="change">change</button></div><script type="text/javascript">const vm = new Vue({el: '#app',data: {isHot: false},computed: {info() {return this.isHot ? '炎热' : '凉爽';}},methods: {change() {this.isHot = !this.isHot;}},watch:{isHot: {handler(newValue,oldValue) {console.log(newValue,oldValue);}}}})</script>
我们在watch里边直接写,isHot发生变化,就会执行handler里边的方法
另外一种写法
<script type="text/javascript">const vm = new Vue({el: '#app',data: {isHot: false},computed: {info() {return this.isHot ? '炎热' : '凉爽';}},methods: {change() {this.isHot = !this.isHot;}}})vm.$watch('isHot',{handler(newValue,oldValue) {console.log(newValue,oldValue);}})</script>
在外边写也可以,只不过要注意的是,这里的isHot必须加引号
深度监视
如果我们要监视一个对象里边的数据的改变,需要加上配置项,deep:true
<script type="text/javascript">const vm = new Vue({el: '#app',data: {numbers: {a: 1,b: 2}},watch: {numbers: {immediate:true,deep: true,handler(newValue, oldValue) {console.log('numbers发生变化',newValue,oldValue);}}}})</script>
绑定样式
适用于class和style
最简单的写法
<!-- 绑定class样式--字符串写法,适用于:样式的类名不确定,需要动态指定 --><div class="basic" :class="mood" @click="changeMood"></div>
数组的绑定
绑定class样式–数组写法,适用于:要绑定的样式个数不确定、名字也不确定
<div class="basic" :class="['atguigu1','atguigu2']"></div>
对象的写法
适用于:要绑定的样式个数确定、名字也确定,但要动态决定用不用
<div class="basic" :class="{atguigu1:false, atguigu2:false}"></div>
简写
只有handler的时候,可以直接写
//原来的
watch: {numbers: {handler(newValue, oldValue) {console.log('numbers发生变化',newValue,oldValue);}}
}
//修改后的:
watch: {numbers(newValue, oldValue) {console.log('numbers发生变化',newValue,oldValue);}
}
computed 和 watch的区别
computed能实现的,watch都能实现,但是watch可以做异步操作
两个原则:
- 被vue管理的函数写成普通函数,this指向的是vm或者组件实例对象
- 所有不被vue所管理的函数,最好写成箭头函数,此时this指向的是vm或者组件实例对象
这里因为如果不被vue管理的函数写成普通函数,this指向的会是window
class 和 style样式
<div id="root"><!-- 字符串写法,适用于:样式的类名不确定,需要动态指定 --><div class="basic" :class="mood">{{number.a}}</div><!-- 数组写法,适用于:要绑定的样式个数不确定,名字也不能确定 --><div class="basic" :class="moodArr">{{number.b}}</div><!-- 对象写法,适用于:要绑定的样式个数确定、名字也确定,但要动态决定用不用 --><div class="basic" :class="moodObj">{{number.b}}</div></div>
...
data:{number:{a:1,b:2,},mood:'happy',moodArr:['happy'],moodObj:{happy:true}},
style的绑定和class的绑定一致
条件渲染
v-if,整个节点不在了
v-show 节点还在,只是隐藏
<h1 v-if="n===1">1</h1><h1 v-if="n===1">2</h1><h1 v-if="n===1">3</h1>
如果条件一样,我们可以再外边写一个div
<div v-if="n===1"><h1>1</h1><h1>2</h1><h1>3</h1>
</div>
但是有可能会破坏结构,所以用template
<template v-if="n===1"><h1>1</h1><h1>2</h1><h1>3</h1>
</template>
template只能和v-if使用
列表渲染
v-for
<!-- 遍历数组 -->在遍历数组时,数组元素后面的一个参数,是遍历时的key值,因为我们这里没有自定义key,所以默认是012<ul><li v-for="(person,index) in persons">{{index}}--{{person.name}}--{{person.age}}</li></ul><!-- 遍历对象 -->遍历对象时,括号中第一个参数是对象中键值对的值,第二个参数是键值对的键,第三个参数是这个这条遍历的数据的key<ul><li v-for="(value,key,index) in car">{{index}}--{{key}}--{{value}}</li></ul>遍历字符串时,括号中第一个参数是字符串这个位置的值,第二个参数是这个这条遍历的数据的key<!-- 遍历字符串 --><ul><li v-for="(value,key) in str">{{key}}--{{value}}</li></ul>...data: {persons: [{ id: "001", name: "ice", age: "13" },{ id: "002", name: "peach", age: "12" },],car: {speed: "20km/h",year: "2014",},str: "i am a word",},
key的原理和使用
key的内部原理
- 虚拟dom中key的作用:
key是虚拟dom对象的标识,vue会根据新数据生成新的虚拟dom,随后进行diff算法 - 对比规则:
(1) 旧的虚拟dom和新的虚拟dom,有相同key
a. 如果虚拟dom中内容没变,则直接复用之前的真实dom
b.如果内容变化,则生成新的dom
(2)没有相同key,创建新的dom
看这个图就知道为什么index作为key,如果有逆序操作的时候,会出现界面问题了
两边新旧都有虚拟dom,此时进行diff比较,我们新的是逆序添加了老刘这条li
key相同都是0,input也相同,只有张三-18
和老刘-30
不同,此时input相同
那么不同的内容,老刘-30
会创建新的dom
对于相同的内容,input输入框,就会复用之前的dom,也就是原本应该是张三的输入框
以此类推,这里整个往上移,就会出现页面问题
解决办法也很简单,就是我们绑定key的时候,用可以唯一标识的key,比如id,身份证,手机号等等
如果不存在对数据的逆序,破坏顺序操作,用index也是没有问题的
列表过滤
实现列表过滤的效果
watch监视
<div id="app"><label>姓名:</label><input v-model="name"><ul><li v-for="(person) in persnsOfSearch">{{person.name}}--{{person.age}}</li></ul></div><script type="text/javascript">const vm = new Vue({el: '#app',data: {name: '',persnsOfSearch: [],persons: [{ id: '001', name: 'ice', age: '17' },{ id: '002', name: 'ipeach', age: '18' },{ id: '003', name: 'icepeach', age: '19' }]},watch: {name: {immediate: true,handler(newValue, oldValue) {console.log('输入发生变化');this.persnsOfSearch = this.persons.filter((p) => {return p.name.indexOf(newValue) != -1;})}}}})</script>
整体的写法就是
输入框的里边的值,我们监视他
如果发生变化,也就是传来新的值newValue
,我们就去过滤数组,
过滤的条件是,在persons数组中,查找每个对象的name
是否含有newValue
利用的是函数 str.indexOf(val) ,如果有就会返回位置,如果没有就会返回-1
所以只有 != -1,就说明含有该值,filter通过
最后我们把过滤好的数据传给persnsOfSearch
,不用原来的数组就是为了不破坏数据
计算属性实现
<div id="app"><label>姓名:</label><input v-model="name"><ul><li v-for="(person) in persnsOfSearch">{{person.name}}--{{person.age}}</li></ul></div><script type="text/javascript">const vm = new Vue({el: '#app',data: {name: '',persons: [{ id: '001', name: 'ice', age: '17' },{ id: '002', name: 'ipeach', age: '18' },{ id: '003', name: 'icepeach', age: '19' }]},computed: {persnsOfSearch() {return this.persons.filter((p) => {return p.name.indexOf(this.name) != -1;})}}})</script>
我们如果使用计算属性的话就可以快速的解决这个问题
第一,我们得监视name的改变,这里的监视name的改变,巧妙的把name写到计算属性里边了,计算属性里边,依赖的数据发生改变,那么计算属性也会发生改变
也就是我们这里的persnsOfSearch
,里边依赖着this.name
第二,我们做过滤
还有一个好处就是,我们不用像watch那样还得写immediate:true,计算属性一开始就会去进行计算
排序
在上面的基础上,加上一个age过滤的功能
<div id="app"><label>姓名:</label><input v-model="name"><ul><li v-for="(person) in persnsOfSearch">{{person.name}}--{{person.age}}</li></ul><button @click="sortType = 0">原顺序</button><button @click="sortType = 1">降序</button><button @click="sortType = 2">升序</button></div><script type="text/javascript">const vm = new Vue({el: '#app',data: {name: '',persons: [{ id: '001', name: 'ice', age: '17' },{ id: '002', name: 'ipeach', age: '18' },{ id: '003', name: 'icepeach', age: '19' }],sortType: 0 },computed: {persnsOfSearch() {const arr = this.persons.filter((p) => {return p.name.indexOf(this.name) != -1;})//是否是原来的顺序if(this.sortType != 0) {arr.sort((p1,p2) => {return this.sortType == '1' ? p2.age - p1.age : p1.age - p2.age;}) } return arr;}}})</script>
获取表单数据
<input type="text"/>
v-model收集的是value值,用户输入的就是value值
<input type="radio"/>
v-model收集的是value值,我们必须给标签设置value值
<input type="checkbox"/>
- 如果没有设置value值,收集的是checked值(布尔)
- 如果设置了value值
(1)v-model的初始值不是数组,收集的就是checked
(2)v-model的初始值是数组,收集的就是value组成的数组
备注: v-model的三个修饰符
lazy: 失去焦点的再收集数据
number: 输入字符串转为有效数字
trim: 去掉首尾空格
生命周期
beforeCreate 是数据代理监测和数据代理创建之前,不是vm
beforeMount 此时只有虚拟dom,所以此时没有把虚拟dom转换为真实dom
beforeUpdate 此时数据是最新的,但是页面是旧的
beforedestroy 此时数据,方法都在,但是此时如果修改数据,方法是不会触发数据更新流程
一般这个时候,清除定时器,解绑自定义事件,取消订阅等等
注意
销毁之后,只是vm没了,但是真实dom还是存在的,也就是vm的工作成果还在,不过管理的他的vue死了