VUE2及其生态查漏补缺

news/2024/10/5 21:59:38/

1、数据代理概括

数据代理过程相当于是进行了 vm 代理 vm_data中的属性,vm._data 是与 我们vue文件中写的 data是全等的

javascript">//创建Vue实例let data = { //data中用于存储数据,数据供el所指定的容器去使用,值我们暂时先写成一个对象。name:'atguigu',address:'北京'}new Vue({el:'#demo', //el用于指定当前Vue实例为哪个容器服务,值通常为css选择器字符串。data})Object.defineProperty(vm, 'name', {get(){return vm._data.name},set(value){vm._data.name = value}})

2、v-on:事件 

只有data里面的东西才会做数据劫持,代理,方法其实也可以写在data,但是会使vue实例变得冗余,因为对方法做了数据代理及劫持,这是没意义的操作

事件的基本使用:

      1.使用v-on:xxx 或 @xxx 绑定事件,其中xxx是事件名;

      2.事件的回调需要配置在methods对象中,最终会在vm上;

      3.methods中配置的函数,不要用箭头函数!否则this就不是vm了;

      4.methods中配置的函数,都是被Vue所管理的函数,this的指向是vm 或 组件实例对象;

       5.@click="demo" 和 @click="demo($event)" 效果一致,但后者可以传参;

 2.1、事件修饰符

Vue中的事件修饰符:

          1.prevent:阻止默认事件(常用);

          2.stop:阻止事件冒泡(常用);

          3.once:事件只触发一次(常用);

          4.capture:使用事件的捕获模式,让事件在捕获阶段执行(默认事件是在冒泡阶段执行的);

          5.self:只有event.target是当前操作的元素时才触发事件;

          6.passive:事件的默认行为立即执行,无需等待事件回调执行完毕;

2.2、键盘事件

1.Vue中常用的按键别名:

          回车 => enter

          删除 => delete (捕获“删除”和“退格”键)

          退出 => esc

          空格 => space

          换行 => tab (特殊,必须配合keydown去使用)

          上 => up

          下 => down

          左 => left

          右 => right

        2.Vue未提供别名的按键,可以使用按键原始的key值去绑定,但注意要转为kebab-case(短横线命名)

        3.系统修饰键(用法特殊):ctrl、alt、shift、meta

          (1).配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。

          (2).配合keydown使用:正常触发事件。

        4.也可以使用keyCode去指定具体的按键(不推荐)

        5.Vue.config.keyCodes.自定义键名 = 键码,可以去定制按键别名

3、列表渲染 key

最终效果图

操作步骤:

复制张三-18到其对应的右侧输入框

李四-19、王五-20同上操作

然后点击添加老刘就会得到上述的效果,具体原因在 下面 3.1 有解释

demo代码

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>key的原理</title><script type="text/javascript" src="../js/vue.js"></script></head><body><!-- 准备好一个容器--><div id="root"><!-- 遍历数组 --><h2>人员列表(遍历数组)</h2><button @click.once="add">添加一个老刘</button><ul><!-- 分别通过 index 与 id作为 key --><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 = falsenew 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>
</html>

 3.1、index作为key

解释:

  1. 当index作为key的时候(没设置key时,默认也是以index作为key的)
  2. 我们一开始渲染的是左侧的三条数据,会生成三个虚拟dom节点,key 分别为0、1、2,
  3. 然后vue根据这三虚拟dom生成真正的三个dom节点,渲染到页面上去
  4. 当我们通过 unshift 逆序添加一个元素到列表时,破坏了原列表的顺序,生成四个虚拟节点
  5. vue会通过diff算法 将左侧(原本的)和右侧(新生成)的虚拟节点进行对比,主要是通过key,去对比
  6. 发现右侧key = 0的地方 文本节点与左侧key = 0的地方文本节点不一样,则在生成真实dom节点的时候,就会重新生成dom节点,对比input虚拟节点的时候,发现是一样的,则会复用已有的dom节点而此时,由于复用的是左侧key = 0 节点下的input,所以老刘对应的输入框本应是无值的变成了 张三-18,以此类推最终就会得到 右图所示的效果,出现数据错乱
  7. 而且如果是类似上述情况,涉及逆序破坏列表顺序的情况,使用idnex作为key,会造成效率问题,让本该复用的节点,得不到复用,重复创建dom节点,所以当是类似上述情况,使用唯一标识很重要,下面解释为啥使用唯一标识重要

