第一章
Vue概述
what? Vue是实现UI层的渐进式js框架,核心库关注视图层,简单的ui构建,复杂的路由控制、网络通信...
Vue.js(读音 /vjuː/,类似于view)是一套构建用户界面的渐进式框架。与其他重量级框架不同的是,Vue 采用自底向上增量开发的设计。Vue 的核心库只关注视图层,它不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与单文件组件和 Vue 生态系统支持的库结合使用时,Vue 也完全能够为复杂的单页应用程序提供驱动,是轻量级的 MVVM 框架
从最简单的数据处理,到数据交互,到DOM操作,到路由处理等,VueJS都有完整的解决方案
why? ① Vue借鉴了Angular指令和React组件的优势开发出来的
② Vue渐进式的框架,学习曲线比较缓和,官方提供了全套的技术支持
③ Vue 效率比较高,代码组织非常轻松
④ 单文件(以 .vue 结尾),其中包含了一个组件所需要用到的html/css/js
⑤ Vue是个人开发者开发的框架,比公司开发的框架发布版本、解决问题的速度更快
⑥ 支持jsx、js、ES6、typescript,支持的语法比较多
⑦ vue2.0引入了Virtual DOM(虚拟DOM),页面更新效率更高,速度更快
where? 可以做 ui 视图层、实现SPA、也可以加入路由、网络、状态管理等,有非常多的官方工具
IE9及更高版本才支持
问题: Vue和jQuery之间的区别?
Vue是声明式,是通过描述状态与视图之间的映射关系,然后通过这样的一个映射关系来操作DOM,或者说是用这样的映射关系来生成一个DOM节点插入到页面中去
jQuery是命令式,使用jQuery操作DOM去局部更新视图,做到局部渲染,命令式就是想做什么就直接去调用方法直接做,简单直接
在逻辑上,Vue只有一个行为: 就是状态,而jQuery是两个行为: 状态+DOM,Vue可以让开发者把关注点只放在状态的维护上,不需要关系操作DOM,降低了代码维护的复杂度
https://cn.vuejs.org //官方教程
https://github.com/vuejs/vue-cli //用CLI搭建vue脚手架
https://github.com/vuejs/vue-devtools //vue调试工具
下载仓库→npm install下载依赖→npm run build编译→Chrome→更多工具→扩展程序
→将shells/chrome文件夹拖过去,在F12(开发者工具)中看到Vue即可(chrome文件夹不能删)
https://router.vuejs.org/zh-cn //路由手册
https://github.com/pagekit/vue-resource //vue-resource文档
https://github.com/vuejs/awesome-vue //Vue的其它库
how?
搭建Vue开发环境
方式1 借助于vue-cli(Vue的脚手架工具,30kB的gzipped)
npm install webpack -g //全局安装webpack
npm install --global vue-cli //全局安装 vue-cli
vue -V //查询安装的vue的版本
vue list //显示可用的模板
vue init webpack(模板名) myapp(项目名) //创建一个基于 webpack 模板的完整项目
初始化设置:
Target directory exists. Continue? (Y/n)直接回车默认会下载 vue2.0模板(可能需要连代理)
Project name (vue-test) 直接回车默认
Project description (A Vue.js project) 直接回车默认
Use ESLint to lint your code 是否使用ESLink检查代码(可选N)
Author 写上自己的名字
cd 项目名 //切换到项目
npm install //安装项目依赖(自动安装package.json中指定的文件到node_modules文件夹中)
npm run dev //运行,调试模式(已自动设置热更新,在src中开发会自动更新)
npm run build //发布,发布模式(编译、打包)
在手机上查看vue.js项目:
① 修改config/index.js文件
module.exports = {
dev: {
host: '0.0.0.0' //原为: host: 'localhost'
}
}
② 查看本机ip(在命令行输入ipconfig)
③ 将项目url中的localhost修改为此ip即可在手机端查看此项目(手机和电脑需要在同一局域网下)
import Vue from 'vue'
import VueRouter from 'vue-router'
import VueResource from 'vue-resource' //这三个都要在单文件的main.js中引入注册才能使用
主目录:
index.html 首页,可自由修改(可设置视口、引入zepto、md5、echarts等,发布时会被单独分开)
package.json 可修改name、版本、描述、作者,项目配置文件,指定项目的依赖
README.md 描述文件,可修改
config 配置,启动相关,里面的index.js可修改
node_modules 项目依赖的包
static 放置静态资源的文件夹(图片、js、css文件等)
build/webpack.base.conf.js 基础配置文件,配置别名alias('@':resolve('src'),将@解析为src)
dist 发布的文件目录,即webpack编译输出的目录
src文件夹 主要在此开发(开发目录)
main.js 入口文件,初始化vue实例并设置需要使用的插件(比如less、css,发布时不会被分开)
比如: require('./assets/css/adapter.less'); 会被合成压缩为一个文件
App.vue 主组件,所有页面都是在App.vue下进行切换的,可以将所有的路由看作它的子组件
router 配置路由组件
components 功能组件,用来与用户交互
mockServer 模拟后端服务,即用webpack开发时模拟调用的后端服务(用nodejs服务模拟)
libs 放置公共的文件,如js、css、img、font等
通常一个前端项目会分有一个 src 目录和 dist 目录,src放置源码,dist放置编译后的代码
static存放第三方静态文件
src/main.js:全局配置
Vue.config.devtools=true; //是否允许vue-devtools检查代码,方便调试
//开发版本默认为true,生产版本默认为false
Vue.config.productionTip=false; //是否需要vue启动时生成生产提示
build/webpack.prod.conf.js: 或config/index.js:
将 productionSourceMap: true, 改为false,不输出.map文件
npm install less less-loader --save //安装less及其依赖项
方式2 直接引入 vue.js 文件到项目工程中
________________________________________________________________________________________________
一、vue编译打包后,css的引用路径错误:
1、在webpack.prod.conf.js文件里的output里面添加: publicPath: './'
2、在utils.js文件里添加:
return ExtractTextPlugin.extract({
use: loaders,
fallback: 'vue-style-loader',
publicPath: '../../',
})
3、在config/index.js文件里,添加: assetsPublicPath: './'
二、使用Object.freeze()可以冻结一个对象,冻结后不能向对象添加新的属性,不能修改已有属性的值,Vue遇到这类对象时,不会为对象加上getter、setter等数据劫持的方法,可以节省浏览器CPU开销,比较适合展示类的场景
三、v-else指令必须紧跟在v-if或者v-else-if元素的后面,否则它将不会被识别,当v-if与v-for一起使用时,v-for具有比v-if更高的优先级
所以当循环的集合是为空时,v-if后面的v-else不会被识别
解决: 可以将一个元素当做不可见的包裹元素,并在上面使用v-if进行渲染分组,最终的渲染结果将不包含元素
注意: v-show不支持template元素
四、判断输入框是否只读
五、绑定内联样式
六、去掉元素与元素之间的空格
在vue-loader中: vue:{ preserveWhitespace:false }
七、问题: vue-loader会对style scoped中的每个选择器添加一个自定义属性,但挂载实例外的元素无法添加自定义属性,导致选择器无效
解决: 在外添加一个写对应样式
_______________________________________________________________________________________________
使用vue
1、vue基本用法
new Vue({
el:"#example", //将实例与该选择器对应的元素绑定在一起(数据、组件会加载在其中)
也可用class:'.example'、标签:'div',vue2.0不允许挂在到body/html上
data:{ }, //初始化数据(所有的变量都要在 data 对象中初始化)
methods:{ }, //方法函数 都要放在 methods 对象中
方法里的this指new Vue的返回值,this.变量名,可获取/修改变量(data/methods...的成员)
directives:{ } //指令放在directives中
})
2、vue自带指令
循环遍历: ————— 遍历得到的数据,后代元素可以直接使用,使用v-for的元素会遍历出现多次
v-for = "临时变量名 in 集合" //集合可以是对象或数组
v-for = "(value值, key下标或属性) in 集合"
{{tmp}}
:key="key"可为遍历的每个元素绑定唯一的key属性,当数据变化时,可重用之前的元素,提高渲染效率
选择 v-if v-else-if v-else 添加分支处理不同的选择结果,添加/移除在DOM树上
v-show 控制元素的显示/隐藏(不移除)
{{msg}}
可以隐藏未编译的标签直到实例准备完毕再编译
需要与css配合使用: [v-cloak]{ display:none; }
只渲染元素和组件一次,随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过
绑定 v-bind 将变量的值,绑定到组件的某一个属性上(如href/width/height...)
//用字符串拼接,将变量绑定到属性上
//直接写变量,绑定到属性上
天猫 //简写,省略v-bind(冒号不省略)
//不带变量可以不用绑定
//绑定常量,需要在引号内加单引号
//绑定变量,直接放在引号内即可
clickMe //为元素绑定事件处理函数,不传参可不加()
clickMe //简写,将 v-on: 换成@
也可以将函数改为其它操作(如赋值)
Vue.js 为 v-on 提供了事件修饰符,通过由点(.)表示的指令后缀来调用修饰符
阻止单击事件冒泡
提交事件不再重载页面
只有修饰符
...
添加事件侦听器时使用事件捕获模式
...
只当事件在该元素本身(而不是子元素)触发时触发回调
点击事件将只会触发一次
修饰符可以串联
注: 使用修饰符时,顺序很重要,相应的代码会以同样的顺序产生
所以,用@click.prevent.self会阻止所有的点击,而@click.self.prevent只会阻止元素上的点击
methods: { //在 methods 对象中定义事件的方法
handleClick: function(e){ //@click="handleClick"不需要传参
console.log(this.name) // this 指向当前 Vue 实例
console.log(e.target.tagName) // e 指向当前的原生DOM元素
}
}
vue可以给class或style属性绑定对象或数组,且可以与普通的class属性共存
//active和'text-danger'的值为真时会添加class,否则会移除
//isActive/hasError也可换成Boolean类型结果的表达式
//也可以绑定数据里的一个对象(对象的属性作为类名)
data: { //要在data中初始化变量为Boolean类型,当变量变化时,class会随之变化
isActive: true,
hasError: false,
classObject: {
active: true,
'text-danger': false
}
}
可以为select指定默认的option:
在option上绑定 :selected="sel.id == item.id" 返回true或false来指定默认值
在Vue 2.0中,为自定义组件绑定原生事件必须使用 .native 修饰符:
Click Me
数据绑定:
用双花括号包裹变量可将数据显示在视图上,比如
{{msg变量}}
v-model是双向数据绑定的指令,可将视图中用户操作的结果绑定到数据上
{{uName}}
v-model 的修饰符:
.lazy
在默认情况下,v-model 在 input(输入后,立即触发,无延迟) 事件中同步输入框的值与数据,但你可以添加一个修饰符 lazy,从而转变为在 change(失焦时触发,有延迟) 事件中同步
//"change"取代"input"事件,进行更新
.number
如果想自动将用户的输入值转为 Number 类型(如果原值的转换结果为 NaN 则返回原值),可以添加一个修饰符 number 给 v-model 来处理输入值
//输入字符串转为数字
这很有用,因为在type="number"时,HTML中输入的值也总是会返回字符串类型
.trim
如果要自动过滤用户输入的首尾空格,可以添加 trim 修饰符到 v-model 上过滤输入
//过滤掉首尾的空格
_______________________________________________________________________________________________
3、vue自定义指令
自定义指令: 在vue的实例中,指定一个directives 属性,对应的值是一个对象,在对象中创建指令、指定指令的钩子函数
每一个指令对应着都有自己的钩子函数(bind绑定、update更新、unbind解绑...),会在指令的调用过程中,在合适的阶段自动执行
new Vue({
directives:{
change指令名:{ //创建指令
bind: function(){ }, //绑定时调用,只调用一次
inserted: function(){ }, //被绑定元素插入父节点时调用
update: function(){ }, //被绑定元素所在的模板更新时调用,不论绑定值是否变化
(指令不会自动发起更新,要用方法发起)
componentUpdated: function(){ }, //被绑定元素所在模板完成一次更新周期时调用
unbind: function(){ }, //解绑时调用,只调用一次
}
} })
指令
//使用指令
钩子函数被赋予了以下参数:
el: 指令所绑定的元素,可以用来直接操作 DOM
binding: 一个对象,包含以下属性:
name: 指令名,不包括 v- 前缀
value:指令的绑定值, 例如 v-my-directive="1 + 1", value 的值是2
oldValue: 指令绑定的前一个值,仅在update和componentUpdated钩子中可用,无论值是否改变都可用
expression: 绑定值的字符串形式。例如 v-my-directive="1 + 1",expression 的值是"1 + 1"
arg: 传给指令的参数。例如 v-my-directive:foo,arg 的值是"foo"
modifiers: 一个包含修饰符的对象。例如 v-my-directive.foo.bar,修饰符对象 modifiers 的值是{ foo: true,bar: true }
_______________________________________________________________________________________________
4、过滤器
vue2.0以后的版本,官方没有提供任何的过滤器,但有创建过滤器的接口
① 方案1: 创建自定义过滤器
过滤器的本质其实就是方法,可以处理输入,返回输出结果,用来实现数据的筛选、过滤、格式化
在Vue实例的filters属性中创建过滤器:
new Vue({
filters:{
filterName过滤器名: function(value,arg1,arg2...){ //value指在|前传过来的参数
return处理后的数据 arg: 过滤器调用时,()中的参数
}
} })
使用: 过滤器通过管道符号 |进行调用,支持多重过滤
{{ price|currency过滤器}}
//不传参时不加()
{{ price|currency过滤器('¥','@'...)}}
//在()中可传多个参数
② 方案2: 借助第三方的过虑器
https://github.com/wy-ei/vue-filter //基于Vue2.0的过滤器的库
引入对应的js文件即可使用
_______________________________________________________________________________________________
5、Vue 的计算属性、监听属性
计算属性定义的是一些方法,任何的计算属性都可以在methods中定义一个相同的函数来替代它,两种方式最终的结果是相同的
但不同的是计算属性是基于它的依赖缓存的,计算属性只有在它的相关依赖发生改变时才会重新取值
也就是说只要 message 没有发生改变,多次访问'计算属性中的方法'会立即返回之前的计算结果,而不必再次执行函数
计算属性的优势:
① 让更新更高效,可以处理复杂的业务逻辑
② 让代码维护更方便
计算属性的创建:
new Vue({
computed:{
handle方法: function(){ }
} })
使用:
{{handle方法名}}
监听属性:
new Vue({
watch:{
kw: function(newValue,oldValue){ //kw: 要监听的变量名,需要初始化
newValue是改变后的值,oldValue改变前的值
},
a: {
handler: function(newVal, oldVal){ ... }, //当数据变化时执行的回调函数
immediate: true //创建组件时立即执行一次回调函数
},
}})
使用:
//双向数据绑定
_______________________________________________________________________________________________
6、vue实例的生命周期
React的组件的生命周期: mount update unmount
Vue的实例的生命周期: create mount update destroy
每个Vue实例在被创建之前都要经过一系列的初始化过程,在这个过程中,实例会调用一些生命周期钩子函数
create:
beforeCreate 组件实例(*.vue页面)刚被创建,组件属性计算之前,如data属性等
created 组件实例创建完成,属性已绑定,但DOM还未生成,el属性还不存在,挂载阶段未开始
mount:
beforeMount 模板编译/挂载之前
mounted 模板编译/挂载之后(不保证组件已在document中)
update:
beforeUpdate 组件更新之前(虚拟DOM重新渲染之前)
updated 组件更新之后
destroy:
beforeDestroy 组件销毁前调用(实例仍然可用)
destroyed 组件销毁后调用(调用后,Vue实例的所有东西都被解绑,所有事件被移除,子实例被销毁)
使用方法:
new Vue({
el: "#example",
data: {
msg: 1
},
mounted: function(){ //在生命周期函数中操作数据
setInterval(()=>{
this.msg++;
console.log(this.msg);
},0)
} })
注意: mounted不保证所有的子组件都已挂载到DOM上,可用this.$nextTick来确保整个视图都渲染完毕
mounted: function(){
this.$nextTick(function(){
//在整个视图都已渲染完毕后运行的代码
})
}
_______________________________________________________________________________________________
7、组件的创建和使用 (分为局部组件和全局组件)
创建组件的方式: ①局部组件 ②全局组件 ③jsx ④使用ES6生成一个.vue结尾的文件
注意: 组件的命名、使用建议使用烤串的写法,字母要求都是小写,不能出现大写字母,不能是标签名
① 局部组件的创建:
new Vue({
components:{
'组件名': {
template: '
局部组件
'
}
} })
使用:
//与普通的html标签用法一样
② 全局组件的创建:
Vue.component('组件名',{ //使用Vue.component方法,创建在new Vue({ })之前
template: `
`
});
全局组件的使用方法与局部组件一样
全局组件和局部组件的区别:
全局组件可以用在任意一个组件中,可以构造更复杂的全局组件
局部组件只能够用在 vue 实例所绑定的元素视图中
③ 通过jsx的语法去指定模板 (必须在命令行创建的模板中才可以,因为会有对应的依赖)
④ 直接创建一个以 .vue 结尾的文件(通过命令行创建的模板默认的方式)
第二章*****************************************************************************************
注意: 若在创建的全局组件中,需要初始化或者声明数据,指定data属性,该属性对应的值是一个方法,该方法需要返回一个对象
Vue.component("全局组件",{
data:function(){ //data属性对应的值是一个方法
return { msg: ""} //返回一个对象
}
});
相当于:
new Vue({
el: "#example",
data: { msg: "" } //相当于 在vue实例中初始化数据
})
字符串里的内容若要换行,建议使用模板字符串
_______________________________________________________________________________________________
组件通信: 2种方式
1、父向子传递数据(通过属性)
Vue.component('father',{
data:function(){
return {value:""} //初始化数据
},
template: ` ` //① 父组件调用子组件时,为其指定属性,
` ` 值为父组件的数据 或 字符串
})
Vue.component('son',{
props: ['msg属性'], //② 子组件指定props属性,对应的值是数组,
数组中的字符串是从父组件传递过来的属性名称
template: '
{{msg}} ' //子组件可以使用'传递过来的属性的值',
}) 与'在data属性中定义的变量'用法一致
2、子向父传递数据(通过事件)
Vue.component('father',{
methods:{
resMsg方法名: function(msg接收到的值){ } //父通过方法接收值,msg就是从子传来的值
},
template: ` ` //① 在父中为子用v-on绑定自定义事件
})
Vue.component('son',{
methods:{
handleClick:function(){ //③ 在方法内调用this.$emit()方法,通过自定义事件向父传值
this.$emit('toF自定义事件','100要传的值'); //传变量时,前加this.
}
},
template:`向父传值 ` //② 点击触发事件,调用方法
})
_______________________________________________________________________________________________
3、父子通信(不仅可以传属性的值,还可以传方法)
优点: 只需要设置接收方即可,发送方不需要额外设置,方便
缺点: 耦合度太高
父向子传递数据,子接受数据($parent):
在子组件中,通过'this.$parent',找到父组件,读取父组件的属性/方法
Vue.component('parent',{ //父组件不需要额外设置
template: ` `
})
Vue.component('son',{
methods:{
handleClick:function(){
this.$parent.父的属性/方法 //this.$parent: 子找到父组件的实例,读取其属性/方法
}
},
template: ` ` //调用方法,找到父的实例
})
子向父传递数据,父接收数据($refs):
ref(reference,引用、参考),给子组件指定一个ref属性,在父组件中通过'this.$refs.指定的属性名'找到子组件
Vue.component('parent',{
methods:{
handleClick:function(){
this.$refs.son指定的属性名.子的属性/方法 //② this.refs.son: 父找到子组件的实例
}
},
template: `
//① 给子组件指定一个ref属性
//调用方法,找到子的实例
`
})
Vue.component('son',{ //子组件不需要额外设置
data:function(){
return { msg:'你好' } //父可接收子的属性/方法
},
template: ` `
})