4183972ec14785244332461fc63e692b.png

作者 | 爱编程的小和尚

责编 | 王晓曼

出品 | CSDN博客

学过 VUE 如果不了解响应式的原理,怎么能说自己熟练使用 VUE,要是没有写过一个简易版的 VUE 怎么能说自己精通 VUE,这篇文章通过300多行代码,带你写一个简易版的 VUE,主要实现 VUE 数据响应式 (数据劫持结合发布者-订阅者)、数组的变异方法、编译指令,数据的双向绑定的功能。

本文需要有一定 VUE 基础,并不适合新手学习。

文章较长,且有些难度,建议大家,找一个安静的环境,并在看之前沐浴更衣,保持编程的神圣感。下面是实现的简易版VUE 的源码地址,一定要先下载下来!因为文章中的并非全部的代码。

Github源码地址:https://github.com/young-monk/myVUE.git

57175186103749a5ff8bff994f8bb52b.png

前言

在开始学习之前,我们先来了解一下什么是 MVVM ,什么是数据响应式。

我们都知道 VUE 是一个典型的 MVVM 思想,由数据驱动视图。

那么什么是 MVVM 思想呢?

MVVM是Model-View-ViewModel,是把一个系统分为了模型( model )、视图( view )和 view-model 三个部分。

VUE在 MVVM 思想下,view 和model 之间没有直接的联系,但是 view 和 view-model 、model和 view-model之间时交互的,当 view 视图进行 dom 操作等使数据发生变化时,可以通过 view-model 同步到 model 中,同样的 model 数据变化也会同步到 view 中。

那么实现数据响应式都有什么方法呢?

1、发布者-订阅者模式:当一个对象(发布者)状态发生改变时,所有依赖它的对象(订阅者)都会得到通知。通俗点来讲,发布者就相当于报纸,而订阅者相当于读报纸的人。

2、脏值检查:通过存储旧的数据,和当前新的数据进行对比,观察是否有变更,来决定是否更新视图。angular.js 就是通过脏值检查的方式。最简单的实现方式就是通过 setInterval() 定时轮询检测数据变动,但这样无疑会增加性能,所以, angular 只有在指定的事件触发时进入脏值检测。

3、数据劫持:通过 Object.defineProperty() 来劫持各个属性的 setter,getter,在数据变动时触发相应的方法。

VUE是如何实现数据响应式的呢?

VUE.js 则是通过数据劫持结合发布者-订阅者模式的方式。

当执行 new VUE() 时,VUE 就进入了初始化阶段,VUE会对指令进行解析(初始化视图,增加订阅者,绑定更新函数),同时通过 Obserber会遍历数据并通过 Object.defineProperty 的 getter 和 setter 实现对的监听, 当数据发生变化的时候,Observer 中的 setter 方法被触发,setter 会立即调用Dep.notify(), Dep 开始遍历所有的订阅者,并调用订阅者的 update 方法,订阅者收到通知后对视图进行相应的更新。

我来依次介绍一下图中的重要的名词

1、Observer:数据监听器,能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者,内部采用 Object.defineProperty 的 getter 和 setter 来实现

2、Compile:指令解析器,它的作用对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数

3、Dep:订阅者收集器或者叫消息订阅器都可以,它在内部维护了一个数组,用来收集订阅者,当数据改变触发 notify 函数,再调用订阅者的 update 方法

4、Watcher:订阅者,它是连接 Observer 和 Compile 的桥梁,收到消息订阅器的通知,更新视图

5、Updater:视图更新

所以我们想要实现一个 VUE 响应式,需要完成数据劫持、依赖收集、 发布者订阅者模式。

下面我来介绍我模仿源码实现的功能:

1、数据的响应式、双向绑定,能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者

2、解析 VUE 常用的指令 v-html,v-text,v-bind,v-on,v-model,包括( @ 和 : )

3、数组变异方法的处理

4、在 VUE 中使用 this 访问或改变 data 中的数据

我们想要完成以上的功能,需要实现如下类和方法:

1、实现 Observe r类:对所有的数据进行监听

2、实现 array 工具方法:对变异方法的处理

3、实现 Dep 类:维护订阅者

4、实现 Watcher 类:接收 Dep 的更新通知,用于更新视图

5、实现 Compile 类:用于对指令进行解析

6、实现一个 CompileUtils 工具方法,实现通过指令更新视图、绑定更新函数Watcher

