开启生产模式 造轮子,挖底层,笨鸟先飞
︿( ̄︶ ̄)︿
这个账号纯粹是为了 监督自己学习 更新,理解课程的
所有的代码 都是在上课后, 我加入自己的理解 来编写的, 没有所谓的硬搬,小白适看 同样欢迎大佬来给予意见和方向 对应的详细解释我都全部 标注到代码里了,所以不会有 分模块的讲解 按照模块的步骤去操作吧
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="x-dns-prefetch-control" content="on">
<link rel="icon" href="data:;base64,=">
<title>vue3造轮子-第一天</title>
</head>
<body>
<div id="App">
<p>{{user}}</p>
</div>
<!-- <script src="http://unpkg.com/vue@next"></script>-->
<script>
/*用自己的理解来实现 vue 的数据渲染*/
/*整体框架分析*/
/*1.Vue 是一个对象*/
/*2.它(Vue)调用了 一个名为 createApp 的方法 传入了一个 options 的对象为参数*/
/*3.在createApp 调用结束 暴露出一个 名为 mount 的方法 把 根节点 作为参数传入 */
const Vue={
createApp(options){
return {
mount(sel){
/*这里的 sel 就是对应的根节点 */
const parent = document.querySelector(sel);
/* 接下来 就要把 parent 里面的子元素和 options 编译,渲染,最后挂载在 parent 里面 */
/* 这里先忽略上面的 p 标签 把 他看成一个template */
/*
* <div id='App'>
* <template>
* <p>{{user}}</p>
* </template>
* </div>
* */
/*这里的template 他是一个虚拟节点 它最后需要进行编译处理 最后挂载在parent 里*/
/* 而 处理template 他一般是在 render()的一个方法里 处理 ---> 步骤4 */
/*5.接下来 把 options里的数据 和template 绑定 (对应步骤4 里面的可以直接 this获取到options 里面的data() 定义的user)*/
/*先判断 我们声明的实例里有没有 render() 没有我们再创建它 */
if(!options.render){
options.render = this.compile(parent.innerHTML);
}
/*6.把options 里的data 数据绑定在 rander 里 让它内部能够调用 data() 里面的数据*/
// const el = options.render.call(options.data());
/*7.最后把对应的 子节点 el append 到 parent */
// parent.innerHTML = "";
// parent.appendChild(el);
/*在定义的实例里 有data() 和setup() 都对应的 定义里user 值,在vue3 中
* 它会先查询实例里是否含有 setup 如果里面定义了我们需要的值 那它直接从里面获取
* 如果没有 那么它才会去查询 data 里的值
* 相同的值 它以setup为主 */
/*8.根据以上步骤
* 先判断options 里是否含有 setup 和 data
* 如果有 那我们分别定义一个对象来保存里面的数据 */
if(options.setup){
options.setupData = options.setup();
}
if(options.data){
options.data = options.data();
}
/*接下来我们要在数据被读取的时候去对比 setup 和 data
* 判断 我们所读取的值按照之前定义的顺序 分别从对应的 setup 和 data 中返回
* 这里在 vue2 中监听数据的变化 用的是Object.defineproperty() 中的getter 来监听
* 在vue3 中 监听数据变化 改用了 es6 的 proxy
* 对于 Object.defineproperty 它是 监听对象的属性
* 而 proxy 是监听对象本身 弊端 兼容性差 ie 11 下不兼容
* */
this.proxy = new Proxy(this,{
set(target, p, value, receiver) {
},
get(target, p, receiver) {
/*9.接下来 在监听到数据被读取的时候 我们要判断 p(属性本身)存在哪个 对象里
* setup 为主 查不到才去 data 里查找*/
if(p in options.setupData){
return options.setupData[p];
}else {
return options.data[p];
}
}
})
/*10.接下来 把之前 步骤6 给render 赋值的数据 改变成proxy */
const el = options.render.call(this.proxy);
parent.innerHTML = "";
parent.appendChild(el);
},
/*步骤4*/
compile(template)
{
return function render() {
/*这里相当于 解析 template 然后创建为虚拟节点 然后渲染数据 最后把虚拟节点 暴露出去作为子节点 */
const p = document.createElement('p');
/*到这里会 很好奇 this.user 是怎么来的 这里不用纠结 按照步骤来 */
p.textContent = this.user2;
return p;
}
}
}
}
}
</script>
<script>
// 搭建基础的vue3框架
const App = Vue.createApp({
//vue 2 数据对象
data(){
return {
user:"小面",
user2:"我是小米"
}
},
//vue 3 的数据对象定义
setup(){
return {
user:"setup 定义的 user"
}
}
}).mount("#App");
</script>
</body>
</html>
在原先的基础上添加 了 createRender方法来实现 渲染的平台上
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="x-dns-prefetch-control" content="on">
<link rel="icon" href="data:;base64,=">
<title>vue3造轮子-第二天</title>
</head>
<body>
<!--<div id="App">-->
<!-- <p>{{user}}</p>-->
<!--</div>-->
<canvas id="App" width="200px" height="200px" style="background: darkseagreen"></canvas>
<script>
/*在之前的day1 的基础上
*
* vue3 新增了自定义渲染 平台
* 可以按自己的需求去吧 数据渲染到我们想要的平台上
* */
const Vue={
/*定义一个 createRender 传入两个参数
* querySelect 处理返回要渲染的根节点
* inset 把虚拟子节点 挂载到 根节点*/
createRender({querySelect,inset}){
return {
createApp(options){
return {
mount(sel){
//把根节点作为参数传入 querySelect 返回它的实例
this.parent = querySelect(sel);
if(!options.render){
options.render = this.compile(parent.innerHTML);
}
if(options.setup){
options.setupData = options.setup();
}
if(options.data){
options.data = options.data();
}
this.proxy = new Proxy(this,{
set(target, p, value, receiver) {
},
get(target, p, receiver) {
if(p in options.setupData){
return options.setupData[p];
}else {
return options.data[p];
}
}
});
const el = options.render.call(this.proxy);
/*把原先 渲染到根节点的方式封装为一个方法 把子节点,和跟节点都传入 inset 在里面处理渲染的方式*/
inset(el,this.parent);
},
compile(template)
{
return function render() {
//canvas 返回对应的数据
return this.user2;
// const p = document.createElement('p');
// p.textContent = this.user;
// return p;
}
}
}
}
}
},
createApp(options){
const render = this.createRender({
querySelect(sel){
//模拟canvas
const canvas = document.querySelector(sel);
const ctx = canvas.getContext("2d");
return ctx;
/* dom */
// return document.querySelector(sel);
},
inset(el,parent){
//canvas
parent.font="12px Arial";
parent.fillText(el,0,50);
//dom
// parent.innerHTML = "";
// parent.appendChild(el);
}
});
return render.createApp(options)
}
}
</script>
<script>
const App = Vue.createApp({
//vue 2 数据对象
data(){
return {
user:"小面",
user2:"我是小米"
}
},
//vue 3 的数据对象定义
setup(){
return {
user:"setup 定义的 userzh值"
}
}
}).mount("#App");
</script>
</body>
</html>