vue3.0 简单笔记

// 项目初始化

转换网址: https://vue-next-template-explorer.netlify.app

// 安装cli

npm install -g @vue/cli

// 初始化vue

vue create vue-next-test

// 使用 vite 快速创建

npm init @vitejs/app hello-vue3 

*** 推荐使用 ·vite· vite 是vue3中专用! 打包加载更快

vue源码转换网址: https://vue-next-template-explorer.netlify.app

// watch 跟coumputed

    watch(() => count.value, 
      val => {
        console.log(`count is ${val}`)
      })

** 第一个参数是监听的值,count.value 表示当 count.value 发生变化就会触发监听器的回调函数,即第二个参数,第二个参数可以执行监听时候的回调

// getCurrentInstance 获取当前的路由

import  { getCurrentInstance } from 'vue'
export default {
    setup () {
        const {ctx} = getCurrentInstance()
        console.log(ctx.$router.currentRoute.value)
    }

// Vuex的集成

// 定义vuex
import  Vuex from 'vuex'
export default Vuex.ceeateStore({
    state: {
        test: {
            a: 1
        }
    },
    mutations: {
        // 添加了修改 state.test.a 状态的方法: setTestA
        setTestA(state, value) {
            state.test.a = value
        }
    },
    actions: {
    },
    modules: {
    }
})
// vuex引用
<template>
    <div class='test'>
        <h1>test: count: {{count}}</h1>
        <div>count * 2 = {{doubleCount}}</div>
        <div>state  from vuex => {{a}}</div>
        <div @click='add'>add</div>
    </div>
</template>
<script>
    import { ref, computed, watch, getCurrentInstance } from 'vue'
    export default {
        serup () {
            const count = ref(0)
            const add = () => {
                count.value++
            }
        }
        watch(() => count.value, val => {
            console.log(`count is ${val}`)
        })
        const doubleCount = computed(() => count.vaule * 2)
        const { ctx } = getCurrentInstance()
        console.log(ctx.$router.currentRoute.value)
        //   通过计算属性来引用vuex中的状态   
        //   $store 是  vuex的文件
        const a = computed(() => ctx.$store.state.test.a)
        return {
            count,
            doubleCount,
            add,
            a
        }
    }
</script>
// 更新vuex的状态

更新 Vuex 状态仍然使用 commit 方法

<template>
    <div class='test'>
        <h1>test: count: {{count}}</h1>
        <div>count * 2 = {{doubleCount}}</div>
        <div>state  from vuex => {{a}}</div>
        <div @click='add'>add</div>
        <div @click='update'>update a</div>
    </div>
</template>
<script>
    import { ref, computed, watch, getCurrentInstance } from 'vue'
    export default {
        serup () {
            const count = ref(0)
            const add = () => {
                count.value++
            }
        }
        watch(() => count.value, val => {
            console.log(`count is ${val}`)
        })
        const doubleCount = computed(() => count.vaule * 2)
        const { ctx } = getCurrentInstance()
        console.log(ctx.$router.currentRoute.value)
        //   通过计算属性来引用vuex中的状态   
        //   $store 是  vuex的文件
        const a = computed(() => ctx.$store.state.test.a)
        const update = () => {
            ctx.$store.commit('setTestA', count)
        }
        return {
            count,
            doubleCount,
            add,
            a,
            update
        }
    }
</script>
// vue3.0优化点:
// diff方法的优化
DOM渲染时,仅更新变化的DOM
// hoistStatic 静态提升 和 cacheHandlers 事件侦听存储
    对于不更新的元素,只会创建一次!
<!-- 原生文件 -->
<div>Hello World!</div>
<div>{{msg}}</div>
<div @click=fn()>点击</div>
<sctipt>
    /* vue2.0 */
    export function render(_ctx, _cache, $props, $setup, $data, $options) {
        return (_openBlock(), _createBlock(_Fragment, null, [
        _createVNode("div", null, "Hello World!"),
        _createVNode("div", null, _toDisplayString(_ctx.msg), 1 /* TEXT */),
        _createVNode("div", {
        onClick: $event => (_ctx.fn())
        }, "点击", 8 /* PROPS */, ["onClick"])
        ], 64 /* STABLE_FRAGMENT */))
    }
    /* vue3.0 */
    const _hoisted_1 = /*#__PURE__*/_createVNode("div", null, "Hello World!", -1 /* HOISTED */)
    export function render(_ctx, _cache, $props, $setup, $data, $options) {
      return (_openBlock(), _createBlock(_Fragment, null, [
        _hoisted_1,
        _createVNode("div", null, _toDisplayString(_ctx.msg), 1 /* TEXT */),
        _createVNode("div", {
          onClick: _cache[1] || (_cache[1] = $event => (_ctx.fn()))
        }, "点击")
      ], 64 /* STABLE_FRAGMENT */))
    }
// 静态提升即: 将未变化的进行变量提升 `_hoisted_1` 
</sctipt>
    **附录:** PathFlags vue装换状态
export const enum PatchFlags {
    TEXT = 1, // 动态文本节点
    CLASS = 1 << 2, // 2 // 动态 class
    STYLE = 1 << 2, // 4 // 动态 style
    PROPS = 1 << 3, // 8 // 动态属性,但不包含类名和样式
    FULL_PROPS = 1 << 4, // 16 // 具有动态 key 属性,当 key 改变时,需要进行完整的 different 比较
    HYDRATE_EVEBNTS = 1 << 5, // 32 // 带有简体昂是案件
    STABLE_FRAGMENT = 1 << 6, // 64 // 一个不会改变子节点顺序的 fragment
    KEYED_FRAGMENT = 1 << 7, // 128 // 带有 key 属性的 fragment 或部分子节点带有 key
    UNKEYED_FRAGMENT = 1 << 8, // 256 // 子节点没有 key 的 fragment
    NEED_PATCH = 1 << 9, // 512 // 一个节点只会进行非 props 比较
    DYNAMIC_SLOTS = 1 << 10, // 1024 // 动态 slot
    HOISTED = -1, // 静态节点 
    BAIL = -2 // 指令在 diff 过程应该要退出优化模式
}

// Vite 项目管理

安装: `npm install -g create-vite-app`

创建: `create-vite-app projectName`

安装依赖运行项目:

        `cd projectName`

        `npm install`

        `npm run dev`

// 组合api

**`ref:`**  ref 函数系只能监听简单的类型数据的变化(单个值的)
  • *所有的变量和方法都需要通过 return 暴露出去,才能使用
// 引入:
import {ref} from 'vue'
export default {
    name: 'app',
    setup () {
        // 表示声明了一个 count 的变量,并赋值为 2
        let count = ref(2)
        return {ref}
    }
}

reactive: reactive 函数系能监听复杂的类型数据的变化(object, array)

import { reactive } from "vue";
export default {
  name: "App",
  // setup 是组合API的入口函数 setup 是在 beforecreate 钩子之前完成的
  setup() {
    // 定义一个 count 的变量
    let stuList = reactive({
      stus: [
        { age: 19, name: "小明", id: "s1" },
        { age: 16, name: "小憨憨", id: "s2" },
        { age: 18, name: "小涵", id: "s3" },
      ],
    });
    let addStu = reactive({
      newStu: {
        age: "",
        name: "",
        id: "",
      },
    });
    function del(id) {
      stuList.stus = stuList.stus.filter((ele) => ele.id !== id);
    }
    function add() {
      let obj = Object.assign({}, addStu.newStu);
      stuList.stus.push(obj);
      addStu.newStu.age = "";
      addStu.newStu.name = "";
      addStu.newStu.id = "";
    }
    // 组合API中定义的方法与变量都需要 return 暴露出去
    return { stuList, del, addStu, add };
  },
};

// Composition API 和 Option API

1、 Composition API 和 Option API 的混合使用

import {ref} from 'vue'
export default {
    // option  API
    name: "HelloWorld",
    props: {
        msg: String,
    },
    data() {
        return {
            count: 0,
        };
    },
    methods: {
        add() {
            count++;
        },
        // Composition API 中的方法与属性 相当于 在 option 中添加新的属性和 方法
        //  comit () {
        //      count ++  // Composition API 插入的
        //  }
        // 
    },
    setup () {
        // 表示声明了一个 count 的变量,并赋值为 2
        let count = ref(2)
        let function comit () {
            count ++ 
        }
        return {ref, comit}
    }
}      
2、 Composition API 的本质(组合API / 注入API)
    Composition api的本质是将其代码 注入到 Option API 中

// setup 函数

1、 setup 执行时机
     `setup`  函数的执行 是在  `beforecreate` 之前就已经完成 
2、 setup注意点
  • 由于在 setup 函数创建的时候 datamethods 还没创建,故而 无法使用; 因此 在 setup 函数中的 thisundefined
  • setup 函数不能包含异步函数

// reactive 函数

**` reactive:`** 

    **作用:**是vue3种提供实现响应式数据的方法;

    **原理:**在vue2中 响应数据是通过 `defineProperty` 来实现的,vue3是通过而是ES6中的 `Proxy` 来实现的;
  • 注意点 :

    • reactive 的参数传递 必须是一个对象(json / array)
    import { reactive } from "vue";
    export default {
      name: "App",
      // setup 是组合API的入口函数 setup 是在 beforecreate 钩子之前完成的
      setup() {
        let addStu = reactive({
          newStu: {
            age: "",
            name: "",
            id: "",
          },
        });
        return { addStu };
      },
    };
    
    • 如果给 reactive 传递了其他对象(指: 非 json 和 array 对象),此时,修改对象则 不更新,需要通过重新赋值的方式。
    import { reactive } from "vue";
    export default {
      name: "App",
      // setup 是组合API的入口函数 setup 是在 beforecreate 钩子之前完成的
      setup() {
        let state = reactive({
          time: new Date()
          },
          function comitFn () {
             // 获取时间    
             let newTime = new Date(state.time.getTime())
             //     
             newTime.setDate(state.time.getDate + 1)
             state.time = newTime
          }
        });
        return { state, comitFn };
      },
    };
    