3.2、唯一标识作为key

解释:

  1. 当唯一标识(这里是id)作为key的时候
  2. 我们一开始渲染的是左侧的三条数据,会生成三个虚拟dom节点,key 分别为001、002、003,
  3. 然后vue根据这三虚拟dom生成真正的三个dom节点,渲染到页面上去
  4. 当我们通过 unshift 逆序添加一个元素到列表时,破坏了原列表的顺序,生成四个虚拟节点
  5. vue会通过diff算法 将左侧(原本的)和右侧(新生成)的虚拟节点进行对比,主要是通过key,去对比
  6. 发现右侧key = 001的地方 文本节点与左侧key = 001的地方文本节点与input节点是一样的,则会复用已有的dom节点,此时就能正确显示,以此类推 002、003对应的节点杜能正确配对显示,所以就能得到我们想要的效果

4、vue监测数据改变的原理

引出问题

demo代码,注意要引入 vue.js (vue 2.xx.xx版本)

javascript"><!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>更新时的一个问题</title><script type="text/javascript" src="../js/vue.js"></script></head><body><!-- 准备好一个容器--><div id="root"><h2>人员列表</h2><button @click="updateMei">更新马冬梅的信息</button><ul><li v-for="(p,index) of persons" :key="p.id">{{p.name}}-{{p.age}}-{{p.sex}}</li></ul></div><script type="text/javascript">Vue.config.productionTip = falseconst vm = new Vue({el:'#root',data:{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:'男'}]},methods: {updateMei(){// this.persons[0].name = '马老师' //奏效// this.persons[0].age = 50 //奏效// this.persons[0].sex = '男' //奏效this.persons[0] = {id:'001',name:'马老师',age:50,sex:'男'} //不奏效// this.persons.splice(0,1,{id:'001',name:'马老师',age:50,sex:'男'}) // 奏效}}}) </script>
</html>

一个属性一个属性的更改是可以检测到的,但是当是数组的整个对象更新的时候,就不行 

临时添加的对象属性,也不具有响应式(因为没有通过Object.defineProperty 进行劫持),除非使用$set添加的 ($set就相当于是重新使用 Object.defineProperty 进行数据劫持)

数组通过索引更改值,vue也检测不到,页面也不会实时更新(当然也可以使用set,就可以检测到了),必须通过数组的七个方法更新数组(push、pop、unshift、shift、splice、sort、reverse),vue才能检测到,因为vue对上述方法进行了重写

$set局限性不能vue实例/根数据对象身上使用 set, 会报错:避免添加响应式属性到xxx

5、其他

5.1、v-model 相关的三个修饰符

v-model的三个修饰符:

           lazy:失去焦点再收集数据

           number:输入字符串转为有效的数字

           trim:输入首尾空格过滤

5.2、过滤器

过滤器:

        定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)。

        语法:

            1.注册过滤器:Vue.filter(name,callback) 或 new Vue{filters:{}}

            2.使用过滤器:{{ xxx | 过滤器名}}  或  v-bind:属性 = "xxx | 过滤器名"

        备注:

            1.过滤器也可以接收额外参数、多个过滤器也可以串联

            2.并没有改变原本的数据, 是产生新的对应的数据

5.3、内置指令

v-text指令:

            1.作用:向其所在的节点中渲染文本内容。

            2.与插值语法的区别:v-text会替换掉节点中的内容,{{xx}}则不会。

v-html(慎用)

因为有了可能会在指令后面的内容中,插入一些获取 cookie的操作(document.cookie),这样就会导致xss攻击,也就是类似仿冒的操作,如果服务器返回的cookie 没有设置成 httpOnly ,则可以通过document.cookie 获取到,攻击者就可以获取到cookie,v-html 就有可能会带有,类似获取cookie,然后发送到攻击者的服务器的操作,所以如果要使用v-html 一定要确保是可信的,并进行相应的校验

 v-once 只加载一次,后续相关值改变, 此处值不作变更

  v-once指令:

            1.v-once所在节点在初次动态渲染后,就视为静态内容了。

            2.以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。

 v-pre

