[toc]
邂逅Vue.js
1.编程范式
- 命令式编程 如jquery
- 声明式编程 只需要生命需要显示的东西就可以
- 创建Vue实例的时候,传入了一个对象options,这个opentions中包含了
- el: 类型:string|HTMLElement 作用:决定之后Vue实例会管理哪一个DOM
- data: 类型:Object|Function(组件中data必须是一个函数) 作用:Vue实例对应的数据对象
- methods: 类型:{[key: string] : Function} 作用: 定义属于Vue的一些方法,可以在其他地方调用,也可以在指令中使用。
Vue基础语法
- 前端代码缩进最好两个空格,多数大型开源框架都是两个
模板语法
Mustache语法({{}})
- 可以写变量,也可以写简单的表达式
{{message}} {{firstName + ' ' + lastName}}
指令
- v-once
- 该指令后面不需要跟任何表达式
- 该指令表示元素和组件只渲染一次,不会随数据的改变而改变<h2 v-once>{{message}}<h2>
- v-html
- 该指令后面往往会跟上一个string类型
- 会将string的html解析出来并且进行渲染<h2 v-html='url'><h2> data:{ url: '<a href="http://www.baidu.com">百度一下<a>' }
- v-text
一般不用,不够灵活,会覆盖innerHTML内容{{message}} <h2 v-text='message'><h2>
- v-pre , 原生显示
<h2 v-pre>{{message}}</h2>
- v-cloak
- 在vue解析之前,div中有一个属性v-cloak
- 在vue解析之后,div中的v-cloak属性被删除
- 主要是用来防止界面上显示未渲染的代码<div id='app'>{{message}}<div> <script> new Vue({ el: '#app', data{ message: 'aaa' } }) </script> <style> [v-cloak] { display: none } </style>
- v-bind
- 动态绑定属性
- 缩写: ':'
- 预期: any(with argument) | Object(without argument)
- 参数: attrOrProp(optional)<img v-bind:src='imgUrl'> 语法糖写法 <img :src='imgUrl'> <h1 :class = '{class1(类名): showClass1(bool值), class2: showClass2}'><h1> <h1 :class = 'getClass()'><h1> methods:{ getClass: function(){ return {class1: showClass1, class2: showClass2} } }
- v-bind:style
- 动态绑定样式
-<h2 :style="{fontSize: '50px'}"></h2>
-<h2 style="{fontSize: finalSize + 'px'}"></h2>
计算属性
- 数据展示前需要进行处理时使用。
computed:{ fullName: function(){ return this.firstName + this.lastName } }
- 计算属性会缓存,多次调用只会计算一次,方法不会缓存
- 计算属性的getter和setter(set不常用)
computed: { fullName: { get: function(){ return this.firstName + ' ' + this.lastName }, set: function(name){ this.fullName = name } } }
对象字面量增强写法
// 普通写法
name = 'aaa'
age = 18
const obj = {
name: name,
age: age
}
// 增强写法
const obj = {
name,
age
}
事件监听
- v-on
- 作用: 绑定事件监听器
- 缩写: @
- 预期: Function| Inline Statement | Object
- 参数: event - 参数传递
- 如果方法不需要额外参数,那么方法后的()可以不添加
- 如果方法本身有参数,但是调用的时候没有传递,省略小括号,默认会将event参数传递进去
- 如果同时需要传入某个参数,同时需要event,可以通过$event传入事件<button @click="btnClick"></button> 默认传过去event <button @click="btnClick()"></button> 默认传过去undefined <button @click="btn1Click('aaa',$event)"></button> methods:{ btnClick(aaa){ console.log(aaa) }, btn1Click(aaa, event){ console.log(aaa) } }
- v-on修饰符的使用
- .stop 阻止冒泡<div @click='divClick'> <button @click.stop="btnClick"></button> </div>
- prevent 阻止默认事件
<form action="baidu" @click.prevent="submitClick"> </form> # 阻止默认提交事件
- .{keyCode | keyAlias} 只当事件是从特定键触发时才触发回调 - .native 监听根元素的原生事件 - .once 只触发一次回调
条件判断
- v-if
- v-else-if
- v-else
- vue内部会复用dom元素,如果不想让复用,可以加个key
- v-show
- v-show dom会渲染,使用样式决定是否显示
- v-if 根据条件判断是否要渲染
- 频繁切换使用v-show
循环
-
v-for遍历
官方推荐在使用v-for遍历时,给对应的元素或组件加上一个:key属性
为什么要使用key属性?
假设有一个数组,现在想在数组中间插入一个元素,如果没有key,则插入位置以及后面的位置都需要改变,而如果有key,则会先使用key与元素对应起来,插入时只修改插入位置即可。key必须与item一一对应,所以不能用index。
- 使用key给每一个节点做一个唯一标识
- Diff算法可以正确的识别此节点
- 找到正确的位置插入新的节点
==key的作用主要是为了更高效的更新虚拟DOM==
- 哪些数组的方法是响应式的?
- push(): 可以添加多个 arr.push('aaa', 'bbb', 'ccc')
- pop(): 删除数组的最后一个元素
- shift() : 删除数组第一个元素
- unshift(): 在数组前面添加元素, 可以添加多个
- sort():
- reverse()
- splice() : 删除元素/插入元素/替换元素 会影响原始数组
第一个参数start,表示开始操作的索引
删除元素: 第二个参数表示要删除几个元素,如果没有传,表示删除所有
替换元素: 第二个参数表示要替换的个数,后面参数表示要替换的元素
插入元素: 第二个参数为0,后面参数为添加的元素arr = [1,2,3,4,5] arr.splice(1) => [2,3,4,5] arr = [1,2,3,4,5] arr.splice(1,2)⇒ [2,3] arr = [1,2,3,4,5] arr.splice(1,2,'m','n', 'o') => [1, "m", "n", "o", 4, 5] arr = [1,2,3,4,5] arr.splice(1,0,'m', 'n')==> [1, "m", "n", 2, 3, 4, 5]
==通过数组索引直接修改元素不是响应式的, 直接修改界面不会更新==
- Vue.set(obj, index, newValue)
Vue.set(arr, 0, ''a)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<h2 v-show="books.length==0">购物车为空</h2>
<table v-show="books.length!=0">
<tr>
<th>书籍名称</th>
<th>出版日期</th>
<th>价格</th>
<th>购买数量</th>
<th>操作</th>
</tr>
<tr v-for="(book, index) in books" :key='book.bookName'>
<td>{{book.bookName}}</td>
<td>{{book.publishDate}}</td>
<td>{{book.price | transPrice}} </td>
<td><button @click="increment(index)">+</button>{{book.number}}<button @click="decrement(index)">-</button></td>
<td><button @click="remove(index)">移除</button></td>
</tr>
</table>
<p>总价:¥{{totalPrice | transPrice}}</p>
</div>
</body>
</html>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
books: [
{'bookName': '《算法导论》', 'publishDate': '2006-9', 'price': 85.00, 'number': 0},
{'bookName': '《UNIX变成艺术》', 'publishDate': '2006-2', 'price': 59.00, 'number': 0},
{'bookName': '《编程珠玑》', 'publishDate': '2008-10', 'price': 39.00, 'number': 0},
{'bookName': '《代码大全》', 'publishDate': '2006-12', 'price': 128.00, 'number': 0}
],
},
computed: {
totalPrice(){
let result = this.books.reduce(function(preValue, book){
return preValue + book.price * book.number
}, 0)
return result;
}
},
methods:{
increment(index, book){
this.books[index].number--;
},
decrement(index, book){
this.books[index].number--;
},
remove(index){
this.books.splice(index,1);
}
},
filters: {
transPrice(price){
if(typeof(price)=='number'){
return price.toFixed(2);
}
}
}
})
</script>
- 高阶函数
- filter() : filter回调函数必须返回一个bool值
- map()
- reduce(): 对数组中所有内容进行汇总arr = [1,2,3,4] arr.reduce(function(preValue, n){ return preValue + n; }, 0) 0 表示preValue的初始值, n表示每次遍历时的元素
- 表单绑定v-model
- v-model其实是一个语法糖,他的背后本质包含两个操作
- v-bind绑定一个value属性
- v-on指令给当前元素绑定input事件<input type='text' v-model='message'> 相当于 <input type='text', :value='message' @input='message= $event.target.value'> 相当于 <input type='text' value='message' @input='changeMessage'> methods:{ changeMessage(event){ this.message = event.target.value; } }
- v-model结合radio
<label for="male"> <input type="radio" id="male" value="男" v-model='sex'>男 </label> <label for="female"> <input type="radio" id="female" value="女" v-model='sex'>女 </label> data{ sex: '男' }
- v-model结合checkbox
<!-- 单选 --> <label for="license"> <input type="checkbox" id="license" v-model='isAgree'>同意协议 </label> <button :disabled='!isAgree'>下一步</button> data{ isAgree: false } <div> <!-- 多选 --> <input type="checkbox" value="篮球" v-model="hobbies">篮球 <input type="checkbox" value="足球" v-model="hobbies">足球 <input type="checkbox" value="乒乓球" v-model="hobbies">乒乓球 <input type="checkbox" value="羽毛球" v-model="hobbies">羽毛球 </div> data{ hobbies: [] }
- v-select :单选(字符串) 多选(数组)
- v-model 修饰符
- .lazy<input type="text" v-model.lazy="message">
敲回车或失去焦点才同步数据
- .number<input type="number" v=model.number="age">
- .trim<input type="text" v-model.trim="message">
组件化开发
组件注册步骤
- 创建组件构造器对象
- 注册组件
- 使用组件
const cpn = Vue.extend({ template: `<div><h2>hhhhh</h2></div>` }) Vue.components('my-cpn', cpn)
- 全局组件和局部组件
- 全局组件可以在多个Vue实例中使用
- 局部组件const app = new Vue({ el: '#app', data:{} conponents: { cpn: cpn #局部组件 } }
- 父组件和子组件
- 注册组件语法糖, 主要时省去了调用Vue.extend()的步骤,而是直接可以使用一个对象来代替
// 全局 Vue.components('cpn', { template: `<div><h2>hhhhh</h2></div>` })
组件间的通信
父子组件的通信
- 通过props向子组件传递数据
<div id="app">
<cpn :cmovies="movies"></cpn>
</div>
<template id="cpn">
<div>
<ul>
<li v-for="movie of cmovies" :key="movie">{{movie}}</li>
</ul>
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
movies: ['aaa', 'bbb', 'ccc'],
message: 'hello world'
},
components: {
'cpn': {
template: '#cpn',
data(){
return{
}
},
// props: ['cmovies']
// props:{
// cmovies: Array
// }
props:{
cmovies: {
type: Array,
default(){
return [1,2,3]
}
required: true
}
}
}
}
})
</script>
==props里面如果是驼峰式写法,在v-bind用的时候要使用-连接==
cMovies ---> :c-movies="movies"
- 通过事件向父组件发送消息
- 在子组件中,通过$emit()来触发事件
- 在父组件中,通过v-on来监听子组件事件
<div id="app">
<cpn :cmovies="movies" @item-click="cpnClick"></cpn>
</div>
<template id="cpn">
<div>
<button v-for="movie of cmovies" :key="movie" @click="itemClick(movie)">{{movie}}</button>
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
movies: ['aaa', 'bbb', 'ccc'],
message: 'hello world'
},
methods:{
cpnClick(item){
console.log(item);
}
},
components: {
'cpn': {
template: '#cpn',
data(){
return{
}
},
methods: {
itemClick(item){
this.$emit('item-click', item)
}
}
}
}
})
</script>
父子组件的访问方式
- 父组件访问子组件:使用$children或$refs
this.$children 是一个数组,包含了所有的子组件<child refs='aaa'></child> 在父组件中使用this.$refs.aaa获取对应子组件
- 子组件访问父组件:使用$parent
- 访问根组件$root
插槽(slot)
- 组件的插槽是为了让封装的组件更加有扩展性
- 让使用者可以决定组件内部的一些内容到底展示什么
- 插槽的基本使用
<slot></slot>
- 插槽的默认值
<slot><button>按钮</button></slot>
- 如果有多个值,同时放到组件中替换,一起作为替换元素
具名插槽
<cpn>
<button slot='left'><button>
<cpn>
<template id='cpn'>
<slot name='left'></slot>
<slot name='center'></slot>
<slot name='right'></slot>
</template>
作用域插槽
父组件替换插槽的标签,但是内容由子组件来提供
<div id="app">
<cpn>
<template slot-scope='slot'>
<span>{{slot.data.join('-')}}</span>
</template>
</cpn>
<cpn>
<template slot-scope='slot'>
<span>{{slot.data.join('*')}}</span>
</template>
</cpn>
</div>
<template id="cpn">
<div>
<slot :data='languages'>
<ul>
<li v-for="item of languages">{{item}}</li>
</ul>
</slot>
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var app = new Vue({
el: '#app',
data(){
return {
}
},
components: {
cpn: {
template: '#cpn',
data(){
return {
languages: ['aaa', 'bbb', 'ccc', 'ddd']
}
}
}
}
})
</script>
模块化开发
模块化有两个核心:导出和导入
CommonJs的导出
moduleA.exports = {
flag: true,
sum(a,b){
return a + b
}
}
CommonJS的导入
let {flag, sum} = require(‘moduleA’)
webpack入门
Vue Cli详解
安装
npm install -g @vue/cli
初始化项目
- Vue cli3
vue create projectName
- 兼容Vue cli2
npm install @vue/cli-init -g
vue init webpack projectName
Vue CLI2使用
vue init webpack vuecli2test
输入上述命令后有以下配置:
- Project name : 项目名称,不能包含大写
- Project description
- Author: 作者信息,默认会从git中读取
- Vue build : runtime + complier和runtime-only
- Install vue-router : 是否安装路由
- 其他不重要的
chrome使用v8引擎直接将js代码转换成二进制文件,所以运行速度较快
node环境可以直接执行js文件
node test.js
static文件夹和assets文件夹的区别?
都是存放静态文件的,assets中的图片会根据设置的limits,将大于的使用hash重命名后放到dist下对应文件夹中,小于的转换成base64格式直接显示。而static中的文件,webpack不会进行处理,会原样显示。
runtime+complier和runtime-only的区别
//runtime+complier
import App from './App'
new Vue({
el: '#app',
template: '<App/>'
})
template-->ast(抽象语法树)-->render-->virtualDom-->realDom
//runtime-only (性能更高,代码更少(少了complier那部分))
new Vue({
el: '#app',
render: h=>h(App)
})
h实际是createElement函数
普通用法: createElement(标签, {属性}, [内容])
createElement('h2', {class: 'box', ['hello world']})
也可以直接传入组件: createElement(App)
render-->virtualDom-->realDom
runtime-only模式不包含template,那么app中的template是如何处理的?使用vue-template-complier将template编译成render函数后才使用
Vue Cli3
vue-cli3和vue-cli2的区别
- vue-cli3是基于webpack4打造,vue-cli2还是webpack3
- vue-cli3的设计原则是“0配置”,移除了配置文件目录下的build和config等目录
- vue-cli3提供vue ui命令,提供了可视化配置,更加人性化
- 移除了static文件夹,新增了public文件夹,并且index.html移动到public中
Vue Cli3修改配置的方法
vue ui启动一个本地服务,在界面上修改
node_modules下面的@vue/webpack.config.js
-
在当前项目下创建一个文件,vue.config.js
module.exports = { }