7、实现 this.data 代理:实现对 this. data 代理:实现对 this.data 代理:实现对 this.data 代理,可以直接在 VUE 中使用 this 获取当前数据

我是使用了webpack作为构建工具来协同开发的,所以在我实现的VUE响应式中会用到ES6模块化,webpack的相关知识。

2dde0148a307515606852f2450836830.png

实现 Observer 类

我们都知道要用 Obeject.defineProperty() 来监听属性的数据变化,我们需要对 Observer 的数据对象进行递归遍历,包括子属性对象的属性,都加上 setter 和 getter ,这样的话,当给这个对象的某个值赋值,就会触发 setter,那么就能监听到了数据变化。当然我们在新增加数据的时候,也要对新的数据对象进行递归遍历,加上 setter 和 getter 。

但我们要注意数组,在处理数组时并不是把数组中的每一个元素都加上 setter 和 getter ,我们试想一下,一个从后端返回的数组数据是非常庞大的,如果为每个属性都加上 setter 和 getter ,性能消耗是十分巨大的。我们想要得到的效果和所消耗的性能不成正比,所以在数组方面,我们通过对数组的7 个变异方法来实现数据的响应式。只有通过数组变异方法来修改和删除数组时才会重新渲染页面。

那么监听到变化之后是如何通知订阅者来更新视图的呢?我们需要实现一个Dep(消息订阅器),其中有一个 notify() 方法,是通知订阅者数据发生了变化,再让订阅者来更新视图。

我们怎么添加订阅者呢?我们可以通过 new Dep(),通过 Dep 中的addSaubs() 方法来添加订阅者。我们来看一下具体代码。

我们首先需要声明一个 Observer 类,在创建类的时候,我们需要创建一个消息订阅器,判断一下是否是数组,如果是数组,我们便改造数组,如果是对象,我们便需要为对象的每一个属性都加入 setter 和 getter 。

import { arrayMethods } from './array' //数组变异方法处理 class Observer {  constructor(data) {    //用于对数组进行处理,存放数组的观察者watcher    this.dep = new Dep()    if (Array.isArray(data)) {      //如果是数组,使用数组的变异方法      data.__proto__ = arrayMethods      //把数组数据添加 __ob__ 一个Observer,当使用数组变异方法时,可以更新视图      data.__ob__ = this      //给数组的每一项添加数据劫持(setter/getter处理)      this.observerArray(data)    } else {      //非数组数据添加数据劫持(setter/getter处理)      this.walk(data)    }  }}

在上面,我们给 data 的__proto__原型链重新赋值,我们来看一下 arrayMethods 是什么,arrayMethods 是 array.js 文件中,抛出的一个新的 Array 原型:

// 获取Array的原型链const arrayProto = Array.prototype;// 重新创建一个含有对应原型的对象,在下面称为新Arrayconst arrayMethods = Object.create(arrayProto);// 处理7个数组变异方法['push', 'pop', 'shift', 'unshift', 'reverse', 'sort', 'splice'].forEach(ele => {    //修改新Array的对应的方法    arrayMethods[ele] = function () {        // 执行数组的原生方法,完成其需要完成的内容        arrayProto[ele].call(this, ...arguments)        // 获取Observer对象        const ob = this.__ob__        // 更新视图        ob.dep.notify()    }})export {    arrayMethods}

此时呢,我们就拥有了数组的变异方法,我们还需要通过 observerArray 方法为数组的每一项添加 getter 和setter ,注意,此时的每一项只是最外面的一层,并非递归遍历。

//循环遍历数组,为数组每一项设置setter/getterobserverArray(items) {    for (let i = 0; i 

如果是一个对象的话,我们就要对对象 的每一个属性递归遍历,通过 walk() 方法:

