解决了用 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等框架的火热,我们可以很容易构建一个用户体验良好的单页面应用,但是如果我们要在浏览器改变路由的时候,在不请求服务器的情况下渲染不同的内容,就要类似于后端的路由系统,在前端也实现一套完整的路由系统
文章浏览阅读70次。传送门解题思路 第二个操作其实就是把\(T\)在\(S\)上跑一遍匹配然后输出最后那个匹配点\(right\)集合的大小。现在考虑如何动态维护,发现\(right\)集合的转移为\(siz_{fa_i}=\sum siz_i\),那么假如一个字符其实就是给\(parent\)树上这个点的每个祖先\(+1\),这样就可以\(lct\)维护。代码#include<iostream>...
文章浏览阅读401次。可以使用position进行固定样式position:stickyCss position:sticky 初探_css 粘性 +2020
文章浏览阅读4.6k次。一、课题背景概述文本挖掘是一门交叉性学科,涉及数据挖掘、机器学习、模式识别、人工智能、统计学、计算机语言学、计算机网络技术、信息学等多个领域。文本挖掘就是从大量的文档中发现隐含知识和模式的一种方法和工具,它从数据挖掘发展而来,但与传统的数据挖掘又有许多不同。文本挖掘的对象是海量、异构、分布的文档(web);文档内容是人类所使用的自然语言,缺乏计算机可理解的语义。传统数据挖掘所处理的数据是结构化_特征选择创新点
文章浏览阅读1.1k次。下面是asp.net连接远程Oracle数据库服务器步骤:1.asp.net连接oracle服务器需要添加Sytem.Data.OracleClient命名空间,将System.Data.OracleClient.dll加入到项目中。2.连接时需要ConnectionString字符串,出现在web.config文件中,如下所示: 如下所示: C_aspnet连接oracle
文章浏览阅读583次,点赞12次,收藏15次。应该是每一章一个博客,顺便在每篇博客的最后放一些PAT上坑爹的知识点类的题,估计期末就是只考这个,最讨厌这种要死记硬背的东西了,哪有算法题有意思。(模板先暂时搁置,现在主要把《算法竞赛进阶指南刷一遍》)的,没有太多的时间)。
文章浏览阅读87次。虽然大多数应用程序都可以进行双开,但对于一些涉及敏感信息或需要特殊权限的应用程序来说,开启双开可能会导致安全风险或功能异常。因此,在尝试双开功能时,用户需要谨慎考虑哪些应用程序适合进行双开。对于许多用户来说,他们可能需要同时使用多个相同的应用程序以满足不同的需求。这意味着用户可以在同一部手机上同时登录多个相同的应用程序,满足用户在工作和生活中各种需求。3:信任设置完成后,您可以尝试再次打开应用,这时就不会再有问题了。1:当您进入下载并启动它的时候,可能会遇到无法打开的问题,需要进行一些设置。
文章浏览阅读7k次。Afx前缀是微软MFC一个小组的名称简写,并没有别的意义。 MFC的很多代码,包括全局函数名、宏、头文件名都使用了"Afx"。 Afx*.h是一组MFC的核心头文件, 比如: afxwin.h 定义MFC的核心和标准组件 afxext.h 定义MFC的扩展 afxdisp.h 是MFC自动化支持的类定义头文件 afxdb.h 是MFC的ODBC类封装AFX_msg 在_函数体前 带不带afx_msg有什么区别
文章浏览阅读1.2w次,点赞30次,收藏187次。通用快捷键:快捷键作用Ctrl+Shift+P,F1展示全局命令面板Ctrl+P快速打开最近打开的文件Ctrl+Shift+N打开新的编辑窗口Ctrl+Shift+W关闭编辑器基础编辑快捷键:快捷键作用Ctrl + X剪切Ctrl + C复制Alt + up/down移动上下行Shift + Alt up/down上下复制当前行Ctrl + Shift + K删除当前行Ctrl + Enter_vscode shezhikj
文章浏览阅读3.4k次。Ubuntu 16.04 parted 对 GPT 格式硬盘 (12 TB) 分区1. sudo fdisk -lstrong@foreverstrong:~$ fdisk -lfdisk: cannot open /dev/loop1: Permission deniedfdisk: cannot open /dev/loop2: Permission deniedfdisk: cannot open /dev/loop3: Permission deniedfdisk: cannot ope_ubuntu parted
文章浏览阅读646次。if(test_form.addEventListener) { test_form.addEventListener("submit", my_onsubmit_function,false);} elseif(test_form.attachEvent) { test_form.attachEvent("onsubmit",my_onsubmit_functio_js form表达增加addeventlistener
文章浏览阅读8.7k次,点赞4次,收藏40次。一、手动卸载VS2015主体。 打开控制面板-程序-程序功能,找到VS2015,右键更改,然后卸载,等待卸载完成。二、下载TotalUninstaller工具。链接:https://pan.baidu.com/s/1nd2RV-t29gG-hBWAJO0cvA提取码:bjul三、解压下载的文件并以管理员身份运行Setup.ForcedUninstall.exe四、在弹出的命令行窗口中输入“Y”,然后回车,等待卸载完成。提示:可以重复几次这个操作,..._vs2015卸载工具
文章浏览阅读3k次,点赞7次,收藏12次。简单的说,激活的意思就是如果这个靠近的某个落脚点,就把它算成某个落脚点的值. 在训练模型的时候,机器会调整这些权重,这些权重会用某个落脚点的值来表示。总的来看,模型量化的精度损失取决于多种因素,包括所使用的量化策略、模型的特性,以及实际应用中的需求等。现在,我们把这个0到1之间的范围称为一个权重,看成一片连续的水面,上面表中的值看成一个一个的“落脚点”。依此类推,你有2的n次方种方法来表示这个范围,这里的n就是比特的位数。就是说,你仅用少量的精度损失的代价节省了大量的存储空间,是非常划算的。_大模型量化等级是什么意思