Vue笔记0.2
文章目录
- 7.1 可变参数
- 7.2 for of 的使用**
- 7.3 高阶函数 filter/map/reduce
- 7.3.1.filter函数的使用
- 7.3.2.map函数的使用
- 7.3.3.reduce函数的使用
- 4.高阶函数链式编程
- 10.1 遍历数组
- 10.2遍历对象
- 10.2 v-for使用过程绑定key
- 10.3哪些数组的方法是响应式的
- push(), pop(), shift(), unshift(), splice(), sort(), reverse()
- 10.3.1 一个点击变色小作业的实现
- 12.1 v-model基本使用
- 12.2 v-model原理
- 12.3 v-model结合radio类型
- 12.4 v-model结合checkbox单选、多选框使用
- 12.5 v-model结合checkbox进行值绑定
- 12.6 v-model结合select
- 12.7 v-model修饰符的使用
- 13.1 注册组建的基本步骤
- 13.2 注册组件步骤解析
- 13.3 局部组件和全局组件
- 13.4 父组件和子组件
- 13.5 组件的语法糖注册方式
- 13.6 模板的抽离写法
- 13.7 组件中的数据存放问题
- 13.8 组件中的data为什么是函数
- 13.9 父组件向子组件添加数据
- 13.10 props中的驼峰标识
- 13.11 组件通信-子传父(自定义事件)
- 13.12 父子组件通信案例
- 13.13 组件访问-父访问子-children-refs
- 13.14 组件访问-子访问父-parent-root
- 18.1 url的hash 和 html5 的history
- 18.2 配置路由相关的信息
- 18.3 router-link的其他属性补充
- 18.4 通过代码的方式修改路径
- 18.5 动态路由
- 18.6 路由的懒加载
- 18.7 嵌套路由
- 18.8 全局导航守卫
Vue学习笔记
1. 概述、特征、以及初始化
- Vue 是一种灵活的渐进式相应框架
- 采用声明式的编程范式
1.1 引用Vue的方法
<script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', //用于挂载需要挂载的元素 data: { message: 'fuck you' } }) </script>
通过采用el挂载和HTML代码定义id的方式来使用Vue.
data是vue实例对应的数据对象
methods定义属于vue的一些方法,可以再其他地方调用,也可以在指令中使用
2. 创建、显示、更新列表
<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: { message: 'ssda', movies: ['星际穿越', '大话西游', '少年派'], } }) </script>
声明式的编程范式,使得列表操作变得简易。
3. 案例:计数器
包含新的指令,v-on 和新的属性 methods,可以通过函数的形式来匹配复杂的操作
<button v-on:click="add">+</button> <button v-on: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('+++') this.counter++ }, sub: function () { console.log('---') this.counter-- } }, }) </script>
此处 v-on:click 可以用语法糖 @click 来替代。
4. 关于插值的操作
4.1 mustache 语法
<h2>{{message}}, 李银河!</h2> <h2>{{firstName + lastName}}</h2>
mustache语法中不仅可以写变量,亦可以写简单的表达式。
4.2 v-once 指令
表示页面的元素和组件只渲染一次,不会随着数据的改变而改变。
<h2 v-once>{{message}}</h2>
v-for 指令
表示循环元素或组件中的内容,后接表达式。
<ul> <li v-for="item in movies">{{item}}</li> </ul>
v-html 指令
表示有选择性地解析html中的内容
<h2 v-html='url'>{{url}}</h2>
v-text 指令
<h2 v-text='message'>,栗子</h2>
表示显示指定的数据,但是很不灵活,会直接覆盖原标签内的数据,而且不支持拼接等操作。
该指令用得不多
v-pre 指令
<h2 v-pre>{{message}}</h2>
表示原封不动地显示标签内的内容,而不会对其进行解析。
该指令用得不多
v-cloak 指令
一般用来消除抖动
<style> [v-cloak] { display: none } </style> <div id="app"> <h2 v-cloak>{{message}}</h2> </div> <script src="../js/vue.js"></script> <script> // 由于 v-cloak 在被解析之后会被删除,所以 // 在vue解析之前,div中有v-cloak属性 // 在vue解析之后,div中没有v-cloak setTimeout(function () { const app = new Vue({ el: '#app', data: { message: '你好' } }) }, 1000) </script>
5. v-bind 数据绑定
5.1 v-bind 的基本使用
绑定属性
<img v-bind:src='imgURL' alt="谷歌"> <a v-bind:href="ahref">百度一下</a>
v-bind 语法糖
<a :href="ahref">百度一下</a>
实际上是省略了v-bind指令。
5.2 v-bind 绑定到class有两种方式 对象语法和数组语法**
用法一 可以直接通过{}绑定一个类
用法二 可以通过判断传入多个值
用法三 和普通的类同时存在,并不冲突
v-bind 绑定到class(对象语法)
此案例添加了一个按钮,并设置了一个监听事件,使得点击按钮时改变绑定到
<h2>的class中的布尔值,从而实现对样式的改变。
<div id='app'> <h2 v-bind:class='{active: isActive, line: isLine}'>{{message}}</h2> <button v-on:click='btnClick'>按钮</button> </div> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { message: '你好', isActive: true, isLine: false, }, methods: { btnClick: function () { this.isActive = !this.isActive } } })
v-bind 绑定到class(数组语法)
<div id='app'> <h2 class="title" :class="[active, line]">{{message}}</h2> <h2 class="title" :class="getClasses()">{{message}}</h2> </div> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { message: 'hello', active: 'one', line: 'two' }, methods: { getClasses: function () { return [this.active, this.line] } } }) </script>
v-bind 动态绑定style(对象语法)
原理大致相同,引号后面改成style即可。
<div id='app'> <!-- <h2 v-bind:style='{属性名:属性值}'>{{message}}</h2> --> <!-- <h2 v-bind:style='{fontSize: theSize + "px", color: theColor}'>{{message}}</h2> --> <h2 v-bind:style='getStyles()'>{{message}}</h2> </div> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { message: 'hello', theSize: 100, theColor: 'red', }, methods: { getStyles: function () { return { fontSize: this.theSize + "px", color: this.theColor } }, } }) </script>
v-bind 动态绑定style(数组语法)
<div id="app"> <h2 :style='[baseStyle, baseStyle1]'>{{message}}</h2> </div> <script src='../js/vue.js'></script> <script> const app = new Vue({ el: '#app', data: { message: 'hello', baseStyle: { backgroundColor: 'red' }, baseStyle1: { fontSize: '88px' } } }) </script>
6. 计算属性
特点:html代码里面非常清晰
<h2>{{fullMessage}}</h2> </div> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { message: 'asd', message2: 'dd', }, computed: { fullMessage() { return this.message + ' ' + this.message2 } }, methods: { getFullMessage() { return this.message + ' ' + this.message2 } } }) </script>
6.1 复杂操作
<script> const app = new Vue({ el: '#app', data: { books: [ { id: 110, name: 'Unix程艺术', price: 119 }, { id: 111, name: 'Unx编程艺术', price: 1192 }, { id: 112, name: 'Uix编程艺术', price: 1191 }, { id: 113, name: 'Uni编程艺术', price: 11945 }, ] }, methods: {}, computed: { totalPrice: function () { let result = 0 // for (let i = 0; i < this.books.length; i++) { // result += this.books[i].price // } for (let book of this.books) { result += book.price } return result } } }); </script>
7. es6语法补充
<script> // 属性的增强写法 const name = 'chen' const age = 18 const obj = { name: name, age: age } //以上是es5的写法 // es60的写法如下 const obj = { name, age } // 函数的增强写法 //es5中的 const obj = { run: function () { }, eat: function () { }, } //es6中的 const obj = { run() { }, eat() { }, } </script>
var没有块级作用域,所以用let
const指定对象之后不可更改,但是const内部可以进行更改
7.1 可变参数
function sum(...num) { console.log(num) } sum(1,2,3,4,5)
7.2 for of 的使用**
var arr = ['nick','freddy','mike','james']; for(let item of arr){ console.log(item); }
数组对象都支持,非常的简洁!
7.3 高阶函数 filter/map/reduce
7.3.1.filter函数的使用filter中的回调函数有一个要求:必须返回一个boolean值
true:当返回true时,函数内部会自动将这次回调的n加入到新的数组中
false:当返回false时,函数内部会过滤掉这次的n
const nums = [10, 230, 22, 55, 11] let newNums = nums.filter(function(n) { return n < 100 })7.3.2.map函数的使用
let new2Nums = newNums.map(function(n) { return n * 2 })7.3.3.reduce函数的使用
作用:对数组中的所有内容进行汇总。
new2Nums.reduce(function(preValue, n) { return preValue + n }, 0)4.高阶函数链式编程
const nums = [1,2,3,55,66,22,66,33] let total = nums.filter(function(n) { return n < 100 }).map(function (n) { return n * 2 }).reduce(function (prevValue, n) { return prevValue + n }, 0) // 箭头函数 let total2 = num.filter(n => n < 100).map(n => n * 2).reduce((pre, n) => pre + n)
8. 事件监听
8.1 v-on的基本使用
<div id='app'> <h2>{{message}}</h2> <h2>{{counter}}</h2> <!-- <button v-on:click="increment">+</button> <button v-on:click="decrement">-</button> --> <button @click="increment">+</button> <button @click="decrement">-</button> </div> <script src='../js/vue.js'></script> <script> const app = new Vue({ el: '#app', data: { message: 'Hello, world!', counter: 0 }, methods: { increment() { return this.counter++ }, decrement() { return this.counter-- }, }, computed: { }, }) </script>
8.2 v-on的参数问题
<div id='app'> <!-- 事件调用的方法没有参数 --> <button @click="btnClick()">按钮1</button> <!-- 在事件定义时,写方法时省略了小括号,但是方法本身是需要一个参数的, 这个时候,vue会默认将浏览器生成的event对象作为参数传入到方法。 --> <button @click="btn2Click">按钮2</button> <!-- 方法定义时,我们需要event对象,同时又需要其他参数 --> <!-- 在调用方法时,如何手动获取到浏览器参数的event对象:$event --> <button @click="btn3Click(123, $event)">按钮3</button> <button>按钮4</button> </div> <script src='../js/vue.js'></script> <script> const app = new Vue({ el: '#app', data: { event: 'chen' }, methods: { btnClick() { console.log('hello world!') }, btn2Click(item) { console.log('====', item) }, btn3Click(item, event) { console.log('+++++', item, event); } } }) </script>
8.3 v-on修饰符
<div @click='divClick'> 点这里 <button @click.stop="btnClick">按钮1</button> </div> <br> <div> <!-- 2.prevent 修饰符使用,阻止默认事件 --> <form action='baidu'> <input type='submit' value="提交" @click.prevent='submitClick'> </form> <!-- 3.监听某个键盘案件的点击,加.enter 则监听回车 --> <input type='text' @keyup='keyUp'> <!-- 4.once修饰符的使用 只触发一次回调--> <button @click.once="btn2Click">按钮2</button> </div>
9. 条件判断
9.1 v-if 和 v-else的使用
<div id='app'> <h2 v-if='score>=90'>优秀</h2> <h1 v-else-if='score>=80'>良好</h1> <h1 v-else-if='score>=60'>及格</h1> <h1 v-else>不及格</h1> </div>
指令判断,不建议写到标签里,建议写计算属性
9.2 登陆切换的一个小案例
<div id='app'> <span v-if='isUser'> <label for="username">用户账户</label> <input type="text" id="username" placeholder="用户账户"> </span> <span v-else=''> <label for="useremail">用户邮箱</label> <input type="text" id="useremail" placeholder="用户邮箱"> </span> <button @click='isUser=!isUser'>切换类型</button> </div>
如果不希望输入框内容复用。可以加入一个key属性
<span v-if='isUser'> <label for="username">用户账户</label> <input type="text" id="username" placeholder="用户账户" key='username'> </span> <span v-else=''> <label for="useremail">用户邮箱</label> <input type="text" id="useremail" placeholder="用户邮箱" key='useremail'> </span>
v-show的使用
<div id='app'> <h2 v-if='isShow' id='aaa'>{{message}}</h2> <h2 v-show='isShow' id='bbb'>{{message}}</h2> </div>
v-if中,条件为false时,包含v-if指令的元素,根本不会出现在dom中。
v-show中,当条件为false时,v-show只是给元素增加了一个行内样式 display:none。
如何选择:频繁切换时用show,否则用if。
10. 循环遍历
10.1 遍历数组
<div id='app'> <!-- 1.在遍历的过程中,没有使用索引值 --> <h2>{{message}}</h2> <ul> <li v-for='item in names'>{{item}}</li> </ul> <!-- 2.在遍历的过程中,获取索引值。 --> <ul> <li v-for='(item, index) in names'>{{index+1}}.{{item}}</li> </ul> </div>
10.2遍历对象
<div id='app'> <!-- 1.在遍历对象的过程中,如果只是获取一个值,那么获取到的是value --> <ul> <li v-for='item in info'>{{item}}</li> </ul> <!-- 2.获取key和value 格式(value, key)--> <ul> <li v-for='(value, key) in info'>{{key}}-{{value}}</li> </ul> <!-- 3.获取key和value和index 格式(value, key index) --> <ul> <li v-for='(value, key, index) in info'>{{key}}-{{value}}-{{index}}</li> </ul> </div>
10.2 v-for使用过程绑定key
<ul> <li v-for='item in letters' v-bind:key='item'>{{item}}</li> </ul>
添加唯一标志key进行绑定后,中间插入效率高。
10.3哪些数组的方法是响应式的
push(), pop(), shift(), unshift(), splice(), sort(), reverse()methods: { btnClick() { // 1.push方法。改变的时候是响应式的 this.letters.push(this.mark) this.mark += 1 // 2.pop(),响应式,删除数组中最后一个元素。 this.letters.pop(); // 3.shift(),响应式,删除数组中的第一个元素。 this.letters.shift(); // 4.unshift(), 响应式,在数组开头添加元素 this.letters.unshift('thing', 'asd', 'ddd') 5.splice() // 可以删除、插入、替换元素 // 删除元素: 第二个参数传入你要删除几个元素(如果不传,则删除后面所有的元素) // 替换元素: 第二个参数表示我们要替换几个元素,后面接用于替换的元素 // 插入元素:第二个参数,传入0,后面跟上要插入的元素。 // splice(start, ), 响应式 this.letters.splice() // 6.sort() 响应式,排序 this.letters.sort() // 7.reverse() 响应式,反转 this.letters.reverse() // 0.通过索引值修改数组中的元素,不是响应式 this.letters[0] = 'haa'},
通过索引值修改数组中的元素,不是响应式
this.letters[0] = 'haa'
Vue中定义的响应式修改方式:
Vue.set(this.letters, 0, 'bbbbbb')10.3.1 一个点击变色小作业的实现
<ul> <li v-for='(item, index) in movies' v-bind:key='item' v-bind:class='{active: currentIndexL === index}' @click='liClick(index)'>{{item}} </li> </ul> </div> <script src='../js/vue.js'></script> <script> const app = new Vue({ el: '#app', data: { message: 'Hello world!', movies: ['海王', '肖申克的救赎', '海上钢琴师', '复仇者联盟'], currentIndex: 0, }, methods: { liClick(index) { this.currentIndex = index } } }) </script>
解决此作业的精髓在于:在列表处循环时讲index 分离出来,并在点击事件中调用liClick(index)方法时把index传回去,并在data里面定义一个currentIndex来记录状态,作为动态绑定样式的依据,非常精妙!妙啊!
11. 书籍购物车案例
11.1过滤器的实现
<td>{{item.price | getFinalPrice}}</td>
filters: { getFinalPrice (price) { return '$' + price.toFixed(2) }
11.2计算总价格的四种循环实现方法
computed: { totalPrice () { let totalPrice = 0 // 1.普通的for循环 // for (let i = 0; i < this.books.length; i++) { // totalPrice += this.books[i].price * this.books[i].count // } // return totalPrice // 2.for (let i in this.books) // for (let i in this.books) { // totalPrice += this.books[i].price * this.books[i].count // } // return totalPrice // 3. for的 // for (let item of this.books) { // totalPrice += item.price * item.count // } //return totalPrice //} //}, // 4.reduce实现 return this.books.reduce((pre, book) => pre + book.price * book.count) } },
11.3 v-if/else的实现
<div id='app'> <div v-if='books.length'> <table> <thead> <tr> <th></th> <th>书籍名称</th> <th>出版日期</th> <th>价格</th> <th>购买数量</th> <th>操作</th> </tr> </thead> <tbody> <tr v-for='(item, index) in books'> <td>{{item.id}}</td> <td>{{item.name}}</td> <td>{{item.date}}</td> <!-- <td>{{getFinalPrice(item.price)}}</td> --> <td>{{item.price | getFinalPrice}}</td><td> <button @click='decrement(index)' v-bind:disabled='item.count <= 1'>-</button> {{item.count}} <button @click='increment(index)'>+</button> </td> <td> <button @click='removeHandle(index)'>移除</button> </td> </tr> </tbody> </table> <h2>总价格 {{totalPrice | getFinalPrice}}</h2> </div> <h2 v-else>购物车为空</h2> </div>
12. v-model使用
12.1 v-model基本使用
<div id='app'> <input type="text" v-model='message'> <h2>{{message}}</h2> </div>
12.2 v-model原理
v-model其实是一个语法糖,它的背后本质上时包含两个操作:
1.v-bind绑定一个value属性.
2.v-on指令给当前元素绑定input事件.
<input type="text" v-bind:value='message' v-on:input='message = $event.target.value'>
12.3 v-model结合radio类型
<div id='app'> <h2>您选择的性别是:{{sex}}</h2> <label for='male'> <input type="radio" id='male' name='gender' value="男zxc" v-model='sex'>男 </label> <label for='female'> <input type="radio" id='female' name='gender' value="女" v-model='sex'>女 </label> </div>
12.4 v-model结合checkbox单选、多选框使用
单选框对应布尔值,多选框则对应数组类型。
<div id='app'> <!-- checkbox单选框 --> <!-- <label for="license"> <input type="checkbox" id='license' v-model='isAgree'>同意协议 </label> --> <!-- <h2>您选择的是{{isAgree}}</h2> <button v-bind:disabled='!isAgree'>下一步</button> --> <!-- checkbox复选框 --> <label for="hobby1"> <input type="checkbox" value="唱" id='hobby1' v-model='hobbies'>唱 </label> <label for="hobby2"> <input type="checkbox" value="跳" id='hobby2' v-model='hobbies'>跳 </label> <label for="hobby3"> <input type="checkbox" value="Rap" id='hobby3' v-model='hobbies'>Rap </label> <label for="hobby4"> <input type="checkbox" value="篮球" id='hobby4' v-model='hobbies'>篮球 </label> <h2>您的爱好是: {{hobbies}}</h2> </div>
12.5 v-model结合checkbox进行值绑定
值不要写死,去动态获取
<label v-for='item in originHobbies' v-bind:for='item'> <input type="checkbox" v-bind:value="item" v-bind:id='item' v-model='hobbies'>{{item}} </label> <h2>您的爱好是: {{hobbies}}</h2> </div> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { message: 'Hello world!', isAgree: false, hobbies: [], originHobbies: ['唱', '跳', 'rap', '篮球'] } }) </script>
12.6 v-model结合select
<div id='app'> <select name='name' v-model='fruit'> <option value="唱">唱</option> <option value="跳">跳</option> <option value="rap">rap</option> <option value="篮球">篮球</option> </select> <h2>{{message}} {{fruit}}</h2> </div>
select多选现在用的不多,不做介绍。
12.7 v-model修饰符的使用
<div id='app'> <!-- 修饰符lazy,添加后不实时更新,失去焦点时再更新 --> <input type="text" v-model.lazy='message'> <h2>{{message}}</h2> <!-- 修饰符number,强制将绑定值更新为number类型 --> <input type="text" v-model.number='age'> <h2>{{age}}-{{typeof age}}</h2> <!-- 修饰符trim,剥除空格 --> <input type="text" v-model.trim='name'> <h2>您输入的名字:{{name}}</h2> </div>
13. 组件化
13.1 注册组建的基本步骤
- 创建组件构造器
- 注册组件
- 使用组件
- 调用Vue.extend()方法创建组件构造器
- 调用Vue.component()方法注册组件
- 在Vue实例的作用范围内使用组件
<script> // 1. 创建组件构造器对象 const cpnC = Vue.extend({ template: ` <div> <h2>我是蔡徐坤!</h2> <p>唱</p> <p>跳</p> <p>rap</p> <p>篮球</p> </div> ` }) // 2.注册组件 Vue.component('my-cpn', cpnC) const app = new Vue({ el: '#app', data: { message: 'Hello world!' } }) </script>
13.2 注册组件步骤解析
Vue.extend():
- 调用Vue.extend()创建的是一个组件构造器。
- 通常在创建组件构造器时,传入template代表我们自定义的模板。
- 该模板是在使用到组建的地方,要显示的HTML代码。
- 事实上,现在多用语法糖来处理。
13.3 局部组件和全局组件
注:此为比较老的方法
<div id='app'> <h2>{{message}}</h2> <!-- 3. 使用组件 --> <my-cpn></my-cpn> <cpn></cpn> </div> <div id='app2'> <my-cpn></my-cpn> <cpn></cpn> </div> <script src="../js/vue.js"></script> <script> // 1. 创建组件构造器对象 const cpnC = Vue.extend({ template: ` <div> <h2>呵呵</h2> </div> ` }) // 2.注册组件(全局组件,意味着可以在多个Vue的实例下面使用) Vue.component('my-cpn', cpnC) // 疑问:怎么注册的组件才是局部组件? const app = new Vue({ el: '#app', data: { message: 'Hello world!' }, components: { // cpn-使用组件时的标签名 cpn: cpnC } }) const app2 = new Vue({ el: '#app2', }) </script>
13.4 父组件和子组件
这里像套娃一样,可以再父组件的构造器中注册子组件,之后可以在父组件的template中调用子组件的template。
但是至少需要在根组件中注册父组件、而且子组件在未被根组件注册时并不能使用其中的模板!
<script> // 构建子组件构造器? const cpn1 = Vue.extend({ template: ` <div> <p>呵呵</p> </div> ` }) // 构建父组件构造器 const cpn2 = Vue.extend({ template: ` <div> <p>吼吼</p> <cpn1></cpn1> </div>`, components: { cpn1, } }) const app = new Vue({ el: '#app', data: { message: 'Hello world!' }, components: { cpn2, cpn1 } }) </script>
13.5 组件的语法糖注册方式
<script> // 语法糖注册全局组件 Vue.component('cpn1', { template: ` <div> <h2>mdzz</h2> </div> ` }) const app = new Vue({ el: '#app', data: { message: 'Hello world!', }, // 语法糖注册局部组件 components: { 'cpn2': { template: ` <div> <h2>mdzzzzzzzzzzzzzz</h2> </div> ` } } }) </script>
13.6 模板的抽离写法
<body> <div id='app'> <cpn></cpn> </div> <template id='cpn2'> <div> <h2>fuck</h2> </div> </template> <script src="../js/vue.js"></script> <script> Vue.component('cpn', { template: '#cpn2' }) const app = new Vue({ el: '#app', }) </script> </body>
13.7 组件中的数据存放问题
<body> <div id='app'> <cpn></cpn> <cpn></cpn> <cpn></cpn> </div> <template id='cpn'> <h2>asd</h2> <h2>{{title}}</h2> <p>fuck</p> </template> <script src="../js/vue.js"></script> <script> Vue.component('cpn', { template: '#cpn', data() { return { title: 'the title' } } }) const app = new Vue({ el: '#app' }) </script> </body>
13.8 组件中的data为什么是函数
因为函数有各自的作用域和地址,所以设置成函数能避免组件之间相互影响。
<body> <div id='app'> <h2>{{message}}</h2> <cpn></cpn> </div> <template id='cpn'> <div> <h2>当前计数: {{counter}}</h2> <button @click='decrement'>-</button> <button @click='increment'>+</button> </div> </template> <script src="../js/vue.js"></script> <script> Vue.component('cpn', { template: '#cpn', data() { return { counter: 0 } }, methods: { increment() { this.counter++ }, decrement() { this.counter-- } } }) const app = new Vue({ el: '#app', data: { message: 'Hello world!', } }) </script> </body>
13.9 父组件向子组件添加数据
<div id='app'> <!-- <h2>{{message}}</h2> <cpn v-bind:cmovies='movies' :cmessages='message'></cpn> --> <cpn :cmessages='message' :cmovies='movies'></cpn> </div> <template id='cpn'> <div> <ul> <li v-for='item in cmovies'>{{item}}</li> </ul> <h2>{{cmessages}}</h2> </div> </template> <script src="../js/vue.js"></script> <script> // 父传子,props const cpn = { template: '#cpn', // props: ['cmovies', 'cmessages'], props: { // # 类型限制传props // cmovies: Array, // cmessages: String, // }, // 提供默认值以及必传值 cmessages: { type: String, default: 'aaaaa', required: true, }, // 类型是对象或者数组时,默认值必须是一个函数 cmovies: { type: Array, default() { return [] } } }, data() { return { } }, methods: { } } const app = new Vue({ el: '#app', data: { message: 'Hello!', movies: ['海王', '海贼王', '海尔兄弟'] }, components: { cpn, } }) </script>
13.10 props中的驼峰标识
在父组件引用子组件的模板中使用v-bind绑定时,不能用驼峰标识,要将其转换为’-'连接的形式。
<body> <div id='app'> <cpn :c-info='info'></cpn> </div> <template id='cpn'> <div> <h2>{{cInfo}}</h2> </div> </template> <script src="../js/vue.js"></script> <script> const cpn = { template: '#cpn', props: { cInfo: { type: Object, dafault: { ss: 'ss' }, required: true, }, }, data() { return {} }, methods: { }, } const app = new Vue({ el: '#app', data: { message: 'Hello world!', info: { name: 'chen', age: 23, color: 'black' }, }, methods: { }, components: { cpn } }) </script> </body>
13.11 组件通信-子传父(自定义事件)
子组件自定义事件、并发射事件,由父组件来监听、接收、处理、显示。
- 在子组件中,通过$emit()来触发事件
- 在父组件中,通过v-on来监听子组件事件
<body> <!-- 父组件模板 --> <div id='app'> <cpn @item-clicked='cpnClick'></cpn> </div> <!-- 子组件模板 --> <template id='cpn'> <div> <button v-for='item in categroies' @click='btnClick(item)'>{{item.name}}</button> </div> </template> <script src='../js/vue.js'></script> <script> // 这是子组件 const cpn = { template: '#cpn', data() { return { categroies: [ { id: '1', name: '唱' }, { id: '2', name: '跳' }, { id: '3', name: 'rap' }, { id: '4', name: '篮球' }, ], } }, methods: { // 自定义事件,发射自定义事件! btnClick(item) { this.$emit('item-clicked', item) }, } } // 这是父组件 const app = new Vue({ el: '#app', data: { message: 'Hello world!' }, methods: { cpnClick(item) { console.log('secceed!!', item); } }, // 这是注册子组件 components: { cpn, } }) </script> </body>
13.12 父子组件通信案例
<body> <div id='app'> <h2>{{message}}</h2> <cpn v-bind:number1='num1' v-bind:number2='num2' @num1change='num1change' @num2change='num2change'></cpn> </div> <template id='cpn'> <div> <h2>props:{{number1}}</h2> <h2>data:{{dnumber1}}</h2> <!-- <input type='text' v-model='dnumber1'> --> <input type="text" v-bind:value='dnumber1' @input='num1Input'> <h2>{{number2}}</h2> <h2>data:{{dnumber2}}</h2> <!-- <input type='text' v-model='dnumber2'> --> <input type="text" v-bind:value='dnumber2' @input='num2Input'> </div> </template> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { message: 'Hello world!', num1: 1, num2: 0, }, methods: { num1change(value) { this.num1 = parseFloat(value) }, num2change(value) { this.num2 = parseFloat(value); } }, components: { cpn: { template: '#cpn', props: { number1: Number, number2: Number }, data() { return { dnumber1: this.number1, dnumber2: this.number2 } }, methods: { num1Input(event) { this.dnumber1 = event.target.value this.$emit('num1change', this.dnumber1) }, num2Input(event) { this.dnumber2 = event.target.value this.$emit('num2change', this.dnumber2) } } } } }) </script> </body>
13.13 组件访问-父访问子-children-refs
- refs用得很多,children基本不用
<body> <div id='app'> <h2>{{message}}</h2> <cpn></cpn> <cpn></cpn> <cpn ref='mark'></cpn> <button @click="btnClick">按钮</button> </div> <template id='cpn'> </template> <script src="../js/vue.js"></script> <script> const cpn = { template: '#cpn', data() { return { name: '我是子组件的name' } }, methods: { show() { console.log('show!!!!'); } } } const app = new Vue({ el: '#app', data: { message: 'Hello World!' }, components: { cpn, }, methods: { btnClick() { // 1. $children // console.log(this.$children) // for (let item of this.$children) { // console.log(item.name) // item.show() // 2. $refs => 对象类型,默认是一个空的对象 ref='item' console.log(this.$refs.mark.name) } } }) </script> </body>
13.14 组件访问-子访问父-parent-root
- root用得很多,parent基本不用
<body> <div id='app'> <h2>{{message}}</h2> <cpn></cpn> </div> <template id='cpn'> <div> <h2>我是子组件</h2> <button @click="btnClick">按钮</button> <div> </template> <script src="../js/vue.js"></script> <script> const cpn = { template: '#cpn', data() { return {} }, methods: { btnClick() { // 1.访问父组件 $parent // console.log(this.$parent) // 2.访问根组件 $root console.log(this.$root) console.log(this.$root.message) } }, } const app = new Vue({ el: '#app', data: { message: 'Hello world!' }, methods: { }, components: { cpn, } }) </script> </body>
14. 组件化高级
14.1 slot插槽的基本使用
<body> <div id='app'> <h2>{{message}}</h2> <cpn></cpn> <cpn><button>按钮</button></cpn> <cpn><span>这是span</span></cpn> </div> <template id='cpn'> <!-- 1.插槽的基本使用 <slot></slot> 2.插槽的默认值<slot>button</slot> 3.如果有多个值,同时放入到组件中进行替换时,一起作为替换元素 --> <div> <h2>插槽用法测试</h2> <p>我是组件</p> <slot></slot> <!-- 添加默认值 --> <!-- <v-slot><button>按钮!</button>按钮!</v-slot> --> </div> </template> <script src="../js/vue.js"></script> <script> const cpn = { template: '#cpn', data() { return {} }, } const app = new Vue({ el: '#app', data: { message: 'Hello World!', }, components: { cpn } }) </script> </body>
14.2 具名插槽的使用(v-slot)
- 新的v-slot用法代替了slot用法,语法和写的位置也都有变化
<body> <div id='app'> <h2>{{message}}</h2> <cpn></cpn> <cpn v-slot:center><span>标题</span></cpn> <cpn><button slot='right'>按钮</button></cpn> </div> <template id='cpn'> <div> <p>这是模板</p> <slot name='left'><span>左</span></slot> <slot name='center'><span>中</span></slot> <slot name='right'><span>右</span></slot> </div> </template> <script src="../js/vue.js"></script> <script> const cpn = { template: '#cpn', } const app = new Vue({ el: '#app', data: { message: 'Hello world!' }, components: { cpn } }) </script> </body>
14.3 什么是编译的作用域
- 在哪个模板,那么其中的任何变量、数据等等都属于模板本身内部的作用域。
- 在哪个组件,就用哪个组建的数据。
<body> <div id='app'> <h2>{{message}}< 4000 ;/h2> <cpn v-show='isShow'></cpn> </div> <template id='cpn'> <div> <h2>这是id为cpn的模板</h2> <p v-show='isShow'>你猜这行会不会显示?</p> </div> </template> <script src="../js/vue.js"></script> <script> const cpn = { template: '#cpn', data() { return { name: 'chen!', isShow: false } } } const app = new Vue({ el: '#app', data: { message: 'Hello world!', isShow: true }, components: { cpn } }) </script> </body>
14.4 作用域插槽
父组件替换插槽的标签,但是内容由子组件来提供。
已废弃,用v-slot
15 前端模块化
15.1 es6 模块化实现
导出方式一 :
export { flag, sum }
导出方式二:
export let num1 = 100
导出函数\类:
export function add(num1, num2) { return num1 + num2 } export class Person { run() { console.log("在奔跑") } }
导入export default 中的内容:
const address = '北京市' export default address // export default 只能有一个
统一全部导入
import * as all from './'
16. webpack的一些配置
// prod.config.js const UglifyjswebpackPlugin = require('uglifyjs-webpack-plugin') const webpackMerge = require('webpack-merge') const baseConfig = require('./base.config') module.exports = webpackMerge(baseConfig, { plugins: [ new UglifyjswebpackPlugin() ], })
//dev.config.js const webpackMerge = require('webpack-merge') const baseConfig = require('./base.config') module.exports = webpackMerge(baseConfig, { devServer: { contentBase: './dist', inline: true, }, })
//base.config.js const path = require('path'); const VueLoaderPlugin = require('vue-loader/lib/plugin') const webpack = require('webpack') const HtmlwebpackPlugin = require('html-webpack-plugin') const UglifyjswebpackPlugin = require('uglifyjs-webpack-plugin') module.exports = { entry: './src/main.js', output: { path: path.resolve(__dirname, '../dist'), filename: 'bundle.js', // publicPath: '../dist/', }, module: { rules: [ { test: /\.css$/, use: ['style-loader', 'css-loader'] }, { test: /\.less$/, use: [{ loader: "style-loader" // creates style nodes from JS strings }, { loader: "css-loader" // translates CSS into CommonJS }, { loader: "less-loader" // compiles Less to CSS }] }, { test: /\.(png|jpg|gif)$/, use: [ { loader: 'url-loader', options: { 20000 limit: 1300, name: 'img/[name].[hash:8].[ext]' }, } ] }, { test: /\.js$/, exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader', options: { presets: ['es2015'] } } }, { test: /\.vue$/, use: { loader: 'vue-loader' } } ] }, plugins: [ // 请确保引入这个插件! new VueLoaderPlugin(), new webpack.BannerPlugin('这是我的'), new HtmlwebpackPlugin({ template: './src/index.html' }), new UglifyjswebpackPlugin() ], devServer: { contentBase: './dist', inline: true, }, resolve: { alias: { 'vue$': 'vue/dist/vue.esm.js', } } }
入口文件
import name from './js/info' // 使用es6的模块化规范 const { add, mul } = require('./js/mathUtils') // 使用commonjs的模块化规范 console.log(add(20, 30)) console.log(mul(20, 30)) console.log(name) // 依赖css文件 require('./css/normal.css') // 依赖less文件 require('./css/special.less') document.writeln('<h2>Hello</h2>') // 使用Vue进行开发 import Vue from 'vue' import App from './vue/App.vue' new Vue({ el: '#app', template: '<App/>', components: { App } })
一些用到的package
{ "name": "meetwebpack", "version": "1.0.0", "description": "practice", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack --config ./build/prod.config.js", "dev": "webpack-dev-server --open --config ./build/dev.config.js" }, "author": "", "license": "ISC", "dependencies": { "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", "html-webpack-plugin": "^3.2.0", "less": "^3.9.0", "less-loader": "^4.1.0", "style-loader": "^0.23.1", "uglifyjs-webpack-plugin": "^1.1.1", "url-loader": "^1.1.2", "vue": "^2.5.21", "vue-loader": "^15.4.2", "vue-template-compiler": "^2.5.21", "webpack": "^3.6.0", "webpack-dev-server": "^2.9.3", "webpack-merge": "^4.1.5" } }
17. vue-cli
vue-cli2 中的项目初始化命令
vue init webpack project-name
vue-cli3 中的项目初始化命令
vue creat project-name
17.1 runtimecompiler 和 runtimeonly的区别
runtime-compiler
template => ast => render => vdom => ui
runtime-only
性能更高、代码更少
render => vdom => ui
17.2 vue的配置
vue ui
17.3 箭头函数补充
标准格式
const demo = (item1, item2) => { return item1 + item2 }
const demo = item => console.log(item)
当函数中只有一个参数时,可以省略小括号,当函数中只有一行代码时,可以省略return。
箭头函数中的this:向外层作用域中一层层查找,直到有this的定义。
18. vue-router
18.1 url的hash 和 html5 的history
URL的hash也就是锚点(#),本质上是改变window.location的href属性
我们可以直接赋值location.hash来改变href,而页面不发生刷新
location.hash
history 入栈和出栈
history.pushState({}, '', 'item')``` replace 不可返回 ```history.replaceState({}, '', 'item')``` history.go()方法和history.back()\history.forward()等价 history.go(-1) history.go(1)
18.2 配置路由相关的信息
import VueRouter from 'vue-router' import Vue from 'vue'
- 通过Vue.use(插件), 安装插件
Vue.use(VueRouter)
- 创建VueRouter对象
const routes = [ ]
- 配路由和组件之间的应用关系
const router = new VueRouter({ routes })
- 将router对象传入到 Vue 实例
new Vue({ router, render: h => h(App) }).$mount('#app')
使用vue-router步骤
第一步:创建路由组件
第二步:配置路由映射-组件和路径映射关系
第三步:使用路由
<template> <div id="app"> <router-view /> <div id="nav"> <router-link to="/home">Home</router-link> | <router-link to="/about">About</router-link> </div> </div> </template>
18.3 router-link的其他属性补充
<router-link> 中的to属性--指定路径 tag属性--指定组建的类型(比如说渲染成button) <router-link to="/" tag="button" replace> replace模式不会留下历史记录,所以不能返回
18.4 通过代码的方式修改路径
pushState()方法:
export default { name: 'App', methods: { btnHome () { this.$router.push('/home') }, btnAbout () { this.$router.push('/about') } } }
replace()方法:
export default { name: 'App', methods: { btnHome () { this.$router.replace('/home') }, btnAbout () { this.$router.replace('/about') } } }
解决Vue-cli中进行两次路由切换的坑
import Vue from 'vue' import VueRouter from 'vue-router' Vue.use(VueRouter) const VueRouterPush = VueRouter.prototype.push VueRouter.prototype.push = function push (to) { return VueRouterPush.call(this, to).catch(err => err) }
以App.vue中的path为主,index.js中的path为配合,其中首字母大小写不做严格限制
btnHome () { this.$router.push('/home') { path: '/Home', name: 'Home', component: Home }
像这样也是可以的
18.5 动态路由
动态路由的使用(userId)
{ path: '/user/:userId', name: 'User', component: User } btnUser () { this.$router.push('/user' + '/' + this.userId) } <template> <div> <h2>用户界面</h2> <p>用户界面的内容</p> <p>你的id是:{{userId}}</p> </div> </template> <script> export default { name: 'User', computed: { userId () { return this.$route.params.userId } } } </script>
18.6 路由的懒加载
作用:动态加载资源,缩短加载时间,提升用户体验!
const User = () => import('../views/user.vue') 或者 component () => import('./views/Home.vue')
第一种更清晰!
18.7 嵌套路由
{ path: '/Home', name: 'Home', component: Home, children: [ { path: 'news', component: HomeNews }, { path: 'message', component: HomeMessage } ] }
然后在home.vue中
<router-link to="/home/news">新闻</router-link> <router-link to="/home/message">消息</router-link>
18.8 全局导航守卫
{ name: 'Profile', path: '/Profile', component: Profile, meta: { title: '档案' } } 前置钩子 从from 跳转到 to router.beforeEach((to, from, next) => { document.title = to.matched[0].meta.title next() })
keep-alive 保持状态,避免被频繁创建与销毁
<keep-alive> <router-view /> </keep-alive>
使用keep-alive 之后,activated/deactivated的使用成为可能。
进行排除的方法:
<keep-alive exclude="Profile"> <router-view /> </keep-alive>
19. Promise
什么情况下会用到promise?
一般情况下是有异步操作时,需要Promise对这个异步操作进行封装 在执行传入的回调函数时,会传入两个参数,一个叫resolve,一个叫reject,而这两个参数本身又是函数 new => 构造函数(1.保存了一些状态信息 2.执行传入的函数) 成功时用resolve 失败时候用reject new Promise((resolve, reject) => { setTimeout(() => { // resolve('Hello world') reject('error') }, 1000) }).then((data) => { console.log(data) console.log(data) console.log(data) }).catch((error) => { console.log(error) }) 或者 new Promise ((resolve, reject) => { setTimeout(() => { resove('Hello world!') }, 1000) }).then(data => { console.log(data) }, error => { console.log(error) })
19.1 Promise 的三种状态
penging:等待状态,比如正在进行网络请求,或者定时器没有到时间。
fullfill:满足状态,当主动调用resolve时,就处于该状态,并且会回调.then()
reject:拒绝状态,当主动调用reject时,就处于该状态,并且会回调.catch()
20. vuex
vuex的基本使用
<template> <div id="app"> <h2>{{message}}</h2> <h2>{{$store.state.counter}}</h2> <button @click="addition">+</button> <button @click="substraction">-</button> <hello-vuex></hello-vuex> </div> </template> <script> import HelloVuex from './components/HelloVuex.vue' export default { name: 'App', data () { return { message: '我是app组件', } }, components: { HelloVuex }, methods: { addition () { this.$store.commit('increment') }, substraction () { this.$store.commit('decrement') } } } </script> <template> <div> <h2> {{$store.state.counter}} </h2> </div> </template> <script> export default { name: 'HelloVuex', } </script>
Vue.use(Vuex) export default new Vuex.Store({ state: { counter: 1000 }, mutations: { increment (state) { state.counter++ }, decrement (state) { state.counter-- } }, actions: { }, modules: { } })
axios的封装
import axios from 'axios' export function request (config) { // return new Promise((resolve, reject) => { const instance = axios.create({ baseUrl: 'http://123.207.32.32:8000/api/wh/', timeout: 5000 }) return instance(config) // instance(config) // .then(res => { // resolve(res) // }) // .catch(err => { // reject(err) // }) // }) } import { request } from './network/request.js' request({ url: '/home/multidata' }).then(res => { console.log(res) }).catch(err => { console.log(err) })讨嫌喵 原创文章 7获赞 4访问量 196 关注 私信
- Vuex 笔记
- vue学习笔记---tap切换配置(vue-router)
- Vue学习笔记:Vue组件的核心概念(上)
- 笔记-VUE滚动加载更多数据
- Vue.js第三天学习笔记(计算属性computed)
- Vue.js 2.0之全家桶系列视频课程——笔记(一)
- Vue学习笔记:提升开发效率和体验的常用工具
- vue学习笔记
- vue学习笔记---3.v-if与v-show的区别以及使用
- vue学习笔记(六)表单输入绑定
- VUE 学习笔记 从零开始一步一步构建 VUE 单页应用(一)
- Vue.js学习笔记-2-MVVM
- Vue.js 2.x笔记:路由Vue Router(6)
- 临时笔记:豪大推荐的Vue 学习路径(一)
- Vue学习笔记-常用属性
- Vue学习笔记——指令和部分组件
- Vue学习笔记16-组件之子向父通信
- VUE入门笔记(3)
- vue笔记------router
- Vue学习笔记3.5 计算属性computed中的 get方法和set方法