walk(data) {    //数据劫持    if (data && typeof data === "object") {      for (const key in data) {        //绑定setter和getter        this.defineReactive(data, key, data[key])      }    }}

在上面的调用了 defineReactive() ,我们来看看这个方法是干什么的?这个方法就是设置数据劫持的,每一行都有注释。

//数据劫持,设置 setter/getteer  defineReactive(data, key, value) {    //如果是数组的话,需要接受返回的Observer对象    let arrayOb = this.observer(value)    //创建订阅者/收集依赖    const dep = new Dep()    //setter和getter处理    Object.defineProperty(data, key, {      //可枚举的      enumerable: true,      //可修改的      configurable: false,      get() {        //当 Dep 有 watcher 时, 添加 watcher        Dep.target && dep.addSubs(Dep.target)        //如果是数组,则添加上数组的观察者        Dep.target && arrayOb && arrayOb.dep.addSubs(Dep.target)        return value      },      set: (newVal) => {        //新旧数据不相等时更改        if (value !== newVal) {          //为新设置的数据添加setter/getter          arrayOb = this.observer(newVal);          value = newVal          //通知 dep 数据发送了变化          dep.notify()        }      }    })  }}

我们需要注意的是,在上面的图解中,在 Observer 中,如果数据发生变化,会通知消息订阅器,那么在何时绑定消息订阅器呢?就是在设置 setter 和 getter 的时候,创建一个 Dep,并为 Dep添加订阅者,Dep.target&& dep.addSubs(Dep.target),通过调用 dep 的 addSubs 方法添加订阅者。

b3e64cf4dd9719f096ea2cb79248ab84.png

实现 Dep

Dep 是消息订阅器,它的作用就是维护一个订阅者数组,当数据发送变化是,通知对应的订阅者,Dep中有一个 notify() 方法,作用就是通知订阅者,数据发送了变化:

// 订阅者收集器export default class Dep {    constructor() {        //管理的watcher的数组        this.subs = []    }    addSubs(watcher) {        //添加watcher        this.subs.push(watcher)    }    notify() {        //通知watcher更新dom        this.subs.forEach(w => w.update())    }}
d4cf04ef298edd4d700f2751a660118e.png

实现 watcher

Watcher 就是订阅者, watcher 是 Observer 和 Compile 之间通信的桥梁,当数据改变时,接收到 Dep 的通知(Dep 的notify()方法),来调用自己的update()方法,触发 Compile 中绑定的回调,达到更新视图的目的。

import Dep from './dep'import { complieUtils } from './utils'export default class Watcher {    constructor(vm, expr, cb) {        //当前的vue实例        this.vm = vm;        //表达式        this.expr = expr;        //回调函数,更新dom        this.cb = cb        //获取旧的数据,此时获取旧值的时候,Dep.target会绑定上当前的this        this.oldVal = this.getOldVal()    }    getOldVal() {        //将当前的watcher绑定起来        Dep.target = this        //获取旧数据        const oldVal = complieUtils.getValue(this.expr, this.vm)        //绑定完成后,将绑定的置空,防止多次绑定        Dep.target = null        return oldVal    }    update() {        //更新函数        const newVal = complieUtils.getValue(this.expr, this.vm)        if (newVal !== this.oldVal || Array.isArray(newVal)) {            //条用更新在compile中创建watcher时传入的回调函数            this.cb(newVal)        }    }}

上面中用到了 ComplieUtils 中的 getValue() 方法,会在下面讲,主要作用是获取到指定表达式的值。

我们把整个流程分成两条路线的话:

newVUE() ==> Observer数据劫持 ==> 绑定Dep ==> 通知watcher ==> 更新视图

newVUE() ==> Compile解析模板指令 ==> 初始化视图 和 绑定watcher

此时,我们第一条线的内容已经实现了,我们再来实现一下第二条线。

62be78e42b5ef60f5eae3ee5cf33df87.png

实现 Compile

Compile 主要做的事情是解析模板指令,将模板中的变量替换成数据,初始化渲染页面视图。同时也要绑定更新函数,添加订阅者。

因为在解析的过程中,会多次的操作 dom,为提高性能和效率,会先将 VUE 实例根节点的 el 转换成文档碎片 fragment 进行解析编译操作,解析完成,再将 fragment 添加回原来的真实 dom 节点中。

class Complie {    constructor(el, vm) {        this.el = this.isNodeElement(el) ? el : document.querySelector(el);        this.vm = vm;        // 1、将所有的dom对象放到fragement文档碎片中,防止重复操作dom,消耗性能        const fragments = this.nodeTofragments(this.el)        // 2、编译模板        this.complie(fragments)        // 3、追加子元素到根元素        this.el.appendChild(fragments)    }  }

我们可以看到,Complie 中主要进行了三步,第一步 nodeTofragments 是讲所有的 dom 节点放到文档碎片中操作,最后一步,是把解析好的 dom 元素,从文档碎片重新加入到页面中,这两步的具体方法,大家去下载我的源码,看一下就明白了,有注释。我就不再解释了。

我们来看一下第二步,编译模板:

 complie(fragments) {    //获取所有节点    const nodes = fragments.childNodes;    [...nodes].forEach(ele => {        if (this.isNodeElement(ele)) {            //1. 编译元素节点            this.complieElement(ele)        } else {            //编译文本节点            this.complieText(ele)        }        //如果有子节点,循环遍历,编译指令        if (ele.childNodes && ele.childNodes.length) {            this.complie(ele)        }    })}

我们要知道,模板可能有两种情况,一种是文本节点(含有双大括号的插值表达式)和元素节点(含有指令)。我们获取所有节点后对每个节点进行判断,如果是元素节点,则用解析元素节点的方法,如果是文本节点,则调用解析文本的方法。

complieElement(node) {    //1.获取所有的属性    const attrs = node.attributes;    //2.筛选出是属性的    [...attrs].forEach(attr => {        //attr是一个对象,name是属性名,value是属性值        const {name,value} = attr        //判断是否含有v-开头 如:v-html        if (name.startsWith("v-")) {            //将指令分离  text, html, on:click            const [, directive] = name.split("-")            //处理on:click或bind:name的情况 on,click            const [dirName, paramName] = directive.split(":")             //编译模板            complieUtils[dirName](node, value, this.vm, paramName)            //删除属性,在页面中的dom中不会再显示v-html这种指令的属性            node.removeAttribute(name)        } else if (name.startsWith("@")) {            // 如果是事件处理 @click='handleClick'            let [, paramName] = name.split('@');            complieUtils['on'](node, value, this.vm, paramName);            node.removeAttribute(name);        } else if (name.startsWith(":")) {            // 如果是事件处理 :href='...'            let [, paramName] = name.split(':');            complieUtils['bind'](node, value, this.vm, paramName);            node.removeAttribute(name);        }    })}

我们在编译模板中调用了 complieUtils[dirName](node, value, this.vm, paramName)方法,这是工具类中的一个方法,用于处理指令。

我们再来看看文本节点,文本节点就相对比较简单,只需要匹配{{}}形式的插值表达式就可以了,同样的调用工具方法,来解析。

complieText(node) {    //1.获取所有的文本内容    const text = node.textContent    //匹配{{}}    if (/{{(.+?)}}/.test(text)) {        //编译模板        complieUtils['text'](node, text, this.vm)    }}

上面用来这么多工具方法,我们来看看到底是什么。

0969605bd36022f4c1a8b69c8ff410d4.png

实现 ComplieUtils 工具方法

这个方法主要是对指令进行处理,获取指令中的值,并在页面中更新相应的值,同时我们在这里要绑定 watcher 的回调函数。

我来以 v-text 指令来解释,其他指令都有注释,大家自己看。

import Watcher from './watcher'export const complieUtils = {    //处理text指令    text(node, expr, vm) {        let value;        if (/{{.+?}}/.test(expr)) {            //处理 {{}}            value = expr.replace(/{{(.+?)}}/g, (...args) => {                //绑定观察者/更新函数                new Watcher(vm, args[1], () => {                    //第二个参数,传入回调函数                    this.updater.updaterText(node, this.getContentVal(expr, vm))                })                return this.getValue(args[1], vm)            })        } else {            //v-text            new Watcher(vm, expr, (newVal) => {                this.updater.updaterText(node, newVal)            })            //获取到value值            value = this.getValue(expr, vm)        }        //调用更新函数        this.updater.updaterText(node, value)    },}

Text 处理函数是对 dom 元素的 TextContent 进行操作的,所以有两种情况,一种是使用 v-text 指令,会更新元素的 textContent,另一种情况是{{}} 的插值表达式,也是更新元素的 textContent。

在此方法中我们先判断是哪一种情况,如果是 v-text 指令,那么就绑定一个 watcher 的回调,获取到 textContent 的值,调用 updater.updaterText 在下面讲,是更新元素的方法。如果是双大括号的话,我们就要对其进行特殊处理,首先是将双大括号替换成指定的变量的值,同时为其绑定 watcher 的回调。

//通过表达式, vm获取data中的值, person.namegetValue(expr, vm) {    return expr.split(".").reduce((data, currentVal) => {        return data[currentVal]    }, vm.$data)},

获取 textContent 的值是用一个 reduce 函数,用法在最后面的链接中,因为数据可能是 person.name 我们需要获取到最深的对象的值。

 //更新dom元素的方法updater: {    //更新文本    updaterText(node, value) {        node.textContent = value    }}

updater.updaterText更新dom的方法,其实就是对 textContent 重新赋值。

我们再来将一下v-model指令,实现双向的数据绑定,我们都知道,v-model其实实现的是 input 事件和 value 之间的语法糖。所以我们这里同样的监听一下当前 dom 元素的 input 事件,当数据改变时,调用设置新值的方法:

//处理model指令model(node, expr, vm) {    const value = this.getValue(expr, vm)    //绑定watcher    new Watcher(vm, expr, (newVal) => {        this.updater.updaterModel(node, newVal)    })    //双向数据绑定    node.addEventListener("input", (e) => {        //设值方法        this.setVal(expr, vm, e.target.value)    })    this.updater.updaterModel(node, value)},

这个方法同样是通过 reduce 方法,为对应的变量设置成新的值,此时数据改变了,会自动调用更新视图的方法,我们在之前已经实现了。

//通过表达式,vm,输入框的值,实现设置值,input中v-model双向数据绑定setVal(expr, vm, inputVal) {    expr.split(".").reduce((data, currentVal) => {        data[currentVal] = inputVal    }, vm.$data)},
5d150fe6851b8cfac10e29517135a2ca.png

实现VUE

最后呢,我们就要来整合这些类和工具方法,在创建一个 VUE 实例的时候,我们先获取 options 中的参数,然后对起进行数据劫持和编译模板:

class Vue {    constructor(options) {        //获取模板        this.$el = options.el;        //获取data中的数据        this.$data = options.data;        //将对象中的属性存起来,以便后续使用        this.$options = options        //1.数据劫持,设置setter/getter        new Observer(this.$data)        //2.编译模板,解析指令        new Complie(this.$el, this)    }}

此时我们想要使用 VUE 中的数据,比如我们想要在 vm 对象中使用person.name, 必须用 this.$data.person.name 才能获取到,如果我们想在 vm 对象中使用 this.person.name 直接修改数据,就需要代理一下 this.$data 。其实就是将当前的 this.$data 中的数据放到全局中进行监听。

export default class Vue {    constructor(options) {        //...        //1.数据劫持,设置setter/getter        //2.编译模板,解析指令        if (this.$el) { //如果有模板            //代理this            this.proxyData(this.$data)        }    }    proxyData(data) {        for (const key in data) {            //将当前的数据放到全局指向中            Object.defineProperty(this, key, {                get() {                    return data[key];                },                set(newVal) {                    data[key] = newVal                }            })        }    }}

文章到了这里,就实现了一个简易版的 VUE,建议大家反复学习,仔细体验,细细品味。

在文章的最后,我通过问、答的形式,来解答一些常见的面试题:

问:什么时候页面会重新渲染?

答:数据发生改变,页面就会重新渲染,但数据驱动视图,数据必须先存在,然后才能实现数据绑定,改变数据,页面才会重新渲染。

问:什么时候页面不会重新渲染?

答:有3种情况不会重新渲染:

1、未经声明和未使用的变量,修改他们,都不会重新渲染页面

2、通过索引的方式和更改长度的方式更改数组,都不会重新渲染页面

3、增加和删除对象的属性,不会重新渲染页面

问:如何使 未声明/未使用的变量、增加/删除对象属性可以使页面重新渲染?

答:添加利用 vm.$set/VUE.set,删除利用vm.$delete/VUE.delete方法

问:如何更改数组可以使页面重新渲染?

答:可以使用数组的变异方法(共 7 个):push、pop、unshift、shift、splice、sort、reverse

问:数据更新后,页面会立刻重新渲染么?

答:更改数据后,页面不会立刻重新渲染,页面渲染的操作是异步执行的,执行完同步任务后,才会执行异步的

同步队列,异步队列(宏任务、微任务)

问:如果更改了数据,想要在页面重新渲染后再做操作,怎么办?

答:可以使用 vm.$nextTick 或 VUE.nextTick

问:来介绍一下vm.$nextTick 和 VUE.nextTick 吧。

答:我们来看个小例子就明白啦:

{{ name }}

问:vm.$nextTick 和 VUE.nextTick 有什么区别呢 ?

答:VUE.nextTick 内部函数的 this 指向 Window,vm.$nextTick 内部函数的 this 指向 VUE 实例对象。

Vue.nextTick(function () {    console.log(this); // window})vm.$nextTick(function () {    console.log(this); // vm实例})

问:vm.$nextTick 和 VUE.nextTick 是通过什么实现的呢?

答:二者都是等页面渲染后执行的任务,都是使用微任务。

  if(typeof Promise !== 'undefined') {    // 微任务    // 首先看一下浏览器中有没有promise    // 因为IE浏览器中不能执行Promise    const p = Promise.resolve();  } else if(typeof MutationObserver !== 'undefined') {    // 微任务    // 突变观察    // 监听文档中文字的变化,如果文字有变化,就会执行回调    // vue的具体做法是:创建一个假节点,然后让这个假节点稍微改动一下,就会执行对应的函数  } else if(typeof setImmediate !== 'undefined') {    // 宏任务    // 只在IE下有  } else {    // 宏任务    // 如果上面都不能执行,那么则会调用setTimeout  }

同样的这也是 VUE 的一个小缺点:VUE 一直是等主线程执行完以后再执行渲染任务,如果主线程卡死,则永远渲染不出来。

问:利用 Object.defineProperty 实现响应式有什么缺点?

答:

1、天生就需要进行递归

2、监听不到数组不存在的索引的改变

3、监听不到数组长度的改变

4、监听不到对象的增删

版权声明:本文为CSDN博主「爱编程的小和尚」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/Newbie___/article/details/105973085

【END】

e4a87c9f06011c8f9e7ed36eda7f81da.png
查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. 长沙双眼皮爱思特榜首

    ...

    2024/5/2 13:38:23
  2. 二维叉乘求三角形面积_角形

    二维叉乘求三角形面积Today we will look into Angular Form elements. We are all familiar with HTML forms and its usage. Therefore, I’m not going to discuss about the input controls in detail. In this post, we are going to discuss about forms in the AngularJ…...

    2024/5/3 18:13:24
  3. 数据定义与引用逻辑

    一、数据定义与引用逻辑 定义数组: 一维数组: var arr new Array(William,aWilliam,abWilliam);//数组初始化 二维数组: var arr[{id:"001",name:"上海"},{id:"002",name:"北京"}]; 删除数组值:arr.splice(0,2);//从0项开始,删除后…...

    2024/4/21 5:49:17
  4. 可观察对象在input中的使用

    使用场景: 根据input输入内容,实时查询api接口。 思路流程: 通常对于输入内容做以下一系列操作: 1.监听input输入值; 2.去除输入值两端空格键,判断是否满足最小输入长度; 3.debounce.即不是…...

    2024/4/26 14:19:08
  5. 细说数组常用遍历的方法_@jie

    前言 本文主要介绍数组常见遍历方法:forEach、map、filter、find、every、some、reduce,它们有个共同点:不会改变原始数组。 一、forEach:遍历数组 var colors ["red","blue","green"]; // ES5遍…...

    2024/4/21 5:49:15
  6. 广尾形弯月形张家口四医院双眼皮恢复图

    ...

    2024/4/21 5:49:13
  7. 前端讲义56_AngularJS的数组(含二维演示)

    一维数组与二维数组的&#xff0c;代码案例 <!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>AngularJS 数组 </title> <script src"angular/angular-1.6.2.min.js"></script> </head&g…...

    2024/4/21 5:49:13
  8. ajax传递数组到后台,js传递数组到后台

    用angular做商品搜索功能&#xff0c;后台接口那边过滤条件必须传递数组过去&#xff0c;结果比较尴尬的事情发生了&#xff0c;每次传递数组过去&#xff0c;查看请求发送的数据&#xff0c;自动被转成了对象&#xff0c;很头疼。 数据传递前 传递后我查看了请求的数据 很尴尬…...

    2024/4/21 5:49:12
  9. Angular 表单嵌套、动态表单

    说明: 组件使用了ng-zorro (https://ng.ant.design/docs/introduce/zh) 第一类&#xff1a;嵌套表单 1. 静态表单嵌套 demo.component.html <form [formGroup]"formGroup"><div><label>名称: </label><input type"text" formCo…...

    2024/4/21 5:49:10
  10. 猪蹄 切宋慧乔平行双眼皮常见形态

    ...

    2024/4/21 5:49:09
  11. 割双眼皮能吃酱油么

    ...

    2024/4/21 5:49:08
  12. 割完双眼皮能吃红烧猪蹄吗

    ...

    2024/4/20 19:17:08
  13. 重庆华美双眼皮做得好

    ...

    2024/4/20 19:17:08
  14. 你们介意女生割双眼皮吗

    ...

    2024/4/20 19:17:06
  15. AngularJS中使用$resource(已更新)

    这个服务可以创建一个资源对象&#xff0c;我们可以用它非常方便地同支持RESTful的服务端数据源进行交互&#xff0c;当同支持RESTful的数据模型一起工作时&#xff0c;它就派上用场了。 REST是Representational State Transfer&#xff08;表征状态转移&#xff09;的缩写…...

    2024/5/3 14:38:05
  16. Angular Cookie 读写

    var app angular.module(Mywind,[ui.router]) app.controller(Myautumn,function($scope,$http,$filter){ //angular Cookie写 //用法 &#xff1a;$scioe.addCookie("姓名",值,时间,"/") $scope.addCookie function(name, value, days, path) {   va…...

    2024/4/20 19:17:04
  17. angular中的$http服务,对应的参数

    method:http请求方式&#xff0c;可以为GET,DELETE,HEAD,JSONP,POST,PUT url:字符串&#xff0c;请求的url地址 params:字符串或者对象&#xff0c;会被转换成为查询字符串追加的url后面 data:在发送post请求时使用&#xff0c;作为消息体发送到服务器 headers:一个列表&#x…...

    2024/4/20 19:17:03
  18. 双眼皮修复术疗法艺星

    ...

    2024/4/20 19:17:06
  19. Angular 4 Cookie vs Token 认证

    相关链接&#xff1a;Angular 4 Authentication With Auth0Serverless 架构应用开发&#xff1a;基于 Auth0 授权的 Serverless 应用登录原文链接:Angular 4 Cookie vs Token Authentication基于Cookie的认证 - 基于cookie的认证是默认的&#xff0c;且是有状态的(stateful)。…...

    2024/4/20 19:17:03
  20. 博爱苏州医院做双眼皮多钱

    ...

    2024/4/21 5:49:07

最新文章

  1. postman一直转圈圈,无法启动

    解决 地址栏输入%appdata%进入此目录&#xff0c;删除%appdata%目录下的postman文件可以解决问题。...

    2024/5/3 22:03:01
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/3/20 10:50:27
  3. 最新在线工具箱网站系统源码

    内容目录 一、详细介绍二、效果展示1.部分代码2.效果图展示 三、学习资料下载 一、详细介绍 系统内置高达72种站长工具、开发工具、娱乐工具等功能。此系统支持本地调用API&#xff0c;同时还自带免费API接口&#xff0c; 是一个多功能性工具程序&#xff0c;支持后台管理、上…...

    2024/5/3 2:32:18
  4. Java-运算符

    运算符 Java语言支持如下运算符&#xff1a; 算术运算符&#xff1a;&#xff0c;-&#xff0c;*&#xff0c;/&#xff0c;%&#xff0c;&#xff0c;--复制运算符&#xff1a;关系运算符&#xff1a;>, <, >, <, , !instanceof逻辑运算符&#xff1a;&&…...

    2024/5/2 14:40:28
  5. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/5/1 17:30:59
  6. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/5/2 16:16:39
  7. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/4/29 2:29:43
  8. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/5/2 9:28:15
  9. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/4/27 17:58:04
  10. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/4/27 14:22:49
  11. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/4/28 1:28:33
  12. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/4/30 9:43:09
  13. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/4/27 17:59:30
  14. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/5/2 15:04:34
  15. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/4/28 1:34:08
  16. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/4/26 19:03:37
  17. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/4/29 20:46:55
  18. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/4/30 22:21:04
  19. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/5/1 4:32:01
  20. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/4/27 23:24:42
  21. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/4/28 5:48:52
  22. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/4/30 9:42:22
  23. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/5/2 9:07:46
  24. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/4/30 9:42:49
  25. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  26. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  27. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  28. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  29. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  30. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  31. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  32. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  33. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  34. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  35. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  36. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  37. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  38. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  39. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  40. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  41. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  42. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  43. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  44. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57