vue_basic
vue官网:https://cn.vuejs.org/
服务端渲染
sequenceDiagram
浏览器->>+服务器: http://xxx.com/news
Note right of 服务器: 组装页面(服务端渲染)
服务器->>-浏览器: 完整页面
前后端分离
sequenceDiagram
浏览器->>服务器: http://xxx.com/news
服务器->>浏览器: 无内容的html
activate 浏览器
浏览器-->>服务器: ajax
服务器-->>浏览器: 各种业务数据
Note left of 浏览器: 运行js,创建元素,渲染页面
deactivate 浏览器
单页应用
sequenceDiagram
浏览器->>服务器: http://xxx.com/news
服务器->>浏览器: 无内容的html
activate 浏览器
Note left of 浏览器: 运行js,创建元素,渲染页面
浏览器-->>服务器: ajax
服务器-->>浏览器: 各种业务数据
Note left of 浏览器: 跳转页面
浏览器-->>服务器: ajax
服务器-->>浏览器: 各种业务数据
Note left of 浏览器: JS重新构建页面元素
deactivate 浏览器
vue框架
sequenceDiagram
浏览器->>服务器: http://xxx.com/news
服务器->>浏览器: 无内容的html
activate 浏览器
Note left of 浏览器: 运行包含vue的js,使用框架渲染页面
浏览器-->>服务器: ajax
服务器-->>浏览器: 各种业务数据
Note left of 浏览器: 使用vue-router跳转页面
deactivate 浏览器
注入
vue会将以下配置注入到vue实例:
- data:和界面相关的数据
- computed:通过已有数据计算得来的数据,将来详细讲解
- methods:方法
模板中可以使用vue实例中的成员
虚拟DOM树
直接操作真实的DOM会引发严重的效率问题,vue使用虚拟DOM(vnode)的方式来描述要渲染的内容
vnode是一个普通的JS对象,用于描述界面上应该有什么,比如:
1 |
|
上面的对象描述了:
1 |
|
vue模板并不是真实的DOM,它会被编译为虚拟DOM
1 |
|
上面的模板会被编译为类似下面结构的虚拟DOM
1 |
|
虚拟DOM树会最终生成为真实的DOM树
当数据变化后,将引发重新渲染,vue会比较新旧两棵vnode tree,找出差异,然后仅把差异部分应用到真实dom tree中
可见,在vue中,要得到最终的界面,必须要生成一个vnode tree
vue通过以下逻辑生成vnode tree:
注意:虚拟节点树必须是单根的
挂载
将生成的真实DOM树,放置到某个元素位置,称之为挂载
挂载的方式:
- 通过
el:"css选择器"
进行配置 - 通过
vue实例.$mount("css选择器")
进行配置
完整流程
组件的出现是为了实现以下两个目标:
- 降低整体复杂度,提升代码的可读性和可维护性
- 提升局部代码的可复用性
绝大部分情况下,一个组件就是页面中某个区域,组件包含该区域的:
功能(JS代码)
内容(模板代码)
样式(CSS代码)
要在组件中包含样式,需要构建工具的支撑
组件开发
创建组件
组件是根据一个普通的配置对象创建的,所以要开发一个组件,只需要写一个配置对象即可
该配置对象和vue实例的配置是几乎一样的
1 |
|
值得注意的是,组件配置对象和vue实例有以下几点差异:
- 无
el
data
必须是一个函数,该函数返回的对象作为数据- 由于没有
el
配置,组件的虚拟DOM树必须定义在template
或render
中
注册组件
注册组件分为两种方式,一种是全局注册,一种是局部注册
全局注册
一旦全局注册了一个组件,整个应用中任何地方都可以使用该组件
全局注册的方式是:
1 |
|
在模板中,可以使用组件了
1 |
|
但在一些工程化的大型项目中,很多组件都不需要全局使用。
比如一个登录组件,只有在登录的相关页面中使用,如果全局注册,将导致构建工具无法优化打包
因此,除非组件特别通用,否则不建议使用全局注册
局部注册
局部注册就是哪里要用到组件,就在哪里注册
局部注册的方式是,在要使用组件的组件或实例中加入一个配置:
1 |
|
应用组件
在模板中使用组件特别简单,把组件名当作HTML元素名使用即可。
但要注意以下几点:
- 组件必须有结束
组件可以自结束,也可以用结束标记结束,但必须要有结束
下面的组件使用是错误的:
1 |
|
- 组件的命名
无论你使用哪种方式注册组件,组件的命名需要遵循规范。
组件可以使用kebab-case 短横线命名法
,也可以使用PascalCase 大驼峰命名法
下面两种命名均是可以的
1 |
|
实际上,使用
小驼峰命名法 camelCase
也是可以识别的,只不过不符合官方要求的规范
使用PascalCase
方式命名还有一个额外的好处,即可以在模板中使用两种组件名
1 |
|
模板中:
1 |
|
因此,在使用组件时,为了方便,往往使用以下代码:
1 |
|
组件树
一个组件创建好后,往往会在各种地方使用它。它可能多次出现在vue实例中,也可能出现在其他组件中。
于是就形成了一个组件树
向组件传递数据
大部分组件要完成自身的功能,都需要一些额外的信息
比如一个头像组件,需要告诉它头像的地址,这就需要在使用组件时向组件传递数据
传递数据的方式有很多种,最常见的一种是使用组件属性 component props
首先在组件中申明可以接收哪些属性:
1 |
|
在使用组件时,向其传递属性:
1 |
|
注意:在组件中,属性是只读的,绝不可以更改,这叫做单向数据流
工程结构
见代码
vue-cli: https://cli.vuejs.org/zh/
vue-cli
vue-cli
是一个脚手架工具,用于搭建vue
工程
它内部使用了webpack
,并预置了诸多插件(plugin
)和加载器(loader
),以达到开箱即用的效果
除了基本的插件和加载器外,vue-cli
还预置了:
- babel
- webpack-dev-server
- eslint
- postcss
- less-loader
SFC
单文件组件,Single File Component,即一个文件就包含了一个组件所需的全部代码
1 |
|
预编译
当vue-cli
进行打包时,会直接把组件中的模板转换为render
函数,这叫做模板预编译
这样做的好处在于:
- 运行时就不再需要编译模板了,提高了运行效率
- 打包结果中不再需要vue的编译代码,减少了打包体积
遇到的问题:
使用npm指令显示打开方式然后执行了 C:\WINDOWS\system32\npm这个可执行文件,方法:删除她
搭建vue :vue create [name]
npm run serve开发使用
面试题:计算属性和方法有什么区别?
1 |
|
完整的计算属性书写:
1 |
|
只包含getter的计算属性简写:
1 |
|
在某些组件的模板中,有一部分区域需要父组件来指定
1 |
|
插槽的简单用法
此时,就需要使用插槽来定制组件的功能
1 |
|
具名插槽
如果某个组件中需要父元素传递多个区域的内容,也就意味着需要提供多个插槽
为了避免冲突,就需要给不同的插槽赋予不同的名字
1 |
|
vue-router 官网:https://router.vuejs.org/zh/
- 如何根据地址中的路径选择不同的组件?
- 把选择的组件放到哪个位置?
- 如何无刷新的切换组件?
路由插件
1 |
|
路由插件的使用
1 |
|
基本使用
1 |
|
1 |
|
路由模式
路由模式决定了:
- 路由从哪里获取访问路径
- 路由如何改变访问路径
vue-router
提供了三种路由模式:
hash:默认值。路由从浏览器地址栏中的 hash 部分获取路径,改变路径也是改变的 hash 部分。该模式兼容性最好。
1
2http://localhost:8081/#/blog --> /blog
http://localhost:8081/about#/blog --> /bloghistory:路由从浏览器地址栏的
location.pathname
中获取路径,改变路径使用的 H5 的history api
。该模式可以让地址栏最友好,但是需要浏览器支持history api
1
2
3http://localhost:8081/#/blog --> /
http://localhost:8081/about#/blog --> /about
http://localhost:8081/blog --> /blogabstract:路由从内存中获取路径,改变路径也只是改动内存中的值。这种模式通常应用到非浏览器环境中。
1
2
3内存: / --> /
内存: /about --> /about
内存: /blog --> /blog
导航
vue-router
提供了全局的组件RouterLink
,它的渲染结果是一个a
元素
1 |
|
激活状态
默认情况下,vue-router
会用 当前路径 匹配 导航路径 :
- 如果当前路径是以导航路径开头,则算作匹配,会为导航的 a 元素添加类名
router-link-active
- 如果当前路径完全等于导航路径,则算作精确匹配,会为导航的 a 元素添加类名
router-link-exact-active
例如,当前访问的路径是/blog
,则:
导航路径 | 类名 |
---|---|
/ | router-link-active |
/blog | router-link-active router-link-exact-active |
/about | 无 |
/message | 无 |
可以为组件RouterLink
添加 bool 属性exact
,将匹配规则改为:必须要精确匹配才能添加匹配类名router-link-active
例如,当前访问的路径是/blog
,则:
导航路径 | exact | 类名 |
---|---|---|
/ | true | 无 |
/blog | false | router-link-active router-link-exact-active |
/about | true | 无 |
/message | true | 无 |
例如,当前访问的路径是/blog/detail/123
,则:
导航路径 | exact | 类名 |
---|---|---|
/ | true | 无 |
/blog | false | router-link-active |
/about | true | 无 |
/message | true | 无 |
另外,可以通过active-class
属性更改匹配的类名,通过exact-active-class
更改精确匹配的类名
命名路由
使用命名路由可以解除系统与路径之间的耦合
1 |
|
1 |
|
使用css module
需要将样式文件命名为xxx.module.ooo
xxx
为文件名
ooo
为样式文件后缀名,可以是css
、less
得到组件渲染的Dom
1 |
|
扩展vue实例
ref
1 |
|
通过
ref
可以直接操作dom
元素,甚至可能直接改动子组件,这些都不符合vue
的设计理念。除非迫不得已,否则不要使用
ref
本节课内容和vue没有任何关系!
vue cli: https://cli.vuejs.org/zh/
axios: https://github.com/axios/axios
mockjs:http://mockjs.com/
远程获取数据的意义
开发环境有跨域问题
vuecli中webpack搭建的服务器,由浏览器请求后端服务器,端口号或者请求域名不一样
虽然可以得到数据,但是浏览器为了安全进行劫持
sequenceDiagram
浏览器->>前端开发服务器: http://localhost:8080/
前端开发服务器->>浏览器: 页面
浏览器->>后端测试服务器: ajax 跨域:http://test-data:3000/api/news
后端测试服务器->>浏览器: JSON数据
rect rgb(224,74,74)
Note right of 浏览器: 浏览器阻止数据移交
end
生产环境一般 没有跨域问题
sequenceDiagram
浏览器->>服务器: http://www.my-site.com/
服务器->>浏览器: 页面
浏览器->>服务器: ajax:http://www.my-site.com/api/news
服务器->>浏览器: JSON数据
sequenceDiagram
浏览器->>静态资源服务器: http://www.my-site.com/
静态资源服务器->>浏览器: 页面
浏览器->>数据服务器: ajax 跨域:http://api.my-site.com/api/news
数据服务器->>浏览器: [后端允许www.my-site.com]JSON数据
解决开发环境的跨域问题
脱离浏览器环境就解决了跨域问题,通过代理(在vue-cli config.js中查找 )
sequenceDiagram
浏览器->>前端开发服务器: http://localhost:8080/
前端开发服务器->>浏览器: 页面
浏览器->>前端开发服务器: ajax:http://localhost:8080/api/news
前端开发服务器->>后端测试服务器: 代理请求:http://test-data:3000/api/news
后端测试服务器->>前端开发服务器: JSON数据
前端开发服务器->>浏览器: JSON数据
为什么要Mock数据
后端服务器未搭建完,需要模拟数据,在浏览器内部拦截
sequenceDiagram
浏览器->>前端开发服务器: http://localhost:8080/
前端开发服务器->>浏览器: 页面
浏览器->>前端开发服务器: ajax:http://localhost:8080/api/news
前端开发服务器->>后端测试服务器: 代理请求:http://test-data:3000/api/news
后端测试服务器->>前端开发服务器: 404 (后端正在开发中)
前端开发服务器->>浏览器: 404
sequenceDiagram
participant 浏览器
participant 浏览器内的MockJS
participant 前端开发服务器
activate 浏览器内的MockJS
Note left of 浏览器内的MockJS: 定义ajax拦截规则
deactivate 浏览器内的MockJS
浏览器->>前端开发服务器: http://localhost:8080/
前端开发服务器->>浏览器: 页面
浏览器->>浏览器内的MockJS: ajax:http://localhost:8080/api/news
浏览器内的MockJS->>浏览器: 模拟的JSON数据
组件生命周期
常见应用
不要死记硬背,要根据具体情况灵活处理
加载远程数据
1 |
|
直接操作DOM
1 |
|
启动和清除计时器
1 |
|
Home
组件负责呈现整体效果
CarouselItem
组件负责呈现单张轮播图
Home
组件负责:
- 整体布局
- 监听鼠标滚轮事件,切换轮播图
- 提供上下按钮,切换轮播图
- 提供指示器,切换轮播图
CarouselItem
组件负责:
- 单张轮播图的全部事务
定义指令
全局定义
1 |
|
之后,所有的组件均可以使用mydirec1
和mydirec2
指令
1 |
|
局部定义
局部定义是指在某个组件中定义指令,和局部注册组件类似。
定义的指令仅在该组件中有效。
1 |
|
和局部注册组件一样,为了让指令更加通用,通常我们会把指令的配置提取到其他模块。
1 |
|
指令配置对象
没有配置的指令,就像没有配置的组件一样,毫无意义
vue
支持在指令中配置一些钩子函数,在适当的时机,vue
会调用这些钩子函数并传入适当的参数,以便开发者完成自己想做的事情。
常用的钩子函数:
1 |
|
每个钩子函数在调用时,vue
都会向其传递一些参数,其中最重要的是前两个参数
1 |
|
bingding 对象
配置简化
比较多的时候,在配置自定义指令时,我们都会配置两个钩子函数
1 |
|
这样,在元素绑定和更新时,都能运行到钩子函数
如果这两个钩子函数实现的功能相同,可以直接把指令配置简化为一个单独的函数:
1 |
|
利用上述知识,可满足大部分自定义指令的需求
更多的自定义指令用法见官网
有的时候,许多组件有着类似的功能,这些功能代码分散在组件不同的配置中。
于是,我们可以把这些配置代码抽离出来,利用混入融合到组件中。
具体的做法非常简单:
1 |
|
混入并不复杂,更多细节参见官网
在某些组件的模板中,有一部分区域需要父组件来指定
1 |
|
插槽的简单用法
此时,就需要使用插槽来定制组件的功能
1 |
|
具名插槽
如果某个组件中需要父元素传递多个区域的内容,也就意味着需要提供多个插槽
为了避免冲突,就需要给不同的插槽赋予不同的名字
1 |
|
vue-router 官网:https://router.vuejs.org/zh/
- 如何根据地址中的路径选择不同的组件?
- 把选择的组件放到哪个位置?
- 如何无刷新的切换组件?
路由插件
1 |
|
路由插件的使用
1 |
|
基本使用
1 |
|
1 |
|
路由模式
路由模式决定了:
- 路由从哪里获取访问路径
- 路由如何改变访问路径
vue-router
提供了三种路由模式:
hash:默认值。路由从浏览器地址栏中的 hash 部分获取路径,改变路径也是改变的 hash 部分。该模式兼容性最好。
1
2http://localhost:8081/#/blog --> /blog
http://localhost:8081/about#/blog --> /bloghistory:路由从浏览器地址栏的
location.pathname
中获取路径,改变路径使用的 H5 的history api
。该模式可以让地址栏最友好,但是需要浏览器支持history api
1
2
3http://localhost:8081/#/blog --> /
http://localhost:8081/about#/blog --> /about
http://localhost:8081/blog --> /blogabstract:路由从内存中获取路径,改变路径也只是改动内存中的值。这种模式通常应用到非浏览器环境中。
1
2
3内存: / --> /
内存: /about --> /about
内存: /blog --> /blog
导航
vue-router
提供了全局的组件RouterLink
,它的渲染结果是一个a
元素
1 |
|
激活状态
默认情况下,vue-router
会用 当前路径 匹配 导航路径 :
- 如果当前路径是以导航路径开头,则算作匹配,会为导航的 a 元素添加类名
router-link-active
- 如果当前路径完全等于导航路径,则算作精确匹配,会为导航的 a 元素添加类名
router-link-exact-active
例如,当前访问的路径是/blog
,则:
导航路径 | 类名 |
---|---|
/ | router-link-active |
/blog | router-link-active router-link-exact-active |
/about | 无 |
/message | 无 |
可以为组件RouterLink
添加 bool 属性exact
,将匹配规则改为:必须要精确匹配才能添加匹配类名router-link-active
例如,当前访问的路径是/blog
,则:
导航路径 | exact | 类名 |
---|---|---|
/ | true | 无 |
/blog | false | router-link-active router-link-exact-active |
/about | true | 无 |
/message | true | 无 |
例如,当前访问的路径是/blog/detail/123
,则:
导航路径 | exact | 类名 |
---|---|---|
/ | true | 无 |
/blog | false | router-link-active |
/about | true | 无 |
/message | true | 无 |
另外,可以通过active-class
属性更改匹配的类名,通过exact-active-class
更改精确匹配的类名
命名路由
使用命名路由可以解除系统与路径之间的耦合
1 |
|
1 |
|
使用css module
需要将样式文件命名为xxx.module.ooo
xxx
为文件名
ooo
为样式文件后缀名,可以是css
、less
得到组件渲染的Dom
1 |
|
扩展vue实例
ref
1 |
|
通过
ref
可以直接操作dom
元素,甚至可能直接改动子组件,这些都不符合vue
的设计理念。除非迫不得已,否则不要使用
ref
本节课内容和vue没有任何关系!
vue cli: https://cli.vuejs.org/zh/
axios: https://github.com/axios/axios
mockjs:http://mockjs.com/
远程获取数据的意义
开发环境有跨域问题
sequenceDiagram
浏览器->>前端开发服务器: http://localhost:8080/
前端开发服务器->>浏览器: 页面
浏览器->>后端测试服务器: ajax 跨域:http://test-data:3000/api/news
后端测试服务器->>浏览器: JSON数据
rect rgb(224,74,74)
Note right of 浏览器: 浏览器阻止数据移交
end
生产环境没有跨域问题
sequenceDiagram
浏览器->>服务器: http://www.my-site.com/
服务器->>浏览器: 页面
浏览器->>服务器: ajax:http://www.my-site.com/api/news
服务器->>浏览器: JSON数据
sequenceDiagram
浏览器->>静态资源服务器: http://www.my-site.com/
静态资源服务器->>浏览器: 页面
浏览器->>数据服务器: ajax 跨域:http://api.my-site.com/api/news
数据服务器->>浏览器: [允许www.my-site.com]JSON数据
解决开发环境的跨域问题
sequenceDiagram
浏览器->>前端开发服务器: http://localhost:8080/
前端开发服务器->>浏览器: 页面
浏览器->>前端开发服务器: ajax:http://localhost:8080/api/news
前端开发服务器->>后端测试服务器: 代理请求:http://test-data:3000/api/news
后端测试服务器->>前端开发服务器: JSON数据
前端开发服务器->>浏览器: JSON数据
为什么要Mock数据
sequenceDiagram
浏览器->>前端开发服务器: http://localhost:8080/
前端开发服务器->>浏览器: 页面
浏览器->>前端开发服务器: ajax:http://localhost:8080/api/news
前端开发服务器->>后端测试服务器: 代理请求:http://test-data:3000/api/news
后端测试服务器->>前端开发服务器: 404 (后端正在开发中)
前端开发服务器->>浏览器: 404
sequenceDiagram
participant 浏览器
participant MockJS
participant 前端开发服务器
activate MockJS
Note left of MockJS: 定义ajax拦截规则
deactivate MockJS
浏览器->>前端开发服务器: http://localhost:8080/
前端开发服务器->>浏览器: 页面
浏览器->>MockJS: ajax:http://localhost:8080/api/news
MockJS->>浏览器: 模拟的JSON数据
组件生命周期
常见应用
不要死记硬背,要根据具体情况灵活处理
加载远程数据
1 |
|
直接操作DOM
1 |
|
启动和清除计时器
1 |
|
定义指令
全局定义
1 |
|
之后,所有的组件均可以使用mydirec1
和mydirec2
指令
1 |
|
局部定义
局部定义是指在某个组件中定义指令,和局部注册组件类似。
定义的指令仅在该组件中有效。
1 |
|
和局部注册组件一样,为了让指令更加通用,通常我们会把指令的配置提取到其他模块。
1 |
|
指令配置对象
没有配置的指令,就像没有配置的组件一样,毫无意义
vue
支持在指令中配置一些钩子函数,在适当的时机,vue
会调用这些钩子函数并传入适当的参数,以便开发者完成自己想做的事情。
常用的钩子函数:
1 |
|
每个钩子函数在调用时,vue
都会向其传递一些参数,其中最重要的是前两个参数
1 |
|
bingding 对象
配置简化
比较多的时候,在配置自定义指令时,我们都会配置两个钩子函数
1 |
|
这样,在元素绑定和更新时,都能运行到钩子函数
如果这两个钩子函数实现的功能相同,可以直接把指令配置简化为一个单独的函数:
1 |
|
利用上述知识,可满足大部分自定义指令的需求
更多的自定义指令用法见官网
有的时候,许多组件有着类似的功能,这些功能代码分散在组件不同的配置中。
于是,我们可以把这些配置代码抽离出来,利用混入融合到组件中。
具体的做法非常简单:
1 |
|
混入并不复杂,更多细节参见官网
文章列表页逻辑
路由跳转逻辑
组件逻辑
BlogList
BlogCategory
知识
动态路由
我们希望下面的地址都能够匹配到Blog
组件
/article
,显示全部文章/article/cate/1
,显示分类id
为1
的文章/article/cate/3
,显示分类id
为3
的文章- …
第一种情况很简单,只需要将一个固定的地址匹配到Blog
组件即可
1 |
|
但后面的情况则不同:匹配到Blog
组件的地址中,有一部分是动态变化的,则需要使用一种特殊的表达方式:
1 |
|
在地址中使用:xxx
,来表达这一部分的内容是变化的,在vue-router
中,将变化的这一部分称之为params
,可以在vue
组件中通过this.$route.params
来获取
1 |
|
动态路由的导航
1 |
|
编程式导航
除了使用<RouterLink>
超链接导航外,vue-router
还允许在代码中跳转页面
1 |
|
watch
利用watch
配置,可以直接观察某个数据的变化,变化时可以做一些处理
1 |
|