2019-09-04
JiangRen Mr
vue3.0选择了这个属性, 虽然也会提供兼容版本, 但基本也算是跟老版ie说再见了, Proxy会解决之前无法监听数组的修改这个痛点, 也算是我辈前端的福音了.
使用方面会有很大不同, defineProperty是监控一个对象, 而Proxy是返回一个新对象, 这就需要我完全重写Observer模块了, 话不多说先把基本功能演示一下.
由下面的代码可知:
let ary = [1, 2, 3, 4];
let proxy = new Proxy(ary, {
get(target, key) {
return target[key];
},
set(target, key, value) {
console.log('我被触发了');
return value;
}
});
console.log(Array.isArray(proxy)); // true
proxy.length = 1; // 我被触发了
我之前写的劫持模块就需要彻底改版了
cc_vue/src/Observer.js
改变$data指向我选择在这里做, 为了保持主函数的纯净.
// 数据劫持
import { Dep } from './Watch';
let toString = Object.prototype.toString;
class Observer {
constructor(vm, data) {
// 由于Proxy的机制是返回一个代理对象, 那我们就需要更改实例上的$data的指向了
vm.$data = this.observer(data);
}
}
export default Observer;
observer
对象与数组是两种循环的方式, 每次递归的解析里面的元素, 最后整个对象完全由Proxy组成.
observer(data) {
let type = toString.call(data),
$data = this.defineReactive(data);
if (type === '[object Object]') {
for (let item in data) {
data[item] = this.defineReactive(data[item]);
}
} else if (type === '[object Array]') {
let len = data.length;
for (let i; i < len; i++) {
data[i] = this.defineReactive(data[i]);
}
}
return $data;
}
defineReactive
遇到基本类型我会直接return;
代理基本类型还会报错😯;
defineReactive(data) {
let type = toString.call(data);
if (type !== '[object Object]' && type !== '[object Array]') return data;
let dep = new Dep(),
_this = this;
return new Proxy(data, {
get(target, key) {
Dep.target && dep.addSub(Dep.target);
return target[key];
},
set(target, key, value) {
if (target[key] !== value) {
// 万一用户付给了一个新的对象, 就需要重新生成监听元素了.
target[key] = _this.observer(value);
dep.notify();
}
return value;
}
});
}
Observer模块改装完毕
现在vm上面的data已经是Proxy代理的data了, 也挺费性能的, 所以说用vue开发的时候, 尽量不要弄太多数据在data身上.
这个属性也蛮有趣的, 它的出现很符合设计模式, 数据就是要有一套专用的处理方法, 而且函数式处理更符合js的设计理念.
下面把常用的方法演示一下
操作成功或失败会返回布尔值
let obj = {name:'lulu'};
console.log(Reflect.get(obj,'name')) // name
console.log(Reflect.has(obj,'name')) // true
console.log(Reflect.has(obj,'name1')) // false
console.log(Reflect.set(obj,'age',24)) // true
console.log(Reflect.get(obj,'age')) // 24
把我的代码稍微改装一下
cc_vue/src/index.js
proxyVm(data = {}, target = this) {
for (let key in data) {
Reflect.defineProperty(target, key, {
enumerable: true, // 描述属性是否会出现在for in 或者 Object.keys()的遍历中
configurable: true, // 描述属性是否配置,以及可否删除
get() {
return Reflect.get(data,key)
},
set(newVal) {
if (newVal !== data[key]) {
Reflect.set(data,key,newVal)
}
}
});
}
}
我见过很多人离开axios或者jq中的ajax就没法做项目了, 其实完全可以自己封装一个, 原理都差不多, 而且现在也可以用'feach'弄, 条件允许的情况下真的不一定非要依赖插件.
独立的文件夹负责网络相关的事宜;
cc_vue/use/http
class C_http {
constructor() {
// 请求可能很多, 并且需要互不干涉, 所以决定每个类生成一个独立的请求
let request = new XMLHttpRequest();
request.responseType = 'json';
this.request = request;
}
}
编写插件的时候, 先要考虑用户会怎么用它
http.get('http:xxx.com', { name: 'lulu'}).then(data => {});
http.post('http:xxx.com', { name: 'lulu'}).then(data => {});
get与post方法其实不用每次都初始化, 我们直接写在外面
处理好参数直接调用open方法, 进入open状态某些参数才能设置;
在有参数的情况下为链接添加'?';
参数品在链接后面, 我之前遇到一个bug, 拼接参数的时候如果结尾是'&'部分手机出现跳转错误, 所以为了防止特殊情况的发生, 我们要判断一下干掉结尾的'&';
function get(path, data) {
let c_http = new C_http();
let str = '?';
for (let i in data) {
str += `${i}=${data[i]}&`;
}
if (str.charAt(str.length - 1) === '&') {
str = str.slice(0, -1);
}
path = str === '?' ? path : `${path}${str}`;
c_http.request.open('GET', path);
return c_http.handleReadyStateChange();
}
post
这个就很好说了, .data是请求自带的.
function post(path, data) {
let c_http = new C_http();
c_http.request.open('POST', path);
c_http.data = data;
return c_http.handleReadyStateChange();
}
handleReadyStateChange
handleReadyStateChange() {
// 这个需要在open之后写
// 设置数据类型
this.request.setRequestHeader(
'content-type',
'application/json;charset=utf-8'
);
// 现在前端所有返回都是Promise化;
return new Promise((resolve) => {
this.request.onreadystatechange = () => {
// 0 UNSENT 代理被创建,但尚未调用 open() 方法。
// 1 OPENED open() 方法已经被调用。
// 2 HEADERS_RECEIVED send() 方法已经被调用,并且头部和状态已经可获得。
// 3 LOADING 下载中; responseText 属性已经包含部分数据。
// 4 DONE 下载操作已完成。
if (this.request.readyState === 4) {
// 这里因为是独立开发, 就直接写200了, 具体项目里面会比较复杂
if (this.request.status === 200) {
// 返回值都在response变量里面
resolve(this.request.response);
}
}
};
// 真正的发送事件.
this.send();
});
}
send
send() {
// 数据一定要JSON处理一下
this.request.send(JSON.stringify(this.data));
}
很多人提到 "拦截器" 会感觉很高大上, 其实真的没啥
简易的拦截器"interceptors"👇
// 1: 使用对象不使用[]是因为可以高效的删除拦截器
const interceptorsList = {};
// 2: 每次发送数据之前执行所有拦截器, 别忘了把请求源传进去.
send() {
for (let i in interceptorsList) {
interceptorsList[i](this);
}
this.request.send(JSON.stringify(this.data));
}
// 3: 添加与删除拦截器的方法, 没啥东西所以直接协议期了.
function interceptors(cb, type) {
if (type === 'remove') {
delete interceptorsList[cb];
} else if (typeof cb === 'function') {
interceptorsList[cb] = cb;
}
}
边边角角的小功能
class C_http {
constructor() {
let request = new XMLHttpRequest();
request.timeout = 5000;
request.responseType = 'json';
request.ontimeout = this.ontimeout;
this.request = request;
}
ontimeout() {
throw new Error('超时了,快检查一下');
}
abort() {
this.request.abort();
}
}
简易的'axios'就做好, 普通的请求都没问题的
请求做好了, 当然要启动服务了, 本次就不连接数据库了, 要不然就跑题了.
koa2
不了解koa的同学跟着做也没问题
npm install koa-generator -g
Koa2 项目名
cc_vue/use/server 是本次工程的服务相关存放处.
cc_vue/use/server/bin/www
端口号可以随意更改, 当时9999被占了我就设了9998;
const pros = '9998';
var port = normalizePort(process.env.PORT || pros);
cc_vue/use/server/routes/index.js
这个页面就是专门处理路由相关, koa很贴心, router.get就是处理get请求.
每个函数必须写async也是为了著名的'洋葱圈'.
想了解更多相关知识可以去看koa教程, 我也是用到的时候才会去看一眼.
写代码的时候遇到需要测试延迟相关的时候, 不要总用定时器, 要多自己启动服务.
const router = require('koa-router')();
router.get('/', async (ctx, next) => {
ctx.body = {
data: '我是数据'
};
});
router.post('/', async (ctx, next) => {
ctx.body = ctx.request.body;
});
module.exports = router;
写到现在可以开始跑起来试试了
😺一个很传统的问题出现了'跨域'.
这里我们简单的选择插件来解决, 十分粗暴.
cc_vue/use/server/app.js
npm install --save koa2-cors
var cors = require('koa2-cors');
app.use(cors());
既然说到这里就, 那就总结一下吧
跨域的几种方式
本次测试的dom结构
<div id="app">
<p>n: {{ n.length }}</p>
<p>m: {{ m }}</p>
<p>n+m: {{ n.length + m }}</p>
<p>{{ http }}</p>
</div>
let vm = new C({
el: '#app',
data: {
n: [1, 2, 3],
m: 2,
http: '等待中'
}
});
http.get('http://localhost:9998/', { name: 'lulu', age: '23' }).then(data => {
vm.http = data.data;
vm.n.length = 1
vm.n.push('22')
});
具体效果请在工程里面查看
做这个工程能让自己对vue对框架以及数据的操作有更深的理解, 受益匪浅.
下一集:
本文章转载自SegmentFault.
IT Support 训练营01期
2025/11/10 06:13 (Sydney)
CCNA考证班01期
2025/09/06 08:37 (Sydney)
商业数据分析实战班第17期
2025/03/15 06:13 (Sydney)
地址
Level 10b, 144 Edward Street, Brisbane CBD(Headquarter)Level 2, 171 La Trobe St, Melbourne VIC 3000四川省成都市武侯区桂溪街道天府大道中段500号D5东方希望天祥广场B座45A13号Business Hub, 155 Waymouth St, Adelaide SA 5000Disclaimer
JR Academy acknowledges Traditional Owners of Country throughout Australia and recognises the continuing connection to lands, waters and communities. We pay our respect to Aboriginal and Torres Strait Islander cultures; and to Elders past and present. Aboriginal and Torres Strait Islander peoples should be aware that this website may contain images or names of people who have since passed away.
匠人学院网站上的所有内容,包括课程材料、徽标和匠人学院网站上提供的信息,均受澳大利亚政府知识产权法的保护。严禁未经授权使用、销售、分发、复制或修改。违规行为可能会导致法律诉讼。通过访问我们的网站,您同意尊重我们的知识产权。 JR Academy Pty Ltd 保留所有权利,包括专利、商标和版权。任何侵权行为都将受到法律追究。查看用户协议
© 2017-2024 JR Academy Pty Ltd. All rights reserved.
ABN 26621887572