Vue.js
一、Vue基础
Vue基础1
1.0 前端开发流程规范
1.1 前端框架发展历史
html
html [1990]----> html5 [2008.1.12]
css
css 1.0 1996
css 2.0 1998
css 3.0 2001
EcmaScript
1997年诞生
2015 EcmaScript 2015
2016 EcmaScript 2016 dart语言 vs javascript
随着前端项目的逻辑越来越复杂和难以维护,那么前端这边引进了后端的架构思想( MV* )
M Model 数据层
V View 视图层
C Controller 控制器 ( 业务逻辑 ) MVC
P Presenter 提出者( Controller 改名得来的 ) MVP
VM ViewModel 视图模型( 业务逻辑 VM 是 由 P 改名得来的) MVVM
Backbone.js MVP 2010.10
Angular.js( 1.0 ) MVC 2010.10
Angular.ts ( 2.0 ) MVC -> MVVM 2016 目前已经更新到了 Angular9 ( 也属于angular2.0 版本 )
Vue 1.0 MVVVM 2014/07
Vue 2.0 MVVM 2016/09
React 2012 不太认可前端MVC这种架构思想, 你可以将React单纯看做是MVC中V
github统计量 ( 国际使用量 )不代表大陆地区 单位是: K
angular.js angular.ts vue React
59.6 49.1 146 134
学习难度: Vue < React < Angular( 2.0 )
前端流行
移动 web && hybird app( 混合app )
app
1. native app ( 安卓 ios java ME)
2. webapp ( 应用在浏览器中的app )
3. Hybird app ( 混合app )
1. webapp 嵌入 第三方原生应用库( 可以访问原生设备(手机) 的接口权限,比如:照相机 )
2016年:
1. es6
2. vue2.0
3. angular2.0x
4. 微信小程序 / 微信小游戏
总结表:
[外链图片转存失败(img-DHkj6OLI-1568092342824)(E:\工作文件\2019-三阶段-授课笔记\2\img\前端框架时间图.png)]
-
前端js框架到底在干嘛! 为什么要用?
-
js框架帮助开发者写js逻辑代码,在开发应用的时候js的功能划分为如下几点:
-
渲染数据
[外链图片转存失败(img-faTMGwAX-1568092342826)(E:\工作文件\2019-三阶段-授课笔记\2\img\购物车.png)]
-
操作DOM
[外链图片转存失败(img-frnMFlmt-1568092342828)(E:\工作文件\2019-三阶段-授课笔记\2\img\2019-01-11-03.gif)]
-
操作cookie等存储机制api
[外链图片转存失败(img-slQvxbJz-1568092342829)(E:\工作文件\2019-三阶段-授课笔记\2\img\cookie.png)]
-
-
在前端开发中
- 难题: 如何高效的操作dom、渲染数据是一个前端工程师需要考虑的问题,而且当数据量大,流向较乱的时候,如何正确使用数据,操作数据也是一个问题???
- 解决:
- 而js框架对上述的几个问题都有自己趋于完美的解决方案,
- 开发成本降低。高性能高效率。
- 唯一的缺点就是需要使用一定的成本来学习。
- 解决:
- 难题: 如何高效的操作dom、渲染数据是一个前端工程师需要考虑的问题,而且当数据量大,流向较乱的时候,如何正确使用数据,操作数据也是一个问题???
-
1.2 初始Vue.js
-
Vue.js框架项目介绍
-
作者: 尤雨溪
[外链图片转存失败(img-yGxR6yKH-1568092342830)(E:\工作文件\2019-三阶段-授课笔记\2\img\尤雨溪.jpg)]-
-
Vue.js是尤雨溪的个人项目
-
Vue.js也是一个MVVM框架
-
Vue.js它是一个单项数据流的框架
-
Vue.js是一个Js渐进式框架
- 渐进式: 越学越难
-
学习Vue的必要性
Vue近几年来特别的受关注,三年前的时候angularJS霸占前端JS框架市场很长时间,接着react框架横空出世,因为它有一个特性是虚拟DOM,从性能上碾轧angularJS,这个时候,vue1.0悄悄的问世了,它的优雅,轻便也吸引了一部分用户,开始收到关注,16年中旬,VUE2.0问世,这个时候vue不管从性能上,还是从成本上都隐隐超过了react,火的一塌糊涂 学习vue是现在前端开发者必须的一个技能
-
1.3 MV*模式介绍
1.4 Vue实现数据绑定的原理
-
书写第一个Vue案例
-
Vue深入响应式原理图
[外链图片转存失败(img-v7bcVJYh-1568092342831)(E:\工作文件\2019-三阶段-授课笔记\2\img\data.png)]
// Vue 底层原理 // 目的: 使用原生js来实现Vue深入响应式 var box = document.querySelector('.box') var button = document.querySelector('button') var data = { name: 'Jick' } // 观察者对象 var observer = {...data} // es5提供的api方法,这个方法不兼容ie8以及以下 // Object.defineProperty(对象,对象的属性,对象属性的修饰符 ) Object.defineProperty( data,'name',{ // get/set 统称为: '存储器' get () { return observer.name // 初始化赋值一个值给name属性 }, set ( val ) { console.log( val ) box.innerHTML = val } }) button.onclick = function () { data.name = "Rose" } box.innerHTML = data.name
- 面试题/理解: 如何理解深入响应式原理?
- Vue是通过数据劫持和事件的订阅发布来实现的,数据劫持指的是Vue通过observer观察者对象对data选项中的数据进行getter和setter设置【 Object.defineProperty 】,事件的订阅发布指的是Vue通过事件来监听,通知Vue进行视图更新
- 监听: 选项/watch
- Vue是通过数据劫持和事件的订阅发布来实现的,数据劫持指的是Vue通过observer观察者对象对data选项中的数据进行getter和setter设置【 Object.defineProperty 】,事件的订阅发布指的是Vue通过事件来监听,通知Vue进行视图更新
- 面试题/理解: 如何理解深入响应式原理?
Vue基础2
2.1 模板语法
mustache 语法中是支持写js的
-
用法:
-
内容: 必须加 {{ js语法 }}
-
属性: 属性中属性值可以直接写js语法,并且属性值中的数据相当于全局变量
-
给一个标签加一个自定义属性/已有属性
img中的src就是已有属性 <img src = "" /> //data-index就是自定义属性 , web网页中建议我们使用data-形式来定义自定义属性 <img data-index = "0" />
-
思考: Vue现在想要在html中使用自己的属性,并且要和他的语法和数据结合?
-
咋整?
-
分析: 如何我能够标识出哪一个属性是具有vue标志的那就好了,也就是属性前加 v
- Vue给这种带v标识的属性,起了一个名字: 指令【 借鉴angular 】
<div v-html = "msg"> </div>
-
-
-
-
研究它js的支持性
- 数据类型
- 市场上js的数据类型分类有两种?
- 第一种
- 初始数据类型: number string null undefine boolean
- 引用数据类型: Object [ function array … ]
- 第二种
- 基础数据类型: number string boolean
- 特殊数据类型: null undefine
- 复杂数据类型; Object [ function array …]
- 第一种
- 市场上js的数据类型分类有两种?
- 输出语法
- console
- alert
- 表达式 / 运算符
- 三元表达式
- 数据类型
-
总结;
- null 和 undefined 是不会显示的,其他数据类型都是支持的,可以显示的
- 挂载在window身上的全局属性,我们都不能用的: 比如; console alert
- {{ }} 不写流程控制
- for
- if
- while
- do…while
- {{}} 支持三元表达式,同样也支持运算符
- 短路原则也是支持的
2.2 指令
指令的目的是做什么: 操作DOM
解释 : MVVM vm -> v 数据驱动
所以: 今天开始,我们不想二阶段一样操作dom,改成操作数据,数据要想操控DOM,那么我们需要依赖指令,因为指令是直接绑定在dom身上的
-
v-html 转义输出,也就是可以解析 xml 数据
-
v-text: 非转义输出,也就是无法解析 xml 类型数据
-
v-bind
-
将数据和属性进行单向数据绑定: 将vue中数据赋值给属性值
<img v-bind:src = "src" /> <div v-bind:class = ""> </div> <div v-bind:style = ""> </div>
-
简写形式
<img v-bind:src="src" alt=""> <img :src="src" alt="">
-
类名绑定
-
用法
-
对象形式用法
<p :class = "{ bg: true,size: true }"></p> <p :class = "{ bg: true,size: false }"></p> <p :class = "{ [classA]: true,[classB]: true }"></p>
-
数组形式用法
<p :class = "[ 'size','bg' ]"></p> <p :class = "[ classA,classB ]"></p> <p :class = "[ classA,classB,5>3?'a':'b']"> </p>
-
-
-
样式绑定
-
用法
-
对象形式用法
<p :style = "{width: '100px',height: '100px',background: 'yellow'}"></p> <p :style = "styleObj"></p>
-
数组形式用法
<p :style = "[{width:'100px',height: '100px'},{ background: 'green'}]"></p> <p :style = "[size,bg]"></p>
-
-
-
2.3 条件渲染
-
v-if
-
v-else-if
-
v-else
-
v-show 条件展示
<h3> 条件渲染 - 单路分支 </h3> <p v-if = "flag"> A </p> <h3> 条件渲染 - 双路分支 </h3> <p v-if = "flag"> A </p> <p v-else > B </p> <h3> 条件渲染 - 多路分支 </h3> <p v-if = "type === '美食'"> 美食 </p> <p v-else-if = " type === '游戏' "> 游戏 </p> <p v-else> 睡觉 </p> <h3> 条件展示 </h3> <p v-show = " showFlag "> 条件展示 </p>
-
思考总结
思考: v-if vs v-show 1. 效果看起来一样 2. why Vue要出两个相似的指令? v-if控制的是元素的存在与否 v-show控制的是元素的display:none属性 思考? 如果出事条件为假时? v-if v-show 谁的性能损耗较高? v-show 总结: 项目中如何选择哪一个? 频繁切换用 v-show 如果不是很频繁的切换,那我们用 v-if
2.4 列表渲染
-
v-for 指令
<h3> 数组 </h3> <ul> <li v-for = "(item,index) in arr" :key = " index "> {{ item }} -- index{{ index }} </li> </ul> <h3> 对象 </h3> <ul> <li v-for = "(item,key,index) of obj" :key = "index"> {{ item }} -- {{ key }} -- {{ index }} </li> </ul> <h3> json </h3> <ul> <li v-for = "item in json" :key = "item.id"> <span> 商品名称: {{ item.shop_name }} </span> <span> 商品价格: {{ item.price }} </span> </li> </ul> <h3> 循环嵌套 </h3> <ul> <li v-for = "item in lists" :key = "item.id"> <h3> 商品类型: {{ item.shop_name }} </h3> <ul> <li v-for = "item in item.type" :key = "item.id"> <p> 制造商: {{ item.maker }} </p> </li> <!-- <li v-for = "ele in item.type" :key = "ele.id"> <p> 制造商: {{ ele.maker }} </p> </li> --> </ul> </li> </ul> <h3> 循环number / string </h3> <p v-for = "item in 10"> {{ item }} </p> <p v-for = "item in 'abc'"> {{ item }} </p>
-
总结:
-
- 列表渲染参数可以写三个,分别为 item key index
-
- 列表渲染,要在渲染的元素身上加一个key,作为这个元素唯一的标识 ,
- 思考: 这是为什么?
- 这个key最好是id,因为id唯一?思考: 为什么不能是index
- 列表渲染,要在渲染的元素身上加一个key,作为这个元素唯一的标识 ,
-
- 循环嵌套式,参数名称是可以一致的
-
- in / of 都可以使用
-
2.5 事件处理器
-
v-on
-
key的重要性
给列表渲染的每一层Vdom添加一个唯一标识,以便diff算法进行同层级比较
扩展: 理解key
2.6 表单控件绑定
- v-model
- 双向数据绑定
- VM 改变 V随之改变
- V改变, VM也随之改变
- v-model只用于表单
- 理由: v-model默认绑定value属性
- 技巧: 看到表单直接 v-model
- 双向数据绑定
2.7 作业
- 整理好笔记,发送博客
- 用深入响应式原理来解释 双向数据绑定原理 【 思考 】
- 使用单向数据绑定来实现 v-model 效果
- 今天讲的知识点在官网上对一对,看一看,
Vue基础3
3.1 axios && fetch
目的: 是在框架中使用数据请求
回顾:
- 封装ajax
2. jquery 【 . g e t .get .get .post . a j a x .ajax .ajax .load 】框架:
数据请求
- 使用原生js提供的fetch
- 使用第三方封装库: axios
- Vue中可以统一对axios进行挂载
Vue.prototype.$http = axios
- fetch vs axios
- axios 对已获得的数据进行了一层封装 XSRF
- axios底层自动对数据进行了格式化
- fetch并没有进行封装,拿到就是格式化后的数据
- fetch进行了多一层的格式化
- res.json()
- res.blob() 格式化二进制
- res.text()
Axios总结
1.get方法
A: 无参数
axios.get(url).then(res=>console.log(res).catch(error=>conosle.log(error))
B: 有参数
axios({
url: 'http://xxx',
method: 'get' //默认就是get,这个可以省略,
params: {
key: value
}
})
2.post
注意: axios中post请求如果你直接使用npmjs.com官网文档, 会有坑
解决步骤:
1. 先设置请求头
2. 实例化 URLSearchParams的构造器函数得到params对象
3. 使用params对象身上的append方法进行数据的传参
// 统一设置请求头
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
let params = new URLSearchParams()
// params.append(key,value)
params.append('a',1)
params.append('b',2)
axios({
url: 'http://localhost/post.php',
method: 'post',
data: params,
headers: { //单个请求设置请求头
'Content-Type': "application/x-www-form-urlencoded"
}
})
.then(res => {
console.log( res )
})
.catch( error => {
if( error ){
throw error
}
})
Fetch
1.get
fetch('http://localhost/get.php?a=1&b=2')
.then(res=> res.text()) // 数据格式化 res.json() res.blob()
.then(data => {
console.log( data )
})
.catch(error => {
if( error ){
throw error
}
})
注意事项:
A: fetch 的 get 请求的参数是直接连接在url上的, 我们可以使用Node.js提供的url或是qureystring模块来将
Object --> String
B: fetch 的请求返回的是Promise对象,所以我们可以使用.then().catch(),但是要记住.then()至少要写两个, 第一个then是用来格式化数据的,第二个then是可以拿到格式化后的数据
格式化处理方式有
fetch('./data.json')
.then(res=>{
res.json() //res.text() res.blob()
})
.then( data => console.log(data))
.catch( error => console.log( error ))
2.post
fetch 文档
https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch#%E8%BF%9B%E8%A1%8C_fetch_%E8%AF%B7%E6%B1%82
fetch项目使用的博客
https://blog.csdn.net/hefeng6500/article/details/81456975
- 历史
- Vue1.0
- Vue1.0数据请求我们使用的是一个第三方的封装库,这个封装库叫做 vue-resource
- vue-resource现在已经淘汰了,它的作者也推荐我们使用axios
- vue-resource使用形式和axios一样的
- this.$http.get
- this.$http.post
- this.$http({})
- vue-resource有jsonp方法,而axios是没有的
- vue-resource使用形式和axios一样的
- Vue2.0
- axios [ 可以说是目前最好的数据请求的封装库 ]
- fetch
- Vue1.0
3.2 计算属性
computed 是Vue中的一个选项
作用: ?
业务: 如果我想让一个字符串反向,如何实现?
分析: 反向 -> 数组【 reverse 】
3.2.1 侦听属性
watch 是Vue中一个选项
作用: ?
监听数据的变化,当数据发生改变时,我们完成一些操作
业务:
watch: {
firstName ( val ) {
this.fullName = val + this.lastName
},
lastName ( val ) {
this.fullName = this.firstName + val
},
num: {
deep: true, // 深度监听
handler ( val ) {
// 当num发生改变时,触发的方法
console.log( val )
}
}
}
-
总结: methods vs computed vs watch
-
项目中如何使用
-
- 事件处理程序: methods
-
-
watch
有大量数据交互和异步处理时进行
-
-
- computed
- 有逻辑处理
- V中像全局变量一样使用
- computed
-
-
3.3 混入 【 青铜 】
minxin
-
混入的形式
- 全局混入 【 不推荐 】
- 局部混入
-
混入的作用:
-
- 将选项中某一个或是多个单独分离出去管理,让职能更加单一高效,符合模块化思想
-
-
局部混入的使用
选项 minxins
-
全局混入
Vue.mixin({})
3.4 组件 【 王者 】
-
了解前端组件化发展历史
- 前后端耦合
- 前后端不分离项目
-
- 找后台搭建项目开发环境
-
- 寻找项目目录中的静态资源目录
- js
- img
- css
- 寻找项目目录中的静态资源目录
-
- 同步修改css
-
- 前后端不分离项目
- 前后端分离
- 前端团队合作项目的出现
- 组件化为了解决多人协作冲突问题
- 复用
- 前后端耦合
-
组件的概念
- 组件是一个html 、 css 、js 、img 等的一个聚合体
-
Vue中的组件属于扩展性功能
-
通过 Vue.extend() 来扩展的
ƒ Vue (options) { if (!(this instanceof Vue) ) { warn('Vue is a constructor and should be called with the `new` keyword'); } this._init(options); } ƒ VueComponent (options) { this._init(options); }
-
VueComponet这个构造函数我们不进行new实例化,我们希望组件是以标签化的形式展示
<Hello/> --> <div></div> <Banner></Banner>
-
组件要想合法化,必须注册解析
-
组件的注册 【 创建 】
- 全局注册
- 局部注册
-
组件的规则
-
is属性 - 动态组件 - 动态缓存组件
-
template模板标签
- 直接子元素有且仅有一个
-
组件的嵌套
-
3.5 作业
- 面试题: ajax 和 fetch 有什么区别? 【 提问率: 60% 】
- 进行数据请求,然后布局一个移动端列表 【 亲亲网 】
- 弹性盒
- rem
- 了解前后端耦合,前后端分离
- 进行页面布局
- 寺库登录页面布局
- 卖座首页布局
Vue基础4
4.0 组件的通信 【 王者 】
为什么要进行组件通信?
组件是一个聚合体,将来项目要合并,那么必然各个组件之间需要建立联系,这个联系就是数据通信
-
分类
-
父子组件通信
-
理解: data选项为什么是一个函数?
- 组件是一个聚合体,也是一个整体,它需要一个独立的作用空间,也就是它的数据需要是独立的,目前js的最大特点是函数式编程,而函数恰好提供了一个独立作用域,所以我们data在出根组件外都是函数
-
理解: 为什么data函数需要返回一个返回值,返回值还是对象,不能是数组吗?
- Vue通过es5的Object.definePerproty属性对一个对象进行getter和setter设置,而data选项是作为Vue深入响应式核心的选项
-
过程
-
父组件将自己的数据同 v-bind 绑定在 子组件身上
-
子组件通过 props属性接收
<template id="father"> <div> <h3> 这里是father </h3> <Son :money = "money"></Son> </div> </template> <template id="son"> <div> <h3> 这里是son </h3> <p> 我收到了父亲给的 {{ money }} </p> </div> </template> <script> Vue.component('Father',{ template: '#father', data () { return { money: 10000 } } }) Vue.component('Son',{ template: '#son', props: ['money'] }) new Vue({ el: '#app' }) </script>
-
-
props属性数据验证
-
验证数据类型
-
验证数据大小【 判断条件 】
// props: ['money'] // 数据验证 // props: { // 'money': Number // } props: { 'money': { validator ( val ) { // 验证函数 return val > 2000 } } }
-
工作中: 第三方验证
- TypeScript [ TS ]
- 插件 vue-validator 等
-
-
-
子父组件通信
-
是通过自定义事件
- 事件的发布
- 通过绑定元素身上实现
- 事件的订阅
- 通过this.$emit触发
// html <div id="app"> <Father></Father> </div> <template id="father"> <div> <h3> 这里是father </h3> <p> 我现在有 {{ gk }} </p> <Son @give = "fn"></Son> </div> </template> <template id="son"> <div> <h3> 这里是son </h3> <button @click = "giveHongbao"> 给父亲红包 </button> </div> </template>
//js Vue.component('Father',{ template: '#father', data ( ) { return { gk: 0 } }, methods: { fn ( val ) { this.gk = val } } }) Vue.component('Son',{ template: '#son', data () { return { money: 5000 } }, methods: { giveHongbao () { this.$emit('give',this.money) } } }) new Vue({ el: '#app' })
- 事件的发布
-
-
非父子组件通信
- ref链
[外链图片转存失败(img-fVVjMwc7-1568092342832)(E:\1906\2-Vue.js\note\vue笔记\img\非父子组件通信-ref(1)].png)
-
bus事件总线
var bus = new Vue() Vue.component('Father',{ template: '#father' }) Vue.component('Son',{ template: '#son', data () { return { flag: false } }, mounted () { // 也是一个选项,表示组件挂载结束 , 也就是说我们可以在View视图中看到这个组件了 // console.log( 'mounted ') // bus.$on(自定义事件名称,执行的事件处理程序) var _this = this bus.$on('cry',function () { _this.flag = true }) } }) Vue.component('Girl',{ template: '#girl', methods: { kick () { bus.$emit('cry') } } }) new Vue({ el: '#app' })
-
非常规手段进行组件通信 【 不推荐 】
-
兵哥建议: 如果标准方案可以实现,就不要用非常规手段
-
- 可以实现子父通信
- 父组件通过 v-bind 绑定一个方法给子组件
- 子组件通过 props选项接收这个方法,然后直接调用
-
- 父组件 通过 v-bind 绑定 一个对象类型数据 给子组件
- 子组件直接使用,如果更改这个数据,那么父组件数据也更改了
- 原因: 同一个地址
- 非常规在哪里?
- 违背了单向数据流
-
-
多组件状态共享 【 vuex 】
-
4.1 slot
slot 插槽
比如: 插卡游戏机
-
分类
-
普通插槽
-
具名插槽
-
给slot加一个name属性
<slot name = "header"></slot>
-
-
注意: 以上内容是 Vue 2.5.x的版本
-
Vue 2.6.x以上的版本将使用 v-slot指令来代替 2.5.x使用方式
-
-
v-slot指令
- 作用:
- 可以将组件的数据在组件的内容中使用
- 作用:
4.2 过渡效果
Vue框架使用css3过渡效果或是js动画
Vue内部提供了一个叫做transition的过渡组件
使用transition包裹过渡元素,那么会自动添加 6 个类名 8个钩子函数
- 默认 类名 开头 v
- 如果有name属性,那么使用 这个name属性值作为类名开头
- 实现方式
- 在 CSS 过渡和动画中自动应用 class 【 自己写 】
- 可以配合使用第三方 CSS 动画库,如 Animate.css
- 在过渡钩子函数中使用 JavaScript 直接操作 DOM
- 可以配合使用第三方 JavaScript 动画库,如 Velocity.js
- 第一种 [ 在 CSS 过渡和动画中自动应用 class 【 自己写 】 ]
- 第二种: animate.css 【 推荐 】
- 第三种: Vue提供了8个javascript钩子,我们需要自定义js动画
- 第四种: 使用第三方插件: Velocity.js
4.3 生命周期
4.4 swiper
swiper是一个实现滑动操作的一个第三方库,目前最好的滑动操作库
4.5 自定义指令
业务: 页面开启时自动获得 search 按钮焦点
-
定义形式
-
全局定义
// Vue.directive('focus',{ // // 5个javascript钩子 5个中掌握2个 // bind (el,binding,vnode,oldVnode) { // 当自定义指令和元素绑定时触发 // console.log( 'bind', ) // }, // inserted ( el,binding,vnode,oldVnode ) { // 当自定义指令绑定的元素插入到页面时触发 // console.log( 'el',el) // console.log( 'binding',binding) // console.log( 'vnode',vnode) // console.log( 'oldVnode',oldVnode) // console.log('inserted') // if ( binding.modifiers.a ) { // el.style.background = 'red' // } else { // el.style.background = 'blue' // } // el.value = binding.expression // el.focus() // }, // updated () { // 当自定义指令的元素或是他的子元素发生变化式触发 // }, // componentUpdate () { //当自定义指令的元素或是他的虚拟子节点 发生变化式触发 // }, // unbind () { // 当自定义指令绑定的元素解绑时触发 // } // })
-
局部定义
new Vue({ el: '#app', directives: { 'focus': { bind () { }, inserted ( el ) { el.focus() } } } })
-
4.6 过滤器
-
- 作用
- 是对已经有的数据做数据格式化
- 作用
-
-
使用格式
- 已有数据 | 过滤器名称(arg1,arg2)
<img :src = "item.img | imgFilter " οnerrοr="this.style.visibility='hidden'" >
- | 我们称之为 管道符
-
-
定义
- 全局定义
- Vue.filter
- 局部定义
- filters选项
全局定义 // Vue.filter('imgFilter',function ( val ) { // //val就是需要格式化的数据 // // console.log('val',val) // return val.replace( 'w.h', '128.180') // }) new Vue({ el: '#app', data: { lists: [] }, filters: { // 局部定义 // 过滤器名称:function () {} 'imgFilter': function ( val ) { return val.replace( 'w.h', '128.180') } }, methods: { getList () { fetch('./data/list.json') .then( res => res.json()) .then( data => { this.lists = data.movieList }) .catch( error => console.log( error )) } } })
- 全局定义
4.7 作业
- 自定义指令实现选项卡
- 预习Vue生命周期 并 试着 实现 将 swiper 应用在 Vue中
- 复习着一周内容
- todolist尝试实现
Vue基础5
5.1 todolist案例
- sui - ui库 + Vue + OOCSS
5.2 虚拟DOM & DIff算法
-
掌握程度: 了解
-
案例
- 操作真实DOM越少越好,尽量的去操作数据
- 所以总结出来虚拟dom,
- 所以Vue利用VDOM的对象模型来模拟DOM结构
- 但是当一个页面很复杂式,DOM结构的模拟就变的很复杂了,所以Vue使用了一个新的语法糖,叫做JSX
-
jsx
-
javascript + xml 让我们可以在js中书写dom结构
<template id="mask"> <div class="mask-box"> <div class="mask-bg"></div> <div class="mask-content"> <p> 您确定要离开吗? </p> <button class="button button-warning button-fill pull-right" @click = "removeItem( activeIndex )" > 确定 </button> </div> </div> </template>
-
-
render
- ( createElement => VNode )
- 将 jsx 通过 render 方法解析为对象模型
-
流程
-
第一次时
-
template模板使用jsx语法进行编辑
-
通过render函数将jsx解析为 vdom 对象模型
-
将VDOM对象模型渲染为真实DOM,然后挂载到页面中
-
当我们的数据发生改变时
-
重新生成VDOM
[外链图片转存失败(img-kSM48PWK-1568092342833)(E:\1906\2-Vue.js\note\vue笔记\img\VDOM.png)]
-
总结:
-
- 为什么Vue中要使用VDOM?
-
- VDOM为什么可以优化Vue ?
-
- VDOM渲染流程
-
- JSX语法
-
- render函数
-
-
5.3 生命周期 [ 王者 ]
掌握程度
- 会写
- 会念
- 明白和了解每一个钩子函数的作用和意义
特别注意:
钩子函数不要写成箭头函数,箭头函数可能会改变this指向
-
理解: 为什么要有生命周期 ?
-
Vue为了在一个组件的从创建到销毁的一系列过程中添加一些功能,方便我们更好的去控制组件
-
类比: 人
- 出生 - 哭
- 小学 - 小学
- 中学
- 高中
- 大学 / 专科
- 工作
- 。。。
-
Vue的生命周期分为三个阶段,8个钩子函数
-
初始化
自动执行
研究方向
数据
真实DOM
-
beforeCreate
组件创建前
- 作用: 为整个生命周期做准备工作,初始化事件和自身或是子组件的生命周期做准备
- 类比: 父母为子女的相亲做准备
- 意义:
- 数据拿不到
- 真实dom拿不到
- 项目中
- 不常用
-
created
组件创建结束
- 作用: 初始化注入其他选项 和 激活 选项
- 类比: 我们本人知道了父母准备给我们相亲这件事,父母通知你了
- 意义:
- 数据可以拿到
- 真实dom拿不到
- 项目中:
- 数据请求,也可以修改一次数据
-
beforeMount
组件挂载前
挂载: 组件插入到页面
- 类比:两人初步联系
- 意义:
- 数据可以拿到
- 真实dom没有拿到
- 在项目中
- 数据请求,数据修改
- 建议不要去使用它了,让它完成内部事务,不给加负担
- 内部完成事务
- 判断el选项 - new Vue 是否有 el
- 有,继而判断是否有template选项,证明有子组件
- 有template,那么我们通过render函数将jsx解析为VDOM对象模型
- 无template,那么我们需要通过outerHTML手动渲染
- outerHTML,元素外进行渲染,并且会代替原来的容器盒子,并且template在实例内会被解析,将来不会渲染到页面
- 无: 那么我们需要手动挂载: new Vue().$mount(’#app’)
- 有,继而判断是否有template选项,证明有子组件
- 判断el选项 - new Vue 是否有 el
-
mounted
组件挂载结束,也就是已经插入到页面中了
- 类比: 两人约见面 【 奔现 】
- 意义:
- 数据可以拿到
- 真实DOM也可以拿到
- 项目中
- 数据修改,数据请求
- 真实DOM操作 【 不推荐 】
- 理由: 我们要用数据驱动视图
- 应该做的: 第三方实例化 【 静态数据 】
-
-
运行中
运行中触发条件是: 数据改变
只要数据改变,就会触发这两个钩子函数
-
beforeUpdate
组件更新前
- 类比: 奔现失败后,再一次在一次进行
- 意义:
- 数据是可以拿到更新后的数据
- 也可以拿到更新后的真实DOM
- 兵哥解析:
- 为在一次的更新准准备工作
- 生成Virtual DOM
- 项目中
- 不常用
-
updated
组件更新结束
- 类比: 两人看对眼了 / 两人看不对眼
- 看对眼: 相亲这件事就有一个结果
- 看不对眼: 相亲这件事继续
- 意义:
- 可以拿到修改后的数据
- 也可以拿到真实DOM
- 在项目中:
- 真实DOM操作 【 不推荐 】
- 第三方库动态实例化 【 动态数据 】
- 内部
- VDOM重新渲染,然后通过render函数解析为VDOM对象模型,在通过Diff进行比对生成patch补丁对象,然后重新渲染为真实DOM
- 只改变变化的部分,其他部分不改变
- VDOM重新渲染,然后通过render函数解析为VDOM对象模型,在通过Diff进行比对生成patch补丁对象,然后重新渲染为真实DOM
- 类比: 两人看对眼了 / 两人看不对眼
-
-
销毁
触发条件:组件被删除
- 外部开关销毁
- 内部调用$destroy()
这两个钩子函数没有太大区别,所以我们统一说
- beforeDestroy
- destroyed
- 外部销毁
- 通过开关完成
- DOM被删除了,组件也被删除了
- 通过开关完成
- 内部销毁
- 通过调用$destroy()来完成
- DOM没有被删除,但是组件被删除了
- Dom需要手动删除
- 通过调用$destroy()来完成
- 项目中如何使用:
- 善后
- 比如: 计时器,比如滚动事件等
- 善后
5.4 生命周期案例
- Swiper
- 静态数据
- 动态数据
- updated中写式,会有重复实例化问题
- 第一个解决方案: 加判断条件
- 第二个解决方案: setTimout
- 放在主线程后执行,异步队列中,保证真实DOM渲染完成
- 第三种解决方案: 推荐 Vue内部提供的 nextTick
- nextTick表示真实DOM渲染完成之后才执行
- Vue.nextTick( callback )
- this.$nextTick( callback )
- nextTick表示真实DOM渲染完成之后才执行
- updated中写式,会有重复实例化问题
[外链图片转存失败(img-TquwQTHV-1568092342834)(E:\1906\2-Vue.js\note\vue笔记\img\lifecycle.png)]
-
作业:
- todolist
2. VDOM & Diff
二、Vue高级
Vue高级 - cli
vue项目的快速构建工具 cli 【 脚手架 】
底层 webpack
React 第一天
Vue中我们注重应用
在市场上
cli2 【 扩展 】
cli3 【 大纲 】
-
什么是cli?
cli是vue提供的一个用来快速构建项目环境的一个工具,底层使用的是webpack
-
cli目前有哪些版本?
cli2 cli3
cli3对电脑的配置有一定要求
-
cli如何使用?
-
cli的安装 【 推荐使用yarn 】
npm/cnpm/yarn 都可以使用 【 yarn 配置国内镜像 】$ cnpm i yarn -g
使用国内镜像加速npm和yarn npm config set registry=https://registry.npm.taobao.org yarn config set registry https://registry.npm.taobao.org 下载cnpm:npm install -g cnpm –registry=https://registry.npm.taobao.org
-
$ yarn add @vue/cli global
这个是cli3的版本
如果我们还想使用cli2的脚手架,那么我们可以在安装一个包cli3 @vue/cli
cli2 vue-cli 【 不要安装 】
所以一个电脑只能装一个
问题: 如果我们同时想使用 cli2 和 cli3 怎么办呢?
-
$ yarn add @vue/cli-init global
如果有的同学安装3失败了,
那么你可以使用cnpm继续安装 @vue/cli @vue/cli-init1. `$ cnpm i @vue/cli -g` 这个是cli3的版本 如果我们还想使用cli2的脚手架,那么我们可以在安装一个包 1. `$ cnpm i @vue/cli-init -g`
如果还有问题:
那么你就不要使用cli3了,你可以单独安装cli2 【 先卸载cli3,重启电脑 】`$ cnpm i vue-cli -g`
-
-
验证是否安装成功
命令行输入:$ vue
看是否有东西输出,如果输出命令提示,证明安装成功 -
创建项目
cli3版本创建
-
命令创建 【 推荐 】
$ vue create project
- 手动选择配置
- 如果安装node-sass出问题,如何解决:
- 先切换成npm源 nrm use npm
- 使用cnpm 安装 cnpm i node-sass sass-loader -D
-
图形界面创建
$ vue ui
cli3两种形式创建的结果是一样的
cli2版本创建
- 标准版
$ vue init webpack project
- 简易版
$ vue init webpack-simple project
-
-
please pick a preset( user arrow keys ) 使用键盘上下键来选择一个配置
-
default 默认配置
-
Manually select features 手动选择配置
配置指的是配置webpack
babel 优雅降级 es6 —> es5
eslint js语法检测
CSS Pre-processors css 预处理语言 less sass/scss stylus
Linter / Formatter eslint / jslint
Unit Testing 单元测试
E2E Testing 端到端的测试
In dedicated config files 将所选的每一个选项用一个文件来保存( 配置 )
PWA (web app ) 在浏览器中使用的app
-
Save this as a preset for future projects? 将上面所选的配置保存下来,以备将来的项目使用
我们cli3 使用的包管理器器建议 是 yarn 大家一定要记得配置国内镜像源
-
4.分析几个版本的目录
cli3
目录
- node_modules 项目的依赖包
- cli3 webpack配置放在node_modules中
- public 静态资源目录( 生产环境 )【 这个目录下的静态资源不会被webpack 编译 】
- img
- js
- css
- favicon.ico 项目标题的logo
- index.html 根实例组件的模板,也是整个项目的容器
- src 源代码开发目录( 也是开发者主要开发的目录文件夹 )
- assets 开发环境的静态资源目录 ( 这个目录中的资源会被webpack编译)
- assets中如果图片的大小 > 4k 我们就原样拷贝到dist目录
- assets中如果图片的小小 < 4K 我们就将这个图片转化成 base64
- base64它是我们前端处理图片的一种形式,将图片路径进行编码,它可以减少一次ajax请求,达到前端性能优化的一个方案,但是base64有一个弊端,这个弊端就是会污染html结构
- components 组件存储目录
- xxx.vue文件 单文件组件 一个vue文件就是一个组件
- 组成部分
- template模板( 必须写的 )
- script脚本 ( 可写可不写)
- style样式 ( 可写可不写 )
- scoped 作用是可以减少命名冲突,规定一个样式的作用域
- 组成部分
- xxx.vue文件 单文件组件 一个vue文件就是一个组件
- assets 开发环境的静态资源目录 ( 这个目录中的资源会被webpack编译)
- .gitignore git上传忽略文件配置
- babel.config.js 优雅降级配置文件 用来将es6 --> es5
- package.json 整个项目的依赖配置文件
- README.md 整个项目启动的说明性文件
- yarn.lock 整个项目的依赖性文件的信息
- postcss.config.js 项目css预处理的配置
- .browserslistrc 浏览器版本的支持
cli2 标准版
build webpack配置
config webpack配置
node_modules
src
static 静态资源配置
js
css
img
.babelrc 优雅降级配置文件
.postcssrc css预处理配置文件
.editorconfig 编辑器配置文件
cli2 简易版
src 源代码开发目录
webpack.config.js webpack配置文件
5.学习cli使用
6.cli 它是 webpack + es6模块化来集中实现的
es6模块化
-
模块的定义
const obj = {} const fn = function(){}
-
模块的导出
// 模块的导出有两种形式 export default //默认导出一个 //export // 批量导出,导出多个
-
模块的引入
// 如果是export default 导出 import xxx from '../xxx.xx' // 如果是export 导出 improt { xx } from '../xx.xx' || import * as xx from '../xx.xx'
base64找一个博客了解一下
在cli3中创建组件,然后导出组件,然后使用组件
在 vs code编辑器中安装一个插件( Vetur ),可以使用vue文件代码高亮还可以有代码提示
【HMR】 热重载 自动刷新 webpack配置
cli3 将webpack配置放在 node_modules,所以Vue并不希望大家去修改这部分webpack配置
如果我们将来需要更改webpack配置,那么我们需要在 项目 根目录 下创建一个 vue.config.js文件
作业:
- 在cli3/cli2项目中完成todolist
2. cli3
Vue高级 - vue-router
SPA ( single page App ) 单页面应用
- 多页面应用
有多个html文件,通过a标签的连接联通各个页面
- 缺点
- 开发起来太冗余,编译、压缩很耗时间
- 页面之间的跳转速度太慢,这个时候就会出现一个严重的问题,白屏
- 单页面应用
- 不需要刷新页面,因为它就是一个页面
- 这个页面内容在切换
- 单页面内容之间的切换要想实现我们就是用路由了
- 如今我们的app、后台管理系统 主要的开发形式就是spa
vue路由功能
- 实现单页面的切换
- 路由携带参数
- 路由的导航守卫
- 路由进行数据预载(进入组件前就请求获得数据)
vue-router 基础
-
vue 路由的mode(模式)有几种, 分别是什么?在那些环境下运行? 【 黄金 】
- hash: 使用 URL hash 值来作路由。支持所有浏览器,包括不支持 HTML5 History Api 的浏览器。 #/home
- history: 依赖 HTML5 History API 和服务器配置。【需要后端支持】 /home
- abstract: 支持所有 JavaScript 运行环境,如 Node.js 服务器端。如果发现没有浏览器的 API,路由会自动强制进入这个模式。【 这个模式不常用 】
- hash/history 常用于浏览器端,abstract用于服务端
-
路由的使用步骤
-
安装 vue-router
yarn add vue-router
-
在src目录下创建一个router目录, 里面创建一个index.js文件 , 这个目录就是router的模块
-
引入第三方的依赖包, 并注册路由
import Vue from 'vue' import VueRouter from 'vue-router' Vue.use( VueRouter ) //使用vue-router这个第三方插件
注意: import这个关键字要放在整个文件的上层
-
创建了一个router对象实例,并且创建路由表
const routes = [ { path: '/home', component: Home }//每一个对象就是一个路由 ] const router = new VueRouter({ routes//路由表 必写的 })
-
导出router实例
export default router
-
入口文件main.js中引入路由实例 router , 然后在根实例中注册
import router from './router/index.js' new Vue({ router, render: (h) => App }).$mount('#app')
-
给路由一个路由展示区域
- 如果是以及路由, 那么我们放在App组件中,用一个 router-view 的组件表示
<router-view />
-
当页面第一次的打开的时候, 需要做一个重定向, 就是要自动跳转到 /home 这个路由上
const routes = [ { //我们要求这个路由的配置要放在路由表的最上方 path: '/', redirect: '/home' } ]
-
业务: 错误路由匹配,
const routes = [ { path: '/', redirect: '/home' //重定向 }, { path: '/home', component: Home }, { path: '/list', component: List }, { path: '/detail', component: Detail }, { path: '/login', component: Login }, { path: '/register', component: Register }, { path: '/user', component: User }, { path: '/shopcar', component: Shopcar }, { path: '/error', component: Error }, { //这个就是错误路由匹配, vue规定这个必须放在最下面,它必须将上面的路由全找一遍,找不到才用当前这个 path: '**', redirect: '/error' } ]
-
vue路由模式的确定 mode
- 如果你使用的是 hash , 那么a标签就可以了、
- 如果你使用 history , 那么我们最好将a标签改成 router-link 这个组件
- router-link 这个组件 身上必须要有一个 to 属性
- router-link 这个组件身上加一个 keep-alive属性可以进行浏览器缓存
-
二级路由
const routes = [ { path: '/shopcar', component: Shopcar, children: [ { path: 'yyb', //不写 / component: Yyb } ] } ]
- 注意: 写好配置之后,不要忘记了, 在对应的一级路由的组件中书写 路由展示区域
-
命名路由
作用: 就是简写路径了
{ path: '/shopcar', component: Shopcar, //子路由 children: [ { path: 'yyb', // 容易犯错点 /yyb X component: Yyb, name: 'yyb' //命名路由 }, { path: 'junge', component: Junge } ] },
-
使用: 单项数据绑定to属性
<router-link :to = "{name:'yyb'}"/>
-
-
vue-router 进阶
案例: 移动端
- 【 分类 -》 列表 -》 详情
路由进阶 - 动态路由
核心功能
- 路由传参
- 路由接参
-
动态路由:
- url中路由是改变的,但是改变路由公用一个组件
- 举例:
- localhost:3000/detail/001?a=1&b=2
- localhost:3000/detail/002?a=2&b=3
- detail
-
vue cli3 配置反向代理 20分钟
- 在根目录下面新建一个 vue.config.js
// vue.config.js中可以默认直接使用 http-proxy-middleware
module.exports = {
devServer: {
proxy: {
'/douban': { // /douban 是一个标记
target: 'http://api.douban.com', // 目标源
changeOrigin: true, // 修改源
pathRewrite: {
'^/douban': ''
}
},
'/siku': {
target: 'https://android.secoo.com',
changeOrigin: true,
pathRewrite: {
'^/siku': ''
}
}
}
}
}
/*
注意: 修改了项目配置文件,项目必须重启
*/
- 路由的传参 10分钟
<router-link :to = "{name: 'list',params: {id: xxx}, query: {xxx:xxx}}"></router-link>
-
路由的接参
- 我们发现凡是使用了路由的组件,我们统称为: 路由组件
- 路由组件身上会自动添加一个 $route的数据
id: this.$route.params.id query: this.$route.query.xxx
-
编程式导航 5分钟
- push
this.$router.push('/home')
- this.$router.push({name,params,query})
- push可以将我们的操作存放到浏览器的历史记录
- replace
- this.$router.replace(’/home’)
- this.$router.replace({name,params,query})
- replace没有将我们的操作存放到浏览器的历史记录, 效果为返回了二级
- push/replace的参数就是to属性的参数
- push
-
业务:
- 按钮的返回
- push
- replace
- back
- go
- 按钮的返回
思考: 有一个业务,当我们点击 /mine的时候,要自动跳转到 /mine/login,这个时候我们发现手段不够用了,生命周期钩子函数也实现不了,这个我们想,如果我们能监听到路由的变化,那该有多好?
思考: 如果用户没有登录,那么我们是无法进入首页的,如果登录了,那就可以进入
解决; vue为了能够监听路由的变化情况,给了一个解决方法: 这个就是导航守卫
路由进阶 – 导航守卫
别名:
- 导航守卫
- 路由守卫
- 路由钩子
- 路由拦截
-
作用: — 类似 【保安】
- 守卫路由
- 进
- 举例: 携带数据进
- 出
- 举例: 事情完成才能出
- 进
- 守卫路由
-
导航守卫一共有三种形式 【 项目三选一 】
-
A: 全局导航守卫
针对的整个项目,也就是管理或是监听整个项目
- 全局前置守卫
router.beforeEach(fn)
- fn中有三个参数
- to: 目标路由
- from: 当前路由
- next: 它是一个拦截,表示是否允许通过
- true/false/’/login’/{ name: ‘login’}/ vm => {}
- 使用场景: 当我们本地存储/cookie中有token,那我们就自动跳转 /mine
- fn中有三个参数
- 全局的解析守卫
- 在 2.5.0+ 你可以用 router.beforeResolve 注册一个全局守卫。这和 router.beforeEach 类似,区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。
- 必须保证整个项目的守卫和异步路由组件解析完成
- 全局的后置守卫
- 可以做一些用户友好提示
- 全局前置守卫
-
B: 路由独享守卫 ·
路由配置选项中的一个
- 写在路由表中的守卫钩子
- 针对的是和当前路由相关的,那么其他与之不相关的路由,它是无法监听它的变化情况的
- 做路由拦截
- 案例: 权限验证
- 数据库: 用户组
- 普通用户
- 管理员
- 超级管理员
- 我们登录式,后台会返回给我们info信息,通过信息来判断它是哪个类型用户
- 数据库: 用户组
- 案例: 权限验证
-
C: 组件内守卫【 王者 】
当前组件
-
组件内的前置守卫 beforeRouteEnter((to,from,next)=>{})
- 导航进入组件时,调用
- this是访问不到的,如果非要访问this ,必须通过 next(vm=>{})访问
- 因为组件此时没有创建,所以没有this
- 案例: 数据预载(进入组件前就获得数据)
next(vm => { //vm指的就是组件 const result = JSON.parse(res.data.slice(7,-1)).rp_result.categorys vm.$set(vm.category,'categorys',result) })
-
组件内的后置守卫
- 当离开组件时,调用
- this是可以访问到
- 案例: 注册/内容提交,用户交互提示
-
组件内的更新守卫( 路由传参和路由的接参 )
- 在当前路由改变,但是该组件被复用时调用
- 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
- 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
- 可以访问组件实例
this
-
-
-
功能: 导航守卫可以监听路由变化情况,做路由跳转拦截
-
名词
- 前置: 要进入当前路由 — 老师进入教室前
- 后置: 要离开当前路由 — 老师离开教室
-
关于next的使用
-
next() 等价于 next( true ) 表示可以从当前路由跳转到目标路由
-
next( false ) 表示不通过, 表示从当前路由跳转不到目标路由
-
next(’/login’) 等价于 next({path:’/login’}) 跳转指定的路由
-
next(’/login’) 等价于 next({path:’/login’,params,query})
-
next( fn ) 数据预载
next( vm => {})
-
-
业务: 当我们进入到一个项目的首页时,但是当我们没有注册账号时,它主动跳转到了注册/登录页
router.beforeEach((to,from,next) => {
// next() // 默认true
// next( false )
// next()
// 判断用户是否登录,我们找token值 cookie localStorage
const token = localStorage.getItem('token')
if ( to.path === '/home' ) {
next()
}
if ( to.path == '/aa') {
next()
}
if ( to.path === '/mine/login' ) {
if ( token ) {
// 条件成立,那么我们就可以进行页面跳转了
next('/home')
} else {
// 条件不成立,留在当前页面
next()
}
} else {
next( false )
}
})
- 业务: 当进入mine页面的时候, 要判断用户是否登录,如果没有登录,跳转登录页
- 路由导航守卫
- 3中类型 7个路由监听钩子
- 业务:
- 监听整个项目的路由变化情况 全局的前置守卫
- 监听某个路由的变化情况 路由的独享守卫
- 监听的路由组件的路由变化情况 组件内的导航守卫
-
动效
- animate.css 来实现路由动画
-
路由懒加载
-
Vue异步组件 + Webpack
-
动态缓存
-
router-link 加一个keep-alive属性
<router-link to = "" keep-alive></router-link>
-
-
作业:
- 我演示内容要写出来,脱离视频写出来
Vue高级 - Vuex
Vuex称为Vue的状态管理工具,也是多组件状态共享的工具
Vuex相当于是Vue的一个集中式的存储仓库
- 它存储的是数据 【 状态 】
- 存储仓库: 本地存储 cookie 数据库
什么时候用: 打算开发中大型应用
集中式数据管理, 一处修改,多处使用
思维流程:
store.js
this.
s
t
o
r
e
.
c
o
m
m
i
t
(
′
i
n
c
r
e
m
e
n
t
′
)
−
>
m
u
t
a
t
i
o
n
s
t
h
i
s
.
store.commit('increment') -> mutations this.
store.commit(′increment′)−>mutationsthis.store.dispatch(‘jia’) -> actions
mapActions() ->actions mapGetters()->getters
学生 代课老师 校长 财务 班主任 学生
(view)component - dispatch > action -> mutation -> state <- getter <- component
发送请求 处理 修改状态
业务逻辑 修改state 读取state
异步
案例: 比如我们报名考科一
- 取号
- 排队
- 窗口
- 走流程
为什么要集中式的车管所 ?
- 统一管理、集中管理
- 数据共享
为什么要走流程?
- 控制执行 , 比如 100000个人同时进入会怎么样?
理解为什么要使用vuex
- 它能够实现状态共享
- 实现流程化,让项目的运行更加流程和优化
市场上出现了一个情况,不知道什么情况下使用vuex?
- 中大型应用
- 当你不确定你是否要使用vuex的时候,那就不要用了 flux作者说的
学习阶段: 必须要用
公司: 可用可不用
1. 什么是状态
我们使用一条数据去管理一个视图,那么这个数据我们就称之为 ‘状态’
2. vuex是做什么的?
Vuex是一个集中式的存储管理中心,vuex中可以用来存储 数据( 状态 )
vuex也是一个状态管理中心,它也可以进行状态的管理
3. 什么是状态管理模式?
我们使用一条数据去管理一个视图,那么这种管理模式就称之为 状态管理
4. 什么时候使用vuex
中大型应用使用 (使用的时间)
当你不确定你是否要使用vuex的时候,那就不要用了
5. vuex的开发流程
[外链图片转存失败(img-SstUeUiD-1568092342836)(E:\1906\2-Vue.js\note\vue笔记\img\vuex.png)]
看图说话:
- Vuex的核心组成部分有三个,分别为: actions 、 state 、 mutations
- actions表示动作的创建者,它的作用是创建动作,然后发送动作, 用于用户交互、后端数据交互
- 距离: 一个用户点击了登陆按钮
- mutations 表示动作的出发者,它的作用是用来修改数据 -更新视图
- state它是数据的存储者,它的作用是定义数据【 状态 】
- actions表示动作的创建者,它的作用是创建动作,然后发送动作, 用于用户交互、后端数据交互
- 后端数据交互写在actions中
- vuex调试工具主要调试的mutations
- vuex是流程化执行的,符合单向数据流思维
vuex - 基础操作流程
-
安装vuex
$ yarn add vuex
-
在 src / store / index.js,【 数据不分块 】
import Vuex from 'vuex' import Vue from 'vue' Vue.use( Vuex ) // 1. 定义store 模块 // const store = new Vuex.Store( options ) const store = new Vuex.Store({ state:{ count: 0 }, actions: /* 1. actions是一个对象 2. acitons里面放的都是方法 3. 方法的作用是创建动作,发送动作 increment ( store对象,组件发来的实际参数1,参数2 ) {} */ increment ( { commit }, val ) { console.log('increment执行了') console.log('val', val ) // 动作创建 const action = { type: INCREMENT, val } // 发送动作 commit( action ) } }, mutations:{ /* * 也是一个对象 * 里面存放的也是方法 * 方法名称是actions发送来的动作的类型 * 接收两个参数 * state就是数据 , action就是actions发来的动作 * mutations作用是用来修改数据的 * payload表示从组件传递过来的参数 负载 */ [ INCREMENT ] ( state,action ) { console.log('mutations执行了') console.log( 'state',state) console.log( 'action',action ) //修改数据 state.count ++ } }, getters: {}, //getters表示帮助 视图【 组件 】 获得store中的 state modules // 用来实现数据分块的 /* 数据分块: 一个项目中是有多个功能或是页面的,比如有 home 分类 列表 详情 用户 普通用户 会员 超级会员 底部栏 头部栏 图表数据 一个state管理所有的这些数据,就会变的杂乱,和不好维护,所以我们希望将数据分块,单一管理,一个数据一个模块 */ }) // 2. 导出store模块 export default store
-
在main.js中注入store
import store from './store' new Vue({ router, // 在项目中注入路由,让所有的子组件都用于路由属性 $route $router store, // 在项目中注入store,让所有的子组件拥有一个属性 $store , 用于和vuex进行通信 render: h => h(App), }).$mount('#app')
-
在组件内使用
<template> <div> vuex - 基础 <hr/> <button @click = "add"> + </button> <p> {{ $store.state.count }} </p> </div> </template> <script> export default { methods: { add () { // 执行actions中的increment方法 // this.$store.dispatch( actions中方法的名称 ) this.$store.dispatch('increment',100) } }, created () { console.log( this.$store ) } } </script>
vuex操作流程 - 【 数据分块 】
-
安装vuex
$ yarn add vuex
-
在 src / store /index.js
import Vuex from 'vuex' import Vue from 'vue' import * as todos from '../pages/vuex_basic/store' Vue.use( Vuex ) const store = new Vuex.Store({ modules: { //每一个分块出去的数据包 vue_basic: { state: todos.state, actions: todos.actions, mutations: todos.mutations, getters: todos.getters } } }) export default store
-
在main.js中注册
import store from './store' new Vue({ store, // 在项目中注入store,让所有的子组件拥有一个属性 $store , 用于和vuex进行通信 render: h => h(App), }).$mount('#app')
-
在 vue_basic/store.js中打造 state actions mutations getters
/* 核心组成部分是三个 + getters store 导出的不止一个 */ import axios from 'axios' const ADD_TODOS = 'addTodos' const GET_CATEGORY = 'getCategory' export const state = { todos: [ { id: 1, task: '任务一' }, { id: 2, task: '任务二' } ], category: null } export const actions = { addTodos ({ commit }, val ) { const action = { type: ADD_TODOS, val } commit( action ) }, getCategory ( {commit} ) { axios({ url: '/index.php', params: { r: 'class/category', type: 1 } }).then( res => { // 动作创建 const action = { type: GET_CATEGORY, payload: res.data.data.data } commit( action ) }).catch( error => console.log( error )) } } export const mutations = { [ ADD_TODOS ] ( state,action ) { state.todos.push({ id: state.todos.length + 1, task: action.val }) }, [ GET_CATEGORY ] ( state,action ) { state.category = action.payload } } export const getters = { getTodos ( state ) { return state.todos } }
-
在 vue_basic/index.vue使用
<template> <div> <h3> vuex - 数据分块 - todolist增加功能 </h3> <input type="text" v-model = "val" @keyup.enter="add"> <ul> <li v-for = "item in todos" :key = "item.id"> {{ item.task }} </li> </ul> <button @click = "getCategory"> 点击获取数据 </button> <ul> <li v-for ='item in category' :key = "item.cid"> {{ item.name }} </li> </ul> </div> </template> <script> import { mapState,mapActions } from 'vuex' export default { data () { return { val: '' } }, methods: { ...mapActions(['addTodos','getCategory']), // 容易忘记 add () { this.addTodos( this.val ) this.val = '' } }, computed: { ...mapState({ todos: state => state.vue_basic.todos, // 这里是一个箭头函数来取数据 category: state => state.vue_basic.category }) } } </script>
四个方案:
1. 前: 标准 后: 标准 √
2. 前: 标准 后: 非标准 √
3. 前: 非标准 后: 非标准
4. 前: 非标准 后: 标准 √
component ---dispatch---> actions ---commit--->mutations---state <----getters----component
思考:
- 数据的获取无论是标准还是非标准,都是很麻烦的,并且有些有些违背关注点分离
- actions或是mutations的通信会出现多个 this. s t o r e . d i s p a t c h / t h i s . store.dispatch / this. store.dispatch/this.store.commit
解决方案: 、
使用vuex辅助工具
6. 辅助工具
mapActions
mapMutations
mapGetters
mapState
export default 默认导出一个
export 叫批量导出,可以导出多个
7. vuex数据分块
将来我们的数据
希望是分块管理的,这样方便我们将来为何和更新
vue是通过一个叫做 module 的模块来管理的
vue项目中 store下的一个目录就是一个数据包
案例: todolist的添加操作
作业:
- 使用vuex实现计数
- 使用vuex实现todolist中添加( 使用数据分块 module )
- vuex实现购物车
三、Webpack
- 了解webpack在前端开发当中的一个重要性
- 了解webpack版本信息
- webpack基本配置
- 如何使用webpack配置项目
- 掌握程度: <= webpack 30%
一、前端工程化工具
也叫: 自动化工具
- grunt
- gulp ( 4.x )
- Browserify ( Webpack 前身 )
- Webpack 【 主流行 】
- rollup.js https://www.rollupjs.com/guide/zh 【 小众 】
- parcel 【 小众 】
- FIS https://fis.baidu.com/ 【 小众 】
二、前端工程化工具的发展历程
- grunt
- gulp ( 4.x ) 流的操作 .pipe()
- Browserify ( Webpack 前身 ) 没有兼容模块化问题( es6 )
- Webpack 【 主流行 】 自动解决模块依赖性问题
- AMD
- CMD
- es Module
- 可以将其他类型文件做转换
三、 Webpack版本的发展过程
官网: https://webpack.js.org/
中文: https://www.webpackjs.com/
webpack1
支持CMD和AMD,同时拥有丰富的plugin和loader,webpack逐渐得到广泛应用。
loader 转化器
webpack2
支持ES Module,分析ESModule之间的依赖关系,
webpack1必须将ES,Module转换成CommonJS模块,2支持tree sharking
webpack3
新的特性大都围绕ES Module提出,如Scope Hoisting和Magic Comment;
webpack3以上基本上都可以解决es6提出的模块化
webpack4
可以解决es6模块化【 export default / export import 】
更多个功能性 pulgin【 插件 】 和 loader 【 转换器 】
前端框架中广泛使用: Angular Vue React 的脚手架都是由webpack来进行开发、管理
学习目标:
- 通过webpack自主构建一个项目 【 手动构建一个项目 】
- webpack基本配置
- webpack高级配置
四、 Webpack涉及到的前端环境问题
- Webpack底层是由 Node.js 来开发的,也就是说Webpack的配置文件都是 Node.js 文件
- Webpack的模块化书写的规范是Common.js规范
- 环境支持: Node.js 8+
- 前端环境: 【 重点 】
- 开发环境 - 无法直接在服务器中去运行
- 生产环境 - 将开发环境下的代码经过 打包 压缩 编译 之后的文件
- 测试环境 - 将开发环境的代码经过 打包 压缩 编译 之后的文件,放在测试环境服务器中运行
- unit test 单元测试 【 mocha Jest 】
- e2e 端到端测试
- 预上线环境: 将开发环境的代码经过 打包 压缩 编译 之后的文件,放到一个局域网中去运行
- 上线环境:将开发环境的代码经过 打包 压缩 编译 之后的文件,放到云服务器或是服务器主机中,可以供任何人访问,使用的一个环境( 这个环境的上线要经过国家审核 )
- 云服务器: 类比: 宾馆 中有很多的单间,每一个云服务器就是其中一个单间
- 主机: 整个酒店
- 核心关注点在:
- 开发环境
- 生产环境
五、Webpack的安装
安装可以使用任何一个包管理器: npm yarn cnpm
yarn > cnpm > npm
举例: 我使用npm安装了一个叫做 node-sass 的包 ,但是出错了 ,这时,我们想卸载,发现卸载报错
解决: 覆盖安装
cnpm || yarn 进行覆盖安装
cnpm 和 yarn 优先级没有那么绝对
版本信息
webpack 4.39.3
webpack-cli 3.3.7
1.全局安装
$ npm install webpack webpack-cli -g
$ cnpm install webpack webpack-cli -g
$ yarn add webpack webpack-cli global
2.局部安装
$ npm install webpack webpack-cli -D
$ cnpm install webpack webpack-cli -D
$ yarn add webpack webpack-cli -D
局部优先级 > 全局
六、Webpack的概念
-
webpack* 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle( 分块束 )
理解:
举例: 我们又两个模块,A模块引用B模块,我们现在使用webpack打包A模块,webpack会自动寻找A和B之间的关系,进而打包B模块
七、Webpack 使用
默认源代码开发目录为: src
默认的入口文件名称为: src/index.js
默认出口目录为: dist
默认出口打包文件名称为: main.js
通过mode来设置是那个环境下打包使用
开发环境打包: 代码不经过压缩、注释不会被删除
eval 这个方法时用来解析字符串,这个字符串里面有js代码 【 性能不太好 】
1.终端命令行使用
-
终端运行 webpack
-
当我们局部安装了webpack webpack-cli后,发现运行webpack报错命名找不到
- 解决: 全局安装webpack webpack-cli
-
默认生成环境打包
-
-
webpack --mode development/production 开发环境/生产环境打包
2.配置文件使用
- 默认webpack配置性文件名称为 webpack.config.js,这个文件是在根目录下运行的
- 运行 webpack 命令就会自动的去寻找这个 文件
- webpack.config.js文件中我们配置的就是 webpack的参数
3.配置webpack.config.js文件
我们接下来对这个文件进行配置,主要从以下几个方面来着手
1. 基础功能 : 入口 出口 文件配置 2. 转换器: loader 3. 插件: plugin 单页面配置 vs 多页面配置 单页面配置指的只有一个入口一个出口的项目 【 推荐 】 多页面配置指的是有多个入口多个出口的项目
1.单页面配置
1.1 基础功能
/*
webpack配置文件
也是Node.js文件
这个文件也是一个独立的 Common.js 模块
*/
const path = require('path')
// 1. 创建模块
const webpackConfig = {
entry: './src/index.js', //网络路径( 相对路径 )
output: { //出口目录、文件的配置
path: path.join( __dirname,'dist'), // 磁盘路径
filename: 'js/app.js' // 入口文件将来打包到出口目录中的文件的路径和名称
},
mode: 'development'//确定是生产环境还是开发环境的打包
}
// 2. 导出模块
module.exports = webpackConfig
1.2 问题: 验证webpack是否能自动解决模块化依赖问题 可以
- 打造MVC
- index.js 用了 C
- C 里面用 M
- M里面用 V
1.3 loader 转换器 可以将其他类型文件转换为我们想要的类型文件
举例: 实现css的使用
/* ------------------------------- 转换器 ------------------------------ */
// 在webpack.config.js中做如下配置:
module: { //这里用来存放转换器的配置
rules: [
// {} //每一个对象就是一个转换器的配置
{//css的处理
test: /\.css$/, // 整个项目下匹配 .css结尾的文件
use: ['style-loader','css-loader'] //两个顺序是不写反的
// 我们需要使用css-loader将css文件编译为js,然后通过style-loader将js处理插入到html文件中【 style 嵌入模式 】
}
]
},
1.4 配置前端静态服务器
//需要自动刷新: webServer 搭建前端开发服务器
cnpm install webpack-dev-server -g | -D
参数:
命令行
webpack-dev-server --port 8088 --open --mode development
写到webpack.config.js配置文件:
devServer: {//和module同级
port: 8088,
open:true
}
终端运行方式2: webpack-dev-server
把运行命令放到package.json文件: devServer可以不要了
"scripts":{
"dev": "webpack-dev-server --port 8088 --open"
}
终端: npm run dev
1.5 优雅降级配置
-
先安装转换器需要的包
$ cnpm install babel-loader@8.0.4 @babel/core @babel/preset-env -D
-
配置webpack.config.js
// 在webpack.config.js的module.rules中进行配置 {// 配置优雅降级 test: /\.js$/, exclude: /node_modules/, // 排除node_models中的js文件 use: [{ loader: 'babel-loader', options: { presets: ['@babel/preset-env'] } }] }
1.6 html产出 - 依赖的是插件
-
安装插件需要的第三方包
$ cnpm i html-webpack-plugin -D
const HtmlWebpackPlugin = require('html-webpack-plugin') //添加一个配置项 plugins:[ new HtmlWebpackPlugin({ template: './public/index.html', filename: './index.html',//默认到output目录 hash:true,//防止缓存,会给文件后面加入hash minify:{ removeAttributeQuotes:true//压缩 去掉引号 } }) ]
1.7 css抽离 - 依赖的是插件
将webpack编译过得css文件以 css外部引用的形式引入
-
安装插件
$ cnpm i extract-text-webpack-plugin@next -D
const ExtractTextWebapckPlugin= require("extract-text-webpack-plugin") //loader配置: use: ExtractTextWebapckPlugin.extract({ use: 'css-loader' }) //不再需要style-loader //pulgin配置 new ExtractTextWebapckPlugin('css/[name][hash:6].css')
1.8 图片打包
yarn add url-loader file-loader --dev
npm I url-loader file-loader --save-dev
//url-loader 存base64 file-loader存文件(woff mp3)
{
test:/\.(png|jpg|gif)/,
use:[{
loader: 'url-loader',
options: {
limit: 5000,//字节少于5000 ——》 base64 超过5000 file
outputPath: 'images/', //5000意思存到images
}
}]
}
//css中引入 | js动态(模块化) 引入
1.9 静态资源拷贝
npm i copy-webpack-plugin -D
const CopyWebpackPlugin = require('copy-webpack-plugin')
//plugin配置
new CopyWebpackPlugin([
{ from: path.resolve(__dirname,'static'), to: path.resolve(__dirname,'build/static') }
])
1.10 配置文件拆分
"dev": "webpack --mode development --config config/webpack.config.dev.js",
"build": "webpack --mode production --config config/webpack.config.prod.js",
"server": "webpack-dev-server --mode development --config config/webpack.config.dev.js"
1.11 错误资源定制
// 在webpack.config.js中添加如下配置项:
devtool: 'source-map'
制定报错信息的源
1.12 后缀名省略
// 配置webpack.config.js
resolve: { //与module同级
extensions: [ '.js', '.css', '.json', '.jsx']
}
require('./style')// 可以省略后缀名
2.多页面配置
2.1 基础功能
/*
webpack配置文件
也是Node.js文件
这个文件也是一个独立的 Common.js 模块
*/
const path = require('path')
// 1. 创建模块
const webpackConfig = {
entry: { // 多页面配置,多个入口文件
'index': './src/index.js',
'main': './src/main.js'
}, //网络路径( 相对路径 )
output: { //出口目录、文件的配置
path: path.join( __dirname,'dist'), // 磁盘路径
filename: 'js/[name].js' // 入口文件将来打包到出口目录中的文件的路径和名称
},
mode: 'development'//确定是生产环境还是开发环境的打包
}
// 2. 导出模块
module.exports = webpackConfig
解释: 为什么我们的文件要跟上hash后缀?
- 目的: 就是为了创建多个版本,便于版本切换
[外链图片转存失败(img-sKf4RZcs-1568092342837)(E:\1905\2-Vuejs\Vue高级\day05\code\为什么webpack会给文件加后缀.png)]
作业:
webpack配置功能完成 2 次
配置sass
配置postcss
组件版的 todolist
复习 【 整理 】
四、Vue项目 与 高级应用