| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> JavaScript知识库 -> Vuejs学习笔记终 -> 正文阅读 |
|
[JavaScript知识库]Vuejs学习笔记终 |
Vuejs学习笔记 学习资料来源于哔哩哔哩网课 Vue学习
Vue Day 01一. Vuejs1.1. 认识Vuejs
1.2. 安装Vue
1.3. Vue的初体验
1.4. Vue中的MVVM详细看 PPT*9 1.5. 创建Vue时, options可以放那些东西option
代码规范代码规范:缩进4个空格, 2个空格. vue的template :vscode在用户片段里设置,详细看http://mtw.so/5TnpAu 二.插值语法
三. v-bind3.1. v-bind绑定基本属性除了内容需要动态来决定外,某些属性我们也希望动态来绑定。
3.2. v-bind动态绑定
|
参数 | 描述 |
---|---|
index | 必需。整数,规定添加/删除项目的位置,使用负数可从数组结尾处规定位置。 |
howmany | 必需。要删除的项目数量。如果设置为 0,则不会删除项目。 |
item1, …, itemX | 可选。向数组添加的新项目。 |
<body>
<div id="app">
<ul>
<li v-for="item in letters" :key="item">{{item}}</li>
</ul>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
letters: ['A', 'B', 'C', 'D', 'E']
}
})
</script>
</body>
<body>
<div id="app">
<ul>
<li v-for="item in letters">{{item}}</li>
</ul>
<button @click="btnClick">按钮</button>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
letters: ['a', 'b', 'c', 'd']
},
methods: {
btnClick() {
// 1.push方法
// this.letters.push('aaa')
// this.letters.push('aaaa', 'bbbb', 'cccc')
// 2.pop(): 删除数组中的最后一个元素
// this.letters.pop();
// 3.shift(): 删除数组中的第一个元素
// this.letters.shift();
// 4.unshift(): 在数组最前面添加元素
// this.letters.unshift()
// this.letters.unshift('aaa', 'bbb', 'ccc')
// 5.splice作用: 删除元素/插入元素/替换元素
// 删除元素: 第二个参数传入你要删除几个元素(如果没有传,就删除后面所有的元素)
// 替换元素: 第二个参数, 表示我们要替换几个元素, 后面是用于替换前面的元素
// 插入元素: 第二个参数, 传入0, 并且后面跟上要插入的元素
// splice(start)
// splice(start):
this.letters.splice(1, 3, 'm', 'n', 'l', 'x')
// this.letters.splice(1, 0, 'x', 'y', 'z')
// 5.sort()
// this.letters.sort()
// 6.reverse()//将数组内容进行反转:app.letters.reverse()
// this.letters.reverse()
// 注意: 通过索引值修改数组中的元素,非响应式的
// this.letters[0] = 'bbbbbb';非响应式。
// this.letters.splice(0, 1, 'bbbbbb')
// set(要修改的对象, 索引值, 修改后的值)
// Vue.set(this.letters, 0, 'bbbbbb')
}
}
})
// function sum(num1, num2) {
// return num1 + num2
// }
//
// function sum(num1, num2, num3) {
// return num1 + num2 + num3
// }
// function sum(...num) {
//...bum是一个可变参数,就可以加入无数数字
// console.log(num);
// }
// sum(20, 30, 40, 50, 601, 111, 122, 33)
</script>
</body>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.active {
color: red;
}
</style>
</head>
<body>
<div id="app">
<ul>
<li v-for="(item, index) in movies"
:class="{active: currentIndex === index}"
@click="liClick(index)">
{{index}}.{{item}}
</li>
<!--<li :class="{active: 0===currentIndex}"></li>-->
<!--<li :class="{active: 1===currentIndex}"></li>-->
<!--<li :class="{active: 2===currentIndex}"></li>-->
<!--<li :class="{active: 3===currentIndex}"></li>-->
</ul>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
movies: ['海王', '海贼王', '加勒比海盗', '海尔兄弟'],
currentIndex: 0
},
methods: {
liClick(index) {
this.currentIndex = index
}
}
})
</script>
</body>
</html>
属性 | 描述 |
---|---|
disabled | 返回是否禁用按钮 |
accessKey | 设置或返回访问某个按钮的快捷键。 |
disabled | 设置或返回是否禁用按钮。 |
form | 返回对包含按钮的表单的引用。 |
id | 设置或返回按钮的 id。 |
name | 设置或返回按钮的名称。 |
tabIndex | 设置或返回按钮的 Tab 键控制次序。 |
type | 返回按钮的表单类型。 |
value | 设置或返回显示在按钮上的文本。 |
计算属性 computed
// for (let i in/of this.books)
高阶函数// reduce
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="app">
<div v-if="books.length">
<table>
<thead>
<tr>
<th></th>
<th>书籍名称</th>
<th>出版日期</th>
<th>价格</th>
<th>购买数量</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in books">
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.date}}</td>
<td>{{item.price | showPrice}}</td>
<td>
<button @click="decrement(index)" v-bind:disabled="item.count <= 1">-</button>
{{item.count}}
<button @click="increment(index)">+</button>
</td>
<td><button @click="removeHandle(index)">移除</button></td>
</tr>
</tbody>
</table>
<!--过滤器totalPrice | 过滤器(filters)-->
<h2>总价格: {{totalPrice | showPrice}}</h2>
</div>
<h2 v-else>购物车为空</h2>
</div>
<script src="../js/vue.js"></script>
<script src="main.js"></script>
<script>
</script>
</body>
</html>
main.js
const app = new Vue({
el: '#app',
data: {
books: [
{
id: 1,
name: '《算法导论》',
date: '2006-9',
price: 85.00,
count: 1
},
{
id: 2,
name: '《UNIX编程艺术》',
date: '2006-2',
price: 59.00,
count: 1
},
{
id: 3,
name: '《编程珠玑》',
date: '2008-10',
price: 39.00,
count: 1
},
{
id: 4,
name: '《代码大全》',
date: '2006-3',
price: 128.00,
count: 1
},
]
},
methods: {
// getFinalPrice(price) {
// return '¥' + price.toFixed(2)
// }
increment(index) {
this.books[index].count++
},
decrement(index) {
this.books[index].count--
},
removeHandle(index) {
this.books.splice(index, 1)
}
},
computed: {
totalPrice() {
//let totalPrice = 0
//return this.books.reduce(function (preValue, book) {
// return preValue + book.price * book.count
//}, 0)
//1.
for (let i = 0; i < this.books.length; i++) {
totalPrice += this.books[i].price * this.books[i].count
}
return totalPrice
// 2.for (let i in this.books)
// let totalPrice = 0
// for (let i in this.books) {
// const book = this.books[i]
// totalPrice += book.price * book.count
// }
//
// return totalPrice
// 3.for (let i of this.books)
// let totalPrice = 0
// for (let item of this.books) {
// totalPrice += item.price * item.count
// }
// return totalPrice
// for (let i in/of this.books)
// reduce
}
},
filters: {
showPrice(price) {
return '¥' + price.toFixed(2)
}
}
})
style.css
table {
border: 1px solid #e9e9e9;
border-collapse: collapse;
border-spacing: 0;
}
th, td {
padding: 8px 16px;
border: 1px solid #e9e9e9;
text-align: left;
}
th {
background-color: #f7f7f7;
color: #5c6b77;
font-weight: 600;
}
// 编程范式: 面向对象编程(第一公民:对象)/函数式编程(第一公民:函数)
// filter/map/reduce
// filter中的回调函数有一个要求: 必须返回一个boolean值
// true: 当返回true时, 函数内部会自动将这次回调的n加入到新的数组中
// false: 当返回false时, 函数内部会过滤掉这次的n
const nums = [10, 20, 111, 222, 444, 40, 50]
//一行代码解决问题
// let total = nums.filter(n => n < 100).map(n => n * 2).reduce((pre, n) => pre + n);
// console.log(total);
let total = nums.filter(function (n) {
return n < 100
}).map(function (n) {
return n * 2
}).reduce(function (prevValue, n) {
return prevValue + n
}, 0)
console.log(total);
// 1.filter函数的使用
// // 10, 20, 40, 50
// let newNums = nums.filter(function (n) {
// return n < 100
// })
// // console.log(newNums);
//
// // 2.map函数的使用
// // 20, 40, 80, 100
// let new2Nums = newNums.map(function (n) { // 20
// return n * 2
// })
// console.log(new2Nums);
//
// // 3.reduce函数的使用
// // reduce作用对数组中所有的内容进行汇总
// let total = new2Nums.reduce(function (preValue, n) {
// return preValue + n
// }, 0)
// console.log(total);
// 第一次: preValue 0 n 20
// 第二次: preValue 20 n 40
// 第二次: preValue 60 n 80
// 第二次: preValue 140 n 100
// 240
// // 1.需求: 取出所有小于100的数字
// let newNums = []
// for (let n of nums) {
// if (n < 100) {
// newNums.push(n)
// }
// }
//
// // 2.需求:将所有小于100的数字进行转化: 全部*2
// let new2Nums = []
// for (let n of newNums) {
// new2Nums.push(n * 2)
// }
//
// console.log(new2Nums);
//
//
// // 3.需求:将所有new2Nums数字相加,得到最终的记过
// let total = 0
// for (let n of new2Nums) {
// total += n
// }
//
// console.log(total);
<body>
<div id="app">
<input type="text" v-model="message">
{{message}}
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊'
}
})
</script>
</body>
<input type="text" v-model="message">
<input type="text" v-bind:value="message" v-on:input="message = $event.target.value">
<body>
<div id="app">
<!--<input type="text" v-model="message">-->
<!--<input type="text" :value="message" @input="valueChange">-->
<!-- @input用于监听用户是否在输入-->
<input type="text" :value="message" @input="message = $event.target.value">
<h2>{{message}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊'
},
methods: {
valueChange(event) {
this.message = event.target.value;
}
}
})
</script>
</body>
sex:'女'
<body>
<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>
</body>
<body>
<div id="app">
<!--1.checkbox单选框-->
<!--<label for="agree">-->
<!--<input type="checkbox" id="agree" v-model="isAgree">同意协议-->
<!--</label>-->
<!--<h2>您选择的是: {{isAgree}}</h2>-->
<!--<button :disabled="!isAgree">下一步</button>-->
<!--2.checkbox多选框-->
<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">羽毛球
<h2>您的爱好是: {{hobbies}}</h2>
<label v-for="item in originHobbies" :for="item">
<input type="checkbox" :value="item" :id="item" v-model="hobbies">{{item}}
</label>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊',
isAgree: false, // 单选框
hobbies: [], // 多选框,
//值绑定
originHobbies: ['篮球', '足球', '乒乓球', '羽毛球', '台球', '高尔夫球']
}
})
</script>
</body>
<body>
<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>
</body>
<div id="app">
<label v-for="item in originHobbies" :for="item">
<input type="checkbox" :value="item" :id="item" v-model="hobbies">{{item}}
</label>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊',
isAgree: false, // 单选框
hobbies: [], // 多选框,
//值绑定
originHobbies: ['篮球', '足球', '乒乓球', '羽毛球', '台球', '高尔夫球']
}
})
</script>
</body>
<body>
<div id="app">
<!--1.修饰符: lazy-->
<input type="text" v-model.lazy="message">
<h2>{{message}}</h2>
<!--2.修饰符: number-->
<input type="number" v-model.number="age">
<h2>{{age}}-{{typeof age}}</h2>
<!--3.修饰符: trim-->
<input type="text" v-model.trim="name">
<h2>您输入的名字:{{name}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊',
age: 0,
name: ''
}
})
var age = 0
age = '1111'
age = '222'
</script>
如果我们将一个页面中所有的处理逻辑全部放在一起,处理起来就会变得非常复杂,而且不利于后续的管理以及扩展。
但如果,我们讲一个页面拆分成一个个小的功能块,每个功能块完成属于自己这部分独立的功能,那么之后整个页面的管理和维护就变得非常容易了。
它提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构造我们的应用。
任何的应用都会被抽象成一颗组件树。
组件的使用分成三个步骤:
查看运行结果:
和直接使用一个div看起来并没有什么区别。
但是我们可以设想,如果很多地方都要显示这样的信息,我们是不是就可以直接使用来完成呢?
<body>
<div id="app">
<!--3.使用组件-->
<my-cpn></my-cpn>
<my-cpn></my-cpn>
<my-cpn></my-cpn>
<my-cpn></my-cpn>
<div>
<div>
<my-cpn></my-cpn>
</div>
</div>
</div>
<my-cpn></my-cpn>
<script src="../js/vue.js"></script>
<script>
// 1.创建组件构造器对象
const cpnC = Vue.extend({
template: `
<div>
<h2>我是标题</h2>
<p>我是内容, 哈哈哈哈</p>
<p>我是内容, 呵呵呵呵</p>
</div>`
})
// 2.注册组件
Vue.component('my-cpn', cpnC)
const app = new Vue({
el: '#app',
data: {
message: '你好啊'
}
})
</script>
</body>
<body>
<div id="app">
<cpn></cpn>
<cpn></cpn>
<cpn></cpn>
</div>
<div id="app2">
<cpn></cpn>
</div>
<script src="../js/vue.js"></script>
<script>
// 1.创建组件构造器
const cpnC = Vue.extend({
template: `
<div>
<h2>我是标题</h2>
<p>我是内容,哈哈哈哈啊</p>
</div>
`
})
// 2.注册组件(全局组件, 意味着可以在多个Vue的实例下面使用)
// Vue.component('cpn', cpnC)
const app = new Vue({
el: '#app',
data: {
message: '你好啊'
},
components: {
// cpn使用组件时的标签名
//局部组件
cpn: cpnC
}
})
const app2 = new Vue({
el: '#app2'
})
</script>
</body>
组件和组件之间存在层级关系
<body>
<div id="app">
<cpn2></cpn2>
<!--<cpn1></cpn1>-->
</div>
<script src="../js/vue.js"></script>
<script>
// 1.创建第一个组件构造器(子组件)
const cpnC1 = Vue.extend({
template: `
<div>
<h2>我是标题1</h2>
<p>我是内容, 哈哈哈哈</p>
</div>
`
})
// 2.创建第二个组件构造器(父组件)
const cpnC2 = Vue.extend({
template: `
<div>
<h2>我是标题2</h2>
<p>我是内容, 呵呵呵呵</p>
<cpn1></cpn1>
</div>
`,
//组件构造器里加入了components注册属性,作用cpn1可以在cpnC2里使用
components: {
cpn1: cpnC1
}
})
// root组件
const app = new Vue({
el: '#app',
data: {
message: '你好啊'
},
components: {
cpn2: cpnC2
}
})
</script>
</body>
省去了调用Vue.extend()的步骤,而是可以直接使用一个对象来代替。
<body>
<div id="app">
<cpn1></cpn1>
<cpn2></cpn2>
</div>
<script src="../js/vue.js"></script>
<script>
// 1.全局组件注册的语法糖
// 1.创建组件构造器
// const cpn1 = Vue.extend()
// 2.注册组件
Vue.component('cpn1', {
template: `
<div>
<h2>我是标题1</h2>
<p>我是内容, 哈哈哈哈</p>
</div>
`
})
// 2.注册局部组件的语法糖
const app = new Vue({
el: '#app',
data: {
message: '你好啊'
},
components: {
'cpn2': {
template: `
<div>
<h2>我是标题2</h2>
<p>我是内容, 呵呵呵</p>
</div>
`
}
}
})
</script>
</body>
<script>
标签<template>
标签<body>
<div id="app">
<cpn></cpn>
<cpn></cpn>
<cpn></cpn>
</div>
<!--1.script标签, 注意:类型必须是text/x-template-->
<!--<script type="text/x-template" id="cpn">-->
<!--<div>-->
<!--<h2>我是标题</h2>-->
<!--<p>我是内容,哈哈哈</p>-->
<!--</div>-->
<!--</script>-->
<!--2.template标签-->
<template id="cpn">
<div>
<h2>我是标题</h2>
<p>我是内容,呵呵呵</p>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
// 1.注册一个全局组件
Vue.component('cpn', {
template: '#cpn'
})
const app = new Vue({
el: '#app',
data: {
message: '你好啊'
}
})
</script>
</body>
<body>
<div id="app">
<cpn></cpn>
<cpn></cpn>
<cpn></cpn>
</div>
<!--1.script标签, 注意:类型必须是text/x-template-->
<!--<script type="text/x-template" id="cpn">-->
<!--<div>-->
<!--<h2>我是标题</h2>-->
<!--<p>我是内容,哈哈哈</p>-->
<!--</div>-->
<!--</script>-->
<!--2.template标签-->
<template id="cpn">
<div>
<h2>{{title}}</h2>
<p>我是内容,呵呵呵</p>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
// 1.注册一个全局组件
Vue.component('cpn', {
template: '#cpn',
//函数类型,而非对象类型且必须返回一个对象实例
data() {
return {
title: 'abc'
}
}
})
const app = new Vue({
el: '#app',
data: {
message: '你好啊',
// title: '我是标题'
}
})
</script>
</body>
<body>
<!--组件实例对象-->
<div id="app">
<cpn></cpn>
<cpn></cpn>
<cpn></cpn>
</div>
<template id="cpn">
<div>
<h2>当前计数: {{counter}}</h2>
<button @click="increment">+</button>
<button @click="decrement">-</button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
// 1.注册组件
const obj = {
counter: 0
}
Vue.component('cpn', {
template: '#cpn',
// data() {
// return {
// counter: 0
// }
// },
data() {
return obj
},
methods: {
increment() {
this.counter++
},
decrement() {
this.counter--
}
}
})
const app = new Vue({
el: '#app',
data: {
message: '你好啊'
}
})
</script>
<script>
// const obj = {
// name: 'why',
// age: 18
// }
//
// function abc() {
// return obj
// }
//
// let obj1 = abc()
// let obj2 = abc()
// let obj3 = abc()
//
// obj1.name = 'kobe'
// console.log(obj2);
// console.log(obj3);
</script>
</body>
子组件是不能引用父组件或者Vue实例的数据
但是可以又上层向下层传递数据来进行展示
通过props向子组件传递数据
通过事件向父组件发送消息
Vue实例和子组件的通信和父组件和子组件的通信过程是一样的
props的值有两种方式:
<body>
<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>
</body>
使用驼峰标识的话,绑定时需要
)-
来进行绑定
子组件里有很多的mustcahe需要一个确切的根(即外层的东西,如
<body>
<div id="app">
<cpn :c-info="info" :child-my-message="message" v-bind:class></cpn>
</div>
<template id="cpn">
<div>
<h2>{{cInfo}}</h2>
<h2>{{childMyMessage}}</h2>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const cpn = {
template: '#cpn',
props: {
cInfo: {
type: Object,
default() {
return {}
}
},
childMyMessage: {
type: String,
default: ''
}
}
}
const app = new Vue({
el: '#app',
data: {
info: {
name: 'why',
age: 18,
height: 1.88
},
message: 'aaaaaa'
},
components: {
cpn
}
})
</script>
</body>
//整个操作的过程还是在子组件中完成,但是之后的展示交给父组件。
//需要将子组件中的counter,传给父组件的某个属性,比如total。
<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>
<body>
<div id="app">
<cpn :number1="num1"
:number2="num2"
@num1change="num1change"
@num2change="num2change"/>
</div>
<template id="cpn">
<div>
<h2>props:{{number1}}</h2>
<h2>data:{{dnumber1}}</h2>
<!--<input type="text" v-model="dnumber1">-->
<input type="text" :value="dnumber1" @input="num1Input">
<h2>props:{{number2}}</h2>
<h2>data:{{dnumber2}}</h2>
<!--<input type="text" v-model="dnumber2">-->
<input type="text" :value="dnumber2" @input="num2Input">
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
num1: 1,
num2: 0
},
methods: {
num1change(value) {
this.num1 = parseFloat(value)
},
num2change(value) {
this.num2 = parseFloat(value)
}
},
components: {
cpn: {
template: '#cpn',
props: {
number1: Number,
number2: Number
},
data() {
return {
dnumber1: this.number1,
dnumber2: this.number2
}
},
methods: {
num1Input(event) {
// 1.将input中的value赋值到dnumber中
this.dnumber1 = event.target.value;
// 2.为了让父组件可以修改值, 发出一个事件
this.$emit('num1change', this.dnumber1)
// 3.同时修饰dnumber2的值
this.dnumber2 = this.dnumber1 * 100;
this.$emit('num2change', this.dnumber2);
},
num2Input(event) {
this.dnumber2 = event.target.value;
this.$emit('num2change', this.dnumber2)
// 同时修饰dnumber2的值
this.dnumber1 = this.dnumber2 / 100;
this.$emit('num1change', this.dnumber1);
}
}
}
}
})
</script>
</body>
<body>
<div id="app">
<cpn :number1="num1"
:number2="num2"
@num1change="num1change"
@num2change="num2change"/>
</div>
<template id="cpn">
<div>
<h2>props:{{number1}}</h2>
<h2>data:{{dnumber1}}</h2>
<input type="text" v-model="dnumber1">
<h2>props:{{number2}}</h2>
<h2>data:{{dnumber2}}</h2>
<input type="text" v-model="dnumber2">
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
num1: 1,
num2: 0
},
methods: {
num1change(value) {
this.num1 = parseFloat(value)
},
num2change(value) {
this.num2 = parseFloat(value)
}
},
components: {
cpn: {
template: '#cpn',
props: {
number1: Number,
number2: Number,
name: ''
},
data() {
return {
dnumber1: this.number1,
dnumber2: this.number2
}
},
watch: {
dnumber1(newValue) {
this.dnumber2 = newValue * 100;
this.$emit('num1change', newValue);
},
dnumber2(newValue) {
this.number1 = newValue / 100;
this.$emit('num2change', newValue);
}
}
}
}
})
</script>
</body>
父组件访问子组件:使用 c h i l d r e n 或 children或 children或refs
子组件访问父组件:使用$parent
$children的缺陷:
$refs的使用:
this.$refs.ID
就可以访问到该组件了。<child-cpn1 ref="chi1d1"></child-cpn1>
<child-cpn2 ref="chi1d2"></chi1d-cpn2>
<button @click="showRefscpn">通过refs访问子组件</button>
showRefsCpn() {
console.1og(this. $refs.chi1d1.message);
console.log(this. $refs.chi1d2.message);
}
实例
<body>
<div id="app">
<cpn></cpn>
<cpn></cpn>
<my-cpn></my-cpn>
<y-cpn></y-cpn>
<cpn ref="aaa"></cpn>
<button @click="btnClick">按钮</button>
</div>
<template id="cpn">
<div>我是子组件</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊'
},
methods: {
btnClick() {
// 1.$children
// console.log(this.$children);
// for (let c of this.$children) {
// console.log(c.name);
// c.showMessage();
// }
// console.log(this.$children[3].name);
// 2.$refs => 对象类型, 默认是一个空的对象 ref='bbb'
console.log(this.$refs.aaa.name);
}
},
components: {
cpn: {
template: '#cpn',
data() {
return {
name: '我是子组件的name'
}
},
methods: {
showMessage() {
console.log('showMessage');
}
}
},
}
})
</script>
</body>
<body>
<div id="app">
<cpn></cpn>
</div>
<template id="cpn">
<div>
<h2>我是cpn组件</h2>
<ccpn></ccpn>
</div>
</template>
<template id="ccpn">
<div>
<h2>我是子组件</h2>
<button @click="btnClick">按钮</button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊'
},
components: {
cpn: {
template: '#cpn',
data() {
return {
name: '我是cpn组件的name'
}
},
components: {
ccpn: {
template: '#ccpn',
methods: {
btnClick() {
// 1.访问父组件$parent
// console.log(this.$parent);
// console.log(this.$parent.name);
// 2.访问根组件$root
console.log(this.$root);
console.log(this.$root.message);
}
}
}
}
}
}
})
</script>
</body>
<body>
<!--
1.插槽的基本使用 <slot></slot>
2.插槽的默认值 <slot>button</slot>
3.如果有多个值, 同时放入到组件进行替换时, 一起作为替换元素
-->
<div id="app">
<cpn></cpn>
<cpn><span>哈哈哈</span></cpn>
<cpn><i>呵呵呵</i></cpn>
<cpn>
<i>呵呵呵</i>
<div>我是div元素</div>
<p>我是p元素</p>
</cpn>
<cpn></cpn>
<cpn></cpn>
<cpn></cpn>
<cpn></cpn>
</div>
<template id="cpn">
<div>
<h2>我是组件</h2>
<p>我是组件, 哈哈哈</p>
<!-- 设置默认值-->
<slot><button>按钮</button></slot>
<!--<button>按钮</button>-->
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊'
},
components: {
cpn: {
template: '#cpn'
}
}
})
</script>
</body>
<slot name='myslot'></slot>
<body>
<div id="app">
<cpn><span slot="center">标题</span></cpn>
<cpn><button slot="left">返回</button></cpn>
</div>
<template id="cpn">
<div>
<slot name="left"><span>左边</span></slot>
<slot name="center"><span>中间</span></slot>
<slot name="right"><span>右边</span></slot>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊'
},
components: {
cpn: {
template: '#cpn'
}
}
})
</script>
</body>
<body>
<div id="app">
<cpn v-show="isShow"></cpn><!--实例里的isShow-->
<cpn v-for="item in names"></cpn>
</div>
<template id="cpn">
<div>
<h2>我是子组件</h2>
<p>我是内容, 哈哈哈</p>
<button v-show="isShow">按钮</button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊',
isShow: true
},
components: {
cpn: {
template: '#cpn',
data() {
return {
isShow: false
}
}
},
}
})
</script>
</body>
父组件替换插槽的标签,但是内容由子组件来提供。
<template slot-scope="slotProps">
获取到slotProps属性<body>
<!-- 内容在子组件,希望父组件告诉我们如何展示
利用slot作用域插槽就可以了
-->
<div id="app">
<cpn></cpn>
<cpn>
<!--目的是获取子组件中的pLanguages-->
<template slot-scope="slot">
<!--<span v-for="item in slot.data"> - {{item}}</span>-->
<span>{{slot.data.join(' - ')}}</span>
</template>
</cpn>
<cpn>
<!--目的是获取子组件中的pLanguages-->
<template slot-scope="slot">
<!--<span v-for="item in slot.data">{{item}} * </span>-->
<span>{{slot.data.join(' * ')}}</span>
</template>
</cpn>
<!--<cpn></cpn>-->
</div>
<template id="cpn">
<div>
<slot :data="pLanguages">
<ul>
<li v-for="item in pLanguages">{{item}}</li>
</ul>
</slot>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊'
},
components: {
cpn: {
template: '#cpn',
data() {
return {
pLanguages: ['JavaScript', 'C++', 'Java', 'C#', 'Python', 'Go', 'Swift']
}
}
}
}
})
</script>
</body>
(function(){
var flag = true;
})()
但是代码的复用性降低
–
但是我们不使用以下的模块化去使用代码
我们采用的是别人已经规范的模块进行代码
bbb.js
var moduleB = (function () {
var obj = {}
var name = '小红'
var flag = false
console.log(name);
obj.flag = flag
return obj
})()
aaa.js
var moduleA = (function () {
// 导出的对象
var obj = {}
// 小明
var name = '小明'
var age = 22
function sum(num1, num2) {
return num1 + num2
}
var flag = true
if (flag) {
console.log(sum(10, 20));
}
obj.flag = flag;
obj.sum = sum;
return obj
})()
mmm.js
;(function () {
// 1.想使用flag
if (moduleA.flag) {
console.log('小明是天才, 哈哈哈');
}
// 2.使用sum函数
console.log(moduleA.sum(40, 50));
})()
a.js
//小明
var name = '小明'
var age = 22
function sum(num1, num2) {
return num1 + num2
}
var flag = true
if (flag) {
console.log(sum(10, 20));
}
//必须有webpack低沉来解析这个代码
module.exports = {
flag: flag,
sum: sum
}
b.js
//导入
var aaa = require('./aaa.js')
var flag = aaa.flag;
var sum = aaa.sum;
//上面三步等于下面一步
var {flag, sum} = require('./aaa.js')
sum(20, 30)
// 1.导入的{}中定义的变量
import {flag, sum} from "./aaa.js";
if (flag) {
console.log('小明是天才, 哈哈哈');
console.log(sum(20, 30));
}
// 2.直接导入export定义的变量
import {num1, height} from "./aaa.js";
console.log(num1);
console.log(height);
// 3.导入 export的function/class
import {mul, Person} from "./aaa.js";
console.log(mul(30, 50));
const p = new Person();
p.run()
// 4.导入 export default中的内容
import addr from "./aaa.js";
addr('你好啊');
// 5.统一全部导入
// import {flag, num, num1, height, Person, mul, sum} from "./aaa.js";
import * as aaa from './aaa.js'
console.log(aaa.flag);
console.log(aaa.height);
index.html
里<script src="aaa.js" type="module"></script>
设置type="module"
则定义aaa.js为一个模块(闭包)。export
aaa.js
var name = '小明'
var age = 18
var flag = true
function sum(num1, num2) {
return num1 + num2
}
if (flag) {
console.log(sum(20, 30));
}
// 1.导出方式一:
export {
flag, sum
}
// 2.导出方式二:
export var num1 = 1000;
export var height = 1.88
// 3.导出函数/类
export function mul(num1, num2) {
return num1 * num2
}
export class Person {
run() {
console.log('在奔跑');
}
}
// 5.export default
// const address = '北京市'
// export {
// address
// }
// export const address = '北京市'
//只能有一个default*2*
// const address = '北京市'
// export default address
export default function (argument) {
console.log(argument);
}
bbb.js
import {sum} from './aaa.js'
var name = '小红'
var flag = false
console.log(sum(100, 200));
npm init
用于创建package.json
文件--save-dev
是开发时依赖,项目打包后不需要继续使用的(本地的webpack,局部的)一个项目往往依赖特定的webpack版本,全局的版本可能很这个项目的webpack版本不一致,导出打包出现问题,所以通常一个项目,都有自己局部的webpack。
package.json
中的script
中定义"build":"webpack"
就可以使用npm run build
相当于在终端使用webpack
npm run server
同理webpack
都是全局的,但如果在package.json
中配置脚本script
则优先在本地找webpack
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack"
},
./node_module/.bin/webpack 名
来执行npm install webpack@版本号 --save-dev
{
test: /\.(png|jpg|gif|jpeg)$/,
use: [
{
loader: 'url-loader',
options: {
// 当加载的图片, 小于limit时, 会将图片编译成base64字符串形式.
// 当加载的图片, 大于limit时, 需要使用file-loader模块进行加载.
limit: 13000,
name: 'img/[name].[hash:8].[ext]'
},
}
]
},
将ES6转化为ES5:因为很多浏览器不能识别ES6
npm install --save-dev babel-loader@7 babel-core babel-preset-es2015
module: {
rules: [
{
test: /\.js$/,
//排除
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['es2015']
}
}
}
]
}
npm i vue --save
-dev
是因为整个项目都需要用Vue,运行时也需要依赖,故不需要开发依赖npm run build
出现Error
ERROR:[Vue warn]: You are using the runtime-only build of Vue where the template compiler is rotavailable. Either pre-compile the templates into render functions,or use the compiler-includc( found in <Root>)
原因:打包Vue会出现两个情况:
<div id = 'app'></div>
相当于template
resolve: {
//alias:别名的意思
alias: {
//指向一个具体的文件夹vue.esm.js报含runtime-complier
'vue$ ': 'vue/dist/vue.esm.js'
}
},
1.新的形式
import Vue from 'vue'
//这个形式也可以,不用 const app =new Vue(...)
new Vue({
el: '#app',
data:{
message:'hello'
}
})
2.template
需求:
new Vue({
el: '#app' ,
template: '<div id="app">{message}</div>',
data: {
message: 'leng'
}
})
1.el用于指定Vue要管理的DOM,可以帮助解析其中的指令、事件监听等等,而如果Vue实例中同时指定了template,那么template模板的内容会替换掉挂载的对应el的模板。
2.将template模板中的内容进行抽离,会分成三部分书写:template、script、style,结构变得非常清晰。
const App = {
template: `
<div>
<h2>{{message}}</h2>
<button @click="btnClick">按钮</button>
<h2>{{name}}</h2>
</div>
`,
data() {
return {
message: 'Hello Webpack',
name: 'leng'
}
},
methods: {
btnClick() {
}
}
}
new Vue({
el: '#app',
template: '<App/>',
components: {
App
}
})
app.js
export default {
template: `
<div>
<h2>{{message}}</h2>
<button @click="btnClick">按钮</button>
<h2>{{name}}</h2>
</div>
`,
data() {
return {
message: 'Hello Webpack',
name: 'leng'
}
},
methods: {
btnClick() {
}
}
main.js
//引用vue文件下的app.js
import App from './vue/app.js'
App.vue
<template>
<div>
<h2 class="title">{{message}}</h2>
<button @click="btnClick">按钮</button>
<h2>{{name}}</h2>
<Cpn/>
</div>
</template>
<script>
import Cpn from './Cpn'
export default {
name: "App",
components: {
Cpn
},
data() {
return {
message: 'Hello Webpack',
name: 'leng'
}
},
methods: {
btnClick() {
}
}
}
</script>
<!--可以定义样式-->
<style scoped>
.title {
color: green;
}
</style>
main.js
// import App from './vue/app'
import App from './vue/App.vue'
npm install vue-loader vue-template-compiler --save-dev
//webpack.config.js
{
test: /\.vue$/,
use: ['vue-loader']
}
"vue-loader":^13.0.0
到指定版本13.0.0npm install
Cpn.vue
import Cpn from './Cpn.js'
.js
需要更改webpack.config.js的配置,如果使用脚手架就不用这种操作resolve: {
// alias: 别名
//如果想省略`.js`需要更改webpack.config.js的配置
extensions: ['.js', '.css', '.vue'],
//配置路径与该内容点无关
alias: {
'vue$': 'vue/dist/vue.esm.js'
}
}
Cpn.vue
<template>
<div>
<h2>我是cpn组件的标题</h2>
<p>我是cpn组件的内容, 哈哈哈</p>
<h2>{{name}}</h2>
</div>
</template>
<script>
export default {
name: "Cpn",
data() {
return {
name: 'CPN组件的name'
}
}
}
</script>
<style scoped>
</style>
App.vue
<template>
<div>
<h2 class="title">{{message}}</h2>
<button @click="btnClick">按钮</button>
<h2>{{name}}</h2>
<Cpn/>
</div>
</template>
<script>
import Cpn from './Cpn'
export default {
name: "App",
components: {
Cpn
},
data() {
return {
message: 'Hello Webpack',
name: 'leng'
}
},
methods: {
btnClick() {
}
}
}
</script>
<!--可以定义样式-->
<style scoped>
.title {
color: green;
}
</style>
webpack.config.js
中的plugins中配置插件。const path = require( ' path')
const webpack = require( 'webpack')
module.exports = {
//...
plugins: [
new webpack.BannerPlugin('最终版权归***所有')
]
npm install html-webpack-plugin --save-dev
(@3.2.0)plugins: [
new webpack.BannerPlugin('最终版权归***所有'),
new HtmlWebpackPlugin({
template: 'index.html'
}),
]
uglifyjs-webpack-plugin
,并且版本号指定1.1.1
,和CLI2
保持一致npm install uglifyjs-webpack-plugin@1.1.1 --save-dev
const path = require( ' path ')
const webpack = require( 'webpack ' )
const uglifyJsPlugin = require( 'uglifyjs-webpack-plugin ')
module.exports = {
plugins: [
new webpack.BannerPlugin('最终版权归***所有')
//开发阶段一般不需要丑化,运行阶段才需要
new uglifyJsPlugin()
]
}
npm install --save-dev webpack-dev-server@2.9.1
(对应脚手架2使用的版本)npm install --save-dev webpack-dev-server@3.10.3
(对应webpack@4.42.0使用的版本)--open
参数表示直接打开浏览器
"dev" : "webpack-dev-server --open"
webpack-dev-server
跑不起来,是因为server是局部安装的,这条命令是访问全局
./node_modules/.bin/webpack-dev-server
"dev":"webpack-dev-server"
plugins: [
new webpack.BannerPlugin('最终版权归aaa所有'),
new HtmlWebpackPlugin({
template: 'index.html'
}),
new UglifyjsWebpackPlugin()
],
devServer: {
//用于服务的当前文件夹
contentBase: './dist',
inline: true
}
webpack.config.js
中有些配置是开发环境需要,有些配置是运行环境需要,所以我们可以进行配置分离,高效使用代码。base.config.js``dev.config.js``prod.config.js
文件后需要安装npm install webpack-merge --save-dev
将base.config.js
和prod.config.js
合并在一起
webpack-merge@4.1.5
创建base.config.js
文件
webpack.config.js
中的文件全部复制到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'
})
]
}
创建dev.config.js
文件
const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin')
const webpackMerge = require('webpack-merge')
const baseConfig = require('./base.config')
module.exports = webpackMerge(baseConfig, {
plugins: [
new UglifyjsWebpackPlugin()
]
})
创建prod.config.js
文件
const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin')
const webpackMerge = require('webpack-merge')
//导入
const baseConfig = require('./base.config')
//将base 和 prod进行了合并
module.exports = webpackMerge(baseConfig, {
plugins: [
new UglifyjsWebpackPlugin()
]
})
需要配置文件
base.config.js``dev.config.js``prod.config.js
文件后需要安装npm install webpack-merge --save-dev
将base.config.js
和prod.config.js
合并在一起。const webpackMerge = require('webpack-merge')
ERROR_01:
webpack.config.js
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
//增添 --config ./文件路径
"build": "webpack --config ./build/prod.config.js",
"dev": "webpack-dev-server --open --config ./build/dev.config.js"
},
ERROR_02:
path:path.resolve(__dirname,'dist')
指的是当前所在目录的dist文件夹,base在build,故就在build文件夹中创建了打包文件dist文件path:path.resolve(__dirname,'../dist')
指当前的上一层目录进行创建以上内容,脚手架会自动配置
使用Vue.js开发大型应用时,我们需要考虑代码目录结构、项目结构和部署、热加载、代码单元测试等事情。
如果每个项目都要手动完成这些工作,那无以效率比较低效,所以通常我们会使用一些脚手架工具来帮助完成这些事情。
什么是CLI?
使用CLI的前提
由于国内直接使用 npm 的官方镜像是非常慢的,这里推荐使用淘宝 NPM 镜像。
你可以使用淘宝定制的 cnpm (gzip 压缩支持) 命令行工具代替默认的 npm:
npm install -g cnpm --registry=https://registry.npm.taobao.org
这样就可以使用 cnpm 命令来安装模块了:cnpm install [name]
也可以使用代理registry:
npm config set registry https://registry.npm.taobao.org
后可以直接使用npm
node -v; npm -v;
npm install webpack -g
npm install -g @vue/cli
vue --version
检查是否安装成功vue init webpack my-project
vue create my-project
拉取 VueCLI 2.x 模板 (如果要用旧版本就需要npm以下内容)
npm install -g @vue/cli-init
// `vue init` 的运行效果将会跟 `vue-cli@2.x` 相同
vue init webpack my-project
Vue/cli安装不成功
C:\Users\12987\AppData\Roaming\npm-cache删除npm-cache
或者cmd:npm clean cache -force
然后重新安装npm
vue init webpack 项目名称
=>vue init webpack vuecli2test
? Author Noerrorist <1298711060@qq.com>
默认选取git中的信息,可以在gitconfig中更改? Use ESLint to lint your code? No
你选择为yesSetup e2e tests with Nightwatch? (Y/n)
安装Nightwatch,是一个利用selenium或webdriver或phantomjs等进行自动化测试的框架
"scripts": {--progress监听进度
//
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
"start": "npm run dev",
"build": "node build/build.js"
},
将runtime-compiler中的main.js改掉一样可以变成runtime-only
import Vue from 'vue'
import App from './App'
Vue.config.productionTip = false
/* eslint-disable no-new */
// const cpn = {
// template: '<div>{{message}}</div>',
// data() {
// return {
// message: '我是组件message'
// }
// }
// }
new Vue({
el: '#app',
render: function (createElement) {
// 1.普通用法: createElement('标签', {标签的属性}, [''])
//将el:'#app'给替换了,即也将index.html中的div替换成了h2标签
// return createElement('h2',
// {class: 'box'},
// ['Hello World', createElement('button', ['按钮'])])
// 2.传入组件对象:
return createElement(App)
}
})
// runtime-compiler(v1)
// template -> ast -> render -> vdom -> UI
// runtime-only(v2)(1.性能更高 2.下面的代码量更少)
// render -> vdom -> UI
resolve: {
extensions: ['.js ', '.vue ', '.json ' ],
alias: {
'@': resolve ( 'src '),
'pages' : resolve( 'src/pages '),' common' : resolve( 'src / common '),
'components': resolve( 'src/ components ' ),
'network ': resolve( 'src/network ')
}
},
vue-cli 3 与 2 版本有很大区别
? Where do you prefer placing config for Babel, ESLint,etc.? (Use arrow keys).
? Save this as a preset for future projects? (y/N) //是否新建一个选项作为工程
.vuerc
文件部分内容注释掉{
{
"useTaobaoRegistry": false,
"presets": {
// "leng": {
// "useConfigFiles": true,
// "plugins": {
// "@vue/cli-plugin-babel": {}
// },
// "vueVersion": "3"
}
}
}
Git 存储库“d:\Desktop\My Files\RecentlyStudy\VUE\Vue.js\demo\Day06\ClassCode\LearnVuejs06\testvuecli3”中存在大量活动更改,将仅启用部分 Git 功能。 是否要将“node_modules”添加到 .gitignore?
package-lock.json: For performance reasons, document symbols have been limited to 5000 items.
Use setting ‘json.maxItemsComputed’ to configure the limit.
vue.config.js
vue ui
箭头函数的基本使用
<body>
<script>
// 箭头函数: 也是一种定义函数的方式
// 1.定义函数的方式: function
const aaa = function () {
}
// 2.对象字面量中定义函数
const obj = {
bbb() {
}
}
// 3.ES6中的箭头函数
// const ccc = (参数列表) => {
//
// }
const ccc = () => {
}
</script>
</body>
箭头函数的返回值
<body>
<script>
// 1.参数问题:
// 1.1.放入两个参数
const sum = (num1, num2) => {
return num1 + num2
}
// 1.2.放入一个参数
const power = num => {
return num * num
}
// 2.函数中
// 2.1.函数代码块中有多行代码时
const test = () => {
// 1.打印Hello World
console.log('Hello World');
// 2.打印Hello Vuejs
console.log('Hello Vuejs');
}
// 2.2.函数代码块中只有一行代码
// const mul = (num1, num2) => {
// return num1 + num2
// }
const mul = (num1, num2) => num1 * num2
console.log(mul(20, 30));
// const demo = () => {
// console.log('Hello Demo');
// }
const demo = () => console.log('Hello Demo')
console.log(demo()); // undefined
</script>
</body>
//箭头函数中的this总时指向函数定义生效时所在的对象({id:52})
//console输出函数为52
function foo(){
setTimeout(()=>{
console.log('id',this,id);
},100)
}
var id=21;
foo.call({id:52});
箭头函数中this的使用
<body>
<script>
// 什么时候使用箭头
// setTimeout(function () {
// console.log(this);
// }, 1000)
//
// setTimeout(() => {
// console.log(this);
// }, 1000)
//以上打印出都是window
// 问题: 箭头函数中的this是如何查找?
// 答案: 向外层作用域中, 一层层查找this, 直到有this的定义.
// const obj = {
// aaa() {
// setTimeout(function () {
// console.log(this); // window
// })
//
// setTimeout(() => {
// console.log(this); // obj对象
// })
// }
// }
//
// obj.aaa()
const obj = {
aaa() {
setTimeout(function () {
setTimeout(function () {
console.log(this); // window
})
setTimeout(() => {
console.log(this); // window
})
})
setTimeout(() => {
setTimeout(function () {
console.log(this); // window
})
setTimeout(() => {
console.log(this); // obj
})
})
}
}
obj.aaa()
</script>
</body>
使用脚手架 Vue CLI2
路由(routing)就是通过互联的网络把信息从源地址传输到目的地址的活动
面试:什么是前端渲染,什么是后端渲染?
目的:改变url 但是页面不刷新
可以在控制台进行验证
hash
URL的hash
URL的hash也就是锚点(#), 本质上是改变window.location的href属性.
可以通过直接赋值location.hash
来改变href, 但是页面不发生刷新.
eg:location.hash=‘aaa’
HTML5的history模式:pushState 和 history.replaceState() 和 go
history接口是HTML5新增的, 它有五种模式改变URL而不刷新页面.
history.pushState()
eg:history.pushState({},’’,‘home’)//入栈
history.back() //弹栈
history.replaceState()
eg:history.replaceState({},’’,‘home’)//是替换,不能返回
history.go()
history.back() 等价于 history.go(-1)
history.forward() 则等价于 history.go(1)
这三个接口等同于浏览器界面的前进后退。
安装 Vue-Router
npm install vue-router --save
使用 vue-router的步骤:
<router-link>
和<router-view>
在没有配置Vue-Router的情况下配置路由
在当前路径下创建路由文件./src/router
在当前路径下创建index文件./src/router/index.js
./src/router/index.js
配置路由相关信息./src/router/index.js
// 配置路由相关的信息
//VueRouter就是一个插件
import VueRouter from 'vue-router'
import Vue from 'vue'
import Home from '../components/Home'
import About from '../components/About'
// 1.通过Vue.use(插件), 安装插件
Vue.use(VueRouter)
// 2.创建VueRouter对象
const routes = [
{
path: '',
// redirect重定向
redirect: '/home'
},
{
path: '/home',
component: Home
},
{
path: '/about',
component: About
}
]
const router = new VueRouter({
// 配置路由和组件之间的映射关系
routes,
mode: 'history',
//用于<router-link>处理active-class
linkActiveClass: 'active'
})
// 3.将router对象传入到Vue实例(导出)
export default router
main.js
//挂载到Vue实例中
import Vue from 'vue'
import App from './App'
//import router from './router/index.js'自动导入index.js
import router from './router'
Vue.config.productionTip = false
new Vue({
el: '#app',
router,
render: h => h(App)
})
./src/components
配置路由相关信息,创建路由实例 vscode模板快捷键在vue后缀文件中输入vue
Home.vue
<template>
<div>
<h2>我是首页</h2>
</div>
</template>
<script>
export default {
name: "Home"
}
</script>
<style>
</style>
About.vue
<template>
<div>
<h2>我是关于</h2>
</div>
</template>
<script>
export default {
name:"About"
}
</script>
<style>
</style>
App.vue
配置路由相关信息App.vue
<template>
<div id="app">
<!-- <router-view></router-view> -->
<!-- //vuerouter 定义 注册的组件,是全局的,被渲染成<a> -->
<router-link to="/home">首页</router-link>
<router-link to="/about">关于</router-link>
<!-- //router-view路劲发生改变 进行渲染 相当于占位 在上方也可以 -->
<router-view></router-view>
</div>
<router-link>
: 该标签是一个vue-router中已经内置的组件, 它会被渲染成一个<a>
标签.
<router-view>
: 该标签会根据当前的路径, 动态渲染出不同的组件.
网页的其他内容, 比如顶部的标题/导航, 或者底部的一些版权信息等会和<router-view>
处于同一个等级.
在路由切换时, 切换的是<router-view>
挂载的组件, 其他内容不会发生改变.
如何可以让路径默认跳到到首页, 并且<router-view>
渲染首页组件
const routes = [
{
path: '',
// redirect重定向
redirect: '/home'
},
默认是hash模式
const router = new VueRouter({
// 配置路由和组件之间的映射关系
routes,
mode: 'history',
linkActiveClass: 'active'
})
<router-link>
补充
tag: tag可以指定<router-link>
之后渲染成什么组件, 比如上面的代码会被渲染成一个<li>
元素, 而不是<a>
<router-link to='/home' tag='button'>
replace: replace不会留下history记录, 所以指定replace的情况下, 后退键返回不能返回到上一个页面中
active-class: 当<router-link>
对应的路由匹配成功时, 会自动给当前元素设置一个router-link-active
的class
, 设置active-class
可以修改默认的名称.
exact-active-class
const router = new VueRouter({
// 配置路由和组件之间的映射关系
routes,
mode: 'history',
//用于<router-link>处理active-class
linkActiveClass: 'active'
})
在进行高亮显示的导航菜单或者底部tabbar
时, 会使用到该类.
但是通常不会修改类的属性, 会直接使用默认的router-link-active
即可.
通过代码跳转路由
App.vue
<template>
<div id="app">
<!-- <router-view></router-view> -->
<!-- //vue-router 定义 注册的组件,是全局的,被渲染成<a> -->
<!-- <router-link to="/home">首页</router-link>
<router-link to="/about">关于</router-link> -->
<!-- //router-view路劲发生改变 进行渲染 相当于占位 在上方也可以 -->
<router-view></router-view>
<button @click="homeClick">首页</button>
<button @click="aboutClick">关于</button>
</div>
</template>
<script>
export default{
name:'App',
methods:{
homeClick(){
// //相当于绕过了vue-router去做,不可取
// history.pushState();
// this.$router来自vuerouter源码中的
this.$router.push('/home');
// this.$router.repalce('/home');
console.log('homeClick');
},
aboutClick(){
this.$router.push('/about');
// this.$router.repalce('/about');
console.log('aboutClick');
}
}
}
</script>
动态路由
动态路由创建步骤
./src/components/User.vue
./src/router/index.js
import User from '../components/User.vue'
{
path: '/user/:userID',
component: User
}
App.vue
<router-link to = "/user/zhangsan">用户</router-link>
<router-view></router-view>
动态路由 动态绑定创建步骤
./src/components/User.vue
./src/router/index.js
import User from '../components/User.vue'
{
path: '/user/:userID',
component: User
}
App.vue
<template>
<div id="app">
<router-link to = "/home">首页 </router-link>
<router-link to = "/about">关于 </router-link>
<router-link v-bind:to = "'/user/'+userID">用户</router-link>
<router-view></router-view>
</div>
</template>
<script>
export default{
name:'App',
data(){
return{
userID:'lisi',
}
}
}
</script>
<style>
</style>
在动态绑定后,点击及展现userID的信息
User.vue
<template>
<div>
<h2>我是用户界面</h2>
<h2>{{userID}}</h2>
<h2>{{$route.params.userID}}</h2>
</div>
</template>
<script>
export default {
name:"User",
computed:{
userID(){
// .$route表示当前哪个路由处于活跃状态就是那一个路由
//userID指的是/component/index.js配置的userID
return this.$route.params.userID
}
}
}
</script>
<style>
</style>
vue-router 打包文件的解析
路由的懒加载
const routes = [
{
path: '/home',
component:() => import( '../components/Home')
},
{
path: '/about',
component:() => import('../components/About.vue')
},
];
懒加载的三种方式
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')
./src/router/index.js
:(建议使用这种方式)// 配置路由相关的信息
//VueRouter就是一个插件
import VueRouter from 'vue-router'
import Vue from 'vue'
// import Home from '../components/Home'
// import About from '../components/About'
// import User from '../components/User.vue'
const Home=()=>import('../components/Home.vue')
const About=()=>import('../components/About.vue')
const User=()=>import('../components/User.vue')
// 1.通过Vue.use(插件), 安装插件
Vue.use(VueRouter)
// 2.创建VueRouter对象
const routes = [
//配置路由映射
{
path: '',
// redirect重定向
redirect: '/home'
},
{
path: '/home',
component:Home
},
{
path: '/about',
component:About
},
{
path: '/user/:userID',
component: User
}
]
const router = new VueRouter({
// 配置路由和组件之间的映射关系
routes,
mode: 'history',
linkActiveClass: 'active'
})
// 3.将router对象传入到Vue实例(导出)
export default router
文件中的map文件只用来输出错误信息(追踪错误),不要此文件可以修改 `config/index.js` 中的`productionSourceMap:false` ./src/components/HomeNews.vue
组件./src/components/HomeMessage.vue
组件./router/index.js
const Home=() => import('../components/Home.vue')
const About=() => import('../components/About.vue')
const HomeMessage=() => import('../components/HomeMessage.vue')
const HomeNews=() => import('../components/HomeNews.vue')
//在之前的基础上添加children属性进行配置
{
path: '/home',
component:Home,
children:[
{
//路由默认路径
path: '',
// redirect重定向
redirect: 'news'
},
{
//子路由不用加/=》'/news'
path:'news',
component:HomeNews
},
{
path:'message',
component:HomeMessage
}
]
},
.src/components/Home.vue
<template>
<div>
<h2>我是首页</h2>
<!-- 用于子组件路由嵌套:router-view -->
<router-link to="/home/news">HomeNews</router-link>
<router-link to="/home/message">HomeMessage</router-link>
<router-view></router-view>
</div>
</template>
创建一个组件, 并且将其配置好 Profile相当于用户档案:= Mine
传递参数主要有两种类型: params和query
Profile.vue
<template>
<div>
<h2>Profile组件</h2>
<h2>{{$route.query}}</h2>
<h2>{{$route.query.name}}</h2>
</div>
</template>
index.js
App.vue
<router-link :to = "{path:'/profile',
query:{name:'why'}}"
>档案</router-link>
将<router-link>
改为<button>
且重构<router-link v-bind:to = "'/user/'+userID">用户</router-link>
使用代码进行跳转
<template>
<div id="app">
<!-- <router-view></router-view> -->
<!-- //vuerouter 定义 注册的组件,是全局的,被渲染成<a> -->
<!-- <router-link to="/home">首页</router-link>
<router-link to="/about">关于</router-link> -->
<!-- //router-view路劲发生改变 进行渲染 相当于占位 在上方也可以 -->
<!-- <router-view></router-view> -->
<!-- <button @click="homeClick">首页</button>
<button @click="aboutClick">关于</button> -->
<router-link to = "/home">首页 </router-link>
<router-link to = "/about">关于 </router-link>
<!-- <router-link v-bind:to = "'/user/'+userID">用户</router-link> -->
<!-- <router-link to = "/profile">档案</router-link> -->
<!-- to="{}"输出的是字符串{},故要加上动态绑定":" -->
<!-- <router-link :to = "{path:'/profile',query:{name:'why'}}">档案</router-link> -->
<button @click="userClick">用户</button>
<button @click="profileClick">档案</button>
<router-view></router-view>
</div>
</template>
<script>
export default{
name:'App',
data(){
return{
userID:'lisi',
}
},
methods:{
homeClick(){
// //相当于绕过了vue-router去做,不可取
// history.pushState();
// this.$router来自vuerouter源码中的
this.$router.push('/home');
// this.$router.repalce('/home');
console.log('homeClick');
},
aboutClick(){
this.$router.push('/about');
// this.$router.repalce('/about');
console.log('aboutClick');
},
userClick(){
this.$router,push('/user'+this.userID)
},
profileClick(){
this.$router,push({
path:'/profile',
query:{
name:'kobe',
age:19,
height:1.87
}
})
}
}
}
</script>
<style>
</style>
r o u t e 和 route和 route和router的区别
前者用于当前活跃的路由,后者用于全局
r o u t e r 为 V u e R o u t e r 实 例 , 想 要 导 航 到 不 同 U R L , 则 使 用 router为VueRouter实例,想要导航到不同URL,则使用 router为VueRouter实例,想要导航到不同URL,则使用router.push方法
r o u t e 为 当 前 r o u t e r 跳 转 对 象 里 面 可 以 获 取 n a m e 、 p a t h 、 q u e r y 、 p a r a m s 等 < i m a g e s r c = " . / i m a g M d / route为当前router跳转对象里面可以获取name、path、query、params等 <image src="./imagMd/ route为当前router跳转对象里面可以获取name、path、query、params等<imagesrc="./imagMd/route和$router区别.png">
需求:
网页标题是通过<title>
来显示的, 但是SPA只有一个固定的HTML, 切换不同的页面时, 标题并不会改变.
但是我们可以通过JavaScript来修改<title>
的内容.window.document.title = '新的标题'
方法一:改变每一个文件的配置
以home.vue
为例创建document.title='首页';
<script>
export default {
name: "Home",
data(){
return{
message:"你好"
}
},
created(){
console.log("created");
document.title='首页';
},
//挂载在DOM上回回调
mounted(){
console.log("mounted");
},
updated(){
console.log("updated");
}
}
</script>
方法二:监听每个路由跳转的过程 使用全局导航守卫
导航钩子beforeEach的三个参数解析:
to: 即将要进入的目标的路由对象.
from: 当前导航即将要离开的路由对象.
next: 调用该方法后, 才能进入下一个钩子.
修改/router/index.js
创建meta
属性
创建函数并配置router.beforeEach()
嵌套路由:document.title=to.meta.title;
改为document.title=to.matched[0].meta.title;
const routes = [
...
{
path: '/about',
component:About,
meta:{
title:'首页'
}
},
{
path: '/user/:userID',
component: User,
meta:{
title:'用户'
}
},
{
path:'/profile',
component:Profile,
meta:{
title:'档案'
}
}
]
const router = new VueRouter({
// 配置路由和组件之间的映射关系
routes,
mode: 'history',
linkActiveClass: 'active'
})
router.beforeEach((to,from,next)=>{
//document.title=to.meta.title;
document.title=to.matched[0].meta.title;
//to: 即将要进入的目标的路由对象.
console.log(to);
next()
})
// 3.将router对象传入到Vue实例(导出)
export default router
导航守卫补充
路由独享的守卫.
组件内的守卫.
更多查看:
网址:https://router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E8%B7%AF%E7%94%B1%E7%8B%AC%E4%BA%AB%E7%9A%84%E5%AE%88%E5%8D%AB
<keep-alive exclude="name1,name2,..."></keep-alive>
<keep-alive>
<router-view>
<! --所有路径匹配到的视图组件都会被缓存!-->
</router-view>
</keep-alive>
/router/index.js
中的部分配置children:[
// {
// path: '',
// // redirect重定向
// redirect: 'news'
// },
...
]
home.vue
<script>
export default {
name: "Home",
data(){
return{
message:"你好",
path:"/home/news"
}
},
created(){
console.log("home created");
// document.title='首页';
},
destroyed(){
console.log("home destroyed");
},
// 这两个函数, 只有该组件被保持了状态使用了keep-alive时, 才是有效的
activated() {
this.$router.push(this.path);
console.log('activated');
},
deactivated() {
console.log('deactivated');
},
beforeRouteLeave (to, from, next) {
console.log(this.$route.path);
this.path = this.$route.path;
next();
}
// //挂载在DOM上回回调
// mounted(){
// console.log("mounted");
// },
// updated(){
// console.log("updated");
// }
}
activated/deactivated在keep-alive中使用
App.vue
<template>
<div id="app">
<tab-bar>
<tab-bar-item>
<img slot="item-icon" src="./assets/img/tabbar/home.svg" alt="">
<div slot="item-text">首页</div>
</tab-bar-item>
<tab-bar-item>
<img slot="item-icon" src="./assets/img/tabbar/category.svg" alt="">
<div slot="item-text">分类</div>
</tab-bar-item>
<tab-bar-item>
<img slot="item-icon" src="./assets/img/tabbar/shopcart.svg" alt="">
<div slot="item-text">购物车</div>
</tab-bar-item>
<tab-bar-item>
<img slot="item-icon" src="./assets/img/tabbar/profile.svg" alt="">
<div slot="item-text">我的</div>
</tab-bar-item>
</tab-bar>
</div>
</template>
<script>
import TabBar from './components/tabbar/TabBar.vue'
import TabBarItem from './components/tabbar/TabBarItem.vue'
export default {
name: 'App',
components:{
TabBar,
TabBarItem
}
}
</script>
<style>
@import url('./assets/css/base.css');
</style>
TarBar.vue
<template>
<div id="tab-bar">
<slot></slot>
</div>
</template>
<script>
export default {
name:"TabBar"
}
</script>
<style>
#tab-bar{
display: flex;
background-color: #f6f6f6;
position: fixed;
left: 0px;
right: 0px;
bottom: 0px;
box-shadow: 0px -2px 1px rgba(100,100,100,.1);
}
</style>
TabBatItem.vue
<template>
<div class="tab-bar-item">
<slot name="item-icon"></slot>
<slot name="item-text"></slot>
<!-- <img src="../../assets/img/tabbar/home.svg" alt="">
<div>首页</div> -->
</div>
</template>
<script>
export default {
}
</script>
<style>
.tab-bar-item{
flex: 1;
text-align: center;
height: 49px;
font-size: 12px;
}
.tab-bar-item img{
margin-top: 3px;
width: 24px;
height: 24px;
vertical-align: middle;
}
</style>
TabBatItem.vue
<template>
<div class="tab-bar-item">
<slot v-if="!isActive" name="item-icon"></slot>
<slot v-else name="item-icon-active"></slot>
<div :class="{active:isActive} "><slot name="item-text"></slot></div>
<!-- <img src="../../assets/img/tabbar/home.svg" alt="">
<div>首页</div> -->
</div>
</template>
<script>
export default {
name:"TabBarItem",
data(){
return{
isActive:true
}
}
}
</script>
<style>
.tab-bar-item{
flex: 1;
text-align: center;
height: 49px;
font-size: 12px;
}
.tab-bar-item img{
margin-top: 3px;
width: 24px;
height: 24px;
vertical-align: middle;
}
.active{
color: red;
}
</style>
<router-view>
组件TabBatItem.vue
<template>
<div class="tab-bar-item" @click="itemClick">
<slot v-if="!isActive" name="item-icon" ></slot>
<slot v-else name="item-icon-active"></slot>
<div :class="{active:isActive} "><slot name="item-text"></slot></div>
<!-- <img src="../../assets/img/tabbar/home.svg" alt="">
<div>首页</div> -->
</div>
</template>
<script>
export default {
name:"TabBarItem",
props:{
path:String
},
data(){
return{
isActive:true
}
},
methods:{
itemClick(){
// console.log("yes");
this.$router.replace(this.path);
}
}
}
App.vue
<template>
<div id="app">
<tab-bar>
<tab-bar-item path="/home">
<img slot="item-icon" src="./assets/img/tabbar/home.svg" alt="">
<img slot="item-icon-active" src="./assets/img/tabbar/home_active.svg" alt="">
<div slot="item-text">首页</div>
</tab-bar-item>
<tab-bar-item path="/category">
<img slot="item-icon" src="./assets/img/tabbar/category.svg" alt="">
<img slot="item-icon-active" src="./assets/img/tabbar/category_active.svg" alt="">
<div slot="item-text">分类</div>
</tab-bar-item>
<tab-bar-item path="/cart">
<img slot="item-icon" src="./assets/img/tabbar/shopcart.svg" alt="">
<img slot="item-icon-active" src="./assets/img/tabbar//shopcart_active.svg" alt="">
<div slot="item-text">购物车</div>
</tab-bar-item>
<tab-bar-item path="/profile">
<img slot="item-icon" src="./assets/img/tabbar/profile.svg" alt="">
<img slot="item-icon-active" src="./assets/img/tabbar/profile_active.svg" alt="">
<div slot="item-text">我的</div>
</tab-bar-item>
</tab-bar>
<router-view></router-view>
</div>
</template>
<script>
import TabBar from './components/tabbar/TabBar.vue'
import TabBarItem from './components/tabbar/TabBarItem.vue'
export default {
name: 'App',
components:{
TabBar,
TabBarItem
}
}
</script>
<style>
@import url('./assets/css/base.css');
</style>
TabBatItem.vue
<script>
export default {
name:"TabBarItem",
props:{
path:String
},
data(){
return{
// isActive:true
}
},
computed:{
isActive(){
//拿到处于活跃路由的路径
return this.$route.path.indexOf(this.path)!==-1;
}
},
methods:{
itemClick(){
// console.log("yes");
this.$router.replace(this.path);
}
}
}
</script>
TabBatItem.vue
<template>
<div class="tab-bar-item" @click="itemClick">
<slot v-if="!isActive" name="item-icon" ></slot>
<slot v-else name="item-icon-active"></slot>
<div :style="activeStyle"><slot name="item-text"></slot></div>
<!-- <img src="../../assets/img/tabbar/home.svg" alt="">
<div>首页</div> -->
</div>
</template>
<script>
export default {
name:"TabBarItem",
props:{
path:String,
activeColor:{
type:String,
default:'red'
}
},
data(){
return{
// isActive:true
}
},
computed:{
isActive(){
//拿到处于活跃路由的路径
return this.$route.path.indexOf(this.path)!==-1;
},
activeStyle(){
//如果不处于活跃状态就返回一个空对象
return this.isActive ? {color:this.activeColor} : {}
}
},
methods:{
itemClick(){
// console.log("yes");
this.$router.replace(this.path);
}
}
}
</script>
<style>
.tab-bar-item{
flex: 1;
text-align: center;
height: 49px;
font-size: 12px;
}
.tab-bar-item img{
margin-top: 3px;
width: 24px;
height: 24px;
vertical-align: middle;
}
/* .active{
color: red;
} */
</style>
App.vue
<template>
<div id="app">
<tab-bar>
<tab-bar-item path="/home" activeColor="blue">
<img slot="item-icon" src="./assets/img/tabbar/home.svg" alt="">
<img slot="item-icon-active" src="./assets/img/tabbar/home_active.svg" alt="">
<div slot="item-text">首页</div>
</tab-bar-item>
<tab-bar-item path="/category" activeColor="blue">
<img slot="item-icon" src="./assets/img/tabbar/category.svg" alt="">
<img slot="item-icon-active" src="./assets/img/tabbar/category_active.svg" alt="">
<div slot="item-text">分类</div>
</tab-bar-item>
<tab-bar-item path="/cart" activeColor="blue">
<img slot="item-icon" src="./assets/img/tabbar/shopcart.svg" alt="">
<img slot="item-icon-active" src="./assets/img/tabbar//shopcart_active.svg" alt="">
<div slot="item-text">购物车</div>
</tab-bar-item>
<tab-bar-item path="/profile" activeColor="blue">
<img slot="item-icon" src="./assets/img/tabbar/profile.svg" alt="">
<img slot="item-icon-active" src="./assets/img/tabbar/profile_active.svg" alt="">
<div slot="item-text">我的</div>
</tab-bar-item>
</tab-bar>
<router-view></router-view>
</div>
</template>
<script>
import TabBar from './components/tabbar/TabBar.vue'
import TabBarItem from './components/tabbar/TabBarItem.vue'
export default {
name: 'App',
components:{
TabBar,
TabBarItem
}
}
</script>
<style>
@import url('./assets/css/base.css');
</style>
App.vue
<template>
<div id="app">
<main-tab-bar></main-tab-bar>
<router-view></router-view>
</div>
</template>
<script>
import MainTabBar from "./components/MainTabBar.vue"
export default {
name: 'App',
components:{
MainTabBar
}
}
</script>
<style>
@import url('./assets/css/base.css');
</style>
MainTabBar.vue
<template>
<tab-bar>
<tab-bar-item path="/home" activeColor="blue">
<img slot="item-icon" src="../assets/img/tabbar/home.svg" alt="">
<img slot="item-icon-active" src="../assets/img/tabbar/home_active.svg" alt="">
<div slot="item-text">首页</div>
</tab-bar-item>
<tab-bar-item path="/category" activeColor="blue">
<img slot="item-icon" src="../assets/img/tabbar/category.svg" alt="">
<img slot="item-icon-active" src="../assets/img/tabbar/category_active.svg" alt="">
<div slot="item-text">分类</div>
</tab-bar-item>
<tab-bar-item path="/cart" activeColor="blue">
<img slot="item-icon" src="../assets/img/tabbar/shopcart.svg" alt="">
<img slot="item-icon-active" src="../assets/img/tabbar//shopcart_active.svg" alt="">
<div slot="item-text">购物车</div>
</tab-bar-item>
<tab-bar-item path="/profile" activeColor="blue">
<img slot="item-icon" src="../assets/img/tabbar/profile.svg" alt="">
<img slot="item-icon-active" src="../assets/img/tabbar/profile_active.svg" alt="">
<div slot="item-text">我的</div>
</tab-bar-item>
</tab-bar>
</template>
<script>
import TabBar from '../components/tabbar/TabBar.vue'
import TabBarItem from '../components/tabbar/TabBarItem.vue'
export default {
components: {
TabBar,
TabBarItem
}
}
</script>
<style>
</style>
TabBarItem.vue
<template>
<div class="tab-bar-item" @click="itemClick">
<slot v-if="!isActive" name="item-icon" ></slot>
<slot v-else name="item-icon-active"></slot>
<div :style="activeStyle"><slot name="item-text"></slot></div>
<!-- <img src="../../assets/img/tabbar/home.svg" alt="">
<div>首页</div> -->
</div>
</template>
<script>
export default {
name:"TabBarItem",
props:{
path:String,
activeColor:{
type:String,
default:'red'
}
},
data(){
return{
// isActive:true
}
},
computed:{
isActive(){
//拿到处于活跃路由的路径
return this.$route.path.indexOf(this.path)!==-1;
},
activeStyle(){
//如果不处于活跃状态就返回一个空对象
return this.isActive ? {color:this.activeColor} : {}
}
},
methods:{
itemClick(){
// console.log("yes");
this.$router.replace(this.path);
}
}
}
</script>
<style>
.tab-bar-item{
flex: 1;
text-align: center;
height: 49px;
font-size: 12px;
}
.tab-bar-item img{
margin-top: 3px;
width: 24px;
height: 24px;
vertical-align: middle;
}
/* .active{
color: red;
} */
</style>
TabBar.vue
<template>
<div id="tab-bar">
<slot></slot>
</div>
</template>
<script>
export default {
name:"TabBar"
}
</script>
<style>
#tab-bar{
display: flex;
background-color: #f6f6f6;
position: fixed;
left: 0px;
right: 0px;
bottom: 0px;
box-shadow: 0px -2px 1px rgba(100,100,100,.1);
}
</style>
./router/index.js
import Vue from 'vue'
import Router from 'vue-router'
const Home=()=>import('../views/home/Home.vue')
const Category=()=>import('../views/category/Category.vue')
const Profile=()=>import('../views/profile/Profile.vue')
const Cart=()=>import('../views/cart/Cart.vue')
Vue.use(Router)
export default new Router({
routes: [
{
path: '',
redirect:'/home'
},
{
path:'/category',
component:Category
},
{
path:'/profile',
component:Profile
},
{
path:'/cart',
component:Cart
},
{
path:'/home',
component:Home
}
],
mode:'history'
})
../../../
这种很不方便webpack.config.js
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'@': resolve('src'),
'assets':resolve('src/assets'),
'components':resolve('src/components'),
'views':resolve('src/views')
}
},
import TabBar from '../components/tabbar/TabBar.vue'
import TabBar from 'components/tabbar/TabBar.vue'
~
符号<img slot="item-icon" src="~assets/img/tabbar/shopcart.svg" alt="">
Promise是异步编程的一种解决方案
一种很常见的场景应该就是网络请求。
我们封装一个网络请求的函数,因为不能立即拿到结果,所以不能像简单的3+4=7一样将结果返回。
所以往往我们会传入另外一个函数,在数据请求成功时,将数据通过传入的函数回调出去。
如果只是一个简单的网络请求,那么这种方案不会给我们带来很大的麻烦。
回调地狱
考虑下面的场景:
如果每一步都有跟多的代码那么同步操作会消耗大量时间,且代码难看,很难维护
$.ajax( 'url1' , function (data1) {
$.ajax(data1 ['url2 '], function (data2) {
$.ajax(data2 ['url3'], function (data3) {
$.ajax(data3 [ 'url4'], function (data4){
console.log(data4);
})
})
})
})
Promise的模拟使用
Promise 对异步操作进行封装
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
// 1.使用setTimeout
// setTimeout(() => {
// console.log('Hello World');
// }, 1000)
// 什么情况下会用到Promise?
// 一般情况下是有异步操作时,使用Promise对这个异步操作进行封装
// new -> 构造函数(1.保存了一些状态信息 2.执行传入的函数)
// 在执行传入的回调函数时, 会传入两个参数, resolve, reject.本身又是函数
new Promise((resolve, reject) => {
setTimeout(() => {
// 成功的时候调用resolve,来处理结果
// resolve('Hello World')
// 失败的时候调用reject
reject('error message')
}, 1000)
}).then((data) => {
// 1.100行的处理代码
console.log(data);
console.log(data);
console.log(data);
console.log(data);
console.log(data);
}).catch((err) => {
console.log(err);
})
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
new Promise((resolve, reject) => {
setTimeout(() => {
// resolve('Hello Vuejs')
reject('error message')
}, 1000)
}).then(data => {
console.log(data);
}, err => {
console.log(err);
})
</script>
</body>
</html>
链式调动(一)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
// 参数 -> 函数(resolve, reject)
// resolve, reject本身它们又是函数
// 链式编程
new Promise((resolve, reject) => {
// 第一次网络请求的代码
setTimeout(() => {
resolve()
}, 1000)
}).then(() => {
// 第一次拿到结果的处理代码
console.log('Hello World');
console.log('Hello World');
console.log('Hello World');
console.log('Hello World');
console.log('Hello World');
console.log('Hello World');
return new Promise((resolve, reject) => {
// 第二次网络请求的代码
setTimeout(() => {
resolve()
}, 1000)
})
}).then(() => {
// 第二次处理的代码
console.log('Hello Vuejs');
console.log('Hello Vuejs');
console.log('Hello Vuejs');
console.log('Hello Vuejs');
console.log('Hello Vuejs');
console.log('Hello Vuejs');
return new Promise((resolve, reject) => {
// 第三次网络请求的代码
setTimeout(() => {
resolve()
})
})
}).then(() => {
// 第三处理的代码
console.log('Hello Python');
console.log('Hello Python');
console.log('Hello Python');
console.log('Hello Python');
console.log('Hello Python');
})
</script>
</body>
</html>
链式调动(二)(Promise的第二种处理形式)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
// 参数 -> 函数(resolve, reject)
// resolve, reject本身它们又是函数
// 链式编程
new Promise((resolve, reject) => {
// 第一次网络请求的代码
setTimeout(() => {
resolve()
}, 1000)
}).then(() => {
// 第一次拿到结果的处理代码
console.log('Hello World');
console.log('Hello World');
console.log('Hello World');
console.log('Hello World');
console.log('Hello World');
console.log('Hello World');
return new Promise((resolve, reject) => {
// 第二次网络请求的代码
setTimeout(() => {
resolve()
}, 1000)
})
}).then(() => {
// 第二次处理的代码
console.log('Hello Vuejs');
console.log('Hello Vuejs');
console.log('Hello Vuejs');
console.log('Hello Vuejs');
console.log('Hello Vuejs');
console.log('Hello Vuejs');
return new Promise((resolve, reject) => {
// 第三次网络请求的代码
setTimeout(() => {
resolve();
reject();
})
})
//第二种处理形式
}).then((data) => {
// 第三处理的代码
console.log('Hello Python');
console.log('Hello Python');
console.log('Hello Python');
console.log('Hello Python');
console.log('Hello Python');
},err=>{
console.loh("error");
})
</script>
</body>
</html>
需求:网络请求->aaa ,自己处理10行 -> 处理aaa后面加111 , 自己处理10行 -> 处理aaa111后面加222 , 自己处理10行代码 ->…
<script>
// wrapped into
// 网络请求: aaa -> 自己处理(10行)
// 处理: aaa111 -> 自己处理(10行)
// 处理: aaa111222 -> 自己处理
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('aaa')
}, 1000)
}).then(res => {
// 1.自己处理10行代码
console.log(res, '第一层的10行处理代码');
// 2.对结果进行第一次处理
return new Promise((resolve, reject) => {
// resolve(res + '111')
reject('err')
})
}).then(res => {
console.log(res, '第二层的10行处理代码');
return new Promise(resolve => {
resolve(res + '222')
})
}).then(res => {
console.log(res, '第三层的10行处理代码');
}).catch(err => {
console.log(err);
})
// new Promise(resolve => resolve(结果))简写
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('aaa')
}, 1000)
}).then(res => {
// 1.自己处理10行代码
console.log(res, '第一层的10行处理代码');
// 2.对结果进行第一次处理
return Promise.resolve(res + '111')
}).then(res => {
console.log(res, '第二层的10行处理代码');
return Promise.resolve(res + '222')
}).then(res => {
console.log(res, '第三层的10行处理代码');
})
// 省略掉Promise.resolve
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('aaa')
}, 1000)
}).then(res => {
// 1.自己处理10行代码
console.log(res, '第一层的10行处理代码');
// 2.对结果进行第一次处理,内部对“res + '111'”进行了Promise包装
return res + '111'
}).then(res => {
console.log(res, '第二层的10行处理代码');
return res + '222'
}).then(res => {
console.log(res, '第三层的10行处理代码');
})
//省略 return res + '111'
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('aaa')
}, 1000)
}).then(res => {
// 1.自己处理10行代码
console.log(res, '第一层的10行处理代码');
// 2.对结果进行第一次处理
// return Promise.reject('error message')
throw 'error message'
}).then(res => {
console.log(res, '第二层的10行处理代码');
return Promise.resolve(res + '222')
}).then(res => {
console.log(res, '第三层的10行处理代码');
}).catch(err => {
console.log(err);
})
</script>
链式调动(三)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
// 请求一:
let isResult1 = false
let isResult2 = false
$ajax({
url: '',
success: function () {
console.log('结果1');
isResult1 = true
handleResult()
}
})
// 请求二:
$ajax({
url: '',
success: function () {
console.log('结果2');
isResult2 = true
handleResult()
}
})
function handleResult() {
if (isResult1 && isResult2) {
//
}
}
</script>
</body>
</html>
链式调动(四)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
//Promise.all([iterator])可迭代的
Promise.all([
// new Promise((resolve, reject) => {
// $.ajax({
// url: 'url1',
// success: function (data) {
// resolve(data)
// }
// })
// }),
// new Promise((resolve, reject) => {
// $.ajax({
// url: 'url2',
// success: function (data) {
// resolve(data)
// }
// })
// })
new Promise((resolve, reject) => {
setTimeout(() => {
resolve({name: 'why', age: 18})
}, 2000)
}),
new Promise((resolve, reject) => {
setTimeout(() => {
resolve({name: 'kobe', age: 19})
}, 1000)
})
]).then(results => {
console.log(results);
})
//result[0]输出结果
//result[1]输出结果
</script>
</body>
</html>
token令牌请求数据
State: 状态
View: 视图层
Actions:主要是用户的各种操作:点击、输入等等,会导致状态的改变。
共享状态
单界面:方法一:父子组件的传递
App.vue
<template>
<div id="app">
<h2>{{message}}</h2>
<h2>{{counter}}</h2>
<button @click="counterUp()">+</button>
<button @click="counterDown()">-</button>
<!--方法一:父子组件的传递 -->
<hello-vuex :counter="counter"></hello-vuex>
</div>
</template>
<script>
import HelloVuex from './components/HelloVuex.vue'
export default {
name: 'App',
data() {
return{
message: "我是App组件",
counter:0
}
},
components:{
HelloVuex
},
methods:{
counterUp(){
this.counter ++;
// return counter
},
counterDown(){
this.counter --;
// return counter
}
}
}
</script>
<style>
</style>
HelloVuex.vue
<template>
<div>
<h1>{{counter}}</h1>
</div>
</template>
<script>
export default {
props:{
counter:Number
}
}
</script>
<style>
</style>
多界面:方法二:使用Vuex传递
npm install vuex --save
./src/store
存放包管理工具,创建index.js
文件index.js
import Vue from 'vue'
import Vuex from 'vuex'
//1.安装插肩
Vue.use(Vuex)
//2.创建对象
const store =new Vuex.Store({
//状态
state:{
counter:1000
},
mutations:{
},
actions:{
},
getters:{
},
modules:{
}
})
export default store;
main.js
中引入vuexmain.js
import Vue from 'vue'
import App from './App'
import store from './store'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
store,
render: h => h(App)
})
HelloVuex.vue
和App.vue
中的{{messagge}}
替换为{{$store.state.counter}}
index.js
import Vue from 'vue'
import Vuex from 'vuex'
//1.安装插肩
Vue.use(Vuex)
//2.创建对象
const store =new Vuex.Store({
//状态
state:{
counter:1000
},
mutations:{
//方法,state指的就是上方的state
increment(state){
state.counter++
},
decrement(state){
state.counter--
}
},
actions:{
},
getters:{
},
modules:{
}
})
export default store;
App.vue
<template>
<div id="app">
<h2>{{message}}</h2>
<h2>{{$store.state.counter}}</h2>
<button @click="counterUp()">+</button>
<button @click="counterDown()">-</button>
<hello-vuex></hello-vuex>
</div>
</template>
<script>
import HelloVuex from './components/HelloVuex.vue'
export default {
name: 'App',
data() {
return{
message: "我是App组件",
}
},
components:{
HelloVuex
},
methods:{
counterUp(){
this.$store.commit('increment')
},
counterDown(){
this.$store.commit('decrement')
// return counter
}
}
}
</script>
<style>
</style>
单一状态树能够让我们最直接的方式找到某个状态的片段,而且在之后的维护和调试过程中,也可以非常方便的管理和维护。
即使你有很多状态,但还是只使用放在一个 store里。
./src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
//1.安装插肩
Vue.use(Vuex)
//2.创建对象
const store =new Vuex.Store({
//状态
state:{
counter:1000,
students: [
{id: 110, name: 'why', age: 18},
{id: 111, name: 'kobe', age: 24},
{id: 112, name: 'james', age: 30},
{id: 113, name: 'curry', age: 10}
]
},
mutations:{
//方法,state指的就是上方的state
increment(state){
state.counter++
},
decrement(state){
state.counter--
}
},
actions:{
},
getters:{
powerCounter(state){
return state.counter * state.counter
},
more20stu(state){
return state.students.filter(s=>s.age>20)
}
},
modules:{
}
})
export default store;
App.vue
<template>
<div id="app">
<h2>------------App内容----------------</h2>
<h2>{{message}}</h2>
<h2>{{$store.state.counter}}</h2>
<button @click="counterUp()">+</button>
<button @click="counterDown()">-</button>
<h2>------------App内容:getters相关信息----------------</h2>
<h2>{{$store.state.counter * $store.state.counter}}</h2>
<h2>{{$store.getters.powerCounter}}</h2>
<h2>{{$store.getters.more20stu}}</h2>
<hello-vuex></hello-vuex>
</div>
</template>
<script>
import HelloVuex from './components/HelloVuex.vue'
export default {
name: 'App',
data() {
return{
message: "我是App组件",
}
},
components:{
HelloVuex
},
methods:{
counterUp(){
this.$store.commit('increment')
},
counterDown(){
this.$store.commit('decrement')
// return counter
}
}
}
</script>
<style>
</style>
./src/store/index.js
getters:{
powerCounter(state){
return state.counter * state.counter
},
more20stu(state){
return state.students.filter(s=>s.age>20)
},
more20stulength(state,getters){
return getters.more20stu.length
}
},
App.vue
<h2>{{$store.getters.moreagestu(25)}}</h2>
./src/store/index.js
getters:{
powerCounter(state){
return state.counter * state.counter
},
more20stu(state){
return state.students.filter(s=>s.age>20)
},
more20stulength(state,getters){
return getters.more20stu.length
},
moreagestu(state){
// return function(age){
// return state.students.filter(s=>s.age>age)
// }
return age = > {
return state.students.filter(s=>s.age>age)
// }
}
}
},
Mutation主要包括两部分:
在通过mutation更新数据的时候, 有可能我们希望携带一些额外的参数
参数被称为是mutation的载荷(Payload)
如果参数不是一个呢?
需求:添加两个个按钮:+5,+10
<button @click="addFive">+5</button>
<button @click="addTen">+10</button>
//如果这样操作又要重新定义两个方法
可以变为
App.vue
export default {
name: 'App',
data() {
return{
message: "我是App组件",
}
},
components:{
HelloVuex
},
methods:{
counterUp(){
this.$store.commit('increment')
},
counterDown(){
this.$store.commit('decrement')
// return counter
},
addCount(count){
//payload: 负载
this.$store.commit('incrementCount',count)
}
}
}
</script>
./src/store/index.js
mutations:{
//方法,state指的就是上方的state
increment(state){
state.counter++
},
decrement(state){
state.counter--
},
incrementCount(state,count){
state.counter+=count
}
},
App.vue
methods:{
counterUp(){
this.$store.commit('increment')
},
counterDown(){
this.$store.commit('decrement')
// return counter
},
addCount(count){
// 1.普通的提交风格
this.$store.commit('incrementCount',count)
//2.特殊的提交风格
this.$store.commit({
type:'incrementCount',
count:count//此时传出的是对象,故要改变index.js中的传入参数
})
}
}
./src/store/index.js
mutations:{
//方法,state指的就是上方的state
increment(state){
state.counter++
},
decrement(state){
state.counter--
},
//将这里改掉
incrementCount(state,payload){
state.counter+=payload.count
}
},
一旦定义,
这些属性都会被加入到响应式系统中,而响应式系统会监听属性的变化当属性发生变化时,
会通知所有界面中用到该属性的地方,让界面发生刷新。
Vue.set(state.info,'address','洛杉矶')
Vue.delete(state.info,'age')
state.info = {...state.info,'height':payload.height}
updateInfo(state){
state.info.name="leng"
//address未做到响应式,因为address未定义
//现在vuex3 cli4可以是响应式的
//添加属性
//state.info['address']='洛杉矶'
//删除属性 //非响应式
//delete state.info.age
//响应式
Vue.delete(state.info,'age')
}
现在Vuex3 已经可以做到响应式了
在mutation中, 我们定义了很多事件类型(也就是其中的方法名称).
当我们的项目增大时, Vuex管理的状态越来越多, 需要更新状态的情况越来越多, 那么意味着Mutation中的方法越来越多.
方法过多, 使用者需要花费大量的经历去记住这些方法, 甚至是多个文件间来回切换, 查看方法名称, 甚至如果不是复制的时候, 可能还会出现写错的情况.
解决方案:我们可以创建一个文件:mutation-types.js
, 并且在其中定义我们的常量.
定义常量时, 我们可以使用ES2015中的风格, 使用一个常量来作为函数的名称.
创建一个文件./src/store/mutations-types.js
如果直接使用异步,vue devtool不会有记录(但我使用时,是会有记录的)
context是和store对象具有相同方法和属性的对象.
也就是说, 我们可以通过context去进行commit相关的操作, 也可以获取context.state等.
在mutations中不要有异步操作,有异步操作是就需要使用action函数
action传递参数
例:
./src/store/index.js
mutations:{
//方法,state指的就是上方的state
[INCREMENT](state){
state.counter++
},
decrement(state){
state.counter--
},
incrementCount(state,payload){
state.counter+=payload.count
},
updateInfo(state){
state.info.name="leng"
// state.info['address']='洛杉矶'
//非响应式
// delete state.info.age
// Vue.delete(state.info,'age')
// setTimeout(()=>{
// state.info.name="leng"
// },1000)
}
},
actions:{
//context此时理解成store
aUpdateInfo(context,payload){
setTimeout(()=>{
context.commit('updateInfo');
console.log(payload);
},1000)
}
},
App.vue
methods:{
updateInfo(){
// this.$store.commit('updateInfo')
this.$store.dispatch('aUpdateInfo','我是payload')
}
}
App.vue
updateInfo(){
// this.$store.commit('updateInfo')
this.$store.dispatch('aUpdateInfo',()=>{
console.log('里面已经完成了');
})
}
./src/store/index.js
actions:{
//context此时理解成store
aUpdateInfo(context,payload){
setTimeout(()=>{
context.commit('updateInfo')
payload()
},1000)
}
},
弊端:无法再传其它参数
改进:
App.vue
updateInfo(){
// this.$store.commit('updateInfo')
this.$store.dispatch('aUpdateInfo',{
payload:"我是携带的信息",
success:()=>{
console.log("里面已经完成了")
}
})
}
./src/store/index.js
actions:{
//context此时理解成store
aUpdateInfo(context,payload){
setTimeout(()=>{
context.commit('updateInfo')
console.log(payload.message);
payload.success()
},1000)
}
},
Promise
App.vue
updateInfo(){
this.$store
.dispatch('aUpdateInfo',"我是携带的信息")
.then(res=>{
console.log('里面的任务完成了');
console.log(res);
})
}
./src/store/index.js
actions:{
//简介的做法:
aUpdateInfo(context,payload){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
context.commit('updateInfo')
console.log(payload);
resolve('111')
},1000)
})
}
},
Vue使用单一状态树,那么也意味着很多状态都会交给Vuex来管理.
当应用变得非常复杂时,store对象就有可能变得相当臃肿.
为了解决这个问题, Vuex允许我们将store分割成模块(Module),
而每个模块拥有自己的state、mutations、actions、getters等
例:
modules:{
a:{
state:{},
mutations:{},
actions:{},
getters:{}
},
b:{
state:{},
mutations:{},
actions:{},
getters:{}
}
//...
}
App.vue
<template>
<div id="app">
<h1>4.modules内容</h1>
<h2>{{$store.state.a.name}}</h2>
<button @click="updateName">修改名字</button>
<h2>{{$store.getters.fullname}}</h2>
<h2>{{$store.getters.fullname2}}</h2>
<h2>{{$store.getters.fullname3}}</h2>
<button @click="asyncUpdateName">异步修改名字</button>
<h1>1.App内容</h1>
<h2>{{message}}</h2>
<h2>{{$store.state.counter}}</h2>
<button @click="counterUp()">+</button>
<button @click="counterDown()">-</button>
<button @click="addCount(5)">+5</button>
<button @click="addCount(10)">+10</button>
<h1>2.App内容:getters相关信息</h1>
<h2>{{$store.state.counter * $store.state.counter}}</h2>
<h2>{{$store.getters.powerCounter}}</h2>
<h2>{{$store.getters.more20stu}}</h2>
<h2>{{$store.getters.more20stulength}}</h2>
<h2>{{$store.getters.moreagestu(25)}}</h2>
<hello-vuex></hello-vuex>
<h1>3.展示info的内容是否时响应式的</h1>
<h2>{{$store.state.info}}</h2>
<button @click="updateInfo">修改信息</button>
</div>
</template>
<script>
import HelloVuex from './components/HelloVuex.vue'
import {INCREMENT} from './store/mutations-types'
export default {
name: 'App',
data() {
return{
message: "我是App组件",
}
},
components:{
HelloVuex
},
methods:{
counterUp(){
this.$store.commit(INCREMENT)
},
counterDown(){
this.$store.commit('decrement')
// return counter
},
addCount(count){
// 1.普通的提交风格
// this.$store.commit('incrementCount',count)
//2.特殊的提交风格
this.$store.commit({
type:'incrementCount',
count:count//此是传出的是对象
})
},
updateInfo(){
// this.$store.commit('updateInfo')
// this.$store.dispatch('aUpdateInfo',{
// message:"我是携带的信息",
// success:()=>{
// console.log("里面已经完成了")
// }
// })
this.$store
.dispatch('aUpdateInfo',"我是携带的信息")
.then(res=>{
console.log('里面的任务完成了');
console.log(res);
})
},
updateName(){
this.$store.commit('updateName','222')
},
asyncUpdateName(){
this.$store.dispatch('aUpdateName')
}
}
}
</script>
<style>
</style>
./src/store/index.js
<template>
<div id="app">
<h1>4.modules内容</h1>
<h2>{{$store.state.a.name}}</h2>
<button @click="updateName">修改名字</button>
<h2>{{$store.getters.fullname}}</h2>
<h2>{{$store.getters.fullname2}}</h2>
<h2>{{$store.getters.fullname3}}</h2>
<button @click="asyncUpdateName">异步修改名字</button>
<h1>1.App内容</h1>
<h2>{{message}}</h2>
<h2>{{$store.state.counter}}</h2>
<button @click="counterUp()">+</button>
<button @click="counterDown()">-</button>
<button @click="addCount(5)">+5</button>
<button @click="addCount(10)">+10</button>
<h1>2.App内容:getters相关信息</h1>
<h2>{{$store.state.counter * $store.state.counter}}</h2>
<h2>{{$store.getters.powerCounter}}</h2>
<h2>{{$store.getters.more20stu}}</h2>
<h2>{{$store.getters.more20stulength}}</h2>
<h2>{{$store.getters.moreagestu(25)}}</h2>
<hello-vuex></hello-vuex>
<h1>3.展示info的内容是否时响应式的</h1>
<h2>{{$store.state.info}}</h2>
<button @click="updateInfo">修改信息</button>
</div>
</template>
<script>
import HelloVuex from './components/HelloVuex.vue'
import {INCREMENT} from './store/mutations-types'
export default {
name: 'App',
data() {
return{
message: "我是App组件",
}
},
components:{
HelloVuex
},
methods:{
counterUp(){
this.$store.commit(INCREMENT)
},
counterDown(){
this.$store.commit('decrement')
// return counter
},
addCount(count){
// 1.普通的提交风格
// this.$store.commit('incrementCount',count)
//2.特殊的提交风格
this.$store.commit({
type:'incrementCount',
count:count//此是传出的是对象
})
},
updateInfo(){
// this.$store.commit('updateInfo')
// this.$store.dispatch('aUpdateInfo',{
// message:"我是携带的信息",
// success:()=>{
// console.log("里面已经完成了")
// }
// })
this.$store
.dispatch('aUpdateInfo',"我是携带的信息")
.then(res=>{
console.log('里面的任务完成了');
console.log(res);
})
},
updateName(){
this.$store.commit('updateName','222')
},
asyncUpdateName(){
this.$store.dispatch('aUpdateName')
}
}
}
</script>
<style>
</style>
index.js原始
import Vue from 'vue'
import Vuex from 'vuex'
import {INCREMENT} from './mutations-types'
//1.安装插肩
Vue.use(Vuex)
//2.创建对象
const moduleA= {
state:{
name:'hua'
},
mutations:{
updateName(state,payload){
state.name=payload
}
},
actions:{
//此时的context不再是store
aUpdateName(context){
setTimeout(()=>{
context.commit('updateName','leng1')
},1000)
}
},
getters:{
fullname(state){
return state.name+'232323'
},
fullname2(state,getters){
return getters.fullname+'222333'
},
//模块里有rootState
fullname3(state,getters,rootState){
return getters.fullname2+ rootState.counter
}
}
}
const store =new Vuex.Store({
//状态
state:{
counter:1000,
students: [
{id: 110, name: 'why', age: 18},
{id: 111, name: 'kobe', age: 24},
{id: 112, name: 'james', age: 30},
{id: 113, name: 'curry', age: 10}
],
info:{
name:'kobe',
age:40,
height:1.98
}
},
mutations:{
//方法,state指的就是上方的state
[INCREMENT](state){
state.counter++
},
decrement(state){
state.counter--
},
incrementCount(state,payload){
state.counter+=payload.count
},
updateInfo(state){
state.info.name="leng"
// state.info['address']='洛杉矶'
//非响应式
// delete state.info.age
// Vue.delete(state.info,'age')
// setTimeout(()=>{
// state.info.name="leng"
// },1000)
}
},
actions:{
//context此时理解成store
// aUpdateInfo(context,payload){
// setTimeout(()=>{
// context.commit('updateInfo')
// console.log(payload.message);
// payload.success()
// },1000)
// }
//简介的做法:
aUpdateInfo(context,payload){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
context.commit('updateInfo')
console.log(payload);
resolve('111')
},1000)
})
}
},
getters:{
powerCounter(state){
return state.counter * state.counter
},
more20stu(state){
return state.students.filter(s=>s.age>20)
},
more20stulength(state,getters){
return getters.more20stu.length
},
moreagestu(state){
return function(age){
return state.students.filter(s=>s.age>age)
}
}
},
modules:{
a: moduleA
}
})
export default store;
./src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import mutations from './mutations'
import actions from './actions'
import moduleA from './modules/moduleA'
import getters from './getters'
// import {INCREMENT} from './mutations-types'
//1.安装插肩
Vue.use(Vuex)
//2.创建对象
const state={
counter:1000,
students: [
{id: 110, name: 'why', age: 18},
{id: 111, name: 'kobe', age: 24},
{id: 112, name: 'james', age: 30},
{id: 113, name: 'curry', age: 10}
],
info:{
name:'kobe',
age:40,
height:1.98
}
}
const store =new Vuex.Store({
//状态
state:state,
mutations:mutations,
actions:actions,
getters,
modules:{
a: moduleA
}
})
export default store;
./store/getter.js
export default{
powerCounter(state){
return state.counter * state.counter
},
more20stu(state){
return state.students.filter(s=>s.age>20)
},
more20stulength(state,getters){
return getters.more20stu.length
},
moreagestu(state){
return function(age){
return state.students.filter(s=>s.age>age)
}
}
}
./store/actions.js
export default{
//context此时理解成store
// aUpdateInfo(context,payload){
// setTimeout(()=>{
// context.commit('updateInfo')
// console.log(payload.message);
// payload.success()
// },1000)
// }
//简介的做法:
aUpdateInfo(context,payload){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
context.commit('updateInfo')
console.log(payload);
resolve('111')
},1000)
})
}
}
./store/mutations.js
import {INCREMENT} from './mutations-types'
export default{
//方法,state指的就是上方的state
[INCREMENT](state){
state.counter++
},
decrement(state){
state.counter--
},
incrementCount(state,payload){
state.counter+=payload.count
},
updateInfo(state){
state.info.name="leng"
// state.info['address']='洛杉矶'
//非响应式
// delete state.info.age
// Vue.delete(state.info,'age')
// setTimeout(()=>{
// state.info.name="leng"
// },1000)
}
}
./store/module/moduleA.js
export default{
state:{
name:'hua'
},
mutations:{
updateName(state,payload){
state.name=payload
}
},
actions:{
//此时的context不再是store
aUpdateName(context){
setTimeout(()=>{
context.commit('updateName','leng1')
},1000)
}
},
getters:{
fullname(state){
return state.name+'232323'
},
fullname2(state,getters){
return getters.fullname+'222333'
},
//模块里有rootState
fullname3(state,getters,rootState){
return getters.fullname2+ rootState.counter
}
}
}
./store/mutations-type.js
export const INCREMENT = 'increment'
2.选择什么网络模块
选择一: 传统的Ajax是基于
XMLHttpRequest(XHR)
为什么不用它呢?
配置和调用方式等非常混乱.
编码起来看起来就非常蛋疼.
所以真实开发中很少直接使用, 而是使用jQuery-Ajax
选择二: 在前面的学习中, 我们经常会使用
jQuery-Ajax
相对于传统的Ajax非常好用.
为什么不选择它呢?
首先, 我们先明确一点: 在Vue的整个开发中都是不需要使用jQuery了.
那么, 就意味着为了方便我们进行一个网络请求, 特意引用一个jQuery, 你觉得合理吗?
jQuery的代码1w+行.
Vue的代码才1w+行.
完全没有必要为了用网络请求就引用这个重量级的框架.
选择三: 官方在Vue1.x的时候, 推出了
Vue-resource.
Vue-resource的
体积相对于jQuery小很多.
另外Vue-resource是官方推出的.
为什么不选择它呢?
在Vue2.0退出后, Vue作者就在GitHub的Issues中说明了去掉vue-resource
, 并且以后也不会再更新.
那么意味着以后vue-reource不再支持新的版本时, 也不会再继续更新和维护.
对以后的项目开发和维护都存在很大的隐患.
选择四: 在说明不再继续更新和维护
vue-resource
的同时, 作者还推荐了一个框架: axios
为什么不用它呢?
axios有非常多的优点, 并且用起来也非常方便.
稍后, 我们对他详细学习.
npm install axios --save
main.js
import Vue from 'vue'
import App from './App'
import router from './router'
import axios from 'axios'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
render: h => h(App)
})
//方法1.
//axios(config),config可以是对象类型
//默认情况是get请求
axios({
//这个网址用于网络封装的模拟,url:'http://httpbin,org/'
//http://123.207.32.32/home 学习项目接口域名
// 向http://123.207.32.32:8000/home/multidata 请求数据
url:'http://123.207.32.32:8000/home/multidata',
//不设置methods,默认情况是get请求 需设置post
methods:'get'
})
//axios本身会返回一个Promise,在内部拿到数据后,会调用内部的resolve,然后执行then方法
.then(res=>{
console.log(res);
})
axios({
url:'http://123.207.32.32:8000/home/data?typr=sell&page=3'
})
.then(res=>{
console.log(res);
})
axios({
url:'http://123.207.32.32:8000/home/data',
//专门针对get请求的参数拼接
params:{
type:'pop',
page:'1'
}
})
.then(res=>{
console.log(res);
})
//接口可能出现跨域 所以设计的时候也支持jsonp
//http://123.207.32.32:8000/home/multidata?callback=json123
//方法2.
//axios.post()
axios.all([])
返回的结果是一个数组,使用 axios.spread
可将数组 [res1,res2]
展开为res1, res2
main.js
import Vue from 'vue'
import App from './App'
import router from './router'
import axios from 'axios'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
render: h => h(App)
})
//一。 axios的并发请求使用
//1.
// axios.all([axios({
// url:'http://123.207.32.32:8000/home/multidata'
// }),axios({
// url:'http://123.207.32.32:8000/home/data?typr=sell&page=3'
// })])
// .then(results=>{
// console.log(results);
// console.log(results[0]);
// console.log(results[1]);
// })
//2.
axios.all([axios({
url:'http://123.207.32.32:8000/home/multidata'
}),axios({
url:'http://123.207.32.32:8000/home/data',
params:{
type:'sell',
page:5
}
})])
.then(axios.spread((res1,res2) => {
console.log(res1);
console.log(res2);
}))
// 3.对象的解构
// 数组解构
// const names=['yhy','my','se']
// const name1 = names[0];
// const name2 = names[1];
// const name3 = names[2];
// const [name1,name2,name3]=names;
axios.defaults.baseURL = '123.207.32.32:8000'
axios.defaults.headers.post[‘Content-Type’] = 'application/x-www-form-urlencoded';
main.js
axios.defaults.baseURL='http://123.207.32.32:8000'
axios.defaults.timeout= 5000
axios.all([axios({
url:'/home/multidata'
}),axios({
url:'/home/data',
params:{
type:'sell',
page:5
}
})])
.then(axios.spread((res1,res2) => {
console.log(res1);
console.log(res2);
}))
为什么要创建axios的实例呢?
当我们从axios模块中导入对象时, 使用的实例是默认的实例.
当给该实例设置一些默认配置时, 这些配置就被固定下来了.
但是后续开发中, 某些配置可能会不太一样.
比如某些请求需要使用特定的baseURL或者timeout或者content-Type等.
这个时候, 我们就可以创建新的实例, 并且传入属于该实例的配置信息.
main.js
// 5.实例封装配置 创建对应的axios实例
//第一个实例
// const instance1 = axios.create({
// baseURL:'http://152.136.185.210:7878/api/m5',
// timeout:5000
// })
// instance1({
// url:'/home/multidata'
// })
// .then(res=>{
// console.log(res);
// })
// instance1({
// url:'/home/data',
// params:{
// type:'pop',
// page:1
// }
// })
// .then(res=>{
// console.log(res);
// })
// //第二个实例
// const instance2 = axios.create({
// baseURL:'http://baidu.com',
// timeout:5000
// })
//6.封装request模块
import { request } from './network/request'
request({
url:'/home/multidata'
},res=>{
console.log(res);
},err=>{
console.log(err);
})
//6.1.
// request({
// baseConfig:{
// },
// success:function(res){
// },
// failure:function(err){
// }
// })
// 6.2和6.3
request({
url:'/home/multidata'
}).then(res=>{
console.log(res);
}).catch(err=>{
console.log(err);
})
./src/network/request
import axios from 'axios'
//6.3
export function request(config){
// 1.创建axios实例
const instance = axios.create({
baseURL:'http://152.136.185.210:7878/api/m5',
timeout:5000
})
//2.发送真正的网络请求
//返回的就是Promise
return instance(config)
}
// 6.2
// export function request(config){
// return new Promise((resolve,reject)=>{
// // 1.创建axios实例
// const instance = axios.create({
// baseURL:'http://152.136.185.210:7878/api/m5',
// timeout:5000
// })
// //2.发送真正的网络请求
// instance(config)
// .then(res=>{
// // console.log(res);
// // config.success(res);
// resolve(res);
// })
// .catch(err=>{
// // console.log(err);
// // config.failure(err);
// reject(err);
// })
// })
// }
//6.1
// export function request(config){
// // 1.创建axios实例
// const instance = axios.create({
// baseURL:'http://152.136.185.210:7878/api/m5',
// timeout:5000
// })
// //2.发送真正的网络请求
// instance(config.baseConfig)
// .then(res=>{
// // console.log(res);
// config.success(res);
// })
// .catch(err=>{
// // console.log(err);
// config.failure(err);
// })
// }
// 6.
// export function request(config,success,failure){
// // 1.创建axios实例
// const instance = axios.create({
// baseURL:'http://152.136.185.210:7878/api/m5',
// timeout:5000
// })
// //2.发送真正的网络请求
// instance(config)
// .then(res=>{
// // console.log(res);
// success(res);
// })
// .catch(err=>{
// // console.log(err);
// failure(err);
// })
// }
main.js
import Vue from 'vue'
import App from './App'
import router from './router'
import axios from 'axios'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
render: h => h(App)
})
import { request } from './network/request'
request({
url:'/home/multidata'
}).then(res=>{
console.log(res);
}).catch(err=>{
// console.log(err);
})
./src/network/request.js
import axios from 'axios'
//6.3
export function request(config){
// 1.创建axios实例
const instance = axios.create({
baseURL:'http://152.136.185.210:7878/api/m5',
timeout:5000
})
//2.axios拦截器
//全局拦截axios.interceptors
// instance.interceptors.request.use(config=>{
// console.log(config);
// /*
// * 2.1请求拦截的作用
// * 比如config中的一些信息不符合服务器的要求
// * 比如每次发送网络请求时,都希望在界面中显示一个请求的图标,先show,请求成功后隐藏show
// * 某些网络请求(比如登录(token)),想要进入目的地,必须携带一些特殊信息,未携带着提示用户登录
// * 等待
// */
// //拦截了还要还回去
// return config;
// },err=>{
// console.log(err);
// })
//2.2响应拦截
instance.interceptors.response.use(res=>{
// console.log(res);
return res.data
},err=>{
console.log(err);
})
//3.发送真正的网络请求
//返回的就是Promise
return instance(config)
}
|
JavaScript知识库 最新文章 |
ES6的相关知识点 |
react 函数式组件 & react其他一些总结 |
Vue基础超详细 |
前端JS也可以连点成线(Vue中运用 AntVG6) |
Vue事件处理的基本使用 |
Vue后台项目的记录 (一) |
前后端分离vue跨域,devServer配置proxy代理 |
TypeScript |
初识vuex |
vue项目安装包指令收集 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 | -2024/12/27 20:42:59- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |
数据统计 |