Vue#
- Vue 是专注于 “网页视图层” 的前端框架。
- Vue 是用 js 来操作某一注册的视图组件的框架。
- Vue 已经拥有许多优秀的特性。
<html>
<div id="app">{{message}}</div>
<script>
new Vue({
el: '#app',
data: {
message: 'hello world!'
}
})
</script>
</html>
以上为一个典型的最小 Vue 框架的 HTML
Vue 接受了 id 为 app 的整个 div 块,而 Vue 将对此块进行操作。
{{message}} 是 HTML 无法识别的语法,但是由于把整个 div 块交给 Vue 处理,因此,Vue 可以识别即可。
周期图#
一、创建阶段(beforeCreate、created)#
created 前#
我们发现,在上面的 html 代码中,我们使用了 "{{}}" 双大括号语法模板语法,众所周知,这在 html 中可是没有这样的语法的。再看看 new Vue 时传入的 el: '#app' 参数。是的,我们将整个 id 为 app 的 div 交给了的 Vue 来管理,我们就是使用 Vue 对这个 id 为 app 的 div(专业点就是 DOM)操作来操作去的,故 vue 的文档里也有说:Vue 的核心库只关注视图层 。
所以,vue 在创建阶段(created 前)就是将传入的 div、data 以及 methods 等做初始化。
也就是说,做点准备工作
二、挂载阶段(beforeMount、mounted):#
created 到 beforeMount 阶段#
在 created 到 beforeMount 这个阶段发生了一件很不得了的事情 ——Vue 将传入的这个 id 为 app 的 div 转化成了一个貌似高大上的东西,叫抽象语法树(AST)。可又为什么要转化成抽象语法树呢?
(哈哈,这个问题稍加思索一下便可得到答案,读者可稍微暂停一下,再往下继续看文章)
因为这样 Vue 才能理解我们在 html 写的双大括号语法模板语法,以及在标签里添加的 v-bind,v-for,v-if,甚至是自定义标签等等乱七八糟的东西,将我们上面写的这个 id 叫 app 的 div——
<div id="app">
<p>
{{message}}
</p>
</div>
转化成 AST 之后就会像这个样子 ——
{
tag: "div",
type: 1,
staticRoot: false,
static: false,
plain: true,
parent: undefined,
attrsList: [],
attrsMap: {},
children: [
{
tag: "p",
type: 1,
staticRoot: false,
static: false,
plain: true,
parent: {
tag: "div",
type: 1,
staticRoot: false,
static: false,
plain: true,
parent: undefined,
attrsList: [],
attrsMap: {},
},
attrsList: [],
attrsMap: {},
children: [
{
type: 2,
text: {{message}},
static: false,
expression: "_s(message)"
}
]
}
]
}
我们可以看到,这个所谓的抽象语法树(AST)也就是一个 js 对象而已,在这个 js 对象里包含了所有需要用到的各种信息。
Vue 将这么一转化,js 操作这些变量不是轻轻松松,就可以将双大括号里的变量换成 data 里的数据啦。
beforeMount 到 mounted 阶段#
可浏览器根本读不懂这个 AST 啊,所以 Vue 在 beforeMount 到 mounted 这个阶段就是将 AST 再转化为 html 代码,也就是说,到 mounted 完成后
模板语法
<div id="app">
<p>
{{message}}
</p>
</div>
已经变成了
<div id="app">
<p>
Hello world!
</p>
</div>
三、挂载完毕(beforeUpdate、updated):#
到这,久闻大名的虚拟 DOM 终于出场了。
我们可以先看看虚拟 DOM 大概长什么样子 ——
{
tag:'div', // 元素标签
attrs:{ // 属性
id:'app'
}
children:[
{tag:'p',...} // 子元素
]
}
(可以看到,也是一个 js 的对象,通过键和值描述节点,但可千万不能认为这和 AST 没两样,这是两个不同阶段的产物)
然后再想想我们为什么需要虚拟 DOM—— 现代网页程序在运行时,状态会不停的发生变化,有可能是用户点击了按钮,有可能是发送了一次 axios 请求,这些行为都是异步的。每当状态发生某些改变时,就会需要重新渲染
难道每次发生变化了都把整个 DOM 删掉然后重新渲染一份出来吗?
我们看看真实 DOM 的体量 ——
一个 div 元素就这么多东西,这样做的代价无疑非常庞大的
故我们希望检测出来哪些 DOM 发生了变化,然后更新变化的 DOM 便能极大的优化性能,
在各大框架中都有自己的一套解决方案,在 Angular.js 中使用的是脏检查手法,这里不讨论。而 react.js 与 Vue.js 的手段便是虚拟 DOM
下图就是虚拟 DOM 的具体流程
看了此图,也就能理解 Vue生命周期里 beforeUpdate 与 updated 是什么时候触发的了
总之,虚拟 DOM 就起这么一个对比的作用,找出不同,然后有选择的替换渲染,达到优化性能的目的
四、销毁阶段(beforeDestroy、destroyed):#
就是 Vue 实例不再被使用的销毁阶段,具体细节在一开始图片里已描述的非常详细,这里不再赘述#
再讲讲 Vue 的响应式数据绑定
什么是响应式数据绑定?
响应式数据绑定就是在修改 data 里的数据后页面视图也随之改变,这可是在没有刷新页面的情况下做到的喔。
Vue 是怎么做到的呢?
想要做到数据变化后修改视图,首先要做到的就是监听到哪变化了,
1. 侦测变化:
深入学习过 JavaScript 的同学会知道:有两种方法可以侦测到 js 对象的变化,Object.defineProperty 和 ES6 的 Proxy(如果有不清楚的同学,具体的使用方法这里就不讨论了,大概就是在 Object.defineProperty 中当传入的对象被读取时会触发 get 回调,当传入的对象被修改时会触发 set 回调,ES6 的 Proxy 就是 js 提供的一种源编程能力,可以直接检测到对象的变化)
2. 通知变化
通过一系列的代码监听到都有哪些数据发生变化之后的下一步就是通知使用此数据的地方,希望使用此数据的地方使用新的数据,直接通知到 DOM 吗? Vue1.x 的版本确实是这样做的,可这么精确是有代价的,依赖追踪在内存的开销会很大,因此 Vue 搞了一个叫 Watcher 的东西,当数据变化时就先通知 Watcher,Watcher 再通知使用此数据的地方,每一个 Vue 组件也就是 Vue 实例都会有一个 Watcher,统一管理达到了优化的效果
PS:其实也挺简单的对吧,就是监听数据变化,然后通知到使用此数据的地方
但还有一个值得注意的地方,如果数组数据变化了的话,Object.defineProperty 和 ES6 的 Proxy 都是没有办法监听到的,所以数组的变化是通过在 Array 原型里构造一个拦截器,每当使用了 6 种数组方法的其中一个就能被拦截器监听到,实现监听数据改变的方法。
总结
在上述的每一步,Vue 都做了非常多的工作,从将 html 代码转化成 AST,到虚拟 DOM 的渲染,再到 diff 算法,又或是各种 API 的实现,源码都有很多有意思的地方,使用了函数柯里化优化速度,在抽象语法树里使用优化器,避免更改没有变化的 DOM 元素,还有更多的 v-if, v-for 的原理,过滤器等。多少了解一些可以很大程度的帮助你写出更优秀的 Vue 代码
————————————————
版权声明:本文为 CSDN 博主「我叫李文(living)」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_45963949/article/details/124575471
Vue 的使用#
文本绑定#
- {{variable}} 定义在标签内部,插值绑定
- v-text=‘variable’ 定义在标签属性中,让元素的文本内容与变量绑定
- v-html=‘variable’ 定义在标签属性中,让元素的标签内容和变量绑定
属性绑定#
让元素的 属性值 与 变量 单项绑定,属性值发生变化,变量会跟随发生变化。
- v-bind='variable' / ='variable'
<div>
<input type="text" :value="info">
<input type=="text" v-bind:value="info">
<a :href="url">超链接</a>
<img :src="imageName" alt="">
</div>
<script src="../vue.js"></script>
<script>
new Vue({
el:'div',
data:{
info:"属性绑定"
url:'http://www.baidu.com',
imageName:'head.jpg'
}
})
</script>
双向绑定#
定义在标签属性中,让标签控件的 value 值和变量双向绑定,相互影响同步
- v-model=‘variable’
<div id="app">
<input type="text" v-model='info'>
{{info}}
<h1>
注册表单
</h1>
<form action="">
用户名:<input type="text" v-model="user.username"><br>
密码:<input type="password" v-model="user.password"><br>
</form>
<script src="../vue.js"></script>
<script>
let v = new Vue({
el:"#app",
data:{
info:"双向绑定",
user:{
username:"",
password:""
}
}
})
</script>
</div>
事件绑定#
绑定元素的事件与函数。
鼠标点击实例: v-on:click="func"
或 @click="func"
<div id="app">
<input type="button" value="bt1" @click='f()'>
<input type="button" value="bt2" v-on:click='f()'>
</div>
<script src="../vue.js"></script>
<script>
let v = new Vue({
el:"div",
data:{},
methods:{
f:function(){
console.log("按钮被点击!");
}
}
})
</script>
遍历#
让元素根据绑定对象的成员数量自动遍历生成相同元素。
v-for="(variable,i) in vars"
<table>
<caption>汽车列表</caption>
<Tr>
<th>编号</th><th>品牌名称</th><th>售价</th><th>类型</th>
</Tr>
<tr v-for="(car,i) in arr">
<td>{{i+1}}</td>
<td>{{car.name}}</td>
<td>{{car.price}}</td>
<td>{{car.type}}</td>
</tr>
</table>
<script src="../vue.js"></script>
<script>
let v=new Vue({
el:"table",
data:{
arr:[ {name:'a',price:50000,type:'car1'}
{name:'b',price:60000,type:"car2"}
{name:'c',price:40231,type:'car3'}]
}
})
</script>
判断#
让元素的显示状态与变量绑定,true 则显示,false 则不显示
v-if="variable"
和v-else
<div id="app">
<h1 v-if="isVisible">
标题可调整显示
</h1>
<h1 v-else>
否则显示此内容
</h1>
</div>
<script src="../vue.js"></script>
<script>
let v=new Vue({
el:'div',
data:{
isVisible:true
}
})
</script>
Tips#
property
attribute
前者是标签固有属性,后者是标签内定义的 k-v 键值对npm root -g
查看全局插件路径
css#
Position 定位#
-
static
默认文档流定位,从上到下,从左到右
-
relative
相对于自身正常文档流的定位,其占用空间不会改变,只是带有一定 偏移
-
fixed
相对于 == 浏览器窗口 == 的定位,不会跟随页面滚动而移动
-
absolute
绝对定位是相对于已定位的父元素 / 或 html 元素。
绝对定位使元素脱离文档流,不占据文档流空间,可以重叠。
-
sticky
粘性定位,使元素在 relative 和 fixed 之间切换,实现 “滚动到一定范围,元素附着在页面上” 的效果。