v-pre指令:

          1.跳过其所在节点的编译过程。

          2.可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译。

5.4、自定义指令

自定义指令总结:

            一、定义语法:

                  (1).局部指令:

                        new Vue({                          

                            directives{指令名:回调函数}

                        })                                 

                  (2).全局指令:

                          Vue.directive(指令名,配置对象) 或   Vue.directive(指令名,回调函数)

            二、配置对象中常用的3个回调:

                  (1).bind:指令与元素成功绑定时调用。

                  (2).inserted:指令所在元素被插入页面时调用。

                  (3).update:指令所在模板结构被重新解析时调用。

            三、备注:

                  1.指令定义时不加v-,但使用时要加v-;

                  2.指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名。

指令可以分为函数式写法与对象式写法

javascript">new Vue({el:'#root',data:{name:'尚硅谷',n:1},directives:{big(element,binding){console.log('big',this) //注意此处的this是window// console.log('big')element.innerText = binding.value * 10},fbind:{//指令与元素成功绑定时(一上来)bind(element,binding){element.value = binding.value},//指令所在元素被插入页面时inserted(element,binding){element.focus()},//指令所在的模板被重新解析时update(element,binding){element.value = binding.value}}}})

函数式写法就相当于只写了bind 与 update, 向上面这样bind、inserted、update都写,一些小细节的东西才能处理

5.5、Vm 与 Vc

关于this指向:

                (1).组件配置中:

                      data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【VueComponent实例对象】。

                (2).new Vue(options)配置中:

                      data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【Vue实例对象】。

Vm是指Vue实例, Vc是指VueComponent实例,一般Vm中的$children 会有相应的Vc实例,如果是嵌套组件,则其下也会有$children  也会有Vc实例, Vm 与 Vc基本长一个样 ,且Vm创建的时候又el配置项用于指定服务于那个容器,而Vc则没有

1.一个重要的内置关系:VueComponent.prototype.__proto__ === Vue.prototype

2.为什么要有这个关系:让组件实例对象(vc)可以访问到 Vue原型上的属性、方法。

5.6、插件

 写法 plugins.js

javascript">export default {install(Vue,x,y,z){console.log(x,y,z)//全局过滤器Vue.filter('mySlice',function(value){return value.slice(0,4)})//定义全局指令Vue.directive('fbind',{//指令与元素成功绑定时(一上来)bind(element,binding){element.value = binding.value},//指令所在元素被插入页面时inserted(element,binding){element.focus()},//指令所在的模板被重新解析时update(element,binding){element.value = binding.value}})//定义混入Vue.mixin({data() {return {x:100,y:200}},})//给Vue原型上添加一个方法(vm和vc就都能用了),但是一般不这么做,需要慎重考虑Vue.prototype.hello = ()=>{alert('你好啊')}}
}

在项目入口文件中引入安装一下

javascript">import Vue from 'vue'
//引入App
import App from './App.vue'
//引入插件
import plugins from './plugins'
//关闭Vue的生产提示
Vue.config.productionTip = false//应用(使用)插件
Vue.use(plugins,1,2,3)
//创建vm
new Vue({el:'#app',render: h => h(App)
})

经过上面操作,这些定义好的过滤器、指令、mixin就可以全局使用了

5.7、npm 查看依赖版本

5.8、解决跨域方法

  •  服务端设置cors
  • jsonp,需要前后端配置,且只能处理get请求
  • 脚手架/打包工具 代理
  • nginx代理

6、生命周期

6.1、原理图

常用的生命周期钩子:

            1.mounted: 发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】。

            2.beforeDestroy: 清除定时器、解绑自定义事件、取消订阅消息等【收尾工作】。

        关于销毁Vue实例

            1.销毁后借助Vue开发者工具看不到任何信息。

            2.销毁后自定义事件会失效,但原生DOM事件依然有效。

            3.一般不会在beforeDestroy操作数据,因为即便操作数据,也不会再触发更新流程了。

