飞道的博客

手写Vue3初始化源码

326人阅读  评论(0)

一、Vue3优点

1.类型支持更好:

  • vue2中使用new Vue的方式会产生动态的东西,像this
  • 使用函数的方式,函数方式减少this的使用,有益于对ts的支持

2.利于tree-shaking:

  • vue2中很多组件与vue实例连在一起,即便没有使用,还是会被打包,没法优化
  • vue3中通过命名导出导入其中的大多数 API,以便打包器可以检测出未使用的代码并删除它们

3.API简化、一致性:render函数、sync修饰符,指令定义等

4.复用性:composition API

5.性能优化:响应式、编译优化

6.扩展性:自定义渲染器

 

二、vue3初始化

vue3初始化用法:


  
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  6. <meta http-equiv="X-UA-Compatible" content="ie=edge" />
  7. <title>Document </title>
  8. </head>
  9. <body>
  10. <div id="app">
  11. <h3>{{ title }} </h3>
  12. </div>
  13. <script src="http://unpkg.com/vue@next"> </script>
  14. <script>
  15. const { createApp } = Vue;
  16. const app = createApp({
  17. data() {
  18. return {
  19. title: "hello"
  20. };
  21. }
  22. });
  23. app.mount( "#app");
  24. </script>
  25. </body>
  26. </html>

 

三、手写vue3初始化代码

整体思路:

从上面的初始化代码可看出,要有以下几点:

1.createApp:从vue中导出createApp

2.mount:createApp()返回包含mount的对象


  
  1. const Vue = {
  2. createApp(options) {
  3. return {
  4. mount(selector) {
  5. }
  6. };
  7. }
  8. };

 mount中会获取渲染函数,渲染DOM元素,并追加到宿主元素

3.compile:返回render函数

4.render:返回DOM元素


  
  1. const Vue = {
  2. createApp(options) {
  3. return {
  4. mount(selector) {
  5. const parent = document.querySelector(selector);
  6. // 获取渲染函数:模板=>渲染函数render
  7. if (options.render) {
  8. options.render = this.compile(parent.innerHTML);
  9. }
  10. // 渲染DOM,追加到宿主元素
  11. const el = options.render.call(options.data());
  12. parent.innerHTML = "";
  13. parent.appendChild(el);
  14. },
  15. // 将模板转化为渲染函数
  16. compile(template) {
  17. return function render() {
  18. // 描述视图
  19. // 假设返回h3
  20. const h3 = document.createElement( "h3");
  21. h3.textContent = this.title;
  22. return h3;
  23. };
  24. }
  25. };
  26. }
  27. };

5.兼容composion API:用Proxy对属性进行监听,当setup中有该属性时,获取setup中的值,没有时再去data、methods等中查找


  
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  6. <meta http-equiv="X-UA-Compatible" content="ie=edge" />
  7. <title>Document </title>
  8. </head>
  9. <body>
  10. <div id="app">
  11. <h3>{{ title }} </h3>
  12. </div>
  13. <!-- <script src="http://unpkg.com/vue@next"></script> -->
  14. <script>
  15. const Vue = {
  16. createApp(options) {
  17. return {
  18. mount(selector) {
  19. const parent = document.querySelector(selector);
  20. // 获取渲染函数:模板=>渲染函数render
  21. if (!options.render) {
  22. options.render = this.compile(parent.innerHTML);
  23. console.log(options.render);
  24. }
  25. // 将setup和data中的数据分别保存到app实例上,以便后面使用
  26. if (options.setup) {
  27. this.setupState = options.setup();
  28. } else {
  29. this.data = options.data();
  30. }
  31. // 兼容composition API
  32. this.proxy = new Proxy( this, {
  33. get(target, key) {
  34. if (key in target.setupState) {
  35. return target.setupState[key];
  36. }
  37. return target.data[key];
  38. },
  39. set(target, key, val) {
  40. if (key in target.setupState) {
  41. target.setupState[key] = val;
  42. } else {
  43. target.data[key] = val;
  44. }
  45. }
  46. });
  47. // 渲染DOM,追加到宿主元素
  48. const el = options.render.call( this.proxy);
  49. parent.innerHTML = "";
  50. parent.appendChild(el);
  51. },
  52. // 将模板转化为渲染函数
  53. compile(template) {
  54. return function render() {
  55. // console.log(this); //call绑定后this指向绑定的数据
  56. // 描述视图
  57. const h3 = document.createElement( "h3");
  58. h3.textContent = this.title;
  59. return h3;
  60. };
  61. }
  62. };
  63. }
  64. };
  65. </script>
  66. <script>
  67. const { createApp } = Vue;
  68. const app = createApp({
  69. data() {
  70. return {
  71. title: "hello"
  72. };
  73. },
  74. setup() {
  75. return {
  76. title: "setup title"
  77. };
  78. }
  79. });
  80. app.mount( "#app");
  81. </script>
  82. </body>
  83. </html>

