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 selfvm._self = vminitLifecycle(vm)initEvents(vm)initRender(vm)callHook(vm, 'beforeCreate')initInjections(vm) // resolve injections before data/propsinitState(vm)initProvide(vm) // resolve provide after data/propscallHook(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组件