vue3.0 簡單筆記

// 項目初始化

轉(zhuǎn)換網(wǎng)址: https://vue-next-template-explorer.netlify.app

// 安裝cli

npm install -g @vue/cli

// 初始化vue

vue create vue-next-test

// 使用 vite 快速創(chuàng)建

npm init @vitejs/app hello-vue3 

*** 推薦使用 ·vite· vite 是vue3中專用闽铐! 打包加載更快

vue源碼轉(zhuǎn)換網(wǎng)址: https://vue-next-template-explorer.netlify.app

// watch 跟coumputed

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

** 第一個參數(shù)是監(jiān)聽的值,count.value 表示當(dāng) count.value 發(fā)生變化就會觸發(fā)監(jiān)聽器的回調(diào)函數(shù)释树,即第二個參數(shù)纵散,第二個參數(shù)可以執(zhí)行監(jiān)聽時候的回調(diào)

// getCurrentInstance 獲取當(dāng)前的路由

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 狀態(tài)的方法: 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中的狀態(tài)   
        //   $store 是  vuex的文件
        const a = computed(() => ctx.$store.state.test.a)
        return {
            count,
            doubleCount,
            add,
            a
        }
    }
</script>
// 更新vuex的狀態(tài)

更新 Vuex 狀態(tài)仍然使用 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中的狀態(tài)   
        //   $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優(yōu)化點:
// diff方法的優(yōu)化
DOM渲染時蕴掏,僅更新變化的DOM
// hoistStatic 靜態(tài)提升 和 cacheHandlers 事件偵聽存儲
    對于不更新的元素并徘,只會創(chuàng)建一次!
<!-- 原生文件 -->
<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 */))
    }
// 靜態(tài)提升即: 將未變化的進行變量提升 `_hoisted_1` 
</sctipt>
    **附錄:** PathFlags vue裝換狀態(tài)
export const enum PatchFlags {
    TEXT = 1, // 動態(tài)文本節(jié)點
    CLASS = 1 << 2, // 2 // 動態(tài) class
    STYLE = 1 << 2, // 4 // 動態(tài) style
    PROPS = 1 << 3, // 8 // 動態(tài)屬性像云,但不包含類名和樣式
    FULL_PROPS = 1 << 4, // 16 // 具有動態(tài) key 屬性剂癌,當(dāng) key 改變時淤翔,需要進行完整的 different 比較
    HYDRATE_EVEBNTS = 1 << 5, // 32 // 帶有簡體昂是案件
    STABLE_FRAGMENT = 1 << 6, // 64 // 一個不會改變子節(jié)點順序的 fragment
    KEYED_FRAGMENT = 1 << 7, // 128 // 帶有 key 屬性的 fragment 或部分子節(jié)點帶有 key
    UNKEYED_FRAGMENT = 1 << 8, // 256 // 子節(jié)點沒有 key 的 fragment
    NEED_PATCH = 1 << 9, // 512 // 一個節(jié)點只會進行非 props 比較
    DYNAMIC_SLOTS = 1 << 10, // 1024 // 動態(tài) slot
    HOISTED = -1, // 靜態(tài)節(jié)點 
    BAIL = -2 // 指令在 diff 過程應(yīng)該要退出優(yōu)化模式
}

// Vite 項目管理

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

創(chuàng)建: `create-vite-app projectName`

安裝依賴運行項目:

        `cd projectName`

        `npm install`

        `npm run dev`

// 組合api

**`ref:`**  ref 函數(shù)系只能監(jiān)聽簡單的類型數(shù)據(jù)的變化(單個值的)
  • *所有的變量和方法都需要通過 return 暴露出去,才能使用
// 引入:
import {ref} from 'vue'
export default {
    name: 'app',
    setup () {
        // 表示聲明了一個 count 的變量佩谷,并賦值為 2
        let count = ref(2)
        return {ref}
    }
}

reactive: reactive 函數(shù)系能監(jiān)聽復(fù)雜的類型數(shù)據(jù)的變化(object, array)

