B 站 – Vue 学习笔记
0 课程介绍
- 邂逅 Vue.js
- Vue 根底语法
- 组件化开发
- Vue CLI 详解
- vue-router
- vuex 详解
- 网络封装
- 我的项目实战
1 邂逅 Vue.js
Vue (读音 /vjuː/,相似于 view) 是一套用于构建用户界面的 渐进式框架。
1.1 MVVM 架构
-
MVVM 架构
-
MVC 与 MVP
-
MVVM
-
-
Vue.js 的定位
-
Vue.js 在 MVVM 架构中的定位
-
Vue.js 的功能定位
-
-
Vue.js 的核心思想与特点
-
Reactive Components for Modern Web Interfaces(数据驱动的组件,为古代的 Web 界面而生)
- 数据驱动
- 组件化
- 特点:侵入性低、激励模块化、轻量、高性能
-
-
数据驱动的 Web 开发
-
Vue.js 装置
- 下载 Vue.js,通过 <script> 标签引入
-
CDN
<!-- 开发环境版本,蕴含了有帮忙的命令行正告 --> <script class="lazy" referrerpolicy="no-referrer" data-src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <!-- 生产环境版本,优化了尺寸和速度 --> <script src="https://cdn.jsdelivr.net/npm/vue"></script>
- NPM 装置
- vue-cli:配套路由等灵便的命令行框架
-
传统视图驱动 Web 开发:通过原生 js/jquery 操作 DOM 元素
-
数据驱动的 Web 开发:将 Vue 实例中数据和办法等,通过 Vue 指令的形式绑定到 html 中
-
1.2 Vue 初体验
-
HelloVue.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>TestVue</title> </head> <body> <div id="app">{{message}}</div> <script class="lazy" referrerpolicy="no-referrer" data-src="../js/vue.js"></script> <script> // 一、一般 js 写法(编程范式:命令式编程)//1. 创立 div 元素,设置 id 属性 //2. 定义一个变量叫 message //3. 将 message 变量放在后面的 div 元素中显示 //4. 批改 message 数据 //5. 将 message 数据替换原数据 // 二、Vue 写法(编程范式:申明式编程)// let 变量、const 常量 // new Vue()就是 ViewModel const app = new Vue({ //el、data 属于 Vue 的 options el: '#app', // 挂载治理的元素 data: { // 定义数据 message: 'zfcer' } }) // Vue 能够间接在浏览器中批改 message:app.message = 'hello' </script> </body> </html>
-
Vue 列表展现
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Vue 列表展现 </title> </head> <body> <!-- Vue 申明式 + 响应式 --> <!-- 能够在浏览器中间接 app.movies.push('d'),向数组 movies 中增加元素 d --> <div id="app"> <ul> <li v-for="item in movies">{{item}}</li> </ul> </div> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', data:{movies: ['a', 'b', 'c'] } }) </script> </body> </html>
-
Vue 案例 - 计数器
<!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> 计数器 </title> </head> <body> <div id="app"> <h1> 以后计数:{{counter}}</h1> <!-- <button v-on:click="counter++">+</button> <button v-on:click="counter--">-</button> --> <button @click="add">+</button> <button @click="sub">-</button> </div> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: {counter: 0}, methods: {add: function(){console.log("add 执行了") // 要用 this. this.counter++; }, sub: function(){this.counter--;} } }) </script> </body> </html>
1.3 Vue 生命周期
在 github 中下载 vue 源码(tag2.6.14)后,用 vscode 关上 src –> core –> index.js,发现import Vue from './instance/index'
关上./instance/index 后就能够看到 Vue 函数,==options 示意 new Vue({})传入的 el、data、methods==
import {initMixin} from './init'
function Vue (options) {
if (process.env.NODE_ENV !== 'production' &&
!(this instanceof Vue)
) {warn('Vue is a constructor and should be called with the `new` keyword')
}
this._init(options)
}
进入./init 后,发现生命周期
// expose real self
vm._self = vm
initLifecycle(vm)
initEvents(vm)
initRender(vm)
callHook(vm, 'beforeCreate')
initInjections(vm) // resolve injections before data/props
initState(vm)
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created')
-
Vue 官网生命周期流程
-
了解 Vue 生命周期
2 Vue 根底语法
-
vscode 前端代码标准缩进设置为 2
2.1 Mustache 语法
- Mustache 中能够写简略的表达式,如果是简单的表达式举荐应用 computed 计算属性。==Mustache 也是响应式的 ==。Mustache 作用在显示内容自身。
<div id="app">
<h2>{{message}}</h2>
<!-- Mystache 语法实用于标签内容上,不应用与标签属性 -->
<!-- Mustache 语法中,不仅仅能够间接写变量,也能够写简略的表达式 -->
<h2>{{firstName + lastName}}</h2>
<h2>{{firstName + ' ' + lastName}}</h2>
<h2>{{firstName}} {{lastName}}</h2>
<h2>{{counter*2}}</h2>
</div>
<script class="lazy" referrerpolicy="no-referrer" data-src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
firstName: "zfcer",
lastName: "520",
counter: 1,
},
});
</script>
2.2 v-once 语法
- ==v-once 是非响应式的 ==,不心愿随便更改数据或界面。v-once 作用在标签上。
<div id="app">
<h2>{{message}}</h2>
<!-- 应用 v -once 后,上面的 h2 标签不会随着 message 扭转而扭转 -- 非响应式 -->
<!-- v-once 间接应用 -->
<h2 v-once>{{message}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {message: "zfcer",},
});
</script>
2.3 v-html 语法
- v-html 用来依照 html 格局进行解析显示,v-html 作用在标签上。
- 历史遗留问题:{{{}}}以前能够应用三大括号来解决 html 格局解析,当初不再反对了。
<div id="app"> <!-- 以前能够应用 {{{}}} 来代替,然而因为跟 {{}} 容易弄混,当初应用 v -html --> <!-- v-html 前面跟 url,作用在标签上 --> <h2 v-html="url"></h2></div><script src="../js/vue.js"></script><script> const app = new Vue({el: "#app", data: { url: "<a href='http://ww.baidu.com'> 百度一下 </a>",}, });</script>
2.4 v-text 语法
- v-text 与 Mustache 比拟类似,然而 v -text 作用在标签上,并且会笼罩掉原来的内容。== 不举荐应用 ==
<div id="app"> <h2>{{message}}, zfcer</h2> <!-- 应用 v -text 会用 message 笼罩掉‘,zfcer’--> <!-- 不举荐应用 --> <h2 v-text="message">, zfcer</h2></div><script src="../js/vue.js"></script><script> const app = new Vue({el: "#app", data: { message: "hello Vue",}, });</script>
2.5 v-pre 语法
- v-pre 用于跳过这个元素和它子元素的编译过程,用于 == 显示本来的 Mustache 语法 ==。
<div id="app"> <h2>{{message}}</h2> <!-- 应用 v -pre 最开始编译过程中会显示{{message}} --> <h2 v-pre>{{message}}</h2></div><script src="../js/vue.js"></script><script> const app = new Vue({el: "#app", data: { message: "hello Vue",}, });</script>
2.6 v-cloak 语法
- 当 Vue 加载前,v-cloak 存在;当 Vue 加载后,v-cloak 被移除。
<!DOCTYPE html><html lang="en"> <head> <meta charset="UTF-8" /> <title>v-cloak 语法 </title> <style> /* 暗藏元素信息{{message}} */ [v-cloak] {display: none;} </style> </head> <body> <div id="app" v-cloak>{{message}}</div> <script src="../js/vue.js"></script> <script> // 当 Vue 加载前,v-cloak 存在 // 当 Vue 加载后,v-cloak 被移除 setTimeout(// 延时加载 function () {const app = new Vue({ el: "#app", data: { message: "hello Vue",}, }); }, 2000 ); </script> </body></html>
2.7 v-bind 语法
- v-bind 根本用法:== 作用于标签属性上 ==,语法糖
:
<div id="app"> <!-- Mustache 语法不适用于标签属性上 --> <a href="{{url}}"> 百度一下 </a> <br /> <!-- v-bind 作用与标签属性上 --> <a v-bind:href="url"> 百度一下 </a> <!-- v-bind 语法糖: --> <a :href="url"> 百度一下 </a></div><script src="../js/vue.js"></script><script> const app = new Vue({el: "#app", data: { url: "http://www.baidu.com",}, });</script>
-
v-bind 动静绑定 class 对象:通过 v -bind:class 绑定标签上的 class 属性
- 在 v -bind:class 属性中传入对象{style1: boolean1, style2: boolean2},能够做到 == 主动依据 boolean 来进行拼接 ==
- 在一般 class 上定义公共 style,最终会实现将一般 class 与 v -bind:class 进行拼接
- v-bind:class 也能够通过函数的形式传入对象
<!DOCTYPE html><html lang="en"> <head> <meta charset="UTF-8" /> <title>v-bind 动静绑定 class(对象语法)</title> <style> .active {color: red;} .line {padding: 100px;} </style> </head> <body> <div id="app"> <!-- 用一般 class 定义固定款式,v-bind 绑定的 class 定义动静款式对象(会依据 boolean 值主动抉择对应的款式)--> <!-- 最终会将一般 class 与 v -bind 绑定的 class 拼接起来 --> <h2 class="title" :class="{active: isActive, line: isLine}"> Hello Vue (对象) </h2> <!-- 补充: 绑定函数 --> <h2 class="title" :class="getClasses()">Hello Vue (对象)</h2> <button @click="btnClick"> 点击变色 </button> </div> <script src="../js/vue.js"></script> <script> const app = new Vue({el: "#app", data: { isActive: true, isLine: true,}, methods: {btnClick: function () {this.isActive = !this.isActive;}, getClasses: function () { return { active: this.isActive, line: this.isLine}; }, }, }); </script> </body></html>
- v-bind 绑定 class 数组:与 v -bind 绑定 class 对象相似
<!DOCTYPE html><html lang="en"> <head> <meta charset="UTF-8" /> <title>v-bind 动静绑定 class(数组语法) - 不举荐 </title> <style> .active {color: red;} .line {padding: 100px;} </style> </head> <body> <div id="app"> <!-- 用一般 class 定义固定款式,v-bind 绑定的 class 定义动静款式数组(会依据 boolean 值主动抉择对应的款式)--> <!-- 最终会将一般 class 与 v -bind 绑定的 class 拼接起来 --> <!-- 不举荐 --> <h2 class="title" :class="[active, line]">Hello Vue (数组)</h2> <!-- 补充: 绑定函数 --> <h2 class="title" :class="getClasses()">Hello Vue (数组)</h2> <button @click="btnClick"> 点击变色 </button> </div> <script src="../js/vue.js"></script> <script> const app = new Vue({el: "#app", data: { active: "active", line: "line",}, methods: {btnClick: function () {this.isActive = !this.isActive;}, getClasses: function () { return [this.active, this.line]; }, }, }); </script> </body></html>
- 练习:v-for 与 v -bind 联合
<!DOCTYPE html><html lang="en"> <head> <meta charset="UTF-8" /> <title> 作业 - v-for 和 v -bind</title> <style> .active {color: red;} </style> </head> <body> <div id="app"> <!-- 计划一 --> <ul> <li :class="{active: isActive[index]}" v-for="(m, index) in movies"> {{index}} - {{m}} <button @click="btnClick(index)"> 点击 </button> </li> </ul> <br> <br> <br> <!-- 计划二:举荐 --> <ul> <li :class="{active: index == curIdx}" v-for="(m, index) in movies" @click="liClick(index)"> {{index}} - {{m}} </li> </ul> </div> <script src="../js/vue.js"></script> <script> const app = new Vue({el: "#app", data: { movies: ["aaa", "bbb", "ccc", "ddd"], isActive: [true, false, false, false], preIdx: 0, curIdx: 0 }, methods: {btnClick: function (index) {console.log("点击了" + index); this.isActive.splice(this.preIdx, 1, false) this.preIdx = index; Vue.set(this.isActive, index, true) }, liClick(index){this.curIdx = index} }, }); </script> </body></html>
- v-bind 动静绑定 style 对象:应用驼峰命名
<!DOCTYPE html><html lang="en"> <head> <meta charset="UTF-8" /> <title>v-bind 动静绑定 style(对象语法)</title> </head> <body> <div id="app"> <!-- 应用驼峰命名:fontSize -- font-size --> <h2 :style="{fontSize: finalSize+'px', color: finalColor}"> Hello Vue (对象) </h2> <!-- 补充: 绑定函数 --> <h2 class="title" :style="getStyles()">Hello Vue (对象)</h2> </div> <script src="../js/vue.js"></script> <script> const app = new Vue({el: "#app", data: { finalSize: 100, finalColor: "red",}, methods: {getStyles: function () {return { fontSize: this.finalSize + "px", color: this.finalColor,}; }, }, }); </script> </body></html>
- v-bind 动静绑定 style 数组
<!DOCTYPE html><html lang="en"> <head> <meta charset="UTF-8" /> <title>v-bind 动静绑定 style(数组语法)</title> </head> <body> <div id="app"> <!-- 应用驼峰命名:fontSize -- font-size --> <h2 :style="[baseStyle, bgcStyle]">Hello Vue (对象)</h2> <!-- 补充: 绑定函数 --> <h2 class="title" :style="getStyles()">Hello Vue (对象)</h2> </div> <script src="../js/vue.js"></script> <script> const app = new Vue({el: "#app", data: { baseStyle: { fontSize: "100px", color: "red"}, bgcStyle: {backgroundColor: "blue"}, }, methods: {getStyles: function () {return [this.baseStyle, this.bgcStyle]; }, }, }); </script> </body></html>
2.8 computed 属性
- computed 根本语法
<div id="app"> <!-- 间接拼接,过于繁琐 --> <h2>{{firstName +''+ lastName}}</h2> <h2>{{firstName}} {{lastName}}</h2> <!-- Mustache 语法中反对解析办法 --> <h2>{{getFullName()}}</h2> <!-- computed 计算属性 --> <h2>{{fullName}}</h2></div><script src="../js/vue.js"></script><script> const app = new Vue({el:"#app", data: { firstName:"zfcer", lastName:"best",}, // 计算属性:在变量没有批改状况下,第一次执行后有缓存 computed: {fullName: function () {return this.firstName +" "+ this.lastName;}, }, // 办法 options:每次执行都要调用办法 methods: {getFullName: function () {return this.firstName +" " + this.lastName;}, }, });</script>
- computed 简单操作
<div id="app"> 总价格为:{{totalPrice}}</div><script src="../js/vue.js"></script><script> const app = new Vue({el: "#app", data: { books: [ { id: 100, name: "aaa", price: 119}, {id: 101, name: "bbb", price: 120}, {id: 102, name: "ccc", price: 101}, {id: 103, name: "ddd", price: 99}, ], }, computed: {totalPrice: function () {let finalPrices = 0; // for(let i=0; i<this.books.length; i++){// finalPrices += this.books[i].price; // } for (let book of this.books) {finalPrices += book.price;} return finalPrices; }, }, });</script>
- computed 的 setter 和 getter
<div id="app"> <!-- computed 计算属性 --> <h2>{{fullName}}</h2></div><script src="../js/vue.js"></script><script> const app = new Vue({el: "#app", data: { firstName: "zfcer", lastName: "best",}, // 计算属性:在变量没有批改状况下,第一次执行后有缓存 computed: {// 写法 1: 简写 // fullName: function () {// return this.firstName + "" + this.lastName; //}, // 写法 2 // 第一种写法是第二种写法的简写 fullName: {get: function () {// 获取返回值:由 vue 本身来调用 get 办法 return this.firstName +" "+ this.lastName;}, set: function (newValue) {// 个别 computed 只读,所以 setter 办法能够省略 let names = newValue.split(" "); this.firstName = names[0]; this.lastName = names[1]; }, }, }, });</script>
- computed 与 methods 属性比照:computed 有缓存,methods 没有缓存
<div id="app"> <!-- Mustache 语法中反对解析办法 --> <!-- 会执行 6 次,无缓存。--> <h2>{{getFullName()}}</h2> <h2>{{getFullName()}}</h2> <h2>{{getFullName()}}</h2> <h2>{{getFullName()}}</h2> <h2>{{getFullName()}}</h2> <h2>{{getFullName()}}</h2> <!-- computed 计算属性 --> <!-- 会执行 1 次,有缓存。只有当 firstName 或 lastName 产生更改时,才会从新执行 --> <h2>{{fullName}}</h2> <h2>{{fullName}}</h2> <h2>{{fullName}}</h2> <h2>{{fullName}}</h2> <h2>{{fullName}}</h2> <h2>{{fullName}}</h2></div><script src="../js/vue.js"></script><script> const app = new Vue({el: "#app", data: { firstName: "zfcer", lastName: "best",}, // 计算属性:在变量没有批改状况下,第一次执行后有缓存 computed: {fullName: function () {console.log("fullName"); return this.firstName + "" + this.lastName; }, }, // 办法 options:每次执行都要调用办法 methods: {getFullName: function () {console.log("getFullName"); return this.firstName +" " + this.lastName; }, }, });</script>
2.9 v-on 语法
- v-on 工夫监听根本语法:== 在事件监听时,如果办法没有参数,能够省略(); 然而在 Mustache 语法中是不能省略的 ==。v-on 语法糖
@
<div id="app"> <h1> 以后计数:{{counter}}</h1> <button v-on:click="counter++">+</button> <button v-on:click="counter--">-</button> <br /> <br /> <!-- v-on 的语法糖 @ --> <!-- 留神:在事件监听时,如果办法没有参数,能够省略(); 然而在 Mustache 语法中是不能省略的 --> <button @click="add">+</button> <button @click="sub()">-</button></div><script src="../js/vue.js"></script><script> const app = new Vue({el: "#app", data: { counter: 0,}, methods: {add() {console.log("add 执行了"); // 要用 this. this.counter++; }, sub() { this.counter--;}, }, });</script>
- v-on 工夫监听参数问题:浏览器参数能够通过 $event 取得;当函数只须要 event 一个参数时,能够通过省略()的形式取得浏览器产生的 event
<div id="app"> <!-- 当监听事件没有参数时,能够省略 () --> <button @click="btn1Click"> 按钮 1 </button> <!-- 当监听事件须要一个参数,然而省略小括号时,Vue 会默认将浏览器产生的 event 工夫对象作为参数传入到办法中 --> <!-- 不省略() 会呈现 undefined --> <button @click="btn2Click()"> 按钮 2 - 不省略 ()</button> <!-- 省略() 会获取浏览器 event 对象 --> <button @click="btn2Click"> 按钮 2 - 省略()</button> <!-- 当监听事件须要蕴含 event 对象在内的多个参数时,须要借助 $event 传入 event 对象 --> <button @click="btn3Click('zfcer', $event)"> 按钮 3 - 间接传参 </button> <button @click="btn3Click(name, $event)"> 按钮 3 -vue data 传参 </button></div><script src="../js/vue.js"></script><script> const app = new Vue({el: "#app", data: { counter: 0, name: "best",}, methods: {btn1Click() {console.log("btn1Click ..."); }, btn2Click(event) {console.log("btn2Click ...", event); }, btn3Click(name, event) {console.log("btn2Click ...", name, event); }, }, });</script>
-
v-on 事件监听修饰符
- .stop – 调用 event.stopPropagation()。
- .prevent – 调用 event.preventDefault()。
- .{keyCode | keyAlias} – 只当事件是从特定键触发时才触发回调。
- .native – 监听组件根元素的原生事件。
- .once – 只触发一次回调。
<div id="app"> <!-- 1. .stop 修饰符阻止冒泡 --> <div @click="divClick"> aaaaa <br /> <button @click.stop="btn1Click"> 按钮 1 </button> </div> <br /> <br /> <!-- 2. .prevent 修饰符阻止默认提交 --> <form action="www.baidu.com"> <input type="submit" value="提交" @click.prevent="submitClick" /> </form> <br /> <br /> <!-- 3. 监听键盘的键帽:当回车键还原时触发 --> <input type="text" @keyup.enter="keyUp" /> <br /> <br /> <!-- 4. 按钮只触发一次: 理解 --> <button @click.once="btn2Click"> 按钮 2 </button></div><script src="../js/vue.js"></script><script> const app = new Vue({el: "#app", data: { counter: 0, name: "best",}, methods: {divClick() {console.log("divClick"); }, btn1Click() { console.log("btn1Click"); }, submitClick() { console.log("submitClick"); }, keyUp() { console.log("keyUp"); }, btn2Click() { console.log("btn2Click"); }, }, });</script>
2.10 v-if/v-else-if/v-else 语法
- v-if 与 v -else 应用:==v-if 与 v -else 作用在标签上 ==
<div id="app"> <h2 v-if="isShow"> <span>aaa</span> <span>bbb</span> <span>ccc</span> {{message}} </h2> <h2 v-else>isShow 为 false,不显示...</h2></div><script src="../js/vue.js"></script><script> const app = new Vue({el: "#app", data: { message: "zfcer", isShow: true,}, });</script>
- v-else-if
<div id="app"> <!-- 个别简单的逻辑是通过 computed 计算属性来进行解决的 --> <h2 v-if="score>=90"> 优良 </h2> <h2 v-else-if="score>=80"> 良好 </h2> <h2 v-else-if="score>=60"> 及格 </h2> <h2 v-else> 不及格 </h2> <h2> 问题体现:{{result}}</h2></div><script src="../js/vue.js"></script><script> const app = new Vue({el: "#app", data: { message: "zfcer", score: 92,}, computed: {result() {let resultMessage = ""; if (this.score >= 90) resultMessage =" 优良 "; else if (this.score >= 80) resultMessage =" 良好 "; else if (this.score >= 60) resultMessage =" 及格 "; else resultMessage =" 不及格 "; return resultMessage; }, }, });</script>
-
练习:用户登录形式切换。
- 留神:== 因为虚构 DOM 的存在,导致两个 input 控件会复用。== 这里须要特地应用 key 来做辨别,保障登录形式切换后,input 不被复用(明码清空)
<div id="app"> <!-- 留神:因为虚构 DOM 的存在,导致两个 input 控件会复用。这里须要特地应用 key 来做辨别,保障登录形式切换后,input 不被复用(明码清空)--> <span v-if="isUser"> <label for="username"> 用户登录:</label> <input type="text" id="username" key="username-input" placeholder="输出用户明码" /> </span> <span v-else> <label for="email"> 邮箱登录:</label> <input type="text" id="email" key="email-input" placeholder="输出邮箱明码" /> </span> <button @click="btnClick"> 切换登录形式 </button></div><script src="../js/vue.js"></script><script> const app = new Vue({el: "#app", data: { isUser: true,}, methods: {btnClick() {this.isUser = !this.isUser;}, }, });</script>
-
v-if 与 v -show 的区别
- v-if 是真正把标签元素移除了、
- v-show 只是加了个 display: none
留神:== 当元素在显示与暗藏之间频繁切换时,举荐应用 v -show 当元素就是一次切换,举荐应用 v -if==
<div id="app"> <!-- 留神:当元素在显示与暗藏之间频繁切换时,举荐应用 v -show 当元素就是一次切换,举荐应用 v -if --> <!-- v-if 当为 false 时,会使 v -if 润饰的元素不在 DOM 中 --> <h2 v-if="isShow">{{message}}</h2> <!-- v-show 当为 false 时,只是简略的加了 display:none 款式 --> <h2 v-show="isShow">{{message}}</h2></div><script src="../js/vue.js"></script><script> const app = new Vue({el: "#app", data: { message: "zfcer", isShow: true,} });</script>
2.11 v-for 语法
- v-for 遍历数组 & 对象
<div id="app"> <!-- 数组遍历 --> <ul> <li v-for="item in movies">{{item}}</li> </ul> <ul> <li v-for="(item, index) in movies">{{index}} -> {{item}}</li> </ul> <br> <!-- 对象遍历 --> <ul> <li v-for="value in info">{{value}}</li> </ul> <ul> <li v-for="(value, key) in info">{{key}} -> {{value}}</li> </ul> <ul> <li v-for="(value, key, index) in info">{{index}} -> {{key}} -> {{value}}</li> </ul></div><script src="../js/vue.js"></script><script> const app = new Vue({el: '#app', data: { movies: ['aaa', 'bbb', 'ccc', 'ddd'], info: {name: 'zfcer', age: 23, height: 17.8} } })</script>
- v-for 中绑定 key:绑定惟一的 key 后让 v -for 操作更加高效 —DIff 算法
<div id="app"> <!-- 退出 key 的次要作用是为了高效更新 DOM。然而 key 肯定要能惟一代表一个 item(index 显然不行,如果在 BC 之间插入 E,index 就会产生扭转)应用 key 前:在 BC 之间插入 E,先把 C 更新为 E,D 更新为 C,最初插入 D --- 默认 Diff 算法 应用 key 后:间接在 BC 之间插入 E,高效很多 --- 有惟一标识的 Diff 算法 --> <!-- 浏览器输出:app.letters.splice(2, 0, 'E')示意在第二个地位后,删除 0 个元素,插入元素 E --> <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'], } })</script>
2.12 数组的响应式操作
-
数组响应式函数:push、pop、shift、unshift、splice、sort、reverse
- 留神:== 间接依据数组下标批改数组元素不是响应式的 ==
- 非响应式操作改良伎俩:用 splice/set 代替
<div id="app"> <ul> <li v-for="item in letters" :key="item">{{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(){// 响应式操作:push、pop、shift、unshift、splice、sort、reverse // 1. push // this.letters.push('aaa', 'bbb', 'ccc') // 2. pop // 3. shift 删除第一个元素 // this.letters.shift() // 4. unshift 从头插入元素 // this.letters.unshift('aaa', 'bbb', 'ccc') // 5. splice 删除元素 / 插入元素 / 替换元素 // this.letters.splice(1, 1, 'aa', 'bb') // 能够同时插入多个元素 // 6. sort // 7. reverse // 非响应式操作 // this.letters[0] = 'bbbb' // 解决办法:用 splice/set 代替 // this.letters.splice(0, 1, 'bbbb') Vue.set(this.letters, 0, 'bbbb') } } })</script>
2.13 书籍购物车案例
- 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;}
- main.js:== 过滤器属性 filters==
const app = new Vue({el: '#app', data: { books: [ { id: 1, name: 'java study', date: '2021-7-15', count: 1, price: 127.00}, {id: 2, name: 'java study', date: '2021-7-15', count: 1, price: 100.00}, {id: 3, name: 'java study', date: '2021-7-15', count: 2, price: 152.00}, {id: 4, name: 'java study', date: '2021-7-15', count: 3, price: 59.00} ] }, computed: {totalPrice(){let allPrices = 0; // for 循环的三种写法 // for(let book of this.books) // allPrices += book.price * book.count; // for(let i = 0; i<this.books.length; i++){// allPrices += this.books[i].price * this.books[i].count // } // for(let i in this.books){// // i 为索引 // allPrices += this.books[i].price * this.books[i].count // } // 应用 reduce 高阶函数求 price 总和: 箭头函数 allPrices = this.books.reduce((preValue, book) => preValue + book.price * book.count, 0) return allPrices; } }, methods: {showFixedPrice(price){return '¥'+price.toFixed(2); //toFixed(2)保留两位小数 }, decrement(index){if(this.books[index].count == 1) return; this.books[index].count--; }, increment(index){this.books[index].count++; }, removeBook(index){this.books.splice(index, 1); } }, // 过滤器:留神它的应用 {{value | showPrice}} filters: {showPrice(price){return '¥'+price.toFixed(2); //toFixed(2)保留两位小数 } }})
- index.html
<!DOCTYPE html><html lang="en"> <head> <meta charset="UTF-8" /> <title> 书籍购物车案例 </title> <link rel="stylesheet" href="style.css" /> </head> <body> <div id="app"> <table> <thead v-if="books.length"> <tr> <th> 编号 </th> <th> 书籍名 </th> <th> 出版日期 </th> <th> 价格 </th> <th> 数量 </th> <th> 操作 </th> </tr> </thead> <tbody> <tr v-for="(book, index) in books" :key="book.id"> <td>{{book.id}}</td> <td>{{book.name}}</td> <td>{{book.date}}</td> <!-- 函数形式解决价格保留两位小数 --> <!-- <td>{{showFixedPrice(book.price)}}</td> --> <!-- 过滤器形式解决价格保留两位小数 --> <td>{{book.price | showPrice}}</td> <td> <!-- 绑定 disabled 属性 --> <button @click="decrement(index)" :disabled="book.count <= 1"> - </button> {{book.count}} <button @click="increment(index)">+</button> </td> <td><button @click="removeBook(index)"> 移除 </button></td> </tr> </tbody> </table> <div v-if="books.length"> <!-- 复用过滤器 --> <h2> 总价格为:{{totalPrice | showPrice}}</h2> </div> <div v-else><h2> 购物车为空 </h2></div> </div> <script src="../../js/vue.js"></script> <script src="main.js"></script> </body></html>
2.14 v-model 语法
- 表单双向绑定
<div id="app"> <!-- 在输入框上应用 v -model 就是实现双向绑定 --> <input type="text" v-model="message" /> <textarea name=""id="" cols="30" rows="10" v-model="message"></textarea> {{message}}</div><script src="../js/vue.js"></script><script> const app = new Vue({el: "#app", data: { message: "zfcer",}, });</script>
- v-model 原理:==v-bind 与 v-on==
<div id="app"> <!-- v-model 原理:v-bind 与 v-on --> <input type="text" v-model="message" /> <input type="text" :value="message" @input="valueChange($event)" /> <!-- 当 valueChange 不传入参数时,默认会把 $event 传入 --> <input type="text" :value="message" @input="valueChange" /> <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: "zfcer",}, methods: {valueChange(event) {console.log(event); this.message = event.target.value; }, }, });</script>
- v-model 与 radio 联合应用
<div id="app"> <!-- 在不应用 v -model 的状况下,肯定要加 name 属性来保障 radio 互斥 --> <label for="male"> <input type="radio" id="male" name="sex" value="男" v-model="sex" /> 男 </label> <label for="female"> <input type="radio" id="female" name="sex" value="女" v-model="sex" /> 女 </label> <h2>{{sex}}</h2></div><script src="../js/vue.js"></script><script> const app = new Vue({el: "#app", data: { sex: "女",}, });</script>
- v-model 与 checkbox 联合应用
<div id="app"> <!-- 单选框:boolean 类型 --> <label for="agree"> <input type="checkbox" name="agree" value="isAgree" v-model="isAgree" /> 批准协定 </label> <button :disabled="!isAgree"> 下一步 </button> <h2>{{isAgree}}</h2> <br /> <br /> <!-- 复选框:数组类型 --> <input type="checkbox" value="篮球" v-model="hobbits" /> 篮球 <input type="checkbox" value="足球" v-model="hobbits" /> 足球 <input type="checkbox" value="乒乓球" v-model="hobbits" /> 乒乓球 <input type="checkbox" value="羽毛球" v-model="hobbits" /> 羽毛球 <h2>{{hobbits}}</h2></div><script src="../js/vue.js"></script><script> const app = new Vue({el: "#app", data: { isAgree: false, hobbits: [], }, });</script>
- v-model 与 select 联合应用
<div id="app"> <!-- 单选框:boolean 类型 --> <select name="aaa" v-model="fruit"> <option value="苹果"> 苹果 </option> <option value="橘子"> 橘子 </option> <option value="香蕉"> 香蕉 </option> </select> <h2>{{fruit}}</h2> <br /> <br /> <br /> <!-- 复选框:数组类型 --> <!-- 应用 multiple 变为多选框 --> <select name="bbb" v-model="fruits" multiple> <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: { fruit: "香蕉", fruits: [], }, });</script>
- v-model 与 v -bind 值绑定:就是动静的给 value 赋值,如果不应用 v -bind:value 就会导致 value=“f”值就是 f 字符串,而不是 fruit
<div id="app"> <!-- 复选框:数组类型 --> <!-- 通过绑定 label for 和 input id 实现 label 与 input 关联 --> <label :for="f" v-for="f in orginalFruits"> <input type="checkbox" :id="f" :value="f" v-model="fruits" />{{f}} </label> <br /> <br /> <br /> <!-- 复选框:数组类型 --> <!-- 应用 multiple 变为多选框 --> <select v-model="fruits" multiple> <!-- value 属性肯定要应用 v -bind:value --> <option v-for="f in orginalFruits" :value="f">{{f}}</option> </select> <h2>{{fruits}}</h2></div><script src="../js/vue.js"></script><script> const app = new Vue({el: "#app", data: { fruit: "香蕉", fruits: [], orginalFruits: ["苹果", "香蕉", "橘子", "葡萄"], }, });</script>
-
v-model 修饰符的应用
- lazy 懒加载
- number 修饰符
- trim 修饰符
<div id="app"> <!-- 1. lazy 懒加载:只在 input 回车 / 移出焦点下才触发双向绑定 --> <input type="text" v-model.lazy="message" /> {{message}} <br /> <br /> <br /> <!-- 2. number, 限度 input 中类型为 number 类型 --> <!-- 默认状况下,输入框会被转化为 string 解决,即便咱们在 vue 中 age: 23,更改 age 后还是会转为 string --> <!-- 修饰符能够叠加应用 --> <!-- typeof(value)用来展现 value 的类型 --> <input type="text" v-model.number.lazy="age" /> {{age +'age 的类型为:'+ typeof(age)}} <br /> <br /> <br /> <!-- 3. trim, 去除多余首尾空格 --> <input type="text" v-model.trim="message" /> {{message}}</div><script src="../js/vue.js"></script><script> const app = new Vue({el: "#app", data: { message: "zfcer", age: 23,}, });</script>
3 Vue 组件化开发
组件化是 Vue.js 中的重要思维:组件树
3.1 组件化根本应用
-
组件化应用步骤:1 创立组件结构器对象;2 注册组件;3 应用组件
<div id="app"> <!-- 3. 应用组件 --> <my-cpn></my-cpn> <my-cpn></my-cpn></div><script class="lazy" referrerpolicy="no-referrer" data-src="../js/vue.js"></script><script> // 组件化的三步操作 // 1. 创立组件结构器对象 const cpnC = Vue.extend({template: ` <div> <h2> 我是题目 </h2> <p> 我是内容 </p> </div>`,}); // 2. 注册组件(全局组件)Vue.component("my-cpn", cpnC); const app = new Vue({el: "#app", data: { message: "zfcer",}, });</script>
3.2 全局组件 & 部分组件
<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: "zfcer",}, components: {// 这里注册的是部分组件:作用范畴在 div#app 标签下 cpn: cpnC,}, });</script>
3.3 父组件与子组件
<div id="app"> <cpn1></cpn1> <!-- 子组件因为既没有在全局组件中注册,也没有在 Vue 实例中注册,故有效!--> <cpn2></cpn2></div><script src="../js/vue.js"></script><script> // 1. 两个组件结构器 // 留神:父组件肯定要写在子组件前面,不然就会报找不到子组件异样(父组件须要注册子组件)// 子组件 const cpnC2 = Vue.extend({template: `<div><h2> 题目 2 </h2><p> 这是内容局部 2 </p> </div>`,}); // 父组件 const cpnC1 = Vue.extend({template: `<div><h2> 题目 1 </h2><p> 这是内容局部 1 </p><!-- 应用子组件 --><cpn2></cpn2> </div>`, components: { // 注册子组件 cpn2: cpnC2,}, }); // 2. 注册部分组件 const app = new Vue({el: "#app", components: { // 注册部分组件 cpn1: cpnC1,}, });</script>
3.4 组件注册语法糖
- 将 Vue.extend({})省略掉,间接在注册组件时传入 template
<div id="app"> <!-- 3. 应用组件 --> <!-- 这种语法糖的注册形式,实质还是进行了 Vue.extend({})操作 --> <cpn1></cpn1> <cpn2></cpn2></div><script src="../js/vue.js"></script><script> // 组件化的三步操作 // 1. 创立组件结构器对象:省略构建器对象 const cpnC = Vue.extend({template: `<div><h2> 我是题目 </h2><p> 我是内容 </p> </div>`,}); // 2. 注册组件(这里注册的是全局组件,能够在多个 Vue 实例中应用) Vue.component("cpn1", { template: `<div><h2> 我是全局组件 </h2><p> 我是内容 </p> </div>`,}); const app = new Vue({el: "#app", data: { message: "zfcer",}, components: {// 这里注册的是部分组件 cpn2: { template: `<div><h2> 我是部分组件 </h2><p> 我是内容 </p> </div>`,}, }, });</script>
3.5 组件模板的拆散写法
- 举荐应用模板拆散 template 的形式进行模板拆散
<div id="app"> <!-- 3. 应用组件 --> <cpn1></cpn1> <cpn2></cpn2></div><!-- 1. 模板拆散 script 办法 --><script type="text/x-template" id="cpn1"> <div> <h2> 我是题目 </h2> <p> 我是内容 </p> </div></script><!-- 2. 模板拆散 template -- 举荐 --><template id="cpn2"> <div> <h2> 我是题目 </h2> <p> 我是内容 </p> </div></template><script src="../js/vue.js"></script><script> // 2. 注册组件(这里注册的是全局组件,能够在多个 Vue 实例中应用) Vue.component("cpn1", { template: "#cpn1",}); const app = new Vue({el: "#app", data: { message: "zfcer",}, components: {// 这里注册的是部分组件 cpn2: { template: "#cpn2",}, }, });</script>
3.6 组件中数据寄存问题
- 组件中数据应用函数形式寄存:防止多个组件实例之间数据共享导致凌乱
<div id="app"> <!-- 3. 应用组件 --> <!-- data 应用函数的作用:划分作用域,当应用多个组件实例时,如果不必 data 函数,非常容易混同 counter 变量,导致全局 counter 呈现 --> <cpn1></cpn1> <cpn1></cpn1> <cpn1></cpn1> <!-- <cpn2></cpn2> --></div><template id="cpn1"> <div> <!-- 在模板中应用的数据,必须是在组件中 data 函数中定义的,如果只是在 Vue 实例 data 中注册是读取不到的 --> <h2> 以后计数为:{{counter}}</h2> <button @click="decrement" :disabled="counter <= 0">-</button> <button @click="increment">+</button> </div></template><script src="../js/vue.js"></script><script> // 定义全局对象 let obj = {counter: 0,}; // 2. 注册组件(这里注册的是全局组件,能够在多个 Vue 实例中应用) Vue.component("cpn1", { template: "#cpn1", data() {return { // 在组件中定义 counter 变量 counter: 0,}; // return obj; // 此时应用的是同一个 obj 对象,显然不符合要求 }, methods: {decrement() {if (this.counter <= 0) return; this.counter--; }, increment() { this.counter++;}, }, }); const app = new Vue({el: "#app", data: { message: "zfcer123",}, components: {// 这里注册的是部分组件 cpn2: { template: "#cpn1", data() {return { message: "zfcer best",}; }, }, }, });</script>
3.7 父子组件通信
- 父传子(props)
<div id="app"> <!-- 实现父传子:v-bind 绑定子属性 chobbits、cmessage 如果不应用 v -bind 绑定子属性就会把 hobbits、message 辨认为字符串 --> <cpn :chobbits="hobbits" :cmessage="message"></cpn></div><template id="cpn"> <!-- template 模板外面的内容要用 div 包裹一下 --> <div> <ul> <li v-for="hobbit in chobbits">{{hobbit}}</li> </ul> {{cmessage}} </div></template><script src="../js/vue.js"></script><script> // 自定义构造函数 PersonName function PersonName(firstName, lastName) {this.firstName = firstName; this.lastName = lastName;} // 子组件: prop -- 父传子 const cpn = {template: "#cpn", // 1. 间接传值 // props: { // cmessage: "", // chobbits: [], // }, // 2. 退出类型检测,默认值,required props: {cmessage: { // 验证 type: String、Number、Boolean、Array、object、Date、Function、Symbol // 当咱们有自定义构造函数时,验证也反对自定义类型 type: [String, Number, PersonName], default:"aaaa", required: true, // 示意必须加上这个属性 }, chobbits: {type: Array, // default: [], //vue2.5 以上,当 type 是 Array/Object 时不能间接给数组赋默认值 [] {} default() { return []; }, }, }, data() { // 如果写 data,必须要加上 return,否则就会报错 return {}; }, }; // 把 Vue 实例看成 Root 组件 const app = new Vue({el:"#app", data: { message:"zfcer", hobbits: [" 篮球 "," 足球 "," 羽毛球 "], }, // 子组件 components: {// cpn: cpn, // 加强写法 cpn,}, });</script>
- 父传子的驼峰标识问题:v-bind:props 属性时,必须把 props 属性改为驼峰命名
<div id="app"> <!-- 留神:在父传子的时候,因为 v -bind 不反对驼峰命名,这里的 cInfo 必须转为 c -info --> <!-- <cpn :cInfo="info"></cpn> --> <cpn :c-info="info"></cpn></div><template id="cpn"> <!-- template 中尽量用 div 包裹 --> <div> <h2>{{cInfo}}</h2> </div></template><script src="../js/vue.js"></script><script> const cpn = {template: "#cpn", props: { // 在标签上应用 cInfo: { type: Object, defalut() {return {}; }, required: true, }, }, }; const app = new Vue({el: "#app", data: { info: { name: "zfcer", age: 23, height: 1.87,}, }, components: {cpn,}, });</script>
-
子传父
- 子组件 emit 发射出数据
- 子组件标签中通过属性将数据传递给父组件
<div id="app"> <!-- 利用 v -on 依据子组件 emit 出的名字来承受 category-click,防止应用驼峰命名没有传入参数是因为能够默认承受发射进去的 category 对象: 与承受浏览器默认 event 相似 --> <cpn @category-click="recive"></cpn> <div :style="{fontSize:'20px'}">{{result}}</div></div><template id="cpn"> <div> <button v-for="category in categories" @click="btnClick(category)"> {{category.name}} </button> </div></template><script src="../js/vue.js"></script><script> // 子组件 const cpn = {template: "#cpn", data() {return { categories: [ { id: "aaa", name: "热门举荐"}, {id: "bbb", name: "手机数码"}, {id: "ccc", name: "家电家用"}, {id: "ddd", name: "电脑办公"}, ], }; }, methods: {btnClick(category) {// 1. 子组件发射出 event,带名字 category-click, 这里不要用驼峰命名 // 能够发送多个信息,接管时按程序承受即可 this.$emit("category-click", category, "abc"); }, }, }; const app = new Vue({el: "#app", data: { result: null,}, components: {cpn,}, methods: {recive(category, name) {// 父组件接管子组件发射来的 category // 也能够接管多个参数(按程序接管)console.log(category); console.log(name); this.result = category; }, }, });</script>
- 父子通信案例:通过 props 和 emit 实现版
<!--------props 和 emit 实现版 ---------><div id="app"> <cpn :cnum1="num1" :cnum2="num2" @num1change="num1Recive" @num2change="num2Recive" ></cpn></div><template id="cpn"> <div> cnum1: {{cnum1}} <br /> dnum1: {{dnum1}} <br /> <input type="text" :value="dnum1" @input="dnum1Input" /> <br /> <br /> cnum2: {{cnum2}} <br /> dnum2: {{dnum2}} <input type="text" :value="dnum2" @input="dnum2Input" /> </div></template><script src="../js/vue.js"></script><script> const cpn = {template: "#cpn", data() {return { dnum1: this.cnum1, dnum2: this.cnum2,}; }, props: {cnum1: { type: Number, default: 0,}, cnum2: {type: Number, default: 0,}, }, methods: {dnum1Input(event) {this.dnum1 = parseFloat(event.target.value); if (event.target.value == null || event.target.value == "") this.dnum1 = 0; this.$emit("num1change", this.dnum1); this.$emit("num2change", this.dnum1 * 100); }, dnum2Input(event) {this.dnum2 = parseFloat(event.target.value); if (event.target.value == null || event.target.value =="") this.dnum2 = 0; this.$emit("num2change", this.dnum2); this.$emit("num1change", this.dnum2 / 100); }, }, }; const app = new Vue({el: "#app", data: { num1: 1, num2: 2,}, components: {cpn,}, methods: {num1Recive(dnum1) {console.log(typeof dnum1); this.num1 = parseFloat(dnum1); }, num2Recive(dnum2) {console.log(typeof dnum2); this.num2 = parseFloat(dnum2); }, }, });</script>
- 父子通信案例:通过 watch 实现版
<div id="app"> <cpn :cnum1="num1" :cnum2="num2" @num1change="num1Recive" @num2change="num2Recive" ></cpn></div><template id="cpn"> <div> cnum1: {{cnum1}} <br /> dnum1: {{dnum1}} <br /> <input type="text" v-model.lazy="dnum1" /> <br /> <br /> cnum2: {{cnum2}} <br /> dnum2: {{dnum2}} <input type="text" v-model.lazy="dnum2" /> </div></template><script src="../js/vue.js"></script><script> const cpn = {template: "#cpn", data() {return { dnum1: this.cnum1, dnum2: this.cnum2,}; }, props: {cnum1: { type: Number, default: 0,}, cnum2: {type: Number, default: 0,}, }, methods: {// dnum1Input(event) {// this.dnum1 = parseFloat(event.target.value); // if (event.target.value == null || event.target.value == "") // this.dnum1 = 0; // this.$emit("num1change", this.dnum1); // this.$emit("num2change", this.dnum1 * 100); // }, // dnum2Input(event) {// this.dnum2 = parseFloat(event.target.value); // if (event.target.value == null || event.target.value =="") // this.dnum2 = 0; // this.$emit("num2change", this.dnum2); // this.$emit("num1change", this.dnum2 / 100); // }, }, watch: {dnum1(newValue, oldValue) {// 函数名与监听的变量名同名,newValue 示意监听的新值,oldValue 示意监听的旧值 console.log(oldValue) this.dnum2 = newValue * 100; //dnum2 的扭转也会被 watch 监听到 this.$emit("num1change", newValue); }, dnum2(newValue) {this.dnum1 = newValue / 100; //dnum1 的扭转也会被 watch 监听到 this.$emit("num2change", newValue); }, }, }; const app = new Vue({el: "#app", data: { num1: 1, num2: 2,}, components: {cpn,}, methods: {num1Recive(dnum1) {console.log(typeof dnum1); this.num1 = parseFloat(dnum1); }, num2Recive(dnum2) {console.log(typeof dnum2); this.num2 = parseFloat(dnum2); }, }, });</script>
3.8 组件拜访
-
父拜访子($childern、\$refs)
- 在组件标签上设置 ref 属性名后,就能够通过 $childern、\$refs 实现对子组件数据的拜访。== 举荐应用 $refs==。
<div id="app"> <cpn></cpn> <cpn></cpn> <!-- 用 ref 属性标识,这样 $refs.aaa 就能够间接取出这个组件实例 --> <cpn ref="aaa"></cpn> <button @click="btnClick"> 按钮 </button></div><template id="cpn"> <div> <h2> 这是组件 </h2> </div></template><script src="../js/vue.js"></script><script> const app = new Vue({el: '#app', components: { cpn: { template: '#cpn', data(){return { name: '我是 cpn 组件'} } } }, methods: {btnClick(){// 1. $childern 利用索引下标从数组中取 vueComponent 元素 -- vueComponent 元素示意组件实例 // this.$children 不举荐应用,因为当插入新元素后,数组索引下标后扭转 // console.log(this.$children[0].name) // 2. this.$refs:举荐应用 console.log(this.$refs.aaa.name) this.$refs.aaa.name = "hahahaahaha"; // 批改 console.log('按钮执行了') } } })</script>
- 子拜访父($parent、\$root)
<div id="app"> <cpn></cpn></div><template id="cpn"> <div> <h2> 这是 cpn 组件 </h2> <ccpn></ccpn> </div></template><template id="ccpn"> <div> <h2> 这是 ccpn 组件 </h2> <button @click="btnClick"> 按钮 </button> </div></template><script src="../js/vue.js"></script><script> const app = new Vue({el: '#app', data: { message: 'zfcer root'}, components: {cpn: { template: '#cpn', data(){return { name: '我是 cpn 组件'} }, // 在 cpn 下 components: {ccpn: { template: '#ccpn', data(){return { name: '我是 ccpn 组件'} }, methods: {btnClick(){// 1. this.$parent: 获取以后 vueComponent 元素的父组件 // 不举荐应用,个别组件要尽量减少与其余组件的分割 // console.log(this.$parent) // console.log(this.$parent.name) // 2. this.$root: 间接获取 root 组件 -- vue 实例 console.log(this.$root) console.log(this.$root.message) console.log('按钮执行了') } } } }, } }, })</script>
3.9 slot 插槽
- slot 根本应用
<div id="app"> <!-- 应用默认标签 --> <cpn></cpn> <cpn><span>haha</span></cpn> <cpn><span>hehe</span></cpn> <cpn> <h2> 插槽标签 1 </h2> <span> 插槽标签 2 </span> <button> 插槽按钮 </button> </cpn> <cpn></cpn></div><template id="cpn"> <div> <h2> 组件题目 </h2> <span> 组件内容 </span> <!-- 定义插槽 --> <!-- 1. 应用空插槽 2. 插槽中能够设置默认标签 button3. 插槽中能够插入多个标签 --> <slot><button> 组件按钮 </button></slot> </div></template><script src="../js/vue.js"></script><script> const cpn = {template: '#cpn',} const app = new Vue({el: '#app', data: { message: 'zfer'}, components: {cpn} })</script>
- slot 具名插槽
<div id="app"> <!-- 应用默认标签 --> <cpn></cpn> <!-- 只能替换没有设置 name 的插槽 --> <cpn><span>haha</span></cpn> <!-- 替换指定插槽 --> <cpn> <span slot="left">left</span> <span slot="center">center</span> <span slot="right">right</span> </cpn></div><template id="cpn"> <div> <!-- 当有多个插槽时,能够给 slot 设置 name 属性,来指定 slot 插入一旦 slot 设置了 name 属性,在应用的时候标签中必须应用 slot 属性指定插槽的 name --> <!-- 默认插槽 --> <slot><button> 组件按钮 </button></slot> <slot name="left"><button> 返回 </button></slot> <slot name="center"><input type="text" value="请输出搜寻内容"></slot> <slot name="right"><button> 搜寻 </button></slot> </div></template><script src="../js/vue.js"></script><script> const cpn = {template: '#cpn',} const app = new Vue({el: '#app', data: { message: 'zfer'}, components: {cpn} })</script>
-
作用域插槽
- 编译的作用域:== 父组件模板的所有货色都会在父级作用域内编译;子组件模板的所有货色都会在子级作用域内编译 ==
- 作用域插槽的应用:1 template 标签上绑定数据 languages;2 组件标签上应用 slot-scope 获取 slotProps
<!-- 父组件替换插槽的标签,然而内容由子组件来提供 --><div id="app"> <cpn></cpn> <cpn> <!-- 2. 在应用组件时,最好在 template 标签下应用 slot-scope 属性,来获取组件传来的值 --> <!-- vue2.5.x 版本以前必须应用 template 标签包裹 --> <template slot-scope="slot"> <!-- join 函数 --> <span>{{slot.languages.join('*')}}</span> </template> </cpn></div><template id="cpn"> <div> <!-- 1. 在模板 slot 上绑定 data --- 命名是随便的然而必须应用 v -bind --> <slot :languages="languages"> <ul> <li v-for="language in languages">{{language}}</li> </ul> </slot> </div></template><script src="../js/vue.js"></script><script> const app = new Vue({el: "#app", components: { cpn: { template: "#cpn", data() {return { languages: ["java", "vue", "javascript", "c++"], }; }, }, }, });</script>
4 Vue CLI 详解
4.1 ES6 模块化实现
- 导入形式
// 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/classimport {mul, Person} from "./aaa.js";console.log(mul(30, 50));const p = new Person();p.run()// 4. 导入 export default 中的内容,能够省略{}import defaultname from "./aaa.js";defaultname('你好啊');// 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);
- 导出形式
var name = '小明'var age = 18var flag = truefunction 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('在奔跑'); }}// 4.export default// 一个模块内只能有一个 export default,不然导出的时候就乱套了 const address = '北京市'export default addressexport default function (argument) {console.log(argument);}
4.2 webpack 应用
-
webpack 起步
-
装置 webpack,须要 node.js 的环境
- node -v #查看 node 版本
- npm install webpack@3.6.0 -g #全局装置 webpack3.6.0
- npm install webpack@3.6.0 –save-dev #部分装置 webpack3.6.0
- 留神:在终端间接执行 webpack 命令,应用的全局装置的 webpack;当在 package.json 中定义了 scripts 时,其中蕴含了 webpack 命令,那么应用的是部分 webpack。
- webpack 打包指令:
webpack src/main.js dist/bundle.js
-
//1. webpack 导入形式 const {add, mul} = require('./mathUtils.js')//2. webpack 导出形式 function add(num1, num2){return num1+num2}function mul(num1, num2){return num1*num2}// webpack 形式导出 module.exports = {add, mul}
-
webpack 配置
-
通过
npm init
生成package.json
文件, 并配置脚本{"name": "meetwebpack", "version": "1.0.0", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\"&& exit 1", "build": "webpack"}, "author": "","license":"ISC","devDependencies": {"babel-core":"^6.26.3","babel-loader":"^7.1.5","babel-preset-es2015":"^6.24.1","css-loader":"^2.0.2","file-loader":"^3.0.1","less":"^3.9.0","less-loader":"^4.1.0","style-loader":"^0.23.1","url-loader":"^1.1.2","webpack":"^3.6.0"},"description":""}
-
创立
webpack.config.js
文件, 配置入口、进口// 这里导入的 path 是 node.js 中的 path: 通过 npm init 获取 node 中的 path 环境 const path = require('path')module.exports = {entry: './src/main.js', output: { // __dirname 的下划线是两个 path: path.resolve(__dirname, 'dist'), filename: 'bundle.js' }}// 通过 npm install webpack@3.6.0 --save-dev 装置部分开发 webpack,// 最初会生成 package.json、package-lock.json 文件
-
-
webpack 的 loader:在开发中咱们不仅仅有根本的 js 代码解决,咱们也须要加载 css、图片,也包含一些高级的将 ES6 转成 ES5 代码,将 TypeScript 转成 ES5 代码,将 scss、less 转成 css,将.jsx、.vue 文件转成 js 文件等等。给 webpack 扩大对应的 loader 就能够实现上述的性能。
- loader 应用过程:1 npm 装置对应的 loader(loader 能够在 webpack 官网上找到);2 在 webpack.config.js 中的 modules 关键字下进行配置;
- 装置的 loader:css-loader、style-loader、less-loader、url-loader、file-loader、ES6 语法解决 loader(babel-loader、babel-core、babel-preset-es2015)
//package.json 中的 devDependencies"devDependencies": {"babel-core": "^6.26.3", "babel-loader": "^7.1.5", "babel-preset-es2015": "^6.24.1", "css-loader": "^2.0.2", "file-loader": "^3.0.1", "less": "^3.9.0", "less-loader": "^4.1.0", "style-loader": "^0.23.1", "url-loader": "^1.1.2", "webpack": "^3.6.0"}, // webpack.config.js 中的 moudlemodule: {rules: [ { test: /\.css$/i, // css-loader 只负责将 css 文件进行加载 // style-loader 负责将款式增加到 DOM 中 // 应用多个 loader 时,是从右往左加载 -- 留神先后顺序 use: ["style-loader", "css-loader"], }, {test: /\.less$/i, loader: [ // compiles Less to CSS "style-loader", "css-loader", "less-loader",], }, {test: /\.(png|jpg|gif)$/i, use: [{ loader: 'url-loader', options: { // 当加载的图片,小于 limit 时,会将图片编译为 base64 字符串模式 -- 不须要打包到 dist 中 // 当加载的图片,大于 limit 时,须要应用 file-loader 模块进行加载 -- 须要打包到 dist 中 limit: 5632, // 人为设置打包到 dist 文件夹下的图片门路,hash:8 标识截取 hash 的前 8 位做图片名;ext 是图片后缀 name: 'img/[name].[hash:8].[ext]' }, }, ], }, {test: /\.js$/, exclude: /(node_modules|bower_components)/, use: {// 利用 babel 将 es6 转为 es5,// 装置命令如下:npm install --save-dev babel-loader@7.1.5 babel-core@6.26.3 babel-preset-es2015@6.24.1 loader: 'babel-loader', options: { presets: ['es2015'] } } } ],}, //main.js 中导入 css、less// 导入 base.css 文件 require('./css/base.css')// 导入.less 文件 require('./css/special.less')
-
webpack 配置 vue
- 装置 vue:
npm install vue --save
-
runtime-only 问题:在 webpack.config.js 中配置
// 增加 vue:runtime-compiler 环境 resolve: {alias: { 'vue$': 'vue/dist/vue.esm.js' // 用 webpack 时需用 'vue/dist/vue.common.js'}}
- .vue 文件封装解决::装置 vue-loader、vue-template-compiler
- 装置 vue:
-
webpack 配置 plugin:loader 次要用于转换某些类型的模块,它是一个转换器;plugin 是插件,它是对 webpack 自身的扩大,是一个扩展器。
- plugin 的应用过程:1 通过 npm 装置须要应用的 plugins;2 在 webpack.config.js 中的 plugins 中配置插件
//webpack.config.js// 援用插件 plugins: [new HtmlWebpackPlugin({ template: 'index.html' // 为了在 dist index.html 中配置 index.html 中的 div#app}), new UglifyJsPlugin(), // 压缩 dist 中的 bundle.js new webpack.BannerPlugin('最终版权归 zfcer 所有'),]
-
webpack-server 配置
- 装置 webpack-server:
npm install --save-dev webpack-dev-server@2.9.1
-
在 webpack.config.js 中配置
devServer: {contentBase: './dist', inline: true //inline 示意页面实时刷新}
- 装置 webpack-server:
-
配置拆散
- 配置 base.config.js,在 dev.config.js 和 pro.config.js 中导入根本配置
-
在 package.json 中应用配置
"scripts": {"test": "echo \"Error: no test specified\"&& exit 1", "build": "webpack --config ./build/pro.config.js", "dev": "webpack-dev-server --open --config ./build/dev.config.js"},
4.3 Vue CLI2
- 装置 Vue CLI:
npm install -g @vue/cli@3.0.4
、npm install -g @vue/cli-init
依照 Vue CLI2 的形式初始化我的项目 -
初始化我的项目:
vue init webpack my-project
-
目录构造
-
Runtime-Compiler 和 Runtime-only 的区别
-
Vue 程序运行过程:template –> ast –> render –> VDOM –> UI
当咱们在应用.vue 文件时,==.vue 中的 template 是由 vue-template-compiler 来解决的 ==,所以能够间接应用 render 来渲染。runtime only , 比 runtime compiler 少 6k 空间(效率更高,空间更小)。
-
-
npm run build
-
npm run dev
-
webpack.base.conf.js 起别名
4.4 Vue CLI3
-
Vue CLI3 与 Vue CLI2 区别
- vue-cli 3 是基于 webpack 4 打造,vue-cli 2 还是 webapck 3
- vue-cli 3 的设计准则是“0 配置”,移除的配置文件根目录下的,build 和 config 等目录
- vue-cli 3 提供了 vue ui 命令,提供了可视化配置,更加人性化
- 移除了 static 文件夹,新增了 public 文件夹,并且 index.html 挪动到 public 中
-
创立 Vue CLI3 我的项目:
npm create my-project
-
目录构造
-
在
vue ui
上查看配置:一大堆配置文件被放在 node_modules/@vue/cli-service/lib/Service.js 文件下 -
起别名,在我的项目目录下创立
vue.config.js
文件
5 vue-router
5.1 vue-router 的应用
-
后端路由 & 前端路由
- 后端路由:晚期的网站开发整个 HTML 页面是由服务器来渲染的(JSP)。当咱们页面中须要申请不同的 门路 内容时, 交给服务器来进行解决, 服务器渲染好整个页面, 并且将页面返回给客户端.p 这种状况下渲染好的页面, 不须要独自加载任何的 js 和 css, 能够间接交给浏览器展现, 这样也有利于 SEO 的优化.
-
前端路由:
- 前后端拆散阶段:后端只提供 API 来返回数据, 前端通过 Ajax 获取数据, 并且能够通过 JavaScript 将数据渲染到页面中。(依据 Ajax 申请从动态服务器中获取对应的动态资源)
- 单页面富利用 SPA 阶段:SPA 最次要的特点就是在前后端拆散的根底上加 了一层前端路由。(依据 axios 路由申请一次性把所有动态资源获取(路由 lazy 加载))
-
URL 变更
- location.href 来批改 url
- history.pushState({}, ”, ‘/foo’)相当于压栈,在浏览器中能够点击返回按钮
- history.replaceState({}, ”, ‘/foo’), 在浏览器中不能点击返回按钮
- history.go(-1)弹栈一个元素,等价于 history.back()(并不是真正弹栈)
- history.forward() 则等价于 history.go(1)
- 装置 vue-router:
npm install vue-router --save
-
应用 vue-router:1 创立路由组件;2 配置路由映射:组件与门路映射关系;3 应用路由:通过 <router-link> 和 <router-view>
- 路由嵌套:children: []
- 参数传递:动静 url,’/user/:userId’ 传递参数 userId。在组件中通过
this.$route.params.userId
来获取 - 导航守卫:通过在 beforeEach 中利用 to 来获取 meta 来传递导航栏题目, 通过
to.matched[0].meta.title
来获取 meta 参数 -
kepp-alive:keep-alive 包裹 router-view 设置拜访缓存, 这样能够让组件 created 只执行一次(缓存)。组件内的 activated 和 deactived 生命周期函数必须在设置 keep-alive 标签的包裹下来失效
- <router-link>: 该标签是一个 vue-router 中曾经内置的组件, 它会被渲染成一个标签.
- <router-view>: 该标签会依据以后的门路, 动静渲染出不同的组件.
//index.jsimport Vue from 'vue'import Router from 'vue-router'// 路由懒加载:不要间接注册组件 const Home = () => import('@/components/Home')const HomeMessages = () => import('@/components/HomeMessages')const HomeNews = () => import('@/components/HomeNews')const About = () => import('@/components/About')const User = () => import('@/components/User')const Profile = () => import('@/components/Profile')// 1. 通过 Vue.use(插件),装置插件 Vue.use(Router)// 源码剖析:Vue.use(Router)的外部操作是 install(Vue 类), 在 install 外部全局注册了 RouterView 和 RouterLinkconst routes = [{ path: '/', redirect: '/home', meta: { title: '首页'}, }, {path: '/home', component: Home, meta: { title: '首页'}, children: [{ path: '', component: HomeNews}, {path:'news', //children 中的 path 肯定不能加'/',routes 中的 path 能够加'/'component: HomeNews}, {path:'messages', component: HomeMessages} ] }, {path:'/about', component: About, meta: { title:' 对于 '}, }, {path:'/user/:userId', // 注册动静 url component: User, meta: { title:' 用户 '}, }, {path:'/profile', component: Profile, meta: { title:' 档案 '}, }]// 2. 创立 Router 对象, 并导出 Routerconst router = new Router({routes, mode:'history', // 默认应用的是 hash, 这里替换为 history 形式没有# linkActiveClass:'active', //linkActiveClass 在路由上设置默认激活款式; active 是 App.vue 中定义的 style})router.beforeEach((to, from, next) => {document.title = to.matched[0].meta.title // 获取 meta.title next() // 开释 router,相似 filter 成果 console.log(to) // 当存在 children 时,间接取 meta 是没有值的。通过打印发现,在 matched 数组中有要的 meta.title})// 3. 将 router 对象传入的 Vue 实例中 export default router
//main.jsimport Vue from 'vue'import App from './App'import router from './router' // 主动找 router/index.jsVue.config.productionTip = false/* eslint-disable no-new */new Vue({el: '#app', router, // 将 router 传给 Vue 类中的 $router, 所以 router 与 $router 是同一个对象 render: h => h(App)})
<template><div id="app"> <!-- router-link 申请 path --> <!-- router-link 属性:to 示意映射的 path;tag 示意渲染成其余标签;replace 示意应用 replaceStatus,让浏览器无奈记录栈,而不能操作返回按钮;active-class 当 <router-link> 对应的路由匹配胜利时, 会主动给以后元素设置一个 router-link-active 的 classrouter-link-active 是 router-link 标签中被选中的元素默认加上的 class --> <!-- <router-link to="/home" tag="button" replace active-class="active">Home</router-link><router-link to="/about" replace active-class="active">About</router-link> --> <!-- <router-link to="/home" tag="button" replace>Home</router-link><router-link to="/about" replace>About</router-link> --> <br> <br> <br> <button @click="homeClick">Home</button> <button @click="aboutClick">About</button> <!-- <router-link :to="'/user/'+userId">User</router-link><router-link :to="{path: '/profile', query: {name: 'zfcer', age: 23, height: 1.87}}">Profile</router-link> --> <button @click="userClick">User</button> <button @click="profileClick">Profile</button> <!-- router-view 占位展现 --> <!-- keep-alive 包裹 router-view 设置拜访缓存, 这样能够让组件 created 只执行一次(缓存)--> <!-- exclude 能够用来勾销组件缓存,exclude 自身是正则表达式,所以不能有空格 --> <keep-alive exclude="User"> <router-view/> </keep-alive> </div></template><script> export default {name:'App', data(){return { userId:'1206682'} }, methods: {homeClick(){this.$router.push('/home') console.log('homeClick run') }, aboutClick(){ this.$router.push('/about') console.log('aboutClick run') }, userClick(){ this.$router.push('/user/'+ this.userId) }, profileClick(){ this.$router.push({ path:'/profile', query: { name:'best', age: 18, height: 1.87} }) } } } // 1. $router 和 $route 是 Vue 类的原型中定义的:// 源码中的 install.js 中通过 object.defineProperty(Vue.prototype,'$router', ...)实现 $router 和 $route 的注册 // 2. 所有本人创立的组件都继承自 Vue 类,所以都有 $router 和 $route // 3. 将 router(main.js 中定义的)传给 Vue 类中的 $router, 所以 router 与 $router 是同一个对象 // 4. $route 示意以后被激活的 route // $router 与 $route 都来自 Vue 类中的 this._routerRoot 对象 </script><style> /* router-link-active 是 vue 默认加上的 class */ .active{color: #f00;}</style>
-
$router 和 $route 阐明
- $router 和 $route 是 Vue 类的原型中定义的:源码中的 install.js 中通过
object.defineProperty(Vue.prototype, '$router', ...)
实现$router 和 $route 的注册 - 所有本人创立的组件都继承自 Vue 类,所以都有 $router 和 $route
- 将 router(main.js 中定义的)传给 Vue 类中的 $router, 所以 router 与 $router 是同一个对象
- $route 示意以后被激活的 route
- $router 与 $route 都来自 Vue 类中的 this._routerRoot 对象
- $router 和 $route 是 Vue 类的原型中定义的:源码中的 install.js 中通过
5.2 案例 tabbar
学会封装组件
6 Vuex 详解
6.1 Promise 语法
// Promise 根本应用 new Promise((resolve, reject) => {setTimeout(() => {// 胜利的时候调用 resolve resolve("Hello World"); // 失败的时候调用 reject // reject("error message"); }, 1000);}) .then((data) => {// data 是 resolve 传过来的数据 // 1.100 行的解决代码 console.log(data); console.log(data); console.log(data); console.log(data); console.log(data);}) .catch((err) => {// err 是 reject 传过来的数据 console.log(err);});// Promise all 实现两个申请同时实现后再进行下一步操作 Promise.all([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);});
6.2 Vuex 应用
Vuex 就是为了提供这样一个在多个组件间共享状态的插件,Vuex 扮演着大管家的角色(全局单例模式)
- vuex 装置:npm install vuex –save
-
vuex 应用步骤:1 注册插件;2 创建对象并导出 store;3 应用 store
//index.jsimport Vue from 'vue'import Vuex from 'vuex'import {INCREMENT} from '@/store/mutations-types.js'const moduleA = {state: { // 模块中的 state 调用:this.$store.state.a.name name: 'zfcer'}, mutations: {// 模块中的 mutations 调用:this.$store.commit('mutations 名字'),调用形式不变 updateName(state, payload){state.name = payload} }, actions: {aUpdateName(context) {setTimeout(() => {console.log('模块内 actions 执行了') // 模块内的 actions 只能调用模块内的 mutations context.commit('updateName', '模块内的 actions 调用了模块内的 mutations') }) } }, getters: {// 模块中的 getters 调用:this.$store.getters.fullName 调用形式不变。所以办法名不要与模块外的办法名反复 fullName(state){return state.name + '111'}, fullName2(state, getters){return getters.name + '222'}, fullName3(state, getters, rootState){// rootState 获取 Root 的 state return getters.fullName2 + rootState.counter} }, modules: {}}// 1. 注册插件 Vue.use(Vuex)// 2. 创建对象, 并导出 export default new Vuex.Store({state: { // 共享变量 counter: 1000, students: [ {id: 110, name: 'zfcer', age: 23}, {id: 111, name: 'best', age: 26}, {id: 112, name: 'one', age: 18}, ], info: {name: 'zfcer', age: 23, height: 1.87} }, mutations: {// Vuex 的 store 状态的更新惟一形式:提交 Mutations // 办法中是 默认传入 state 参数的 // mutations 中提交参数能够通过 payload 提交对象 [INCREMENT](state){state.counter++}, decrement(state){state.counter--}, incrementCountOne(state, payload){console.log(payload) //payload 是一个对象 }, incrementCount(state, payload){console.log(payload.cnt1 + payload.cnt2) state.counter += payload.cnt1 }, addStudent(state, stu){state.students.push(stu) }, updateInfo(state){// 间接批改 info 中已有的属性是响应式的,这是因为 Vue 对属性做了监听能够做到响应式 state.info.name = 'zfcer best' // 间接在 info 中增加新属性不是响应式的 // state.info['address'] = '苏州' // Vue.set(state.info, 'address', '苏州') // 利用 Vue.set 实现响应式 // 间接删除 info 中属性不是响应式的 // delete state.info.age // Vue.delete(state.info, 'age') // 利用 Vue.delete 实现响应式 } }, actions: {// actions 用来解决异步操作 // 办法一 // context 上下文,能够了解为就是 store 对象 // payload 传递参数 // aUpdateInfo(context, payload){// console.log("---------------") // 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('外部执行胜利') // 传递信息能够被内部 then 获取 }, 1000) }) } }, getters: {// 与计算属性相似 powerCounter(state){return state.counter * state.counter}, more20Stus(state){return state.students.filter(s => s.age >= 20) }, more20StusLength(state, getters){// 在 getters 中能够定义 getters 参数来取得 getters 中其余函数 return getters.more20Stus.length}, moreAgeStus(state){// 通过外部创立函数来取得 moreAgeStus 传过来的参数 // return function(age){// return state.students.filter(s => s.age >= age) // } return age => state.students.filter(s => s.age >= age) } }, modules: {// 模块最终会被加载到 state 中,还是一个实体 a: moduleA}})// 对象的解构 const obj = {name: 'zfcer', age: 18, height: 1.87}const {name, height, age} = obj// 数组解构 const names = ['zfcer', 'best', 'one']const [name1, name2, name3] = names
// main.jsimport Vue from 'vue'import App from './App'import router from './router'import store from './store'Vue.config.productionTip = false/* eslint-disable no-new */new Vue({el: '#app', router, store, //3. 应用 store render: h => h(App)})
-
vuex 我的项目构造
7 axios
- 全局 Axios
// 一、间接用 axios 是用的全局 Axios// axios 全局配置 axios.defaults.baseURL = 'http://152.136.185.210:7878/api/m5'axios.defaults,headers.post['Content-Type'] = 'application/x-www-form-urlencoded'axios.defaults.timeout = 5000// 1. axios 根本应用 // 指定 method 的申请,默认是 get 申请 axios({// 部分配置 baseURL: 'http://152.136.185.210:7878/api/m5', url: '/home/multidata', method: 'get'}).then(res => console.log(res))// get 申请 axios.get('/home/multidata')// 带参数的 get 申请 axios({url: '/home/data', // 针对 get 申请的参数拼接 params: { type: 'pop', page: 1}}).then(res => console.log(res))// 2. axios 并发应用 // then 中获取后果集 axios.all([axios({ url: '/home/multidata',}), axios({url: '/home/data', // 针对 get 申请的参数拼接 params: { type: 'sell', page: 5}})]).then(res => { // 合并后的后果 console.log(res)})// then 中获取别离的后果 axios.all([axios({ url: 'http://123.207.32.32:8000/home/multidata',}), axios({url: 'http://152.136.185.210:7878/api/m5/home/data', // 针对 get 申请的参数拼接 params: { type: 'sell', page: 5}})]).then(axios.spread((res1, res2) => {// 两个异步申请别离的后果 console.log(res1) console.log(res2)}))
- 部分 Axios
// 二、axios 实例创立与应用 const instance = axios.create({baseURL: 'http://152.136.185.210:7878/api/m5', timeout: 5000, headers: { 'Content-Type': 'application/x-www-form-urlencoded'}})instance({url: '/home/data', method: 'get'}).then(res => { console.log(res)}).catch(err => { console.log(err)})
- 封装 axios:创立 network 文件夹 –>request.js 工具类,在 main.js 中调用封装的 request
//--------------------request.js---------------------------import axios from 'axios'export function request1(config, success, failure){// 1. 创立 axios 实例 const instance = axios.create({ baseURL: 'http://152.136.185.210:7878/api/m5', timeout: 5000}) // 2.1.axios 拦截器 instance.interceptors.request.use(config => { console.log(config) // (1)config 中一些信息不合乎服务器要求须要拦挡 // (2)每次发送网络申请时,都心愿在界面中显示一个申请的图标 // (3)某些网络申请(比方登录 token),必须携带一些非凡信息 return config // 开释拦挡, 如果不开释拦挡就无奈继续执行 }, err => {console.log(err) }) // 2.2.axios 响应拦挡 instance.interceptors.response.use(res => { console.log(res) // 做一些响应解决拦挡 return res.data // 只须要把 data 信息返回即可 }, err => {console.log(err) }) // 3. 发送真正的网络申请 instance(config) .then(res => { // console.log(res); success(res) }) .catch(err => { // console.log(err) failure(err) })}export function request2(config){// 创立 axios 实例 const instance = axios.create({ baseURL: 'http://152.136.185.210:7878/api/m5', timeout: 5000}) // 发送真正的网络申请 instance(config.baseConfig) .then(res => { // console.log(res); config.success(res) }) .catch(err => { // console.log(err) config.failure(err) })}export function request3(config) {return new Promise((resolve, reject) => {// 创立 axios 实例 const isntance = axios.create({ baseURL: 'http://152.136.185.210:7878/api/m5', timeout: 5000}) // 发送真正的网络申请 instance(config) .then(res => { resolve(res) }) .catch(err => { reject(err) }) })}export function request4(config) {// 创立 axios 实例 const instance = axios.create({ baseURL: 'http://152.136.185.210:7878/api/m5', timeout: 5000}) // 发送真正的网络申请 return instance(config) // 因为 axios 实例自身就是 Promise}//-------------------main.js----------------------------// 三、axios 封装 import {request1, request2, request3, request4} from '@/network/request.js'request1({url: '/home/multidata'}, res => {console.log(res)}, err => {console.log(err)})request2({baseConfig: '/home/multidata', success: function(res){console.log(res) }, failure: function(err){console.log(err) }})request3({url: '/home/multidata'}).then(res => console.log(res)).catch(err => console.log(err))request4({url: '/home/multidata'}).then(res => console.log(res)).catch(err => console.log(err))
8 我的项目实战
将本地我的项目与近程仓库关联起来:
git remote add origin https://github.com/zfcer/testmall.gitgit push -u origin master
8.1 我的项目根本设置
- 我的项目构造
-
设置 css 款式和全局款式
- initialize.css:normalize.css,能够在 github 中查看 normalize.css
- base.css:定义全局款式
-
vue.config.js 全局配置:起别名
module.exports = {configureWebpack: { resolve: { alias: { 'assets': '@/assets', 'common': '@/common', 'components': '@/components', 'network': '@/network', 'views': '@/views',} } }}
-
.editorconfig 全局配置我的项目编辑
root = true[*]charset = utf-8indent_style = spaceindent_size = 2end_of_line = lfinsert_final_newline = truetrim_trailing_whitespace = true
8.2 tabbar& 导航栏组件
-
tabbar 组件
- 导入写好的 TabBar、TabBarItem、MainTabBar 组件
- 装置 vue-router,编写 index.js 路由,最初在 mian.js 中注入 router
- 编写对应的 views
- 在 App.vue 中引入组件,template 中应用组件
-
导航栏组件
- 创立通用组件 NarBar, 定义三个 slot(left、center、right)
- 在 views 中应用 NarBar 组件
8.3 首页 – 轮播图 & 举荐组件
- axios 封装
-
轮播图
- 创立 swiper 通用组件
- 在 home 文件夹下创立 childComps/HomeSwiper(应用 swiper 通用组件)
- 在 Home.vue 通过 axios 获取数据,在 HomeSwiper 中通过 props 获取数据并展现
- 在 Home.vue 中应用 HomeSwiper 组件
-
举荐组件
- 在 /src/views/home/childCops 创立 RecommendView.vue 组件
- 在 Home.vue 通过 axios 获取数据,在 RecommendView 中通过 props 获取数据并展现
- 在 Home.vue 中应用 RecommendView 组件