// 项目初始化
转换网址: 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
函数创建的时候data
和methods
还没创建,故而 无法使用; 因此 在setup
函数中的this
为undefined
-
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_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 设置的值 不需要用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}
}
}
// 递归监听
**递归监听:** 监听所有层级的数据,并依层级 依次修改,断层则断层后的数据不变。
**非递归监听:** 仅监听第一层级的修改
默认情况下,
ref
和reactive
都是进行递归监听的即是外到内依次监听
<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>