7、事件总线

1、Vue原型对象上包含事件处理的方法

1)$on(eventName,listener):绑定自定义事件监听

2)$emit(eventName,data):分发自定义事件

3)$off(eventName):解绑自定义事件监听4)

$once(eventName,listener):绑定事件监听,但只能处理一次

2、所有组件实例对象的原型对象的原型对象就是Vue的原型对象

1)所有组件对象都能看到Vue原型对象上的属性和方法

2)Vue.prototype.$bus=newVue(),所有的组件对象都能看到$bus这个属性对象

 事件总线其实大概就是这样一个东西

javascript">new Vue({el:'#app',render: h => h(App),beforeCreate() {Vue.prototype.$bus = this //安装全局事件总线},
})

8、Vuex

8.1、基础使用

vuex官网 都是使用

但是需要注意是,vue开发者工具只能检测到mutation 的动作

mapState、mapGetters使用

javascript">computed:{//靠程序员自己亲自去写计算属性/* sum(){return this.$store.state.sum},school(){return this.$store.state.school},subject(){return this.$store.state.subject}, *///借助mapState生成计算属性,从state中读取数据。(对象写法)// ...mapState({he:'sum',xuexiao:'school',xueke:'subject'}),//借助mapState生成计算属性,从state中读取数据。(数组写法)...mapState(['sum','school','subject']),/* ******************************************************************** *//* bigSum(){return this.$store.getters.bigSum}, *///借助mapGetters生成计算属性,从getters中读取数据。(对象写法)// ...mapGetters({bigSum:'bigSum'})//借助mapGetters生成计算属性,从getters中读取数据。(数组写法)...mapGetters(['bigSum'])},

mapMutations、mapActions 

javascript">methods: {//程序员亲自写方法/* increment(){this.$store.commit('JIA',this.n)},decrement(){this.$store.commit('JIAN',this.n)}, *///借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法)...mapMutations({increment:'JIA',decrement:'JIAN'}),//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(数组写法)// ...mapMutations(['JIA','JIAN']),/* ************************************************* *///程序员亲自写方法/* incrementOdd(){this.$store.dispatch('jiaOdd',this.n)},incrementWait(){this.$store.dispatch('jiaWait',this.n)}, *///借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(对象写法)...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})//借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(数组写法)// ...mapActions(['jiaOdd','jiaWait'])},

模块化开发

 模块化的作用,顾名思义就是模块化,减少在同事间在协作的过程中可能会出现的冲突,代码结构更清晰,方便维护

9、vue-router

官网

9.1、params参数 

携带 params 参数进行路由跳转,必须使用 name,不能使用path

若路由定义了 props: true 则会将params 作为组件的props,可以在组件里面通过prosp获取

若props 直接返回对象也可以,也可以写成函数形式

 路由跳转方式:声明式导航 router-link、编程式导航,router.push/replace

9.2、路由守卫

全局路由守卫

javascript">//全局前置路由守卫————初始化的时候被调用、每次路由切换之前被调用
router.beforeEach((to,from,next)=>{console.log('前置路由守卫',to,from)if(to.meta.isAuth){ //判断是否需要鉴权if(localStorage.getItem('school')==='atguigu'){next()}else{alert('学校名不对,无权限查看!')}}else{next()}
})//全局后置路由守卫————初始化的时候被调用、每次路由切换之后被调用
router.afterEach((to,from)=>{console.log('后置路由守卫',to,from)document.title = to.meta.title || '管理系统'
})

路由独享守卫

javascript">{name:'zhuye',path:'/home',component:Home,meta:{title:'主页'},children:[{name:'xinwen',path:'news',component:News,meta:{isAuth:true,title:'新闻'},beforeEnter: (to, from, next) => {console.log('独享路由守卫',to,from)if(to.meta.isAuth){ //判断是否需要鉴权if(localStorage.getItem('school')==='xxxx'){next()}else{alert('学校名不对,无权限查看!')}}else{next()}}}
}

组件内路由守卫

