vue基础
介绍
vue.js是一套构建用户界面(UI)的渐进式框架。Vue的核心库 只关注视图层,采用自底向上增量开发,上手简单,便于与第三方库或既有项目整合。
- 渐进式:一步一步来,不是说所有东西都要一次用上。
- 自底向上 : 声明式渲染–>组件系统–>客户端路由–>大规模状态管理–>构建工具
使用
- vue 使用基础步骤
<!-- 第1步: 引入vue库文件 -->
<script src="./js/vue.js"></script>
<body>
<!-- 第2步:根容器,js对象转为真实的html后,在放在网页的什么位置中,挂载点挂载点可以写多个,但是在工作中一般只有一个 -->
<div id="app">
<!-- 第4步:使用 -->
<!-- 模板语法 插值表达式 mustcache模板名称[小胡子语法] -->
<!-- title它就是在vue实例化配置data属性中的数据 -->
<h3>{{ title }}</h3>
<!-- vue模板不建议里面进行运算{{ 1+1 }} -->
<div> {{ 1+1 }}</div>
<!-- 调用函数或方法 -->
<div>{{sum()}}</div>
<!-- 调用js内置方法 不太推荐 -->
<div>{{ '电饭锅是'.substr(0,3) }}</div>
<!-- 三目运算 -->
<div>{{ age<20?'未成年':'成年' }}</div>
<!-- <input type="text" id="setTitle"> -->
<!-- v-model,语法糖 -->
<input type="text" v-model="title">
</div>
<!-- 第3步:实例化vue -->
<script>
const vm = new Vue({
// vue处理完成数据展示在页面中的挂载点dom
el: '#app',
// vue2中,唯一一个地方可以把data写成对象地方,就是在实例化vue类时
// 声明vue要渲染的数据
data: {
title: 'hello vue'
},
methods:{
// 调用函数 data中可以写函数,但是会被劫持,这完全没必要, vue为了提升性能可以新增一个methods:配置选项,用来定义函数并且不会被劫持
sum() {
return 1 + 2
}
}
})
</script>
</body>
vue2中优化方法
- vue的性能的瓶颈主要就是在数据的劫持,定义数据源尽可能的扁平化,可以减少递归带来的性能消耗
- vue中当你不想让一个数据变为响应性的,可以将它使用Object.freeze()冻结起来
- vue 中函数可以写在data中,但是data中的数据都会被劫持,而函数并没有被劫持的必要,为了提升性能,新增了一个配置选项methods,用于定义函数方法
- 对于静态的标签比如ul中的数据可以使用v-pre指令,这是一个固定的并不需要通过vue转化成为虚拟dom,可以直接写入html显示输出
- vue 中可以通过v-once 只绑定元素和组件一次,执行之后元素和组件会失去响应能力,相对于一直绑定在性能上确实有优化
- 条件判断的v-if 和v-show这两个指令在效果上几乎一样,但v-if在判断条件成立时会删除以创建的元素,这在页面初始化显示权限相关信息时性能更高一些,但是在频繁切换时如果使用v-if会造成频繁创建和删除元素的现象,相比之下v-show的性能会更好一些
Object.defineProperty和Proxy的区别
- Object.defineProperty 代理的是对象的某个属性,如果想获取嵌套格式的数据需要进行递归操作,Proxy代理的是整个对象
- Object.defineProperty 能监听数组中的每一项,但是监听不到数组中新增的元素和删减的元素,能够对数组已有的元素只能获取和修改,虽然通常情况下我们不希望监听整个数组,在数据量较大时会非常影响效率 ; Proxy可以代理整个对象,并可以监听到元素的增删改
diff比对
- 同层级相比
- 新的有 旧的没有,创建新的
- 旧的有 新的没有, 旧的删除
- 一样的就开始比对 有key按key比,没有就按照索引从两端向中间比
vue指令
- 为了操作dom
-
- 权限控制
- 表单验证
- 主题设置
v-html
- 解析html字符串,可以解决花括号闪现问题,
v-text
- 转义html字符串输出,为了解决早期没有使用工程化时,直接映入srcipt产生的花括号闪现问题 一般用不到,在单文件vue中不会有闪现问题,也用不着
v-cloak
- 专门用来解决闪现问题的但是需要配合css样式,一般也不咋用。v-html或v-text能解决闪现,但是项目早期没用,如果后来全部修改的工程量太大,这时候可以用v-cloak
v-pre
- 一个性能优化指令,使用可以提升性能,静态的元素可以用,因为是固定的所以不转成虚拟dom直接存储html显示输出,但是动态元素使用之后会直接变成静态的
v-once
- 只绑定元素和组件一次,之后元素和组件会失去响应能力,也是个性能优化的方案,也不咋用
v-if
-
显示或隐藏
-
条件不满足,不会创建或删除已创建的元素;条件满足直接删除元素
v-show
-
显示或隐藏
-
条件满足会给元素添加css属性display:none
v-bind
- 动态属性数据绑定
- 标准语法: v-bind:属性=“动态数据”,数据不一定是data的methods之类的也可以
- 简写语法: :属性=“动态数据”
<img v-bind:src="src" alt="">
<img :src="src" alt="">
data: {
src: 'http://www.mobiletrain.org/images_index/right-fixed-face.png'
}
v-for
-
vue对v-for进行了增强,可以使用for in/of 都可以,而且两者都可以进行对象的迭代
-
语法: v-for=“(元素,索引) in/of 数组”
v-for=“(元素,键名,索引) in/of 对象”
-
注意 :vue2中小括号可以不写,但是vue3必须写
vue2中如果一个标签同时有v-if和v-for ,则v-for 的优先级会高于v-if,所以还是会循环完
vue3中v-if的优先级高于v-for
- **注意**:v-for 建议给循环的每一项添加一个key来作为标识,用来提升性能,key的值一定唯一不重复,不建议使用循环的索引当key值。一般和后端请求数据时,要求后端提供唯一的id号
-
对象也能循环,但是一般都用枚举
<div id="app">
<!-- <li v-for="(item,index) in user">{{index}} -- {{item}}</li> -->
<!-- <li v-for="item,index in user">{{index}} -- {{item}}</li> -->
<!-- <li v-for="item,index in user" :key="item.id">{{item.name}}</li> -->
<!-- 循环对象 -->
<div v-for="item,key,index in user" :key="key">{{index}} -- {{key}} -- {{item}}</div>
</div>
<!-- 第3步:实例化vue -->
<script>
const vm = new Vue({
el: '#app',
data: {
// user: ['张三', '李四', '王五']
user: [{ id: 2, name: '李四' }, { id: 1, name: '张三' }]
// user: { id: 1, name: '张三' }
}
})
v-on
-
v-on:事件名=“实现的方法[此方法定义在vue配置的methods中]”
-
简写 @事件名=“方法”
-
<button v-on:click="clickFn">点击事件</button> <button @click="clickFn">点击事件</button>
-
绑定的方法可以写小括号也可以不写,但是会有区别
-
-
如果绑定方法时没有写小括号,则vue在解析时,会自动给添加映射一个event给绑定方法
-
<button @click="clickFn">点击事件</button>
-
如果绑定方法时,写了小括号,则需要手动把event对象传过去 e v e n t , event, event,event可以传多个,但是通常建议只传一个,写在第一位或最后一位
-
<button uname="李四" data-uname="张三" @click="evt=>clickFn($event)">点击事件</button>
-
event可以用来完成dom数据的获取
-
绑定的方法写在methods
-
-
注意 : methods中定义的方法不使用箭头函数,因为箭头函数没有作用域,找父级会导致this指向混乱,但是方法体中还是建议使用箭头函数来保持this指向
事件修饰符
多个修饰符通过点(.)来连接,
<!-- 键盘按键绑定 -->
<input placeholder="请输入内容" type="text" @keyup.alt.112="onEnter" >
<!-- 阻止冒泡 -->
<button @click.stop="doThis"></button>
<!-- 阻止默认行为 -->
<a @click.prevent="doThis"></a>
<!-- 只执行一次 -->
<div @click.once="incr()">自增一下</div>
<!-- 绑定的元素本身触发时才触发回调 -->
<ul @click.self="incr()">
<li>你好世界</li>
</ul>
<!-- 串联修饰符 -->
<button @click.stop.prevent="doThis"></button>
动态样式
class样式的动态添加,对象和数组方式
- 对象:{key就是他的样式名称:布尔值[true生效,false不生效]} -->一般用于开关的样式,不太适合添加新的样式属性
- 动态的给对象添加成员属性:this.$set(给谁, “加什么”, 值是什么)
- Object.assign() 对象合并,第一层是深复制,其他的是浅复制
- let obj=Object.assign(第一个参数的地址和返回值地址相等)
- 数组:[元素样式名称],一般用于新样式的追加
v-model
语法糖是value和事件的结合体,根据不同表单项选择不同的事件,实现了双向数据绑定
单个复选框
<div id="app">
<div>
<!-- 单个复选框,定义的数据类型为布尔类型 true选中,false未选中 -->
<input type="checkbox" v-model="checked">
<!-- click事件可以用,但它是的状态有太过提前,用onchange事件,改变后来获取 -->
<!-- <input type="checkbox" @click="clickFn"> -->
<!-- <input type="checkbox" @change="clickFn"> -->
</div>
</div>
<!-- 第3步:实例化vue -->
<script>
const vm = new Vue({
el: '#app',
data: {
// 单个复选框一定要用布尔类型
checked: false
},
methods: {
clickFn(evt) {
console.log(evt.target.checked);
}
}
})
</script>
多个复选框
<div>
<!--
多个复选框,定义的数据类型为数组
指定它的value值
-->
<ul>
<li>
<input type="checkbox" value="html" v-model="lessons">html
</li>
<li>
<input type="checkbox" value="css" v-model="lessons">css
</li>
<li>
<input type="checkbox" value="js" v-model="lessons">js
</li>
</ul>
<hr>
<div>{{lessons}}</div>
</div>
</div>
<!-- 第3步:实例化vue -->
<script>
const vm = new Vue({
el: '#app',
data: {
lessons: ["js",'css','html']
},
methods: {
}
})
全选
<div id="app">
<div>
<!--
多个复选框,定义的数据类型为数组
指定它的value值
-->
<input type="checkbox" v-model="checked" @change="onSelected">
<hr>
<ul>
<li>
<input type="checkbox" value="html" @change="selectlesson" v-model="lessons">html
</li>
<li>
<input type="checkbox" value="css" @change="selectlesson" v-model="lessons">css
</li>
<li>
<input type="checkbox" value="js" @change="selectlesson" v-model="lessons">js
</li>
</ul>
<hr>
<div>{{lessons}}</div>
</div>
</div>
<!-- 第3步:实例化vue -->
<script>
const vm = new Vue({
el: '#app',
data: {
lessons: [],
checked: false
},
methods: {
onSelected(evt) {
// 选中了
if (evt.target.checked) {
this.lessons = ["js", 'html', 'css']
} else {
this.lessons = []
}
},
selectlesson() {
// 只要来操作数据源就可以改变视图
this.checked = this.lessons.length == 3
}
}
})
</script>
单选框&下拉框
<div id="app">
<div>
<h3>{{sex}} -- {{city}}</h3>
<!-- 定义的数据类型为字符串 -->
<label>
<input type="radio" value="先生" v-model="sex">建行
</label>
<label>
<input type="radio" value="女神" v-model="sex">招行
</label>
</div>
<hr>
<div>
<select v-model="city">
<option value="0">==选择==</option>
<option value="wh">芜湖</option>
<option value="bj">北京</option>
</select>
</div>
</div>
<!-- 第3步:实例化vue -->
<script>
const vm = new Vue({
el: '#app',
data: {
sex: '先生',
city: '0'
}
})
</script>
v-model修饰符
v-model.lazy 延时更新数据源,会在光标移开之后更新数据,每天一个性能提升小技巧有点类似防抖节流
<input v-model.lazy="title">
v-model.trim 两端去空格,中间的空格去不了但是可以用正则替换
<input v-model.trim="title">
v-model.number 转为数值
<input type="number" v-model.number="n1">
自定义指令
- 自定义指令中不能直接通过this获取vue实例中的data数据或方法
- 直接通过dom来完成数据的收集
- 使用时要以’v-'开头,定义的时候不需要以’v-'开头
全局定义
定义之后所有组件或vue实例全都生效,可以使用
-
Vue.directive(‘指令名称 不需要写v-开头’,对象或函数)
-
对象的写法提供了5个钩子函数
-
- bind 第一次绑定到元素时调用 相当于初始化
- inserted (加入
- update (数据修改
- componentupdated
- unbind (数据删除
局部定义
–>只对于当前组件生效
directives: {} 这个有s全局那个没s
使用指令进行权限管理
计算属性
- vue使用中,不推荐在模板中写过多的逻辑
- 对于一个结果进行计算,可以使用vue提供的计算属性来完成,计算属性还具有缓存的功能
- 如果依赖项没有发生改变,则它在下一次调用时会读取缓存中的数据
- 提高运行效率,每天一个墨鱼小技巧,哎对性能优化
- 不能异步
data: {
n1: 1,
n2: 2
},
computed: {
<!-- 简写写法 , 只是实现了get-->
total(){
console.log("------------");
return this.n1+this.n2;
}
<!-- 完整写法-->
total: {
get() {
return this.n1 + this.n2
},
set(newVal) {
if (newVal > 10) {
this.msg = '值有点的大'
}
}
}
}
},
methods: {
sum() {
// console.log('sum --- methods', this.total);
// 如果你定义的计算属性,为简写方式,则给计算属性赋值,会报错
// 只有标准的写法时,它才可以对于计算属性进行赋值操作
// 赋值只会触发标准方式中的set方法,然后你可以得到它,完成一些别的工作
if (this.total > 6) {
this.total = 101
}
return this.n1 + this.n2
}
}
监听器 - watch
- 用来侦听 data 中数据变化,默认情况下初始化不触发
- 监听器的依赖项只能有一个,可以看成一对一
- 监听器中可以写异步
- immediate:true 初始时执行一次,一般情况下不使用,只有标准写法下才有这个配置
const vm = new Vue({
el: '#app',
data: {
username: 'aaa',
errorUsername: '',
user: { id: 100, name: 'aaa' }
},
watch: {
<!--标准写法-->
username: {
handler(newValue, oldValue) {//变化前后的值
console.log(newValue, oldValue);
if (newValue.length >= 3) this.errorUsername = '太大了';
else this.errorUsername = ''
},
immediate: true
},
<!--简写-->
username(newValue, oldValue) {
if (newValue.length >= 3) this.errorUsername = '放不进去';
else this.errorUsername = ''
}
<!--监听对象中指定属性数据的变化建议这种写法-->
'user.id'(newValue,oldValue){
console.log(newValue,oldValue)
}
}
})
-
监听对象只能用标准写法来写
-
监听对象变化时newValue, oldValue两个值是一样的,没办法区分
-
监听对象 ->深度监听
user: {
// 深度监听
deep: true,
handler(newValue, oldValue) {
console.log(newValue, oldValue);
},
}
过滤器
- 为了对于界面中显示的数据进行一个处理操作
- 过滤器的定义方案:全局过滤器、局部过滤器
<!--Vue.filter('过滤器的名称,可以随便起',value=>{
//回调函数,回调函数的参数最少一个,最后一位的参数永远指向要过滤的数据
})-->
<div id="app">
<h3>{{phone}}</h3>
<h3>{{phone|phoneCrypt}}</h3>
<h3>{{phone|phoneCrypt('!!!!')}}</h3>
</div>
<script>
/*全局过滤器*/
Vue.filter('phoneCrypt',value=>{
return value.slice(0,3)+'****'+value.slice(7)
})
//参数的个数可以多个
Vue.filter('phoneCrypt', (value, salt = '****') => {
return value.slice(0, 3) + salt + value.slice(7)
})
const vm=new Vue({
el:'#app',
data:{
phone:'12345678987'
},
/*局部过滤器*/
filters:{//注意了局部的这个filters是有s的全局的没有
phoneCrypt(value, salt = '****'){
return value.slice(0, 3) + salt + value.slice(7)
}
}
})
</script>
混入 -Mixins
- 用于公共代码复用
- 方式 :全局、局部
- 混入的配置,可以把几乎所有new Vue配置中的所有配置都能混入,但el配置不可
- data配置,在混入方式中,只能写函数的方式,且函数一定要返回一个对象,混入可以被多次调佣,如果直接是对象的话就会产生污染
- data混入的优先级 组件(实例) > 局部 > 全局 =>只调用一个
- 生命周期方法的执行顺序 全局 > 局部 > 组件 依次执行
<body>
<div id="app">
<h3>{{name}}</h3>
<hr>
<h3>{{showName}}</h3>
<hr>
<div>{{run()}}</div>
</div>
<script>
Vue.mixin({
data() {
return {
name: '张三 -- 全局'
}
},
computed: {
showName() {
return 'abc -- 全局'
}
},
methods: {
run() {
return 'run -- 全局'
}
},
created() {
console.log('created -- 全局');
}
})
// 局部混入
const mix = {
data() {
return {
name: '张三 -- 局部'
}
},
computed: {
showName() {
return 'abc -- 局部'
}
},
methods: {
run() {
return 'run -- 局部'
}
},
created() {
console.log('created -- 局部');
}
}
const vm = new Vue({
el: '#app',
data: {
// name: '张三 -- 实例'
},
mixins: [mix],
methods: {
run() {
return 'run -- 实例'
}
},
created() {
console.log('created -- 实例');
}
})
</script>
</body>
插件
vue中提供了插件机制,可以通过它的要求来完成插件的封装,运用到项目中
Vue.use(函数 | 类 | 对象 ,[可选参数])
- 函数
<!-- function plugin(Vue类,options可选参数){} -->
<body>
<div id="app">
<h3 v-red>{{title}}</h3>
</div>
<script>
function plugin(Vue,options){//optionsshi 可选参数
console.log('options+',options);
Vue.directive('red',el=>{
console.log('el+',el)
el.style.cssText='color:red'
})
Vue.mixin({
data(){
return{
title:'这就是个标题'
}
},
created(){
this.title=options.title
console.log('this.title',this.title)
}
})
//静态属性
// Vue.run=()=>console.log('run')
//添加成员属性,除了prototype还有混入可以添加
Vue.prototype.run=()=>console.log('run')
}
Vue.use(plugin,{title:'abc'})
const vm=new Vue({
el:'#app',
data:{
},
created(){
// 调用静态属性
// Vue.run()
this.run()
}
})
</script>
</body>
- 类,必须有个静态方法
<!--
必须静态方法,名称必须为install
class Plugin{
static install(Vue类,options可选参数){}
}
-->
<body>
<div id="app">
<h3 v-red>{{title}}</h3>
</div>
<script>
class Plugin {
static install(Vue, options) {
console.log(options);
Vue.directive('red', el => {
el.style.cssText = 'color:red'
})
Vue.mixin({
data() {
return {
title: `() => console.log('run');`//这似乎没啥用
}
},
created() {
this.title = options.title
console.log('混入了');
}
})
// 添加成员属性
Vue.prototype.run = () => console.log('run');
}
}
Vue.use(Plugin, { title: 'asd' })
const vm = new Vue({
el: '#app',
data: {},
created() {
this.run()
}
})
</script>
</body>
对象
<body>
<div id="app">
<h3 v-red>{{title}}</h3>
</div>
<script>
const Plugin = {
install(Vue, options) {
console.log(options);
Vue.directive('red', el => {
el.style.cssText = 'color:red'
})
Vue.mixin({
data() {
return {
title: `() => console.log('run');`
}
},
created() {
this.title = options.title
console.log('混入了');
}
})
// 添加成员属性
Vue.prototype.run = () => console.log('run');
}
}
// 插入插件
Vue.use(Plugin, { title: 'abc' })
const vm = new Vue({
el: '#app',
data: {
},
created() {
// Vue.run()
this.run()
}
})
</script>
</body>
生命周期
-
也可以被叫做页面钩子函数 (一共11个vue3还要再加2个)
-
初始化阶段一次 有4个
-
更新阶段 生命周期会执行N次 有2个
-
销毁阶段 只执行一次 有2个
-
剩那3个属于特定场景触发暂时不考虑
<script>
const vm = new Vue({
el: '#app',
data: {
username: ''
},
// 初始化阶段生命周期 -- 它只都只执行1次
beforeCreate() {
console.log('beforeCreate');
},
created() {
console.log('created');
},
beforeMount() {
console.log('beforeMount');
},
mounted() {
console.log('mounted');
setTimeout(() => {
// 销毁
this.$destroy()
}, 2000);
this.timer = setInterval(() => {
console.log(111);
}, 1000);
},
// 更新阶段生命周期,它们会执行N次
beforeUpdate() {
console.log('beforeUpdate');
},
updated() {
console.log('updated');
},
// 销毁阶段 只执行1次
beforeDestroy() {
clearInterval(this.timer)
console.log('beforeDestroy');
},
destroyed() {
console.log('destroyed');
}
})
</script>
网络请求
status是XMLHttpRequest对象的一个属性,表示响应的http状态码
在http1.1协议下,http状态码总共可分为5大类
1xx:信息响应类,表示接收到请求并且继续处理
2xx:处理成功响应类,表示动作被成功接收、理解和接受
3xx:重定向响应类,为了完成指定的动作,必须接受进一步处理
4xx:客户端错误,客户请求包含语法错误或者是不能正确执行
5xx:服务端错误,服务器不能正确执行一个正确的请求
100——客户必须继续发出请求
101——客户要求服务器根据请求转换HTTP协议版本
200——交易成功
201——提示知道新文件的URL
202——接受和处理、但处理未完成
203——返回信息不确定或不完整
204——请求收到,但返回信息为空
205——服务器完成了请求,用户代理必须复位当前已经浏览过的文件
206——服务器已经完成了部分用户的GET请求
300——请求的资源可在多处得到
301——删除请求数据
302——在其他地址发现了请求数据
303——建议客户访问其他URL或访问方式
304——客户端已经执行了GET,但文件未变化
305——请求的资源必须从服务器指定的地址得到
306——前一版本HTTP中使用的代码,现行版本中不再使用
307——申明请求的资源临时性删除
400——错误请求,如语法错误
401——请求授权失败
402——保留有效ChargeTo头响应
403——请求不允许
404——没有发现文件、查询或URl
405——用户在Request-Line字段定义的方法不允许
406——根据用户发送的Accept拖,请求资源不可访问
407——类似401,用户必须首先在代理服务器上得到授权
408——客户端没有在用户指定的饿时间内完成请求
409——对当前资源状态,请求不能完成
410——服务器上不再有此资源且无进一步的参考地址
411——服务器拒绝用户定义的Content-Length属性请求
412——一个或多个请求头字段在当前请求中错误
413——请求的资源大于服务器允许的大小
414——请求的资源URL长于服务器允许的长度
415——请求资源不支持请求项目格式
416——请求中包含Range请求头字段,在当前请求资源范围内没有range指示值,请求也不包含If-Range请求头字段
417——服务器不满足请求Expect头字段指定的期望值,如果是代理服务器,可能是下一级服务器不能满足请求
500——服务器产生内部错误
501——服务器不支持请求的函数
502——服务器暂时不可用,有时是为了防止发生系统过载
503——服务器过载或暂停维修
504——关口过载,服务器使用另一个关口或服务来响应用户,等待时间设定值较长
505——服务器不支持或拒绝支请求头中指定的HTTP版本
使用catch()会捕获之前所有.then的异常,()=>只捕获前面一个.then的异常
axios.get('/api/users', {
headers: {
token: 'abc'
}
}).then(ret => {
console.log(ret.data);
}).catch(() => console.log('有异常'))
组件
- 全局组件 一旦声明完成,就可以在所有的组件中直接使用,不需要引入和注册
- 定义的组件,它的元素必须要有一个顶层元素进行包裹,否则报错
<body>
<div id="app">
<!-- 自定义的标签,在vue项目中,称为组件 -->
<mytitle></mytitle>
<mytitle></mytitle>
<mytitle></mytitle>
<mytitle></mytitle>
</div>
<script>
Vue.component('mytitle', {
data() {
return {
title: '我是一个标题'
}
},
// 生成直接生成 虚拟dom方法
// 在工程化中,render中可以直接写jsx,在引入一个babel可以写jsx语法(js的增强版本)
// render(h) {
// // console.log(h);
// // let vnode = h('h3', null, '我是一个标签')
// let vnode = h('h3', { attrs: { name: 'abc', style: 'color:red' } }, '我是一个标签')
// return vnode
// }
template: `<div>
<h3 name='abc' @click="setTitle">{{title}}</h3><div>aaaa</div>
<hr />
<subtitle></subtitle>
</div>`,
methods: {
setTitle() {
this.title = Date.now()
}
}
})
Vue.component('subtitle', {
template: `<div>
<h3>我是一个子标题</h3>
</div>`
})
const vm = new Vue({
el: '#app',
data: {
}
})
</script>