ref 订阅
.net的关键字 [1]  ,ref关键字--让参数按照引用传递。其效果是,当控制权传递回调用方法时,在方法中对参数所做的任何更改都将反映在该变量中;也等同将值类型的数据使用引用方式传参。若要使用ref参数,则方法定义和调用方法都必须显示使用ref关键字。ref是 Reference的缩写。 展开全文
.net的关键字 [1]  ,ref关键字--让参数按照引用传递。其效果是,当控制权传递回调用方法时,在方法中对参数所做的任何更改都将反映在该变量中;也等同将值类型的数据使用引用方式传参。若要使用ref参数,则方法定义和调用方法都必须显示使用ref关键字。ref是 Reference的缩写。
信息
引用方法
显式使用
外文名
ref
用    途
C#中关键字通过引用传递参数
中文名
ref
proteus功能特点
例如:传递到ref参数的参数必须最先初始化。这与 out 不同,out 的参数在传递之前不需要显式初始化。尽管ref和out在运行时的处理方式不同,但它们在编译时的处理方式是相同的。因此,如果一个方法采用ref参数,而另一个方法采用out 参数,则无法重载这两个方法。注意:属性不是变量,因此不能作为ref参数传递。ref和out两个参数的不同在于:1、ref传进去的参数必须在调用前初始化,out不必,即:2、ref传进去的参数在函数内部可以直接使用,而out不可:3、ref传进去的参数在函数内部可以不被修改,但out必须在离开函数体前进行赋值。
收起全文
精华内容
下载资源
问答
  • ref
    千次阅读
    2022-04-19 18:31:09

    0x01 概述

    使用electron开进行桌面程序的开发,似乎成了WEB前端开发人员转桌面程序开发的首选。近期有一些使用在electron中使用加密锁的需求,学习了一下在Node.js中通过ffi-napi模块调用动态链接库,把几款加密锁产品的动态库使用javascript封装了一下,实现了electron中使用加密锁功能。

    开发过程中遇到了一些问题,踩了一些坑,这里总结记录一下。这里使用接口函数参数类型比较复杂的ROCKEY-ARM的动态链接库来进行开发。

    NOTE: javascript封装的ROCKEY-ARM接口模块源码,我已经分享出来,如果只是需要electron或者Node.js工程中使用ROCKEY-ARM的网友,可以直接使用。

    # 克隆
    $ git clone https://github.com/youngbug/js-rockeyarm.git
    

    0x02 准备

    1. 安装依赖模块

    首先需要在node.js项目中安装调用动态链接库时需要依赖的模块ffi-napi,ref-napi,ref-array-napi,ref-struct-napi

    npm install ffi-napi
    npm install ref-napi
    npm install ref-array-napi
    npm install struct-napi
    

    下面大概介绍一下这几个模块的用途:

    • ffi-napi: 在javascript中调用动态链接库(.dll/.so),在Node.js中使用这个模块可以不写任何C/C++代码来创建一个对本地库的绑定。

    • ref-napi: 这个模块定义了很多C/C++的常见数据类型,可以在声明和调用动态库的时候直接使用。

    • ref-array-napi: 这个模块在Node.js中提供了一个数组的实现,在声明和调用函数中,所有的指针都可以声明成一个uchar数组。

    • ref-struct-napi: 这个模块在Node.js中提供了一个结构体类型的实现。ROCKEY-ARM的函数很多参数都是结构体指针,如果声明称uchar的数组,那么传出的数据都是uchar数组,解析的时候不方便,需要自己拼接,除了麻烦,还要考虑字节序的问题。如果使用结构体,并定义一个结构体数组来作为指针传入,函数返回的结构体参数,就可以直接用结构体进行解析,会比较方便。

    2.准备调用的动态库

    飞天诚信购买ROCKEY-ARM加密锁产品,可以获得ROCKEY-ARM的SDK,可以获得Windows和Linux的动态链接库,文件名一般为Dongle_d.和libRockeyARM.so.0.3。

    0x03 声明函数接口

    ffi-napi支持Windows,Linux系统,所以.dll和.so都可以支持,在不同的操作系统下去加载不同的动态库文件就可以了。加载动态库的方法如下:

    import { Library as ffi_Library } from 'ffi-napi'
    
    libRockey = new ffi_Library('d:/rockey/x64/Dongle_d.dll',rockeyInterface)
    

    Library()第一个参数是.dll的路径,Linux系统是.so的路径。第二个参数rockeyInterface是动态库导出函数的声明,ROCKEY-ARM的导出函数比较多,我单独拿出来定义。具体下面会讲到。

    1. 声明几个简单函数

    首先从ROCKEY-ARM中找几个参数简单的函数来声明一下。

    typedef void * DONGLE_HANDLE;
    
    DWORD WINAPI Dongle_Open(DONGLE_HANDLE * phDongle, int nIndex);
    
    DWORD WINAPI Dongle_ResetState(DONGLE_HANDLE hDongle);
    
    DWORD WINAPI Dongle_Close(DONGLE_HANDLE hDongle);
    
    DWORD WINAPI Dongle_GenRandom(DONGLE_HANDLE hDongle, int nLen, BYTE * pRandom);
    

    首先看一下上面几个接口用到的数据类型有:DONGLE_HANDLE,DWORD,DONGLE_HANDLE*,int,BYTE*这几种。
    再看下ffi-napi支持的ref-napi支持的数据类型有以下类型:

    void,int64,ushort,int,uint64,float,uint,long,
    double,int8,ulong,Object,uint8,longlong,CString,
    int16,char,byte,int32,uchar,size_t,uint32,short
    

    参数这里应该用长度一致的数据类型,可以有以下匹配。

    C类型长度ref-napi类型说明
    DONGLE_HANDLE4/8uintC的定义是void*,是一个指针长度是4/8字节,用uint
    DONGLE_HANDLE*4/8ptrHandle定义一个指向DONGLE_HANDLE的指针,用uint应该也是可以,但我没测试
    int4int
    BYTE*4/8prtByte定义一个指向uchar的指针,用uint应该也是可以,但我没测试

    声明的写法如下:

     const rockeyInterface = {
        'Dongle_Open' :             ['int', [ptrHandle, 'int']],
        'Dongle_ResetState' :       ['int', [ryHandle]],
        'Dongle_Close':             ['int', [ryHandle]],
        'Dongle_GenRandom' :        ['int', [ryHandle, 'int', ptrByte]]
     }
    

    一个json,key是动态库导出函数名,比如’Dongle_Open’,value是个列表,第一个元素是返回值,第二个元素是参数。其中参数还是个列表。这个ref-napi中有适合类型的,直接写称具体类型即可,比如返回值DWORD和传入的长度int,我这里都用’int’。其他的参数我额外定义了句柄ryHandle、句柄的指针ptrHandle、字节的指针ptrByte。其中ryHandle,ptrryHandle,ptrByte的定义如下:

    const refArray = require('ref-array-napi')
    
    var ryHandle = refArray(ref.types.uint)
    var ptrHandle = refArray(ryHandle)
    var ptrByte = refArray(ref.types.uchar)
    

    2. void*类型参数

    DONGLE_HANDLE本质是void *类型, void* 类型最开始的时候妄图定义一个void的数组,然后用void数组来表示void*,然后发现报断言错误,数组不支持void类型。所以就直接用无符号数来表示void指针,在64位系统是8字节,32位系统是4字节,使用uint类型就可以了。DONGLE_HANDLE*。

    3. 结构体数组类型参数

    在ROCKEY-ARM的函数中也有很多带参数的接口,比如:

    typedef struct {
    	unsigned int  bits;                   
    	unsigned int  modulus;				  
    	unsigned char exponent[256];     
    
    } RSA_PUBLIC_KEY;
    
    typedef struct {
    	unsigned int  bits;                   
    	unsigned int  modulus;                
    	unsigned char publicExponent[256];    
    	unsigned char exponent[256];          
    
    } RSA_PRIVATE_KEY;
    
    typedef struct
    {
    	unsigned short  m_Ver;               
    	unsigned short  m_Type;              
    	unsigned char   m_BirthDay[8];       
    	unsigned long   m_Agent;             
    	unsigned long   m_PID;               
    	unsigned long   m_UserID;            
    	unsigned char   m_HID[8];            
    	unsigned long   m_IsMother;          
      unsigned long   m_DevType;        
    
    } DONGLE_INFO;
    
    DWORD WINAPI Dongle_Enum(DONGLE_INFO * pDongleInfo, int * pCount);
    
    DWORD WINAPI Dongle_RsaGenPubPriKey(DONGLE_HANDLE hDongle, WORD wPriFileID, RSA_PUBLIC_KEY * pPubBakup, RSA_PRIVATE_KEY * pPriBakup);
    

    拿以上两个函数接口举例,Dongle_Enum中的第一个参数是一个指向DONGLE_INFO结构体的指针,运行后返回设备信息的列表,使用ROCKEY-ARM的时候需要通过枚举函数获得设备信息列表,然后比较产品ID或者硬件ID决定打开哪一个设备。为了方便从枚举函数返回的设备信息中方便的解析出产品ID或者硬件ID等信息,需要把DONGLE_INFO* pDongleInfo这个参数声明成一个结构体数组。Dongle_RsaGenPubPriKey()函数中有RSA_PUBLIC_KEY*,RSA_PRIVATE_KEIY*两个结构体指针参数,因为在这里一般用户并不需要解析RSA密钥中的n,d,e等分量,可以直接做作为一个字节数组,直接声明成上面的ptrByte类型即可。所以在声明如下:

    const ref = require('ref-napi')
    const refArray = require('ref-array-napi')
    const StructType = require ('ref-struct-napi')
    
    var dongleInfo = StructType({
        m_VerL:     ref.types.uchar,
        m_VerR:     ref.types.uchar,
        m_Type:     ref.types.ushort,
        m_BirthdayL:ref.types.uint32,
        m_BirthdayR:ref.types.uint32,
        m_Agent:    ref.types.uint32,
        m_PID:      ref.types.uint32,
        m_UserID:   ref.types.uint32,
        m_HIDL:     ref.types.uint32,
        m_HIDR:     ref.types.uint32,
        m_IsMother: ref.types.uint32,
        m_DevType:  ref.types.uint32
    })
    
    var ptrInt = refArray(ref.types.int)
    var ryHandle = refArray(ref.types.uint)
    var ptrHandle = refArray(ryHandle) 
    var ptrDongleInfo = refArray(dongleInfo)
    var ptrByte = refArray(ref.types.uchar)
    
    const rockeyInterface = {
      'Dongle_Enum' :             ['int', [ptrDongleInfo, ptrInt]],
      'Dongle_RsaGenPubPriKey' :  ['int', [ryHandle, 'ushort', ptrByte, ptrByte]]
    }
    

    0x04 调用声明的函数

    调用ffi-napi声明的函数,主要是给自己定义的数据类型赋初值以及获得自定义参数的返回值。下面分别说明。

    1. int*

    这里的int*,是让函数返回设备的数量,或者传入输入数据的长度或者传出输出数据的长度,所以只要定义一个长度为1的int数组即可,如下:

     var piCount = new ptrInt(1) //
     piCount[0] = 0
    

    给传入的数据赋值,只要给下标为0的元素赋值即可。

    2. DONGLE_INFO*

    这个参数是枚举函数传出枚举到设备信息的列表,枚举到多少设备,就传出多少个DONGLE_INFO,所以需要传入足够数量的的DONGLE_INFO,如下:

    libRockey.Dongle_Enum(null, piCount)//获得设备的数量
    var DongleList = new ptrDongleInfo(piCount[0])
    libRockey.Dongle_Enum(DongleList, piCount)
    console.log(DongleList[0].m_PID) //输出枚举到的第一个设备的PID
    

    3. BYTE*

    这个参数一般是作为传入传出数据的缓冲区的,所以创建数组的时候,需要创建足够长的空间,如下:

    var buffer = new ptrByte(len)
    

    0x05 踩坑总结

    开发的过程中,踩到一些坑耽误了不少时间,这里总结一下。

    ROCKEY-ARM的结构体是按字节对齐的,ref-struct-napi没有找到设置字节对齐的方法。当时声明的结构体如下:

    var dongleInfo = StructType({
        m_VerL:     ref.types.uchar,
        m_VerR:     ref.types.uchar,
        m_Type:     ref.types.ushort,
        m_Birthday: ref.types.uint64,
        m_Agent:    ref.types.uint32,
        m_PID:      ref.types.uint32,
        m_UserID:   ref.types.uint32,
        m_HID:      ref.types.uint64,
        m_IsMother: ref.types.uint32,
        m_DevType:  ref.types.uint32
    })
    

    测试的时候会发现定义的结构体和ROCKEY-ARM定义的结构体对齐方式不一样,于是把m_Birthday和m_HID两个成员从ref.types.uint64,拆分成左右两个uint32,这样就可以让结构体对齐方式和ROCKEY-ARM的一致。使用m_Birthday和m_HID的时候,需要讲左右两个uint32拼接一些,稍微麻烦一点,但是在没找到配置StructType对齐方的情况,保证结果正确,还是可以接受的。

    更多相关内容
  • 为元素添加一个ref属性 该属性的值可任取 以作为该节点的引用名 ref即为reference(引用)的前三个字母 This is title This is content 用Vue实例中的自带的$refs属性获取元素 用预先定义好的引用名来获取 即在...
  • 一个在JSON对象中转换$ref并将其替换为_ref的模块,以便可以将其存储在Mongo中,反之亦然。 我创建此模块是因为$ref是和的标准。 用法 import { replaceRefsJSON , replaceRefsMongo } from 'ref-replace' ; const...
  • ref

    千次阅读 2017-12-21 23:53:21
    Examples referencing ...var ref = require('ref') // so we can all agree that a buffer with the int value written // to it could be represented as an "int *" var buf = new Buffer

    Examples

    referencing and derefencing

    var ref = require('ref')
    
    // so we can all agree that a buffer with the int value written
    // to it could be represented as an "int *"
    var buf = new Buffer(4)
    buf.writeInt32LE(12345, 0)
    
    // first, what is the memory address of the buffer?
    console.log(buf.hexAddress())  // ← '7FA89D006FD8'
    
    // using `ref`, you can set the "type", and gain magic abilities!
    buf.type = ref.types.int
    
    // now we can dereference to get the "meaningful" value
    console.log(buf.deref())  // ← 12345
    
    
    // you can also get references to the original buffer if you need it.
    // this buffer could be thought of as an "int **"
    var one = buf.ref()
    
    // and you can dereference all the way back down to an int
    console.log(one.deref().deref())  // ← 12345

    See the full API Docs for more examples.

    The "type" interface

    You can easily define your own "type" objects at attach to Buffer instances. It just needs to be a regular JavaScript Object that contains the following properties:

    NameData TypeDescription
    sizeNumberThe size in bytes required to hold this type.
    indirectionNumberThe current level of indirection of the buffer. Usually this would be 1, and gets incremented on Buffers from ref() calls. A value of less than or equal to 0 is invalid.
    getFunction (buffer, offset)The function to invoke when dereferencing this type when the indirection level is 1.
    setFunction (buffer, offset, value)The function to invoke when setting a value to a buffer instance.
    nameString(optional) The name to use during debugging for this type.
    alignmentNumber(optional) The alignment of this type when placed in a struct. Defaults to the type's size.

    Be sure to check out the Wiki page of "Known Types", for the list of built-in ref types, as well as known external type implementations.

    For example, you could define a "bigint" type that dereferences into a bigint instance:

    var ref = require('ref')
    var bigint = require('bigint')
    
    // define the "type" instance according to the spec
    var BigintType = {
        size: ref.sizeof.int64
      , indirection: 1
      , get: function (buffer, offset) {
          // return a bigint instance from the buffer
          return bigint.fromBuffer(buffer)
        }
      , set: function (buffer, offset, value) {
          // 'value' would be a bigint instance
          var val = value.toString()
          return ref.writeInt64(buffer, offset || 0, val)
        }
    }
    
    // now we can create instances of the type from existing buffers.
    // "buf" is some Buffer instance returned from some external data
    // source, which should contain "bigint" binary data.
    buf.type = BigintType
    
    // and now you can create "bigint" instances using this generic "types" API
    var val = buf.deref()
                .add('1234')
                .sqrt()
                .shiftLeft(5)
     
     
     
     
     
     
     
     
      

    Turn Buffer instances into "pointers"

    What is ref?

    ref is a native addon for Node.js that aids in doing C programming in JavaScript, by extending the built-in Buffer class with some fancy additions like:

    • Getting the memory address of a Buffer
    • Checking the endianness of the processor
    • Checking if a Buffer represents the NULL pointer
    • Reading and writing "pointers" with Buffers
    • Reading and writing C Strings (NULL-terminated)
    • Reading and writing JavaScript Object references
    • Reading and writing int64_t and uint64_t values
    • A "type" convention to define the contents of a Buffer

    There is indeed a lot of meat to ref, but it all fits together in one way or another in the end. For simplicity, ref's API can be broken down into 3 sections:

    ref exports

    All the static versions of ref's functions and default "types" available on the exports returned from require('ref').

    "type" system

    The "type" system allows you to define a "type" on any Buffer instance, and then use generic ref() and deref() functions to reference and dereference values.

    Buffer extensions

    Buffer.prototype gets extended with some convenience functions. These all just mirror their static counterpart, using the Buffer's this variable as the buffer variable.


    ref exports

    This section documents all the functions exported from require('ref').

    ref.NULL ⇒ Buffer

    Buffer that references the C NULL pointer. That is, its memory address points to 0. Its length is 0 because accessing any data from this buffer would cause a segmentation fault.

    console.log(ref.NULL);
    <SlowBuffer@0x0 >
    

    ref.NULL_POINTER ⇒ Buffer

    NULL_POINTER is a pointer-sized Buffer instance pointing to NULL. Conceptually, it's equivalent to the following C code:

    char *null_pointer;
    null_pointer = NULL;
    

    ref.address(Buffer buffer→ Number

    • buffer - The buffer to get the memory address of.
    • Return: The memory address the buffer instance.

    Accepts a Buffer instance and returns the memory address of the buffer instance.

    console.log(ref.address(new Buffer(1)));
    4320233616
    
    console.log(ref.address(ref.NULL)));
    0
    

    ref.alloc(Object|String type? value→ Buffer

    • type - The "type" object to allocate. Strings get coerced first.
    • value - (optional) The initial value set on the returned Buffer, using type's set() function.
    • Return: A new Buffer instance with it's type set to "type", and (optionally) "value" written to it.

    Returns a new Buffer instance big enough to hold type, with the given value written to it.

    var intBuf = ref.alloc(ref.types.int)
    var int_with_4 = ref.alloc(ref.types.int, 4)
    

    ref.allocCString(String stringString encoding→ Buffer

    • string - The JavaScript string to be converted to a C string.
    • encoding - (optional) The encoding to use for the C string. Defaults to 'utf8'.
    • Return: The new Buffer instance with the specified String wrtten to it, and a trailing NUL byte.

    Returns a new Buffer instance with the given String written to it with the given encoding (defaults to 'utf8'). The buffer is 1 byte longer than the string itself, and is NUL terminated.

    var buf = ref.allocCString('hello world');
    
    console.log(buf.toString());
    'hello world\u0000'
    

    ref.coerceType(Object|String type→ Object

    • type - The "type" Object or String to coerce.
    • Return: A "type" object

    Coerces a "type" object from a String or an actual "type" object. String values are looked up from the ref.types Object. So:

    • "int" gets coerced into ref.types.int.
    • "int *" gets translated into ref.refType(ref.types.int)
    • ref.types.int gets translated into ref.types.int (returns itself)

    Throws an Error if no valid "type" object could be determined. Most ref functions use this function under the hood, so anywhere a "type" object is expected, a String may be passed as well, including simply setting the buffer.type property.

    var type = ref.coerceType('int **');
    
    console.log(type.indirection);
    3
    

    ref.deref(Buffer buffer→ ?

    • buffer - A Buffer instance to dereference.
    • Return: The returned value after dereferencing buffer.

    Accepts a Buffer instance and attempts to "dereference" it. That is, first it checks the indirection count of buffer's "type", and if it's greater than 1 then it merely returns another Buffer, but with one level less indirection.

    When buffer's indirection is at 1, then it checks for buffer.type which should be an Object with its own get() function.

    var buf = ref.alloc('int', 6);
    
    var val = ref.deref(buf);
    console.log(val);
    6
    

    ref.derefType(Object|String type→ Object

    • type - The "type" object to create a dereference type from. Strings get coerced first.
    • Return: The new "type" object with its indirection decremented by 1.

    Returns a new clone of the given "type" object, with its indirection level decremented by 1.

    ref.endianness ⇒ String

    A string that represents the native endianness of the machine's processor. The possible values are either "LE" or "BE".

    console.log(ref.endianness);
    'LE'
    

    ref.get(Buffer bufferNumber offsetObject|String type→ ?

    • buffer - The Buffer instance to read from.
    • offset - (optional) The offset on the Buffer to start reading from. Defaults to 0.
    • type - (optional) The "type" object to use when reading. Defaults to calling getType() on the buffer.
    • Return: Whatever value the "type" used when reading returns.

    Calls the get() function of the Buffer's current "type" (or the passed in type if present) at the given offset.

    This function handles checking the "indirection" level and returning a proper "dereferenced" Bufffer instance when necessary.

    ref.getType(Buffer buffer→ Object

    • buffer - The Buffer instance to get the "type" object from.
    • Return: The "type" object from the given Buffer.

    Returns the "type" property of the given Buffer. Creates a default type for the buffer when none exists.

    ref.isNull(Buffer buffer→ Boolean

    • buffer - The buffer to check for NULL.
    • Return: true or false.

    Accepts a Buffer instance and returns true if the buffer represents the NULL pointer, false otherwise.

    console.log(ref.isNull(new Buffer(1)));
    false
    
    console.log(ref.isNull(ref.NULL));
    true
    

    ref.readCString(Buffer bufferNumber offset→ String

    • buffer - The buffer to read a Buffer from.
    • offset - The offset to begin reading from.
    • Return: The String that was read from buffer.

    Returns a JavaScript String read from buffer at the given offset. The C String is read until the first NULL byte, which indicates the end of the String.

    This function can read beyond the length of a Buffer.

    var buf = new Buffer('hello\0world\0');
    
    var str = ref.readCString(buf, 0);
    console.log(str);
    'hello'
    

    ref.readInt64BE(Buffer bufferNumber offset→ Number|String

    • buffer - The buffer to read a Buffer from.
    • offset - The offset to begin reading from.
    • Return: The Number or String that was read from buffer.

    Returns a big-endian signed 64-bit int read from buffer at the given offset.

    If the returned value will fit inside a JavaScript Number without losing precision, then a Number is returned, otherwise a String is returned.

    var buf = ref.alloc('int64');
    ref.writeInt64BE(buf, 0, '9223372036854775807');
    
    var val = ref.readInt64BE(buf, 0)
    console.log(val)
    '9223372036854775807'
    

    ref.readInt64LE(Buffer bufferNumber offset→ Number|String

    • buffer - The buffer to read a Buffer from.
    • offset - The offset to begin reading from.
    • Return: The Number or String that was read from buffer.

    Returns a little-endian signed 64-bit int read from buffer at the given offset.

    If the returned value will fit inside a JavaScript Number without losing precision, then a Number is returned, otherwise a String is returned.

    var buf = ref.alloc('int64');
    ref.writeInt64LE(buf, 0, '9223372036854775807');
    
    var val = ref.readInt64LE(buf, 0)
    console.log(val)
    '9223372036854775807'
    

    ref.readObject(Buffer bufferNumber offset→ Object

    • buffer - The buffer to read an Object from.
    • offset - The offset to begin reading from.
    • Return: The Object that was read from buffer.

    Reads a JavaScript Object that has previously been written to the given buffer at the given offset.

    var obj = { foo: 'bar' };
    var buf = ref.alloc('Object', obj);
    
    var obj2 = ref.readObject(buf, 0);
    console.log(obj === obj2);
    true
    

    ref.readPointer(Buffer bufferNumber offsetNumber length→ Buffer

    • buffer - The buffer to read a Buffer from.
    • offset - The offset to begin reading from.
    • length - (optional) The length of the returned Buffer. Defaults to 0.
    • Return: The Buffer instance that was read from buffer.

    Reads a Buffer instance from the given buffer at the given offset. The size parameter specifies the length of the returned Buffer instance, which defaults to 0.

    var buf = new Buffer('hello world');
    var pointer = ref.alloc('pointer');
    
    var buf2 = ref.readPointer(pointer, 0, buf.length);
    console.log(buf.toString());
    'hello world'
    

    ref.readUInt64BE(Buffer bufferNumber offset→ Number|String

    • buffer - The buffer to read a Buffer from.
    • offset - The offset to begin reading from.
    • Return: The Number or String that was read from buffer.

    Returns a big-endian unsigned 64-bit int read from buffer at the given offset.

    If the returned value will fit inside a JavaScript Number without losing precision, then a Number is returned, otherwise a String is returned.

    var buf = ref.alloc('uint64');
    ref.writeUInt64BE(buf, 0, '18446744073709551615');
    
    var val = ref.readUInt64BE(buf, 0)
    console.log(val)
    '18446744073709551615'
    

    ref.readUInt64LE(Buffer bufferNumber offset→ Number|String

    • buffer - The buffer to read a Buffer from.
    • offset - The offset to begin reading from.
    • Return: The Number or String that was read from buffer.

    Returns a little-endian unsigned 64-bit int read from buffer at the given offset.

    If the returned value will fit inside a JavaScript Number without losing precision, then a Number is returned, otherwise a String is returned.

    var buf = ref.alloc('uint64');
    ref.writeUInt64LE(buf, 0, '18446744073709551615');
    
    var val = ref.readUInt64LE(buf, 0)
    console.log(val)
    '18446744073709551615'
    

    ref.ref(Buffer buffer→ Buffer

    • buffer - A Buffer instance to create a reference to.
    • Return: A new Buffer instance pointing to buffer.

    ref() accepts a Buffer instance and returns a new Buffer instance that is "pointer" sized and has its data pointing to the given Buffer instance. Essentially the created Buffer is a "reference" to the original pointer, equivalent to the following C code:

    char *buf = buffer;
    char **ref = &buf;
    

    ref.refType(Object|String type→ Object

    • type - The "type" object to create a reference type from. Strings get coerced first.
    • Return: The new "type" object with its indirection incremented by 1.

    Returns a new clone of the given "type" object, with its indirection level incremented by 1.

    Say you wanted to create a type representing a void *:

    var voidPtrType = ref.refType(ref.types.void);
    

    ref.reinterpret(Buffer bufferNumber sizeNumber offset→ Buffer

    • buffer - A Buffer instance to base the returned Buffer off of.
    • size - The length property of the returned Buffer.
    • offset - The offset of the Buffer to begin from.
    • Return: A new Buffer instance with the same memory address as buffer, and the requested size.

    Returns a new Buffer instance with the specified size, with the same memory address as buffer.

    This function "attaches" buffer to the returned Buffer to prevent it from being garbage collected.

    ref.reinterpretUntilZeros(Buffer bufferNumber sizeNumber offset→ Buffer

    • buffer - A Buffer instance to base the returned Buffer off of.
    • size - The number of sequential, aligned NULL bytes are required to terminate the buffer.
    • offset - The offset of the Buffer to begin from.
    • Return: A new Buffer instance with the same memory address as buffer, and a variable length that is terminated by size NUL bytes.

    Accepts a Buffer instance and a number of NULL bytes to read from the pointer. This function will scan past the boundary of the Buffer's length until it finds size number of aligned NULL bytes.

    This is useful for finding the end of NUL-termintated array or C string. For example, the readCString() function could be implemented like:

    function readCString (buf) {
      return ref.reinterpretUntilZeros(buf, 1).toString('utf8')
    }
    

    This function "attaches" buffer to the returned Buffer to prevent it from being garbage collected.

    ref.set(Buffer bufferNumber offset? valueObject|String type)

    • buffer - The Buffer instance to write to.
    • offset - The offset on the Buffer to start writing to.
    • value - The value to write to the Buffer instance.
    • type - (optional) The "type" object to use when reading. Defaults to calling getType() on the buffer.

    Calls the set() function of the Buffer's current "type" (or the passed in type if present) at the given offset.

    This function handles checking the "indirection" level writing a pointer rather than calling the set() function if the indirection is greater than 1.

    ref.writeCString(Buffer bufferNumber offsetString stringString encoding)

    • buffer - The Buffer instance to write to.
    • offset - The offset of the buffer to begin writing at.
    • string - The JavaScript String to write that will be written to the buffer.
    • encoding - (optional) The encoding to read the C string as. Defaults to 'utf8'.

    Writes the given string as a C String (NULL terminated) to the given buffer at the given offset. "encoding" is optional and defaults to 'utf8'.

    Unlike readCString(), this function requires the buffer to actually have the proper length.

    ref.writeInt64BE(Buffer bufferNumber offsetNumber|String input)

    • buffer - The buffer to write to.
    • offset - The offset to begin writing from.
    • input - This String or Number which gets written.

    Writes the input Number or String as a big-endian signed 64-bit int into buffer at the given offset.

    var buf = ref.alloc('int64');
    ref.writeInt64BE(buf, 0, '9223372036854775807');
    

    ref.writeInt64LE(Buffer bufferNumber offsetNumber|String input)

    • buffer - The buffer to write to.
    • offset - The offset to begin writing from.
    • input - This String or Number which gets written.

    Writes the input Number or String as a little-endian signed 64-bit int into buffer at the given offset.

    var buf = ref.alloc('int64');
    ref.writeInt64LE(buf, 0, '9223372036854775807');
    

    ref.writeObject(Buffer bufferNumber offsetObject object)

    • buffer - A Buffer instance to write object to.
    • offset - The offset on the Buffer to start writing at.
    • object - The Object to be written into buffer.

    Writes a pointer to object into buffer at the specified _offset.

    This function "attaches" object to buffer to prevent it from being garbage collected.

    var buf = ref.alloc('Object');
    ref.writeObject(buf, 0, { foo: 'bar' });
    

    ref.writePointer(Buffer bufferNumber offsetBuffer pointer)

    • buffer - A Buffer instance to write _pointer to.
    • offset - The offset on the Buffer to start writing at.
    • pointer - The Buffer instance whose memory address will be written to buffer.

    Writes the memory address of pointer to buffer at the specified offset.

    This function "attaches" object to buffer to prevent it from being garbage collected.

    var someBuffer = new Buffer('whatever');
    var buf = ref.alloc('pointer');
    ref.writePointer(buf, 0, someBuffer);
    

    ref.writeUInt64BE(Buffer bufferNumber offsetNumber|String input)

    • buffer - The buffer to write to.
    • offset - The offset to begin writing from.
    • input - This String or Number which gets written.

    Writes the input Number or String as a big-endian unsigned 64-bit int into buffer at the given offset.

    var buf = ref.alloc('uint64');
    ref.writeUInt64BE(buf, 0, '18446744073709551615');
    

    ref.writeUInt64LE(Buffer bufferNumber offsetNumber|String input)

    • buffer - The buffer to write to.
    • offset - The offset to begin writing from.
    • input - This String or Number which gets written.

    Writes the input Number or String as a little-endian unsigned 64-bit int into buffer at the given offset.

    var buf = ref.alloc('uint64');
    ref.writeUInt64LE(buf, 0, '18446744073709551615');
    

    ref._attach(Buffer bufferObject|Buffer object)

    • buffer - A Buffer instance to attach object to.
    • object - An Object or Buffer to prevent from being garbage collected until buffer does.

    Attaches object to buffer such that it prevents object from being garbage collected until buffer does.

    ref._reinterpret(Buffer bufferNumber sizeNumber offset→ Buffer

    • buffer - A Buffer instance to base the returned Buffer off of.
    • size - The length property of the returned Buffer.
    • offset - The offset of the Buffer to begin from.
    • Return: A new Buffer instance with the same memory address as buffer, and the requested size.

    Same as ref.reinterpret(), except that this version does not attach buffer to the returned Buffer, which is potentially unsafe if the garbage collector runs.

    ref._reinterpretUntilZeros(Buffer bufferNumber sizeNumber offset→ Buffer

    • buffer - A Buffer instance to base the returned Buffer off of.
    • size - The number of sequential, aligned NULL bytes that are required to terminate the buffer.
    • offset - The offset of the Buffer to begin from.
    • Return: A new Buffer instance with the same memory address as buffer, and a variable length that is terminated by size NUL bytes.

    Same as ref.reinterpretUntilZeros(), except that this version does not attach buffer to the returned Buffer, which is potentially unsafe if the garbage collector runs.

    ref._writeObject(Buffer bufferNumber offsetObject object)

    • buffer - A Buffer instance to write object to.
    • offset - The offset on the Buffer to start writing at.
    • object - The Object to be written into buffer.

    Same as ref.writeObject(), except that this version does not attach the Object to the Buffer, which is potentially unsafe if the garbage collector runs.

    ref._writePointer(Buffer bufferNumber offsetBuffer pointer)

    • buffer - A Buffer instance to write _pointer to.
    • offset - The offset on the Buffer to start writing at.
    • pointer - The Buffer instance whose memory address will be written to buffer.

    Same as ref.writePointer(), except that this version does not attach pointer to buffer, which is potentially unsafe if the garbage collector runs.


    "type" system

    A "type" in ref is simply an plain 'ol JavaScript Object, with a set of expected properties attached that implement the logic for getting & setting values on a given Buffer instance.

    To attach a "type" to a Buffer instance, you simply attach the "type" object to the Buffer's type property. ref comes with a set of commonly used types which are described in this section.

    Creating your own "type"

    It's trivial to create your own "type" that reads and writes your own custom datatype/class to and from Buffer instances using ref's unified API.
    To create your own "type", simply create a JavaScript Object with the following properties defined:

    NameData TypeDescription
    sizeNumberThe size in bytes required to hold this datatype.
    indirectionNumberThe current level of indirection of the buffer. When defining your own "types", just set this value to 1.
    getFunctionThe function to invoke when ref.get() is invoked on a buffer of this type.
    setFunctionThe function to invoke when ref.set() is invoked on a buffer of this type.
    nameString(Optional) The name to use during debugging for this datatype.
    alignmentNumber(Optional) The alignment of this datatype when placed inside a struct. Defaults to the type's size.

    The built-in "types"

    Here is the list of ref's built-in "type" Objects. All these built-in "types" can be found on the ref.types export Object. All the built-in types use "native endianness" when multi-byte datatypes are involved.

    types.void

    The void type.

    types.int8

    The int8 type.

    types.uint8

    The uint8 type.

    types.int16

    The int16 type.

    types.uint16

    The uint16 type.

    types.int32

    The int32 type.

    types.uint32

    The uint32 type.

    types.int64

    The int64 type.

    types.uint64

    The uint64 type.

    types.float

    The float type.

    types.double

    The double type.

    types.Object

    The Object type. This can be used to read/write regular JS Objects into raw memory.

    types.CString

    The CString (a.k.a "string") type.

    CStrings are a kind of weird thing. We say it's sizeof(char *), and indirection level of 1, which means that we have to return a Buffer that is pointer sized, and points to a some utf8 string data, so we have to create a 2nd "in-between" buffer.

    types.bool

    The bool type.

    Wrapper type around types.uint8 that accepts/returns true or false Boolean JavaScript values.

    types.byte

    The byte type.

    types.char

    The char type.

    types.uchar

    The uchar type.

    types.short

    The short type.

    types.ushort

    The ushort type.

    types.int

    The int type.

    types.uint

    The uint type.

    types.long

    The long type.

    types.ulong

    The ulong type.

    types.longlong

    The longlong type.

    types.ulonglong

    The ulonglong type.

    types.size_t

    The size_t type.


    Buffer extensions

    Buffer.prototype gets extended with some convenience functions that you can use in your modules and/or applications.

    Buffer#address()

    Shorthand for ref.address(this, …).

    Accepts a Buffer instance and returns the memory address of the buffer instance.

    console.log(ref.address(new Buffer(1)));
    4320233616
    
    console.log(ref.address(ref.NULL)));
    0
    

    Buffer#deref()

    Shorthand for ref.deref(this, …).

    Accepts a Buffer instance and attempts to "dereference" it. That is, first it checks the indirection count of buffer's "type", and if it's greater than 1 then it merely returns another Buffer, but with one level less indirection.

    When buffer's indirection is at 1, then it checks for buffer.type which should be an Object with its own get() function.

    var buf = ref.alloc('int', 6);
    
    var val = ref.deref(buf);
    console.log(val);
    6
    

    Buffer#hexAddress()

    Shorthand for ref.hexAddress(this, …).

    Buffer#inspect()

    ref overwrites the default Buffer#inspect() function to include the hex-encoded memory address of the Buffer instance when invoked.

    This is simply a nice-to-have.

    Before:

    console.log(new Buffer('ref'));
    <Buffer 72 65 66>
    

    After:

    console.log(new Buffer('ref'));
    <Buffer@0x103015490 72 65 66>
    

    Buffer#isNull()

    Shorthand for ref.isNull(this, …).

    Accepts a Buffer instance and returns true if the buffer represents the NULL pointer, false otherwise.

    console.log(ref.isNull(new Buffer(1)));
    false
    
    console.log(ref.isNull(ref.NULL));
    true
    

    Buffer#readCString()

    Shorthand for ref.readCString(this, …).

    Returns a JavaScript String read from buffer at the given offset. The C String is read until the first NULL byte, which indicates the end of the String.

    This function can read beyond the length of a Buffer.

    var buf = new Buffer('hello\0world\0');
    
    var str = ref.readCString(buf, 0);
    console.log(str);
    'hello'
    

    Buffer#readInt64BE()

    Shorthand for ref.readInt64BE(this, …).

    Returns a big-endian signed 64-bit int read from buffer at the given offset.

    If the returned value will fit inside a JavaScript Number without losing precision, then a Number is returned, otherwise a String is returned.

    var buf = ref.alloc('int64');
    ref.writeInt64BE(buf, 0, '9223372036854775807');
    
    var val = ref.readInt64BE(buf, 0)
    console.log(val)
    '9223372036854775807'
    

    Buffer#readInt64LE()

    Shorthand for ref.readInt64LE(this, …).

    Returns a little-endian signed 64-bit int read from buffer at the given offset.

    If the returned value will fit inside a JavaScript Number without losing precision, then a Number is returned, otherwise a String is returned.

    var buf = ref.alloc('int64');
    ref.writeInt64LE(buf, 0, '9223372036854775807');
    
    var val = ref.readInt64LE(buf, 0)
    console.log(val)
    '9223372036854775807'
    

    Buffer#readObject()

    Shorthand for ref.readObject(this, …).

    Reads a JavaScript Object that has previously been written to the given buffer at the given offset.

    var obj = { foo: 'bar' };
    var buf = ref.alloc('Object', obj);
    
    var obj2 = ref.readObject(buf, 0);
    console.log(obj === obj2);
    true
    

    Buffer#readPointer()

    Shorthand for ref.readPointer(this, …).

    Reads a Buffer instance from the given buffer at the given offset. The size parameter specifies the length of the returned Buffer instance, which defaults to 0.

    var buf = new Buffer('hello world');
    var pointer = ref.alloc('pointer');
    
    var buf2 = ref.readPointer(pointer, 0, buf.length);
    console.log(buf.toString());
    'hello world'
    

    Buffer#readUInt64BE()

    Shorthand for ref.readUInt64BE(this, …).

    Returns a big-endian unsigned 64-bit int read from buffer at the given offset.

    If the returned value will fit inside a JavaScript Number without losing precision, then a Number is returned, otherwise a String is returned.

    var buf = ref.alloc('uint64');
    ref.writeUInt64BE(buf, 0, '18446744073709551615');
    
    var val = ref.readUInt64BE(buf, 0)
    console.log(val)
    '18446744073709551615'
    

    Buffer#readUInt64LE()

    Shorthand for ref.readUInt64LE(this, …).

    Returns a little-endian unsigned 64-bit int read from buffer at the given offset.

    If the returned value will fit inside a JavaScript Number without losing precision, then a Number is returned, otherwise a String is returned.

    var buf = ref.alloc('uint64');
    ref.writeUInt64LE(buf, 0, '18446744073709551615');
    
    var val = ref.readUInt64LE(buf, 0)
    console.log(val)
    '18446744073709551615'
    

    Buffer#ref()

    Shorthand for ref.ref(this, …).

    ref() accepts a Buffer instance and returns a new Buffer instance that is "pointer" sized and has its data pointing to the given Buffer instance. Essentially the created Buffer is a "reference" to the original pointer, equivalent to the following C code:

    char *buf = buffer;
    char **ref = &buf;
    

    Buffer#reinterpret()

    Shorthand for ref.reinterpret(this, …).

    Returns a new Buffer instance with the specified size, with the same memory address as buffer.

    This function "attaches" buffer to the returned Buffer to prevent it from being garbage collected.

    Buffer#reinterpretUntilZeros()

    Shorthand for ref.reinterpretUntilZeros(this, …).

    Accepts a Buffer instance and a number of NULL bytes to read from the pointer. This function will scan past the boundary of the Buffer's length until it finds size number of aligned NULL bytes.

    This is useful for finding the end of NUL-termintated array or C string. For example, the readCString() function could be implemented like:

    function readCString (buf) {
      return ref.reinterpretUntilZeros(buf, 1).toString('utf8')
    }
    

    This function "attaches" buffer to the returned Buffer to prevent it from being garbage collected.

    Buffer#writeCString()

    Shorthand for ref.writeCString(this, …).

    Writes the given string as a C String (NULL terminated) to the given buffer at the given offset. "encoding" is optional and defaults to 'utf8'.

    Unlike readCString(), this function requires the buffer to actually have the proper length.

    Buffer#writeInt64BE()

    Shorthand for ref.writeInt64BE(this, …).

    Writes the input Number or String as a big-endian signed 64-bit int into buffer at the given offset.

    var buf = ref.alloc('int64');
    ref.writeInt64BE(buf, 0, '9223372036854775807');
    

    Buffer#writeInt64LE()

    Shorthand for ref.writeInt64LE(this, …).

    Writes the input Number or String as a little-endian signed 64-bit int into buffer at the given offset.

    var buf = ref.alloc('int64');
    ref.writeInt64LE(buf, 0, '9223372036854775807');
    

    Buffer#writeObject()

    Shorthand for ref.writeObject(this, …).

    Writes a pointer to object into buffer at the specified _offset.

    This function "attaches" object to buffer to prevent it from being garbage collected.

    var buf = ref.alloc('Object');
    ref.writeObject(buf, 0, { foo: 'bar' });
    

    Buffer#writePointer()

    Shorthand for ref.writePointer(this, …).

    Writes the memory address of pointer to buffer at the specified offset.

    This function "attaches" object to buffer to prevent it from being garbage collected.

    var someBuffer = new Buffer('whatever');
    var buf = ref.alloc('pointer');
    ref.writePointer(buf, 0, someBuffer);
    

    Buffer#writeUInt64BE()

    Shorthand for ref.writeUInt64BE(this, …).

    Writes the input Number or String as a big-endian unsigned 64-bit int into buffer at the given offset.

    var buf = ref.alloc('uint64');
    ref.writeUInt64BE(buf, 0, '18446744073709551615');
    

    Buffer#writeUInt64LE()

    Shorthand for ref.writeUInt64LE(this, …).

    Writes the input Number or String as a little-endian unsigned 64-bit int into buffer at the given offset.

    var buf = ref.alloc('uint64');
    ref.writeUInt64LE(buf, 0, '18446744073709551615');
    

    展开全文
  • vue3.0 ref 函数

    千次阅读 2021-07-19 21:11:57
    ref 对象具有指向内部值的单个 property .value 根据vue官方文档的提示,通过ref包裹普通数据,通过.value能够拿到响应式数据,我们可以先打印一下name <template> 个人信息</p> 姓名:{{ name }}</p> 年龄:{{ age ...

    先写一个基础的vue3模板

    <template>
      <div>
        <p>个人信息</p>
        <p>姓名:{{ name }}</p>
        <p>年龄:{{ age }}</p>
      </div>
    </template>
    
    <script lang="ts">
    import { defineComponent } from "vue";
    
    export default defineComponent({
      name: "Home",
      components: {},
      setup() {
        const name = "张三";
        const age = 18;
        return {
          name,
          age
        };
      },
    });
    </script>
    

    页面正常显示
    在这里插入图片描述
    这时候加一个定时器,修改张三的年龄

    <template>
      <p>个人信息</p>
      <p>姓名:{{ name }}</p>
      <p>年龄:{{ age }}</p>
    </template>
    
    <script lang="ts">
    import { defineComponent } from "vue";
    export default defineComponent({
      name: "Home",
      components: {},
      setup() {
        let name = "张三";
        let age = 18;
        setInterval(function () {
          age++;
          console.log("age", age);
        }, 1000);
        return {
          name,
          age,
        };
      },
    });
    </script>
    

    在这里插入图片描述
    会发现控制台正常打印,但是数据不是响应式的,也就是数据发生了变化,页面却没有更新。原因是我们仅仅定义了一个string类型和number类型的普通数据,并不是一个vue响应式数据。现在引入vue3中一个函数,ref,官方定义如下:

    接受一个内部值并返回一个响应式且可变的 ref 对象。ref 对象具有指向内部值的单个 property .value

    根据vue官方文档的提示,通过ref包裹普通数据,通过.value能够拿到响应式数据,我们可以先打印一下name

    <template>
      <p>个人信息</p>
      <p>姓名:{{ name }}</p>
      <p>年龄:{{ age }}</p>
    </template>
    
    <script lang="ts">
    import { defineComponent, ref } from "vue";
    export default defineComponent({
      name: "Home",
      components: {},
      setup() {
        let name = ref("张三");
        let age = ref(18);
        console.log("name", name);
        return {
          name,
          age,
        };
      },
    });
    </script>
    

    发现’name’是被refImpl类包裹的一个实例对象,这个类可以等会研究研究,先看实例对象上的一个属性:value,值为(…),并且提示’invoke property getter’,意思是调用属性 getter,这说明vue3.0的ref函数是通过vue2.x数据响应式方法Object.defineProperty()作为响应式数据的手段。

    Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。–MDN

    在这里插入图片描述
    再继续往下看原型链_proto_,打印如下图,其中包含了Object.defineProperty的get和set方法,来读/写value属性完成一个ref数据响应式。_proto_下还存在了一个value属性,值为’张三’,通过数据代理传递到最外层,方便调用。(可以理解成vue2.x中,vm.data与vm._data都能获取data中定义的数据,在template中写value比_data.value更加方便)

    数据代理:通过一个对象代理对另一个对象中属性的操作(读/写)

    在这里插入图片描述

    <template>
      <p>个人信息</p>
      <p>姓名:{{ name }}</p>
      <p>年龄:{{ age }}</p>
    </template>
    
    <script lang="ts">
    import { defineComponent, ref } from "vue";
    export default defineComponent({
      name: "Home",
      components: {},
      setup() {
        let name = ref("张三");
        let age = ref(18);
        console.log("name", name);
        return {
          name,
          age,
        };
      },
    });
    </script>
    

    通过控制台发现name是被refImpl包裹的一个对象
    在这里插入图片描述
    再通过.value获取到name值

    <template>
      <p>个人信息</p>
      <p>姓名:{{ name }}</p>
      <p>年龄:{{ age }}</p>
    </template>
    
    <script lang="ts">
    import { defineComponent, ref } from "vue";
    export default defineComponent({
      name: "Home",
      components: {},
      setup() {
        let name = ref("张三");
        let age = ref(18);
        setInterval(function () {
          age.value++;//被ref包裹的数据,需要通过.value获取值
          console.log("age", age);
        }, 1000);
        return {
          name,
          age,
        };
      },
    });
    </script>
    

    此时数据被ref包裹成为vue响应式数据,页面也可以正常更新。
    在这里插入图片描述

    此时一个响应式的ref数据便完成了,此时再回去看为什么要通过.value的形式获取值,先来看一下refImpl,全称是reference Implement,可以理解成引用对象,来看一下RefImpl关键源码

    class RefImpl<T> {
      private _value: T
      public readonly __v_isRef = true
      constructor(private _rawValue: T, private readonly _shallow = false) {
        this._value = _shallow ? _rawValue : convert(_rawValue)
      }
      get value() {
        track(toRaw(this), TrackOpTypes.GET, 'value')
        return this._value
      }
      set value(newVal) {
        if (hasChanged(toRaw(newVal), this._rawValue)) {
          this._rawValue = newVal
          this._value = this._shallow ? newVal : convert(newVal)
          trigger(toRaw(this), TriggerOpTypes.SET, 'value', newVal)
        }
      }
    }
    

    可以看见RefImpl class传递了一个泛型类型T,做了如下操作

    1. 申明一个私有属性 _value 内容为泛型T,申明了一个公开只读属性__v_isRef值为true

    2. 有一个构造函数constructor,用于构造对象。构造函数接受两个参数:
      第一个参数_rawValue,要求是T类型,第二个参数_shallow,默认值为true

    3. 提供了两个方法,get value(){}和set value(){},分别对应私有属性的读写操作,用于供外界操作value

    当通过它构建对象时,会给对象的_value属性赋值为_rawValue或者convert(_rawValue)
    再看convert源码如下:

    const convert = <T extends unknown>(val: T): T =>
      isObject(val) ? reactive(val) : val
    

    最终Vue会根据传入的数据是不是对象isObject(val),如果是对象本质调用的是reactive,否则返回原始数据。
    现在思考一个问题,通过ref包装的结果,当原始数据改变时会触发界面更新吗?即原始数据和返回的响应式数据是否有关联?
    修改一段代码:

    <template>
      <p>个人信息</p>
      <p>姓名:{{ name }}</p>
      <p>年龄:{{ age }}</p>
      <button @click="add">++</button>
    </template>
    
    <script lang="ts">
    import { defineComponent, ref } from "vue";
    export default defineComponent({
      name: "Home",
      components: {},
      setup() {
        let name = ref("张三");
        let age = 18;
        let curAge = ref(age);
        console.log("name", name);
        const add = () => {
          age++;
          console.log("age", age);
          console.log("curAge", curAge.value);
        };
        return {
          name,
          age,
          add,
        };
      },
    });
    </script>
    
    

    再打印一下
    在这里插入图片描述
    实例发现,当原始数据发生修改时,并不会影响响应式数据,更不会触发界面UI的更新。
    再修改一段代码,让curAge.value++

    <template>
      <p>个人信息</p>
      <p>姓名:{{ name }}</p>
      <p>年龄:{{ curAge }}</p>
      <button @click="add">++</button>
    </template>
    
    <script lang="ts">
    import { defineComponent, ref } from "vue";
    export default defineComponent({
      name: "Home",
      components: {},
      setup() {
        let name = ref("张三");
        let age = 18;
        let curAge = ref(age);
        console.log("name", name);
        const add = () => {
          curAge.value++;
          console.log("age", age);
          console.log("curAge", curAge.value);
        };
        return {
          name,
          age,
          curAge,
          add,
        };
      },
    });
    </script>
    
    

    打印如下
    在这里插入图片描述
    实例发现如果响应式数据发生改变,对应界面UI是会自动更新的,注意不影响原始数据
    总结
    小结一下:

    1. ref本质是将一个数据变成一个对象,这个对象具有响应式特点
    2. ref接受的原始数据返回的对象本质都是RefImpl类的实例
    3. 无论传入的原始数据时什么类型,当原始数据发生改变时,并不会影响响应数据,更不会触发UI的更新。但当响应式数据发生改变,对应界面UI是会自动更新的,注意不影响原始数据。所以ref中,原始数据和经过ref包装后的响应式数据是无关联的。
    展开全文
  • Vue3入门到精通--ref以及ref相关函数

    万次阅读 多人点赞 2020-10-27 14:58:38
    在Vue2.x通过给元素添加ref=‘xxx’,,然后使用refs.xxx的方式来获取元素 在Vue3.x中我们也可以通过ref来获取元素 用法 创建变量 import { ref } from 'vue' const count = ref(0) console.log(coun

    大家好,我是半夏👴,一个刚刚开始写文的沙雕程序员.如果喜欢我的文章,可以关注➕ 点赞 👍 加我微信:frontendpicker,一起学习交流前端,成为更优秀的工程师~关注公众号:搞前端的半夏,了解更多前端知识!点我探索新世界!

    扫码或搜索添加文末公众号「搞前端的半夏」:
    🍗 硬核资料:领取1000+PPT模板、100+简历模板、行业经典书籍PDF。
    🍗 回复 ”网站模板“,免费送网站模板!
    🍗 回复 ”面试“:免费送你面试题库!
    🍗 加我:frontendpicker, 更多精彩等你来!
    🍗 回复[算法],获取各种算法资料!

    系列文章目录

    1. Vue3入门到精通-setup
    2. Vue3入门到精通–ref以及ref相关函数
    3. Vue3入门到精通–reactive以及reactive相关函数

    创作不易 拒绝白嫖 点个赞呗
    关注我,带你走进前端的世界!!!


    是什么

    1. 将基础数据–>响应式数据 ==> 把值类型的数据包装编程响应式的引用类型的数据

    2. 函数

    3. 通过返回值的 value 属性获取响应式的值 ,修改也需要对 .value进行修改。

    4. 获取元素

      在Vue2.x通过给元素添加ref=‘xxx’,,然后使用refs.xxx的方式来获取元素

      在Vue3.x中我们也可以通过ref来获取元素

    用法

    创建变量

    import { ref } from 'vue'
    
    const count = ref(0)
    
    console.log(count.value) // 0
    
    count.value = 2
    
    console.log(count.value) // 2
    

    ref也可以接收复杂的数据类型作为参数,只是建议不使用ref处理复杂类型数据。

    在单文件组件中,不必写value,因为setup方法会自动解析

    <template>
      <div>
        <span>{{ count }}</span>
        <button @click="count ++">Increment count</button>
      </div>
    </template>
    

    获取元素

    <template>
        <div ref="refDiv">我是div</div>
    </template>
    
    <script>
    import {ref, onMounted} from 'vue';
    export default {
        name: 'App',
        setup() {
            let refDiv = ref(null); 
            onMounted(()=>{
                console.log('onMounted',refDiv.value);
                //  onMounted <div style=​"color:​ red;​">​我是div​</div>​
                refDiv.value.style.color="red"
                // 字体颜色变成红色
            });
    
            // setup 生命周期在mounted之前
            console.log(box.value);
            // null
    
            return {box};
        }
    }
    </script>
    

    ref和reactive的区别

    ref是把值类型添加一层包装,使其变成响应式的引用类型的值。

    reactive 则是引用类型的值变成响应式的值。

    所以两者的区别只是在于是否需要添加一层引用包装

    本质上

     ref(0)  --> reactive( { value:0 })
    

    注意点

    ref 对于 基本类型

    ref-创建出来的数据和以前无关(复制) 与js中的基本类型表现一致

    let a = 1;
    let aRef = ref(a);
    console.log(a, aRef.value); //1 1
    
    
    a = 2;
    console.log(a, aRef.value);// 2 1
    
    aRef.value = 3;
    console.log(a, aRef.value);// 2 3
    

    ref 对于 引用数据类型

    ref-创建出来的数据和以前相关(引用) 与js中的引用数据类型表现一致

    let obj = { name: "1" };
    let stateRef = ref(obj);
    console.log("obj", obj.name);
    console.log("ref", stateRef.value.name);
    // obj 1
    // ref 1
    
    stateRef.value.name = '2';
    console.log("obj", obj.name);
    console.log("ref", stateRef.value.name);
    // obj 2
    // ref 2
    
    
    obj.name='3'
    console.log("obj", obj.name);
    console.log("ref", stateRef.value.name);
    // obj 3
    // ref 3
    

    相关API

    isRef

    作用

    判断是都是ref对象

    其实内部是判断数据对象上是否包含__v_isRef 属性并且其值为 true

    用法

    const a = ref(a)
    const b = 'b'
    cosnole.log(isRef(a)) // true
    console.log(isRef(b)) // false
    

    unref

    如果参数为 ref,则返回内部值,否则返回参数本身。这是 val = isRef(val) ? val.value : val

    const temp=ref(3)  
    const newTemp = unref(1) // newTemp 确保现在是数字类型 3
    
    const a = unref(1) // a 确保现在是数字类型 1
    
    

    toRef

    作用

    引用数据类型转换为ref数据类型

    将reavtive数据类型转换为ref数据类型

    用法

    引用数据类型
    let obj = { name: "syl", age: "123" };
    let stateToref = toRef(obj, "name"); // 将name拿出来
    
    stateToref.value = "zs";
    console.log("obj", obj.name);
    console.log("ref", stateToref.value);
    // obj zs
    // ref zs
    
    
    obj.name = "ls";
    console.log("obj", obj.name);
    console.log("ref", stateToref.value);
    // obj ls
    // ref ls
    
    
    reactive数据类型
    let obj = reactive({ name: "syl", age: "123" });
    let stateToref = toRef(obj, "name"); // 将name拿出来
    
    stateToref.value = "zs";
    console.log("obj", obj.name);
    console.log("ref", stateToref.value);
    // obj zs
    // ref zs
    
    
    obj.name = "ls";
    console.log("obj", obj.name);
    console.log("ref", stateToref.value);
    // obj ls
    // ref ls
    

    注意点

    数据发生改变, 界面也不会自动更新

    案例1
    <p>toref----------{{ stateToref }}</p> // 这里显示的是zs
    
    let obj = { name: "syl" };
    let stateToref = toRef(obj, "name");
    
    stateToref.value = "zs";
    console.log("obj", obj.name);
    console.log("ref", stateToref.value);
    // obj zs
    // ref zs
    
    
    案例2
    <p>toref----------{{ stateToref }}</p>
    <button @click="changeToref">changeToref</button>   
    
    
    let obj = { name: "syl" };
    let stateToref = toRef(obj, "name");
    
    function changeToref() {
        stateToref.value = "ls";
        console.log("obj", obj.name);
        console.log("toref", stateToref.value);
    }
    // 点击changeToref,页面没有任何变化,仍然显示syl
    // console的结果是
    // obj ls
    // toref ls
    

    一个有意思的例子

       <p>toref----------{{ stateToref }}</p>
        <p>temp----------{{ temp }}</p>
        <button @click="changeToref">changeToref</button>
    
    let obj = { name: "syl" };
    let stateToref = toRef(obj, "name");
    let temp = ref("我是ref");
    
    function changeToref() {
    temp.value = "我是ref我改变啦!";
    stateToref.value = "ls";
    }
    // 点击按钮,页面的ui从
    toref----------syl
    temp----------我是ref
    // 变成
    oref----------ls
    temp----------我是ref我改变啦!
    
    这里可以看到ref触发了ui更新,导致toref的值也进行了更新
    
    其实不把这个ref的更新写到这个函数里面,比如新建函数,也会导致这个现象
    
    这个现象对其他函数也出现,例如shallowRef。
    

    toRefs

    作用

    批量转换。将响应式对象转换为普通对象,会将传入对象的每个属性处理为 ref 的值。

    官网例子

    当从合成函数返回响应式对象时,toRefs 非常有用,这样消费组件就可以在不丢失响应性的情况下对返回的对象进行分解/扩散:

    function useFeatureX() {
      const state = reactive({
        foo: 1,
        bar: 2
      })
      // 返回时转换为ref
      return toRefs(state)
    }
    
    export default {
      setup() {
        // 可以在不失去响应性的情况下破坏结构
        const { foo, bar } = useFeatureX()
    
        return {
          foo,
          bar
        }
      }
    }
    

    shallowRef 与triggerRef

    作用

    shallowRef

    创建一个 ref,它跟踪自己的 .value 更改,但不会使其值成为响应式的。也就是对value进行更新

    才会触发页面的更新,但是如果是一个引用数据类型,只对改引用数据进行值的修改,则不会触发更新。

    案例- 基本数据类型
    <p>{{ state1 }}</p>
    <button @click="myFn1">基本数据类型</button>
    
    let state1 = shallowRef(1);
    function myFn1() {
    	state1.value = 2;
    }
    // 点击按钮,页面会显2
    // 也就是对value进行修改可以触发页面更新
    
    
    案例- 引用数据类型
    <p>{{ state.a }}</p>
    <p>{{ state.b.c }}</p>
    <button @click="myFn1">引用数据类型-直接修改value</button>
    <button @click="myFn2">引用数据类型-对数据进行修改</button>
    
    let state = shallowRef({
        a: "a",
        b: {
            c: "c",
        },
    });
    
    function myFn1() {
        state.value={
            a: "a-new",
            b: {
                c: "c-new",
            },
        }
    }
    // 点击mufun1 页面从
    a
    c
    // 变成
    a-new
    c-new
    // 由此可以看出直接对value进行修改可以触发页面更新
    
    function myFn2() {
        state.value.a = "1";
        state.value.b.c = "1";
        console.log(state.value.a , state.value.b.c )
    }
    // 点击mufun2 页面仍然显示
    a
    c
    // console的结果是1 1
    
    triggerRef

    通常与shallowRef 一起使用,主要是主动触发界面更新的

    参数是ref变量

    <p>{{ state.a }}</p>
    <p>{{ state.b.c }}</p>
    <button @click="myFn2">使用triggerRef</button>
    
    let state = shallowRef({
        a: "a",
        b: {
            c: "c",
        },
    });
    function myFn2() {
        state.value.a = "1";
        state.value.b.c = "1";
        triggerRef(state);
    }
    // 点击mufun2 页面变成
    1
    1
    
    

    customRef

    自定义ref

    返回一个ref对象,可以显式地控制依赖追踪和触发响应

    <template>
      <div>
        <p>{{age}}</p>
        <button @click="myFn">按钮</button>
      </div>
    </template>
    
    import {ref, customRef} from 'vue';
    function myRef(value) {
      return customRef((track, trigger)=>{
        return {
          get(){
            track(); // 告诉Vue这个数据是需要追踪变化的
            console.log('get', value);
            return value;
          },
          set(newValue){
            console.log('set', newValue);
            value = "我的年龄==="+newValue;
            trigger(); // 告诉Vue触发界面更新
          }
        }
      });
    }
    
      setup() {
        // let age = ref(18); // reactive({value: 18})
        let age = myRef(18);
        function myFn() {
          age.value += 1;
        }
        return {age, myFn}
      }
    }
    
    // 页面显示的是18
    // 点击button按钮后,变成了我的年龄===19
    // 注意点:
    // 不能在get方法中发送网络请求
    
    
    
    

    扫码或搜索添加文末公众号「半夏话前端」:
    🍗 硬核资料:领取1000+PPT模板、100+简历模板、行业经典书籍PDF。
    🍗 回复 ”网站模板“,免费送网站模板!
    🍗 回复 ”面试“:免费送你面试题库!
    🍗 加我:frontendpicker, 更多精彩等你来!

    展开全文
  • C#——ref

    千次阅读 2021-01-05 10:57:12
    C#——ref关键字 ref 关键字指示按引用传递的值。 它用在四种不同的上下文中: 1.在方法签名和方法调用中,按引用将参数传递给方法。 2.在方法签名中,按引用将值返回给调用方。 3.在成员正文中,指示引用返回值是否...
  • react中使用ref

    千次阅读 2022-04-25 10:51:03
    在react中不推荐直接操作DOM元素,我们可以通过ref来获取DOM元素。 下面介绍一下react中使用ref的三种方法: 1、字符串格式(官方不推荐) //标签中设置ref,值为字符串 <input type="text" ref="name"/> //...
  • 一文搞懂 React ref

    千次阅读 2020-12-02 08:43:00
    最近也开始研究React,这篇文章主要是讲述 Ref 相关的内容,如有错误请指正。ref 的由来在典型的 React 数据流中,props 是父组件与子组件交互的唯一方式。要修改一个子组...
  • vue ref :refIn my previous post, I implemented my first Vue3 component. 在上一篇文章中,我实现了我的第一个Vue3组件。 I implementend a very simple web app (roll the dice) where I had a “div” where ...
  • React中使用ref

    千次阅读 2021-11-14 16:19:18
    一、如何使用ref 在React的开发模式中,通常情况下不需要、也不建议直接操作DOM原生,但是某些特殊的情况,确实需要获取到DOM进行某些操作: 管理焦点,文本选择或媒体播放; 触发强制动画; 集成第三方 DOM 库; ...
  • 化妆品ref什么意思

    千次阅读 2021-05-08 16:33:20
    3、REF E114100 LOT197013是什么意思 和你没关系 REF是参考号 LOT是批次号 这两个数据一般都是用来应付工商局的 4、香水包装盒上写着ref.251702是什么意思 ref。就是reference。参考号。可以是商品号,或者合同号...
  • vue2 中 ref 的使用

    千次阅读 2021-11-14 22:41:32
    ref 是什么 ref 被用来给DOM元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上。 如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素; 如果用在子组件上,引用就指向组件实例。 获得了引用...
  • vue 设置动态 ref

    千次阅读 2022-06-28 15:15:28
    最近使用el-tabs组件遇到了一个问题:就是el-tabs中el-tab-pane是动态生成的,而且后续需要获取el-tab-pane中元素的ref,代码大概是下面的样子:
  • C++11的std::ref、std::cref源码解析

    万次阅读 多人点赞 2021-05-09 11:13:21
    C++11的std::ref、std::cref源码解析1、源码准备2、std::ref和std::cref的作用3、std::ref相关源码解析3.1、std::ref解析3.1、std::reference_wrapper解析3.3、std::remove_cv解析3.4、std::_Reference_wrapper_base...
  • ref与shallowRef区别

    千次阅读 2022-03-24 17:31:37
    vue3组合式API-ref与shallowRef区别
  • Vue学习之ref属性

    千次阅读 2022-02-14 21:36:28
    主要讲解vue中的ref属性。 直接先说结论: 专门被用来给html元素和子组件注册引用信息的(id属性的替代者)。 该属性应用在html元素时,获取的是dom元素对象,如果应用在组件标签上,那么获取的是组件实例对象(vc...
  • vue3中效率为什么会提升很多,composition-api 和ref reactive、setup 等详解,带你更快的学习,使用vue3.0。
  • vue中ref的使用

    千次阅读 2022-04-15 20:51:04
    于是vue可以帮我们解决这个问题,vue也有自带的获取DOM的方法,那就是ref。它不仅可以获取DOM元素还可以获取组件。 一、获取DOM,操作DOM 1、给dom节点记上ref属性,可以理解为给dom节点起了个名字。 2、加上ref...
  • react中ref使用方法解析

    千次阅读 2022-03-17 14:55:29
    如果需要从组件中获取真实的DOM节点,那就需要官方提供ref的属性。React提供了ref用于访问在render方法中创建的DOM元素或者是React的组件实例。 ref分为三种,我们这里先学两种。 string类型 看下面的小例子:点击...
  • 【C#基础】ref 和 out 的区别

    千次阅读 2022-03-08 20:59:47
    因此,当一个方法需要返回多个值的时候,就需要用到ref和out。 正确的使用ref: class Program { static void Main(string[] args) { Program pg = new Program(); int x = 10; int y = 20; pg.GetVal
  • C++ std::ref————详解

    千次阅读 2022-04-05 10:01:26
    想学习ref,必须先学习reference_rapper 1、是什么? ref是个函数模板: 用来构建一个reference_wrapper对象并返回,该对象拥有传入的elem变量的引用。如果参数本身是一个reference_wrapper类型的对象,则创建该...
  • 对SV中的ref关键字的理解

    千次阅读 2022-05-04 17:21:31
    绿皮书《systemverilog验证——测试平台编写指南》97页,有对ref端口的简单描述: ref端口是对变量(不能是net)的引用,它的值是该变量最后一次赋值。 并没有给出具体使用场景,那上边这句话要怎么理解呢? ...
  • vue 3.0新特性之reactive与ref

    千次阅读 2020-07-30 14:02:03
    const count = ref(0) const state = reactive({ count }) console.log(state.count) // 0 state.count = 1 console.log(count.value) // 1 reactive属性绑定新的ref对象后,原来ref的value不变(断开了)。...
  • 众所周知在vue2中,ref主要的作用还是便于快速的获取dom元素或组件,因为ref操作相比document.getElementbyId会减少dom操作的节点消耗。 具体关于vue2中ref和$refs的使用,这篇文章写的十分全面: ...
  • c++ 中ref关键字的应用

    千次阅读 2021-08-27 16:32:49
    来实现引用 ,那为什么还会出现ref 呢? ref()方法的返回值是reference_wrapper类型,这个类的源码大概的意思就是维持一个指针,并且重载操作符。 ref()能用包装类型reference_wrapper来代替原本会被识别的值类型,...
  • vue3之定义数据(Ref全家桶)

    千次阅读 2022-03-03 16:33:53
    目录vue3之定义数据只是用于展示的数据(非响应式数据)响应式数据 (Ref全家桶)定义简单数据类型判断是否为 ref对象(定义为响应式数据对象)shallowRef 定义简单数据类型的响应式customRef实现类似计算属性封装一个防抖...
  • React函数式组件使用Ref

    千次阅读 2021-06-15 10:47:26
    大家都知道React中的ref属性可以帮助我们获取子组件的实例或者Dom对象,进而对子组件进行修改,是一个很方便的特性。在传统类组件中,我们通过使用React.createRef()创建的,并通过ref属性附加到React 元素来使用。...
  • ref 的三种用法

    万次阅读 2020-02-27 01:25:08
    ref 有三种用法:  1、ref 加在普通的元素上,用this.$refs.(ref值) 获取到的是dom元素 2、ref 加在子组件上,用this.refs.(ref值)获取到的是组件实例,可以使用组件的所有方法。在使用方法的时候直接this...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 954,040
精华内容 381,616
关键字:

ref