// ref 函数

**`ref:`** 是veu3用于响应简单数据的方法;

**特点:** 是对原始数据进行复制 ,修改时并不修改原始数据
let per = {
      name: "ls",
      age: 12,
    };
    let data = ref(per);
    data.name = "ww";
    console.log(data, per);
    // data => RefImpl {_rawValue: {…}, _shallow: false, __v_isRef: true, _value: Proxy, name: "ww"}
    // per => {name: "ls", age: 12}
**本质:** `ref`  的底层仍是 `reactive` 系统会根据我们 `ref` 传入的值转换成 `ref(xx) => reactive({value: xx})`
  • 注意点:

    在vue中使用 ref 的值不用通过 value 获取;在 JS 中使用 ref 的值必须通过value获取

      即: 在DOM 中  ref 设置的值  不需要用value来获取, 但在修改时需要用valuel来进行设置
    
// 引入:
import {ref} from 'vue'
export default {
   name: 'app',
   setup () {
       // 表示声明了一个 count 的变量,并赋值为 2
       let count = ref(2)
       function change () {
           // 在DOM 中  ref 设置的值  不需要用value来获取, 但在修改时需要用valuel来进行设置
           count.value = 111
       }
       return {ref, count}
   }
}
// ref 和 reactive 的区别:
`ref` 在给DOM赋值时,是不需要添加 `.value` ;

