我相信每个人学习Vue的目的是各部相同的。
可能你的公司正要将原有的项目使用Vue进行重构。
也可能是你的公司新项目决定使用Vue的技术栈。
当然,如果你现在正在换工作,你会发现招聘前端的需求中,10个有8个都对Vue有或多或少的要求。
当然,作为学习者我们知道Vuejs目前非常火,可以说是前端必备的一个技能。
Vue (读音 /vjuː/,类似于 view),不要读错。
Vue是一个渐进式的框架,什么是渐进式的呢?
Vue有很多特点和Web开发中常见的高级功能
这些特点,你不需要一个个去记住,我们在后面的学习和开发中都会慢慢体会到的,一些技术点我也会在后面进行讲解。
学习Vuejs的前提?
使用一个框架,我们第一步要做什么呢?安装下载它
安装Vue的方式有很多:
方式一:直接CDN引入
方式二:下载和引入
开发环境 https://vuejs.org/js/vue.js
生产环境 https://vuejs.org/js/vue.min.js
我们来做我们的第一个Vue程序,体验一下Vue的响应式
代码做了什么事情?
我们来阅读JavaScript代码,会发现创建了一个Vue对象。
创建Vue对象的时候,传入了一些options:{}
浏览器执行代码的流程:
并且,目前我们的代码是可以做到响应式的。
现在可以看到我了
点击我
什么是MVVM呢?
我们直接来看Vue的MVVM
你会发现,我们在创建Vue实例的时候,传入了一个对象options。
这个options中可以包含哪些选项呢?
目前掌握这些选项:
如何将data中的文本数据,插入到HTML中呢?
我们可以像下面这样来使用,并且数据是响应式的
但是,在某些情况下,我们可能不希望界面随意的跟随改变
v-once:
该指令后面不需要跟任何表达式(比如之前的v-for后面是由跟表达式的)
该指令表示元素和组件(组件后面才会学习)只渲染一次,不会随着数据的改变而改变。
代码如下:
{{message}}
{{message}}
//只展现一次message的数据,当后面的message发生改变的时候,这个message不会发生改变。
某些情况下,我们从服务器请求到的数据本身就是一个HTML代码
如果我们希望解析出HTML展示
可以使用v-html指令
该指令后面往往会跟上一个string类型
会将string的html解析出来并且进行渲染
v-text作用和Mustache比较相似:都是用于将数据显示在界面中
v-text通常情况下,接受一个string类型
{{message}}
//你好啊!!!
//你好啊!!!
v-pre用于跳过这个元素和它子元素的编译过程,用于显示原本的Mustache语法。
比如下面的代码:
在某些情况下,我们浏览器可能会直接显然出未编译的Mustache标签。
cloak: 斗篷
前面我们学习的指令主要作用是将值插入到我们模板的内容当中。
但是,除了内容需要动态来决定外,某些属性我们也希望动态来绑定。
这个时候,我们可以使用v-bind指令:
下面,我们就具体来学习v-bind的使用。
v-bind用于绑定一个或多个属性值,或者向另一个组件传递props值(这个学到组件时再介绍)
在开发中,有哪些属性需要动态进行绑定呢?
v-bind有一个对应的语法糖,也就是简写方式
比如通过Vue实例中的data绑定元素的src和href,代码如下:
很多时候,我们希望动态的来切换class,比如:
绑定class有两种方式:
绑定方式:对象语法
对象语法有下面这些用法::
用法一:直接通过{}绑定一个类
Hello World
用法二:也可以通过判断,传入多个值
Hello World
用法三:和普通的类同时存在,并不冲突
注:如果isActive和isLine都为true,那么会有title/active/line三个类
Hello World
用法四:如果过于复杂,可以放在一个methods或者computed中
注:classes是一个计算属性
Hello World
案例:
{{message}}
{{message}}
数组语法有下面这些用法:
用法一:直接通过{}绑定一个类
Hello World
用法二:也可以传入多个值
Hello World
用法三:和普通的类同时存在,并不冲突
注:会有title/active/line三个类
Hello World
用法四:如果过于复杂,可以放在一个methods或者computed中
注:classes是一个计算属性
Hello World
我们可以利用v-bind:style来绑定一些CSS内联样式。
在写CSS属性名的时候,比如font-size
绑定class有两种方式:
:style="{color: currentColor, fontSize: fontSize + 'px'}"
style后面跟的是一个对象类型
对象的key是CSS属性名称
对象的value是具体赋的值,值可以来自于data中的属性
案例:
{{message}}
{{message}}
{{message}}
{{message}}
{{fullName}}
style后面跟的是一个数组类型
多个值以,分割即可
我们知道,在模板中可以直接通过插值语法显示一些data中的数据。
但是在某些情况,我们可能需要对数据进行一些转化后再显示,或者需要将多个数据结合起来进行显示
我们可以将上面的代码换成计算属性:
OK,我们发现计算属性是写在实例的computed选项中的。
计算属性中也可以进行一些更加复杂的操作,比如下面的例子
总价格:{{totalPrice}}
{{counter}}
//定义各种各样的方法
methods: {
increment(count) {
console.log(count);
this.counter++;
},
decrement() {
this.counter--;
}
}
当通过methods中定义方法,以供@click调用时,需要注意参数问题:
情况一:如果该方法不需要额外参数,那么方法后的()可以不添加。
情况二:如果需要同时传入某个参数,同时需要event时,可以通过$event传入事件。
在某些情况下,我们拿到event的目的可能是进行一些事件处理。
Vue提供了修饰符来帮助我们方便的处理一些事件:
官方推荐我们在使用v-for时,给对应的元素或组件添加上一个:key属性。
为什么需要这个key属性呢(了解)?
当某一层有很多相同的节点时,也就是列表节点时,我们希望插入一个新的节点
所以我们需要使用key来给每个节点做一个唯一标识
所以一句话,key的作用主要是为了高效的更新虚拟DOM。
响应式的:
- {{item}}
表单控件在实际开发中是非常常见的。特别是对于用户信息的提交,需要大量的表单。
Vue中使用v-model指令来实现表单元素和数据的双向绑定。
案例的解析:
当然,我们也可以将v-model用于textarea元素
{{message}}
v-model其实是一个语法糖,它的背后本质上是包含两个操作:
也就是说下面的代码:等同于下面的代码:
等同于
当存在多个单选框时
复选框分为两种情况:单个勾选框和多个勾选框
单个勾选框:
多个复选框:
案例:
你的选择是:{{isAgree}}
篮球
足球
乒乓球
羽毛球
你的爱好是:{{hoddy}}
和checkbox一样,select也分单选和多选两种情况。
单选:只能选中一个值。
多选:可以选中多个值。
你喜欢的水果:{{mySelect}}
你喜欢的水果:{{mySelects}}
初看Vue官方值绑定的时候,我很疑惑:what the hell is that?
但是仔细阅读之后,发现很简单,就是动态的给value赋值而已:
这不就是v-bind在input中的应用吗?搞的我看了很久,搞不清他想将什么。
这里不再给出对应的代码,因为会用v-bind,就会值绑定的应用了。
lazy修饰符:
number修饰符:
trim修饰符:
当前内容:{{message}}
年龄:
年龄:{{age}} 类型{{typeof age}}
当前内容:----{{message}}------
人面对复杂问题的处理方式:
组件化也是类似的思想:
我们将一个完整的页面分成很多个组件。
每个组件都用于实现页面的一个功能块。
而每一个组件又可以进行细分。
组件化思想的应用:
所以,组件是Vue开发中,非常重要的一个篇章,要认真学习。
组件的使用分成三个步骤:
我们来看看通过代码如何注册组件
查看运行结果:
这里的步骤都代表什么含义呢?
1.Vue.extend():
2.Vue.component():
3.组件必须挂载在某个Vue实例下,否则它不会生效。(见下页)
当我们通过调用Vue.component()注册组件时,组件的注册是全局的
如果我们注册的组件是挂载在某个实例中, 那么就是一个局部组件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jpDFBYUF-1638328245757)(https://gitee.com/hzg-sss/typora-picture/raw/master/img/image-20210917103355282.png)]
在前面我们看到了组件树:
我们来看通过代码如何组成的这种层级关系:
父子组件错误用法:以子标签的形式在Vue实例中使用
在上面注册组件的方式,可能会有些繁琐。
语法糖注册全局组件和局部组件:
刚才,我们通过语法糖简化了Vue组件的注册过程,另外还有一个地方的写法比较麻烦,就是template模块写法。
如果我们能将其中的HTML分离出来写,然后挂载到对应的组件上,必然结构会变得非常清晰。
Vue提供了两种方案来定义HTML模块内容:
组件是一个单独功能模块的封装:
组件中的数据是保存在哪里呢?顶层的Vue实例中吗?
我们发现不能访问,而且即使可以访问,如果将所有的数据都放在Vue实例中,Vue实例就会变的非常臃肿。
结论:Vue组件应该有自己保存数据的地方。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-imbA9jQz-1638328245767)(https://gitee.com/hzg-sss/typora-picture/raw/master/img/image-20210917113741987.png)]
在上一个小节中,我们提到了子组件是不能引用父组件或者Vue实例的数据的。
但是,在开发中,往往一些数据确实需要从上层传递到下层:
如何进行父子组件间的通信呢?Vue官方提到
通过props向子组件传递数据
通过事件向父组件发送消息
在下面的代码中,我直接将Vue实例当做父组件,并且其中包含子组件来简化代码。
真实的开发中,Vue实例和子组件的通信和父组件和子组件的通信过程是一样的。
在组件中,使用选项props来声明需要从父级接收到的数据。
props的值有两种方式:
我们先来看一个最简单的props传递:
在前面,我们的props选项是使用一个数组。
我们说过,除了数组之外,我们也可以使用对象,当需要对props****进行类型等验证时,就需要对象写法了。
验证都支持哪些数据类型呢?
当我们有自定义构造函数时,验证也支持自定义的类型
3.子集向父级传递
props用于父组件向子组件传递数据,还有一种比较常见的是子组件传递数据或事件到父组件中。
我们应该如何处理呢?这个时候,我们需要使用自定义事件来完成。
什么时候需要自定义事件呢?
自定义事件的流程:
我们来看一个简单的例子:
有时候我们需要父组件直接访问子组件,子组件直接访问父组件,或者是子组件访问跟组件。
我们先来看下$children的访问
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UxfDNeJW-1638328245776)(https://gitee.com/hzg-sss/typora-picture/raw/master/img/image-20210917145920562.png)]
$children的缺陷:
$refs的使用:
如果我们想在子组件中直接访问父组件,可以通过$parent
注意事项:
刚才我们讨论的都是父子组件间的通信,那如果是非父子关系呢?
在Vue1.x的时候,可以通过 d i s p a t c h 和 dispatch和 dispatch和broadcast完成
在Vue2.x中,有一种方案是通过中央事件总线,也就是一个中介来完成。
在真正学习插槽之前,我们需要先理解一个概念:编译作用域。
官方对于编译的作用域解析比较简单,我们自己来通过一个例子来理解这个概念:
我们来考虑下面的代码是否最终是可以渲染出来的:
答案:最终可以渲染出来,也就是使用的是Vue实例的属性。
为什么呢?
slot翻译为插槽:
组件的插槽:
例子:移动网站中的导航栏。
但是,每个页面的导航是一样的吗?No,我以京东M站为例
如何去封装这类的组件呢?
如何封装合适呢?抽取共性,保留不同。
这就是为什么我们要学习组件中的插槽slot的原因。
了解了为什么用slot,我们再来谈谈如何使用slot?
我们通过一个简单的例子,来给子组件定义一个插槽:
当子组件的功能复杂时,子组件的插槽可能并非是一个。
如何使用具名插槽呢?
我们来给出一个案例:
哈哈哈哈
左边
中间的
右边
作用域插槽是slot一个比较难理解的点,而且官方文档说的又有点不清晰。
这里,我们用一句话对其做一个总结,然后我们在后续的案例中来体会:
我们先提一个需求:
我们来看看子组件的定义:
在父组件使用我们的子组件时,从子组件中拿到数据:
我们通过获取到slotone属性
在通过slotone.data就可以获取到刚才我们传入的data了
{{item}}-
- {{item}}
如果你只是简单写几个Vue的Demo程序, 那么你不需要Vue CLI.
如果你在开发大型项目, 那么你需要, 并且必然需要使用Vue CLI
CLI是什么意思?
安装NodeJS
检测安装的版本
什么是NPM呢**?**
注:cnpm安装
由于国内直接使用 npm 的官方镜像是非常慢的,这里推荐使用淘宝 NPM 镜像。
你可以使用淘宝定制的 cnpm (gzip 压缩支持) 命令行工具代替默认的 npm:npm install -g cnpm --registry=https://registry.npm.taobao.org
这样就可以使用 cnpm 命令来安装模块了:cnpm install [name]
注:具体的安装可以分为命令行安装和图像化界面安装,看哪个适合,这一部分初学者最好是自己先摸索如何安装和使用,如果自己没有配置或者安装好,找已经走过本阶段的学长学姐或则朋友指导如何安装。
流程:
说起路由你想起了什么?
额, 啥玩意? 没听懂
路由中有一个非常重要的概念叫路由表.
早期的网站开发整个HTML页面是由服务器来渲染的.
但是, 一个网站, 这么多页面服务器如何处理呢?
上面的这种操作, 就是后端路由.
后端路由的缺点:
前后端分离阶段:
单页面富应用阶段:
前端路由的核心是什么呢?
history接口是HTML5新增的, 它有五种模式改变URL而不刷新页面.
history.pushState()
目前前端流行的三大框架, 都有自己的路由实现:
当然, 我们的重点是vue-router
vue-router是基于路由和组件的
后续开发中我们主要是通过工程化的方式进行开发的.
所以在后续, 我们直接使用npm来安装路由即可.
步骤一: 安装vue-router
步骤二: 在模块化工程中使用它(因为是一个插件, 所以可以通过Vue.use()来安装路由功能)
第一步:导入路由对象,并且调用 Vue.use(VueRouter)
第二步:创建路由实例,并且传入路由映射配置
第三步:在Vue实例中挂载创建的路由实例
import Vue from ‘vue’
import VueRouter from 'vue-route
Vue.use(VueRouter)
使用vue-router的步骤:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8mSbM93D-1638328245789)(https://gitee.com/hzg-sss/typora-picture/raw/master/img/image-20210917215258160.png)]
: 该标签是一个vue-router中已经内置的组件, 它会被渲染成一个标签.(跳转路由)
: 该标签会根据当前的路径, 动态渲染出不同的组件.
网页的其他内容, 比如顶部的标题/导航, 或者底部的一些版权信息等会和处于同一个等级.
在路由切换时, 切换的是挂载的组件, 其他内容不会发生改变.
我们这里还有一个不太好的实现:
如何可以让路径默认跳到到首页, 并且渲染首页组件呢?
我们前面说过改变路径的方式有两种:
如果希望使用HTML5的history模式, 非常简单, 进行如下配置即可:
效果如下图:
在前面的中, 我们只是使用了一个属性: to, 用于指定跳转的路径.
还有一些其他属性:
该class具体的名称也可以通过router实例的属性进行修改
nexact-active-class
类似于active-class, 只是在精准匹配下才会出现的class.
后面看到嵌套路由时, 我们再看下这个属性.
有时候, 页面的跳转可能需要执行对应的JavaScript代码, 这个时候, 就可以使用第二种跳转方式了
比如, 我们将代码修改如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F7ZDJpfR-1638328245807)(https://gitee.com/hzg-sss/typora-picture/raw/master/img/image-20210917222234793.png)]
官方给出了解释:
官方在说什么呢?
路由懒加载做了什么?
const Home = resolve => { require.ensure(['../components/Home.vue'], () => { resolve(require('../components/Home.vue')) })};=
const About = resolve => require(['../components/About.vue'], resolve);
const Home = () => import('../components/Home.vue')
嵌套路由是一个很常见的功能
路径和组件的关系如下:
1.定义两个组件
2.router中路由嵌套
3.实际页面中跳转路由
4.效果图;
params的类型:
query的类型:
如何使用它们呢? 也有两种方式: 的方式和JavaScript代码方式
获取参数通过$route对象获取的.
通过$route获取传递的信息如下:
我们来考虑一个需求: 在一个SPA应用中, 如何改变网页的标题呢?
普通的修改方式:
有没有更好的办法呢? 使用导航守卫即可.
什么是导航守卫?(重点)
我们可以利用beforeEach来完成标题的修改.
导航钩子的三个参数解析:
补充一:如果是后置钩子, 也就是afterEach, 不需要主动调用next()函数.
补充二: 上面我们使用的导航守卫, 被称之为全局守卫.
更多内容, 可以查看官网进行学习:
keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。
router-view 也是一个组件,如果直接被包在 keep-alive 里面,所有路径匹配到的视图组件都会被缓存:
官方解释:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。
状态管理到底是什么?
等等,如果是这样的话,为什么官方还要专门出一个插件Vuex呢?难道我们不能自己封装一个对象来管理吗?
但是,有什么状态时需要我们在多个组件间共享的呢?
OK,从理论上理解了状态管理之后,让我们从实际的代码再来看看状态管理。
我们先来看看单界面的状态管理吧.
我们知道,要在单个组件中进行状态管理是一件非常简单的事情
这图片中的三种东西,怎么理解呢?
在这个案例中,我们有木有状态需要管理呢?没错,就是个数counter。
counter需要某种方式被记录下来,也就是我们的State。
counter目前的值需要被显示在界面中,也就是我们的View部分。
界面发生某些操作时(我们这里是用户的点击,也可以是用户的input),需要去更新状态,也就是我们的Actions
这不就是上面1.3的流程图了吗?
Vue已经帮我们做好了单个界面的状态管理,但是如果是多个界面呢?
也就是说对于某些状态(状态1/状态2/状态3)来说只属于我们某一个试图,但是也有一些状态(状态a/状态b/状态c)属于多个试图共同想要维护的
全局单例模式(大管家)
我们还是简单的实现一个计数器的案例
首先,我们需要在某个地方存放我们的Vuex代码:
好的,这就是使用Vuex最简单的方式了。
我们来对使用步骤,做一个简单的小节:
注意事项:
Vuex有几个比较核心的概念:
我们对它进行一一介绍.
Vuex提出使用单一状态树, 什么是单一状态树呢?
但是,它是什么呢?我们来看一个生活中的例子。
这个和我们在应用开发中比较类似:
Vuex的store状态的更新唯一方式:提交Mutation
Mutation主要包括两部分:
mutation的定义方式:
mutations:{
increment(state){
state.count++
}
}
increment: function(){
this.$store.commit('increment')
}
在通过mutation更新数据的时候, 有可能我们希望携带一些额外的参数
Mutation中的代码:
上面的通过commit进行提交是一种普通的方式
Vue还提供了另外一种风格, 它是一个包含type属性的对象
this.$store.commit({
type:'changeCount',
count:100
})
changeCount(State,payload){
state.count = payload.count
}
Vuex的store中的state是响应式的, 当state中的数据发生改变时, Vue组件会自动更新.
这就要求我们必须遵守一些Vuex对应的规则:
我们来看一个例子:
如何才能让它改变呢?
我们来考虑下面的问题:
如何避免上述的问题呢?
具体怎么做呢?
通常情况下, Vuex要求我们Mutation中的方法必须是同步方法.
比如我们之前的代码, 当执行更新时, devtools中会有如下信息: 图1
但是, 如果Vuex中的代码, 我们使用了异步函数: 图2
你会发现state中的info数据一直没有被改变, 因为他无法追踪到.
So, 通常情况下,不要再mutation中进行异步的操作
我们强调, 不要再Mutation中进行异步操作.
Action的基本使用代码如下:
context是什么?
这样的代码是否多此一举呢?
前面我们学习ES6语法的时候说过, Promise经常用于异步操作.
OK, 我们来看下面的代码:
Module是模块的意思, 为什么在Vuex中我们要使用模块呢?
我们按照什么样的方式来组织模块呢?
上面的代码中, 我们已经有了整体的组织结构, 下面我们来看看具体的局部模块中的代码如何书写.
注意:
actions的写法呢? 接收一个context参数对象
Vue中发送网络请求有非常多的方式, 那么, 在开发中, 如何选择呢?
为什么不用它呢?
选择二: 在前面的学习中, 我们经常会使用jQuery-Ajax
为什么不选择它呢?
选择三: 官方在Vue1.x的时候, 推出了Vue-resource.
为什么不选择它呢?
选择四: 在说明不再继续更新和维护vue-resource的同时, 作者还推荐了一个框架: axios为什么不用它呢?
n为什么选择axios? 作者推荐和功能特点、
n功能特点
请求地址
请求类型
请根路径
请求前的数据处理
请求后的数据处理
自定义的请求头
URL查询对象
查询对象序列化函数
request body
超时设置s
跨域是否带Token
自定义请求处理
身份验证信息
响应的数据格式 json / blob /document /arraybuffer / text / stream
axios封装
axios提供了拦截器,用于我们在发送每次请求或者得到相应后,进行对应的处理。
如何使用拦截器呢?
请求拦截中错误拦截较少,通常都是配置相关的拦截
可能的错误比如请求超时,可以将页面跳转到一个错误页面中。
4.3.拦截器中都做了什么?
let new2Nums = newNums.map(function(n) {
return n * 2
})
console.log(new2Nums);
//preValue:前面值得累加,有点像sum
//n:数组中的值,
let total = new2Nums.reduce(function(preValue, n) {
return preValue + n;
}, 0)
console.log(total);
const aaa = function(){
}
const obj = {
bbb:function(){
},
bbb(){
}
}
const ccc = (参数列表)=>{
}
在非箭头函数下, this 指向调用其所在函数的对象,而且是离谁近就是指向谁(此对于常规对象,原型链, getter & setter等都适用);构造函数下,this与被创建的新对象绑定;DOM事件,this指向触发事件的元素;内联事件分两种情况,bind绑定, call & apply 方法等, 容以下一步一步讨论。箭头函数也会穿插其中进行讨论。
在全局环境下,this 始终指向全局对象(window), 无论是否严格模式;
console.log(this.document === document); // true
// 在浏览器中,全局对象为 window 对象:
console.log(this === window); // true
this.a = 37;
console.log(window.a); // 37
普通函数内部的this分两种情况,严格模式和非严格模式。
非严格模式下,this 默认指向全局对象window
function f1(){
return this;
}
f1() === window; // true
而严格模式下, this为undefined
function f2(){
"use strict"; // 这里是严格模式
return this;
}
f2() === undefined; // true
对象内部方法的this指向调用这些方法的对象,
箭头函数中的this引用的是最近作用域中的this
待总结…
console.log(newNums);
##### 1.1.2map函数的使用:
- 让数组中的值都有一次变化的话用**map**(例如:数组中的每一个值都乘以2)
```vue
let new2Nums = newNums.map(function(n) {
return n * 2
})
console.log(new2Nums);
//preValue:前面值得累加,有点像sum
//n:数组中的值,
let total = new2Nums.reduce(function(preValue, n) {
return preValue + n;
}, 0)
console.log(total);
const aaa = function(){
}
const obj = {
bbb:function(){
},
bbb(){
}
}
const ccc = (参数列表)=>{
}
在非箭头函数下, this 指向调用其所在函数的对象,而且是离谁近就是指向谁(此对于常规对象,原型链, getter & setter等都适用);构造函数下,this与被创建的新对象绑定;DOM事件,this指向触发事件的元素;内联事件分两种情况,bind绑定, call & apply 方法等, 容以下一步一步讨论。箭头函数也会穿插其中进行讨论。
在全局环境下,this 始终指向全局对象(window), 无论是否严格模式;
console.log(this.document === document); // true
// 在浏览器中,全局对象为 window 对象:
console.log(this === window); // true
this.a = 37;
console.log(window.a); // 37
普通函数内部的this分两种情况,严格模式和非严格模式。
非严格模式下,this 默认指向全局对象window
function f1(){
return this;
}
f1() === window; // true
而严格模式下, this为undefined
function f2(){
"use strict"; // 这里是严格模式
return this;
}
f2() === undefined; // true
对象内部方法的this指向调用这些方法的对象,
箭头函数中的this引用的是最近作用域中的this
待总结…