本文共 28000 字,大约阅读时间需要 93 分钟。
原理上:proxy和defineProperty
vue 在实例初始化的时候遍历data中的所有属性,并使用Object.defineProperty把这些属性全部转化成getters/setter。这样当追踪数据发生变化时,setter会被自动调用,但是这样做有问题,在添加或者是删除对象的属性时,vue检测不到,因为添加或者是删除的对象没有在初始化进行响应式处理,只能通过$set来调用Object.defineProperty处理,然而vue3的proxy直接代理整个对象而非对象属性,这样只需做一层代理就可以监听同级结构下的所有属性变化,包括新增属性和删除属性。 使用上:安装时的不同;创建项目的不同;目录文件夹的不同。Vue作为前端主流的框架之一,其基本的使用步骤如下
1、引入
初级阶段可以通过官网下载开发版本 ,在相应的JS代码段引入。 2、示例如下Document //响应式原理的体现 输出 "hello shixue" hello { { name}} //输出一个无须列表 包含data中的moives所有数据****//实现了一个基本的计数功能 当前计数:{ { counter}}
- { { item}}
3、Vue实例化对象的组成
1>el: 表示要挂载到哪一个HTML元素上。是字符串型的HTML元素 2>data: 用于显示在浏览器页面的基本数据对象。是对象或者是函数形式,组件中必须使用函数形式,原因如下:JS的对象是引用数据类型,当不同的实例想要引用该对象时,只要有一个实例更改了对象,其他实例中的对象也会更改。但vue中更多的是组件的复用,这就要求每个组件要有自己的数据,所以data采用函数形式而不是对象形式。数据采用函数值返回,可在不同的实例中得到不同的返回值,即每个组件有自己的数据空间,不会干扰其他的组件。 3>methods: 相当于实例化对象的方法。是函数形式4、viewmodel中methods的基本数据得在data函数中声明之后 才会通过双向绑定送到DOM否则的话,不会在用户界面显示出来,也就是只有view的内容才会显示在用户界面。解决方法:通过vue的全局api $set
()把viewmodel中methods想要输出的属性手动改成一个响应式的。比如以下代码。
- { { value}}
这里,我们使用计数器案例分析
当前计数:{ { counter}}
MVVM(Model-view-viewmodel)是一种软件架构设计模式,目的是实现用户界面开发和业务逻辑或后端开发相分离。在计数器案例中,View(视图层)就是浏览器页面,即DOM元素;Model(数据层)是计数器中obj,即data数据;View-Model(视图模型层)是创建的实例化对象。
响应式原理:首先,View-Model根据Data Binding将Model的obj传给DOM元素,显示在用户界面;之后,View-Model根据DOM Listening把监听到的点击事件传给Model,通过methods中的方法,改变obj中的数据,obj更新后的数据在显示在页面中。
MVVM设计模式的优点:
1>、实现了用户界面和业务逻辑的分离,提高View或者是Model的复用性。一个viewmodel可以被多个view绑定,当view中的数据改变的时候,model中的数据必然会改变,这样当想改变整个项目中某一个view的数据时,只需更改model数据即可;同理,一个viewmodel也可以被多个view绑定,提高viewmodel的复用性 2>、实现的DOM数据的自动更新,利用双向绑定,当数据更新后视图自动更新。MVVM设计模式的缺点:
1>、BUG很难调试,因为是双向绑定的,所以当代码出现bug时不知道是view的问题还是model的问题。双向绑定使得某个位置的bug很容易传送到别的地方。并且数据绑定的声明是指令式的放在view中的,不能利用断点调试错误。 2>、大型项目中的view比较多,viewmodel的建立和维护成本高。1、Mastache语法 { {}}
作用:用于显示model中的内容,实现数据的双向绑定{ { message}}
2、v-once语法
作用:DOM内容只被渲染一次,即使model数据改变,view数据也不会变,适用于内容不允许徐更改的时候 在console中可以改变model的数据,但加了这个属性之后,view永远不会更改{ { message}}
3、v-html语法
作用:当model中的数据为 HTML 代码时 能被DOM正确解析,不会显示HTML代码。后边跟 字符串4、v-text语法
作用:和{ {}}一样 后边跟字符串5、v-pre语法
作用:该元素及其子元素不会被编译用于显示原本的Mastache内容。{ { message}}
6、v-cloak语法
作用:该属性在HTML元素解析完之前有,显示Mastache原本内容,解析完之后该属性就消失了。{ { message}}
//在6秒之前显示 { {message}} 6秒之后显示 shixue
1、作用
实现DOM Listener,实现用户和界面的交互,比如点击按钮 拖拽事件 键盘事件等。 2、基本使用{ { counter}}
//引用函数 //直接改data数据
3、HTML中函数调用时的参数问题
{ { counter}}
//按钮1 //按钮1 //按钮2 undefined //按钮2 MouseEvent //按钮3 5 undefined //按钮3 MouseEvent undefined //按钮3 5 MouseEvent //按钮3 undefined
注释:
1>、函数没有参数时,小括号写和不写是一样的。 2>、函数有一个参数时,小括号不写 默认传入 函数参数;小括号写了不传参数 undefined;小括号写了传了参数完全正确。 3>、函数有两个参数并且有event时,小括号不写输出MouseEvent但参数未定义; 小括号只写参数,输出参数和undefined; 小括号只写事件,输出事件但参数undefined;小括号都写,参数和事件被正确调用。 4、v-on的修饰符//.stop是阻止冒泡 只进行btnClick 不进行divClick百度一下 //.prevent是阻止默认事件,像下边这样的就是点击百度一下之后不会进入百度界面 //点击 enter 才有效 //点击 13 才有效 //只允许被点击一次 第二次无效
1、作用
绑定从服务器请求过来的动态数据,某些属性。 比如a的href属性,img的src属性,css, style。语法糖 : { {}} 是用来绑定内容的。 v-html 是解析内容中有HTML代码的,这种情况下,HTML的标签一般是h p等然而model的数据有HTML代码,所以需要对HTML代码解析。 2、绑定a的href属性和 img的src属性3、绑定class常见案例
案例一:点击按钮,变换颜色。{ { message}}
案例二:点击哪一个列表,哪一个变换颜色。
- { { item}}
//实现动态切换列表颜色
案例三:class中放多个样式 以对象形式存在
{ { message}}
案例四:class太多的话,放在methods的函数中
.active { color: red } .line { text-decoration: underline; }{ { message}}
4、绑定style
案例一:以对象形式呈现//以下两种方式均可以实现 字体颜色变红 和class不同的是,这个属性值是以字符串形式存在的{ { message}}
{ { message}}
案例二:style样式太多的话,把属性放在methods的函数里
{ { message}}
1、作用
实现了表单内容和model中数据的双向绑定。 双向绑定的原理:input中的v-model绑定了message,当在input中输入内容时会传给message,message的数据发生改变。又因为上边采用了Mastache语法,message会把内容实时渲染到DOM,从而实现页面的实时更新。 2、基本使用请输入用户信息{ { message}}
3、多个单选按钮的情况
男 女您选择的性别是:{ { gender}}
注释:当存在多个单选按钮的时候 v-model和value搭配使用。
4、复选框的情况 1>、只有一个复选框 使用布尔值存储同意协议您的选择是:{ { isAgree}}
2>、多个复选框 采用数组存储
足球 蓝球 羽毛球 乒乓球您的选择是:{ { hobbies}}
5、select情况 v-model和value配合使用
1>、单选您喜欢的水果是 { { fruit}}
2>、多选 用数组存储数据
您喜欢的水果是 { { fruits}}
注释:多个被选中的值在数组中存放,v-model和value配合使用,加一个multiple属性,表示可以多选。
6、修饰符 1>、lazy修饰符 作用:默认情况下,input中的内容和model中的data是同步更新的,但有些情况下不需要实时更新。lazy是在失去焦点或者是按下enter之后才会改变data内容。{ { message}}
2>、number修饰符
作用:默认情况下表单的输入内容是字符串型数据,如果我们希望处理的是数字类型,就把字符串转换成数字。{ { message}}
3>、trim修饰符
作用:过滤字符串两边的空格{ { message}}
1、作用
遍历数组或者是对象的每一个元素 2、遍历数组{ { item}}{ { index}}
注释:遍历数组的时候 可以放两个参数 item和index index可选可不选。
3、遍历对象{ { item}}{ { index}}{ { key}}
//输出 shixuename0 18age1 1.88height2 所以item是属性值 index是属性名 key是索引
注释:遍历对象的时候可以放三个参数,item index key。item是属性值,index是属性名,key是索引值
4、v-for的key属性 作用:根据DOM和DIFF算法。当给含有多个节点的一层插入某个节点的时候,DIFF算法的执行效率很低,为了提高代码的执行效率,快速更新DOM, 利用key属性给每一个节点添加一个唯一标识,在数组中就是动态绑定item,在对象中就是动态绑定item。这样DIFF算法就可以正确识别新节点,并且插入到正确的位置。{ { item}}{ { index}}{ { key}}
5、数组的增删改查
{ { item}}
1、作用
当遇到不同的情况时,渲染不同的DOM元素。 2、v-if v-else-if v-else的基本使用优秀
良好
及格
不及格
3、条件渲染的经典案例
需求:点击按钮实现用户名和邮箱的切换登录//label标签的作用是当点击label内容时,也能聚焦到对应的input输入框中
注释:案例的小bug: 从用户界面切换到邮箱登录界面的时候,原用户界面input中的内容不会消失,这是因为VUE在进行DOM渲染的时候,处于性能的考虑,会复用原本存在的元素 不会创建新的元素。解决方法:在每一个input后边加入不同的key,这样就不会出现复用的问题了。
4、v-show 作用和v-if相同, 不同的是,当v-if的值为false时,DOM元素不会被渲染;但是v-show的DOM元素会被渲染,只不过是把display设置为none了。 应用场合:v-show用于频繁切换显示与否。显示内容
1、作用
data中的数据需要进行计算在显示在浏览器中的话,则计算函数需要在computed中被定义。 2、基本使用语法{ { fullName()}}
{ { fullName()}}
{ { fullName()}}
{ { fullName1}}
{ { fullName1}}
注释:在 后台 “methods被调用了” 输出了三次,因为在methods中定义的函数,调用几次就会被执行几次。”computed被调用了“,只输出了一次,因为computed中的函数不管在view中被调用几次,均会执行一次。
3、methods和computed的区别和联系 联系:均在里边定义函数。 区别: 1>、由于computed中函数的计算效率低,放在methods中会影响其他函数的执行。 2>、放在computed中的函数不管被调用几次,均只执行一次;但是methods中的函数被调用几次就会执行几次函数。 3>、调用时 在computed中定义的函数直接调用函数名即可,但是methods中的函数需要加小括号。 4、计算总价案例总价:{ { totalPrice}}
注释:reduce函数是用来遍历数组的,对数组中的每个元素按照函数进行计算,参数为函数,函数后可以跟初始值。
1、作用
一个大型页面是由好多小的独立的功能实现的,一个大型复杂页面的创建和维护成本很高,因此我们把大页面分割成一个个小的,功能独立的,可复用的组件。 2、基本使用 1>、创建组件构造器 extend 2>、注册组件 component 3>、使用组件//3、使用组件
3、全局组件与局部组件
1>、全局组件 通过Vue.component注册的组件,可在任一Vue实例化对象中使用。 2>、局部组件:在Vue实例化对象中注册的组件4、语法糖
作用:省去了extend步骤 1>、全局组件2>、局部组件
5、模板分离
作用:把template抽离出来 基本使用:把template内容用一个包含 id 的标签抽出来,在注册组件的时候,只需要写template的 id 名即可。石雪
6、父组件与子组件
1>、基本使用 注释:子组件的注册和使用必须要放在父组件中,使用时要放在当下的 div 中,组件使用的三个步骤,不管使用不使用语法糖,步骤一个都不能丢。2>、语法糖写法
7、组件数据的存放
组件数据存放在自己的data函数中,该函数返回一个 对象。为什么是一个函数呢? JS的对象是引用数据类型,当一个对象发生改变之后,引用该组件的所有位置的data均会发生改变,但我们的需求是每个组件有自己的data,所以使用函数定义。函数中返回对象,也是为了让每个组件有自己的data。//石雪 //石雪 //石雪 { { message}}
8、组件通信
1>、父组件传递给子组件 通过 props 作用:由上边可知,子组件不能直接从父组件中请求数据,但是从服务器请求过来的数据有时需要在子组件中呈现,所以要把父组件的数据传给子组件。{ { message}}
2>、子组件传到父组件
{ { counter}}
注释:这是由子组件实现的按钮点击事件,下边用子组件计算,由父组件展示。
//子组件发送的事件由 @事件名=“父组件的函数名” 接收//展示是 放在父组件 { { total}}
//子组件的 函数在这里实现 但是 展示是放在父组件的
3>、父组件访问子组件 $children $refs
方案一:children
//表示父组件有两个子组件哈哈哈
嘿嘿嘿
注释:通过 children 来访问子组件 是 以数组形式访问的,必须有索引值,但是有些情况不需要索引值,所以采用refs来访问 只需要给子组件绑定一个 id 即可。
方案二:refs
哈哈哈
嘿嘿嘿
4>、子组件访问父组件 $parent
嘿嘿嘿
9、插槽
1>、作用 以导航栏为例,虽然风格迥异,但是有共性,在这里我们把各种不同的导航栏的共性抽取出来,组成组件,不同的暴漏成插槽,至于插槽中写什么,由用户自己确定。 2>、基本使用我是替换内容1
我是替换内容2
我是插槽内容
注释:默认情况下使用插槽的默认内容,插槽内容一旦被更改,就会显示被更改后的内容。
3>、具名插槽: 给插槽一个具体的名字,在用到该插槽的时候,更改此插槽内容,剩余插槽仍然保持默认。//输出 我是左边 我是中间 我是右边 我是新的左边 //输出 我是新的左边 我是中间 我是右边 我是左边 我是中间 我是右边
4>、总结
父组件负责替换插槽标签,子组件负责更改插槽内容//slot-scope插槽范围,表示插槽的所有信息
- { { item}}
{ { item}}
1、作用
当前端代码量越来越大,需要引入多个js文件时会出现变量名冲突的问题,为了解决这个问题,使用匿名函数;但是匿名函数是局部变量,在其余文件中无法使用该变量。所以,进行模块化开发。模块化开发的作用就是:在某一个JS文件中定义有一定功能的函数 变量和类,并把他们作为接口暴露出来,有需要该功能的情况之下,就导入该模块。 2、ES6语法 modules 在某一个js文件中 比如 info.js,可以导出变量 类 和函数 export。//导出变量export let a = 18//导出函数export function bb(paras) { console.log(paras);}//导出类export class Person { constrctor(name, age) { this.name = name this.age = age } run() { console.log(this.name + this.age); }}//导出无函数名的的函数 在导入时可以自己命名export default function(cc) { console.log(cc);}
在index.html中导入各个模块 import
//在这里 要 标明 type 因为html默认的是解析js文件,在这里是模块化 所以无法正常解析 不写的话 后台会报错 ‘Cannot use import statement outside a module’
1、作用
快速搭建大型项目的VUE环境配置,即webpack配置,就是不用自己配置webpack了(目录结构和代码结构) 淘宝镜像安装 这样之后就可以使用 cnpm 安装了,速度比 npm 快npm install -g cnpm --registry=https://registry.npm.taobao.org
2、基本使用
1、基本环境 有node环境 2、安装脚手架 CLI3cnpm install @vue/cli
CLI2 cnpm install @vue/cli-init
3、创建项目 CLI2: vue init webpack 项目名
CLI3: vue creat 项目名
使用CLI2在创建项目过程中会遇到以下几个选项: Project name: 项目名,不能大写 Project description: 项目描述 Author: 作者 Vue build: runtime-compiler(关系到VUE的运行过程,下边有讲解) Use ESlint to lint to your code: No,eslint是一种代码规范,会对代码格式有严格要求 Set up unit tests: No, 设置测试单元‘ Set up e2e tests with Nightwatch: No,设置端到端测试 Should we run ‘npm install’…: Yes,use NPM 使用CLI3在创建项目过程中会遇到以下几个选项: 3、CLI2的目录结构
4、VUE的运行过程
总结下来就是:有template的时候,使用runtime-compiler,没有template的时候使用 runtime-only。 5、CLI2项目运行过程 首先打包npm run build
,之后运行npm run dev
6、CLI3的目录结构 基本的目录配置在这个文件中被隐藏了 项目目录中的node_modules@vue\cli_servers\webpack.sonfig.js 7、CLI3的运行过程 首先npm run build
进行打包,之后npm run serve
运行 正常情况下,一个组件中的变量只能在一个视图下使用,但是有的组件中的变量需要在多个视图中共用,所以把这样的变量交给vuex管理,在任一视图需要这个变量的时候调用就可以。运行过程如下:
1>安装
cnpm install --save vuex
2>、建立store文件夹,文件夹下建立index.js文件,index.js中创建vuex实例并导出。
import Vuex from 'vuex'import Vue from 'vue'Vue.use(Vuex)const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment(state) { state.count ++ }, decrement(state) { state.count -- } }})export default store
之后在main.js中引入vuex模块,就是把这些vuex状态变量放到全局中,所有组件都可以调用。
import Vue from 'vue'import App from './App'import router from './router'import store from './store'Vue.config.productionTip = falsenew Vue({ el: '#app', router, store, render: h => h(App),})
最后在对应的组件中使用该状态 这里是计数器案例
{ { count}}
如果在另一个组件中也引入该状态,则变化是同步的,即满足响应式准则。
1>、state: 相当于之前的data,用于存放基本属性
2>、motations: 相当于之前的methods,用于更改state状态的唯一途径 3>、actions:motations中的异步操作放在这里边,比如网络请求 4>、getters:类似于之前的计算属性 computed 用于基本属性经过一系列改变之后应用的数据信息。 5>、modules:vuex的封装可能会出现各种不同的模块,所以对不同的模块需要的状态进行单独的封装,比如购物车模块封装成cart.js,详情模块封装成detail.js等等。在modules中注册一下即可。vuex的状态存储是响应式的,当vue组件从store中读取状态的时候,若store中的状态发生改变,那么相应地组件也会相应地高效更新。
改变store中的状态的唯一路径就是显式的提交(commit )mutation,这样可以方便的跟踪每一个状态的变化。mutations:修改state的唯一路径,用于同步操作,使用vuex的时候需要通过commit来提交需要操作的内容,mutations有一个字符串的 事件类型和一个回调函数,回调函数就是状态更改的地方,回调函数的第一个参数就是state。
const store = new Vuex.Store({ state: { count: 1 }, mutations: { increment (state) { state.count++ // 变更状态 } }})
组件中使用的时候:
store.commit('increment')
action:用于异步请求,提交的是mutations而不是直接更改状态state。context.commit
提交一个 mutation,或者通过 context.state
和 context.getters
来获取 state 和 getters。使用的时候通过dispatch调用
const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment (state) { state.count++ } }, actions: { increment (context) { context.commit('increment') } }})
综上所述,不同点有以下几个方面
1、作用不同:mutations是用来更改state,actions提交的是mutations 2、同异步操作:mutations是用来进行同步操作的,然而actions是异步操作的 3、函数的参数不同:mutations的参数的是state,包含state中的参数,actions的参数是context,是state的同级,包含state和getters。1、存储位置的不同
vuex存储在内存中,localstorage存储在本地,只能存储字符串类型的数据,并且得通过JSON的stringfy和prase转换。读取内存的速度比本地要快 2、压面刷新会不会消失 vuex中存储的数据刷新压面会消失,但是localstorage不会 3、应用场合不同 vuex应用于组件之间数据的传输,localstorage用于浏览器页面之间的数据交换,vuex不是响应式的,但是localstorage是响应式的。vuex的数据是响应式的
vuex的数据不能直接更改,只能通过mutations显示的提交commit来进行更改。因为mutations对状态进行更新之后在devtools工具中是能够实时的看到状态更新的,可以对状态实现实时追踪,但是如果进行异步操作的话,就不知道啥时候更新的了,所以不能进行状态的异步操作。
严格模式就是无论什么情况下状态发生了更新并且不是由mutations函数引起的时候,都会抛出错误;设置如下:就是在创建vuex实例的时候加入严格模式。
const store = new Vuex.Store({ strict:true,})
调用mapGetters函数,然后利用对象展开运算符把getters属性混入到computed对象中
import { mapGetters} from 'vuex'export default{ computed:{ ...mapGetters(['total','discountTotal']) }}
调用mapMutations函数,在组件中这样使用
import { mapMutations } from 'vuex'methods:{ ...mapMutations({ setNumber:'SET_NUMBER', })}
然后调用this.setNumber(10)
相当调用this.$store.commit('SET_NUMBER',10)
,10是mutations改变state状态的时候还想要传递的参数。
state:存放基本数据
getters:存放从基本数据派生出来的数据 mutations:提交更改状态的唯一路径,同步操作 actions:包裹mutations,进行异步操作,比如发送网络请求 modules:模块化vuex,对于不同的功能模块中的状态放在不同的js文件中,就是模块化管理了。将路径和组件映射起来。就是刚开始的时候一下子把所有的静态资源请求过来放到静态资源服务器,然后通过不同的url去实现静态资源的分配。
1>、安装
npm install vue-router --save
也可以在用CLI2创建项目时安装vue-router
2>、首先在router文件夹下的index.js中导入路由对象,创建路由实例,之后在main.js的vue实例中挂载路由实例。 index.js代码如下:import Vue from 'vue'import Router from 'vue-router'Vue.use(Router)export default new Router({ routes: [ { } ]})
main.js代码如下:
import Vue from 'vue'import App from './App'import router from './router'Vue.config.productionTip = false/* eslint-disable no-new */new Vue({ el: '#app', router, components: { App }, template: ''})
3>、创建路由组件 about.vue
我是about的内容
我是home的内容
4>、配置路由映射
在router的index.js中import Vue from 'vue'import Router from 'vue-router'import Home from '@/components/home'import About from '@/components/about'Vue.use(Router)export default new Router({ routes: [ //这个是配置路由的默认路径 { path: '/', redirect: '/about' }, { path: '/about', component: About } { path: '/home', component: Home }, { path: '/about', component: About }, ], mode: 'history' //将url的路径改变设置成history模式 url路径就没有#了,看起来更舒服,推荐使用这种方式。})
5>、使用路由
在APP.vue中使用我是APP内容
首页 关于
tag: tag可以指定之后渲染成什么组件, 比如上面的代码会被渲染成一个li元素, 而不是a元素。
replace: replace不会留下history记录, 所以指定replace的情况下, 后退键返回不能返回到上一个页面中。 active-class: 当对应的路由匹配成功时, 会自动给当前元素设置一个router-link-active的class, 设置active-class可以修改默认的名称.(在router文件夹下的index.js中加入linkactiveclass:’active’就可以只显示active属性了。当一次性从服务器请求整个页面的时候会出现短暂的白屏现象,为了避免这种情况,我们将路由对应的组件打包成一个个小的js文件,只有当该路由被访问的时候才能加载该组件。
懒加载的实现:在router文件夹下的index.js中配置路由的时候 采用箭头函数加import的方式导入相应的路由组件,如下所示。
import Vue from 'vue'import Router from 'vue-router'const Home = () => import('../components/home.vue')const About = () => import('../components/about.vue')Vue.use(Router)export default new Router({ routes: [ { path: '/', redirect: '/about' }, { path: '/about', component: About }, { path: '/home', component: Home } ], mode: 'history'})
还是按照之前的步骤引入,首先创建子组件,之后在index.js中配置路由,最后在相应的父组件中使用路由。
子组件message.vue我是home的message的内容
index.js的配置
import Vue from 'vue'import Router from 'vue-router'const Home = () => import('../components/home.vue')const About = () => import('../components/about.vue')const Message = () => import('../components/message.vue')Vue.use(Router)export default new Router({ routes: [ { path: '/', redirect: '/about' }, { path: '/about', component: About }, { path: '/home', component: Home, children: [ { path: 'message', //注意:这里的路径不用写 / 写了会报错 component: Message } ] } ], mode: 'history'})
home.vue的引入
我是home的内容
home的massage
作用1:在不同的路由下显示不同的网页标题
使用:钩子函数,在路由跳转之前和之后触发beforeEach和afterEach函数import Vue from 'vue'import Router from 'vue-router'const Home = () => import('../components/home.vue')const About = () => import('../components/about.vue')const Message = () => import('../components/message.vue')Vue.use(Router)const routes = [ { path: '/', redirect: '/about' }, { path: '/about', component: About, meta: { title: '关于' } }, { path: '/home', component: Home, meta: { title: '首页' }, children: [ { path: 'message', component: Message } ] }]var router = new Router({ routes, mode: 'history',})//实现在路由跳转之前改变 文章标题router.beforeEach((to,from, next) => { window.document.title = to.meta.title next()})export default router
作用2:用户用户登录权限的验证,当用户符合条件的时候才实现路由的跳转,反之先进行登录。
1、全局路由钩子:router.beforeEach()路由进入之前 router.afterEach()路由进入之后 //判断页面是否登陆了,没有登录的话,就跳转到登录页面router.beforeEach((to, from, next) => { let ifInfo = Vue.prototype.$common.getSession('userData'); // 判断是否登录的存储信息 if (!ifInfo) { // sessionStorage里没有储存user信息 if (to.path == '/') { //如果是登录页面路径,就直接next() next(); } else { //不然就跳转到登录 Message.warning("请重新登录!"); window.location.href = Vue.prototype.$loginUrl; } } else { return next(); }})
//跳转之后,滚动条滚动到顶部
router.afterEach((to, from) => { // 跳转之后滚动条回到顶部 window.scrollTo(0,0);});
2、单个路由独享的钩子:beforeEnter
export default [ { path: '/', name: 'login', component: login, beforeEnter: (to, from, next) => { console.log('即将进入登录页面') next() } }]
3、组件内部的钩子
beforeRouteUpdate:在组件进入之前触发 beforeRouteEnter:在路由发生改变并且复用同一个组件的时候触发,在/foo/1 和 /foo/2之间切换的时候触发 beforeRouteLeave:在组件离开之后触发 注意在beforeRouteUpdate的时候,组件实例还没有被创建所以,作用:可以使被包含的组件保留状态,或避免重新渲染。
属性:include - 字符串或者是正则表达式,只有被匹配的才会被缓存; exclude - 字符串或者是正则表达式,任何匹配的组件都不会被缓存。1、hash模式,开发中默认的模式,带着一个 # 后边的值就是hash值,hash值出现在浏览器的URL中但是不会出现在http 请求中,也就是当hash值发生变化的时候并不会向服务器发送http 请求,但是可以实现前端hash值和对应的URL联系起来。实现了不用刷新页面就更新页面的内容。
2、history模式,不带 # 更加美观但是会把URL提交给HTTP请求,向服务器端索要数据,如果服务器端没有对应的资源的话返回 404,有两种API,一个是修改历史状态(pushState()和replaceState()),这两个API确实是改变了URL但是不会向服务器端发送请求,如果需要更新页面但是又不向后端发送请求的话,就使用这两个API,还有就是用于切换的API(back() forward() go())。 3、两种模式的配置实在index.js中创建路由对象的时候加一个 mode:‘history’属性,默认的是 hash。 4、两种模式的对比: 1>、history可以发送同源的URL,但是hash只能更改#后边的参数 2>、history可以发送相同的的URL,并且将浏览记录保存在栈中,但是hash不可以发送相同的URL. 3>、history的URL会完成被发送到服务器,hash只发送 # 前边的内容到服务器。1、通过在路由发生改变的时候添加监听函数
// 监听,当路由发生变化的时候执行watch: { $route: { handler: function(val, oldVal){ console.log(val); }, // 深度观察监听 deep: true }},
2、在控制台输入
window.location.hash //得到当前页面的hash值
router是路由的实例化对象,包含要跳转的路由信息。
route是要跳转的路由对象,有hash query name等参数信息1、params方式
配置:在index.js中{ path: '/user/:userid', component: User,},
路由的跳转
// 方法1:router-link按钮
参数的获取
$route.params.userid
2、query方式
配置路由:在index.js中,和普通的配置方式一样 路由跳转:/ 方法1:router-link方法按钮 // 方法2:js方法this.$router.push({ name: 'users', query:{ uname:james }})
获取参数
$route.query.要获取的参数
使用location.href刷新了页面。
使用history.pushatate()无页面刷新,静态跳转。 引进router,实际上就是和history.pushatate()是一样的,均没有页面 刷新出现。url地址显示:query更加类似于ajax中的get传参,params则类似于post传参,前者在浏览器地址栏中显示,后者则不显示。
用法:query要用path来引入,params要用name来引入,接收参数是类似的,均是this.$route.query.name
和 this.$route.params.name
。 丢失数据:query刷新不会丢失里边的数据,但是params刷新会丢失里边的数据。 对于同一个URL的请求,当点击不同位置的时候显示不同的页面,AJAX实现了不用刷新整个页面就更新部分内容的技术,但是他不知道浏览器进行到了哪一步,为SPA页面的每一个视图添加一个唯一的标识。为了实现这个目的,我们首先解决刷新浏览器页面的时候重定位的问题,还有就是用不同的URL对应不同的图片和内容的问题。
方案:拦截用户的刷新操作,感知URL的变化,当变化的时候,比如只有参数发生改变但是并不会 提交到服务器,那就根据这些变化用JS去显示不同的内容。$props
// 父组件
// 子组件{
{ msg}}
$emit发送事件并且可以发送一参数,然后父组件通过 v-on接收并监听参数
// 父组件{
{ currentIndex}}
//子组件{ { item}}
采用事件总线。首先创建事件总线,然后在需要的组件中引入该事件总线,发送方用 $emit,接收方用 o n 进 行 接 收 参 数 。 原 理 就 是 组 件 1 用 on进行接收参数。原理就是组件1用 on进行接收参数。原理就是组件1用emit把参数发送到事件总线上,然后组件2从事件总线上获取数据。
1、创建事件总线// event-bus.jsimport Vue from 'vue'export const EventBus = new Vue()
假设有两个兄弟组件
firstCom中的内容
在第二个组件中接收事件
求和: { { count}}
通过 project发送,通过inject注入,其实这两个就相当于是vue的两个钩子
在父组件中provide() { return { num: this.num };}
在子组件中
inject: ['num']
还可以这样写。这样写的话,就是可以访问父组件的所有属性
provide() { return { app: this };}data() { return { num: 1 };}inject: ['app']console.log(this.app.num)
在子组件中
export default { data () { return { name: 'JavaScript' } }, methods: { sayHello () { console.log('hello') } }}
在父组件中
父子组件通信用:props $emit(on来接收) refs 钩子函数(project和Inject)
非父子组件之间的通信:事件总线定义:vue实例创建到销毁的全部过程
完整过程:开始创建实例-初始化数据-编译模板-挂载DOM-渲染-更新-在重新渲染-销毁 功能:vue的所有功能的实现是由生命周期实现的,不同的生命周期对应不同的钩子函数来完成组件数据管理和DOM渲染,不同生命周期的多个钩子事件更容易形成vue的逻辑处理。beforeCreated:创建前。状态:初始化实例之后,this执行创建的实例,此时时间观察机制尚未形成,不能获取DOM节点。作用:初始化非响应式变量
Created:创建后。状态:实例创建完成,属性已绑定,数据对象data已存在,但DOM未形成, e l 未 存 在 。 作 用 : 初 始 化 完 成 事 件 , 异 步 请 求 。 b e f o r e M o u n t e d : 挂 载 前 。 状 态 : v u e 实 例 的 数 据 对 象 d a t a 和 el未存在。作用:初始化完成事件,异步请求。 beforeMounted:挂载前。状态:vue实例的数据对象data和 el未存在。作用:初始化完成事件,异步请求。beforeMounted:挂载前。状态:vue实例的数据对象data和el已经初始化,此时为虚拟DOM节点,data.message未替换。 Mounted:挂载后。状态:vue实例挂载完成,data.message已经替换。作用:发起后端请求,对挂载的DOM进行操作。 beforeUpdated:更新前。状态:数据驱动DOM。作用:访问现有DOM。 Updated:更新后。状态:完成虚拟DOM的渲染和打补丁。作用:对数据同一处理的函数。 beforeDestoryed:销毁前。状态:组件销毁之前调用。作用:销毁定时器,销毁插件对象,解绑全局事件。 Destoryed:销毁后。状态:组件销毁之后调用,vue实例已解除事件监听和DOM绑定,但是DOM结构依然存在。创建前 创建后 挂载前 挂载后
在created的钩子函数中,此时属性已绑定数据对象data已经存在,但是DOM节点未形成,能更快的获取服务器数据,减少页面的访问时间,用户体验更好。
是vue提供的一个内置组件,用来对组件进行缓存,当组件被切换掉时,将状态保存在内存中,防止重复渲染DOM。如果一个组件被包裹了keep-alive,那么他就不会被销毁也就不会触发beforeDestroy和destroyed,并且多了两个生命周期,deactivated和activated,当组件被换掉的时候会被缓存到内存中触发deactivated生命周期,当组件被切换回来的时候再去缓存中找这个组件,触发activated的钩子函数。
加载和渲染过程
父组件:beforeCreated 父组件:Created 父组件:beforeMounted 子组件:beforeCreated 子组件:Created 子组件:beforeMounted 子组件:Mounted 父组件:Mounted 更新过程 父组件:beforeUpdated 子组件:beforeUpdated 子组件:Updated 父组件:Updated 销毁过程 父组件:beforeDestoryed 子组件:beforeDestoryed 子组件:Destoryed 父组件:Destoryed虚拟DOM本质上是JS和DOM之间的一个缓存。就是用JS对象结构来表示DOM树,然后用这个树生成一个DOM树插入到文档中,当状态更新的时候重新构造一颗对象树,然后把新旧树进行比较,记录差异,然后把差异应用到真正的DOM树中更新视图。
为什么会出现虚拟DOM? 1、因为现在前端框架的主要任务就是不用手动操作DOM元素,所以虚拟DOM应运而生。 2、为了减少更改真实DOM元素的时候,页面的重新渲染的次数,如果是真实DOM的话,会进行整个页面的重绘,虚拟DOM的话,一次性更改所有的变化。 3、虚拟DOM的本质是JS对象,他能够很好的实现跨平台操作,比如服务端渲染工作。不是的,首次渲染大量DOM的时候,由于多了一层虚拟DOM的计算,会比innerHtml插入慢。还有就是针对性的优化DOM元素的时候,还是真实DOM比较快的。
diff算法是当状态更新的时候,创建新的JS对象,将新旧JS对象进行比较记录差异的算法。
首先对比节点本身,判断是否为同一节点,不同的话,就删除该节点,创建新节点进行替换。然后对比子节点,如果新的JS对象没有子节点的话就删除子节点,如果有的话,就对比新旧子节点然后进行子节点的更新。匹配时找到相同的子节点然后递归比较。使用key 能高效的增删改查节点。 在diff算法中只对同层的节点进行比较放弃跨层的节点比较,所以时间复杂度为 O(n)。1、v-if中的使用。vue的渲染规则就是尽可能多的渲染元素,采用就地复用的准则,当通过v-if来切换元素的时候,如果切换前后是同类型的元素,那么就会复用之前的内容,以input标签为例,以前文本框中的输入内容会被保存在浏览器中,为了避免复用内容,加一个 key 来作为识别唯一的元素。
2、v-for是用来遍历元素的,当多个元素的索引发生变化的时候,vue来渲染的时候并不会更改DOM元素的位置,会复用之前该位置的元素,所以加一个key之后相当于给元素加了一个索引,就不会复用之前该位置的元素了。但是key的值最好不能是index,因为index的索引号只能是 0 1 2 3…即使是加了key也会导致vue复用错误的旧子节点。 所以Key解决了diff算法中更准确,更高效的问题。1、监测机制的改变
vue3采用proxy来实现Observe进行了全局的实时追踪,解决了vue2的诸多限制问题 2、只能检测属性不能监测对象 可以监听数组下标和长度的变化 3、模板的改变 vue2中引入插槽会导致父组件重新渲染,然而vue3的插槽采用了函数的形式,只对子组件渲染,提高了渲染性能。 4、代码风格上 vue2使用options API,不利于组件之间数据的共享, vue3使用compositionAPI代码风格和JS很像vue在实例初始化的时候会遍历data中的所有属性,并使用Object.defineProperty把这些属性转化成getters/setters。这样当追踪到数据发生变化的时候,setters就会被调用。但是这样做存在一个问题,就是当添加或者是删除对象的属性的时候,vue检测不到,因为添加或者是删除的属性在刚开始的时候并没有做响应式处理,所以需要通过$set来调用Object.defineProperty,还有他不能检测到数组长度和下标的变化。vue3中的proxy是直接代理整个对象而不是对象属性,这样做只需要做一层代理就可以监听同级结构下的所有属性变化,包括新增和删除属性,另外他可以检测数组的变化。
转载地址:http://poqji.baihongyu.com/