import { reactive } from "vue";
export default {
  name: "App",
  // setup 是組合API的入口函數(shù) 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 中的方法與屬性 相當(dāng)于 在 option 中添加新的屬性和 方法
        //  comit () {
        //      count ++  // Composition API 插入的
        //  }
        // 
    },
    setup () {
        // 表示聲明了一個 count 的變量,并賦值為 2
        let count = ref(2)
        let function comit () {
            count ++ 
        }
        return {ref, comit}
    }
}      
2谐檀、 Composition API 的本質(zhì)(組合API / 注入API)
    Composition api的本質(zhì)是將其代碼 注入到 Option API 中

// setup 函數(shù)

1抡谐、 setup 執(zhí)行時機
     `setup`  函數(shù)的執(zhí)行 是在  `beforecreate` 之前就已經(jīng)完成 
2、 setup注意點
  • 由于在 setup 函數(shù)創(chuàng)建的時候 datamethods 還沒創(chuàng)建桐猬,故而 無法使用麦撵; 因此 在 setup 函數(shù)中的 thisundefined
  • setup 函數(shù)不能包含異步函數(shù)

// reactive 函數(shù)

**` reactive:`** 

    **作用:**是vue3種提供實現(xiàn)響應(yīng)式數(shù)據(jù)的方法;

    **原理:**在vue2中 響應(yīng)數(shù)據(jù)是通過 `defineProperty` 來實現(xiàn)的溃肪,vue3是通過而是ES6中的 `Proxy` 來實現(xiàn)的;
  • 注意點 :

    • reactive 的參數(shù)傳遞 必須是一個對象(json / array)
    import { reactive } from "vue";
    export default {
      name: "App",
      // setup 是組合API的入口函數(shù) 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的入口函數(shù) 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 函數(shù)

**`ref:`** 是veu3用于響應(yīng)簡單數(shù)據(jù)的方法;

**特點:** 是對原始數(shù)據(jù)進行復(fù)制 厨钻,修改時并不修改原始數(shù)據(jù)
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}
**本質(zhì):** `ref`  的底層仍是 `reactive` 系統(tǒng)會根據(jù)我們 `ref` 傳入的值轉(zhuǎn)換成 `ref(xx) => reactive({value: xx})`
  • 注意點:

    在vue中使用 ref 的值不用通過 value 獲榷蟪;在 JS 中使用 ref 的值必須通過value獲取

      即: 在DOM 中  ref 設(shè)置的值  不需要用value來獲取莉撇, 但在修改時需要用valuel來進行設(shè)置
    
