Vue 源码学习

Vue 源码学习

资料
熟悉设计模式
AST 抽象语法树
htmlParse 解析器
wue 仿Vue实现
Vue.js 源码学习笔记
Virtual DOM patching algorithm based on Snabbdom
逐行学习vue 源码

以下是个人学习vue 源码的先后学习过程:(假设我是小白,按照下面顺序学习会轻松很多,循序渐进)

  1. 学习正则表达式

  2. 学习js 设计模式,重点观察者模式 50行代码的MVVM,感受闭包的艺术

  3. 了解AST 抽象语法树的概念,并通过博文开头的资料 htmlParse解析器 中分析parse 原理

  4. 参看githut 上的开源仿 Vue 实现项目 wue 仿Vue实现、Vue 源码注释版(注释版可以放在后面看)

  5. 查看调试vue.js 2.1.3 版本单文件脚本(或其他新版本) https://cdn.bootcss.com/vue/2.1.3/vue.js

  6. 学习 es6 模块化编程,学习 rollup 构建工具,学习 flow 类型检查工具

  7. es6 版本的vue 学习 源码调试方法,对应版本V2.5.9

  8. 待补充~

步骤5 举例

单步调试如下代码:


<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Documenttitle>
  
  <script type="text/javascript" src="./vue.js">script>
head>
<body>
  
  <div id="app">
    <div><span v-text="reverse">span>div>
    <div><span v-text="tip">span>div>
    <div><bar :age="info.age">bar>div>
  div>
  
  <script>
      let bar = {
        name: 'Bar',
        template: '
name: {{name}}
age: {{age}}
'
, props: [ 'age' ], created() { console.log('child created.') }, data() { return { name: 11 } } } var app = new Vue({ el: "#app", components: { bar: bar }, created() { console.log('created.') }, data() { return { message: "hello.", info: { age: 12 } } }, computed: { reverse() { return this.message.split("").reverse().join("") }, tip() { return `${this.message} world.` } } }) setTimeout(()=>{ app.info.age = 23 }, 400)
script> body> html>

编译过程中会生成render code 如下:

_h(
   'div',
   {attrs:{"id":"app"}},
   [
    _h(
       'div',
       [
        _h('span',{domProps:{"textContent":_s(reverse)}})]),
        " ",
        _h(
           'div',
           [_h('span',{domProps:{"textContent":_s(tip)}})]
          ),
        " ",
        _h('div',[_h('bar',{attrs:{"age":info.age}})]
      )
   ]
)

其中_h_s 方法分别对应:

// shorthands used in render functions
Vue.prototype._h = createElement;
// toString for mustaches
Vue.prototype._s = _toString;

官网example 代码对应render code示例:render code 示例

大体过程:

  1. render code 的执行,即代码调用 vm._render() , 后生成了包含dom 结构的vnode 实例;
  2. vmpatch 调用会把 vnode 树转为document 的对象并挂在 vm.$el 上,并挂载到根节点上,如示例中的
    上;
代码案例

示例1
index.html

<body>
  <div id="demo">
    <div v-text="message">div>
    <span>{{info.age}}span>
  div>
  <script src="./app.js">script>
body>

app.js

var demo = new Vue({
  el: '#demo',
  data: {
    info: {
      name: {
        firstName: 'Lili'
      }
    }
  },
  computed: {
    message () {
      return 'Hello' + this.info.name.firstName
    }
  },
  created: function () {
    this.init()
  },
  methods: {
    init () {
      setTimeout(() => {
        this.info.age = 88
        delete this.info.name.firstName
        this.$forceUpdate()
      }, 1000)
    }
  }
})

执行结果:

问题:上述示例如果注释掉 this.$forceUpdate() 的结果呢? 自己动手试一下吧


示例2
index.html

<body>
  <div id="demo">
    <ul style="border-bottom: 1px solid plum;">
       <li v-for="item in info.list" :key="item">{{item}}li>
    ul>
  div>
  <script src="./app.js">script>
body>

app.js

var demo = new Vue({
  el: '#demo',
  data: {
    info: {
      list: ['app', 'web', 'ios']
    }
  }
})
// 通过数组索引修改数值
demo.info.list[0] = 'apple'

执行结果:
Vue 源码学习_第1张图片
问题1:那我如何修改数组的值呢? (下文回答)

现在将app.js 做了如下调整:

var demo = new Vue({
  el: '#demo',
  data: {
    info: {
      list: ['app', 'web', 'ios']
    }
  },
  created: function () {
    this.init()
  },
  methods: {
    init () {
      this.info.list[0] = 'apple'
    }
  }
})

发现执行结果有效了:
Vue 源码学习_第2张图片
问题2:为什么这样又可以了呢?

现在来揭晓答案:
问题1:原因是Object.defineProperty的局限,set方法在一些场景下不会触发(例如示例)。官方给的解决方法是利用 Vue.set或者vm.$set。可参看官网列表渲染。

问题2:原因是init方法是在created生命周期中调用的,此时dom结构还没开始生成。如果放在mounted生命周期中调用则同样不会更新。

(未完,待续)

你可能感兴趣的:(学习,笔记)