javascript">export default {name:'About',/* beforeDestroy() {console.log('About组件即将被销毁了')},*//* mounted() {console.log('About组件挂载完毕了',this)window.aboutRoute = this.$routewindow.aboutRouter = this.$router},  */mounted() {// console.log('%%%',this.$route)},//通过路由规则,进入该组件时被调用beforeRouteEnter (to, from, next) {console.log('About--beforeRouteEnter',to,from)if(to.meta.isAuth){ //判断是否需要鉴权if(localStorage.getItem('school')==='xxxxx'){next()}else{alert('学校名不对,无权限查看!')}}else{next()}},//通过路由规则,离开该组件时被调用beforeRouteLeave (to, from, next) {console.log('About--beforeRouteLeave',to,from)next()}}

路由模式

hash 与 history

history 会将路由地址,当做请求资源的地址,所以会出现404问题,需要后端去做适配,区别那些前段路由,那些是后端路由才行 ,而hash就不会出现这个问题


http://www.ppmy.cn/news/1473599.html

相关文章

如何处理 PostgreSQL 中由于表连接顺序不当导致的性能问题?

文章目录 一、理解表连接和连接顺序二、识别由于表连接顺序不当导致的性能问题三、影响表连接顺序的因素四、解决方案手动调整连接顺序创建合适的索引分析数据分布和优化查询逻辑 五、示例分析手动调整连接顺序创建索引优化查询逻辑 六、总结 在 PostgreSQL 中&#xff0c;表连…

java版本工程项目管理系统 Spring Cloud+Spring Boot+Mybatis+Vue+ElementUI+前后端分离构建工程项目管理系统

工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离构建工程项目管理系统 1. 项目背景 一、随着公司的快速发展&#xff0c;企业人员和经营规模不断壮大。为了提高工程管理效率、减轻劳动强度、提高信息处理速度和准确性&#xff0c;公司对内部工程管理的…

android super.img解包

一、lpunpack工具编译 mmm system/extras/partition_tools/二、稀疏镜像到非稀疏镜像转换 simg2img super.img super_unsparse.img三、super.img解包 lpunpack super_unsparse.img output 四、拆包结果 ls -l -rw-r--r-- 1 rdx rdx-user size Jan 1 14:36 product_a.img -r…

HEX文件格式

HEX文件格式是一种用于存储和传输数据到嵌入式系统&#xff08;如单片机&#xff09;的文件格式。以下是关于HEX文件格式的详细解释&#xff1a; 一、文件概述 HEX文件&#xff0c;全称为Intel HEX&#xff0c;是一种ASCII文本文件&#xff0c;用于记录文本行的数据。它常用于…

流式处理设计模式总结说明

文章目录 前言流式处理设计模式单事件处理使用本地状态多阶段处理和重分区使用外部查找&#xff1a;流和表的连接表与表的连接流与流的连接乱序事件重新处理交互式查询 Streams示例字数统计股票市场统计填充点击事件流 前言 每一种流式处理系统都不一样——从基本的消费者、处…

Linux之进程控制(下)

目录 进程替换的概念 进程替换的函数 execl​编辑 execlp execle execv execvp execve 上期&#xff0c;我们学习了进程创建&#xff0c;进程终止和进程等待&#xff0c;今天我们要学习的是进程控制中相对重要的板块------进程替换。 进程替换的概念 在进程创建时&…

【数据结构】03.顺序表的应用——通讯录

本篇文章我们将使用顺序表来实现通讯录的增删查改功能。 代码如下&#xff1a; //Contact.h#pragma once#define NAME_MAX 30 #define GENDER_MAX 10 #define ADDRESS_MAX 100 #define TEL_MAX 12enum{Exit,Add,Del,Seek,Change,Show};//枚举类型&#xff0c;便于理解数字含义…

线程同步66666

1. 概述 当有多个线程访问同一个共享资源&#xff08;临界资源&#xff09;时&#xff0c;且不允许同时访问&#xff0c;那么就需要线程同步。常见的线程同步方式&#xff1a;互斥锁、读写锁、条件变量、信号量。 2. 互斥锁 互斥锁的方式可以简单概括为&#xff1a;锁定操作…