`rective` 在给DOM赋值时,必须添加 `.value`

Vue中 是通过 __v_reftrue 则为 ref

// 判断 ref 和 reactive 的类型
**`ref:`**  isRef()
import {ref} from 'vue'
export default {
    name: 'app',
    setup () {
        // 表示声明了一个 count 的变量,并赋值为 2
        let count = ref(2)
        function change () {
            // 在DOM 中  ref 设置的值  不需要用value来获取, 但在修改时需要用valuel来进行设置
            count.value = 111
            console.log(isRef(count)) // => true
        }
        return {ref, count}
    }
}
**`reactive:`** isReactive()
import {reactive} from 'vue'
export default {
    name: 'app',
    setup () {
        // 表示声明了一个 count 的变量,并赋值为 2
        let count = reactive({
            num: 1
        })
        function change () {
            // 在DOM 中  ref 设置的值  不需要用value来获取, 但在修改时需要用valuel来进行设置
            count.num = 111
            console.log(isReactive(count)) // => true
        }
        return {ref, count}
    }
}

// 递归监听

**递归监听:** 监听所有层级的数据,并依层级 依次修改,断层则断层后的数据不变。

**非递归监听:** 仅监听第一层级的修改

默认情况下, refreactive 都是进行递归监听的

即是外到内依次监听

