前言
- 注意事项:由于我打包的时候路径没写好,所以笔记上图片加载不出来
- 所以笔记完整记录会放上来
ES6语法补充
let/var
JS中使用var来声明一个变量时, 变量的作用域主要是和函数的定义有关.针对于其他块定义来说是没有作用域的,比如if/for等,这在我们开发中往往会引起一些问题。
总之用let就好了,var淘汰了
const关键字
当我们修饰的标识符不会被再次赋值时, 就可以使用const来保证数据的安全性.
属性初始化简写和方法的简写
let name = "123"
//以前的写法
let people1 = {name:name}
//之后的写法
let people = {name}
fuc2:fuction(){
}
fuc1(){
}
Promise
Promise是异步编程的一种解决方案
三种状态
- pending:等待状态,比如正在进行网络请求,或者定时器没有到时间。
- fulfill:满足状态,当我们主动回调了resolve时,就处于该状态,并且会回调.then()
- reject:拒绝状态,当我们主动回调了reject时,就处于该状态,并且会回调.catch()
函数
Promise.resovle():将数据包装成Promise对象,并且在内部回调resolve()函数
Promise.reject():将数据包装成Promise对象,并且在内部回调reject()函数
写法
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('aaa')
}, 1000)
}).then(res => {
// 1.自己处理10行代码
console.log(res, '第一层的10行处理代码');
// 2.对结果进行第一次处理
return res + '111'
}).then(res => {
console.log(res, '第二层的10行处理代码');
return Promise.resolve(res + '222')
}).then(res => {
return new Promise((resolve, reject) => {
resolve(res + '111')
//reject('err')
})
}).then(res=>{
})
ES6模块化开发
为什么模块化开发
为什么会出现模块化开发
在网页开发的早期,js制作作为一种脚本语言,做一些简单的表单验证或动画实现等,那个时候代码还是很少的。客户端需要完成的事情越来越多,代码量也是与日俱增。为了应对代码量的剧增,我们通常会将代码组织在多个js文件中,进行维护。但是这种维护方式,依然不能避免一些灾难性的问题。例如:全局变量同名等问题
匿名函数可以解决全局变量,但是另外一个文件中不容易使用,因为flag是一个局部变量
export
导出变量
export let name = 5;
export let age = 5;
let name = 5;
export {name}
导出函数或类
export class person{
int age;
}
export function test(){
}
class person{
int age;
}
function test(){
}
export {test,person}
export default
某些情况下,一个模块中包含某个的功能,我们并不希望给这个功能命名,而且让导入者可以自己来命名
export default在同一个模块中,不允许同时存在多个
export default function(){
}
import fun from '路径'
import
首先,我们需要在HTML代码中引入两个js文件,并且类型需要设置为module
<script type = "module" src="文件路径"></script>
import指令用于导入模块中的内容,比如main.js的代码
import {name,age} from "./info.js"
如果我们希望某个模块中所有的信息都导入,一个个导入显然有些麻烦
import * as info from "./info.js"info.ages
Vue简介
编程范式:声明式编程
Vue安装
方式一:直接CDN引入
-
可以选择引入开发环境本办还是生产环境版本
<!-- 开发环境版本,包含了有帮助的命令行警告 --><script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script><!-- 生产环境版本,优化了尺寸和速度 --><script src="https://cdn.jsdelivr.net/npm/vue"></script>
-
下载和引入
https://vuejs.org/js/vue.jshttps://vuejs.org/js/vue.min.js
-
NUM
Vue基础语法
插值操作(Mustache语法)
基础语法
-
用=={{}}==表示
-
里面可以进行运算,一些简单的表达式
其他指令
-
v-once
-
第一次显示什么值就是什么值,不会因为Vue数据的改变发生改变
-
例子
<!DOCTYPE html><html lang="en"><head></head><body> <div id="app"> <div v-once>{{name}}</div> </div></body><script src="vue.js"></script><script> new Vue({ el: "#app", data: { name: "hello", url: "<a href='http://www.baidu.com'>百度一下</a>" } })</script></html>
-
-
v-html
-
将里面的值按照html解析出来
-
例子
<!DOCTYPE html><html lang="en"><head></head><body> <div id="app"> <div v-html = "url"></div> </div></body><script src="vue.js"></script><script> new Vue({ el: "#app", data: { url: "<a href='http://www.baidu.com'>百度一下</a>" } })</script></html>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dNSN2IoA-1621734333674)(图片\v-html测试图.png)]
-
-
v-text
-
v-text会把里面内容覆盖掉
-
例子
<!DOCTYPE html><html lang="en"><head></head><body> <div id="app"> <div v-text="url">{{name}}</div> <div>{{url}},{{name}}</div> </div></body><script src="vue.js"></script><script> new Vue({ el: "#app", data: { name: "hello", url: "<a href='http://www.baidu.com'>百度一下</a>" } })</script></html>
-
-
v-pre
-
v-pre不会将{{}}转义成数据,原封不动显示出来
-
例子
<!DOCTYPE html><html lang="en"><head></head><body> <div id="app"> <div v-pre>{{url}}</div> </div></body><script src="vue.js"></script><script> new Vue({ el: "#app", data: { url: "<a href='http://www.baidu.com'>百度一下</a>" } })</script></html>
-
-
v-cloak
-
由于Vue在解析的时候,还没有将{{}}转化,可能会让用户看见
-
在vue解析之前,div中有一个属性v-cloak,在vue解析之后,div中没有一个属性v-clcak
-
例子
<!DOCTYPE html><html lang="en"><head> <style> [v-cloak]{ display: none } </style></head><body> <div id="app" v-cloak> </div></body><script src="vue.js"></script><script> new Vue({ el: "#app", data: { } })</script></html>
-
示例代码
<!DOCTYPE html><html lang="en"><head> <style> [v-cloak]{ display: none } </style></head><body> <div id="app" v-cloak> {{name}}, {{num1 + num2}} <div v-once>{{name}}</div> <div>{{url}}</div> <div v-html="url"></div> <div v-text="url">{{name}}</div> <div>{{url}},{{name}}</div> <div v-pre>{{url}}</div> </div></body><script src="vue.js"></script><script> new Vue({ el: "#app", data: { name: "hello", num1: 3, num2: 5, url: "<a href='http://www.baidu.com'>百度一下</a>" } })</script></html>
绑定属性
v-bind指令
v-bind用于绑定一个或多个属性值,或者向另一个组件传递props值(这个学到组件时再介绍)
绑定class
方式一:对象语法
-
直接通过{}绑定一个类
<h2 :class="{'active': isActive}">Hello World</h2><h2 :class="{'active': isActive, 'line': isLine}">Hello World</h2><!--用法三:和普通的类同时存在,并不冲突注:如果isActive和isLine都为true,那么会有title/active/line三个类--><h2 class="title" :class="{'active': isActive, 'line': isLine}">Hello World</h2>
-
如果绑定的过于复杂,可以放在一个methods或者computed中
<h2 class="title" :class="classes">Hello World</h2><script> new Vue({ methods:{ classes(){ return {'active': isActive, 'line': isLine} } } })</script>
方式二:数组语法
-
直接通过{}绑定一个类
<h2 :class="['active', 'line']">Hello World</h2><h2 class="title" :class="['active', 'line']">Hello World</h2>
-
如果过于复杂,可以放在一个methods或者computed中
<h2 class="title" :class="classes">Hello World</h2><script> new Vue({ methods:{ classes(){ return ['active', 'line'] } } })</script>
示例代码
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> .active { color: red; font-size: 50px; } .aaaa { color: blueviolet; font-size: 50px; } </style></head><body> <div id="app"> <a v-bind:href="url">百度</a> <a :href="url">百度</a> <a :class="{active:isActive}" :href="url">百度</a> <a :class="getClassesd" :href="url">百度</a> <a :class="[classA]" :href="url">百度</a> <a :class="['classA']" :href="url">百度</a> </div></body><script src="vue.js"></script><script> new Vue({ el: "#app", data: { url: "https://www.baidu.com", isActive: false, classA: "aaaa" }, methods: { getClassesd: function () { return { active: this.isActive } } } })</script></html>
绑定style
对象绑定
-
对象的key是CSS属性名称
-
对象的value是具体赋的值,值可以来自于data中的属性
<body> <div id="app"> <p :style="{color:currentColor}">你好</p> <p :style="{color:'blue'}">你好</p> </div></body><script src="vue.js"></script><script> new Vue({ el: "#app", data: { currentColor: 'red' } })</script>
数组绑定
<body> <div id="app"> <p :style="[styleObject,baseSize]">你好</p> <p :style="{color:'blue'}">你好</p> </div></body><script src="vue.js"></script><script> new Vue({ el: "#app", data: { styleObject: { color: 'red', }, baseSize: { fontSize: '50px' } } })</script>
示例代码
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> .active { color: red; } </style></head><body> <div id="app"> <a v-bind:href="url">百度</a> <a :href="url">百度</a> <a :class="{active:isActive}" :href="url">百度</a> </div></body><script src="vue.js"></script><script> new Vue({ el: "#app", data: { url: "https://www.baidu.com", isActive: false }, method:{ getClasses(){ return {active:this.isActive} } } })</script></html>
计算属性
-
我们可能需要对数据进行一些转化后再显示,或者需要将多个数据结合起来进行显示
-
每个计算属性都包含一个getter和一个setter。
很多情况下,我们只提供getter方法,在某些情况下,你也可以提供一个setter方法(不常用)。
-
计算属性有缓存,在没有改变值情况下,会保存在缓存
<!DOCTYPE html><html lang="en"><body> <div id="app"> <p>{{fullName}}</p> <p>{{tell}}</p> </div></body><script src="vue.js"></script><script> new Vue({ el: "#app", data: { first:"哈哈哈", second:"你好" }, computed:{ fullName(){ return this.first + " "+this.second; }, tell(){ get(){ return this.first + " "+this.second; } set(newValue){ this.first = "123" } } } })</script></html>
事件监听
v-on:
-
介绍:绑定事件监听器
-
v-on也有对应的语法糖:v-on:click可以写成@click
-
注意事项:
-
如果该方法不需要额外参数,那么方法后的()可以不添加
如果方法本身中有一个或者多个参数,那么会默认将原生事件event参数传递进去
-
如果需要同时传入某个参数,同时需要event时,可以通过$event传入事件
-
-
v-on修饰符
-
.stop:防止事件冒泡。等于调用 event.stopPropagation()
-
.prevent:阻止默认行为。等于调用 event.preventDefault()
-
.{keyCode | keyAlias}:只当事件是从特定键触发时才触发回调。
-
.native:监听组件根元素的原生事件
-
.once:只触发一次回调
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ae7MEdjR-1621734333678)(图片\v-on的表达式.png)]
-
条件判断
v-if、v-else-if、v-else
- Vue的条件指令可以根据表达式的值在DOM中渲染或销毁元素或组件
- v-if后面的条件为false时,对应的元素以及其子元素不会渲染。根本没有对应的标签出现在DOM中
v-show
- v-show的用法和v-if非常相似,也用于决定一个元素是否渲染
两者对比
- v-if当条件为false时,压根不会有对应的元素在DOM中。v-show当条件为false时,仅仅是将元素的display属性设置为none
- 当需要在显示与隐藏之间切片很频繁时,使用v-show当只有一次切换时,通过使用v-if
遍历循环
v-for遍历数组
- 在遍历的过程中不需要使用索引值: v-for=“movie in movies”
- 需要拿到元素在数组中的索引值:v-for=(item, index) in items
v-for遍历对象
- v-for可以用户遍历对象
- 在遍历对象的过程中, 如果只是获取一个值, 那么获取到的是value
- 获取key和value 格式: (value, key)
- 获取key和value和index 格式: (value, key, index)
组件的key属性
这个其实和Vue的虚拟DOM的Diff算法有关系
-
有一个数组**[‘A’,‘B’,‘C’,‘D’,‘E’]**我们希望可以在B和C之间加一个F,Diff算法默认执行起来是这样的
即把C更新成F,D更新成C,E更新成D,最后再插入E
-
使用key来给每个节点做一个唯一标识
Diff算法就可以正确的识别此节点
找到正确的位置区插入新的节点。
-
key的作用主要是为了高效的更新虚拟DOM
注意事项:数组更新
因为Vue是响应式的,所以当数据发生变化时,Vue会自动检测数据变化,视图会发生对应的更新。
直接修改不会更新,需要采用下面方法
Vue中包含了一组观察数组编译的方法。
- push()
- pop()
- shift()
- unshift()
- splice()
- sort()
- reverse()
filters
filters: { showPrice(price) { return '¥' + price.toFixed(2) } }
<h2>总价格: {{totalPrice | showPrice}}</h2>
表单绑定
Vue中使用v-model指令来实现表单元素和数据的双向绑定
-
v-model其实是一个语法糖,它的背后本质上是包含两个操作
-
v-bind绑定一个value属性
-
v-on指令给当前元素绑定input事件
<input type="text" v-model="message"><!--等同于--><input type="text" v-bind:value="message" v-on:input="message = $event.target.value">
-
v-model与redio
<div id="app"> <label for="male"> <input type="radio" id="male" value="男" v-model="sex">男 </label> <label for="female"> <input type="radio" id="female" value="女" v-model="sex">女 </label> <h2>您选择的性别是: {{sex}}</h2></div><script src="../js/vue.js"></script><script> const app = new Vue({ el: '#app', data: { message: '你好啊', sex: '女' } })</script>
v-model与checkbox
- 单个勾选框
- v-model即为布尔值
- 此时input的value并不影响v-model的值
- 多个复选框
- 当是多个复选框时,因为可以选中多个,所以对应的data中属性是一个数组
- 当选中某一个时,就会将input的value添加到数组中
<div id="app"> <label for="agree">--> <input type="checkbox" id="agree" v-model="isAgree">同意协议 </label> <input type="checkbox" value="篮球" v-model="hobbies">篮球 <input type="checkbox" value="足球" v-model="hobbies">足球 <input type="checkbox" value="乒乓球" v-model="hobbies">乒乓球 <input type="checkbox" value="羽毛球" v-model="hobbies">羽毛球</div><script src="../js/vue.js"></script><script> const app = new Vue({ el: '#app', data: { message: '你好啊', isAgree: false, // 单选框 hobbies: [], // 多选框, originHobbies: ['篮球', '足球', '乒乓球', '羽毛球', '台球', '高尔夫球'] } })</script>
v-model与select
- 单选
- v-model绑定的是一个值
- 当我们选中option中的一个时,会将它对应的value赋值到mySelect中
- 多选
- v-model绑定的是一个数组
- 当选中多个值时,就会将选中的option对应的value添加到数组mySelects中
<div id="app"> <!--1.选择一个--> <select name="abc" v-model="fruit"> <option value="苹果">苹果</option> <option value="香蕉">香蕉</option> <option value="榴莲">榴莲</option> <option value="葡萄">葡萄</option> </select> <h2>您选择的水果是: {{fruit}}</h2> <!--2.选择多个--> <select name="abc" v-model="fruits" multiple> <option value="苹果">苹果</option> <option value="香蕉">香蕉</option> <option value="榴莲">榴莲</option> <option value="葡萄">葡萄</option> </select> <h2>您选择的水果是: {{fruits}}</h2></div><script src="../js/vue.js"></script><script> const app = new Vue({ el: '#app', data: { message: '你好啊', fruit: '香蕉', fruits: [] } })</script>
Vue组件化
Vue组件化思想
组件化是Vue.js中的重要思想
- 它提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构造我们的应用。
- 任何的应用都会被抽象成一颗组件树
组件化思想的应用
- 有了组件化的思想,我们在之后的开发中就要充分的利用它。
- 尽可能的将页面拆分成一个个小的、可复用的组件。
- 这样让我们的代码更加方便组织和管理,并且扩展性也更强。
注册组件的基本步骤
-
创建组件构造器
-
Vue.extend()
- 调用Vue.extend()创建的是一个组件构造器。
- 通常在创建组件构造器时,传入template代表我们自定义组件的模板。
- 该模板就是在使用到组件的地方,要显示的HTML代码。
- 事实上,这种写法在Vue2.x的文档中几乎已经看不到了,它会直接使用下面我们会讲到的语法糖,但是在很多资料还是会提到这种方式,而且这种方式是学习后面方式的基础。
-
例子
-
没有分离写法
<script>const cpnC = Vue.extend({ template: ` <div> <h2>我是标题</h2> <p>我是内容, 哈哈哈哈</p> <p>我是内容, 呵呵呵呵</p> </div>` })</script>
-
分离写法
<!--分离写法--><template id="cpn"> <div> <h2>我是标题</h2> <p>我是内容,呵呵呵</p> </div></template><script> const i = Vue.extend({ template: "#cpn" })</script>
-
-
-
注册组件
-
Vue.component()
- 调用Vue.component()是将刚才的组件构造器注册为一个组件,并且给它起一个组件的标签名称。
- 所以需要传递两个参数:1、注册组件的标签名 2、组件构造器
-
例子
-
全局注册
<script> Vue.component('cpn', cpnC)</script>
-
局部注册
<script> const app = new Vue({ components: { // cpn使用组件时的标签名 cpn: cpnC } })</script>
-
语法糖
<script> Vue.component('cpn1', { template: ` <div> <h2>我是标题1</h2> <p>我是内容, 哈哈哈哈</p> </div> ` })</script>
-
分离写法
-
template
<template id="cpn"> <div> <h2>我是标题</h2> <p>我是内容,呵呵呵</p> </div></template><script> Vue.component('cpn', { template: '#cpn' })</script>
-
script写法
<script type="text/x-template" id="cpn"><div> <h2>我是标题</h2> <p>我是内容,哈哈哈</p></div></script><script> Vue.component('cpn', { template: '#cpn' })</script>
-
-
-
-
使用组件
-
组件必须挂载在某个Vue实例下,否则它不会生效。
-
例子
<cpn></cpn>
-
组件数据处理
Vue组件应该有自己保存数据的地方
组件自己的数据
- 组件对象也有一个data属性(也可以有methods等属性,下面我们有用到)
- 只是==这个data属性必须是一个函数==
- 首先,如果不是一个函数,Vue直接就会报错。
- 其次,原因是在于Vue让每个组件对象都返回一个新的对象,因为如果是同一个对象的,组件在多次使用后会相互影响。
- 而且这个函数返回一个对象,对象内部保存着数据
父子组件的通信
在开发中,往往一些数据确实需要从上层传递到下层
- 比如在一个页面中,我们从服务器请求到了很多的数据。
- 其中一部分数据,并非是我们整个页面的大组件来展示的,而是需要下面的子组件进行展示。
- 这个时候,并不会让子组件再次发送一个网络请求,而是直接让大组件(父组件)将数据传递给小组件(子组件)
父级向子级传递
props基本用法
-
可以类型限制、
String、Number、Boolean、Array、Object、Date、Function、Symbol
当我们有自定义构造函数时,验证也支持自定义的类型
cmessage: String
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ne3ZsQgX-1621734333682)(图片\数据类型.png)]
-
提供一些默认值, 以及必传值
cmessage: { type: String, default: 'aaaaaaaa', required: true },
-
如果是一个对象,数组需要定义为函数
cmovies: { type: Array, default() { return [] } }
-
在子组件上绑定号数据
<div id="app"> <!--<cpn v-bind:cmovies="movies"></cpn>--> <!--<cpn cmovies="movies" cmessage="message"></cpn>--> <cpn :cmessage="message" :cmovies="movies"></cpn></div>
完整代码
<div id="app"> <!--<cpn v-bind:cmovies="movies"></cpn>--> <!--<cpn cmovies="movies" cmessage="message"></cpn>--> <cpn :cmessage="message" :cmovies="movies"></cpn></div><template id="cpn"> <div> <ul> <li v-for="item in cmovies">{{item}}</li> </ul> <h2>{{cmessage}}</h2> </div></template><script src="../js/vue.js"></script><script> // 父传子: props const cpn = { template: '#cpn', // props: ['cmovies', 'cmessage'], props: { // 1.类型限制 // cmovies: Array, // cmessage: String, // 2.提供一些默认值, 以及必传值 cmessage: { type: String, default: 'aaaaaaaa', required: true }, // 类型是对象或者数组时, 默认值必须是一个函数 cmovies: { type: Array, default() { return [] } } }, data() { return {} }, methods: { } } const app = new Vue({ el: '#app', data: { message: '你好啊', movies: ['海王', '海贼王', '海尔兄弟'] }, components: { cpn } })</script>
子级向父级传递
自定义事件
- 当子组件需要向父组件传递数据时,就要用到自定义事件了。
- v-on不仅仅可以用于监听DOM事件,也可以用于组件间的自定义事件
自定义事件的流程
- 在子组件中,通过$emit()来触发事件
- 在父组件中,通过v-on来监听子组件事件
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title></head><body><!--父组件模板--><div id="app"> <cpn @item-click="cpnClick"></cpn></div><!--子组件模板--><template id="cpn"> <div> <button v-for="item in categories" @click="btnClick(item)"> {{item.name}} </button> </div></template><script src="../js/vue.js"></script><script> // 1.子组件 const cpn = { template: '#cpn', data() { return { categories: [ {id: 'aaa', name: '热门推荐'}, {id: 'bbb', name: '手机数码'}, {id: 'ccc', name: '家用家电'}, {id: 'ddd', name: '电脑办公'}, ] } }, methods: { btnClick(item) { // 发射事件: 自定义事件 this.$emit('item-click', item) } } } // 2.父组件 const app = new Vue({ el: '#app', data: { message: '你好啊' }, components: { cpn }, methods: { cpnClick(item) { console.log('cpnClick', item); } } })</script></body></html>
webpack
概念
模块化
在ES6之前,我们要想进行模块化开发,就必须借助于其他的工具,让我们可以进行模块化开发。并且在通过模块化开发完成了项目后,还需要处理模块间的各种依赖,并且将其进行整合打包。而webpack其中一个核心就是让我们可能进行模块化开发,并且会帮助我们处理模块间的依赖关系。而且不仅仅是JavaScript文件,我们的CSS、图片、json文件等等在webpack中都可以被当做模块来使用(在后续我们会看到)。这就是webpack中模块化的概念。
打包
就是将webpack中的各种资源模块进行打包合并成一个或多个包(Bundle)。并且在打包的过程中,还可以对资源进行处理,比如压缩图片,将scss转成css,将ES6语法转成ES5语法,将TypeScript转成JavaScript等等操作。
grunt/gulp和webpack有什么不同
- grunt/gulp更加强调的是前端流程的自动化,模块化不是它的核心。
- webpack更加强调模块化开发管理,而文件压缩合并、预处理等功能,是他附带的功能。
安装
-
安装webpack首先需要安装Node.js,Node.js自带了软件包管理工具npm
-
查看自己的node版本
node -v
-
全局安装webpack(这里我先指定版本号3.6.0,因为vue cli2依赖该版本)
npm install webpack@3.6.0 -g
-
局部安装webpack
#cd ......npm install webpack@3.6.0 -g --save-dev
-
在终端直接执行webpack命令,使用的全局安装的webpack当在package.json中定义了scripts时,其中包含了webpack命令,那么使用的是局部webpack
文件和文件夹解析
- dist文件夹:用于存放之后打包的文件
- src文件夹:用于存放我们写的源文件
- main.js:项目的入口文件。具体内容查看下面详情。
- mathUtils.js:定义了一些数学工具函数,可以在其他地方引用,并且使用。具体内容查看下面的详情。
- index.html:浏览器打开展示的首页html
- package.json:通过npm init生成的,npm包管理的文件(暂时没有用上,后面才会用上)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m5KxXZMJ-1621734333685)(\图片\webpack.png)]
打包
打包命令
webpack ./src/main.js ./dist/bundle.js
处理完所有模块之间的关系后,将多个js打包到一个js文件中,引入时就变得非常方便了
入口和出口
每次书写还是比较麻烦,故需要写一个webpack.config.js
const path = require('path')module.exports = { entry: './src/main.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'bundle.js', //publicPath: 'dist/'//输出到特定文件里面 },}
局部安装webpack
- 第一步,项目中需要安装自己局部的webpack这里我们让局部安装webpack3.6.0
Vue CLI3中已经升级到webpack4,但是它将配置文件隐藏了起来,所以查看起来不是很方便 - 第二步,通过node_modules/.bin/webpack启动webpack打包
package.json中定义启动
-
package.json中的scripts的脚本在执行时,会按照一定的顺序寻找命令对应的位置。首先,会寻找本地的node_modules/.bin路径中对应的命令。如果没有找到,会去全局的环境变量中寻找。
{ "name": "meetwebpack", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack" }, "author": "", "license": "ISC", "devDependencies": { "webpack": "^3.6.0" }}
loader
在开发中我们不仅仅有基本的js代码处理,我们也需要加载css、图片,也包括一些高级的将ES6转成ES5代码,将TypeScript转成ES5代码,将scss、less转成css,将.jsx、.vue文件转成js文件等等,这时候需要loader
loader使用过程:
-
步骤一:通过npm安装需要使用的loader
-
步骤二:在webpack.config.js中的modules关键字下进行配置
const path = require('path')module.exports = { entry: './src/main.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'bundle.js' }, module: { rules: [ { test: /\.css$/, // css-loader只负责将css文件进行加载 // style-loader负责将样式添加到DOM中 // 使用多个loader时, 是从右向左 use: [ 'style-loader', 'css-loader' ] } ] }}
css文件处理 – style-loader
我们来安装style-loader
npm install --save-dev style-loader
这次因为webpack在读取使用的loader的过程中,是按照从右向左的顺序读取的
module: { rules: [ { test: /\.css$/, // css-loader只负责将css文件进行加载 // style-loader负责将样式添加到DOM中 // 使用多个loader时, 是从右向左 use: [ 'style-loader', 'css-loader' ] } ] }
less文件处理- less-loader
npm install --save-dev less-loader less
{ test: /\.less$/, use: [{ loader: "style-loader", // creates style nodes from JS strings }, { loader: "css-loader" // translates CSS into CommonJS }, { loader: "less-loader", // compiles Less to CSS }]}
图片文件处理 – url-loader
npm install --save-dev url-loader
{ test: /\.(png|jpg|gif|jpeg)$/, use: [ { loader: 'url-loader', options: { // 当加载的图片, 小于limit时, 会将图片编译成base64字符串形式. // 当加载的图片, 大于limit时, 需要使用file-loader模块进行加载. limit: 13000, //设置位置,名字和Hash值 name: 'img/[name].[hash:8].[ext]' }, } ] }
图片文件处理 – file-loader
npm install --save-dev file-loader
ES6语法处理
npm install --save-dev babel-loader@7 babel-core babel-preset-es2015
{ test: /\.js$/, // exclude: 排除 // include: 包含 exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader', options: { presets: ['es2015'] } } }
引入vue.js
npm install vue --save
在main.js上
// 5.使用Vue进行开发import Vue from 'vue'// import App from './vue/app'import App from './vue/App.vue'new Vue({ el: '#app', template: '<App/>', components: { App }})
在index.html
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title></head><body><div id="app"></div><script src="./dist/bundle.js"></script></body></html>
在webpack.config.js
resolve: { // alias: 别名 extensions: ['.js', '.css', '.vue'], alias: { 'vue$': 'vue/dist/vue.esm.js' } }
.vue文件封装处理
演化
自定义了组件,也必须修改index.html来使用组件。为了避免修改----------------->定义template
在前面的Vue实例中,我们定义了el属性,用于和index.html中的#app进行绑定,让Vue实例之后可以管理它其中的内容这里,我们可以将div元素中的{{message}}内容删掉,只保留一个基本的id为div的元素我们定义一个template属性。在我们之前的学习中,我们知道el用于指定Vue要管理的DOM,可以帮助解析其中的指令、事件监听等等。而如果Vue实例中同时指定了template,那么template模板的内容会替换掉挂载的对应el的模板。这样做之后我们就不需要在以后的开发中再次操作index.html,只需要在template中写入对应的标签即可
书写template模块非常麻烦怎么办呢?
没有关系,稍后我们会将template模板中的内容进行抽离。
会分成三部分书写:template、script、style,结构变得非常清晰。
安装vue-loader和vue-template-compiler来处理新文件
npm install vue-loader vue-template-compiler --save-dev
修改webpack.config.js的配置文件
{ test: /\.vue$/, use: ['vue-loader'] }
plugin
plugin是插件的意思,通常是用于对某个现有的架构进行扩展。
webpack中的插件,就是对webpack现有功能的各种扩展,比如打包优化,文件压缩等等。
loader和plugin区别:
- loader主要用于转换某些类型的模块,它是一个转换器。
- plugin是插件,它是对webpack本身的扩展,是一个扩展器。
plugin的使用过程
- 步骤一:通过npm安装需要使用的plugins(某些webpack已经内置的插件不需要安装)
- 步骤二:在webpack.config.js中的plugins中配置插件。
添加版权的Plugin
该插件名字叫BannerPlugin,属于webpack自带的插件。
修改webpack.config.js
plugins: [ new webpack.BannerPlugin('最终版权归aaa所有')]
打包html的plugin
目前,我们的index.html文件是存放在项目的根目录下的。我们知道,在真实发布项目时,发布的是dist文件夹中的内容,但是dist文件夹中如果没有index.html文件,那么打包的js等文件也就没有意义了。所以,我们需要将index.html文件打包到dist文件夹中,这个时候就可以使用HtmlWebpackPlugin插件
HtmlWebpackPlugin插件可以为我们做这些事情:自动生成一个index.html文件(可以指定模板来生成)将打包的js文件,自动通过script标签插入到body中
使用插件,修改webpack.config.js文件中plugins部分的内容如下:这里的template表示根据什么模板来生成index.html另外,我们需要删除之前在output中添加的publicPath属性,否则插入的script标签中的src可能会有问题
npm install html-webpack-plugin --save-dev
plugins: [ new webpack.BannerPlugin('最终版权归aaa所有'), new HtmlWebpackPlugin({ template: 'index.html' }),]
js压缩的Plugin
npm install uglifyjs-webpack-plugin@1.1.1 --save-dev
plugins: [ new webpack.BannerPlugin('最终版权归aaa所有'), new HtmlWebpackPlugin({ template: 'index.html' }), new UglifyjsWebpackPlugin()]
搭建本地服务器
npm install --save-dev webpack-dev-server@2.9.1
devserver也是作为webpack中的一个选项,选项本身可以设置如下属性:
- contentBase:为哪一个文件夹提供本地服务,默认是根文件夹,我们这里要填写./dist
- port:端口号
- inline:页面实时刷新
- historyApiFallback:在SPA页面中,依赖HTML5的history模式
devServer: { contentBase: './dist', inline: true }
"scripts": { "dev": "webpack-dev-server --open" },
配置分离
npm install webpack-merge -D
package.json
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack --config ./build/prod.config.js", "dev": "webpack-dev-server --open --config ./build/dev.config.js" },
dev.config.js
const webpackMerge = require('webpack-merge')const baseConfig = require('./base.config')module.exports = webpackMerge(baseConfig, { devServer: { contentBase: './dist', inline: true }})
base.config.js
const path = require('path')const webpack = require('webpack')const HtmlWebpackPlugin = require('html-webpack-plugin')const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin')module.exports = { entry: './src/main.js', output: { path: path.resolve(__dirname, '../dist'), filename: 'bundle.js', // publicPath: 'dist/' }, module: { rules: [ { test: /\.css$/, // css-loader只负责将css文件进行加载 // style-loader负责将样式添加到DOM中 // 使用多个loader时, 是从右向左 use: [ 'style-loader', 'css-loader' ] }, { test: /\.less$/, use: [{ loader: "style-loader", // creates style nodes from JS strings }, { loader: "css-loader" // translates CSS into CommonJS }, { loader: "less-loader", // compiles Less to CSS }] }, { test: /\.(png|jpg|gif|jpeg)$/, use: [ { loader: 'url-loader', options: { // 当加载的图片, 小于limit时, 会将图片编译成base64字符串形式. // 当加载的图片, 大于limit时, 需要使用file-loader模块进行加载. limit: 13000, name: 'img/[name].[hash:8].[ext]' }, } ] }, { test: /\.js$/, // exclude: 排除 // include: 包含 exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader', options: { presets: ['es2015'] } } }, { test: /\.vue$/, use: ['vue-loader'] } ] }, resolve: { // alias: 别名 extensions: ['.js', '.css', '.vue'], alias: { 'vue$': 'vue/dist/vue.esm.js' } }, plugins: [ new webpack.BannerPlugin('最终版权归aaa所有'), new HtmlWebpackPlugin({ template: 'index.html' }) ]}
Vue CLI
#cnpm安装#由于国内直接使用 npm 的官方镜像是非常慢的,这里推荐使用淘宝 NPM 镜像。#你可以使用淘宝定制的 cnpm (gzip 压缩支持) 命令行工具代替默认的 npm:npm install -g cnpm --registry=https://registry.npm.taobao.org#这样就可以使用 cnpm 命令来安装模块了:#cnpm install [name]
安装Vue脚手架
npm install -g @vue/cli#上面安装的是Vue CLI3的版本,如果需要想按照Vue CLI2的方式初始化项目时不可以的npm install -g @vue/cli-init
初始化项目
Vue CLI2初始化项目
#Vue CLI2初始化项目vue init webpack my-project
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Io52cpko-1621734333687)(图片\创建详解.png)]
Vue CLI3初始化项目
#Vue CLI3初始化项目vue create my-project
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zNlTpPTl-1621734333689)(图片\CLI3.png)]
目录结构详解
Vue CLI 2
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RBEv6xZP-1621734333690)(图片\vue目录结构.png)]
Vue CLI 3
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-32MvGiiR-1621734333691)(图片\CLI3目录结构.png)]
CLI3配置
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QB6cyYSh-1621734333692)(图片\CLI3配置.png)]
vue.config.js
是一个可选的配置文件,如果项目的 (和 package.json
同级的) 根目录中存在这个文件,那么它会被 @vue/cli-service
自动加载。你也可以使用 package.json
中的 vue
字段,但是注意这种写法需要你严格遵照 JSON 的格式来写。
这个文件应该导出一个包含了选项的对象:
// vue.config.js/** * @type {import('@vue/cli-service').ProjectOptions} */module.exports = { // 选项...}
Runtime-Compiler和Runtime-only
- 如果在之后的开发中,你依然使用template,就需要选择Runtime-Compiler(最主要的入口里面还是使用template)
- 如果你之后的开发中,使用的是.vue文件夹开发,那么可以选择Runtime-only
Runtime-Compiler:
new Vue({ el: '#app', router: router, template: '<router-view></router-view>'})
Runtime-only:
new Vue({ el: '#app', router: router, render: h => h('router-view')})
运行过程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZAZ60x4B-1621734333693)(图片\vue运行过程.png)]
render函数
new Vue({ el:"#app", render:(createElement)=>{// return createElement('标签',"相关数据对象(可以不传)",["内容数组"]), return createElement('div',{class:'box'},["create"]) return createElement('div',{class:'box'},["create",createElement('h2',["标题"])]) }})
const cpn = Vue.component('cpn',{ template:"<div></div>", data(){ return{ } }})new Vue({ el:"#app", render:(createElement)=>createElement(cpn)})
运行详情
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-70xP2H4y-1621734333694)(图片\vueBuild.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VRaixjPv-1621734333695)(图片\vueDev.png)]
vue-router
介绍
后端路由阶段
早期的网站开发整个HTML页面是由服务器来渲染的.
一个页面有自己对应的网址, 也就是URL.URL会发送到服务器, 服务器会通过正则对该URL进行匹配, 并且最后交给一个Controller进行处理.Controller进行各种处理, 最终生成HTML或者数据, 返回给前端.这就完成了一个IO操作.
当我们页面中需要请求不同的路径内容时, 交给服务器来进行处理, 服务器渲染好整个页面, 并且将页面返回给客户顿.这种情况下渲染好的页面, 不需要单独加载任何的js和css, 可以直接交给浏览器展示, 这样也有利于SEO的优化.
缺点:
- 一种情况是整个页面的模块由后端人员来编写和维护的.
- 另一种情况是前端开发人员如果要开发页面, 需要通过PHP和Java等语言来编写页面代码.
- 而且通常情况下HTML代码和数据以及对应的逻辑会混在一起, 编写和维护都是非常糟糕的事情.
前端路由阶段
前后端分离阶段:随着Ajax的出现, 有了前后端分离的开发模式.后端只提供API来返回数据, 前端通过Ajax获取数据, 并且可以通过JavaScript将数据渲染到页面中.这样做最大的优点就是前后端责任的清晰, 后端专注于数据上, 前端专注于交互和可视化上.并且当移动端(iOS/Android)出现后, 后端不需要进行任何处理, 依然使用之前的一套API即可.
单页面富应用阶段:
其实SPA最主要的特点就是在前后端分离的基础上加了一层前端路由.也就是前端来维护一套路由规则.
改变路径的方式
URL的hash
URL的hash也就是锚点(#), 本质上是改变window.location的href属性.我们可以通过直接赋值location.hash来改变href, 但是页面不发生刷新
HTML5的history
history接口是HTML5新增的, 它有五种模式改变URL而不刷新页面.
- history.pushState()
- history.replaceState()
- history.go()
- history.back()
- history.forward()
安装和使用
安装
安装vue-router
npm install vue-router --save
在模块化工程中使用它(因为是一个插件, 所以可以通过Vue.use()来安装路由功能)
router文件夹的index.js下
// 配置路由相关的信息import VueRouter from 'vue-router'import Vue from 'vue'// 1.通过Vue.use(插件), 安装插件Vue.use(VueRouter)// 2.创建VueRouter对象const routes = []const router = new VueRouter({ // 配置路由和组件之间的应用关系 routes,})// 3.将router对象传入到Vue实例export default router
main.js挂载
import Vue from 'vue'import App from './App'import router from './router'Vue.config.productionTip = falsenew Vue({ el: '#app', router,//挂载 render: h => h(App)})
使用
创建路由组件
<template> <div> <h2>我是首页</h2> <p>我是首页内容, 哈哈哈</p> </div></template><script> export default { name: "Home" }</script><style scoped></style>
配置组件和路径的映射关系
const routes = [ { path: '', // redirect重定向 redirect: '/home' }, { path: '/home', component: Home }, { path: '/about', component: About }]
使用路由
: 该标签是一个vue-router中已经内置的组件, 它会被渲染成一个标签.
: 该标签会根据当前的路径, 动态渲染出不同的组件.
网页的其他内容, 比如顶部的标题/导航, 或者底部的一些版权信息等会和处于同一个等级.
在路由切换时, 切换的是挂载的组件, 其他内容不会发生改变.
路由的懒加载
当打包构建应用时,Javascript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了
路由懒加载
- 路由懒加载的主要作用就是将路由对应的组件打包成一个个的js代码块.
- 只有在这个路由被访问到的时候, 才加载对应的组件
const Home = () => import('../components/Home')const HomeNews = () => import('../components/HomeNews')const HomeMessage = () => import('../components/HomeMessage')const routes = [ { path: '/home', component: Home, },]const router = new VueRouter({ // 配置路由和组件之间的应用关系 routes, mode: 'history', linkActiveClass: 'active'})
认识嵌套路由
一个路径映射一个组件, 访问这个路径也会分别渲染其他个组件.
嵌套路由也可以配置默认的路径
<template> <div> <ul> <li>消息1</li> </ul> </div></template><script> export default { name: "HomeNews" }</script><style scoped></style>
<template> <div> <h2>我是首页</h2> <p>我是首页内容, 哈哈哈</p> <router-link to="/home/message">消息</router-link> <router-view></router-view> <h2>{{message}}</h2> </div></template><script> export default { name: "Home", data() { return { message: '你好啊', path: '/home/news' } } }</script><style scoped></style>
import VueRouter from 'vue-router'import Vue from 'vue'const HomeMessage = () => import('../components/HomeMessage')const routes = [ { path: '/home', component: Home, meta: { title: '首页' }, children: [ { path: '', redirect: 'news' }, { path: 'news', component: HomeNews }, { path: 'message', component: HomeMessage } ] }]const router = new VueRouter({ // 配置路由和组件之间的应用关系 routes, mode: 'history', linkActiveClass: 'active'})export default router
传递参数的方式
params的类型:
- 配置路由格式: /router/:id
- 传递的方式: 在path后面跟上对应的值
- 传递后形成的路径: /router/123, /router/abc
<router-link :to="'/user/'+userId">用户</router-link>
userClick() { this.$router.push('/user/' + this.userId) },
<h2>{{$route.params.id}}</h2>
query的类型
- 配置路由格式: /router, 也就是普通配置
- 传递的方式: 对象中使用query的key作为传递方式
- 传递后形成的路径: /router?id=123, /router?id=abc
<router-link :to="{path: '/profile', query: {name: 'why', age: 18, height: 1.88}}">
profileClick() { this.$router.push({ path: '/profile', query: { name: 'kobe', age: 19, height: 1.87 } }) }
<h2>{{$route.query.name}}</h2> <h2>{{$route.query.age}}</h2> <h2>{{$route.query.height}}</h2>
$route和$router区别
- $router为VueRouter实例,想要导航到不同URL,则使用$router.push方法
- $route为当前router跳转对象里面可以获取name、path、query、params等
导航守卫
vue-router提供的导航守卫主要用来监听监听路由的进入和离开的
vue-router提供了beforeEach和afterEach的钩子函数, 它们会在路由即将改变前和改变后触发
导航钩子的三个参数解析:
参数 | 意思 |
---|---|
to | 即将要进入的目标的路由对象. |
from | 当前导航即将要离开的路由对象. |
next | 调用该方法后, 才能进入下一个钩子(对于beforeEach) |
keep-alive遇见vue-router
keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。
它们有两个非常重要的属性:
- include - 字符串或正则表达,只有匹配的组件会被缓存
- exclude - 字符串或正则表达式,任何匹配的组件都不会被缓存
- router-view 也是一个组件,如果直接被包在 keep-alive 里面,所有路径匹配到的视图组件都会被缓存
<keep-alive exclude="Profile,User"> <router-view/> </keep-alive>
细节
router-link
属性 | 意义 |
---|---|
to | 用于指定跳转的路径 |
tag | tag可以指定之后渲染成什么组件, 比如上面的代码会被渲染成一个
|
replace | replace不会留下history记录, 所以指定replace的情况下, 后退键返回不能返回到上一个页面中 |
active-class | 当对应的路由匹配成功时, 会自动给当前元素设置一个router-link-active的class, 设置active-class可以修改默认的名称.可以在路由上修改活跃class名 |
路由代码跳转
export default { name: 'App', methods: { homeClick() { // 通过代码的方式修改路由 vue-router // push => pushState // this.$router.push('/home') this.$router.replace('/home') console.log('homeClick'); }, aboutClick() { // this.$router.push('/about') this.$router.replace('/about') console.log('aboutClick'); } }}
动态路由
const routes = [ { path: '/user/:id', component: About }]
<div> {{$route.params.id}}</div>
Vuex
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式
状态信息,我们都可以放在统一的地方,对它进行保存和管理,而且它们还是响应式的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3Z8PMI3o-1621734333700)(图片\vuex.png)]
注册store
import Vue from 'vue'import App from './App'import store from './store'Vue.config.productionTip = falsenew Vue({ el: '#app', store, render: h => h(App)})
import Vue from 'vue'import Vuex from 'vuex'Vue.use(Vuex)const store = new Vuex.Store({ state: {}, mutations: {}, actions: {}, getters: {}, modules: {}})
在其他Vue组件中,我们就可以通过this.$store的方式,获取到这个store对象了
State单一状态树
Vuex也使用了单一状态树来管理应用层级的全部状态。单一状态树能够让我们最直接的方式找到某个状态的片段,而且在之后的维护和调试过程中,也可以非常方便的管理和维护。
Getters
基本使用
有时候,我们需要从store中获取一些state变异后的状态,比如下面的Store。就是需要进行一定运算
import Vue from 'vue'import Vuex from 'vuex'Vue.use(Vuex)const store = new Vuex.Store({ state: {}, mutations: {}, actions: {}, getters: { powerCounter(state) { return state.counter * state.counter }, }, modules: {}})
传参数
自带两个参数
- state:当前的state
- getters:当前的getters
import Vue from 'vue'import Vuex from 'vuex'Vue.use(Vuex)const store = new Vuex.Store({ state: {}, mutations: {}, actions: {}, getters: { powerCounter(state,getters) { return state.counter * state.counter }, }, modules: {}})
getters默认是不能传递参数的, 如果希望传递参数, 那么只能让getters本身返回另一个函数
getters: { moreAgeStu(state) { // return function (age) { // return state.students.filter(s => s.age > age) // } return age => { return state.students.filter(s => s.age > age) } } },
Mutation
基本使用
Vuex的store状态的更新唯一方式:提交Mutation
new Vuex.Store({mutations: { decrement(state) { state.counter-- },}})
通过mutation更新
subtraction() { this.$store.commit('decrement') // 2.特殊的提交封装 this.$store.commit({ type: 'incrementCount', count }) },
传参数
在通过mutation更新数据的时候, 有可能我们希望携带一些额外的参数。参数被称为是mutation的载荷(Payload)
参数只有一个的时候,可以直接写。如果多个参数,传入对象
incrementCount(state, payload) { // console.log(count); state.counter += payload.count },
Mutation响应规则
Vuex的store中的state是响应式的, 当state中的数据发生改变时, Vue组件会自动更新.
这就要求我们必须遵守一些Vuex对应的规则:
提前在store中初始化好所需的属性.
当给state中的对象添加新属性时, 使用下面的方式:
-
方式一: 使用Vue.set(obj, ‘newProp’, 123)
Vue.set(state.info, 'address', '洛杉矶')
-
方式二: 用新对象给旧对象重新赋值
state.info={...state.info,address = "洛杉矶"}
Mutation常量类型
export const SOME_MUTATION = 'SOME_MUTATION'
// store.jsimport Vuex from 'vuex'import { SOME_MUTATION } from './mutation-types'const store = new Vuex.Store({ state: { ... }, mutations: { // 我们可以使用 ES2015 风格的计算属性命名功能来使用一个常量作为函数名 [SOME_MUTATION] (state) { // mutate state } }})
Mutation 必须是同步函数
Vuex要求我们Mutation中的方法必须是同步方法
- 主要的原因是当我们使用devtools时, 可以devtools可以帮助我们捕捉mutation的快照.
- 但是如果是异步操作, 那么devtools将不能很好的追踪这个操作什么时候会被完成
Action
处理异步操作
Vue组件中, 如果我们调用action中的方法, 那么就需要使用dispatch
同样的, 也是支持传递payload
你需要明白 store.dispatch
可以处理被触发的 action 的处理函数返回的 Promise,并且 store.dispatch
仍旧返回 Promise
Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用context.commit
提交一个 mutation,或者通过 context.state
和 context.getters
来获取 state 和 getters
const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment (state) { state.count++ } }, actions: { increment (context) { context.commit('increment') } }})
Module
Vuex 允许我们将 store 分割成模块(module)
const moduleA = { state: () => ({ ... }), mutations: { ... }, actions: { ... }, getters: { ... }}const moduleB = { state: () => ({ ... }), mutations: { ... }, actions: { ... }}const store = new Vuex.Store({ modules: { a: moduleA, b: moduleB }})store.state.a // -> moduleA 的状态store.state.b // -> moduleB 的状态
模块的局部状态
对于模块内部的 mutation 和 getter,接收的第一个参数是模块的局部状态对象。
const moduleA = { state: () => ({ count: 0 }), mutations: { increment (state) { // 这里的 `state` 对象是模块的局部状态 state.count++ } }, getters: { doubleCount (state) { return state.count * 2 } }}
同样,对于模块内部的 action,局部状态通过 context.state
暴露出来,根节点状态则为 context.rootState
const moduleA = { // ... actions: { incrementIfOddOnRootSum ({ state, commit, rootState }) { if ((state.count + rootState.count) % 2 === 1) { commit('increment') } } }}
对于模块内部的 getter,根节点状态会作为第三个参数暴露出来
const moduleA = { // ... getters: { sumWithRootCount (state, getters, rootState) { return state.count + rootState.count } }}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xV0BGpFC-1621734333703)(图片\vuexFile.png)]
axios
发送并发请求
有时候, 我们可能需求同时发送两个请求
- 使用axios.all, 可以放入多个请求的数组.
- axios.all([]) 返回的结果是一个数组,使用 axios.spread 可将数组 [res1,res2] 展开为 res1, res2
全局配置
axios.defaults.baseURL = ‘123.207.32.32:8000’;axios.defaults.headers.post[‘Content-Type’] = ‘application/x-www-form-urlencoded’;
常见的配置选项
参数 | 意思 |
---|---|
请求地址 | url: ‘/user’ |
请求类型 | method: ‘get’ |
请根路径 | baseURL: ‘http://www.mt.com/api’ |
请求前的数据处理 | transformRequest:[function(data){}] |
请求后的数据处理 | transformResponse: [function(data){}] |
自定义的请求头 | headers:{‘x-Requested-With’:‘XMLHttpRequest’} |
URL查询对象 | params:{ id: 12 } |
查询对象序列化函数 | paramsSerializer: function(params){ } |
request body | data: { key: ‘aa’}, |
超时设置 | timeout: 1000 |
跨域是否带Token | withCredentials: false |
自定义请求处理 | adapter: function(resolve, reject, config){} |
身份验证信息 | auth: { uname: ‘’, pwd: ‘12’} |
响应的数据格式 | responseType: ‘json’(json / blob /document /arraybuffer / text / stream) |
拦截器
const instance = axios.create({ baseURL: 'http://123.207.32.32:8000', timeout: 5000 })instance.interceptors.response.use(res => { return res.data })instance.interceptors.request.use(res => { return res.data })
封装
// ES6 Promise的封装export function request(options) { return new Promise((resolve, reject) => { // 1.创建axios的实例对象 const instance = axios.create({ baseURL: 'http://123.207.32.32:8000', timeout: 5000 }) // 过滤器(拦截器) instance.interceptors.response.use(res => { return res.data }) // 通过实例发送网络请求 instance(options) .then(res => { resolve(res) }).catch(err => { reject(err) }) })}
ES5封装方式export function request(options, success, failure) { // 1.创建axios的实例对象 const instance = axios.create({ baseURL: 'http://123.207.32.32:8000', timeout: 5000 }) // 过滤器(拦截器) instance.interceptors.response.use(res => { return res.data }) // 通过实例发送网络请求 instance(options) .then(res => { success(res) }).catch(err => { failure(err) })}
注意
输入框依旧保留原来数据
- 问题描述
- 如果我们在有输入内容的情况下,切换了类型,我们会发现文字依然显示之前的输入的内容。
- 问题解答
- 这是因为Vue在进行DOM渲染时,出于性能考虑,会尽可能的复用已经存在的元素,而不是重新创建新的元素。
- 解决方案
- 对应的input添加key,并且我们需要保证key的不同
父组件和子组件
父子组件错误用法:以子标签的形式在Vue实例中使用
因为当子组件注册到父组件的components时,Vue会编译好父组件的模块
该模板的内容已经决定了父组件将要渲染的HTML(相当于父组件中已经有了子组件中的内容了)
是只能在父组件中被识别的。
类似这种用法,是会被浏览器忽略的。