// 項目初始化
轉(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)建的時候data
和methods
還沒創(chuàng)建桐猬,故而 無法使用麦撵; 因此 在setup
函數(shù)中的this
為undefined
-
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_ref
為 true
則為 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)情況下伯病,
ref
和reactive
都是進行遞歸監(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>