解决了用 DOM API 操作 UI 过于反人类的问题。
React 通过 UI = f(data) 解决。
Vue 通过 Reactive 响应式数据解决。
MVVM分为Model、View、ViewModel三者。
Model和View并无直接关联,而是通过ViewModel来进行联系的,Model和ViewModel之间有着双向数据绑定的联系。因此当Model中的数据改变时会触发View层的刷新,View中由于用户交互操作而改变的数据也会在Model中同步。
简单的说,ViewModel就是View与Model的连接器,View与Model通过ViewModel实现双向绑定。
组件需要注册后才可以使用,有全局注册和局部注册两种方式在实例创建前通过
<script>
Vue.component('自定义标签名称',{
//选项
});
var app = new Vue({
el:'#app'
})
</script>
来注册全局组件,不必把每个组件都注册到全局,在实例中,使用components选项可以局部注册组件,注册后的组件只有在该实例作用域下有效,组件中也可以使用components选项来注册组件,使组件可以嵌套。
<script>
var Child = {
template:'<div>局部注册组件内容</div>'
}
var app = new Vue({
el:'#app',
components:{
'my-component':Child
}
})
</script>
组件关系可分为父子组件通信、兄弟组件通信、跨级组件通信
父子组件通信:
父组件向子组件通信,通过props传递数据
子组件向父组件传递数据时,用到自定义事件,子组件用 e m i t ( ) 触 发 事 件 , 父 组 件 用 emit()触发事件,父组件用 emit()触发事件,父组件用on()监听子组件的事件,父组件也可以直接在子组件的自定义标签上使用v-on来监听
<div id="app">
{
{message}}
<my-component></my-component>
</div>
<script>
Vue.component('my-component',{
template:'<button @click="event"></button>',
methods:{
event:function() {
this.$dispatch('on-message','来自内部组件的数据');
}
}
})
var app = new Vue({
el:'#app',
data:{
message:''
},
events:{
'on-message':function(msg) {
this.message = msg;
}
}
})
</script>
非父子组件通信:
在Vue.js 1.x中,提供 d i s p a t c h ( ) 和 dispatch()和 dispatch()和broadcast()两个方法。 d i s p a t c h ( ) 用 于 向 上 级 派 发 事 件 , 只 要 是 它 的 父 级 ( 一 级 或 多 级 以 上 ) , 都 可 以 在 V u e 实 例 的 e v e n t s 选 项 内 接 收 . dispatch()用于向上级派发事件,只要是它的父级(一级或多级以上),都可以在Vue实例的events选项内接收. dispatch()用于向上级派发事件,只要是它的父级(一级或多级以上),都可以在Vue实例的events选项内接收.broadcast()由上级向下级广播事件。
但在Vue.js 2.x中都废弃了(不能解决兄弟组件通信问题)
在Vue.js 2.x中,推荐使用一个空的vue实例作为中央事件总线(bus),也就是一个中介
<div id="app">
{
{message}}
<component-a></component-a>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var bus = new Vue();
Vue.component('component-a',{
template:'<button @click="event">传递事件</button>',
methods:{
event:function() {
bus.$emit('on-message','来自组件component-a的内容')
}
}
})
var app = new Vue({
el:'#app',
data:{
message:''
},
mounted: function() {
var _this = this;
bus.$on('on-message',function(msg) {
_this.message = msg;
})
}
})
</script>
这种方法实现了任何组件间的通信,如果深入使用,可以扩展bus实例,给它添加data、computed、methods等选项,这些都是可以公用的
除了bus外,还有两种方法可以实现组件间通信,父链和子组件索引
父链
在子组件中,使用this.$parent可以直接访问该组件的父实例或组件,父组件也可以通过this.$children访问它所有的子组件
<div id="app">
{
{message}}
<component-a></component-a>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var bus = new Vue();
Vue.component('component-a',{
template:'<button @click="event">传递事件</button>',
methods:{
event:function() {
//访问到父链后,可以做任何操作,比如直接修改数据
this.$parent.message = "来自组件component-a的内容"
}
}
})
var app = new Vue({
el:'#app',
data:{
message:''
}
})
</script>
父子组件最好还是通过props和$emit来通信
子组件索引
vue的生命周期里边有八个生命周期钩子函数分别是:
先来一张官方的生命周期图镇贴
生命周期函数理解
1.1.1 如何监控一个数据
vue可以直接通过v-model这个指令来实现双向绑定,这是react和小程序都没有,小程序是单向绑定,只能将data中的对象和基本数据类型展示在视图上,却没有办法通过视图来控制data中的数据,需要通过this.setData({})给出一个对象,重新设置数据,达到视图更新。
要达到如图1-1的效果,就要对数据进行监控,只有监控了数据的变化,在数据变化之后,通知视图去自主更新,这就是双向绑定的思路。这个思路很明显涉及到“监控” “更新”两个关键词,就可以联想到观察者模式。
观察一个数据,一旦数据变化,就通知视图执行更新操作。
思路一下子就明了,数据变化还好说,就是拿出一个变量存储旧值,一旦获取到新值,新值与旧值不同时,数据就发生了变化。可问题在于,不可能随时随地对数据进行监控,每分每秒都在取得数据的值去与旧值做对比。
只有当这个数据在被使用时,我们才监控他,拿旧值与新值做对比。
这个过程叫做让数据变为可观察,是通过Object.defineProperty() 来实现。
1.1.2 如何使用Object的静态方法定义属性
Object.defineProperty(obj, prop, descriptor)
被这样定义的属性,所有的数据描述符默认为false,也就是不可删除,不可写,不可枚举
属性描述符
MDN文档上有提到
对象里目前存在的属性描述符有两种主要形式:数据描述符和存取描述符。数据描述符是一个具有值的属性,该值可能是可写的,也可能不是可写的。存取描述符是由getter-setter函数对描述的属性。描述符必须是这两种形式之一;不能同时是两者。
let obj = {
name:1
}
Object.defineProperty(obj,'school',{
configurable:true,//表示configurable可以被删除
writable:true,//为true之后,便可以修改
// enumerable:true,//修改之后才可以被枚举,在遍历时被访问到
value:'zfpx'
})
// delete obj.school;
obj.school = "修改值"
console.log(obj)
for(var key in obj){
console.log(key)
}
只有开启数据描述符为true之后,属性才可被删除,被写入,被枚举打印
getter-setter
这就是数据可监控的关键,使用Object.defineProperty(obj, prop, descriptor)定义的属性,一旦属性被使用,就会被读取,就会调用get函数,一旦属性被写入,就会调用set函数,即可以知道数据一旦发生写入,变化,就可以在set函数中通知视图更新。
由于,
存储描述符get set参数和数据描述符的writeable value存在冲突,二选其一
Object.defineProperty(obj, "school", {
configurable: true, //表示configurable可以被删除
// writable: true,
enumerable: true, //修改之后才可以被枚举,在遍历时被访问到
// value: "zfpx",
get() {
console.log("调用了get方法");
return value;
},
set(newVal) {
console.log("调用了set方法");
value = newVal;
}
});
如图 1-2
1.1.3 数据劫持
知道了get和set的妙用,就可以对数据进行劫持了。
劫持的概念
说白了,就是拿到某数据,持有这个数据,可以操作增删改,也可以不操作,重点在持有他
监听
一旦数据被传入Vue实例就需要对data整个对象实行监听,
这里需要对data中的数据类型进行判断
如果是data中的属性是基本数据类型,只需要监控就好了
如果data中的属性是对象,则需要遍历对象下的所有属性,进行监控
可又有一个疑问,data的属性是对象A,A的属性还包含对象B,B有对象C,所以不能是遍历,而是递归,递归整个对象属性树
<body>
<div id="app">
<p>姓名是{
{ name.firstName }}</p>
<div>年龄是{
{ age }}</div>
{
{ name }}
</div>
<script type="vue.js"></script>
<script type="text/javascript">
let vm = new Vue({
el: "#app",
data: {
name: {
firstName: "姓氏章",
lastName: "名字"
},
age: 12 //通过Obj.defineProperty实现()或者Obj.defineProperties()实现
}
});
</script>
</body>
数据绑定(传入{ 对象的data挂载在vue实例上)
/**
*Vue入口
*@{options} 限定为一个对象,接受这个{}对象
* */
function Vue(options = {}) {
this.$options = options; // 将所有属性挂载在vue实例$options上
var data = (this._data = this.$options.data); //将{}对象的data挂载vue实例上
observe(data);
}
/**
*观察对象变化,如果最开始传入的data是基本数据类型,已经被劫持了,不需要递归再去对属性进行监控
*@{data} 被观察的对象或属性
*/
function observe(data) {
if (typeof data !== "object") return null;
return new Observe(data);
}
class Observe {
constructor(data) {
this.start(data);
}
start(data) {
for (let key in data) {
let val = data[key];
// 如果data中包含属性是对象,则需要递归对象的中属性,进行数据劫持
// 如果data中的属性就是普通数据类型,递归退出 -- 递归出口
Object.defineProperty(data, key, {
enumrable: true,
get() {
console.log("调用get方法");
return val;
},
// 会在数据改变的时候直接设置
set(newVal) {
console.log("调用set方法");
//数据并没有改变
if (newVal === val) {
return;
}
val = newVal;
}
});
}
}
}
上述代码实现了数据劫持和监控数据的功能
接下来是
数据代理(this代理传入的{ }对象去调用data)
编译模板(读取文本节点中的字符串,抽成属性名,通过字面量的形式访问到属性值,去掉双大括号,显示到节点上)
到编译模板这一步,是实现了单向绑定,也就是data中的数据被显示在网页上,如同又通过视图,譬如input输入框,改变data的值。
方法一、props/$emit
父组件 A 通过 props 的方式向子组件 B 传递,B to A 通过在 B 组件中 $emit, A 组件中 v-on 的方式实现。
接下来我们通过一个例子,说明父组件如何向子组件传递值:在子组件 Users.vue 中如何获取父组件 App.vue 中的数据
users:["Henry","Bucky","Emily"]
//App.vue父组件
<template>
<div id="app">
<users v-bind:users="users"></users>//前者自定义名称便于子组件调用,后者要传递数据名
</div>
</template>
<script>
import Users from "./components/Users"
export default {
name: 'App',
data(){
return{
users:["Henry","Bucky","Emily"]
}
},
components:{
"users":Users
}
}
//users子组件
<template>
<div class="hello">
<ul>
<li v-for="user in users">{
{user}}</li>//遍历传递过来的值,然后呈现到页面
</ul>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props:{
users:{ //这个就是父组件中子标签自定义名字
type:Array,
required:true
}
}
}
</script>
总结:父组件通过 props 向下传递数据给子组件。注:组件中的数据共有三种形式:data、props、computed
子组件向父组件传值(通过事件形式)
接下来我们通过一个例子,说明子组件如何向父组件传递值:当我们点击“Vue.js Demo”后,子组件向父组件传递值,文字由原来的“传递的是一个值”变成“子向父组件传值”,实现子组件向父组件值的传递。
// 子组件
<template>
<header>
<h1 @click="changeTitle">{
{title}}</h1>//绑定一个点击事件
</header>
</template>
<script>
export default {
name: 'app-header',
data() {
return {
title:"Vue.js Demo"
}
},
methods:{
changeTitle() {
this.$emit("titleChanged","子向父组件传值");//自定义事件 传递值“子向父组件传值”
}
}
}
</script>
// 父组件
<template>
<div id="app">
<app-header v-on:titleChanged="updateTitle" ></app-header>//与子组件titleChanged自定义事件保持一致
// updateTitle($event)接受传递过来的文字
<h2>{
{title}}</h2>
</div>
</template>
<script>
import Header from "./components/Header"
export default {
name: 'App',
data(){
return{
title:"传递的是一个值"
}
},
methods:{
updateTitle(e){ //声明这个函数
this.title = e;
}
},
components:{
"app-header":Header,
}
}
</script>
总结:子组件通过 events 给父组件发送消息,实际上就是子组件把自己的数据发送到父组件。
vue3.0尝鲜 – 摒弃Object.defineProperty,基于 Proxy 的观察者机制探索
computed:计算属性
1、计算属性是由data中的已知值,得到的一个新值。
2、这个新值只会根据已知值的变化而变化,其他不相关的数据的变化不会影响该新值。
3、计算属性不在data中,计算属性新值的相关已知值在data中。
4、别人变化影响我自己。
watch:监听数据的变化
1、监听data中数据的变化
2、监听的数据就是data中的已知值
3、我的变化影响别人
little-demo:演示watch和computed的区别
<div id="app">
<input type="text" v-model="name" />
<span v-show="isShow">请输入3-6个字符</span>
<br />
<input type="text" v-model="todoName" />
</div>
<script src="./vue.js"></script>
<script>
const vm = new Vue({
el: "#app",
data: {
name: "zs",
todoName: "ls"
},
computed: {
isShow() {
//当this.name的长度小于3或者大于6时显示提示内容(我根据name的变化而变化)
if (this.name.length >= 3 && this.name.length <= 6) {
return false;
} else {
return true;
}
}
},
watch: {
//监听data中的name,如果发生了变化,就把变化的值给data中的todoName(我影响了别人)
name(newVal) {
this.todoName = newVal;
}
}
});
</script>
–
从Vue-router到html5的pushState
最近在用vue的时候突然想到一个问题
首先,我们知道vue实现的单页应用中一般不会去刷新页面,因为刷新之后页面中的vuex数据就不见了。
其次,我们也知道一般情况下,url变更的时候,比如指定location.href、history.push、replace等,页面就会刷新。
那么问题来了,vue页面的页面跳转时怎么实现的?没刷新页面么?没刷新页面,又要改变url,加载新内容怎么做的?
去翻了一下vue-router的源码,找到这样一段
export class HTML5History extends History {
...
push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
const { current: fromRoute } = this
this.transitionTo(location, route => {
pushState(cleanPath(this.base + route.fullPath))
handleScroll(this.router, route, fromRoute, false)
onComplete && onComplete(route)
}, onAbort)
}
replace (location: RawLocation, onComplete?: Function, onAbort?: Function) {
const { current: fromRoute } = this
this.transitionTo(location, route => {
replaceState(cleanPath(this.base + route.fullPath))
handleScroll(this.router, route, fromRoute, false)
onComplete && onComplete(route)
}, onAbort)
}
...
}
再看看方法内部
export function pushState (url?: string, replace?: boolean) {
saveScrollPosition()
// try...catch the pushState call to get around Safari
// DOM Exception 18 where it limits to 100 pushState calls
const history = window.history
try {
if (replace) {
history.replaceState({ key: _key }, '', url)
} else {
_key = genKey()
history.pushState({ key: _key }, '', url)
}
} catch (e) {
window.location[replace ? 'replace' : 'assign'](url)
}
}
答案就是html5在history中新增加的方法:pushState和replaceState。这两个又是干啥的呢?(两个十分类似,以下以pushState为例说明,区别和push与replace一致)
HTML5的pushState()
首先看看这个是干什么的
window.location.href = window.location.href + '#a=b
知道干嘛的了,再看看API怎么用的
history.pushState(state, title, url);
popstate中的
Uncaught DOMException: Failed to execute 'pushState' on 'History': A history state object with URL 'https://www.baidu.com/' cannot be created in a document with origin 'https://mocard-aliyun1.chooseway.com:8443' and URL 'https://mocard-aliyun1.chooseway.com:8443/views/h5/indexasdasd'.
at History.pushState (https://aixuedaiimg.oss-cn-hangzhou.aliyuncs.com/static/m/js/alog/v1.0.0/alog.min.js:1:23259)
at <anonymous>:1:9
HTML5的popstate()
window.addEventListener('popstate', e => {
console.log('popstate', )
})
前面pushState中传入的state对象,可以在这边接收到,并根据需要去做一些处理。
说到这,vue-router是怎么实现页面“刷新”但不刷新的就知道了吧。
vue-router就是利用pushState这个属性,在页面前进的时候动态改变history的内容,添加一条记录,接着location跟着改变。同时根据router前往的路由获取对应的js资源文件并挂载到目标dom上实现页面内容的更新,但是页面本身并没有刷新。
单页面路由即在前端单页面实现的一种路由,由于React,Vue等框架的火热,我们可以很容易构建一个用户体验良好的单页面应用,但是如果我们要在浏览器改变路由的时候,在不请求服务器的情况下渲染不同的内容,就要类似于后端的路由系统,在前端也实现一套完整的路由系统
文章浏览阅读645次。这个肯定是末尾的IDAT了,因为IDAT必须要满了才会开始一下个IDAT,这个明显就是末尾的IDAT了。,对应下面的create_head()代码。,对应下面的create_tail()代码。不要考虑爆破,我已经试了一下,太多情况了。题目来源:UNCTF。_攻防世界困难模式攻略图文
文章浏览阅读2.9k次,点赞3次,收藏10次。偶尔会用到,记录、分享。1. 数据库导出1.1 切换到dmdba用户su - dmdba1.2 进入达梦数据库安装路径的bin目录,执行导库操作 导出语句:./dexp cwy_init/[email protected]:5236 file=cwy_init.dmp log=cwy_init_exp.log 注释: cwy_init/init_123..._达梦数据库导入导出
文章浏览阅读1.9k次。1. 在官网上下载KindEditor文件,可以删掉不需要要到的jsp,asp,asp.net和php文件夹。接着把文件夹放到项目文件目录下。2. 修改html文件,在页面引入js文件:<script type="text/javascript" src="./kindeditor/kindeditor-all.js"></script><script type="text/javascript" src="./kindeditor/lang/zh-CN.js"_kindeditor.js
文章浏览阅读2.3k次,点赞6次,收藏14次。SPI的详情简介不必赘述。假设我们通过SPI发送0xAA,我们的数据线就会变为10101010,通过修改不同的内容,即可修改SPI中0和1的持续时间。比如0xF0即为前半周期为高电平,后半周期为低电平的状态。在SPI的通信模式中,CPHA配置会影响该实验,下图展示了不同采样位置的SPI时序图[1]。CPOL = 0,CPHA = 1:CLK空闲状态 = 低电平,数据在下降沿采样,并在上升沿移出CPOL = 0,CPHA = 0:CLK空闲状态 = 低电平,数据在上升沿采样,并在下降沿移出。_stm32g431cbu6
文章浏览阅读1.2k次,点赞2次,收藏8次。数据链路层习题自测问题1.数据链路(即逻辑链路)与链路(即物理链路)有何区别?“电路接通了”与”数据链路接通了”的区别何在?2.数据链路层中的链路控制包括哪些功能?试讨论数据链路层做成可靠的链路层有哪些优点和缺点。3.网络适配器的作用是什么?网络适配器工作在哪一层?4.数据链路层的三个基本问题(帧定界、透明传输和差错检测)为什么都必须加以解决?5.如果在数据链路层不进行帧定界,会发生什么问题?6.PPP协议的主要特点是什么?为什么PPP不使用帧的编号?PPP适用于什么情况?为什么PPP协议不_接收方收到链路层数据后,使用crc检验后,余数为0,说明链路层的传输时可靠传输
文章浏览阅读587次。软件测试工程师移民加拿大 无证移民,未受过软件工程师的教育(第1部分) (Undocumented Immigrant With No Education to Software Engineer(Part 1))Before I start, I want you to please bear with me on the way I write, I have very little gen...
文章浏览阅读304次。Thinkpad X250笔记本电脑,装的是FreeBSD,进入BIOS修改虚拟化配置(其后可能是误设置了安全开机),保存退出后系统无法启动,显示:secure boot failed ,把自己惊出一身冷汗,因为这台笔记本刚好还没开始做备份.....根据错误提示,到bios里面去找相关配置,在Security里面找到了Secure Boot选项,发现果然被设置为Enabled,将其修改为Disabled ,再开机,终于正常启动了。_安装完系统提示secureboot failure
文章浏览阅读10w+次,点赞93次,收藏352次。1、用strtok函数进行字符串分割原型: char *strtok(char *str, const char *delim);功能:分解字符串为一组字符串。参数说明:str为要分解的字符串,delim为分隔符字符串。返回值:从str开头开始的一个个被分割的串。当没有被分割的串时则返回NULL。其它:strtok函数线程不安全,可以使用strtok_r替代。示例://借助strtok实现split#include <string.h>#include <stdio.h&_c++ 字符串分割
文章浏览阅读2.3k次。1 .高斯日记 大数学家高斯有个好习惯:无论如何都要记日记。他的日记有个与众不同的地方,他从不注明年月日,而是用一个整数代替,比如:4210后来人们知道,那个整数就是日期,它表示那一天是高斯出生后的第几天。这或许也是个好习惯,它时时刻刻提醒着主人:日子又过去一天,还有多少时光可以用于浪费呢?高斯出生于:1777年4月30日。在高斯发现的一个重要定理的日记_2013年第四届c a组蓝桥杯省赛真题解答
文章浏览阅读851次,点赞17次,收藏22次。摘要:本文利用供需算法对核极限学习机(KELM)进行优化,并用于分类。
文章浏览阅读1.1k次。一、系统弱密码登录1、在kali上执行命令行telnet 192.168.26.1292、Login和password都输入msfadmin3、登录成功,进入系统4、测试如下:二、MySQL弱密码登录:1、在kali上执行mysql –h 192.168.26.129 –u root2、登录成功,进入MySQL系统3、测试效果:三、PostgreSQL弱密码登录1、在Kali上执行psql -h 192.168.26.129 –U post..._metasploitable2怎么进入
文章浏览阅读257次。本文将为初学者提供Python学习的详细指南,从Python的历史、基础语法和数据类型到面向对象编程、模块和库的使用。通过本文,您将能够掌握Python编程的核心概念,为今后的编程学习和实践打下坚实基础。_python人工智能开发从入门到精通pdf