vue2.x 学习笔记
1.Vue的核心点
响应的数据绑定
自动更新视图,利用Object.definedProperty中的setter和getter代理数据,监控对数据的操作。写Vue要有一种一切都要是操控数据的思维。
组合和视图组件
1.ui页面印射为组件树
2.划分组件可维护、可重用、可测试
2.虚拟DOM
利用在内存中生成与真实DOM与之对应的数据结构,这个在内存中生成的结构称之为虚拟DOM,当数据发生变化时,能够只能的计算出重新渲染组件的最小代价(只重新渲染发生变化的地方,DOM操作就需要进行重绘或回流)并应用到DOM操作上;虚拟DOM就是将一个对象印射到DOM树里面。
- 重绘:当一些元素需要更新属性,而这些属性只是影响元素的外观,比如换个字体颜色,而不会影响整天布局的,就称为重绘。
- 回流:当DOM树因为元素的数量、尺寸、隐藏等改变而需要重新构建,这就是回流,比如display:none。页面第一次加载的时候也是一次回流。
- 回流必定会引起重绘、重绘不一定会引起回流。
3.MVVM模式
- M : Model 数据模型
- V : view 视图模板
- VM :view-Model 视图模型
<!--数据的双向绑定--> <div id="demo"> <input type="text" v-model="message"/> <p v-on:click='clickHandle'></p> </div> <script type="text/javascript"> let data = { message : 'hello world' } var vm = new Vue({ el : '#demo', data, methods:{ clickHandle(){ alert('click'); } } }) </script>
1.new Vue是实例也是VM(也叫作选项对象),el是挂载的元素,只有挂载的元素及其子元素才可以让Vue监听,data是数据没什么好说的,语法是ES6的,相当于
data:data
;methods是放定义的方法的地方,就像data放数据,v-on是指令,缩写为@。
2.每个Vue实例都会代理其data对象里面所有的属性,这些被代理的属性是响应的,新添加的属性不具备响应功能,改变后不会更新视图。
3.Vue自身的属性和方法以$
开头,比如:$route
4.声明式和命令式
声明式:只需要声明在哪里做什么,而无需关心如何实现。
命令式:需要以具体的代码表达在哪里做什么如何实现。
Vue声明式渲染:初始化根实例,vue自动将数据绑定到DOM模板上。
5.指令
是一种特殊的自定义行间属性。在Vue中指令以v-开头。
指令的职责就是当其表达式改变时相应地将某些行为应用到DOM上。
- 1、v-bind:动态的绑定数据,简写为:
2、v-on:绑定事件监听器,简写为@
3、v-text:更新数据,会覆盖已有结构。
4、v-html:可以解析数据中的html结构
5、v-show:根据值的真假,判断元素的display属性
6、v-if:根据值的真假,切换元素会被销毁、重建。
7、v-else-if:多条件渲染,为真则渲染。
8、v-else:条件都不符合 渲染。
9、v-for:基于源数据多次渲染元素或模板。
10、v-model:在表单控件元素上创建双向数据绑定
11、v-pre:跳过元素和子元素的编译过程
12、v-once:只渲染一次随后数据更新不重新渲染。
13、v-cloak:隐藏未编译的Mustache语法,css中设置[v-cloak]{display:none}<!--给一个元素动态绑定class--> <style>.red{color:red;}</style> <div id="demo"> <p :class='{red:addClass}'> </p> </div> //js p标签里面绑定class,是一个字符串然后里面是一个对象,key是要绑定的类名,后面是一个布尔值。 let obj = { addClass : true } var nv = new Vue({ el:"#demo", data:obj })
6.模板
6.1、HTML模板
基于DOM的模板,模板都是可解析的有效的HTML
6.2、差值
文本:使用Mustache语法:
原生的HTML:不会解析原生HTML,如果想解析成HTML需要在标签上添加指令 v-html,只在可信内容上使用 v-html,永不用在用户提交的内容上。
属性:使用v-bind绑定,可以响应变化
JavaScript表达式:可以写简单的表达式比如三目运算
6.3、字符串模板
template选项对象的属性:
模板会替换挂载的元素,挂载元素的内容会被忽略
根节点只能有一个
可以将HTML结构写在一对script中,设置type="x-template"
不过只能在本页面使用
如果 Vue 选项中包含渲染函数render,该模板将被忽略。//html <div id="demo"> <p>p</p> <!--这里最终会被str里面的替换掉--> </div> //js var str = '<div>div</div>' var nv = new Vue ({ el:"#demo", template:str })
6.4、render模板 render是选项对象的属性是一个函数,是字符串模板的代替方案,允许你发挥 JavaScript 最大的编程能力。该渲染函数接收一个 createElement 方法作为第一个参数用来创建 VNode
Vue 选项中的 render 函数若存在,则 Vue 构造函数不会从 template 选项或通过 el 选项指定的挂载元素中提取出的 HTML 模板编译渲染函数。
render接收一个参数是一个方法,createElement,createElement的第一个参数是要创建的标签名,第二个参数是数据对象(可选),第三个是子元素,子元素为文本或数组。<!--css--> <style>.bg{color:red;}</style> <!--HTML--> <div id="demo"></div> //js var nv = new Vue({ el:"#demo", render(createElement){ return createElement("ul", { // 数据对象 想添加的话直接写个true就可以 class:{bg:true}, // domProps添加的DOM节点优先级高 domProps:{innerHTML:"<li>通过数据对象添加的DOM节点</li>"} }, [ createElement("li",1), createElement("li",2), createElement("li",3) ]) } })
6.5、createElement()的第二个参数的属性集合
* class:{} 绑定class和`v-bind:class`一样的API style:{} 绑定样式,和`v-bind:class`一样的API sttrs:{} 添加行间属性 domProps:{} DOM元素属性 on:{} 绑定事件 nativeOn:{} 监听原生事件 directives:{} 自定义指令 scopedSlots:{} slot作用域 slot:{} 定义slot名称 key:"key" 给元素添加唯一标识 ref:"ref" 引用信息
6.6、watch属性 一个对象,键是需要观察的表达式,值是对应回调函数。值也可以是方法名,或者包含选项的对象。Vue 实例将会在实例化时调用 $watch(),遍历 watch 对象的每一个属性。
不应该使用箭头函数来定义 watcher 函数,因为箭头函数绑定了父作用域的this
watch有两种监控模式,深度监控和浅监控。var vm = new Vue({ data: { a: 1, b: 2, c: 3, d: 4 }, watch: { // 当a的值发生变化的时候就执行后面的函数 a: function () { console.log(this.a) }, // 深度 watcher c: { handler: function (val, oldVal) { /* ... */ }, deep: true } } })
7、组件
7.1、组件其实就是页面组成的一部分,它是一个具有独立的逻辑和功能或界面,同时又能根据规定的接口规则进行相互融合变成一个完美的应用,前端组件化的核心思路就是将一个巨大复杂的东西拆分成各种合理的小东西;组件基本由样式结构、行为逻辑、数据构成。
7.2、在 Vue 里,一个组件是一个自定义标签,vue的编译器为它添加特殊功能,vue也可以扩展原生的HTML元素封装可重用的代码;一个组件本质上是一个拥有预定义选项的一个 Vue 实例,注册一个名为 todo-item 的新组件// 定义名为 todo-item 的新组件 Vue.component('todo-item', { template: '<li>这是个待办项</li>' }) <!-- 创建一个 todo-item 组件的实例 --> <todo-item></todo-item>
7.3、注册组件
7.3.1、全局注册:可以在任何模板中使用,使用之前要注册,语法:Vue.component(组件名,选项对象)
//注册组件
Vue.components("todo-item",{
//组件的模板
template:`<div>div</div>`
})
//实例
new Vue({
el:"#demo"
})
7.3.2、局部注册:在组件实例中通过选项对象注册,只能在注册的作用域中使用。
new Vue({
el:"#app",
// 只能在#app下面使用
components:{
"组件名":选项对象
}
})
7.4、组件间的通信:父组件要传递数据到子组件,子组件需要将它内部发生的事情告诉父组件,组件中的data必须是函数。
7.4.1 父组件->子组件: 可以在组件上使用自定义属性绑定数据,在组件中需要显式的用props声明自定义属性名,props可以进行验证,定义想要的类型的值props:{value:{type:Nbumber}}
,还可以定义默认值props:{value:{default:10}}
,还可以规定必须传值props:{value:{required:true}}
;子组件内部不应该修改父组件传递过来的数据,想修改的话可以存一下,然后改变存的那个值。
//父组件 把循环到的item放到todo里面传给子组件
<todo-item
v-for="item in groceryList"
v-bind:todo="item"
v-bind:key="item.id">
</todo-item>
//注册子组件
Vue.components("todo-item",{
//子组件想要拿到todo需要使用props显式的声明一下todo
props:["todo"],
//子组件的模板
template:`<div></div>`
})
//实例
new Vue({
el:"#demo",
data:{
fruitList: [
{ name : '苹果' },
{ name : '橘子' },
{ name : '香蕉' }
]
}
})
7.4.2 子组件->父组件:需要用到自定义事件,父组件用$on监听自定义事件,$emit触发父组件所关心的自定义事件
//假设这里是一个父组件
Vue.components("parent",{
template:`<div @carry="change"> <child></child> </div>`,
methods : {
change(val){ // 拿到假装有的值并且打印出来
console.log(val)
};
}
})
Vue.components("child",{
// 假装这里props接受到一个值是val
template:`<div @click="change1(val)"> </div>`,
methods:{ // 把假装有的值发送给父组件
change1(val){ // $emit 触发 父组件定义的carry事件并且传值过它
this.$emit("carry",val)
}
}
})
7.5、组件的好处
- 提高开发效率
- 方便重复使用
- 简化调试步骤
- 提升整个项目的可维护性
- 便于协同开发
8.slot插槽——分发内容: 元素可以用一个特殊的特性 name 来进一步配置如何分发内容。多个插槽可以有不同的名字。具名插槽将匹配内容片段中有对应 slot 特性的元素。仍然可以有一个匿名插槽,它是默认插槽,作为找不到匹配的内容片段的备用插槽。如果没有默认插槽,这些找不到匹配的内容片段将被抛弃。
1、用法: 在官方文档。
2、作用域插槽:作用域插槽是一种特殊类型的插槽,用作一个 (能被传递数据的) 可重用模板,来代替已经渲染好的元素。
9.路由
1、创建一个router
文件放在src
目录下面创建一个router
文件夹,然后配置。
import vue from 'vue'
import vueRouter from 'vue-router'
import temp from '@/xx/temp'
vue.use(vueRouter)
export defualt new({
mode: 'history',
// 设置路由激活的样式名,默认为router-link-active
linkActiveClass:'yourClass',
routes: [
{
path:'/',
name: 'temp'
component: temp,
children:[
// 子路由
]
}
]
})
2、重定向和别名
{
path:'/',
cmponent: temp,
alias: 'index' // 别名
},
//重定向
{
path: '*',
redirect: '/home'
redirect: {path:'/home'}
redirect: {name:'home'}
redirect: to=>'/home'
}
3、router-view的默认事件是点击,想修改的话
<router-link event="mouseover"></router-link>
4、路由选择渲染的内容用router-view
展示,精确匹配的话加上exact
,防止点击切换后原先的激活状态的样式不消失。
<router-view exact></router-view>
路由嵌套的话,配置路由,有默认子路由的话,父路由就不要设置name
属性,把name
属性给默认子路由设置上。
{
path:'/',
name: 'temp'
component: temp,
children:[
{
path:'',// 默认子路由
component: 'aboout'
}
]
}
5、有时候需要一个组件里有多个router-view
,这个时候就需要用命名了
<router-view name="slider"></router-view>
<router-view></router-view> <!--默认视图-->
// 路由配置
{
path:'/content',
name: 'content'
components:{
// 默认视图,渲染到没有`name`的那个`router-view`里面
default: content,
// 渲染到`name="slider"的视图组件里面去`
slider: slider
}
}
6、有时候希望点击浏览器的前进后退功能切换路由的时候保存着当前页面的进度,切换过去再切换回来的时候还是原来的位置,这个时候我们就需要搞点事情,vue-router实例上面预留了一个方法,方法接收三个参数,to,from,savaPosition
,第一个参数:要去的目标路由,第二个参数,当前的目标路由,第三个参数记录滚动条的坐标,
实现方法:↓↓↓↓↓ (写mode
属性只是想说明这是在根实例上面)
{
mode:'',
scrollBehavior(to, from, savePosition){
if(savePosition){
return savePosition;
} else {
return {x:0,y:0}
}
}
}
有时候我们想利用hash精确定位到某个元素那里
<router-link :to="{path:'/document#abc'}"></router-link>
// 组件里面设置
<p id="abc"></p>
// vue-router
{
mode:'',
scrollBehavior(to, from, savePosition){
if(to.hash){
return {
selector: to.hash
}
}
}
}
7、动态路由,有时候我们想让路由的设置是动态的。
// items 是一组数据,其中有一个属性id,值为:1 | 2 | 3
<router-link :to="'/user/'+ item.id" v-for="item in items"></router-link>
// 路由里面配置
{
// userId 是变量名,随意叫什么都可以,代表的是user路径后面的值,`?` 和正则里面的问号意思一样,不知道出现几次,可能不出现
path:'/user/:userId?'
}
我们想要拿到user/1
后面的这个1
或者2、3的话需要用到路由信息对象
$router router实例对象
$route 当前激活的路由信息对象,每个组件实例都有
beforeRouteEnter() 进入组件前的钩子函数
beforeRouteLeave() 离开组件前的钩子函数
// 了解了上面的就可以拿到想要的东西了
created(){
this.$route.params.userId
}
this.$route
的值
1、fullPath:字符串,包含查询参数和hash完整的路径
2、hash:字符串,当前路由的哈希值
3、matched:数组,包含当前路由的所有嵌套路径片段的路由记录
4、meta:元信息
5、name:字符串,当前组件名字
6、params:对象,动态路径的参合就是在这里,比如刚才想拿到的`1`
7、path:字符串,当前路由的路径
8、query:字符串,URL的查询字符串
监控$route,如果$route发生变化采取一些操作
watch:{
$route(){
console.log($route.params.userId)
}
} 获取URL里的查询字符串的值 ```
<router-link to="info=follow"></router-link>
### 10、编程式导航
通过$router的事件来修改路径
back:回退一步
forward:前进一步
go:指定前进几步
push:导航到不同的url,向history栈添加一个新记录
replace:导航到不同的url,替换history中的当前记录
使用方法:
<input @click="backUrl"/>
methods:{
// forward用法和back一样
backUrl(){
this.$router.back()
},
goUrl(){
// 正数是前进,负数是后退,不可超出栈里面的记录数量
this.$router.go(1)
},
// replace和push一样
pushUrl(){
//
this.$router.push('/about')
}
} ```