6.createRenderer:渲染器,针对不同的平台环境,有不同的代码,增加代码的扩展性。

浏览器环境:设置一个通用的渲染器,将需要用到的dom操作作为参数传进去


  
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  6. <meta http-equiv="X-UA-Compatible" content="ie=edge" />
  7. <title>Document </title>
  8. </head>
  9. <body>
  10. <div id="app">
  11. <h3>{{ title }} </h3>
  12. </div>
  13. <!-- <script src="http://unpkg.com/vue@next"></script> -->
  14. <script>
  15. const Vue = {
  16. createApp(options) {
  17. const renderer = Vue.createRenderer({
  18. querySelector(selector) {
  19. return document.querySelector(selector);
  20. },
  21. insert(child, parent, anchor) {
  22. parent.insertBefore(child, anchor || null);
  23. }
  24. });
  25. return renderer.createApp(options);
  26. },
  27. createRenderer({ querySelector, insert }) {
  28. // 获得渲染器
  29. return {
  30. createApp(options) {
  31. return {
  32. mount(selector) {
  33. const parent = querySelector(selector);
  34. // 获取渲染函数:模板=>渲染函数render
  35. if (!options.render) {
  36. options.render = this.compile(parent.innerHTML);
  37. console.log(options.render);
  38. }
  39. // 将setup和data中的数据分别保存到app实例上,以便后面使用
  40. if (options.setup) {
  41. this.setupState = options.setup();
  42. } else {
  43. this.data = options.data();
  44. }
  45. // 兼容composition API
  46. this.proxy = new Proxy( this, {
  47. get(target, key) {
  48. if (key in target.setupState) {
  49. return target.setupState[key];
  50. }
  51. return target.data[key];
  52. },
  53. set(target, key, val) {
  54. if (key in target.setupState) {
  55. target.setupState[key] = val;
  56. } else {
  57. target.data[key] = val;
  58. }
  59. }
  60. });
  61. // 渲染DOM,追加到宿主元素
  62. const el = options.render.call( this.proxy);
  63. parent.innerHTML = "";
  64. insert(el, parent);
  65. },
  66. // 将模板转化为渲染函数
  67. compile(template) {
  68. return function render() {
  69. // console.log(this); //call绑定后this指向绑定的数据
  70. // 描述视图
  71. const h3 = document.createElement( "h3");
  72. h3.textContent = this.title;
  73. return h3;
  74. };
  75. }
  76. };
  77. }
  78. };
  79. }
  80. };
  81. </script>
  82. <script>
  83. const { createApp } = Vue;
  84. const app = createApp({
  85. data() {
  86. return {
  87. title: "hello"
  88. };
  89. },
  90. setup() {
  91. return {
  92. title: "setup title"
  93. };
  94. }
  95. });
  96. app.mount( "#app");
  97. </script>
  98. </body>
  99. </html>

 

四、总结

vue3初始化中做了什么:

  • vue3中初始化时设置了一个渲染器,返回createApp方法
  • createApp方法返回一个mount方法
  • mount方法里对composition API做兼容处理,获取渲染函数,渲染DOM并追加到宿主元素
  • 初始化时调用的createApp中,获取设置好的渲染器并传入渲染参数,返回渲染器中的createApp方法

 

 

 

 


转载:https://blog.csdn.net/MiemieWan/article/details/117220491
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场