<template>
  <div>
    reactive 熏染: <br>
    <div>{{state.a}}</div>
    <div>{{state.gf.b}}</div>
    <div>{{state.gf.f.c}}</div>
    <div>{{state.gf.f.s.d}}</div>
    <div @click="fn" class="btn">点击</div>
    <hr>
    ref 渲染 <br>

    <div>{{obj.a}}</div>
    <div>{{obj.gf.b}}</div>
    <div>{{obj.gf.f.c}}</div>
    <div>{{obj.gf.f.s.d}}</div>

    <div @click="fn1" class="btn">点击</div>
  </div>
</template>

<script>
import { reactive, ref } from "vue";
export default {
  name: "HelloWorld",
  setup() {
    // 递归监听:即是外到内依次监听
    // reactive
    let state = reactive({
      a: "a",
      gf: {
        b: "b",
        f: {
          c: "c",
          s: {
            d: "d",
          },
        },
      },
    });
    function fn() {
      state.a = 1;
      state.gf.b = 2;
      state.gf.f.c = 3;
      state.gf.f.s.d = 4;
    }
    // ref
    let obj = ref({
      a: "a",
      gf: {
        b: "b",
        f: {
          c: "c",
          s: {
            d: "d",
          },
        },
      },
    });
    function fn1() {
      obj.value.a = 1;
      obj.value.gf.b = 2;
      obj.value.gf.f.c = 3;
      obj.value.gf.f.s.d = 4;
    }
    return { state, fn, obj, fn1 };
  },
};
</script>
<style lang="">
.btn {
  margin: 20px auto;
  padding: 5px 10px;
  border: 1px solid;
}
</style>
// 劣势
数据大时,损耗性能

    原由: 在vue3中总共的数据渲染是由 `Proxy`  对象

        [`Proxy:`](https://www.jianshu.com/p/8b0834b51183)

// 非递归监听

**特点:**

`reactive 由 shallowReactive 进行创建`   **只能监听第一层,不监听其它层**

`ref 由 shallowRef 进行创建` **`但 shallowRef 监听的是 .value ,因此需要修改时是修改其 .value 的值`**

`triggerRef 是修改某一个值的变化` 只有 `ref` 才有 `triggerRef` 的方法
state.value.gf.f.s.d = 4
triggerRef(state)
<template>
  <div>
    shallowReactive 熏染: <br>
    <div>{{state.a}}</div>
    <div>{{state.gf.b}}</div>
    <div>{{state.gf.f.c}}</div>
    <div>{{state.gf.f.s.d}}</div>
    <div @click="fn" class="btn">点击</div>
    <hr>
    shallowRef 渲染 <br>

    <div>{{obj.a}}</div>
    <div>{{obj.gf.b}}</div>
    <div>{{obj.gf.f.c}}</div>
    <div>{{obj.gf.f.s.d}}</div>

    <div @click="fn1" class="btn">点击</div>
  </div>
</template>

<script>
import { shallowReactive, shallowRef, triggerRef } from "vue";
export default {
  name: "HelloWorld",
  mounted() {},
  methods: {},
  setup() {
    // 递归监听:即是外到内依次监听
    // shallowReactive
    let state = shallowReactive({
      a: "a",
      gf: {
        b: "b",
        f: {
          c: "c",
          s: {
            d: "d",
          },
        },
      },
    });
    function fn() {
      state.a = 1;
      state.gf.b = 2;
      state.gf.f.c = 3;
      state.gf.f.s.d = 4;
    }
    // shallowRef
    let obj = shallowRef({
      a: "a",
      gf: {
        b: "b",
        f: {
          c: "c",
          s: {
            d: "d",
          },
        },
      },
    });
    function fn1() {
      // obj.value = {
      //   a: "1",
      //   gf: {
      //     b: "2",
      //     f: {
      //       c: "3",
      //       s: {
      //         d: "4",
      //       },
      //     },
      //   },
      // };
      //  triggerRef() //修改单个值
      obj.value.gf.b = 2;
      triggerRef(obj);
    }
    return { state, fn, obj, fn1 };
  },
};
</script>
<style lang="">
.btn {
  margin: 20px auto;
  padding: 5px 10px;
  border: 1px solid;
}
</style>
// shallowRef 的本质
`shallowRef` 是通过 `shallowReactive` 的方式进行创建的 故而 是 通过修改 value 的值才能修改

// toRaw 追踪数据(不改DOM)

特点: 是修改原始数据,但不更新 DOM
  • **toRaw: ** 用于获取 ref 中的数据的时候,由于其底层是通过 reactive 的方式来获取的,故而是需要通过 .value 的方式来获取

    let per = {
          name: "ls",
          age: 12,
        };
    let state = reactive(per);
    let data = ref(per);
    let per1 = toRaw(state);
    let per2 = toRaw(per1.value);
    

// markRaw 标记后永不追踪

let per = {
      name: "ls",
      age: 12,
    };
let per1 = markRaw(per);
let state = reactive(per);
state.name = 'ww'
console.log(per) // { name: "ls", age: 12, };
console.log(state) // 此时 state 追踪不到 per

// toRef

**特点:** 通过 `toRef`  修改的数据, 不能修改DOM, 但会修改原始引用数据; 跟 `ref` 一样都需要通过  `.value` 的形式来修改值
let per = {
      name: "ls",
      age: 12,
    };
let data = toRef(per, "name");
data.value = "xx";
console.log(data, per);
    // data => (_v_isRef: true;_key: "name";_object: {name: "xx", age: 12, __v_skip: true};value: (...);__proto__: Object)
    // per => {name: "xx", age: 12, __v_skip: true}

// toRefs

toRef 的升级版,可以修改多个属性

let per = {
      name: "ls",
      age: 12,
    };
let data = toRefs(per);
data.name.value = "xx";
data.name.age = 25;
console.log(data, per);
// data => name: xx age: 25
// per => {name: "xx", age: 12, __v_skip: true}

// customRef 自定义ref

**作用:** 由于 `setup`  函数无法使用异步函数,用于获取异步数据
<template>
  <div>
    <div v-for="(ele, i) in state" :key="i" class="">{{ele.name}}</div>
    <div></div>
    <button @click="btn">点击</button>
  </div>
</template>

<script>
import { customRef, ref } from "vue";
function DIYRef(val) {
  return customRef((track, trigger) => {
    fetch(val)
      .then((res) => {
        return res.json();
      })
      .then((data) => {
        console.log(data);
        val = data;
        trigger(); // 标识 val 是需要更新的
      })
      .catch((err) => {
        console.log(err);
      });
    return {
      get() {
        console.log("get:" + val);
        track(); // 标识 val 是需要追踪的
        return val;
      },
      set(newVal) {
        console.log("set:" + val, "new" + newVal);
        val = newVal;
        trigger(); // 标识 val 是需要更新的
      },
    };
  });
}
export default {
  name: "HelloWorld",
  mounted() {},
  methods: {},
  setup() {
    let state = DIYRef("src/components/dome.json");
    return { state };
  },
};
</script>
<style lang="">
</style>

// readonly、shallowReadonly、isReadonly

**readonly:  ** 只能用于读取不可修改 , 并且是<u>递归只读</u>

**shallowReadonly:**    <u>非递归只读</u>,只读第一层!

**isReadonly:** 返回 `boolean`  是只读返回 `true`
import { readonly, shallowReadonly, isReadonly  } from "vue";
setup() {
   let state1 = readonly({ name: "小樱", attr: { age: 18, id: "sw2" } });
    let state2 = shallowReadonly({
      name: "小瑛",
      attr: { age: 18, id: "sw2" },
    });
    state1.name = "小志";
    state1.attr.age = 17;
    console.log(state1); // Proxy {name: "小樱", attr: {…}} attr => age: 18
    state2.name = "小志";
    state2.attr.age = 17;
    console.log(state2); // Proxy {name: "小樱", attr: {…}} attr => age: 17
    console.log(isReadonly(state1), isReadonly(state2)); // true  true
}
// const 与 readonly 的区别
**相同点:** 

        都是不能修改值
报错: 
let a = readonly({ num: 2 });           
    const b = { num: 3 };
    a = {age: 2};
    b = {age: 2};
    console.log(a, b);
**不同点:**

    cosnt 的属性值可以被修改 readonly 的属性和值均不能修改
let a = readonly({ num: 2 });
    const b = { num: 3 };
    a.num = 0;
    b.num = 5;
    console.log(a, b); // a: Proxy {num: 2} b: {num: 5}
// 手写 shallowReactive 和 shallowRef
function shallowRef(val) {
  return shallowReactive({ value: val });
}
function shallowReactive(obj) {
  return new Proxy(obj, {
    get(obj, key) {
      return obj[key];
    },
    set(obj, key, val) {
      console.log(obj, key, val);
      return (obj[key] = val);
    },
  });
}
let obj = {
  a: "a",
  gf: {
    b: "b",
    f: {
      c: "c",
      s: {
        d: "d",
      },
    },
  },
};
let newObj = shallowReactive(obj);
let newObj1 = shallowRef(obj);
newObj.a = 1;
newObj.gf.b = 2;
newObj1.value = {
  a: 1,
  gf: {
    b: "2",
    f: {
      c: "c",
      s: {
        d: "d",
      },
    },
  },
};
console.log(newObj, newObj1);

// 手写 reactive ,ref

// reactive ref
function ref(val) {
  return reactive({ value: val });
}
function reactive(obj) {
  // 查找出所有的对象并调用 reactive 没找到继续遍历
  if (typeof obj === "object") {
    // 内层有数组
    if (obj instanceof Array) {
      obj.forEach((ele, i) => {
        if (typeof ele === "object") {
          obj[i] = reactive(ele);
        }
      });
    } else {
      for (let key in obj) {
        let ele = obj[key];
        if (typeof ele === "object") {
          obj[key] = reactive(ele);
        }
      }
    }
    return new Proxy(obj, {
      get(obj, key) {
        return obj[key];
      },
      set(obj, key, val) {
        console.log(obj, key, val);
        return (obj[key] = val);
      },
    });
  } else {
    console.warn(`${obj} is not object`);
  }
}
let obj = {
  a: "a",
  gf: {
    b: "b",
    f: {
      c: "c",
      s: {
        d: "d",
      },
    },
  },
};
let arr = [
  { id: "s1", name: "小樱" },
  {
    id: "s2",
    name: "小埋",
    attr: [{ nickName: "干物妹!", title: "不吃辣椒酱" }],
  },
];
let newObj = reactive(obj);
newObj.a = 1;
let newObj1 = ref(obj);
newObj1.value = {
  a: 2,
  gf: {
    b: 222,
    f: {
      c: "c",
      s: {
        d: "d",
      },
    },
  },
};
console.log(newObj, newObj1);
let newArr = reactive(arr);
arr[1].attr[0].title = "资深宅女";
console.log(newArr);

// 手写 isReactive / isRef

根据 `ref`  的赋值 `.value`  的特性进行判断
<template>
</template>
<script>
// reactive ref
function ref(val) {
  return reactive({ value: val });
}
function reactive(obj) {
  // 查找出所有的对象并调用 reactive 没找到继续遍历
  if (typeof obj === "object") {
    // 内层有数组
    if (obj instanceof Array) {
      obj.forEach((ele, i) => {
        if (typeof ele === "object") {
          obj[i] = reactive(ele);
        }
      });
    } else {
      for (let key in obj) {
        let ele = obj[key];
        if (typeof ele === "object") {
          obj[key] = reactive(ele);
        }
      }
    }
    return new Proxy(obj, {
      get(obj, key) {
        return obj[key];
      },
      set(obj, key, val) {
        console.log(obj, key, val);
        return (obj[key] = val);
      },
    });
  } else {
    console.warn(`${obj} is not object`);
  }
}
function isReactive(obj) {
 return (obj.value ? false : true)
}
function isRef(obj) {
 return (!obj.value ? false : true)
}
let obj = {
  a: "a",
  gf: {
    b: "b",
    f: {
      c: "c",
      s: {
        d: "d",
      },
    },
  },
};
let arr = [
  { id: "s1", name: "小樱" },
  {
    id: "s2",
    name: "小埋",
    attr: [{ nickName: "干物妹!", title: "不吃辣椒酱" }],
  },
];
let newObj = reactive(obj);
newObj.a = 1;
let newObj1 = ref(obj);
console.log(isReactive(newObj))
console.log(isRef(newObj1))
newObj1.value = {
  a: 2,
  gf: {
    b: 222,
    f: {
      c: "c",
      s: {
        d: "d",
      },
    },
  },
};
console.log(newObj, newObj1);
let newArr = reactive(arr);
arr[1].attr[0].title = "资深宅女";
console.log(newArr);
export default {
  name: "App",
  setup() {},
};
</script>

// 手写 Readonly / shallowReadonly

function ref(val) {
  return reactive({ value: val });
}
// 注释掉的加上是 readonly
function shallowReadonly(obj) {
  // 查找出所有的对象并调用 reactive 没找到继续遍历
  // if (typeof obj === "object") {
  //   // 内层有数组
  //   if (obj instanceof Array) {
  //     obj.forEach((ele, i) => {
  //       if (typeof ele === "object") {
  //         obj[i] = shallowReadonly(ele);
  //       }
  //     });
  //   } else {
  //     for (let key in obj) {
  //       let ele = obj[key];
  //       if (typeof ele === "object") {
  //         obj[key] = shallowReadonly(ele);
  //       }
  //     }
  //   }
  return new Proxy(obj, {
    get(obj, key) {
      return obj[key];
    },
    set(obj, key, val) {
      // console.log(obj, key, val);
      // return (obj[key] = val);
      return console.warn(`${obj} 仅支持只读,无法赋值`);
    },
  });
  // } else {
  //   console.warn(`${obj} is not object`);
  // }
}
let obj = {
  a: "a",
  gf: {
    b: "b",
    f: {
      c: "c",
      s: {
        d: "d",
      },
    },
  },
};
let newObj = shallowReadonly(obj);
newObj.gf.b = 1;
// newObj.a = 2; // => 仅支持只读,无法赋值
console.log(newObj);

// 手写 isReadonly

根据  `readonly`  中的不返回值的输出 `true`
<template>
</template>
<script>
// isReadonly
function shallowReadonly(obj) {
  // 查找出所有的对象并调用 reactive 没找到继续遍历
  if (typeof obj === "object") {
    // 内层有数组
    if (obj instanceof Array) {
      obj.forEach((ele, i) => {
        if (typeof ele === "object") {
          obj[i] = shallowReadonly(ele);
        }
      });
    } else {
      for (let key in obj) {
        let ele = obj[key];
        if (typeof ele === "object") {
          obj[key] = shallowReadonly(ele);
        }
      }
    }
  return new Proxy(obj, {
    get(obj, key) {
      return obj[key];
    },
    set(obj, key, val) {
      // console.log(obj, key, val);
      // return (obj[key] = val);
      // return console.warn(`${obj} 仅支持只读,无法赋值`);
      return true
    },
  });
  } else {
    console.warn(`${obj} is not object`);
  }
}
function isReadonly (obj) {
  return (shallowReadonly(obj) ? true : false)
}
let obj = {
  a: "a",
  gf: {
    b: "b",
    f: {
      c: "c",
      s: {
        d: "d",
      },
    },
  },
};
let newObj = shallowReadonly(obj);
// newObj.a = 2; // => 仅支持只读,无法赋值
console.log(isReadonly(newObj));

export default {
  name: "App",
  setup() {},
};
</script>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容