// 引入:
import {ref} from 'vue'
export default {
   name: 'app',
   setup () {
       // 表示聲明了一個 count 的變量呢蛤,并賦值為 2
       let count = ref(2)
       function change () {
           // 在DOM 中  ref 設(shè)置的值  不需要用value來獲取惶傻, 但在修改時需要用valuel來進行設(shè)置
           count.value = 111
       }
       return {ref, count}
   }
}
// ref 和 reactive 的區(qū)別:
`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 設(shè)置的值  不需要用value來獲取涂佃, 但在修改時需要用valuel來進行設(shè)置
            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 設(shè)置的值  不需要用value來獲取, 但在修改時需要用valuel來進行設(shè)置
            count.num = 111
            console.log(isReactive(count)) // => true
        }
        return {ref, count}
    }
}

// 遞歸監(jiān)聽

**遞歸監(jiān)聽:** 監(jiān)聽所有層級的數(shù)據(jù)辜荠,并依層級 依次修改汽抚,斷層則斷層后的數(shù)據(jù)不變。

**非遞歸監(jiān)聽:** 僅監(jiān)聽第一層級的修改

默認(rèn)情況下伯病, refreactive 都是進行遞歸監(jiān)聽的

即是外到內(nèi)依次監(jiān)聽

<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() {
    // 遞歸監(jiān)聽:即是外到內(nèi)依次監(jiān)聽
    // 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>
// 劣勢
數(shù)據(jù)大時造烁,損耗性能

    原由: 在vue3中總共的數(shù)據(jù)渲染是由 `Proxy`  對象

        [`Proxy:`](http://www.reibang.com/p/8b0834b51183)

// 非遞歸監(jiān)聽

**特點:**

`reactive 由 shallowReactive 進行創(chuàng)建`   **只能監(jiān)聽第一層,不監(jiān)聽其它層**

`ref 由 shallowRef 進行創(chuàng)建` **`但 shallowRef 監(jiān)聽的是 .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() {
    // 遞歸監(jiān)聽:即是外到內(nèi)依次監(jiān)聽
    // 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 的本質(zhì)
`shallowRef` 是通過 `shallowReactive` 的方式進行創(chuàng)建的 故而 是 通過修改 value 的值才能修改

// toRaw 追蹤數(shù)據(jù)(不改DOM)

特點: 是修改原始數(shù)據(jù)惭蟋,但不更新 DOM
  • **toRaw: ** 用于獲取 ref 中的數(shù)據(jù)的時候,由于其底層是通過 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 標(biāo)記后永不追蹤

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`  修改的數(shù)據(jù), 不能修改DOM, 但會修改原始引用數(shù)據(jù); 跟 `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`  函數(shù)無法使用異步函數(shù),用于獲取異步數(shù)據(jù)
<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(); // 標(biāo)識 val 是需要更新的
      })
      .catch((err) => {
        console.log(err);
      });
    return {
      get() {
        console.log("get:" + val);
        track(); // 標(biāo)識 val 是需要追蹤的
        return val;
      },
      set(newVal) {
        console.log("set:" + val, "new" + newVal);
        val = newVal;
        trigger(); // 標(biāo)識 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 的區(qū)別
**相同點:** 

        都是不能修改值
報錯: 
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) {
  // 查找出所有的對象并調(diào)用 reactive 沒找到繼續(xù)遍歷
  if (typeof obj === "object") {
    // 內(nèi)層有數(shù)組
    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

根據(jù) `ref`  的賦值 `.value`  的特性進行判斷
<template>
</template>
<script>
// reactive ref
function ref(val) {
  return reactive({ value: val });
}
function reactive(obj) {
  // 查找出所有的對象并調(diào)用 reactive 沒找到繼續(xù)遍歷
  if (typeof obj === "object") {
    // 內(nèi)層有數(shù)組
    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) {
  // 查找出所有的對象并調(diào)用 reactive 沒找到繼續(xù)遍歷
  // if (typeof obj === "object") {
  //   // 內(nèi)層有數(shù)組
  //   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

根據(jù)  `readonly`  中的不返回值的輸出 `true`
<template>
</template>
<script>
// isReadonly
function shallowReadonly(obj) {
  // 查找出所有的對象并調(diào)用 reactive 沒找到繼續(xù)遍歷
  if (typeof obj === "object") {
    // 內(nèi)層有數(shù)組
    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>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末贩虾,一起剝皮案震驚了整個濱河市催烘,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌缎罢,老刑警劉巖伊群,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異策精,居然都是意外死亡舰始,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進店門咽袜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來丸卷,“玉大人,你說我怎么就攤上這事询刹∶占担” “怎么了萎坷?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長沐兰。 經(jīng)常有香客問我哆档,道長,這世上最難降的妖魔是什么住闯? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任瓜浸,我火速辦了婚禮,結(jié)果婚禮上比原,老公的妹妹穿的比我還像新娘斟叼。我一直安慰自己,他們只是感情好春寿,可當(dāng)我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布朗涩。 她就那樣靜靜地躺著,像睡著了一般绑改。 火紅的嫁衣襯著肌膚如雪谢床。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天厘线,我揣著相機與錄音识腿,去河邊找鬼。 笑死造壮,一個胖子當(dāng)著我的面吹牛渡讼,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播耳璧,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼成箫,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了旨枯?” 一聲冷哼從身側(cè)響起蹬昌,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎攀隔,沒想到半個月后皂贩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡昆汹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年明刷,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片满粗。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡辈末,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情本冲,我是刑警寧澤,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布劫扒,位于F島的核電站檬洞,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏沟饥。R本人自食惡果不足惜添怔,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望贤旷。 院中可真熱鬧广料,春花似錦、人聲如沸幼驶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽盅藻。三九已至购桑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間氏淑,已是汗流浹背勃蜘。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留假残,地道東北人缭贡。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像辉懒,于是被迫代替她去往敵國和親阳惹。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,933評論 2 355

推薦閱讀更多精彩內(nèi)容